#include "windows.h" #include "stdio.h" LPVOID g_pfWriteFile = NULL; CREATE_PROCESS_DEBUG_INFO g_cpdi; BYTE g_chINT3 = 0xCC, g_chOrgByte = 0; BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde) { // 获取 WriteFile() API 地址 g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile"); // API Hook - WriteFile() // 更改第一个字节为0xCC(INT3) // (orginal byte是g_ch0rgByte备份) memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO)); ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL); WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chINT3, sizeof(BYTE), NULL); return TRUE; } BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde) { CONTEXT ctx; PBYTE lpBuffer = NULL; DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i; PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord; // BreakPoint exception (INT 3) if (EXCEPTION_BREAKPOINT == per->ExceptionCode) { // 断点地址为 WriteFile() 地址时 if (g_pfWriteFile == per->ExceptionAddress) { // #1. Unhook // 将 0xCC 恢复为 original byte WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL); // #2. 获取 Thread Context ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(g_cpdi.hThread, &ctx); // #3. 获取 WriteFile() 的 param 2, 3 值 // 函数参数存在于相应进程的栈 // param 2 : ESP + 0x8 // param 3 : ESP + 0xC ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8), &dwAddrOfBuffer, sizeof(DWORD), NULL); ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC), &dwNumOfBytesToWrite, sizeof(DWORD), NULL); // #4. 分配临时缓冲区 lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite + 1); memset(lpBuffer, 0, dwNumOfBytesToWrite + 1); // #5. 复制WriteFile()缓冲区到临时缓冲区 ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL); printf(" ### original string ### %s ", lpBuffer); // #6. 将小写字母转换为大写字母 for (i = 0; i < dwNumOfBytesToWrite; i++) { if (0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A) lpBuffer[i] -= 0x20; } printf(" ### converted string ### %s ", lpBuffer); // #7. 将变换后的缓冲区复制到WriteFile()缓冲区 WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL); // #8. 释放临时缓冲区 free(lpBuffer); // #9.将线程上下文的EIP更改为WriteFile()首地址 // (当前为 WriteFile() + 1 位置,INT3命令之后) ctx.Eip = (DWORD)g_pfWriteFile; SetThreadContext(g_cpdi.hThread, &ctx); // #10. 运行被调试进程 ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE); Sleep(0); // #11. API Hook WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chINT3, sizeof(BYTE), NULL); return TRUE; } } return FALSE; } void DebugLoop() { DEBUG_EVENT de; DWORD dwContinueStatus; // 等待被调试者发生事件 while (WaitForDebugEvent(&de, INFINITE)) { dwContinueStatus = DBG_CONTINUE; // 被调试进程生成或者附加事件 if (CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode) { OnCreateProcessDebugEvent(&de); } // 异常事件 else if (EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode) { if (OnExceptionDebugEvent(&de)) continue; } // 被调试进程终止事件 else if (EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode) { // 被调试进程中止->调试器终止 break; } // 再次运行被调试者 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus); } } int main(int argc, char* argv[]) { DWORD dwPID; if (argc != 2) { printf(" USAGE : hookdbg.exe <pid> "); return 1; } // Attach Process dwPID = atoi(argv[1]); if (!DebugActiveProcess(dwPID)) { printf("DebugActiveProcess(%d) failed!!! " "Error Code = %d ", dwPID, GetLastError()); return 1; } // 调试器循环 DebugLoop(); return 0; }
这篇博客很全面:https://www.cnblogs.com/UnGeek/p/3515995.html
DebugActiveProcess
说明:此函数允许将调试器捆绑到一个正在运行的进程上。
语法:BOOL DebugActiveProcess(DWORD dwProcessId )
参数:
dwProcessId DWORD 欲捆绑进程的进程标识符
返回值 BOOL 如果函数成功,则返回非零值;如果失败,则返回零
在DebugLoop()中:
WaitForDebugEvent
说明:此函数用来等待被调试进程发生调试事件。
语法:BOOL WaitForDebugEvent(LPDEBUG_ENENT lpDebugEvent, DWORD dwMilliseconds)
参数:
lpDebugEvent LPDEBUG_ENENT 指向接收调试事件信息的DEBUG_ ENENT结构的指针
dwMilliseconds DWORD 该函数用来等待调试事件发生的毫秒数,如果这段时间内没有调试事件发生,函数将返回调用者;如果将该参数指定为INFINITE,函数将一直等待直到调试事件发生
返回值 BOOL:如果函数成功,则返回非零值;如果失败,则返回零
ContinueDebugEvent函数
说明:此函数允许调试器恢复先前由于调试事件而挂起的线程。
语法:BOOL ContinueDebugEvent(DWORD dwProcessId,DWORD dwThreadId, DWORD dwContinueStatus )
参数:
dwProcessId DWORD 被调试进程的进程标识符
dwThreadId DWORD 欲恢复线程的线程标识符
dwContinueStatus DWORD 此值指定了该线程将以何种方式继续,包含两个定义值DBG_CONTINUE和DBG_EXCEPTION_NOT_HANDLED
返回值 BOOL 如果函数成功,则返回非零值;如果失败,则返回零
OnCreateProcessDebugEvent函数:
void *memcpy(void *to, const void *from, size_t count)
函数memcpy()从from指向的数组向to指向的数组复制count个字符。
OnExceptionDebugEvent函数:
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;