用匯編語言編寫內存修改器



在編寫不使用圖形界面(或使用簡單的圖形界面)的32 位Windows應用程序時,使用匯編語言會使程序簡單、快捷,並且這類程序生成的可執行文件比較小(一般不超過10K),執行速度快,無需安裝即可運行。在Windows環境中,彙編語言的編程方式與C語言類似,要調用Windows API。在彙編語言中調用API函數使用call 指令,並且函數的參數使用Push指令先入堆棧,這步工作在masm32v6中可以由invoke 宏指令自動完成。函數的返回值由eax寄存器返回。
例如MessageBox函數的聲明如下:
int MessageBox(HWND h1 , LPCTSTR lp1 , LPCTSTR lp2 , UINT u1 );
則其彙編調用如下:
invoke MessageBox,h1,lp1,lp2,u1
其中參數入棧、檢查等工作,由invoke宏指令自動完成。
一個簡單的Win 32程序如下:
.386
.model flat, stdcall ;使用32位平坦存儲模式
option casemap :none ;區分大小寫字母
include \MASM32\INCLUDE\windows.inc
include \MASM32\INCLUDE\kernel32.inc
includelib \MASM32\LIB\kernel32.lib
.code
start:
invoke ExitProcess,0
end start
以上程序與一個DOS下的彙編程序類似,其中語句“invoke ExitProcess,0”相當於DOS下的 int 20H或mov ah,04cH/int 21h,即結束程序並返回。這個框架程序並沒有完成什麼具體的功能,程序運行後馬上返回,並在其運行期間不生成窗口。
實現內存修改器
對上述程序添加語句,就可以使其成爲一個真正能用的內存修改器。以修改Windows附件中計算器程序爲例,如要修改計算器程序虛擬內存地址40b181H的值爲1234H(在運行修改器程序進行修改前,應先運行計算器程序)。
首先爲程序添加一個數據段。在 .code 前加上以下語句:
.data
ProcessId dd ? ;進程ID
windowname db “計算器”,0 ;窗口程序名
writeaddr dd 40b181H ;寫入的地址
writedate dd 1234H ;寫入的數據
然後在start: 後添加以下語句:
invoke FindWindow,NULL,addr windowname
invoke GetWindowThreadProcessId,eax,addr
ProcessId
invoke OpenProcess,PROCESS_ALL_ACCESS, FALSE, ProcessId
invoke WriteProcessMemory,eax,writeaddr,
addr writedate, 4, 0
invoke ExitProcess,0
在上述代碼中,首先使用 FindWindow 函數取得目標窗口的句柄,其中addr windowname是目標窗口名稱(字符串)在內存中的開始地址,字符串以0結束。窗口句柄由eax返回。如果eax=0表示函數出錯,其他值表示取得的目標窗口句柄。使用GetWindowThreadProcessId 函數,通過窗口句柄eax取得進程標識符ProcessId。同樣,如果函數返回eax=0表示函數出錯,其他值表示成功取得ProcessId。利用OpenProcess 函數,從進程標識符ProcessId得到計算器程序的進程句柄。其中PROCESS_ALL_ACCESS表示擁有所有權限,參數FALSE是固定的。同樣,句柄由eax返回,如果eax=0表示函數出錯,其他值表示取得目標進程句柄。
然後用WriteProcessMemory 函數修改內存,調用方法如下:
invoke WriteProcessMemory,eax,writeaddr, addr writedate, 4, 0
其參數的含義如下:
●eax:從OpenProcess 函數中取得的目標進程的句柄;
●writeaddr :計算器程序的虛擬內存中將要被修改的地址;
●addr writedate :將要寫入上述地址的數據開始的地址;
●4 :希望寫入addr writedate地址的字節數;
●0 :函數執行返回後,寫入目標地址的實際字節數,能被用來確認函數實際的執行情況,這裏用0填入。
該函數返回eax,如果eax=0表示函數出錯。
最後調用ExitProcess函數結束程序,返回0值給操作系統。
編輯好上述代碼存盤後,用masm32v6中的編譯、連接命令生成執行文件。該執行文件可以在Windows 95/98/Me中執行,並完成內存修改。由於Windows NT的內存管理方式與Windows 98等不同,所以在Windows NT中該程序可以執行,但不能修改內存。
編譯:
ML /c /coff 文件名.asm
連接:
LINK /SUBSYSTEM:WINDOWS 文件名.obj
內存修改器的改進
上述程序雖然可以執行,但是缺乏交互性和通用性,這可以從以下兩個方面進行改進。
1.改進程序執行後無論是否修改了內存或者產生了其他錯誤,都沒有任何提示的問題。要增強交互性,可對每個函數執行後返回的eax值作檢查,若eax=0,則產生對話框向用戶提示錯誤,並返回操作系統。
爲了能產生出錯提示,在數據段增加以下定義:
text1 db “提示!”,0 ;對話框的標題
text2 db “目標程序未運行!“,0
text3 db “不能獲得目標進程句柄!”,0
text4 db “不能打開目標進程!”,0
text5 db “未能修改目標內存!”,0
text6 db “命令格式:程序名 窗口名 欲修改地址 新值”,0
使用MessageBox函數彈出對話框,向用戶提示出錯信息:
invoke FindWindow,NULL,windowname
.if eax==0
 invoke MessageBox,NULL,addr text2,addr text1,MB_OK
invoke ExitProcess,1
.endif
2.改進缺乏通用性的問題。以上修改器程序若要修改另外一個窗口程序的內存,則需對源程序數據段中的windowname、writeaddr、writedate進行修改,重新賦予初值,以適應另一窗口程序,然後重新編譯、連接才能實現。如果內存修改器帶有參數,就可提高程序的通用性。格式如下:
執行程序名 參數1 參數2 參數3
執行程序名 窗口程序名 要修改的地址 新值
要獲得命令行參數,可以在數據段定義一個變量,用來存放命令行參數在虛擬內存中存放的開始地址。在代碼段的開始(start:標識符後)插入以下語句獲得命令行參數的首址。
invoke GetCommandLine
mov CommandLine, eax
獲得命令行參數首址後,對命令行參數進行分析,分離出參數1、參數2、參數3,然後將分離出的參數送到數據段的相應變量中即可。
小結
本文只是簡單介紹了用匯編語言編寫Windows程序的方法。一個完整的內存修改器應包含內存搜索功能和儘可能高的搜索效率和準確度。內存搜索功能可以利用具有讀內存功能的API函數ReadProcessMemory,通過一個循環結構完成。但提高搜索效率和準確度需要豐富的編程經驗和技巧才能實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章