理論:
在前面一章中,我們學(xué)會了如何裝載被調(diào)試的進(jìn)程以及如何處理進(jìn)程中發(fā)生的事件。為了有實際用途,我們的程序應(yīng)具有修改被調(diào)試程序的能力。有好幾個API函數(shù)用于這一目的。
ReadProcessMemory該函數(shù)允許你去讀指定的進(jìn)程的內(nèi)存。函數(shù)原型如下:
ReadProcessMemory proto hProcess:DWORD, lpBaseAddress:DWORD, lpBuffer:DWORD, nSize:DWORD, lpNumberOfBytesRead:DWORD
hProcess 待讀進(jìn)程的句柄.
lpBaseAddress 目標(biāo)進(jìn)程中待讀內(nèi)存起始地址。例如,如果你想要讀目標(biāo) 進(jìn)程中從地址401000h開始的4個字節(jié),該參數(shù)值應(yīng)置為401000h。
lpBuffer 接收緩沖區(qū)地址
nSize 想要讀的字節(jié)數(shù)。
lpNumberOfBytesRead 記錄實際讀取的字節(jié)數(shù)的變量地址。如果對這個值 不關(guān)心,填入NULL即可。
WriteProcessMemory 是對應(yīng)于ReadProcessMemory的函數(shù),通過它 可以寫目標(biāo)進(jìn)程的內(nèi)存。其參數(shù)和ReadProcessMemory 相同。
理解接下去的兩個函數(shù)需要一些進(jìn)程上下文的有關(guān)背景知識。在象Windows這樣的 多任務(wù)操作系統(tǒng)中,同一時間里可能運行著幾個程序。Windows分配給每個線程一個 時間片,當(dāng)時間片結(jié)束后,Windows將凍結(jié)當(dāng)前線程并切換到下一具有優(yōu)先級的 線程。在切換之前,Windows將保存當(dāng)前進(jìn)程的寄存器的 內(nèi)容,這樣當(dāng)在該線程再 次恢復(fù)運行時,Windows可以恢復(fù)最近一次線程運行的*環(huán)境*。保存的寄存器內(nèi)容總 稱為進(jìn)程上下文。
現(xiàn)在回到我們的主題。當(dāng)一個調(diào)試事件發(fā)生時,Windows暫停被調(diào)試進(jìn)程,并保存其 進(jìn)程上下文。由于進(jìn)程被暫停運行,我們可以確信其進(jìn)程上下文內(nèi)容將保持不變。 可以用GetThreadContext來獲取進(jìn)程上下文內(nèi)容,并且也可以用GetThreadContext 來修改進(jìn)程上下文內(nèi)容。
這兩個函數(shù)威力非凡。有了他們,對被調(diào)試進(jìn)程你就具有象VxD的能力: 如改變其寄 存器內(nèi)容,而在被調(diào)試程序恢復(fù)運行前,這些值將會寫回寄存器中。在進(jìn)程上下文中 所做的任何改動,將都會反映到被調(diào)試程序中。想象一下: 甚至可以改變eip寄存器 的內(nèi)容,這樣你可以讓程序運行到你想要的任何地方! 在正常情況下是不可能做到這 一點的。
GetThreadContext proto hThread:DWORD, lpContext:DWORD
hThread 你想要獲得上下文的線程句柄
lpContext 函數(shù)成功返回時用來保存上下文內(nèi)容的結(jié)構(gòu)指針。
SetThreadContext 參數(shù)相同。讓我們來看看上下文的結(jié)構(gòu):
CONTEXT STRUCT
ContextFlags dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_DEBUG_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
iDr0 dd ?
iDr1 dd ?
iDr2 dd ?
iDr3 dd ?
iDr6 dd ?
iDr7 dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_FLOATING_POINT,返回本部分
;-----------------------------------------------------------------------------------------------------------
FloatSave FLOATING_SAVE_AREA <>
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_SEGMENTS,返回本部分
;-----------------------------------------------------------------------------------------------------------
regGs dd ?
regFs dd ?
regEs dd ?
regDs dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_INTEGER,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEdi dd ?
regEsi dd ?
regEbx dd ?
regEdx dd ?
regEcx dd ?
regEax dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_CONTROL,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEbp dd ?
regEip dd ?
regCs dd ?
regFlag dd ?
regEsp dd ?
regSs dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_EXTENDED_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?) CONTEXT ENDS
可以看出,該結(jié)構(gòu)中的成員是對實際處理器的寄存器的模仿。在使用該結(jié)構(gòu)之前 要在ContextFlags 中指定哪些寄存器組用來讀寫。如要訪問所有的寄存器, 你可以置ContextFlags 為CONTEXT_FULL 。或者只訪問regEbp, regEip, regCs, regFlag, regEsp 或 regSs, 應(yīng)置ContextFlags 為 CONTEXT_CONTROL
在前面一章中,我們學(xué)會了如何裝載被調(diào)試的進(jìn)程以及如何處理進(jìn)程中發(fā)生的事件。為了有實際用途,我們的程序應(yīng)具有修改被調(diào)試程序的能力。有好幾個API函數(shù)用于這一目的。
ReadProcessMemory該函數(shù)允許你去讀指定的進(jìn)程的內(nèi)存。函數(shù)原型如下:
ReadProcessMemory proto hProcess:DWORD, lpBaseAddress:DWORD, lpBuffer:DWORD, nSize:DWORD, lpNumberOfBytesRead:DWORD
hProcess 待讀進(jìn)程的句柄.
lpBaseAddress 目標(biāo)進(jìn)程中待讀內(nèi)存起始地址。例如,如果你想要讀目標(biāo) 進(jìn)程中從地址401000h開始的4個字節(jié),該參數(shù)值應(yīng)置為401000h。
lpBuffer 接收緩沖區(qū)地址
nSize 想要讀的字節(jié)數(shù)。
lpNumberOfBytesRead 記錄實際讀取的字節(jié)數(shù)的變量地址。如果對這個值 不關(guān)心,填入NULL即可。
WriteProcessMemory 是對應(yīng)于ReadProcessMemory的函數(shù),通過它 可以寫目標(biāo)進(jìn)程的內(nèi)存。其參數(shù)和ReadProcessMemory 相同。
理解接下去的兩個函數(shù)需要一些進(jìn)程上下文的有關(guān)背景知識。在象Windows這樣的 多任務(wù)操作系統(tǒng)中,同一時間里可能運行著幾個程序。Windows分配給每個線程一個 時間片,當(dāng)時間片結(jié)束后,Windows將凍結(jié)當(dāng)前線程并切換到下一具有優(yōu)先級的 線程。在切換之前,Windows將保存當(dāng)前進(jìn)程的寄存器的 內(nèi)容,這樣當(dāng)在該線程再 次恢復(fù)運行時,Windows可以恢復(fù)最近一次線程運行的*環(huán)境*。保存的寄存器內(nèi)容總 稱為進(jìn)程上下文。
現(xiàn)在回到我們的主題。當(dāng)一個調(diào)試事件發(fā)生時,Windows暫停被調(diào)試進(jìn)程,并保存其 進(jìn)程上下文。由于進(jìn)程被暫停運行,我們可以確信其進(jìn)程上下文內(nèi)容將保持不變。 可以用GetThreadContext來獲取進(jìn)程上下文內(nèi)容,并且也可以用GetThreadContext 來修改進(jìn)程上下文內(nèi)容。
這兩個函數(shù)威力非凡。有了他們,對被調(diào)試進(jìn)程你就具有象VxD的能力: 如改變其寄 存器內(nèi)容,而在被調(diào)試程序恢復(fù)運行前,這些值將會寫回寄存器中。在進(jìn)程上下文中 所做的任何改動,將都會反映到被調(diào)試程序中。想象一下: 甚至可以改變eip寄存器 的內(nèi)容,這樣你可以讓程序運行到你想要的任何地方! 在正常情況下是不可能做到這 一點的。
GetThreadContext proto hThread:DWORD, lpContext:DWORD
hThread 你想要獲得上下文的線程句柄
lpContext 函數(shù)成功返回時用來保存上下文內(nèi)容的結(jié)構(gòu)指針。
SetThreadContext 參數(shù)相同。讓我們來看看上下文的結(jié)構(gòu):
CONTEXT STRUCT
ContextFlags dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_DEBUG_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
iDr0 dd ?
iDr1 dd ?
iDr2 dd ?
iDr3 dd ?
iDr6 dd ?
iDr7 dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_FLOATING_POINT,返回本部分
;-----------------------------------------------------------------------------------------------------------
FloatSave FLOATING_SAVE_AREA <>
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_SEGMENTS,返回本部分
;-----------------------------------------------------------------------------------------------------------
regGs dd ?
regFs dd ?
regEs dd ?
regDs dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_INTEGER,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEdi dd ?
regEsi dd ?
regEbx dd ?
regEdx dd ?
regEcx dd ?
regEax dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_CONTROL,返回本部分
;-----------------------------------------------------------------------------------------------------------
regEbp dd ?
regEip dd ?
regCs dd ?
regFlag dd ?
regEsp dd ?
regSs dd ?
;----------------------------------------------------------------------------------------------------------
;當(dāng)ContextFlags包含CONTEXT_EXTENDED_REGISTERS,返回本部分
;-----------------------------------------------------------------------------------------------------------
ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?) CONTEXT ENDS
可以看出,該結(jié)構(gòu)中的成員是對實際處理器的寄存器的模仿。在使用該結(jié)構(gòu)之前 要在ContextFlags 中指定哪些寄存器組用來讀寫。如要訪問所有的寄存器, 你可以置ContextFlags 為CONTEXT_FULL 。或者只訪問regEbp, regEip, regCs, regFlag, regEsp 或 regSs, 應(yīng)置ContextFlags 為 CONTEXT_CONTROL