關於VB中資料的儲存格式和定址方式 菜鳥獻醜了~~~

看雪資料發表於2004-07-01

關於VB中資料的儲存格式和定址方式

前言:
大家最喜歡破什麼語言寫的軟體?相信沒人會回答VB,好像一種語言越是“高階”,越是“傻瓜”,編譯系統自動加入的程式碼就越多,會把源程式的意圖隱藏起來,破解的難度反而越大(我指的是完全破解出演算法,寫出序號產生器)。用ASM直接寫的程式,反彙編的結果和源程式相似得可怕,用VC寫的也比較容易讀懂,用DELPHI寫的就有一定難度了,不過畢竟還有一個好用的DEDE,VB寫的呢?冗長的程式碼,複雜的儲存方式,鋪天蓋地的DLL,足以讓人崩潰。以前大概用VB的還都是些菜鳥級的程式設計師,加的保護也非常簡單,破解VB程式還不算太困難,到了現在,好像VB已經成了一種把程式碼複雜化的加殼軟體,紛紛被各路程式設計好手採用,把高難度的演算法用超複雜的程式碼保護起來,嗚呼哀哉!最近連遇幾個軟體,全是AsProtect+VB(好像還是P-CODE),令人鬱悶的組合,幾天破解未果,想從頭把關於VB的一些東東仔細研究一下,於是就有了這篇文章。我想高手們大概早就研究過了,也許是不屑寫出來而己,還請各位多多指點。

先看看VB常用的資料型別有哪些:

程式碼:
Byte    1個位元組    0到255   Boolean    2個位元組    True或False Integer    2個位元組    -32,768到32,767 Long(長整型)  4個位元組    -2,147,483,648到2,147,483,647 Single(單精度浮點型)  4個位元組    負數時從-3.402823E38到-1.401298E-45           正數時從1.401298E-45到3.402823E38 Double(雙精度浮點型)  8個位元組    負數時從-1.79769313486232E308到-4.94065645841247E-324           正數時從4.94065645841247E-324到1.79769313486232E308 Currency(變比整型)  8個位元組    從-922,337,203,685,477.5808到922,337,203,685,477.5807 Decimal    14個位元組  沒有小數點時為79,228,162,514,264,337,593,543,950,335           而小數點右邊有28位數時為7.9228162514264337593543950335           最小的非零值為0.0000000000000000000000000001 Date    8個位元組    100年1月1日到9999年12月31日 Object    4個位元組    任何Object引用 String(變長)  10位元組+串長度  0到大約20億 String(定長)  字串長度1到大約65,400 Variant(數字)  16個位元組  任何數字值,最大可達Double的範圍 Variant(字元)  22個位元組+串長度  與變長String有相同的範圍

像Integer,Long,Single,Double等“直接”的型別比較容易,和其他的語言一樣直接看記憶體就可以了。
比較特殊的是Currency,他的值要除10000才是真正的值,不過這個用的不多。
最麻煩的是Variant型別,因為VB是設計成一種傻瓜式的語言,對資料型別沒有嚴格的規定,甚至可以不用宣告變數而直接使用,所以在反彙編後的VB程式中,關於型別轉換的語句佔了很大一部分,只要一涉及資料計算,總會看到一堆數來回轉換,其中許多函式的引數,還有未經宣告直接使用的變數等,都是Variant型別。這種型別在VB中到處要用到,十分重要,但又常常使人困惑。它在記憶體中的定址方式很特殊,為此VB還專門為其提供了一組函式(多帶有Var字樣),這些函式其實大多放在Oleaut32.dll中,但往往再由msvbvm60.dll來呼叫,比如__vbaVarTstEq,__vbaVarTstNe,__vbaVarMove,__vbaVarAdd,__vbaVarSub,VarBstrCmp等等。Variant變數的定址方式在看雪的書中略有提及但不詳細,我在這裡補充一些:

首先我們必須明白,那些未宣告型別的Variant變數並不是真的沒有資料型別,只不過是VB編譯系統將這些變數的“型別資訊”也包含在變數的資料中了,等到程式執行時根據對該變數所進行的操作來靈活地決定變數屬於什麼型別,比如有這麼幾句
程式碼:
Dim roba As Variant roba=4321 Text1.Text=roba

