首页
社区
课程
招聘
[原创]VB6反编译详解(二)
发表于: 2006-7-20 10:57 19118

[原创]VB6反编译详解(二)

2006-7-20 10:57
19118

****************************************************************************************************
上次说到最重要的部分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和控件属性的东西


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 8
支持
分享
最新回复 (15)
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
2
支持一下!
一般比较关心的是pcode和vb库的导出函数。呵呵
2006-7-20 11:10
0
雪    币: 112
活跃值: (48)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
PCODE的话,和NATIVE可以说不一样,之后由于要完整整个VBDECOMPILER所以会涉及的,(PCODE比较容易反吧)
关于VB的MSVBVM60。DLL输出函数的解析,会很累的
一般我都会看看他人的东西(盗取一些)自己弄的比较少,不太多,尽量最后总结了给大家看
谢谢NBW的支持,一写好就来支持,谢谢了
2006-7-20 11:13
0
雪    币: 217
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习学习,谢谢。
2006-7-20 12:57
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
很详细,继续
2006-7-20 13:08
0
雪    币: 112
活跃值: (48)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
6
有人问了,大概是这些输出是那里来的,我就说说,这是我的DECOMPILER(因为在写文的时候不得不实现一个,虽然说很简单,但是要涉及到PEFILE、VBPROJ文件还有VBEXE的东西,所以弄起来还是挺费时间的)
先不给大家的原因是还没有完成而且是因为写的很遭,还不完全,没有标准化,没有注释,什么都没有所以不拿来出丑了,以后等所有连载完了,就会出一份完全版(更加详细,更加全面)同时还会给出相关的图片,程序代码以及实例
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
2006-7-20 16:25
0
雪    币: 338
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
感觉这课.蛮深奥的!!
2006-7-20 16:31
0
雪    币: 331
活跃值: (56)
能力值: ( LV13,RANK:410 )
在线值:
发帖
回帖
粉丝
8
支持楼主~~~!!!
2006-7-20 22:38
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
9
楼主真强,你是如何分析得这么细致的呢?也用OD等工具一点一点
的跟出来的吗?文件格式、各个字节的含义不是很好弄明白的哦

PCODE VB反编译器已经有了,头痛的是动态跟踪时
其函数、参数的直观性问题
2006-7-21 00:11
0
雪    币: 179
活跃值: (131)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
10
期待楼主最终工具的发布!
2006-7-21 09:16
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
顶,支持,第一次发帖
2006-7-21 15:38
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
比较多撒....
2006-7-22 17:24
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
顶,收下了。
2006-7-22 20:13
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
PCODE VB反编译器已经有了,头痛的是动态跟踪时
其函数、参数的直观性问题
2006-8-8 08:19
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
虽然看不懂,但是学习了!
2006-8-8 08:21
0
雪    币: 434
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
谢谢,对于vb 软件,我简直没有一点办法,可能这对我有启发,如果能有点例子就好了!
2006-8-8 16:41
0
游客
登录 | 注册 方可回帖
返回
//