int 3指令:
- 機器碼為1個字節(jié),即0xcc
- 沒有數(shù)量限制
- 局限:屬于代碼類斷點,可以讓CPU執(zhí)行到代碼段內(nèi)的某個地址停下來,不適用與數(shù)據(jù)段和I/o空間
- 原理解釋:在VS中某一處按下F9,實際上是VS在那個位置插入了一條int 3指令(替換了那個字節(jié),替換為cc
一個例子:
打開掃雷:
X ntdll!*readfile*
輸入: u ntdll!NtReadFile
ntdll!NtReadFile這個命令可以在上面一個命令里面得到
在此函數(shù)地址設(shè)一個軟件斷點:
bp下軟件斷點,bl列出斷點
讓軟件跑起來:g,可以看到程序加載模塊,然后就開始讀文件了,斷點就命中
命令k:可以查看因為什么命中!
利用反匯編指令繼續(xù)查看剛才那個斷點位置:
調(diào)試器隱藏了int 3,如果非要看,怎么看??
先繼續(xù)g一次,直到:(此時程序跑起來了)
再打開另外一個WinDbg:
以非入侵方式打開的話,只能進行只讀操作!(但是這樣夠了)
反匯編同一個地址:
注意:因為有 int 3指令,故后面的指令被打亂了
Add 的機器碼 00
小結(jié):
調(diào)試器讓一個程序g(運行)起來的時候,才能 把int 3寫到內(nèi)存里面, 當(dāng)程序執(zhí)行起來碰到這個斷點斷(break)下來的時候,把int 3恢復(fù)成原來的內(nèi)容 ,斷下來的時候看不到int 3,因為調(diào)試器把int 3替換掉的內(nèi)容又恢復(fù)回去,所以我們看到的是原來的指令,g起來的時候,再把這條指令(int 3)寫下去
(我的理解:一開始在某一處下了個軟件斷點,只有在調(diào)試運行的時候才將int 3指令寫到那個剛剛下斷點的地方, 然后程序執(zhí)行起來碰到cc就會斷下來(碰到cc就會觸發(fā)異常),調(diào)試器立即int 3替換掉的內(nèi)容又恢復(fù)回去,所以我們看到的是原來的指令,g起來的時候,再把這條指令(int 3)寫下去)
值得注意的是,內(nèi)存斷點被觸發(fā)后需要將EIP減1,還原內(nèi)存數(shù)據(jù),這樣做是為了什么呢?
E912345678這是一條跳轉(zhuǎn)指令,下了0xcc斷點后變成了這樣:
CC12345678,EIP不減1,CPU會從12345678處繼續(xù)執(zhí)行,這樣下去,指令全部亂套.所以要將EIP減1
將EIP減1后,CPU重CC處執(zhí)行,會繼續(xù)觸發(fā)異常,陷入死循環(huán),所以要將內(nèi)存數(shù)據(jù)還原
但是還原內(nèi)存數(shù)據(jù)之后,這個斷點下一次就不能使用了.
解決的辦法就是恢復(fù)數(shù)據(jù)后觸發(fā)TF斷點,在TF斷點觸發(fā)時將CC重新寫入.
舉個例子:
E9 12345678 ,在這個指令地方下個int 3斷點(軟件斷點)
將int 3指令寫入下斷點的地方,調(diào)試運行的時候剛執(zhí)行完cc這個字節(jié),就觸發(fā)了這個異常,停下來,EIP-1(為什么要減1,如果不減1的話,下次執(zhí)行就從12345678這里開始執(zhí)行了,顯然這是不行的!),同時,調(diào)試器立即int 3替換掉的內(nèi)容又恢復(fù)回去,運行起來的時候,再把這條指令(int 3)寫下去