#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("\n### original string ###\n%s\n", lpBuffer);
// #6. 将小写字母转换为大写字母
for (i = 0; i < dwNumOfBytesToWrite; i++)
{
if (0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A)
lpBuffer[i] -= 0x20;
}
printf("\n### converted string ###\n%s\n", 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("\nUSAGE : hookdbg.exe <pid>\n");
return 1;
}
// Attach Process
dwPID = atoi(argv[1]);
if (!DebugActiveProcess(dwPID))
{
printf("DebugActiveProcess(%d) failed!!!\n"
"Error Code = %d\n", 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;