您的位置:首页 > 编程语言

《逆向工程核心原理》<04-30> 通过Debug修改代码实现API钩取的技术

2017-05-03 21:41 946 查看

原理: 通过Debug修改代码实现API钩取的技术

(技术流程: 代码-> 调试-> DebugActiveProcesss.etc)

在”Debugger–Debuggee”的状态下, 将Debuggee的API其实部分修改为0xCC (INT3), 控制权转移到Debugger后执行指定操作, 最后使Debuggee重新进入运行状态

Detail:
1. 对想钩取的进程进行附加操作, 使之成为Debugee;
2. "钩子": 将API的初始地址的第一个字节修改为0xcc;
3. 调用相应API时, 控制权转移到Debugger
4. 执行需要的操作(操作参数, 返回值.etc)
5. "脱钩": 将0xcc恢复原值(为了正常运行API)
6. 运行相关API(无0xcc的正常状态)
7. "钩子": 再次修改为0xcc(为了继续钩取)
8. 控制权返回Debuggee


代码:

01- main()

#include "windows.h"
#include "stdio.h"

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

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;
}


02- DebugLoop()

void DebugLoop()
{
DEBUG_EVENT de; // DEBUG_EVENT结构体
DWORD dwContinueStatus;

// 等待被调试者发生事件
while( WaitForDebugEvent(&de, INFINITE) )
{
dwContinueStatus = DBG_CONTINUE;

// 被调试进程生成或者附加事件
if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode ) // CREATE_PROCESS_DEBUG_EVENT 在DEBUG_EVENT结构体
{
OnCreateProcessDebugEvent(&de);
}
// 异常事件
else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode ) // EXCEPTION_DEBUG_EVENT 在DEBUG_EVENT结构体
{
if( OnExceptionDebugEvent(&de) )
continue;
}
// 被调试进程终止事件
else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
{
// 被调试者终止-调试器终止
break;
}

// 再次运行被调试者
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
// dwContinueStatus 若处理正常, 值为DBG_CONTINUE; 若无法处理, 或者希望在应用程序的SEH中处理, 值为DBG_EXCEPTION_NOT_HANDLED
}
}


03- OnCreateProcessDebugEvent()

BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde) 
{
// 获取WriteFile() API地址
g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");

// API"钩子" - WriteFile()
//   更改第一个字节为0xCC(INT3)
//   orginal byte是g_ch0rgByte备份
memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
// CREATE_PROCESS_DEBUG_INFO g_cpdi;
ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);
// g_cpdi.hProcess 为Debuggee进程
// g_pfWriteFile   为WriteFile()地址
// Read BYTE大小的数据放到 g_chOrgByte
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chINT3, sizeof(BYTE), NULL);
// Write BYTE大小的数据放到地址首位置

return TRUE;
}


04- OnExceptionDebugEvent()

BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
{
CONTEXT ctx;
PBYTE lpBuffer = NULL;
DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;

// 是断点异常(INT3)时
if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
{
// 断点地址为WriteFile() API地址时
if( g_pfWriteFile == per->ExceptionAddress )
{
// #1. Unhook
//   将0xCC恢复为orignal byte
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
&g_chOrgByte, sizeof(BYTE), NULL);

// #2. 获取线程上下文
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);
// Read DWORD大小的数据放到 dwAddrOfBuffer
ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
&dwNumOfBytesToWrite, sizeof(DWORD), NULL);
// // Read DWORD大小的数据放到dwNumOfBytesToWrite

// #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命令之后)
//   简而言之: 执行了INT3后, EIP+1, 需要把EIP+1再还原为EIP原来的值
ctx.Eip = (DWORD)g_pfWriteFile; // 还原EIP
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;
}


05- 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      UnlaodDll;
OUTPUT_DEBUG_STRING_INFO   RipInfo;
}u;
}DEBUG_EVENT, *LPDEBUG_EVENT;


06- CREATE_THREAD_DEBUG_INFO

typedef struct _CREATE_THREAD_DEBUG_INFO {
HANDLE                  hFile;
HANDLE                  hProcess;
HANDLE                  hThread;
LPVOID                  lpBaseOfImage;
DWORD                   dwDebugINfoFileOffset;
DWORD                   nDebugInfoSize;
LPVOID                  lpThreadLocalBase;
LPTHREAD_START_ROUNTINE lpStartAddress;
LPVOID                  lpImageName;
WORD                    fUnicode;
}CREATE_THREAD_DEBUG_INFO, *LPCREATE_THREAD_DEBUG_INFO;


07- CONTEXT

typedef struct _CONTEXT {
DWORD ContextFlag;

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[MAXTINUM_SUPPORTED_EXTENSION];
}CONTEXT;


Debug:

Debug_notepad:

这里目的是观察栈的数据, 找到需要覆盖的数据缓冲区的位置

1. OD-> Attach notepad.exe-> 查找-> 所有模块中的名称-> WriteFile() f2后f9

2. 在notepad输入文本后, save

3. OD断在 WriteFile()

//注: 如果直接attach后 f9运行notepad卡死, 可以先输入文本后再attach

WriteFile()定义:4
```
BOOL WriteFile(){
HANDLE hFile;
LPCVOID lpBuffer;
DWORD nNumberOfBytesWritten;
LPOVERLAPPED lpoverlapped;
}
```
断下后, [esp]为返回地址, [esp+4]为hFile, [esp+8]为lpBuffer
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