調試器第二講,單步步入/步過功能實現,以及基本的斷點功能實現
昨天,我們實現了調試器的基本框架,那么今天我們實現單步功能,還有斷點功能,以及使用反匯編引擎
作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)
一丶反匯編引擎的編譯,生成LIB
首先,我們有一個反匯編引擎的代碼,現在我們編譯連接一下(注意,可以使用GitHub下載,或者百度Google下載)
關於反匯編引擎的介紹: 請參考轉載博客 http://blog.csdn.net/earbao/article/details/72857805
先編譯成OBJ文件,(不連接),上面兩個函數就是我們需要的使用的,准確的來說,是下面的這個,因為上面那個不會給你二進制數據.
比如我們反匯編的結果是
00401000 CC int 3 那么這樣就是用下面這個,有二進制代碼顯示
如果使用上面那個則是
00401000 int3 具體愛好看你使用那個,這里具體講解第二個
先編譯成OBJ
需要的就這有3個, 除了stdafx.obj 還有MyDisasm.obj 其余的都是我們用的
現在我們使用lib工具,講編譯好的obj打包成一個lib(當然你也可以寫)
關於lib工具的使用,請參考我寫的博客,32位匯編第七講,混合編程 連接: http://www.cnblogs.com/iBinary/p/7555503.html
現在回車,生成我們的lib
然后我們還需要這兩個函數的頭文件.
現在准備齊全了,准備開始我們的代碼編寫.
注意: 這里我們是生成的靜態lib,你也可以生成動態鏈接庫 (也就是俗稱的 DLL)
二丶使用反匯編引擎API
直接看代碼吧
新建一個工程,控制台的(隨你便,啥都行,能寫代碼,調用API的就可以)
把我們的lib,和.h文件,拷貝到我們的程序目錄下(靜態使用)
#include "stdafx.h" #include <WINDOWS.H> #include <STDLIB.H> #include "Decode2Asm.h" #pragma comment(lib,"MyDisAsm.lib") int main(int argc, char* argv[]) { BYTE OpCode[] = {0xcc,0x90}; //解析的地址,地址里面有機器碼,我們不知道是什么(這里舉例子我們知道) char szAsm[256] = {NULL}; //通過地址,得出反匯編字符串,放到這個緩沖區中 char szBin[256] = {NULL}; //通過地址,得出二進制機器碼,放到這個緩沖區中 UINT binSize = NULL; //每次只解析一個指令長度,所以我們要遍歷數組,加上這個長度,接着輸出出來. BYTE *pCurCode = OpCode; //遍歷的時候需要給個指針 do { Decode2AsmOpcode(pCurCode,szAsm,szBin,&binSize,(UINT)pCurCode);//調用API,解析, printf("%-16p%-20s%-20s\r\n",pCurCode,szBin,szAsm); //輸出 pCurCode = pCurCode + binSize; //地址 + 指令長度 = 下一次需要解析的指令的地址 } while (pCurCode < (OpCode + sizeof(OpCode))); //判斷是否數組越界 system("pause"); return 0; }
Decode2AsmOpcode的各個參數意思:
第一個參數: 地址,你要給我一個地址,我去解析
第二個參數: 反匯編代碼的緩沖區,通過地址,解析的反匯編會放到這個緩沖區當中
第三個參數: 二進制代碼緩沖區,通過地址,解析二進制,放到這個緩沖區當中
第四個參數: 是否計算偏移
關於這個參數,其實是給EIP的值,(也就是地址),例如我們上面的給的.
這個參數的作用是
比如你有一個 JMP 指令
00401000 JMP 000010 這個是正常的匯編代碼,我們知道JMP是JMP的偏移
而后面給了這個參數,它則會給我們計算出來
00401000 JMP 00401010
看下輸出結果
配合我們昨天寫的,則可以反匯編出來調試進程的代碼了.
三丶匯編調用反匯編引擎API,顯示調試進程的匯編代碼.
在上面,我們對API有了一個認識.
下面我們就結合我們昨天寫的匯編代碼,接着寫,顯示出來斷點位置的反匯編代碼.
首先在處理異常的事件中,我們要調用
先加載我們的LIB,以及INC文件(INC文件中,是那個兩個API的函數聲明,你可以自己通過LIB生成,
具體請參考上面的混合編程的博客鏈接)
1.加載LIB,以及INC文件
2.使用ReadProcessMemory,和我們的API配合使用
首先使用Read..讀取.然后放到我們的數組中,
然后使用API,獲取反匯編的各種信息
invoke ReadProcessMemory,g_hProcess,
[ebx].u.Exception.pExceptionRecord.ExceptionAddress,
@OpCode,sizeof @OpCode,
NULL
invoke ShowAsm,addr @OpCode,[ebx].u.Exception.pExceptionRecord.ExceptionAddress
當然,Read的時候,需要注意要保存g_hProcess句柄.這樣才可以.
下面的ShowAsm是封裝的函數,內部還是調用的Decode2AsmOpco
ShowAsm proc pCode :ptr byte, pAddr:ptr Byte LOCAL @szAsmCode[256]:BYTE LOCAL @szOpCode[256]:BYTE LOCAL @CodeSize1:DWORD invoke Decode2AsmOpcode, pCode, addr @szAsmCode, addr @szOpCode, addr @CodeSize1, pAddr invoke crt_printf, offset g_szDcode, pAddr, addr @szOpCode, addr @szAsmCode ret ShowAsm endp
這里提一下簡單思路,因為真正的都寫出來,匯編代碼很多,影響觀看,所以都是簡單思路,沒有完整代碼,完整代碼,在每天的課件資料中,可以去看,這里這說一下核心代碼的思路
四丶匯編設置F2斷點,以及單步(步入,步過)
簡單思路
1.斷點的設置:
1.首先,系統斷點第一次來,然后在創建進程的時候會有一個地址,我們使用Read...讀取地址內容,然后反匯編出來顯示
2.讀取出來之前,使用VirtualProtectEx將保護屬性去除,(注意保存舊的)
3.使用WriteProcessMemory往地址寫入CC(注意保存以前的值)
4.重新修改保護屬性,改回去(使用舊的)
然后一個斷點即完成了,具體代碼,請看課件.
2.單步的設置(步入,進函數)
如果是單步,我們要判斷斷點是我們設置的還是系統設置的.
1.判斷是否使我們設置的斷點
2.修改內存保護屬性(注意保存舊的)
3.寫入CC,(int 3斷點)
4.讀取內存數據
5.顯示反匯編
設置單步(步入)異常
1.打開線程獲得線程句柄
2.使用GetThreadContext獲取寄存器的值
3.設置單步標志,單步表示是要我們設置的,他是第9個標志
or [esi].regflag,0100h 這樣設置即可.設置第九位為1
4.設置寄存器環境 SetThreadContext
設置單步步過異常
但步步過和單步步入一樣
只不過遇到Call的時候我們要把他的下一條指令設置一個int3斷點才可以.
課堂資料: 鏈接:http://pan.baidu.com/s/1geBOgQR 密碼:5dw9
作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)