- UID
- 6880
注册时间2006-1-12
阅读权限30
最后登录1970-1-1
龙战于野
TA的每日心情 | 开心 2018-2-26 08:32 |
---|
签到天数: 19 天 [LV.4]偶尔看看III
|
浅谈VB6逆向工程(1) by MengLong@RCT
作者:未知
前言
VB的编译有两种方式,一种是P-Code方式,一种是本机代码。P_Code方式是VB
从早期版本保留下来的,也是比较难逆向的一种。而本机代码方式编译的程序已经
和VC很接近了。这里只探讨以本机代码方式编译的程序。由于微软在VB的实现方面
的资料很少,使人觉得VB的逆向比较难。其实掌握一些规律,VB的程序还是很容易
入门的。
这里所提到的知识都是个人的经验,没有什么官方的资料作为依据。所以错误
之处难免,如果你发现什么错误,欢迎指正。
1. 从简单变量的实现开始
一个VB简单变量的表示至少需要12个字节。通常前2个字节是表示类型信息的。
从第5个字节到第8个字节并不总是使用到,实际上很少被使用。我们不妨先叫它
辅助类型信息。从第9个字节开始就是真正的变量的值了。这里有可能存储一个指针
值,也可能是数据,具体是什么取决于变量类型。
另一个值得注意的事实是VB的内存是以4个字节对齐的。即使你使用一个字节,
那至少也要4个字节来表示。而且编译器只初始化它需要的那些字节,剩余的字节
可能是随机数据。下面我们将会看到这些。
想弄明白编译器在内部怎么实现的,最好的方法就是编一段程序跟踪运行看看。
我编写的代码如下:
Dim a, i As Byte
Dim b, j As Integer
Dim c, k As Long
Dim d, l As Boolean
Dim e, m As String
Dim f, n As Date
Dim g, o As Double
Dim h, p As Single
a = &H30
b = 330
c = 66000
d = True
e = "hello"
f = Now
g = 3.1415
h = 1.27
i = a
j = b
k = c
l = d
m = e
n = f
o = g
p = h
这段代码在VB的默认设置(速度优化)下编译。然后用od反汇编出来如下:
去掉了部分无关内容,其余的我在这段代码的实现里做了注释:
00401B02 MOV ESI,DWORD PTR DS:[<&MSVBVM60.__vbaVa>; MSVBVM60.__vbaVarMove
00401B08 XOR EDI,EDI
00401B0A MOV EBX,2
00401B0F MOV DWORD PTR SS:][EBP-DC],EDI
00401B15 LEA EDX,DWORD PTR SS:[EBP-DC]
00401B1B LEA ECX,DWORD PTR SS:[EBP-4C]
00401B1E MOV DWORD PTR SS:[EBP-28],EDI
00401B21 MOV DWORD PTR SS:[EBP-4C],EDI
00401B24 MOV DWORD PTR SS:[EBP-5C],EDI
00401B27 MOV DWORD PTR SS:[EBP-6C],EDI
00401B2A MOV DWORD PTR SS:[EBP-7C],EDI
00401B2D MOV DWORD PTR SS:[EBP-8C],EDI
00401B33 MOV DWORD PTR SS:[EBP-9C],EDI
00401B39 MOV DWORD PTR SS:[EBP-AC],EDI
00401B3F MOV DWORD PTR SS:[EBP-BC],EDI
00401B45 MOV DWORD PTR SS:[EBP-CC],EDI
00401B4B MOV DWORD PTR SS:[EBP-D4],30 //30h = &H30
00401B55 MOV DWORD PTR SS:[EBP-DC],EBX //EBX = 2: integer型
00401B5B CALL ESI ; <&MSVBVM60.__vbaVarMove>
**** a =&H30 即[ebp-4c]
00401B5D LEA EDX,DWORD PTR SS:[EBP-DC]
00401B63 LEA ECX,DWORD PTR SS:[EBP-5C] //变量b
00401B66 MOV DWORD PTR SS:[EBP-D4],14A //14Ah = 330
00401B70 MOV DWORD PTR SS:[EBP-DC],EBX //EBX = 2: integer型
00401B76 CALL ESI
**** b = 330 即[ebp-5c]
00401B78 LEA EDX,DWORD PTR SS:[EBP-DC]
00401B7E LEA ECX,DWORD PTR SS:[EBP-6C]
00401B81 MOV DWORD PTR SS:[EBP-D4],101D0 //101D0 = 66000
00401B8B MOV DWORD PTR SS:[EBP-DC],3 //3:long型
00401B95 CALL ESI
**** c = 66000 即[ebp-6c]
00401B97 LEA EDX,DWORD PTR SS:[EBP-DC]
00401B9D LEA ECX,DWORD PTR SS:[EBP-7C]
00401BA0 MOV DWORD PTR SS:[EBP-D4],-1 //-1 TRUE
00401BAA MOV DWORD PTR SS:[EBP-DC],0B //12:boolean型
00401BB4 CALL ESI
**** d = TRUE 即[ebp-7c]
00401BB6 LEA EDX,DWORD PTR SS:[EBP-DC]
00401BBC LEA ECX,DWORD PTR SS:[EBP-8C]
00401BC2 MOV DWORD PTR SS:[EBP-D4],工程1.00401948 ; UNICODE "hello" //"hello"
00401BCC MOV DWORD PTR SS:[EBP-DC],8 //8:string型
00401BD6 CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarCo>; MSVBVM60.__vbaVarCopy
**** e = "hello" 即][ebp-8c]
00401BDC LEA EAX,DWORD PTR SS:[EBP-CC]
00401BE2 PUSH EAX
00401BE3 CALL DWORD PTR DS:[<&MSVBVM60.#546>] //Now
00401BE9 LEA EDX,DWORD PTR SS:[EBP-CC] //Now
00401BEF LEA ECX,DWORD PTR SS:[EBP-9C]
00401BF5 CALL ESI
**** f = Now 即[ebp-9c]
00401BF7 MOV EBX,5
00401BFC LEA EDX,DWORD PTR SS:[EBP-DC]
00401C02 LEA ECX,DWORD PTR SS:[EBP-AC]
00401C08 MOV DWORD PTR SS:[EBP-D4],C083126F
00401C12 MOV DWORD PTR SS:[EBP-D0],400921CA //3.1415
00401C1C MOV DWORD PTR SS:[EBP-DC],EBX //EBX = 5 :double型
00401C22 CALL ESI
**** g = 3.1415 即[ebp-ac]
00401C24 LEA EDX,DWORD PTR SS:[EBP-DC]
00401C2A LEA ECX,DWORD PTR SS:[EBP-BC]
00401C30 MOV DWORD PTR SS:[EBP-D4],851EB852
00401C3A MOV DWORD PTR SS:[EBP-D0],3FF451EB //1.27
00401C44 MOV DWORD PTR SS:[EBP-DC],EBX //EBX = 5 :double型
00401C4A CALL ESI
// h = 1.27
00401C4C LEA ECX,DWORD PTR SS:[EBP-4C]
00401C4F PUSH ECX
00401C50 CALL DWORD PTR DS:[<&MSVBVM60.__vbaUI1Va>; MSVBVM60.__vbaUI1Var
//取byte, al中
**** i = a
00401C56 LEA EDX,DWORD PTR SS:][EBP-5C]
00401C59 PUSH EDX
00401C5A CALL DWORD PTR DS:[<&MSVBVM60.__vbaI2Var>; MSVBVM60.__vbaI2Var
//取integer, ax中
**** j = b
00401C60 LEA EAX,DWORD PTR SS:][EBP-6C]
00401C63 PUSH EAX
00401C64 CALL DWORD PTR DS:[<&MSVBVM60.__vbaI4Var>; MSVBVM60.__vbaI4Var
//取long, eax中
**** k = c
00401C6A LEA ECX,DWORD PTR SS:][EBP-7C]
00401C6D PUSH ECX
00401C6E CALL DWORD PTR DS:[<&MSVBVM60.__vbaBoolV>; MSVBVM60.__vbaBoolVar
//取boolean, ax中
**** l = d
00401C74 LEA EDX,DWORD PTR SS:][EBP-8C]
00401C7A PUSH EDX
00401C7B CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrVa>; MSVBVM60.__vbaStrVarCopy
//取string, eax中是地址
**** m = e
00401C81 MOV EDX,EAX
00401C83 LEA ECX,DWORD PTR SS:][EBP-28]
00401C86 CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrMo>; MSVBVM60.__vbaStrMove
00401C8C LEA EAX,DWORD PTR SS:][EBP-9C]
00401C92 PUSH EAX
00401C93 CALL DWORD PTR DS:[<&MSVBVM60.__vbaDateV>; MSVBVM60.__vbaDateVar
//取date, 浮点栈中
**** n = f
00401C99 LEA ECX,DWORD PTR SS:][EBP-AC]
00401C9F FSTP ST
00401CA1 PUSH ECX
00401CA2 CALL DWORD PTR DS:[<&MSVBVM60.__vbaR8Var>; MSVBVM60.__vbaR8Var
//取double, 浮点栈中
**** o = g
00401CA8 LEA EDX,DWORD PTR SS:][EBP-BC]
00401CAE FSTP ST
00401CB0 PUSH EDX
00401CB1 CALL DWORD PTR DS:<&MSVBVM60.__vbaR4Var>; MSVBVM60.__vbaR4Var
00401CB7 FSTP ST
//取single, 浮点栈中
**** p = h
==========================================
先总结一下:byte 和 integer一样,内部都是使用integer类型表示,single和double一样,
内部都是使用double类型表示。date占用8个字节。boolean类型占用2个字节&HFFFF表示TRUE,而
0表示FALSE.
这些简单变量在内存中的表示如下:前4个字节含有变量类型,接着4个字节(意义还不清楚),
我们不妨先叫他辅助类型信息,过了这8个字节就是真正的变量数据了。
如变量a的内存表示如下:63F3C4 : 02 00 00 00
63F3C8 : 00 00 00 00
63F3CC : 30 00 00 00
可以看到,63F3CC处是真正的变量值。如果是字符串,这个位置可以看到一个指针。
简单变量里没有提到的类型是Currency,Decimal,这些你可以自己实际观察一下。
关于简单变量就总结这些,不知道你看懂了没有。我没有办法把机器搬上来让你看。所以还是
那句话:如果你想弄明白我所讲的到底是怎么一回事,就把上面那段程序编译一下,然后用ollydbg
跟踪一下看看。
在本文即将结束时要提到的一个事实是:VB并不是强类型语言。例如你可以把一个boolean类型
的变量赋值给一个整数,然后再打印出这个整数的值,你可以看到TRUE的输出为-1,而FALSE的输
出为0。当然在你决定这样做的时候你要保证两个变量所占内存空间大小一样或者被赋值的变量的
空间要大于所赋值的变量的空间。否则你可能会得到一个溢出错误。你可以试试把date类型的变量
赋值给一个integer类型的变量
浅谈VB6逆向工程(2) by MengLong@RCT
作者:未知
2. 复杂变量的内部实现
这里所提到的复杂变量(我自己的叫法 ),是指枚举,数组和记录类型的变量。
1) 枚举类型的实现
先定义一个枚举类型如下:
Enum myweek
sun
mon
tues
wednes
thurs
fri
satur
End Enum
然后再编写一段使用枚举类型的代码:
Dim a As myweek
Dim b As Integer
a = sun
b = a
Print b
默认设置编译这段代码,接着我们看看编译器生成了什么。
; 37 : Dim a As myweek
; 38 : Dim b As Integer
; 39 :
; 40 : a = sun
; 41 : b = a
xor ecx, ecx // a = sun ,即 a = 0
call DWORD PTR __imp_@__vbaI2I4 // b = a
; 42 : Print b
push eax // b
push esi
push OFFSET FLAT:___vba@006255A0
call DWORD PTR __imp____vbaPrintObj //Print
***************************************************
可以看出,枚举类型在代码里是直接用常量数值代替的。
2) 数组类型的实现
数组的概念比较复杂,为了研究方便,这里只讨论一维数组,并且不是嵌套的。
先看看静态数组的定义与实现。
代码:
Dim a(3 To 6) As Integer
反汇编代码:
004019FF PUSH 2
00401A01 LEA EAX,DWORD PTR SS:[EBP-2C] // 数组变量
00401A04 XOR ESI,ESI
00401A06 PUSH 工程1.00401694 // 指向代码段
00401A0B PUSH EAX
00401A0C MOV DWORD PTR SS:[EBP-34],ESI
00401A0F CALL DWORD PTR DS:[<&MSVBVM60.__vbaAryConstruct2>] // 构造一个数组
指行到这里时看[ebp-2c]的内容:
0063F3E4 01 00 92 00 02 00 00 00 .?...
0063F3EC 00 00 00 00 C0 0F 51 00 ....?Q.
0063F3F4 04 00 00 00 03 00 00 00 ......
这些数据除了63F3F0处的地址是__vbaAryConstruct2函数填进去的,其余的都是从
401694处拷贝过来的。因此__vbaAryConstruct2函数的作用可以这样理解:先从401694
处拷贝24个字节到ebp-2c处,然后分配一块空间,把指向新分配的空间的指针填到63F3F0
处。
那么上面这些数据到底是什么意思呢?看下面的分析.
00401A18 PUSH 工程1.00401A30 //指向退出地址
00401A1D LEA EDX,DWORD PTR SS:[EBP-34]
00401A20 LEA ECX,DWORD PTR SS:[EBP-2C] //要释放的数组变量
00401A23 PUSH EDX
00401A24 PUSH 0
00401A26 MOV DWORD PTR SS:[EBP-34],ECX
00401A29 CALL DWORD PTR DS:[<&MSVBVM60.__vbaAryDestruct>] // 释放一个数组
为了弄清楚上面提到的那些内存数据的含义,我分别定义了不同大小不同类型的数组来比较,
下面是dump出来的典型数据:
Dim a(3 To 6)
0063F3E4 01 00 92 08 10 00 00 00 .?...
0063F3EC 00 00 00 00 2C 01 41 00 ....,A.
0063F3F4 04 00 00 00 03 00 00 00 ......
Dim a(3 To 6) As String
0063F3E4 01 00 92 01 04 00 00 00 .?...
0063F3EC 00 00 00 00 C0 0F 51 00 ....?Q.
0063F3F4 04 00 00 00 03 00 00 00 ......
Dim a(3 To 6) As Integer
0063F3E4 01 00 92 00 02 00 00 00 .?...
0063F3EC 00 00 00 00 C0 0F 51 00 ....?Q.
0063F3F4 04 00 00 00 03 00 00 00 ......
我总结的数组变量内存数据的说明:
0063F3E4 处的两个字节代表数组的维数
0063F3E6 处的一个字节 92 代表静态数组
0063F3E7 处的一个字节随着不同类型的变量有不同的变化。
08 : 变体类型
01 : String
00 : Integer,byte,long,single,double,date
0063F3E8 处的两个字节表示一个数组元素所占的内存空间字节数。
0063F3EC 处的4个字节总是0,可能是为了对齐。
0063F3F0 处的两个字节代表分配的空间的地址指针,即数组数据。
0063F3F4 处的两个字节代表静态数组元素的个数。
0063F3F8 处的两个字节代表数组的起始下标。
上面大概的对数组变量的数据做了说明,为了验证一下,再看一个3维数组的定义:
Dim a(1 To 2, 3 To 5, 6 To 9) As Integer
0063F3D4 03 00 92 00 02 00 00 00 .?...
0063F3DC 00 00 00 00 C0 0F 51 00 ....?Q.
0063F3E4 04 00 00 00 06 00 00 00 ......
0063F3EC 03 00 00 00 03 00 00 00 ......
0063F3F4 02 00 00 00 01 00 00 00 ......
可以看出,静态数组的信息在编译时就被编码到了代码段里。
静态数组的构造用 __vbaAryConstruct2
静态数组的释放用 __vbaAryDestruct
///////////////////////////////////////////////////////////
动态数组又是怎样实现的呢?
代码:
Dim a() As Date
ReDim a(2 To 5)
反汇编代码:
004019CF PUSH 2 //起始下标
004019D1 PUSH 5 //结束下标
004019D3 PUSH 1 //数组维数
004019D5 LEA EAX,DWORD PTR SS:[EBP-18]
004019D8 PUSH 7 //变量类型
004019DA PUSH EAX //我们重定义的数组变量
004019DB XOR ESI,ESI
004019DD PUSH 8 //数组元素所占内存空间的字节数
004019DF PUSH 80 //动态数组标记
004019E4 MOV DWORD PTR SS:[EBP-18],ESI
004019E7 CALL DWORD PTR DS:[<&MSVBVM60.__vbaRedim>] // ReDim
004019ED ADD ESP,1C
004019F0 MOV DWORD PTR SS:[EBP-4],ESI
004019F3 PUSH 工程1.00401A05
004019F8 LEA ECX,DWORD PTR SS:[EBP-18] //数组变量
004019FB PUSH ECX
004019FC PUSH 0
004019FE CALL DWORD PTR DS:[<&MSVBVM60.__vbaAryDestruct>] //释放数组
当执行到 004019ED 时,我们查看[ebp-18]处的内存数据,可以看到是
0063F3F8 D0 0F 51 00 ?Q.豇
这是一个指针,我们接着 follow dword in dump,可以看到数据如下:
00510FD0 01 00 80 00 08 00 00 00 . |
|