一、Windows API
1誓焦、進(jìn)程
創(chuàng)建進(jìn)程
參考文檔:CreateProcess
注意:本章內(nèi)容所有使用的函數(shù)都是Windows的內(nèi)核函數(shù)對(duì)編碼要求比較嚴(yán)格,因?yàn)閜ython使用的是kernel32.dll動(dòng)態(tài)鏈接庫里導(dǎo)出的函數(shù)涯雅,在kernel32.dll里沒有CreateProcess()這個(gè)函數(shù)外构,只有CreateProcessA()跟CreateProcessW()。
/***ANSI版本***/
BOOL WINAPI CreateProcessA(
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL blnheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo, //ANSI版本
LPPROCESS_INFORMATION lpProcessInformation
);
/***Unicode版本***/
BOOL WINAPI CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEvironment,
LPCWSTR lpCurrentDirectory
LPSTARTUPINFOW lpStartupInfo, //Unicode版本
LPPROCESS_INFORMATION lpProcessInformation
);
窗口狀態(tài)
參考文檔:STARTUPINFO
typedef struct _STARTUPINFO {
//ANSI版本:STARTUPINFOA
//Unicode版本:STARTUPINFOW
DWORD cb;
LPTSTR lpReserved; //ANSI版本嗜闻;Unicode版本 LPTWSTR lpReserved
LPTSTR lpDesktop; //ANSI版本蜕依;Unicode版本 LPTWSTR lpDesktop
LPTSTR lpTitle; //ANSI版本;Unicode版本 LPTWSTR lpTitle
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
}STARTUPINFO, *LPSTARTUPINFO;
進(jìn)程信息
參考文檔:PROCESS_INFORMATION
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
獲取進(jìn)程句柄
參考文檔:OpenProcess
HANDLE WINAPI OpenProcess(
DWORD dwDesiredAccess, //該參數(shù)用于表示向目標(biāo)進(jìn)程對(duì)象索要的訪問權(quán)限
BOOL bInheritHadnle,
DWORD dwProcessId,
);
附加進(jìn)程
參考文檔:DebugActiveProcess
BOOL WINAPI DebugActiveProcess(
DWORD dwProcessId //進(jìn)程的PID
);
調(diào)試事件
參考文檔:WaitForDebugEvent
BOOL WINAPI WaitForDebugEvent(
LPDEBUG_EVENT lpDebugEvent,
DWORD dwMilliseconds
);
恢復(fù)進(jìn)程
參考文檔:ContinueDebugEvent
BOOL WINAPI ContinueDebugEvent(
DWORD dwProcessId,
DWORD dwThreadId,
DWORD dwContinueStatus
);
分離進(jìn)程
參考文檔:DebugActiveProcessStop
BOOL WINAPI DebugActiveProcessStop(
DWORD dwProcessId
);
調(diào)試事件的信息
參考文檔:DEBUG_EVENT
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode; //事件碼
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
CREATE_THREAD_DEBUG_INFO CreateThread;
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
EXIT_THREAD_DEBUG_INFO ExitThread;
EXIT_PROCESS_DEBUG_INFO ExitProcess;
LOAD_DLL_DEBUG_INFO LoadDll;
UNLOAD_DLL_DEBUG_INFO UnloadDll;
OUTPUT_DEBUG_STRING_INFO DebugString;
RIP_INFO RipInfo;
} u;
} DEBUG_EVENT, *LPDEBUG_EVENT;
2琉雳、異常
參考文檔:EXCEPTION_DEBUG_INFO
typedef struct _EXCEPTION_DEBUG_INFO {
EXCEPTION_RECORD ExceptionRecord;
DWORD dwFirstChance;
} EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO;
參考文檔:EXCEPTION_RECORD
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //異常狀態(tài)碼样眠,參考GetExceptionCode函數(shù)
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD, *PEXCEPTION_RECORD;
3、線程
獲取線程
參考文檔:OpenThread
HANDLE WINAPI OpenThread(
DWORD dwDesiredAccess, //設(shè)置線程訪問權(quán)限
BOOL bInheritHandle,
DWORD dwThreadId
);
枚舉線程
參考文檔:CreateToolhelp32Snapshot
HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, //獲取信息的確切類型(進(jìn)程類型翠肘,線程類型檐束,模塊類型,堆類型)
DWORD th32ProcessID
);
線程篩選
參考文檔:Thread32First
BOOL WINAPI Thread32First(
HANDLE hSnapshot,
LPTHREADENTRY32 lpte //THREADENTRY32結(jié)構(gòu)體
);
參考文檔:Thread32Next
BOOL WINAPI Thread32Next(
HANDLE hSnapshot,
LPTHREADENTRY32 lpte //THREADENTRY32結(jié)構(gòu)體
);
參考文檔:THREADENTRY32
typedef struct tagTHREADENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID;
DWORD th32OwnerProcessID;
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
} THREADENTRY32, *PTHREADENTRY32;
4束倍、CONTEXT
參考文檔:CONTEXT
//CONTEXT
//由于不同處理器的CONTEXT是不同的被丧,MSDN官方?jīng)]有給出具體的結(jié)構(gòu)盟戏。
//有安裝VC,或者VisualStudio的可以轉(zhuǎn)到定義甥桂。查看具體結(jié)構(gòu)柿究。
//x86處理器的CONTEXT結(jié)構(gòu)體
typedef struct _CONTEXT {
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; //MAXIMUM_SUPPORTED_EXTENSION = 512
} CONTEXT;
//ContextFlags
CONTEXT_CONTROL = 0x00010001 //SS:SP, CS:IP, FLAGS, BP
CONTEXT_INTEGER = 0x00010002 //AX, BX, CX, DX, SI, DI
CONTEXT_SEGMENTS = 0x00010004 //DS, ES, FS, GS
CONTEXT_FLOATING_POINT = 0x00010008
CONTEXT_DEBUG_REGISTERS = 0x00010010 //DB 0-3,6,7
CONTEXT_EXTENDED_REGISTERS = 0x00010020
CONTEXT_FULL = 0x00010007 //CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS)
獲取線程的CONTEXT
參考文檔:GetThreadContext
BOOL WINAPI GetThreadContext(
HANDLE hThread,
LPCONTEXT lpContext
);
設(shè)置線程的CONTEXT
參考文檔:SetThreadContext
BOOL WINAPI SetThreadContext(
HANDLE hThread,
const CONTEXT *lpContext
);
5、內(nèi)存操作
參考文檔:ReadProcessMemory
BOOL WINAPI ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesRead
);
參考文檔:WriteProcessMemory
BOOL WINAPI WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);
獲取函數(shù)虛擬內(nèi)存地址
參考文檔:GetProcAddress
FARPROC WINAPI GetProcAddress(
HMODULE hModule,
LPCSTR lpProcName
);
獲取函數(shù)所在模塊(.dll或.exe文件)
參考文檔:[GetModuleHandle][1] [CloseHandle][2]
[1]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms683199(v=vs.85).aspx
[2]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms724211(v=vs.85).aspx
/***ANSI版本***/
HMODULE WINAPI GetModuleHandleA(
LPCSTR lpModuleName
);
/***Unicode版本***/
HMODULE WINAPI GetModuleHandleW(
LPCWSTR lpModuleName
);
//使用完該函數(shù)后記得調(diào)用CloseHandle()關(guān)閉句柄黄选,避免內(nèi)核對(duì)象泄漏(句柄泄漏)
BOOL WINAPI CloseHandle(
HANDLE hObject
);
獲取系統(tǒng)信息
參考文檔:[GetSystemInfo][3] [SYSTEM_INFO][4]
[3]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms724381(v=vs.85).aspx
[4]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms724958(v=vs.85).aspx
void WINAPI GetSystemInfo(
LPSYSTEM_INFO lpSystemInfo //SYSTEM_INFO結(jié)構(gòu)體
);
typedef struct _SYSTEM_INFO {
union {
DWORD dwOemId;
struct {
WORD wProcessorArchitecture;
WORD wReserved;
};
};
DWORD dwPageSize; //系統(tǒng)默認(rèn)內(nèi)存頁大小
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
} SYSTEM_INFO;
獲取指定進(jìn)程內(nèi)存頁信息
參考文檔:[VirtualQueryEx][5] [MEMORY_BASIC_INFORMATION][6] [VirtualProtectEx][7]
[5]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366907(v=vs.85).aspx
[6]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366775(v=vs.85).aspx
[7]: https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366899(v=vs.85).aspx
SIZE_T WINAPI VirtualQueryEx(
HANDLE hProcess,
LPCVOID lpAddress,
PMEMORY_BASIC_INFORMATION lpBuffer,
SIZE_T dwLength
);
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
SIZE_T RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
BOOL WINAPI VirtualProtectEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect, //Memory Protection Constants
PDWORD lpflOldProtect
);
二蝇摸、各種系統(tǒng)宏定義
1、進(jìn)程權(quán)限
參考文檔:Process Access Rights
//Process Access Rights
DELETE = 0x00010000
READ_CONTROL = 0x00020000
WRITE_OWNER = 0x00080000
SYNCHRONIZE = 0x00100000
PROCESS_CREATE_PROCESS = 0x0080
PROCESS_CREATE_THREAD = 0x0002
PROCESS_DUP_HANDLE = 0x0040
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
PROCESS_SET_INFORMATION = 0x0200
PROCESS_SET_QUOTA = 0x0100
PROCESS_SUSPEND_RESUME = 0x0800
PROCESS_TERMINATE = 0x0001
PROCESS_VM_OPERATION = 0x0008
PROCESS_VM_READ = 0x0010
PROCESS_VM_WRITE = 0x0020
PROCESS_ALL_ACCESS = 0x001F0FFF //該值表示目標(biāo)進(jìn)程的所有訪問權(quán)限办陷,其值為以上權(quán)限的其他值取位或運(yùn)算得來貌夕。
2、線程權(quán)限
參考文檔:Thread Access Rights
//Thread Access Rights
DELETE = 0x00010000
READ_CONTROL = 0x00020000
WRITE_DAC = 0x00040000
WRITE_OWNER = 0x00080000
SYNCHRONIZE = 0x00100000
THREAD_DIRECT_IMPERSONATION = 0x0200
THREAD_GET_CONTEXT = 0x0008
THREAD_IMPERSONATE = 0x0100
THREAD_QUERY_INFORMATION = 0x0040
THREAD_QUERY_LIMITED_INFORMATION = 0x0800
THREAD_SET_CONTEXT = 0x0010
THREAD_SET_INFORMATION = 0x0020
THREAD_SET_LIMITED_INFORMATION = 0x0400
THREAD_SET_THREAD_TOKEN = 0x0080
THREAD_SUSPEND_RESUME = 0x0002
THREAD_TERMINATE = 0x0001
THREAD_ALL_ACCESS = 0X001F03FF //包括以上所有訪問權(quán)限民镜。
3啡专、調(diào)試狀態(tài)
//ContinueStatus
DBG_CONTINUE = 0x00010002
DBG_EXCEPTION_NOT_HANDLE = 0x80010001
4、DEBUG_EVENT事件碼
//參考DEBUG_EVENT結(jié)構(gòu)體
EXCEPTION_DEBUG_EVENT = 0x1
CREATE_THREAD_DEBUG_EVENT = 0x2
CREATE_PROCESS_DEBUG_EVENT = 0x3
EXIT_THREAD_DEBUG_EVENT = 0x4
EXIT_PROCESS_DEBUG_EVENT = 0x5
LOAD_DLL_DEBUG_EVENT = 0x6
UNLOAD_DLL_DEBUG_EVENT = 0x7
OUTPUT_DEBUG_STRING_EVENT = 0x8
RIP_EVENT = 0x9
5制圈、信息類型
//參考API函數(shù)CreateToolhelp32Snapshot
TH32CS_INHERIT = 0x80000000
TH32CS_SNAPHEAPLIST = 0x00000001
TH32CS_SNAPMODULE = 0x00000008
TH32CS_SNAPMODULE32 = 0x00000010
TH32CS_SNAPPROCESS = 0x00000002
TH32CS_SNAPTHREAD = 0x00000004
TH32CS_SNAPALL = 0x0000000F //包含進(jìn)程類型们童、線程類型、模塊類型离唐、堆類型病附。
6、內(nèi)存保護(hù)頁狀態(tài)
參考文檔: Momery Protection Constants
三亥鬓、異常事件處理
四完沪、斷點(diǎn)設(shè)置
1、軟斷點(diǎn)
當(dāng)調(diào)試器被告知需要在某一個(gè)內(nèi)存地址上設(shè)置一個(gè)斷點(diǎn)時(shí)嵌戈,調(diào)試器將首先讀取位于這個(gè)內(nèi)存地址上的第一個(gè)操作碼字節(jié)并將器存儲(chǔ)在斷點(diǎn)列表中覆积,接著調(diào)試器將字節(jié)0xCC寫入那個(gè)內(nèi)存地址取代值。當(dāng)cpu試圖執(zhí)行操作碼0xCC時(shí)熟呛,將觸發(fā)一個(gè)斷點(diǎn)事件(INT3事件)宽档,二調(diào)試器將最終捕獲這個(gè)事件。接著調(diào)試器將檢查指令指針是否正指向一個(gè)此前被我們設(shè)置了斷點(diǎn)的內(nèi)存地址庵朝,如果這個(gè)地址在調(diào)試器內(nèi)部的斷點(diǎn)列表中被查找到吗冤,那么調(diào)試器會(huì)將之前存儲(chǔ)的字節(jié)數(shù)據(jù)寫回此內(nèi)存地址中,這樣當(dāng)次進(jìn)程恢復(fù)執(zhí)行后九府,正確的指令操作碼將被執(zhí)行椎瘟。
//本文使用Python3.5版本
//myDebugger.py
//讀取內(nèi)存
def read_process_memory(self, address, length):
data = b"" //初始化字節(jié)碼
read_buf = create_string_buffer(length)
count = c_ulong(0)
if not kernel32.ReadProcessMemory(self.h_process, address, read_buf, length, byref(count)):
print("read_process_memory fail.")
return False
else:
data += read_buf.raw
return data
//寫入內(nèi)存
def write_process_memory(self, address, data):
count = c_ulong(0)
length = len(data)
c_data = c_char_p((data[count.value:]))
if not kernel32.WriteProcessMemory(self.h_process, address, c_data, length , byref(count)):
return False
else:
return True
//設(shè)置軟斷點(diǎn),讀取目標(biāo)進(jìn)程內(nèi)存地址第一個(gè)操作碼字節(jié)侄旬,修改為“\xCC”
def bp_set_sw(self, address):
if not address in self.software_breakpoints.keys():
try:
original_byte = self.read_process_memory(str(address), 2) //為了方便比較這里設(shè)置讀取2個(gè)字節(jié)
print("read process memory %s" %original_byte)
//寫入一個(gè)INT3中斷指令肺蔚,操作碼值為"\xCC"
res = self.write_process_memory(str(address), b"\xCC") //第二個(gè)參數(shù)傳遞的是字節(jié)類型
if res:
print("Write success.")
print("New memory %s"%self.read_process_memory(str(address),2)) //為了方便比較這里設(shè)置讀取2個(gè)字節(jié)
else:
print(kernel32.GetLastError())
print("Write fail.")
//將設(shè)置的斷點(diǎn)記錄進(jìn)斷點(diǎn)列表中
self.software_breakpoints[address] = (str(address), original_byte)
except:
print("bp_set fail.")
print(kernel32.GetLastError()) //返回異常代碼值
return False
return True
2、硬件斷點(diǎn)
通過使用位于cpu上的調(diào)試寄存器來實(shí)現(xiàn)儡羔。一般cpu上有8個(gè)調(diào)試寄存器(DR0到DR7)宣羊。DR0到DR3用于存儲(chǔ)所設(shè)硬件斷點(diǎn)的內(nèi)存地址璧诵。任何時(shí)刻最多只能使用4個(gè)硬件斷點(diǎn)。DR4和DR5保留使用仇冯。DR6為調(diào)試狀態(tài)寄存器之宿,記錄上一次斷點(diǎn)觸發(fā)所產(chǎn)生的調(diào)試事件類型信息。DR7是硬件斷點(diǎn)的激活開關(guān)赞枕,同時(shí)存儲(chǔ)各個(gè)斷點(diǎn)觸發(fā)條件信息澈缺。通過設(shè)置DR7特定標(biāo)記位,可以設(shè)置斷點(diǎn)的觸發(fā)條件:
- 當(dāng)位于一個(gè)特定內(nèi)存地址上的指令被執(zhí)行時(shí)觸發(fā)斷點(diǎn)(00 ——執(zhí)行斷點(diǎn)炕婶,斷點(diǎn)長度標(biāo)記1字節(jié))
- 當(dāng)數(shù)據(jù)被寫入一個(gè)特定內(nèi)存地址是觸發(fā)斷點(diǎn)(01——數(shù)據(jù)寫入斷點(diǎn),斷點(diǎn)長度標(biāo)志2字節(jié) WORD)
- 當(dāng)數(shù)據(jù)被讀出或?qū)懭耄ú话▓?zhí)行)一個(gè)特定非可執(zhí)行內(nèi)存地址是觸發(fā)斷點(diǎn)莱预。(11——數(shù)據(jù)讀寫(但非執(zhí)行)斷點(diǎn)柠掂,斷點(diǎn)長度標(biāo)記4字節(jié) DWORD)
硬件斷點(diǎn)使用1號(hào)中斷(INT1),INT1事件被用于硬件斷點(diǎn)和單步事件依沮。
在cpu試圖執(zhí)行一條指令前涯贞,首先檢查當(dāng)前指令所在的地址是否被設(shè)置了有效的硬件斷點(diǎn),除此之外危喉,cpu還會(huì)檢查當(dāng)前指令包含的操作數(shù)是否位于設(shè)置了硬件斷點(diǎn)的內(nèi)存地址上宋渔。如果以上的內(nèi)存地址被存儲(chǔ)在DR0-DR3中任意的一個(gè)調(diào)試寄存器中,并且滿足之前所設(shè)定的讀辜限、寫或者執(zhí)行條件皇拣,那么cpu暫停并觸發(fā)一個(gè)INT1事件。如果相關(guān)的內(nèi)存地址并沒有存儲(chǔ)在任何一個(gè)調(diào)試寄存器中薄嫡,cpu在執(zhí)行完當(dāng)前的指令后將繼續(xù)執(zhí)行下一條指令并且同樣進(jìn)行斷點(diǎn)檢測氧急,依次往復(fù)。
//myDebugger.py
//設(shè)置硬件斷點(diǎn)
def bp_set_hw(self, address, length, condition):
//檢查硬件斷點(diǎn)的長度是否有效
if length not in (1, 2, 4):
return False
else:
length -= 1
//檢查硬件斷點(diǎn)的觸發(fā)條件是否有效
if condition not in (HW_ACCESS, HW_EXECUTE, HW_WRITE):
//這里的condition其實(shí)就是調(diào)試寄存器DR7的標(biāo)志位毫深,
//HW_ACCESS = 0x00000003, HW_EXECUTE = 0x00000000, HW_WRITE = 0x00000001
return False
//檢查是否存在空置的調(diào)試寄存器
if not 0 in self.hardware_breakpoints.keys():
//Python3.x 的版本沒有has_keys()吩坝,本文所有用到該函數(shù)的都改用in代替
//Python2.x 的寫法:if not self.hardware_breakpoints.has_keys(0):
available = 0
elif not 1 in self.hardware_breakpoints.keys():
available =1
elif not 2 in self.hardware_breakpoints.keys():
available = 2
elif not 3 in self.hardware_breakpoints.keys():
available = 3
else:
return False
//在每個(gè)線程環(huán)境下設(shè)置調(diào)試寄存器
for thread_id in self.enumerate_threads():
context = self.get_thread_context(thread_id=thread_id)
context.Dr7 |= 1 << (available * 2) //設(shè)置DR7中相應(yīng)的標(biāo)志位,來激活斷點(diǎn)
//在空置的寄存器下寫入斷點(diǎn)地址
if available == 0:
context.Dr0 = address
elif available == 1:
context.Dr1 = address
elif available == 2:
context.Dr2 = address
elif available == 3:
context.Dr3 = address
//設(shè)置硬件斷點(diǎn)觸發(fā)條件
context.Dr7 |= condition << ((available * 4) + 16)
//設(shè)置硬件斷點(diǎn)長度
context.Dr7 |= length << ((available * 4) + 18)
//提交改動(dòng)后線程上下文環(huán)境信息
h_thread = self.open_thread(thread_id)
kernel32.SetThreadContext(h_thread, byref(context))
//更新內(nèi)部硬件斷點(diǎn)列表
self.hardware_breakpoints[available] = (str(address), length, condition)
return True
//執(zhí)行單步事件處理
def exception_handler_single_step(self):
//摘自PyDbg源碼中的一段注釋:
//判斷該單步事件是否有一個(gè)硬件斷點(diǎn)所觸發(fā)哑蔫,若是則捕獲這個(gè)斷點(diǎn)事件钉寝,根據(jù)Intel給出的文檔
//應(yīng)當(dāng)能夠通過檢測調(diào)試寄存器Dr6上的BS標(biāo)志位來判斷出該單步事件的觸發(fā)原因,然而Windows
//系統(tǒng)似乎并沒有正確地將這個(gè)標(biāo)志位傳遞過來闸迷。
if self.context.Dr6 &0x01 and 0 in self.hardware_breakpoints.keys():
slot = 0
elif self.context.Dr6 &0x02 and 1 in self.hardware_breakpoints.keys():
slot = 1
elif self.context.Dr6 &0x04 and 2 in self.hardware_breakpoints.keys():
slot = 2
elif self.context.Dr6 &0x08 and 3 in self.hardware_breakpoints.keys():
slot = 3
else:
//此次INT1中斷不是由硬件斷點(diǎn)觸發(fā)
continue_status = DBG_EXCEPTION_NOT_HANDLED
return continue_status
//從列表中移除斷點(diǎn)
if self.bp_del_hw(slot):
continue_status = DBG_CONTINUE
print("[*] Hardware breakpoint removed.")
return continue_status
//刪除硬件斷點(diǎn)
def bp_del_hw(self, slot):
//移除所有線程的硬件斷點(diǎn)
for thread_id in self.enumerate_threads():
context = self.get_thread_context(thread_id=thread_id)
//重新設(shè)置DR7調(diào)試標(biāo)志位
context.Dr7 &= ~(1 << (slot * 2))
//清零斷點(diǎn)地址
if slot == 0:
context.Dr0 = 0x00000000
elif slot == 1:
context.Dr1 = 0x00000000
elif slot == 2:
context.Dr2 = 0x00000000
elif slot == 3:
context.Dr3 = 0x00000000
else:
return False
//清空斷點(diǎn)觸發(fā)條件標(biāo)志位
context.Dr7 &= ~(3 << ((slot * 4) + 16))
//清空斷點(diǎn)長度標(biāo)志位
context.Dr7 &= ~(3 << ((slot * 4) + 18))
//提交移除斷點(diǎn)后的線程上下文環(huán)境信息
h_thread = self.open_thread(thread_id)
kernel32.SetThreadContext(h_thread, byref(context))
//從內(nèi)部斷點(diǎn)列表中移除硬件斷點(diǎn)
del self.hardware_breakpoints[slot]
return True
3嵌纲、內(nèi)存斷點(diǎn)
當(dāng)一個(gè)調(diào)試器設(shè)置一個(gè)內(nèi)存斷點(diǎn)時(shí),調(diào)試器實(shí)質(zhì)上所做的是改變一個(gè)內(nèi)存區(qū)域或一個(gè)內(nèi)存頁的訪問權(quán)限稿黍。當(dāng)cpu視圖訪問者一內(nèi)存區(qū)域時(shí)疹瘦,便會(huì)觸發(fā)GUARD_PAGE_EXCEPTION異常事件。
//myDebugger.py
//設(shè)置內(nèi)存斷點(diǎn)
def bp_set_mem(self, address, size):
mbi = MEMORY_BASIC_INFORMATION()
//判斷是否能獲取一個(gè)完整的MEMORY_BASIC_INFORMATION結(jié)構(gòu)體巡球,否則返回false
if kernel32.VirtualQueryEx(self.h_process, address, byref(mbi), sizeof(mbi)) < sizeof(mbi):
return False
current_page = mbi.BaseAddress
//對(duì)整個(gè)內(nèi)存斷點(diǎn)區(qū)域所覆蓋的所有內(nèi)存頁進(jìn)行設(shè)置訪問權(quán)限
while current_page <= address + size:
//將這個(gè)內(nèi)存頁記錄在列表中言沐,以便于將這些保護(hù)頁與操作系統(tǒng)或debuge進(jìn)程自設(shè)的保護(hù)頁區(qū)別開來
self.guarded_pages.append(current_page)
old_protection = c_ulong(0)
if not kernel32.VirtualProtectEx(self.h_process, current_page, size,
mbi.Protect | PAGE_GUARD, byref(old_protection)):
return False
//以系統(tǒng)所設(shè)置的內(nèi)存頁大小作為增長單位邓嘹,遞增內(nèi)存斷點(diǎn)區(qū)域
current_page += self.page_size
//將該內(nèi)存斷點(diǎn)記錄進(jìn)全局列表中
self.memory_breakpoints[address] = (address, size, mbi)
return True
五、功能整合
//myDebugDefines.py
from ctypes import *
BYTE = c_ubyte
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
PVOID = c_void_p
LPVOID = c_void_p
UINT_PTR = c_ulong
SIZE_T = c_ulong
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010
PROCESS_ALL_ACCESS = 0x001F0FFF
INFINITE = 0xFFFFFFFF
DBG_CONTINUE = 0x00010002
DBG_EXCEPTION_NOT_HANDLED = 0x80010001
EXCEPTION_DEBUG_EVENT = 0x01
CREATE_THREAD_DEBUG_EVENT = 0x02
CREATE_PROCESS_DEBUG_EVENT = 0x03
EXIT_THREAD_DEBUG_EVENT = 0x04
EXIT_PROCESS_DEBUG_EVENT = 0x05
LOAD_DLL_DEBUG_EVENT = 0x06
UNLOAD_DLL_DEBUG_EVENT = 0x07
OUTPUT_DEBUG_STRING_EVENT = 0x08
RIP_EVENT = 0x09
EXCEPTION_ACCESS_VIOLATION = 0xC0000005
EXCEPTION_BREAKPOINT = 0x80000003
EXCEPTION_GUARD_PAGE = 0x80000001
EXCEPTION_SINGLE_STEP = 0x80000004
TH32CS_SNAPTHREAD = 0x00000004
THREAD_ALL_ACCESS = 0x001F03FF
CONTEXT_FULL = 0x00010007
CONTEXT_DEBUG_REGISTERS = 0x00010010
HW_ACCESS = 0x00000003
HW_EXECUTE = 0x00000000
HW_WRITE = 0x00000001
PAGE_GUARD = 0x00000100
class STARTUPINFO(Structure):
_fields_ = [
("cb", DWORD),
("lpReserved", LPTSTR),
("lpDesktop", LPTSTR),
("lpTitle", LPTSTR),
("dwX", DWORD),
("dwY", DWORD),
("dwXSize", DWORD),
("dwYSize", DWORD),
("dwXCountChars", DWORD),
("dwYCountChars", DWORD),
("dwFillAttribute", DWORD),
("dwFlags", DWORD),
("wShowWindow", WORD),
("cbReserved2",WORD),
("lpReserved2", LPTSTR),
("hStdInput", HANDLE),
("hStdOutput", HANDLE),
("hStdError", HANDLE),
]
class PROCESS_INFORMATION(Structure):
_fields_ = [
("hProcess", HANDLE),
("hThread", HANDLE),
("dwProcessId", DWORD),
("dwThreadId", DWORD),
]
class EXCEPTION_RECORD(Structure):
pass
EXCEPTION_RECORD._fields_ = [
("ExceptionCode", DWORD),
("ExceptionFlags", DWORD),
("ExceptionRecord", POINTER(EXCEPTION_RECORD)),
("ExceptionAddress", PVOID),
("NumberParameters", DWORD),
("ExceptionInformation", UINT_PTR*15),
]
class EXCEPTION_DEBUG_INFO(Structure):
_fields_ = [
("ExceptionRecord", EXCEPTION_RECORD),
("dwFirstChance", DWORD),
]
class DEBUG_EVENT_UNION(Union):
_fields_ = [
("Exception", EXCEPTION_DEBUG_INFO),
## ("CreateThread", CREATE_THREAD_DEBUG_INFO),
]
class DEBUG_EVENT(Structure):
_fields_ = [
("dwDebugEventCode", DWORD),
("dwProcessId", DWORD),
("dwThreadId", DWORD),
("u", DEBUG_EVENT_UNION),
]
class THREADENTRY32(Structure):
_fields_ = [
("dwSize", DWORD),
("cntUsage", DWORD),
("th32ThreadID", DWORD),
("th32OwnerProcessID", DWORD),
("tpBasePri", DWORD),
("tpDeltaPri", DWORD),
("dwFlags", DWORD)
]
class FLOATING_SAVE_AREA(Structure):
_fields_ = [
("ControlWord", DWORD),
("StatusWord", DWORD),
("TagWord", DWORD),
("ErrorOffset", DWORD),
("ErrorSelector", DWORD),
("DataOffset", DWORD),
("DataSelector", DWORD),
("RegisterArea", BYTE * 80),
("Cr0NpxState", DWORD),
]
class CONTEXT(Structure):
_fields_ = [
("ContextFlags", DWORD),
("Dr0", DWORD),
("Dr1", DWORD),
("Dr2", DWORD),
("Dr3", DWORD),
("Dr6", DWORD),
("Dr7", DWORD),
("FloatSave", FLOATING_SAVE_AREA),
("SegGs", DWORD),
("SegFs", DWORD),
("SegEs", DWORD),
("SegDs", DWORD),
("Edi", DWORD),
("Esi", DWORD),
("Ebx", DWORD),
("Edx", DWORD),
("Ecx", DWORD),
("Eax", DWORD),
("Ebp", DWORD),
("Eip", DWORD),
("SegCs", DWORD),
("EFlags", DWORD),
("Esp", DWORD),
("SegSs", DWORD),
("ExtendedRegisters", BYTE * 512),
]
class PROC_STRUCT(Structure):
_fields_ = [
("wProcessorArchitecture", WORD),
("wReserved", WORD),
]
class SYSTEM_INFO_UNION(Union):
_fields_ = [
("dsOemId", DWORD),
("sProcStruc", PROC_STRUCT),
]
class SYSTEM_INFO(Structure):
_fields_ = [
("uSysInfo", SYSTEM_INFO_UNION),
("dwPageSize", DWORD),
("lpMinimumApplicationAddress", LPVOID),
("lpMaximumApplicationAddress", LPVOID),
("dwActiveProcessMask", DWORD),
("dwNumberOfProcessors", DWORD),
("dwProcessorType", DWORD),
("dwAllocationGranularity", DWORD),
("wProcessorLevel", WORD),
("wProcessorRevision", WORD),
]
class MEMORY_BASIC_INFORMATION(Structure):
_fields_ = [
("BaseAddress", PVOID),
("AllocationBase", PVOID),
("AllocationProtect", DWORD),
("RegionSize",SIZE_T),
("State", DWORD),
("Protect", DWORD),
("Type", DWORD),
]
//myDebugger.py
//完整代碼
//定義debugger類
class debugger():
def __init__(self):
self.h_process = None
self.h_thread = None
self.pid = None
self.debugger_active = False
self.context = None
self.exception = None
self.exception_address = None
self.software_breakpoints = {}
self.hardware_breakpoints = {}
self.memory_breakpoints = {}
self.first_breakpoints = True
system_info = SYSTEM_INFO()
kernel32.GetSystemInfo(byref(system_info))
self.page_size = system_info.dwPageSize
self.guarded_pages = []
def load(self, path_to_exe):
//參數(shù)dwCreationFlags中的標(biāo)志位控制著進(jìn)程的創(chuàng)建方式险胰。
//以下參數(shù)初始化可以參考CreateProcess函數(shù)
creation_flags = DEBUG_PROCESS
//實(shí)例化之前定義的結(jié)構(gòu)體
startupinfo = STARTUPINFO()
process_information = PROCESS_INFORMATION()
//以下兩個(gè)成員變量的共同作用下汹押,新建進(jìn)程將在一個(gè)單獨(dú)的窗體中被顯示,也可以通過改變
//結(jié)構(gòu)體STARTUPINFO中的各成員變量的值來控制debugee進(jìn)程的行為起便。
startupinfo.dwFlags = 0x1
startupinfo.wShowWindow = 0x0
//設(shè)置結(jié)構(gòu)體STARTUPINFO中的成員變量cb的值棚贾,用以表示結(jié)構(gòu)體本身的大小
startupinfo.cb = sizeof(startupinfo)
if windll.kernel32.CreateProcessW(path_to_exe,
None, None, None, None,
creation_flags,
None, None,
byref(startupinfo),
byref(process_information)):
//注意print的規(guī)范,python3.x是帶有小括號(hào)的榆综,python2.x沒有小括號(hào)妙痹。
print("[*] We have successfully launched the process!")
print("[*] PID : %d" %process_information.dwProcessId)
//保存一個(gè)指向新建進(jìn)程的有效句柄,以供后續(xù)的進(jìn)程訪問所使用鼻疮。
self.h_process = self.open_process(process_information.dwProcessId)
else:
print("[*] Error : 0x%08x." %kernel32.GetLastError())
//打開指定pid的進(jìn)程
def open_process(self, pid):
h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
print(type(h_process))
return h_process
//附加進(jìn)程
def attach(self, pid):
self.h_process = self.open_process(pid)
//試圖附加到目標(biāo)進(jìn)程怯伊,若附加操作失敗,則輸出提示信息后返回
print("attach after h_process: 0x%08x." %self.h_process)
if kernel32.DebugActiveProcess(pid):
self.debugger_active = True
self.pid = int(pid)
##self.run()
else:
print("[*] Unable to attach to the process.")
def run(self):
//等待發(fā)生在debugee進(jìn)程中的調(diào)試事件
while self.debugger_active == True:
self.get_debug_event()
//獲取調(diào)試事件
def get_debug_event(self):
debug_event = DEBUG_EVENT()
continue_status = DBG_CONTINUE
if kernel32.WaitForDebugEvent(byref(debug_event),INFINITE):
##input("press a key to continue...")
##self.debugger_active = False
//獲取相關(guān)線程的句柄并提取其CONTEXT
self.h_thread = self.open_thread(debug_event.dwThreadId)
self.context = self.get_thread_context(debug_event.dwThreadId)
##self.context = self.get_thread_context(self.h_thread)
print("Event code: %d Thread ID: %d" %(debug_event.dwDebugEventCode, debug_event.dwThreadId))
//如果事件碼為異常事件判沟,則檢測該事件碼的類型
if debug_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT:
//獲得異常代碼
self.exception = debug_event.u.Exception.ExceptionRecord.ExceptionCode
self.exception_address = debug_event.u.Exception.ExceptionRecord.ExceptionAddress
if self.exception == EXCEPTION_ACCESS_VIOLATION:
print("[*] Access Violation Detected")
//若檢測到斷點(diǎn)耿芹,則調(diào)用相應(yīng)的處理程序
elif self.exception == EXCEPTION_BREAKPOINT:
continue_status = self.exception_handler_breakpoint()
elif self.exception == EXCEPTION_GUARD_PAGE:
print("Guard Page Access Detected.")
elif self.exception == EXCEPTION_SINGLE_STEP:
self.exception_handler_single_step()
print("Single Stepping.")
kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
//退出附加進(jìn)程
def detach(self):
if kernel32.DebugActiveProcessStop(self.pid):
print("[*] Finished debugging Exiting...")
return True
else:
print("[*] Detach fail.")
return False
//打開線程
def open_thread(self,thread_id):
h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)
if h_thread is not None:
return h_thread
else:
print("[*] Could not obtain a valid thread handle.")
return False
//枚舉線程
def enumerate_threads(self):
thread_entry = THREADENTRY32()
thread_list = []
snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid)
if snapshot is not None:
thread_entry.dwSize = sizeof(thread_entry)
success = kernel32.Thread32First(snapshot, byref(thread_entry))
while success:
if thread_entry.th32OwnerProcessID == self.pid:
thread_list.append(thread_entry.th32ThreadID)
success = kernel32.Thread32Next(snapshot, byref(thread_entry))
kernel32.CloseHandle(snapshot)
return thread_list
else:
print("enumerate_thread fail.")
return False
//獲取線程的CONTEXT
def get_thread_context(self, thread_id):
context = CONTEXT()
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS
h_thread = self.open_thread(thread_id)
if kernel32.GetThreadContext(h_thread, byref(context)):
kernel32.CloseHandle(h_thread)
return context
else:
print("get thread context fail.")
return False
//斷點(diǎn)處理
def exception_handler_breakpoint(self):
print("[*] Inside the breakpoint handler.")
print("Exception address: 0x%08x" %self.exception_address)
print(kernel32.GetLastError())
if not self.exception_address in self.hardware_breakpoints.keys():
if self.first_breakpoints == True:
self.first_breakpoints = False
print("[*] Hit the first breakpoint.")
else:
print("[*] Hit user defined breakpoint.")
return DBG_CONTINUE
//從內(nèi)核模塊提取目標(biāo)函數(shù)虛擬內(nèi)存地址
def func_resolve(self, dll, function):
handle = kernel32.GetModuleHandleW(dll)
print("func_resolve handle 0x%08x" %handle)
address = kernel32.GetProcAddress(handle, function)
print(kernel32.GetLastError())
print("func_resolve address 0x%08x" %address)
kernel32.CloseHandle(handle)
return address
//完整代碼,參考軟斷點(diǎn)
def read_process_memory(self, address, length):
def write_process_memory(self, address, data):
def bp_set_sw(self, address):
//完整代碼挪哄,參考硬件斷點(diǎn)
def bp_set_hw(self, address, length, condition):
def exception_handler_single_step(self):
def bp_del_hw(self, slot):
//完整代碼吧秕,參考內(nèi)存斷點(diǎn)
def bp_set_mem(self, address, size):
//test.py
import myDebugger
from myDebuggerDefines import *
debugger = myDebugger.debugger()
##debugger.load("C:\\windows\\system32\\calc.exe")
##print("h_process:0x%08x"%debugger.h_process)
pid = input("Enter the PID of the process to attach to : ")
debugger.attach(int(pid))
'''
//枚舉線程存入列表中
list = debugger.enumerate_threads()
//從列表中讀取每個(gè)線程的CONTEXT信息
for thread in list:
thread_context = debugger.get_thread_context(thread)
print("[*] Dumping registers for thread ID : 0x%08x" %thread)
print("[**]EIP: 0x%08x"%thread_context.Eip)
print("[**]ESP: 0x%08x"%thread_context.Esp)
print("[**]EBP: 0x%08x"%thread_context.Ebp)
print("[**]EAX: 0x%08x"%thread_context.Eax)
print("[**]EBX: 0x%08x"%thread_context.Ebx)
print("[**]ECX: 0x%08x"%thread_context.Ecx)
print("[**]EDX: 0x%08x"%thread_context.Edx)
print("[*]END DUMP")
'''
//獲取msvcrt.dll模塊里的printf函數(shù)的虛擬地址
printf_address = debugger.func_resolve("msvcrt.dll", b"wprintf") //第二個(gè)參數(shù)需要轉(zhuǎn)化成字節(jié)編碼
print("[*] Address of printf: 0x%08x" %printf_address)
##print("pid:%d"%debugger.pid)
//測試軟斷點(diǎn)
##debugger.bp_set_sw(str(printf_address))
//測試硬件斷點(diǎn)
##debugger.bp_set_hw(printf_address, 1, HW_EXECUTE)
//測試內(nèi)存斷點(diǎn)
debugger.bp_set_mem(printf_address, 0x2000)
debugger.run()
##debugger.detach()
//用于測試
//測試步驟:先運(yùn)行printf_loop.py,然后到系統(tǒng)任務(wù)管理器找到該進(jìn)程的PID迹炼,再運(yùn)行test.py砸彬,提示輸入PID。
//printf_loop.py
from ctype import *
import time
msvcrt = cdll.msvcrt
counter = 0
while 1:
//這里使用printf打印疗涉,測試時(shí)就獲取printf函數(shù)的虛擬內(nèi)存地址
//如果使用wprintf打印拿霉,測試時(shí)就獲取wprintf函數(shù)的虛擬內(nèi)存地址
msvcrt.printf("Loop interation %d!\n" %counter)
time.sleep(1)
counter += 1
六、兼容問題
1咱扣、python2.x跟python3.x部分規(guī)范問題绽淘。
2、ANSI跟Unicode編碼問題闹伪。python3.x用的是Unicode編碼沪铭,如果需要使用到ANSI編碼,只需在字符串前加b偏瓤,例:b“string”
結(jié)束語
本文只是根據(jù)調(diào)試器的基本原理構(gòu)建一個(gè)簡單的調(diào)試器杀怠。