****************************************************************************************************
上次说到最重要的部分OBJECT TABLE就断了实在是对不起大家,所以最近加紧研究马上出了第二篇(连载会很长哦,要考验大家的耐心的哦)
****************************************************************************************************
*7)ObjectInfo
这些东西十分重要,是提取VB工程元素的重要标志,从这里我们可以得到这个工程里有多少FORM,多少MODULE,以及他们的重要索引数据,可以说重要性和SECTION HEAD在EXE程序中重要性相同。
我们反编译只是初期水平,现在由于笔者能力有限,我们只讨论到如何完全反会MOD,VBP,FRX和FRM文件,关于其他文件例如.CLS类文件什么的不再本文讨论范围之内。
入口的地址是由ProjectInfo_t结构提供的(semi提供关于这里提供的资料少之又少,所以需要自己摸索)
从ProjectInfo_t开始指向的是一个ObjectTable_t,由ObjectTable_t提供第一个OBJECT_t结构的地址,索引其他的OBJECT和Import Table里索引IID一样。
typedef struct //这个是OBJECT 的总表,可以索引以后的每个OBJECT
{
DWORD lNull1 As Long; //没有用的填充东西
DWORD aExecProj; //VA指向一块内存结构(研究下来既不没见着这个东西由什么用处
DWORD aProjectInfo2; //VA指向Project Info 2
DWORD Const1; //没有用的填充东西
DWORD Null2; //没有用的填充东西
DWORD lpProjectObject As Long ' 0x14
DWORD Flag1; //标志1
DWORD Flag2; //标志2
DWORD Flag3; //标志3
DWORD Flag4; //标志4
WORD fCompileType; //Internal flag used during compilation
WORD ObjectCount1; //OBEJCT数量1????
WORD iCompiledObjects; //编译后OBJECT数量
WORD iObjectsInUse As Integer; //Updated in the IDE to correspond the total number ' but will go up or down when initializing/unloading modules.
DWORD aObject; //VA指向第一个OBJECT_t结构,很重要
DWORD Null3; //没有用的填充东西
DWORD Null4; //没有用的填充东西
DWORD Null5; //没有用的填充东西
DWORD aProjectName; //执行工程名字的字符串
DWORD LangID1; //language ID1
DWORD LangID2; //language ID2
DWORD Null6; //没有用的填充东西
DWORD Const3; //没有用的填充东西
' 0x54
}ObjectTable_t;
type struct//这个就是每个OBJECT的结构,
{
DWORD aObjectInfo; //VA 指向一个ObjectInfo_t类型,来显示这个OBJECT的数据
DWORD Const1; //没有用的填充东西
DWORD aPublicBytes; //VA 指向公用变量表大小
DWORD aStaticBytes; //VA 指向静态变量表地址
DWORD aModulePublic; //VA 指向公用变量表
DWORD aModuleStatic; //VA 指向静态变量表
DWORD aObjectName; //VA 字符串,这个OBJECT的名字
DWORD ProcCount; // events, funcs, subs(事件\函数\过程)数目
DWORD aProcNamesArray; //VA 一般都是0
DWORD oStaticVar; //OFFSET 从aModuleStatic指向的静态变量表偏移
DWORD ObjectType; //比较重要显示了这个OBJECT的实行,具体见下表
DWORD Null3; //没有用的填充东西
//sizeof() = 0x30
}Object_t;
'Object_t.ObjectTyper 属性...//重要的属性表部分
'#########################################################
'form: 0000 0001 1000 0000 1000 0011 --> 18083
' 0000 0001 1000 0000 1010 0011 --> 180A3
' 0000 0001 1000 0000 1100 0011 --> 180C3
'module: 0000 0001 1000 0000 0000 0001 --> 18001
' 0000 0001 1000 0000 0010 0001 --> 18021
'class: 0001 0001 1000 0000 0000 0011 --> 118003
' 0001 0011 1000 0000 0000 0011 --> 138003
' 0000 0001 1000 0000 0010 0011 --> 18023
' 0000 0001 1000 1000 0000 0011 --> 18803
' 0001 0001 1000 1000 0000 0011 --> 118803
'usercontrol: 0001 1101 1010 0000 0000 0011 --> 1DA003
' 0001 1101 1010 0000 0010 0011 --> 1DA023
' 0001 1101 1010 1000 0000 0011 --> 1DA803
'propertypage: 0001 0101 1000 0000 0000 0011 --> 158003
' | || | | | | |
'[moog] | || | | | | |
'HasPublicInterface ---+ || | | | | | (有公用的接口)
'HasPublicEvents --------+| | | | | | (有公用的事件)
'IsCreatable/Visible? ----+ | | | | | (是否可以创建,可见)
'Same as "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)
typedef struct//这个是显示这个OBJECT信息的结构,每一个OBJECT都有一个
{
WORD Flag1;
WORD ObjectIndex; //OBJECT的索引????????????????????????
DWORD aObjectTable; //指向OBJECT TABLE??????????????????
DWORD Null1; //没有用的填充东西
DWORD aSmallRecord; // 如果这个对象是一个模块(module)那么这个数值是-1
DWORD Const1; //没有用的填充东西
DWORD Null2; //没有用的填充东西
DWORD aObject; //指向OBJECT??????????????????????????????
DWORD RunTimeLoaded; //[can someone verify this?]
DWORD NumberOfProcs; //proc个数
DWORD aProcTable; //指向proc表
WORD iConstantsCount; // 常量个数
WORD iMaxConstants; // 最大的要求分配的常量
DWORD Flag5;
WORD Flag6;
WORD Flag7;
WORD aConstantPool; //指向常量池
// sizeof() = 0x38
'the rest is optional items[OptionalObjectInfo]
}ObjectInfo_t;
typedef struct // 这个是可选的OBJECT_INFO和PEHEADER里的OPTIONAL_HEADER类似,是否有要看每个Object_t里面的ObjectTyper表里的倒数第二个位(详细看上表)
{
DWORD fDesigner; // 如果这个数值是2则表示是一个designer
DWORD aObjectCLSID; //指向CLSID对象
DWORD Null1; //没有用的填充东西
DWORD aGuidObjectGUI; //?????????????????????????????
DWORD lObjectDefaultIIDCount; // 01 00 00 00 ???????????????????????????
DWORD aObjectEventsIIDTable; //指向对象行为IID表
DWORD lObjectEventsIIDCount; //对象行为IID个数
DWORD aObjectDefaultIIDTable; //指向默认对象IID表
DWORD ControlCount; //控件个数
DWORD aControlArray; //指向控件表
WORD iEventCount; // 行为的个数,比较重要,知道有几个行为
WORD iPCodeCount; // PCode个数
WORD oInitializeEvent; // offset从aMethodLinkTable指向初始化行为
WORD oTerminateEvent; // offset从aMethodLinkTable指向终止行为
DWORD aEventLinkArray; //Pointer to pointers of MethodLink
DWORD aBasicClassObject; // Pointer to an in-memory
DWORD Null3; //没有用的填充东西
DWORD Flag2; //一般都是空的
//sizeof() = 0x40
}OptionalObjectInfo_t;
typedef struct
{
WORD Flag1;//Integer ' 0x00
WORD EventCount;//Integer ' 0x02
DWORD Flag2;//Long ' 0x04
DWORD aGUID;//Long ' 0x08
WORD index;//Integer ' 0x0C
WORD Const1;//Integer ' 0x0E
DWORD Null1;//Long ' 0x10
DWORD Null2;//Long ' 0x14
DWORD aEventTable;//Long ' 0x18
BYTE Flag3;//Byte ' 0x1C
BYTE Const2;//Byte ' 0x1D
WORD Const3;//Integer ' 0x1E
DWORD aName;//Long ' 0x20
WORD Index2;//Integer ' 0x24
WORD Const1Copy;//Integer ' 0x26
// 0x28 <-- Structure Size
}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="Visual Basic Decompiler"(这个是描述,(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文件里包含了很多工程的细节,但是我们主要只要抓住重要的字段。
重建MOD
MOD文件比较简单,由于文件名可能和模块名不同,编译的时候舍弃了实际文件名,而用模块名来作为标识,所以我们生成的MOD文件的名字选用模块名,可能与原始的源文件组不同。
获得一个OBJECT之后,我们看Object_t.ObjectType通过查表我们能够确定它的性质
我们确定这个是MOD文件之后,我们通过Object_t.aObjectName(指向一个字符串)这个就是这个模块的名字,也是这个模块在编译后的文件中的标识。
我们用这个标识名来作为文件名,创建一个文件,然后我们通过Object_t.ProcCount知道这个MOD里存储着多少个FUNCTION和SUB,并且由于MOD是全编译,我们得不到具体的SUB和FUNCTION的名字,这些名字在编译的时候被丢弃的所以我们只能知道到底有多少个SUB和FUNCTION。
所以MOD的重建并不能得到什么东西,只能空创建一个文件然后最多写入:'There is totally 100 methods in this module.But we can't show you them.:)
还要记得一点就是我们要为这个BAS文件写上它的VB_ATTRIB,具体的格式就是Attribute VB_Name = "模块的名字(内部标识名)"
这部分的具体代码重建(一般来说不能完全重建),我们只能有待更加强大的代码反编译来完成。
重建FRM
FRM和MOD不同的是我们可以得到FRM里面的所有控件的基本静态状态(属性),并且我们可以得到里面存储的SUB 和FUNCTION的名字(不同于MOD)
同样如何知道这个OBJECT是一个FRM文件还是要查表
知道它是一个FRM文件之后我们首先要了解一下FRM文件的结构,
FRM文件是类似配置文件的格式存储的,主要有外部的VB_ATTRIB定义以及FORM成员的定义
VERSION 5.00//文件的编译版本
Begin VB.Form 内部标识名(表示这个FORM的开始)
Begin VB.VB内部控件对象 内部标识名
End
End
Attribute VB_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的OptionalObjectInfo
if (s==NULL) // it is a module and haven't any optional object info
continue;
cout<<"One frm found!"<<endl;//打印出:获得一个FRM
ct = this->Get_Control_t(s);//获取第一个Control的指针
for (d = 0 ; d<=s->ControlCount-1 ; d++)//循环获得每一个CONTROL的索引
cout<<"one control:"<<this->Get_Control_Name(ct++)<<endl;//获得了一个Control打印出它的名字
}
inline BYTE * VBEXE::Get_Control_Name(VBST::Control_t *x)
{
return (this->buffer + this->sf.VA2Offset(x->aName));
}
这里其实是我的DECOMPILER的一些片段,仅仅是用作调试的还未成型所以就不敢贴出来丢脸了:)。
每个Control_t的aName是一个VA指向一个字符串就是表示这个控件的名字,至于控件的属性我们以后再说:)
我们看看我的VBDECOMPILER输出的读取那个SEMI的DECOMPILER的东西
Load file sucessfully!
Get contols in object
One frm found!
one control:mnuFile
one control:mnuHelpAbout
one control:txtFinal
one control:mnuFileOpen
one control:mnuTools
one control:Label1
one control:Form
one control:Label2
one control:mnuFileRecent1
one control:mnuFileSep1
one control:mnuFileRecent2
one control:mnuFileRecent3
one control:mnuFileRecent4
one control:mnuFileSep2
one control:lblObjectName
one control:mnuOptions
one control:mnuFileDebugProcess
one control:mnuToolsPCodeProcedure
one control:txtCode
one control:tvProject
one control:StatusBar1
one control:sstViewFile
one control:fxgEXEInfo
one control:picPreview
one control:mnuFileAntiDecompiler
one control:mnuFileExportMemoryMap
one control:mnuFileGenerate
one control:lstMembers
one control:lstTypeInfos
one control:mnuFileSaveExe
one control:txtBuffer
one control:txtFunctions
one control:txtEditArray
one control:lblArrayEdit
one control:buffCodeAv
one control:buffCodeAp
one control:mnuFileExit
one control:txtResult
one control:cmdCancel
one control:txtStatus
one control:mnuHelp
one control:FrameStatus
One frm found!
one control:imgFlame
one control:lblTitle
one control:TmrLight
one control:tmrIcon
one control:Form
One frm found!
one control:chkShowOffsets
one control:chkSkipCOM
one control:chkDumpControls
one control:cmdClose
one control:chkPCODE
one control:chkShowColors
one control:Form
One frm found!
one control:Class
One frm found!
one control:Class
One frm found!
one control:lblTitle
one control:cmdClose
one control:Form
one control:Label1
one control:lstProcedures
one control:txtView
到这里,我们以及能够基本将原来的工程组还原出来了,但是仍然有缺点:
1)我们得不到(应该说实我水平不够)VBP文件里的Reference
2)我们即将讨论关于VERSION信息的东西(要从一个EXE的RESOURCE中看出端倪)
3)我们的CODE仍然还是一大问题
4)我们的STARTUP开始段,(马上会研究出来的)
5)我们还不知道哪个FRM有FRX文件哪个没有而且不能还原,而且FRM里面的控件还是不能获得属性
6)PCODE一点都没有讨论
7)还有一些细节的信息没能弄出来
下一篇会说到VERSION INFO和控件属性的东西
[招聘] 欢迎你加入看雪团队!