今天讓我們來看Win32ASM裡面的進階一點的技術——SMC(噹噹噹噹……)!!!
SMC是什麼意思?它的英文名叫“Self Modifying Code”,顧名思義,就是“代碼自修改”(?)(不好意思,小弟的英語六級還沒過,只能翻譯成這樣啦……)
“代碼自修改”?哇,好高深啊!其實不然……
我 們知道,Win32應用程式是運行在保護模式下的,每個Win32應用程式都有相互獨立的4GB地址空間,並且已經摒棄了16位時代的把代碼分為 Data、Code等段的記憶體模式的做法;現在它們只有一種記憶體模式,即FLAT模式,意思是“平坦”的記憶體模式——再也沒有煩人的64KB的段大小限制 啦。如此一來,所有的Win32應用程式都能各自運行在一個連續、平坦、巨大的4GB空間中,作為程式員,也不用再跟段寄存器打交道,您可以用任意的段寄 存器定址任意的地址空間,是不是很方便呢?
不過且慢,Win32時代的編程雖然比之Win16時代已經方便了不止一個數量級,但是畢竟還有一些規則是需要遵守的。最明顯的之一就是不能在程式啟動並執行過程中隨便更改程式碼片段!
(咦?剛剛不是說了Win32裡面沒有段的概念了嗎?怎麼這裡又來了一個“程式碼片段”了?別急,請聽我細細道來……)
雖然Win32下已經沒有了“段”,但是您還是可以給自己的程式分成不同的“段”,一個“分段”的開始即是上一個“分段”的結束。Win32隻有兩種性質的分段:Data和Code。
實 際上,在Win32裡面的分段並不是像DOS下一樣,為不同的段分別指出不同的段寄存器,因為Windows下只有一個4GB的段,Windows程式中 的分段表現在當程式裝載時,賦予不同的段不同的屬性,比如說當你的程式載入時,對於Ring3程式來說,.code段是不可寫的,而.data段是可寫 的,如果你嘗試像在DOS下一樣寫自己的代碼部分,你將會得到一個“很cool”的藍屏錯誤。
怎麼樣?頭暈了嗎?如果沒有的話,讓我們繼續!^_^
上面已經提到程式碼片段是不能在程式運行途中更改的了,那麼怎麼又來了一個“SMC”技術呢?它是如何?的?
其實關鍵就在於連結時的參數,只要指定了程式碼片段的屬性是可寫的,那麼就OK啦!(預設的參數是不可寫的)。也就是說,我們在編譯、連結帶有SMC的Win32ASM時應該這樣做:
ml /c /coff %1.asm link /subsystem:windows /section:.text,RWE %1.obj |
怎 麼樣?明白了嗎? /section:.text,RWE 這句指定了程式碼片段(.text)的屬性是RWE,含義是:R(ReadAble),W(WriteAble),E(ExecuteAble),也就是“可 讀可寫可執行”。這樣我們的程式就可以在運行途中自己改寫自己的程式碼片段啦,怎麼樣?是不是很爽呢?
下面給出了一個完整的帶有SMC技術的Win32ASM例子,很容易理解的,記得要用上面的方法來編譯和連結哦!
;*********************************************** ;程式名稱:示範SMC原理 ;作者:羅聰 ;日期:2002-10-2 ;出處:http://laoluoc.yeah.net(老羅的繽紛天地) ;注意事項:如欲轉載,請保持本程式的完整,並註明: ;轉載自“老羅的繽紛天地”(http://laoluoc.yeah.net) ;***********************************************
.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
ShowMessage proto ReplaceMent proto
.data szMsg1 db "這是未執行SMC之前的代碼!", 0 szMsg2 db "SMC已經執行!", 0 szCaption db "SMC demo by LC, 2002", 0 Replace_Len dd 0
.code main: ;第一次執行子程式ShowMessage,此時還沒執行SMC操作 invoke ShowMessage
lea eax, ReplaceMentEnd ;標號ReplaceMent的結束 lea edx, ReplaceMentStart ;標號ReplaceMent的開始 sub eax, edx ;標號ReplaceMent的長度 mov Replace_Len, eax ;把長度儲存起來
;關鍵代碼!!!!!!!!! lea esi, ReplaceMentStart ;標號ReplaceMent的開始 lea edi, ShowMessageStart ;原程式ShowMessage的標號的開始 mov ecx, Replace_Len ;標號ReplaceMent的長度 rep movsb ;這裡是最關鍵的語句!!!執行SMC操作!
;第二次執行子程式ShowMessage,此時已經執行了SMC操作。 ;換句話說,ShowMessage的內容已經不是第一次運行時的內容了: invoke ShowMessage
invoke ExitProcess, 0
ShowMessage proc ;這裡用“::”的話,就能夠使標號成為全域性的 ShowMessageStart:: invoke MessageBox, NULL, addr szMsg1, addr szCaption, MB_OK ShowMessageEnd::
;用nop來預留空間,以便後面的SMC能夠成功執行; ;否則如果空間不夠,將有可能產生不可預測的錯誤: nop nop nop nop nop nop nop nop
ret ShowMessage endp
ReplaceMent proc ;將要用來SMC的代碼: ReplaceMentStart:: ;invoke MessageBox, NULL, addr szMsg2, addr szCaption, MB_OK or MB_ICONINFORMATION push MB_OK or MB_ICONINFORMATION lea eax, szCaption push eax lea eax, szMsg2 push eax push NULL lea eax, MessageBox call eax ReplaceMentEnd::
ret ReplaceMent endp
end main
|
轉自:http://www.luocong.com/articles/show_article.asp?Article_ID=18