命名
关于这个命名是我自己这样说的,至于这种HOOK技术,先前在一个开源项目中叫做RemoteHook,我比较喜欢自己的这种命名,所以就叫Debug Hook。如果有错误,请指出。
先来说说调试的原理
在Windows操作系统,有两种方法可以来调试一个进程。
: CreateProcess()
可以使用此函数来启动调试一个进程。
CreateProcess(FileFullPath,NULL,NULL,NULL, false,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS| CREATE_NEW_CONSOLE,
NULL,NULL,&StartupInfo,&ProcessInfo)
:DebugActiveProcess(ProcessID)
可以使用此函数来附加到一个进程来进行调试。
我们使用以上两种方法中的任何一种方法来调试一个进程,每当被调试进程发生调试事件的时候,OS都会暂停其运行。并向调试器报告相应的事件,调试器处理之后就可以继续运行。
利用调试技术来HOOK API函数的相关步骤如下
1.对想要钩取的进程进行附加操作,使之成为被调试者。
2.将要钩取的API的起始地址的第一个字节修改为0xcc(或者使用硬件断点)。
3.当调用目标API的时候,控制权就转移到调试器进程。
4.执行需要的操作。
5.脱钩,将API 函数的第一个字节恢复。
6.运行相应的API。
#include<Windows.h>
#include<iostream>
#include<stdio.h>
using namespace std;
LPVOID WriteFileAddress = NULL;
CREATE_PROCESS_DEBUG_INFO CreateProcessDebugInfomation;
BYTE INT3 = 0xCC, OldByte = 0;
BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
// WriteFile()函数地址
WriteFileAddress = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
// API Hook - WriteFile()
//将WriteFile函数的首个字节改为0xcc
memcpy(&CreateProcessDebugInfomation, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
ReadProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress,
&OldByte, sizeof(BYTE), NULL);
WriteProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress,
&INT3, sizeof(BYTE), NULL);
return TRUE;
}
BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pDebugEvent)
{
CONTEXT Context;
PBYTE lpBuffer = NULL;
DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
PEXCEPTION_RECORD pExceptionRecord = &pDebugEvent->u.Exception.ExceptionRecord;
// BreakPoint exception
if( EXCEPTION_BREAKPOINT == pExceptionRecord->ExceptionCode )
{
// 发生异常的地方是否为我们要钩取的函数
if( WriteFileAddress == pExceptionRecord->ExceptionAddress )
{
// #1. Unhook
// 先恢复,以免进入死循环
WriteProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress,
&OldByte, sizeof(BYTE), NULL);
// #2. 获得线程上下背景文 为了修改EIp的值,来使进程恢复正常运行
Context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(CreateProcessDebugInfomation.hThread, &Context);
// #3. WriteFile() 根据ESP来获得WriteFile 函数的参数,以达到修改数据的目的
ReadProcessMemory(CreateProcessDebugInfomation.hProcess, (LPVOID)(Context.Esp + 0x8),
&dwAddrOfBuffer, sizeof(DWORD), NULL);
ReadProcessMemory(CreateProcessDebugInfomation.hProcess, (LPVOID)(Context.Esp + 0xC),
&dwNumOfBytesToWrite, sizeof(DWORD), NULL);
// #4.
lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
// #5. WriteFile()
ReadProcessMemory(CreateProcessDebugInfomation.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. 调用原函数
WriteProcessMemory(CreateProcessDebugInfomation.hProcess, (LPVOID)dwAddrOfBuffer,
lpBuffer, dwNumOfBytesToWrite, NULL);
free(lpBuffer);
// 设置EIP的值来实现正常运行,注意EIP的值为0xcc的下一条指令的地址。
Context.Eip = (DWORD)WriteFileAddress;
SetThreadContext(CreateProcessDebugInfomation.hThread, &Context);
// 运行
ContinueDebugEvent(pDebugEvent->dwProcessId, pDebugEvent->dwThreadId, DBG_CONTINUE);
Sleep(0);
// 再次钩取
WriteProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress,
&INT3, sizeof(BYTE), NULL);
return TRUE;
}
}
return FALSE;
}
void DebugLoop()
{
DEBUG_EVENT DebugEvent;
DWORD dwContinueStatus;
// 等待调试事件
while( WaitForDebugEvent(&DebugEvent, INFINITE) )
{
dwContinueStatus = DBG_CONTINUE;
// 调试事件为创建进程
if( CREATE_PROCESS_DEBUG_EVENT == DebugEvent.dwDebugEventCode )
{
OnCreateProcessDebugEvent(&DebugEvent);
}
// 调试事件
else if( EXCEPTION_DEBUG_EVENT == DebugEvent.dwDebugEventCode )
{
if( OnExceptionDebugEvent(&DebugEvent) )
continue;
}
// 调试进程退出
else if( EXIT_PROCESS_DEBUG_EVENT == DebugEvent.dwDebugEventCode )
{
break;
}
ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, dwContinueStatus);
}
}
int main(int argc, char* argv[])
{
DWORD dwProcessID;
cout << "Input ProcessID" << endl;
cin >> dwProcessID;
// Attach Process
if( !DebugActiveProcess(dwProcessID) )
{
printf("DebugActiveProcess(%d) failed!!!\n"
"Error Code = %d\n", dwProcessID, GetLastError());
return 1;
}
// 调试事件循环
DebugLoop();
return 0;
}