有矛就有盾。
所以我們要討論加密技術。
我們知道,所有的編譯型語
言,例如VC、BCB、Delphi和Win32ASM……最終都會把源代碼編譯成機器能識別的0和1——因此也能夠反過來把這些0和1反編譯成匯編代
碼。反編譯有什么用呢?試想想,你辛辛苦苦寫了一個perfect的軟件出來,正準備把它賣上100萬份,忽然!在市面上出現了很多仿制你的東
西……hoho,不知道你會怎么想呢?反正我是會欲哭無淚的。還有另外一種情況,你的軟件是用注冊碼的形式來授權的,每份license要賣30個美刀。
呵呵,正當你在考慮著一年后是去加利福尼亞還是夏威夷度假的時候,你的軟件被Crack了——也就是說,你一分錢都不會得到……(啊!我想跳樓啦!!)
所以我們要討論如何給自己的程序加密。這次就先說說最簡單的花指令。
在解釋這個“花指令”之前,不妨先做幾個小小的實驗。
我們先來寫一個程序,命名為hua.asm,內容如下:
;*************************************************************** ;花指令實驗1 ;作者:羅聰 ;日期:2002-8-21 ;*************************************************************** .386 .model flat, stdcall option casemap:none include "masm32"include"windows.inc include "masm32"include"kernel32.inc include "masm32"include"user32.inc includelib "masm32"lib"kernel32.lib includelib "masm32"lib"user32.lib .data szText db "嘿嘿,這是一個花指令程序……", 0 szCaption db "花指令演示 by LC 2002-8-21", 0 .code main: jmp Do_It Do_It: invoke MessageBox, NULL, addr szText, addr szCaption, MB_OK invoke ExitProcess, 0 end main |
然后用W32Dasm v10來反編譯它,得到的結果如下:(由于篇幅所限,這里只列出關鍵部分)
+++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++ //********************** Start of Code in Object .text ************** Program Entry Point = 00401000 (hua.exe File Offset:00001600) //******************** Program Entry Point ******** :00401000 EB00 jmp 00401002 * Referenced by a (U)nconditional or ?onditional Jump at Address: |:00401000(U) | :00401002 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"花指令演示 by LC 2002-8-21" | :00401004 681F304000 push 0040301F * Possible StringData Ref from Data Obj ->"嘿嘿,這是一個花指令程序……" | :00401009 6800304000 push 00403000 :0040100E 6A00 push 00000000 * Reference To: USER32.MessageBoxA, Ord:01BBh | :00401010 E80D000000 Call 00401022 :00401015 6A00 push 00000000 * Reference To: KERNEL32.ExitProcess, Ord:0075h | :00401017 E800000000 Call 0040101C |
哇,好夸張啊!你可能會說。反編譯出來的代碼幾乎是跟源代碼一一對應的,這樣一來?我們的程序還有什么秘密可言呢?完全可以從反編譯的結果中理解程序的功能。
而且我們還可以在W32Dasm的“String Data References”中得到:
"嘿嘿,這個是一個花指令程序……" "花指令演示 by LC 2002-8-21" |
把剛才的源程序稍做修改,來做第二個實驗:
;*************************************************************** ;花指令實驗2 ;作者:羅聰 ;日期:2002-8-21 ;*************************************************************** .386 .model flat, stdcall option casemap:none include "masm32"include"windows.inc include "masm32"include"kernel32.inc include "masm32"include"user32.inc includelib "masm32"lib"kernel32.lib includelib "masm32"lib"user32.lib .data szText db "嘿嘿,這是一個花指令程序……", 0 szCaption db "花指令演示 by LC 2002-8-21", 0 .code main: jz Do_It ;注意這里和第一個實驗中的源程序的區別 jnz Do_It ;注意這里和第一個實驗中的源程序的區別 Do_It: invoke MessageBox, NULL, addr szText, addr szCaption, MB_OK end main |
用W32Dasm反編譯一下:
+++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++ //********************** Start of Code in Object .text ************** Program Entry Point = 00401000 (hua.exe File Offset:00001600) //******************** Program Entry Point ******** :00401000 7402 je 00401004 :00401002 7500 jne 00401004 * Referenced by a (U)nconditional or ?onditional Jump at Addresses: |:00401000?, :00401002? | :00401004 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"花指令演示 by LC 2002-8-21" | :00401006 681F304000 push 0040301F * Possible StringData Ref from Data Obj ->"嘿嘿,這是一個花指令程序……" | :0040100B 6800304000 push 00403000 :00401010 6A00 push 00000000 * Reference To: USER32.MessageBoxA, Ord:01BBh | :00401012 E801000000 Call 00401018 |
可以看出,這時的W32Dasm反編譯出來的匯編指令還是正確的。但是W32Dasm其實已經逐漸落入我們設下的“陷阱”了。
下面我們來做第三個實驗,把源程序改成:
;*************************************************************** ;花指令實驗3 ;作者:羅聰 ;日期:2002-8-21 ;*************************************************************** .386 .model flat, stdcall option casemap:none include "masm32"include"windows.inc include "masm32"include"kernel32.inc include "masm32"include"user32.inc includelib "masm32"lib"kernel32.lib includelib "masm32"lib"user32.lib .data szText db "嘿嘿,這是一個花指令程序……", 0 szCaption db "花指令演示 by LC 2002-8-21", 0 .code main: jz Do_It ;注意這里和第一個實驗中的源程序的區別 jnz Do_It ;注意這里和第一個實驗中的源程序的區別 db 0E8h ;注意這里和第二個實驗中的源程序的區別 Do_It: invoke MessageBox, NULL, addr szText, addr szCaption, MB_OK invoke ExitProcess, 0 end main |
我們來看看W32Dasm中反編譯出來的東西:
+++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++ //********************** Start of Code in Object .text ************** Program Entry Point = 00401000 (hua.exe File Offset:00001600) //******************** Program Entry Point ******** :00401000 7403 je 00401005 :00401002 7501 jne 00401005 :00401004 E86A00681D call 1DA81073 :00401009 304000 xor byte ptr [eax+00], al * Possible StringData Ref from Data Obj ->"嘿嘿,這是一個花指令程序……" | :0040100C 6800304000 push 00403000 :00401011 6A00 push 00000000 * Reference To: USER32.MessageBoxA, Ord:01BBh | :00401013 E80E000000 Call 00401026 :00401018 6A00 push 00000000 * Reference To: KERNEL32.ExitProcess, Ord:0075h | :0040101A E801000000 Call 00401020 |
呵呵,很明顯了,這時的 00401004 到 00401009 行出錯了,而且這時查看“String Data References”,也只剩下了:
"嘿嘿,這是一個花指令程序……" |
讓我們進一步隱藏信息,做第四個實驗:
;*************************************************************** ;花指令實驗4 ;作者:羅聰 ;日期:2002-8-21 ;*************************************************************** .386 .model flat, stdcall option casemap:none include "masm32"include"windows.inc include "masm32"include"kernel32.inc include "masm32"include"user32.inc includelib "masm32"lib"kernel32.lib includelib "masm32"lib"user32.lib .data szText db "嘿嘿,這是一個花指令程序……", 0 szCaption db "花指令演示 by LC 2002-8-21", 0 .code main: jz Do_It ;注意這里和第一個實驗中的源程序的區別 jnz Do_It ;注意這里和第一個實驗中的源程序的區別 db 0E8h ;注意這里和第二個實驗中的源程序的區別 Do_It: lea eax, szText ;注意這里和第三個實驗中的源程序的區別 lea ebx, szCaption ;注意這里和第三個實驗中的源程序的區別 invoke MessageBox, NULL, eax, ebx, MB_OK ;注意這里和第三個實驗中的源程序的區別 invoke ExitProcess, 0 end main |
編譯,再用W32Dasm反編譯,得到的是:
+++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++ //********************** Start of Code in Object .text ************** Program Entry Point = 00401000 (hua.exe File Offset:00001600) //******************** Program Entry Point ******** :00401000 7403 je 00401005 :00401002 7501 jne 00401005 :00401004 E88D050030 call 30401596 :00401009 40 inc eax :0040100A 008D1D1D3040 add byte ptr [ebp+40301D1D], cl :00401010 006A00 add byte ptr [edx+00], ch :00401013 53 push ebx :00401014 50 push eax :00401015 6A00 push 00000000 * Reference To: USER32.MessageBoxA, Ord:01BBh | :00401017 E80E000000 Call 0040102A :0040101C 6A00 push 00000000 * Reference To: KERNEL32.ExitProcess, Ord:0075h | :0040101E E801000000 Call 00401024 |
呵呵,這次不但面目全非了,而且“String Data References”按鈕已經變成了灰色。什么蛛絲馬跡都沒有了。
各位看官看到這里明白了嗎?其實花指令就是人為地構造一些“陷阱”和一些無用的字節。例如第二個實驗中的:
jz Do_It
jnz Do_It
其實這個跟 jmp Do_It 還不是一樣嗎?(呵呵,如果在大學的期末考試里這樣寫,一定會被判不及格……)
是的,其實程序原有的功能和邏輯還是一樣的,我們只不過是換了一種表現形式而已。然而,反編譯工具是沒有人腦那么智能的,它們往往就會把這些指令理解錯,從而錯誤地確定了指令的起始位置。
要實現這種絕對跳轉的功能,還可以用很多的方法,例如:
Push Do_It
ret
花指令是很容易理解的,不過大家要注意適時而用,不要濫用啊,能起到迷惑破解者和隱藏信息的作用就行了,不然將來要維護代碼時,我怕被迷惑的反而是你自己哦,呵呵……