[原创]VB6反编译详解(一)
2006-7-9 16:59
23171
[原创]VB6反编译详解(一)
2006-7-9 16:59
23171
VB6反编译详解 by Kenmark-Fenix
**************************************************
最新于2006-7-13更新!
**************************************************
写本文已经惦记了好几年了,由于一直没有完整的资料和充裕的时间,所以一直没有动手。
在这里一方面是写给大家看看,另一方面是招募更多有志于反编译VB6的同志们一起来研究学习!
我的E-MAIL:ken.mingyuan@hotmail.com ken.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函数居然一个都没有出现在编译后程序的IMPORT TABLE里,一般VC和DEPHI都是直接出现在编译后程序的IMPORT TABLE里的,而我们的VB程序用到了如此之多的API函数居然只使用了一个DLL――MSVBVM60.DLL。
然后用工具打开MSVBVM60.DLL,一看,输出的函数还真不少,其中有用__vba和rtc开头的也有直接就是函数名的,仔细一看,哇赛,可以说完全是一个windows API的代理,应有尽有:
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 :Randomize Timer中获取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 :比较两个字符串:If String1 = String2 Then ......
__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变回来,对于代码呢,可以获得SUB MAIN的汇编代码地址,但是不能返回到VB代码,里面还内置了一个假的反汇编器,以后我会说的!
我提供了它的下载,大家去看看,不知道它的资料是哪里来的十分全面,就是用VB写的比较繁!还有由于界面控制太多,代码有点乱!
2.程序初始化
我们用W32DASM打开任何VB程序,跳到ENTRY POINT之后看到的总是一个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文件的ENTRY POINT进入后第一个指令是压入一个指针来表示VBHEADER的位置,这个指令为
push xxxxxxxx地址是经过基址IMAGE_BASE偏移后的地址,所以减去IMAGE_BASE后获得的就是VBHEADER开始的VA然后用PEFile类中的函数加以分析可以得到的是文件中VBHEADER的偏移量。
这个指令在机器码中是这样表示的:68 C0 11 40 00
68是PUSH的机器码,而后是内存存储方式的地址化成汇编语言就是:
push 004011c0 所以减去基址后就是11C0然后进行VA2OFFSET得到的是11c0的偏移,转向那段数据就能得到VBHEADER。
这是VBHEADER结构的C语言描述:
typedef struct
{
char Signature[4]; // 四个字节的签名符号,和PEHEADER里的那个signature是类似性质的东西,VB文件都是"VB5!"
WORD RtBuild; // 运行时创立的变量(类似编译的时间)
BYTE LangDLL[14]; // 语言DLL文件的名字(如果是0x2A的话就代表是空或者是默认的)
BYTE BakLangDLL[14]; // 备份DLL语言文件的名字(如果是0x7F的话就代表是空或者是默认的,改变这个值堆EXE文件的运行没有作用)
WORD RtDLLVer; // 运行是DLL文件的版本
DWORD LangID; // 语言的ID
DWORD BakLangID; // 备份语言的ID(只有当语言ID存在时它才存在)
DWORD pSubMain; // RVA(实际研究下来是VA) sub main过程的地址指针(3.)(如果时00000000则代表这个EXE时从FORM窗体文件开始运行的)
DWORD pProjInfo; // VA 工程信息的地址指针,指向一个ProjectInfo_t结构(2.)
DWORD fMDLIntObjs; // ?详细见"MDL 内部组建的标志表"
DWORD fMDLIntObjs2; // ?详细见"MDL 内部组建的标志表"
DWORD ThreadFlags; // 线程的标志
//* 标记的定义(ThreadFlags数值的含义)
//+-------+----------------+--------------------------------------------------------+
//| 值 | 名字 | 描述 |
//+-------+----------------+--------------------------------------------------------+
//| 0x01 | ApartmentModel | 特别化的多线程使用一个分开的模型 |
//| 0x02 | RequireLicense | 特别化需要进行认证(只对OCX) |
//| 0x04 | Unattended | 特别化的没有GUI图形界面的元素需要初始化 |
//| 0x08 | SingleThreaded | 特别化的静态区时单线程的