程式宣告瞭一個Variant變數roba(或者乾脆什麼也沒宣告)又給它賦了一個值4321,那麼編譯器就知道這時候roba是一個Integer型變數,可是下面呢又把它賦值給了Text1.Text(也就是在一個文字框裡把4321顯示出來)這時候編譯器馬上又插入語句使roba變為字串型變數。(真是難為M$那幫人了)

那麼這種變數究竟是怎樣儲存的呢?看下面的例子:
程式碼:
Private Sub Command1_Click() Dim a, b As Variant a = "RoBa" b = Text1.Text If a = b Then MsgBox "Well done!", vbOKOnly, "Crack" End If End Sub

用W32DASM反彙編,查詢字串,很容易找到下面:
程式碼:
:00401D49 8D45DC                  lea eax, dword ptr [ebp-24] :00401D4C 8D4DCC                  lea ecx, dword ptr [ebp-34] :00401D4F 50                      push eax      ;變數a :00401D50 51                      push ecx      ;變數b * Reference To: MSVBVM60.__vbaVarTstEq, Ord:0000h                                   | :00401D51 FF1540104000            Call dword ptr [00401040]  ;比較 :00401D57 6685C0                  test ax, ax :00401D5A 0F8484000000            je 00401DE4      ;不同就跳走了 * Reference To: MSVBVM60.__vbaVarDup, Ord:0000h                                   | :00401D60 8B3D7C104000            mov edi, dword ptr [0040107C] :00401D66 B904000280              mov ecx, 80020004 :00401D6B 894D8C                  mov dword ptr [ebp-74], ecx :00401D6E B80A000000              mov eax, 0000000A :00401D73 894D9C                  mov dword ptr [ebp-64], ecx :00401D76 8D9564FFFFFF            lea edx, dword ptr [ebp+FFFFFF64] :00401D7C 8D4DA4                  lea ecx, dword ptr [ebp-5C] :00401D7F 894584                  mov dword ptr [ebp-7C], eax :00401D82 894594                  mov dword ptr [ebp-6C], eax * Possible StringData Ref from Code Obj ->"CCrack"                                   | :00401D85 C7856CFFFFFFFC174000    mov dword ptr [ebp+FFFFFF6C], 004017FC :00401D8F 899D64FFFFFF            mov dword ptr [ebp+FFFFFF64], ebx :00401D95 FFD7                    call edi :00401D97 8D9574FFFFFF            lea edx, dword ptr [ebp+FFFFFF74] :00401D9D 8D4DB4                  lea ecx, dword ptr [ebp-4C] * Possible StringData Ref from Code Obj ->"WWell done!"                                   | :00401DA0 C7857CFFFFFFE0174000    mov dword ptr [ebp+FFFFFF7C], 004017E0 :00401DAA 899D74FFFFFF            mov dword ptr [ebp+FFFFFF74], ebx :00401DB0 FFD7                    call edi :00401DB2 8D5584                  lea edx, dword ptr [ebp-7C] :00401DB5 8D4594                  lea eax, dword ptr [ebp-6C] :00401DB8 52                      push edx :00401DB9 8D4DA4                  lea ecx, dword ptr [ebp-5C] :00401DBC 50                      push eax :00401DBD 51                      push ecx :00401DBE 8D55B4                  lea edx, dword ptr [ebp-4C] :00401DC1 56                      push esi :00401DC2 52                      push edx * Reference To: MSVBVM60.rtcMsgBox, Ord:0253h                                   | :00401DC3 FF1528104000            Call dword ptr [00401028]  ;出現成功對話方塊

