标 题: VB6反编译详解(一)
作 者: kenmark
时 间: 2006-07-09 16:59
链 接: http://bbs.pediy.com/showthread.php?threadid=28715
详细信息:
VB6反编译详解byKenmark-Fenix
**************************************************
最新于2006-7-13更新!
**************************************************
写本文已经惦记了好几年了,由于一直没有完整的资料和充裕的时间,所以一直没有动手。
在这里一方面是写给大家看看,另一方面是招募更多有志于反编译VB6的同志们一起来研究学习!
我的E-MAIL:ken.mingyuan@hotmail.comken.mingyuan@gmail.com
我的BLOG:blog.csdn.net/kenmark
我的QQ:188916915
十分期待着与大家一起学习!
——Kenmark
VB6是一个半编译半解释的语言,编译后程序主要在运行库MSVBVM60.DLL下转悠,通过与MSVBVM60的互动来完成程序运行的过程。
1.引入(参考:《VB程序大揭密》我的博客上有转载http://blog.csdn.net/Kenmark/archive/2005/08/11/450985.aspx)
我们用W32DASM打开一个中型的VB程序来反汇编,我们发现程序中用到的MSGBox÷FileCopy等理应对应API函数居然一个都没有出现在编译后程序的IMPORTTABLE里,一般VC和DEPHI都是直接出现在编译后程序的IMPORTTABLE里的,而我们的VB程序用到了如此之多的API函数居然只使用了一个DLL——MSVBVM60.DLL。
然后用工具打开MSVBVM60.DLL,一看,输出的函数还真不少,其中有用__vba和rtc开头的也有直接就是函数名的,仔细一看,哇赛,可以说完全是一个windowsAPI的代理,应有尽有:
rtcRandomize:Randomize函数的对应API;
rtcMidCharVar:Mid函数的对应API;
rtcLeftCharVar、rtcRightCharVar:看出来了吧,这些是Left、Right函数的对应API;
rtcUpperCaseVar:UCase函数的对应API;
rtcKillFiles:Kill语句的对应API;
rtcFileCopy:FileCopy语句的对应API;
rtcFileLength:EOF、FileLen函数的对应API;
rtcGetTimer:RandomizeTimer中获取Timer的对应API;
rtcShell:Shell函数的的对应API;
rtcMakeDir:MkDir语句的对应API;
rtcRemoveDir:RmDir语句的对应API;
rtcDir:Dir函数的对应API;
rtcSpaceVar:Space函数的对应API;
原来,所有VB的操作函数都是在调用MSVBVM60.DLL里面实现
前缀是rtc的是一般的语句和函数
涉及字符串处理的都叫var,例如:
__vbaUbound:UBound的对应API;
__vbaFileOpen:Open语句的对应API;
__vbaStrCmp:比较两个字符串:IfString1=String2Then......
__vbaVarOr:Or运算符的对应API;
__vbaRedim:Redim语句的对应API;
__vbaRedimPreserve:Redim语句加上Preserve参数的对应API;
__vbaGet、vbaPut:Get、Put语句的对应API……
我们还看到一个DLL:DllFunctionCall,这个就是我们调用其他DLL时需要向MSVBVM60申请的,…………。
可以说VB的程序是一个包裹在MSVBVM60阴影控制下的孩子,所有的操作都要直接向它请求,而MSVBVM60完全可以称得上是一个代理的机器。从程序开始,到运行中的所有操作,函数调用,错误报告等等,都是由它一手包办的。
来吧,我们这里不是来介绍它是怎么构成的我们要搞掉它虚伪的外表,把我们的代码从MSVBVM60的封建保护下救出来。
我的资料大量是参考一个开源的VB程序解析程序(居然也是用VB编写的),这个程序可以完全分析出VB程序(没有加壳)的PROJECT信息以及完全将FORM变回来,对于代码呢,可以获得SUBMAIN的汇编代码地址,但是不能返回到VB代码,里面还内置了一个假的反汇编器,以后我会说的!
我提供了它的下载,大家去看看,不知道它的资料是哪里来的十分全面,就是用VB写的比较繁!还有由于界面控制太多,代码有点乱!
2.程序初始化
我们用W32DASM打开任何VB程序,跳到ENTRYPOINT之后看到的总是一个PUSH*****一个地址,然构调用MSVBVM60.DLL的ThunRTMain
而在VC程序里,我们至少要看到进程的创建什么的,其实这个VB函数是一切的开端,由它开始来解析整个编译后的程序,完成系统环境的初始化,然后找到真正的程序入口,跳转到程序的领空。
所以这个PUSH指令压入栈的是VBEXE(姑且这么称呼)初始化结构的开始。我们存下这个地址(这个是一个VA要正确地转换成文件地OFFSET先要-ImageBase然后要对照块表,可以用工具完成,熟悉了一下就能看出来),然后我们跳到那里。
看看是什么,哇赛,是VB程序的招牌也!“VB5!”是所有VB程序初始化结构入口地址指向的MAGIC字符,找到这里,开始能够读取VB初始化结构了(我们现在做的就是ThunRTMain要完成的)
我们开始了!
部分名词解读:
RVA相对虚拟地址,一般需要使用RVA2Offset来将其转换成绝对的文件偏移变量前缀为pr或是a
VA虚拟地址,减去IMAGEBASE就是RVA变量前缀为p或是a
offset相对结构的偏移变量前缀为o
1)VBHEADER
从PE文件的ENTRYPOINT进入后第一个指令是压入一个指针来表示VBHEADER的位置,这个指令为
pushxxxxxxxx地址是经过基址IMAGE_BASE偏移后的地址,所以减去IMAGE_BASE后获得的就是VBHEADER开始的VA然后用PEFile类中的函数加以分析可以得到的是文件中VBHEADER的偏移量。
这个指令在机器码中是这样表示的:68C0114000
68是PUSH的机器码,而后是内存存储方式的地址化成汇编语言就是:
push004011c0所以减去基址后就是11C0然后进行VA2OFFSET得到的是11c0的偏移,转向那段数据就能得到VBHEADER。
这是VBHEADER结构的C语言描述:
typedefstruct
{
charSignature[4];//四个字节的签名符号,和PEHEADER里的那个signature是类似性质的东西,VB文件都是"VB5!"
WORDRtBuild;//运行时创立的变量(类似编译的时间)
BYTELangDLL[14];//语言DLL文件的名字(如果是0x2A的话就代表是空或者是默认的)
BYTEBakLangDLL[14];//备份DLL语言文件的名字(如果是0x7F的话就代表是空或者是默认的,改变这个值堆EXE文件的运行没有作用)
WORDRtDLLVer;//运行是DLL文件的版本
DWORDLangID;//语言的ID
DWORDBakLangID;//备份语言的ID(只有当语言ID存在时它才存在)
DWORDpSubMain;//RVA(实际研究下来是VA)submain过程的地址指针(3.)(如果时00000000则代表这个EXE时从FORM窗体文件开始运行的)
DWORDpProjInfo;//VA工程信息的地址指针,指向一个ProjectInfo_t结构(2.)
DWORDfMDLIntObjs;//?详细见"MDL内部组建的标志表"
DWORDfMDLIntObjs2;//?详细见"MDL内部组建的标志表"
DWORDThreadFlags;//线程的标志
//*标记的定义(ThreadFlags数值的含义)
//+-------+----------------+--------------------------------------------------------+
//|值|名字|描述|
//+-------+----------------+--------------------------------------------------------+
//|0x01|ApartmentModel|特别化的多线程使用一个分开的模型|
//|0x02|RequireLicense|特别化需要进行认证(只对OCX)|
//|0x04|Unattended|特别化的没有GUI图形界面的元素需要初始化|
//|0x08|SingleThreaded|特别化的静态区时单线程的|
//|0x10|Retained|特别化的将文件保存在内存中(只对Unattended)|
//+-------+----------------+--------------------------------------------------------+
//ex:如果是0x15就表示是一个既有多线程,内存常驻,并且没有GUI元素要初始化
DWORDThreadCount;//线程个数
WORDFrmCount;//窗体个数
WORDpExternalComponentCount;//VA外部引用个数例如WINSOCK组件的引用
DWORDThunkCount;//?大概是内存对齐相关的东西
DWORDpGUITable;//VAGUI元素表的地址指针(指向一个GUITable_t结构(4.四))
DWORDpExternalComponentTable;//VA外部引用表的地址指针
//DWORDpProjDep;//VA工程的描述的地址指针(这个其实没有)
DWORDpComRegData;//VACOM注册数据的地址指针
DWORDoProjExename;//Offset指向工程EXE名字的字符串
DWORDoProjTitle;//Offset指向工程标题的字符串
DWORDoHelpFile;//Offset指向帮助文件的字符串
DWORDoProjName;//Offset指向工程名的字符串
}VBHeader_t;
//*MDL内部组建的标志表
//+---------+------------+---------------+
//|ID|值|组建名称|
//+---------+------------+---------------+
//|第一个标志|
//+---------+------------+---------------+
//|0x00|0x00000001|PictureBox|
//|0x01|0x00000002|Label|
//|0x02|0x00000004|TextBox|
//|0x03|0x00000008|Frame|
//|0x04|0x00000010|CommandButton|
//|0x05|0x00000020|CheckBox|
//|0x06|0x00000040|OptionButton|
//|0x07|0x00000080|ComboBox|
//|0x08|0x00000100|ListBox|
//|0x09|0x00000200|HScrollBar|
//|0x0A|0x00000400|VScrollBar|
//|0x0B|0x00000800|Timer|
//|0x0C|0x00001000|Print|
//|0x0D|0x00002000|Form|
//|0x0E|0x00004000|Screen|
//|0x0F|0x00008000|Clipboard|
//|0x10|0x00010000|Drive|
//|0x11|0x00020000|Dir|
//|0x12|0x00040000|FileListBox|
//|0x13|0x00080000|Menu|
//|0x14|0x00100000|MDIForm|
//|0x15|0x00200000|App|
//|0x16|0x00400000|Shape|
//|0x17|0x00800000|Line|
//|0x18|0x01000000|Image|
//|0x19|0x02000000|Unsupported|
//|0x1A|0x04000000|Unsupported|
//|0x1B|0x08000000|Unsupported|
//|0x1C|0x10000000|Unsupported|
//|0x1D|0x20000000|Unsupported|
//|0x1E|0x40000000|Unsupported|
//|0x1F|0x80000000|Unsupported|
//+---------+------------+---------------+
//|第二个标志|
//+---------+------------+---------------+
//|0x20|0x00000001|Unsupported|
//|0x21|0x00000002|Unsupported|
//|0x22|0x00000004|Unsupported|
//|0x23|0x00000008|Unsupported|
//|0x24|0x00000010|Unsupported|
//|0x25|0x00000020|DataQuery|
//|0x26|0x00000040|OLE|
//|0x27|0x00000080|Unsupported|
//|0x28|0x00000100|UserControl|
//|0x29|0x00000200|PropertyPage|
//|0x2A|0x00000400|Document|
//|0x2B|0x00000800|Unsupported|
//+---------+------------+---------------+
//ex:如果值是0x30F000(那个被叫做"静态二进制常量定义在大多数的地方")就是意味着来初始化打印机,窗体,屏幕,剪贴板,组建(0xF000)也有Drive/Dir组建(0x30000).
//这是VB工程的一个默认的设置因为这些组建都能从一个模块中获得module(例如,他们是没有图像的除了经常被创造窗体)
2)工程信息
从VBHEADER->ProjectInfo(是一个VA)-IMAGEBASE后在通过RAV2OFFSET得到的是工程信息的偏移地址.
这是PROJECT_INFO结构的C语言描述:
constlongMAX_PATH=260;//最长的PATH长度
typedefstruct
{
DWORDSignature;//结构的签名特性,和魔术字符类似
DWORDpObjectTable;//VA结构指向的组件列表的地址指针(很重要的!(7.))
DWORDNull1;//?没有用的东西
WORDpStartOfCode;//VA代码开始点,类似PEHEAD->EntryPoint这里告诉了VB代码实际的开始点
DWORDFlag1;//标志1
DWORDThreadSpace;//多线程的空间?????????????????
DWORDpVBAExcrptionhandler;//VAVBA意外处理机器地址指针
DWORdpnativeCode;//VA本地机器码开始位置的地址指针
WORDoProjectLocation;//Offset工程位置?????????????????
WORDFlag2;//标志2
WORDFlag3;//标志3
BYTEOriginalPathName[MAX_PATH*2];//原文件地址,一个字符串,长度最长为MAX_PATH
BYTENullSpacer;//无用的东西,用来占位置????????????????????
DWORDpExternalTable;//VA引用表的指针地址
DWORDExternalCount;//引用表大小(个数)
//sizeof()=0x23c
}ProjectInfo_t;
这个东西基本包含了这个PROJECT的信息,但是主要的意义还是提供了之后的结构的索引,最重要的是它提供了传说中的OBJECTTABLE的入口地址,对于EXE里面有多少FORM,MODULE十分重要。
3)SubMain
从VBHEADER->SubMain可以获得SubMain代码的入口处,但是有些VB程序是从FORM开始运行的所以当这个值为0时就代表此程序不是从SubMain开始运行的而是从FORM开始运行的.
4)GUITable
从VBHEADER->GUITable可以获得GUI元素表的地址指针,也就是指向WINDOWS图形元素的表.
这是GUITable结构的C语言描述:
typedefstruct
{
DWORDlStructSize;//这个结构的总大小
BYTEuuidObjectGUI[15];//ObjectGUI的UUID
DWORDUnknown1;//???????????????????????????????????
DWORDUnknown2;//???????????????????????????????????
DWORDUnknown3;//???????????????????????????????????
DWORDUnknown4;//???????????????????????????????????
DWORDlObjectID;//当前工程的组件ID
DWORDUnknown5;//???????????????????????????????????
DWORDfOLEMisc;//OLEMisc标志
BYTEuuidObject[15];//组件的UUID
DWORDUnknown6;//???????????????????????????????????
DWORDUnknown7;//???????????????????????????????????
DWORDpFormPointer;//VA指向GUIObjectInfo结构的地址指针
DWORDUnknown8;//???????????????????????????????????
//sizeof()=0x50
}GUITable_t;
这个表没有什么重要,主要就是那个pFormPointer比较有用。
5)ExternalComponentTable
从VBHEADER->ExternalComponentTable可以获得外部引用表的地址指针.
这是ExternalTable以及ExternalLibrary的VB结构以及C语言描述:
typedefstruct
{
DWORDFlag;//标志
DWORDpExternalLibrary;//VA指向ExternalLibrary结构的地址指针
}ExternalTable_t;
typedefstruct
{
DWORDpLibraryName;//VA指向NTS
DWORDpLibraryFunction;//VA指向NTS
}ExternalLibrary_t;
6)ComRegDataandCOMRegInfo
从VBHEADER->ComRegData可以获得COM注册数据的地址指针.
这是COMRegData以及COMRegInfo结构的C语言描述:
typedefstruct
{
DWORDoRegInfo;//Offset指向COMInterfacesInfo结构(COM接口信息)
DWORDoNTSProjectName;//Offset指向Project/TypelibName(工程名)
DWORDoNTSHelpDirectory;//Offset指向HelpDirectory(帮助文件目录)
DWORDoNTSProjectDescription;//Offset指向ProjectDescription(工程描述)
BYTEuuidProjectClsId(15);//Project/Typelib的CLSID
DWORDlTlbLcid;//TypeLibrary的LCID
WORDiPadding1;//没有用的内存对齐空间1
WORDiTlbVerMajor;//Typelib主版本
WORDiTlbVerMinor;//Typelib次版本
WORDiPadding2;//没有用的内存对齐空间2
DWORDlPadding3;//没有用的内存对齐空间3
//sizeof()=0x30
}COMRegData_t;
typedefstruct
{
DWORDoNextObject;//OffsettoCOMInterfacesInfo
DWORDoObjectName;//OffsettoObjectName
DWORDoObjectDescription;//OffsettoObjectDescription
DWORDlInstancing;//InstancingMode
DWORDlObjectID;//CurrentObjectIDintheProject
BYTEuuidObjectClsID[15];//CLSIDofObject
DWORDfIsInterface;//SpecifiesifthenextCLSIDisvalid
DWORDoObjectClsID;//OffsettoCLSIDofObjectInterface
DWORDoControlClsID;//OffsettoCLSIDofControlInterface
DWORDfIsControl;//SpecifiesiftheCLSIDaboveisvalid
DWORDlMiscStatus;//OLEMISCFlags(seeMSDNdocs)
BYTEfClassType;//ClassType
BYTEfObjectType;//FlagidentifyingtheObjectType
WORDiToolBoxBitmap32;//ControlBitmapIDinToolBox
WORDiDefaultIcon;//MinimizedIconofControlWindow
WORDfIsDesigner;//SpecifieswhetherthisisaDesigner
DWORDoDesignerData;//OffsettoDesignerData
//sizeof()=0x44
}COMRegInfo_t;
'ObjectTypepartoftCOMRegInfo//没有翻译的意义,就没有翻译,只是看看而已
'+-------+---------------+-------------------------------------------+
'|Value|Name|Description|
'+-------+---------------+-------------------------------------------+
'|0x02|Designer|AVisualBasicDesignerforanAdd.in|
'|0x10|ClassModule|AVisualBasicClass|
'|0x20|UserControl|AVisualBasicActiveXUserControl(OCX)|
'|0x80|UserDocument|AVisualBasicUserDocument|
'+-------+---------------+-------------------------------------------+
*7)ObjectInfo
这些东西十分重要,是提取VB工程元素的重要标志,从这里我们可以得到这个工程里有多少FORM,多少MODULE,以及他们的重要索引数据,可以说重要性和SECTIONHEAD在EXE程序中重要性相同。
入口的地址是由ProjectInfo_t结构提供的(semi提供关于这里提供的资料少之又少,所以需要自己摸索)
从ProjectInfo_t开始指向的是一个ObjectTable_t,由ObjectTable_t提供第一个OBJECT_t结构的地址
typedefstruct//这个是OBJECT的总表,可以索引以后的每个OBJECT
{
DWORDlNull1AsLong;//没有用的填充东西
DWORDaExecProj;//VA指向一块内存结构(研究下来既不没见着这个东西由什么用处
DWORDaProjectInfo2;//VA指向ProjectInfo2
DWORDConst1;//没有用的填充东西
DWORDNull2;//没有用的填充东西
DWORDlpProjectObjectAsLong'0x14
DWORDFlag1;//标志1
DWORDFlag2;//标志2
DWORDFlag3;//标志3
DWORDFlag4;//标志4
WORDfCompileType;//Internalflagusedduringcompilation
WORDObjectCount1;//OBEJCT数量1????
WORDiCompiledObjects;//编译后OBJECT数量
WORdiobjectsInUseAsInteger;//UpdatedintheIDEtocorrespondthetotalnumber'butwillgoupordownwheninitializing/unloadingmodules.
DWORDaObject;//VA指向第一个OBJECT_t结构,很重要
DWORDNull3;//没有用的填充东西
DWORDNull4;//没有用的填充东西
DWORDNull5;//没有用的填充东西
DWORDaProjectName;//执行工程名字的字符串
DWORDLangID1;//languageID1
DWORDLangID2;//languageID2
DWORDNull6;//没有用的填充东西
DWORDConst3;//没有用的填充东西
'0x54
}ObjectTable_t;
typestruct//这个就是每个OBJECT的结构,
{
DWORDaObjectInfo;//VA指向一个ObjectInfo_t类型,来显示这个OBJECT的数据
DWORDConst1;//没有用的填充东西
DWORDaPublicBytes;//VA指向公用变量表大小
DWORDaStaticBytes;//VA指向静态变量表地址
DWORDaModulePublic;//VA指向公用变量表
DWORDaModuleStatic;//VA指向静态变量表
DWORDaObjectName;//VA字符串,这个OBJECT的名字
DWORDProcCount;//events,funcs,subs(时间/函数/过程)数目
DWORDaProcNamesArray;//VA一般都是0
DWORDoStaticVar;//OFFSET从aModuleStatic指向的静态变量表偏移
DWORDObjectType;//比较重要显示了这个OBJECT的实行,具体见下表
DWORDNull3;//没有用的填充东西
//sizeof()=0x30
}Object_t;
'Object_t.ObjectTyper属性...//重要的属性表部分
'#########################################################
'form:000000011000000010000011-->18083
'000000011000000010100011-->180A3
'000000011000000011000011-->180C3
'module:000000011000000000000001-->18001
'000000011000000000100001-->18021
'class:000100011000000000000011-->118003
'000100111000000000000011-->138003
'000000011000000000100011-->18023
'000000011000100000000011-->18803
'000100011000100000000011-->118803
'usercontrol:000111011010000000000011-->1DA003
'000111011010000000100011-->1DA023
'000111011010100000000011-->1DA803
'propertypage:000101011000000000000011-->158003
'||||||||
'[moog]||||||||
'HasPublicInterface---+|||||||(有公用的接口)
'HasPublicEvents--------+||||||(有公用的事件)
'IsCreatable/Visible?----+|||||(是否可以创建,可见)
'Sameas"HasPublicEvents"-----+||||
'[aLfa]|||||
'usercontrol(1)---------------+||||(用户控制)
'ocx/dll(1)----------------------+|||(OCX/DLL)
'form(1)------------------------------+||(是不是FORM是就是1)
'vb5(1)---------------------------------+|(是不是VB5是就是1)
'HasOptInfo(1)-------------------------------+(有没有额外的信息信息由就是1,决定是不是指向OptionalObjectInfo_t类似与PEHEAD里的Optional信息一样)
'|
'module(0)------------------------------------+(如果是Module模块就这里是0)
typedefstruct//这个是显示这个OBJECT信息的结构,每一个OBJECT都有一个
{
WORDFlag1;
WORDObjectIndex;
DWORDaObjectTable;
DWORDNull1;
DWORDaSmallRecord;//whenitisamodulethisvalueis-1[bettername?]
DWORDConst1;
DWORDNull2;
DWORDaObject;
DWORDRunTimeLoaded;//[cansomeoneverifythis?]
DWORDNumberOfProcs;
DWORDaProcTable;
WORDiConstantsCount;//NumberofConstants
WORDiMaxConstants;//MaximumConstantstoallocate.
DWORDFlag5;
WORDFlag6;
WORDFlag7;
WORDaConstantPool;
//sizeof()=0x38
'therestisoptionalitems[OptionalObjectInfo]
}ObjectInfo_t;
PrivateTypetOptionalObjectInfo'if((tObject.ObjectTypeAND&H80)=&H80)
fDesignerAsLong'0x00(0d)Ifthisvalueis2thenthisobjectisadesigner
aObjectCLSIDAsLong'0x04
Null1AsLong'0x08
aGuidObjectGUIAsLong'0x0C
lObjectDefaultIIDCountAsLong'0x1001000000
aObjectEventsIIDTableAsLong'0x14
lObjectEventsIIDCountAsLong'0x18
aObjectDefaultIIDTableAsLong'0x1C
ControlCountAsLong'0x20
aControlArrayAsLong'0x24
iEventCountAsInteger'0x28(40d)NumberofEvents
iPCodeCountAsInteger'0x2C
oInitializeEventAsInteger'0x2C(44d)OffsettoInitializeEventfromaMethodLinkTable
oTerminateEventAsInteger'0x2E(46d)OffsettoTerminateEventfromaMethodLinkTable
aEventLinkArrayAsLong'0x30PointertopointersofMethodLink
aBasicClassObjectAsLong'0x34Pointertoanin-memory
Null3AsLong'0x38
Flag2AsLong'0x3Cusuallynull
'0x40<--Structuresize
EndType
TypetProjectInfo2
lNull1AsLong'0x00(00d)
aObjectTableAsLong'0x04(04d)PointertoObjectTable
lConst1AsLong'0x08(08d)
lNull2AsLong'0x0C(12d)
aObjectDescriptorTableAsLong'0x10(16d)PointertoatableofObjectDescriptors
lNull3AsLong'0x14(20d)
aNTSPrjDescriptionAsLong'0x18(24d)PointertoProjectDescription
aNTSPrjHelpFileAsLong'0x1C(28d)PointertoProjectHelpFile
lConst2AsLong'0x20(32d)
lHelpContextIDAsLong'0x24(36d)ProjectHelpContextID
'0x28(40d)<-Structuresize
EndType
TypeObjectDescriptor
lNull1AsLong'0x00(00d)
aObjectInfoAsLong'0x04(04d)PointertoObjectInfo
lConst1AsLong'0x08(08d)
lNull2AsLong'0x0C(12d)
lFlag1AsLong'0x10(16d)
lNull3AsLong'0x14(20d)
aUnknown1AsLong'0x18(24d)
lNull4AsLong'0x1C(28d)
aUnknown2AsLong'0x20(32d)
aUnknown3AsLong'0x24(36d)
aUnknown4AsLong'0x28(40d)
lNull5AsLong'0x2C(44d)
lNull6AsLong'0x30(48d)
lNull7AsLong'0x34(52d)
lFlag2AsLong'0x38(56d)
fObjectTypeAsLong'0x3C(60d)FlagsforthisObject
'0x40(64d)<-StructureSize
EndType
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
未完待续!!!!!(还有很大大大大一部分类!!!)
××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
标 题: VB6反编译详解(二)
作 者: kenmark
时 间: 2006-07-20 10:57
链 接: http://bbs.pediy.com/showthread.php?threadid=29307
详细信息:
原文链接:https://www.f2er.com/vb/261222.html****************************************************************************************************上次说到最重要的部分OBJECTTABLE就断了实在是对不起大家,所以最近加紧研究马上出了第二篇(连载会很长哦,要考验大家的耐心的哦)*****************************************************************************************************7)ObjectInfo这些东西十分重要,是提取VB工程元素的重要标志,从这里我们可以得到这个工程里有多少FORM,以及他们的重要索引数据,可以说重要性和SECTIONHEAD在EXE程序中重要性相同。我们反编译只是初期水平,现在由于笔者能力有限,我们只讨论到如何完全反会MOD,VBP,FRX和FRM文件,关于其他文件例如.CLS类文件什么的不再本文讨论范围之内。入口的地址是由ProjectInfo_t结构提供的(semi提供关于这里提供的资料少之又少,所以需要自己摸索)从ProjectInfo_t开始指向的是一个ObjectTable_t,由ObjectTable_t提供第一个OBJECT_t结构的地址,索引其他的OBJECT和ImportTable里索引IID一样。typedefstruct//这个是OBJECT的总表,可以索引以后的每个OBJECT{DWORDlNull1AsLong;//没有用的填充东西DWORDaExecProj;//VA指向一块内存结构(研究下来既不没见着这个东西由什么用处DWORDaProjectInfo2;//VA指向ProjectInfo2DWORDConst1;//没有用的填充东西DWORDNull2;//没有用的填充东西DWORDlpProjectObjectAsLong'0x14DWORDFlag1;//标志1DWORDFlag2;//标志2DWORDFlag3;//标志3DWORDFlag4;//标志4WORDfCompileType;//InternalflagusedduringcompilationWORDObjectCount1;//OBEJCT数量1????WORDiCompiledObjects;//编译后OBJECT数量WORdiobjectsInUseAsInteger;//UpdatedintheIDEtocorrespondthetotalnumber'butwillgoupordownwheninitializing/unloadingmodules.DWORDaObject;//VA指向第一个OBJECT_t结构,很重要DWORDNull3;//没有用的填充东西DWORDNull4;//没有用的填充东西DWORDNull5;//没有用的填充东西DWORDaProjectName;//执行工程名字的字符串DWORDLangID1;//languageID1DWORDLangID2;//languageID2DWORDNull6;//没有用的填充东西DWORDConst3;//没有用的填充东西'0x54}ObjectTable_t;typestruct//这个就是每个OBJECT的结构,{DWORDaObjectInfo;//VA指向一个ObjectInfo_t类型,来显示这个OBJECT的数据DWORDConst1;//没有用的填充东西DWORDaPublicBytes;//VA指向公用变量表大小DWORDaStaticBytes;//VA指向静态变量表地址DWORDaModulePublic;//VA指向公用变量表DWORDaModuleStatic;//VA指向静态变量表DWORDaObjectName;//VA字符串,这个OBJECT的名字DWORDProcCount;//events,subs(事件/函数/过程)数目DWORDaProcNamesArray;//VA一般都是0DWORDoStaticVar;//OFFSET从aModuleStatic指向的静态变量表偏移DWORDObjectType;//比较重要显示了这个OBJECT的实行,具体见下表DWORDNull3;//没有用的填充东西//sizeof()=0x30}Object_t;'Object_t.ObjectTyper属性...//重要的属性表部分'#########################################################'form:000000011000000010000011-->18083'000000011000000010100011-->180A3'000000011000000011000011-->180C3'module:000000011000000000000001-->18001'000000011000000000100001-->18021'class:000100011000000000000011-->118003'000100111000000000000011-->138003'000000011000000000100011-->18023'000000011000100000000011-->18803'000100011000100000000011-->118803'usercontrol:000111011010000000000011-->1DA003'000111011010000000100011-->1DA023'000111011010100000000011-->1DA803'propertypage:000101011000000000000011-->158003'||||||||'[moog]||||||||'HasPublicInterface---+|||||||(有公用的接口)'HasPublicEvents--------+||||||(有公用的事件)'IsCreatable/Visible?----+|||||(是否可以创建,可见)'Sameas"HasPublicEvents"-----+||||'[aLfa]|||||'usercontrol(1)---------------+||||(用户控制)'ocx/dll(1)----------------------+|||(OCX/DLL)'form(1)------------------------------+||(是不是FORM是就是1)'vb5(1)---------------------------------+|(是不是VB5是就是1)'HasOptInfo(1)-------------------------------+(有没有额外的信息信息由就是1,决定是不是指向OptionalObjectInfo_t类似与PEHEAD里的Optional信息一样)'|'module(0)------------------------------------+(如果是Module模块就这里是0)typedefstruct//这个是显示这个OBJECT信息的结构,每一个OBJECT都有一个{WORDFlag1;WORDObjectIndex;//OBJECT的索引????????????????????????DWORDaObjectTable;//指向OBJECTTABLE??????????????????DWORDNull1;//没有用的填充东西DWORDaSmallRecord;//如果这个对象是一个模块(module)那么这个数值是-1DWORDConst1;//没有用的填充东西DWORDNull2;//没有用的填充东西DWORDaObject;//指向OBJECT??????????????????????????????DWORDRunTimeLoaded;//[cansomeoneverifythis?]DWORDNumberOfProcs;//proc个数DWORDaProcTable;//指向proc表WORDiConstantsCount;//常量个数WORDiMaxConstants;//最大的要求分配的常量DWORDFlag5;WORDFlag6;WORDFlag7;WORDaConstantPool;//指向常量池//sizeof()=0x38'therestisoptionalitems[OptionalObjectInfo]}ObjectInfo_t;typedefstruct//这个是可选的OBJECT_INFO和PEHEADER里的OPTIONAL_HEADER类似,是否有要看每个Object_t里面的ObjectTyper表里的倒数第二个位(详细看上表){DWORDfDesigner;//如果这个数值是2则表示是一个designerDWORDaObjectCLSID;//指向CLSID对象DWORDNull1;//没有用的填充东西DWORDaGuidObjectGUI;//?????????????????????????????DWORDlObjectDefaultIIDCount;//01000000???????????????????????????DWORDaObjectEventsIIDTable;//指向对象行为IID表DWORDlObjectEventsIIDCount;//对象行为IID个数DWORDaObjectDefaultIIDTable;//指向默认对象IID表DWORDControlCount;//控件个数DWORDaControlArray;//指向控件表WORDiEventCount;//行为的个数,比较重要,知道有几个行为WORDiPCodeCount;//PCode个数WORDoInitializeEvent;//offset从aMethodLinkTable指向初始化行为WORDoTerminateEvent;//offset从aMethodLinkTable指向终止行为DWORDaEventLinkArray;//PointertopointersofMethodLinkDWORDaBasicClassObject;//Pointertoanin-memoryDWORDNull3;//没有用的填充东西DWORDFlag2;//一般都是空的//sizeof()=0x40}OptionalObjectInfo_t;typedefstruct{WORDFlag1;//Integer'0x00WORDEventCount;//Integer'0x02DWORDFlag2;//Long'0x04DWORDaGUID;//Long'0x08WORDindex;//Integer'0x0CWORDConst1;//Integer'0x0EDWORDNull1;//Long'0x10DWORDNull2;//Long'0x14DWORDaEventTable;//Long'0x18BYTEFlag3;//Byte'0x1CBYTEConst2;//Byte'0x1DWORDConst3;//Integer'0x1EDWORDaName;//Long'0x20WORDIndex2;//Integer'0x24WORDConst1Copy;//Integer'0x26//0x28<--StructureSize}Control_t;看到这里我们已经可以通过这个表粗略地勾画出那个工程是怎样的,通过PROJECT_INFO和其他的一些东西,我们已经能够重建VBP文件,而且我们已经知道这个工程到底有多少源文件(FRM,MOD不包括FRX)接下来,我们来读取EXE文件中保存的数据来重建每个MOD,FRM,CLS,VBP重建VBP我们本文不是主要介绍VB工程组文件的内部结构,所以只是一笔带过,VBP文件内部格式和普通的配置文件类似都是(关键词,对应值)对的形式主要有这么几个字段我们要注意(输出的东西和SEMI一样,关于VERSION会比SEMI详细,将在之后介绍)Type=Exe(这个就是这个工程是什么我们当然是EXE)Startup="frmMain"(这个就是工程的启动项目,关于它我们后面要详细介绍)Description="VisualBasicDecompiler"(这个是描述,(char*)COMRegData+COMRegData->oNTSProjectDescription)HelpFile=""(帮助文件(char*)VBHEADER+VBHEADER->oHelpFile就是其在文件中的偏移)Name="VBDecompiler"(工程名字,获得方式和HELPFILE类似,只要看VBHEADER里面就有存储其偏移地址)Title="VBDecompiler"(工程标题,获得方式和HELPFILE类似,只要看VBHEADER里面就有存储其偏移地址)ExeName32="SemiVBDecompiler"(工程EXE32的名字,获得方式和HELPFILE类似,只要看VBHEADER里面就有存储其偏移地址)注意:每找到一个OBJECT对应的文件,我们要将其添加到VBP工程里面,具体的方式是:如果这个OBJECT是FRM文件,只要在VBP文件里面添加一条Form=窗体文件的名字(文件名)(没有空格要求=以后紧跟)如果这个OBJECT是MOD文件,只要在VBP文件里面添加一条Module=内部标识名;文件名(之间需要一个分号来格开)如果这个OBJECT是CLASS文件,只要在VBP文件里面添加一条Class=内部标识名;文件名(之间需要一个分号来格开)这样的话,在VBP文件中维护各个工程文件的工作就大致完成了。其他重要的项还有例如OBJECT项(添加使用的控件)以及版本信息相关的(在后面的资源反编译一节会着重介绍)剩下的就是关于编译时的优化选项了,这些不是很重要,所以就没有加入。其实VBP文件里包含了很多工程的细节,但是我们主要只要抓住重要的字段。重建MODMOD文件比较简单,由于文件名可能和模块名不同,编译的时候舍弃了实际文件名,而用模块名来作为标识,所以我们生成的MOD文件的名字选用模块名,可能与原始的源文件组不同。获得一个OBJECT之后,我们看Object_t.ObjectType通过查表我们能够确定它的性质我们确定这个是MOD文件之后,我们通过Object_t.aObjectName(指向一个字符串)这个就是这个模块的名字,也是这个模块在编译后的文件中的标识。我们用这个标识名来作为文件名,创建一个文件,然后我们通过Object_t.ProcCount知道这个MOD里存储着多少个FUNCTION和SUB,并且由于MOD是全编译,我们得不到具体的SUB和FUNCTION的名字,这些名字在编译的时候被丢弃的所以我们只能知道到底有多少个SUB和FUNCTION。所以MOD的重建并不能得到什么东西,只能空创建一个文件然后最多写入:'Thereistotally100methodsinthismodule.Butwecan'tshowyouthem.:)还要记得一点就是我们要为这个BAS文件写上它的VB_ATTRIB,具体的格式就是AttributeVB_Name="模块的名字(内部标识名)"这部分的具体代码重建(一般来说不能完全重建),我们只能有待更加强大的代码反编译来完成。重建FRMFRM和MOD不同的是我们可以得到FRM里面的所有控件的基本静态状态(属性),并且我们可以得到里面存储的SUB和FUNCTION的名字(不同于MOD)同样如何知道这个OBJECT是一个FRM文件还是要查表知道它是一个FRM文件之后我们首先要了解一下FRM文件的结构,FRM文件是类似配置文件的格式存储的,主要有外部的VB_ATTRIB定义以及FORM成员的定义VERSION5.00//文件的编译版本BeginVB.Form内部标识名(表示这个FORM的开始)BeginVB.VB内部控件对象内部标识名EndEndAttributeVB_Name="FORM的内部标识名"多重的嵌套就完成了这个FORM里面控件的从属关系(例如一个PICTUREBox从属与FORM,而一个TEXT从属于一个FRAME)之后我们要得到这个FRM文件里面的控件信息和SUB以及FUNCTION的信息,来完成我们的FRM写入每个OBJECT里有一个TYPE表示这个OBJECT的属性,我们已经知道凡是是一个FRM都是有OptionalObjectInfo这个结构告诉我们很多元素的索引,获取的方式十分简单,只要先确定有OptionalObjectInfo然后它的地址就是这个Object的(char*)ObjectInfo+sizeof(ObjectInfo)其实就是紧紧跟在ObjectInfo之后的。结构的描述已经在上面列出了,这个结构中的信息十分复杂,我们要注意这么几个项目ControlCount列出控件个数aControlArray指向控件表控件表是一个紧紧挨着的一个指针数组,我们可以逐个读取获得信息伪代码:for(i=0;i<=this->ObjectTable->ObjectCount1-1;++i,++t){//循环获得所有的OBJECT的指针s=this->Get_OptionalObjectInfo(t);//获取一个OBJECT的OptionalObjectInfoif(s==NULL)//itisamoduleandhaven'tanyoptionalobjectinfocontinue;cout<<"Onefrmfound!"<<endl;//打印出:获得一个FRMct=this->Get_Control_t(s);//获取第一个Control的指针for(d=0;d<=s->ControlCount-1;d++)//循环获得每一个CONTROL的索引cout<<"onecontrol:"<<this->Get_Control_Name(ct++)<<endl;//获得了一个Control打印出它的名字}inlineBYTE*VBEXE::Get_Control_Name(VBST::Control_t*x){return(this->buffer+this->sf.VA2Offset(x->aName));}这里其实是我的DECOMPILER的一些片段,仅仅是用作调试的还未成型所以就不敢贴出来丢脸了:)。每个Control_t的aName是一个VA指向一个字符串就是表示这个控件的名字,至于控件的属性我们以后再说:)我们看看我的VBDECOMPILER输出的读取那个SEMI的DECOMPILER的东西Loadfilesucessfully!GetcontolsinobjectOnefrmfound!onecontrol:mnuFileonecontrol:mnuHelpAboutonecontrol:txtFinalonecontrol:mnuFileOpenonecontrol:mnuToolsonecontrol:Label1onecontrol:Formonecontrol:Label2onecontrol:mnuFileRecent1onecontrol:mnuFileSep1onecontrol:mnuFileRecent2onecontrol:mnuFileRecent3onecontrol:mnuFileRecent4onecontrol:mnuFileSep2onecontrol:lblObjectNameonecontrol:mnuOptionsonecontrol:mnuFileDebugProcessonecontrol:mnuToolsPCodeProcedureonecontrol:txtCodeonecontrol:tvProjectonecontrol:StatusBar1onecontrol:sstViewFileonecontrol:fxgEXEInfoonecontrol:picPreviewonecontrol:mnuFileAntiDecompileronecontrol:mnuFileExportMemoryMaponecontrol:mnuFileGenerateonecontrol:lstMembersonecontrol:lstTypeInfosonecontrol:mnuFileSaveExeonecontrol:txtBufferonecontrol:txtFunctionsonecontrol:txtEditArrayonecontrol:lblArrayEditonecontrol:buffCodeAvonecontrol:buffCodeAponecontrol:mnuFileExitonecontrol:txtResultonecontrol:cmdCancelonecontrol:txtStatusonecontrol:mnuHelponecontrol:FrameStatusOnefrmfound!onecontrol:imgFlameonecontrol:lblTitleonecontrol:TmrLightonecontrol:tmrIcononecontrol:FormOnefrmfound!onecontrol:chkShowOffsetsonecontrol:chkSkipCOMonecontrol:chkDumpControlsonecontrol:cmdCloSEOnecontrol:chkPCODEonecontrol:chkShowColorsonecontrol:FormOnefrmfound!onecontrol:ClassOnefrmfound!onecontrol:ClassOnefrmfound!onecontrol:lblTitleonecontrol:cmdCloSEOnecontrol:Formonecontrol:Label1onecontrol:lstProceduresonecontrol:txtView到这里,我们以及能够基本将原来的工程组还原出来了,但是仍然有缺点:1)我们得不到(应该说实我水平不够)VBP文件里的Reference2)我们即将讨论关于VERSION信息的东西(要从一个EXE的RESOURCE中看出端倪)3)我们的CODE仍然还是一大问题4)我们的STARTUP开始段,(马上会研究出来的)5)我们还不知道哪个FRM有FRX文件哪个没有而且不能还原,而且FRM里面的控件还是不能获得属性6)PCODE一点都没有讨论7)还有一些细节的信息没能弄出来