《逆向学习日记》 no.14 利用调试法来实现api钩取

这一章,我们借助notepad.exe,来学习调试法API钩取,最终实现小写输入转大写输入。

过程详解

源代码


#include "stdio.h"

LPVOID g_pfWriteFile = NULL;
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;

BOOL (LPDEBUG_EVENT pde)
{

g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");

// API Hook - WriteFile()
// 更改第一个字节为0xCC(INT3)
// originalbyte是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;

// 是断点异常 (INT 3)时
if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
{
// 断点地址是 WirteFile() API地址时
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%sn", lpBuffer);

// #6. 家巩磊 -> 措巩磊 函券
for( i = 0; i < dwNumOfBytesToWrite; i++ )
{
if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
lpBuffer[i] -= 0x20;
}

printf("n### converted string ###n%sn", lpBuffer);

// #7. 函券等 滚欺甫 WriteFile() 滚欺肺 汗荤
WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);

// #8. 烙矫 滚欺 秦力
free(lpBuffer);

// #9. Thread Context 狼 EIP 甫 WriteFile() 矫累栏肺 函版
// (泅犁绰 WriteFile() + 1 父怒 瘤唱吭澜)
ctx.Eip = (DWORD)g_pfWriteFile;
SetThreadContext(g_cpdi.hThread, &ctx);

// #10. Debuggee 橇肺技胶甫 柳青矫糯
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;
}

// 附加进程
dwPID = atoi(argv[1]);
if( !DebugActiveProcess(dwPID) )
{
printf("DebugActiveProcess(%d) failed!!!n"
"Error Code = %dn", dwPID, GetLastError());
return 1;
}

// 调试器循环
DebugLoop();

return 0;
}