很明顯的比較方式,用SoftICE跟一下,胡亂輸入1111,中斷在401D51處,可是當D eax,D ecx時只能看到08
:d eax
016F:0063F3EC 08 00 00 00 00 00 4A 21-CC 0F 51 00 86 72 6F 17  ......J!..Q..ro.
016F:0063F3FC F4 F8 63 00 B6 10 40 00-34 F3 63 00 A0 10 40 00  ..c...@.4.c...@.
016F:0063F40C 01 00 00 00 1C F4 63 00-73 AD 02 66 CC 05 51 00  ......c.s..f..Q.
:d ecx
016F:0063F3DC 08 00 00 00 36 18 76 8B-E0 0F 51 00 0C 00 0D 00  ....6.v...Q.....
016F:0063F3EC 08 00 00 00 00 00 4A 21-CC 0F 51 00 86 72 6F 17  ......J!..Q..ro.
016F:0063F3FC F4 F8 63 00 B6 10 40 00-34 F3 63 00 A0 10 40 00  ..c...@.4.c...@.
當然不可能是把兩個08比較,實際的UNICODE字串地址是在8個位元組後的地方。即510FCC和510FE0
:d 510fcc
016F:00510FCC 52 00 6F 00 42 00 61 00-00 00 00 00 14 00 00 A0  R.o.B.a.........
016F:00510FDC 08 00 00 00 31 00 31 00-31 00 31 00 00 00 00 00  ....1.1.1.1.....
016F:00510FEC 11 00 00 A0 1C 00 41 00-0C 00 41 00 EC 0F 51 00  ......A...A...Q.
:d 510fe0
016F:00510FE0 31 00 31 00 31 00 31 00-00 00 00 00 11 00 00 A0  1.1.1.1.........
016F:00510FF0 1C 00 41 00 0C 00 41 00-EC 0F 51 00 02 00 00 A0  ..A...A...Q.....
016F:00511000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ................
那麼08是什麼意思呢?為什麼EAX,ECX要指向這麼一個莫名其妙的值呢?我猜想那個08就是表示Varient的實際型別,換一個型別試試:
程式碼:
Private Sub Command1_Click() Dim b As Variant b = Text1.Text If b = 5678 Then MsgBox "Well done!", vbOKOnly, "Crack" End If End Sub

程式碼:
:00401D13 50                      push eax :00401D14 51                      push ecx :00401D15 C7857CFFFFFF2E160000    mov dword ptr [ebp+FFFFFF7C], 0000162E ;162Eh=5678 :00401D1F C78574FFFFFF02800000    mov dword ptr [ebp+FFFFFF74], 00008002 ;型別值 * Reference To: MSVBVM60.__vbaVarTstEq, Ord:0000h                                   | :00401D29 FF1540104000            Call dword ptr [00401040] :00401D2F 6685C0                  test ax, ax :00401D32 0F8484000000            je 00401DBC

在401D29時,d eax仍然看到08,d *(eax+8)可以看到我們隨意輸入的字串,而d ecx時看到
:d ecx
016F:0063F384 02 80 00 00 66 24 27 06-2E 16 00 00 B0 00 DD 00  ....f$'.........
016F:0063F394 00 00 00 00 00 00 00 00-00 00 00 00 00 00 08 00  ................
016F:0063F3A4 00 00 00 00 10 DB 01 00-0E 84 D7 3A 00 00 00 00  ...........:....
可以看到02,那麼按上面方面類推,ecx+8處是什麼呢?162E,呵~~~~,不就是5678的十六進位制嗎,那麼02當然就表示Integer了.(高位的80不知道什麼作用,改成00似乎也沒有影響)
問題清楚一些了,Variant變數的第一個位元組表示資料的實際型別,後面七個位元組不知有什麼用,在第九個位元組處才是資料的值或資料的地址。
我整理出的Variant變數的各種實際型別的程式碼:
程式碼:
02  Integer    用d eax+8可以看到,佔兩位元組 03  Long    用d eax+8可以看到,佔四位元組 04  Single    用ds eax+8可以看到 05  Double    用dl eax+8可以看到 08  String    用d *(eax+8)可以看到 0B  Boolean    用d eax+8可以看到,True為FFFFFFFF 11  Byte    用d eax+8可以看到,佔一位元組

以後當你D出一個05,08這樣的數字時不會再感到莫名其妙了吧。

還是有許多不明白的地方,比如中間的七位到底有什麼用,在那些有Var字樣的函式內部實現的過程究竟是怎樣(我跟進了一個vbaVarAdd發現極其複雜)等等,還請各位大大指出來,幫助我們這些在黑暗中摸索的菜鳥們。

相關文章