今天讓我們來看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
|