查看單個文章
舊 2006-05-25, 03:58 PM   #1 (permalink)
psac
榮譽會員
 
psac 的頭像
榮譽勳章
UID - 3662
在線等級: 級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時級別:30 | 在線時長:1048小時 | 升級還需:37小時
註冊日期: 2002-12-07
住址: 木柵市立動物園
文章: 17381
現金: 5253 金幣
資產: 33853 金幣
預設 VB6逆向工程淺析(1-5全)

訊息來源:[OCN]破解組織論壇(ocn.e5v.com/bbs1)
nnd,前幾天跟蹤一個vb程序算法,幾天沒進展。找了一堆資料,給大家共享!
==================================================

前言

VB的編譯有兩種方式,一種是P-Code方式,一種是本地機程式碼。P_Code方式是VB
從早期版本保留下來的,也是比較難逆向的一種。而本地機程式碼方式編譯的程序已經
和VC很接近了。這裡只探討以本地機程式碼方式編譯的程序。由於微軟在VB的實現方面
的資料很少,使人覺得VB的逆向比較難。其實掌握一些規律,VB的程序還是很容易
入門的。
這裡所提到的知識都是個人的經驗,沒有什麼官方的資料作為依據。所以錯誤
之處難免,如果你發現什麼錯誤,歡迎指正。


1. 從簡單變數的實現開始

一個VB簡單變數的表示至少需要12個字元。通常前2個字元是表示檔案類型訊息的。
從第灌水限制節到第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檔案類型的變數
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

***************************************************
可以看出,枚舉檔案類型在程式碼裡是直接用常量數值替代的。
__________________
http://bbsimg.qianlong.com/upload/01/08/29/68/1082968_1136014649812.gif
psac 目前離線  
送花文章: 3, 收花文章: 1631 篇, 收花: 3205 次