【逆向工程核心原理:API钩取】

0x01、原理
  1. 通过钩子代码,来调试被钩取程序。
  2. 此时钩子代码就是调式器,被钩取程序就是被调试者
  3. 钩子代码在要被钩取程序的API位置下 0xCC 断点.
  4. 当被调试者运行到断点处时,就会触发断点异常,此时要调试器来处理。
  5. 此时就可以更改要钩取的API的函数功能,达到自己的目的。
  6. 执行完放开让程序继续执行。
0x02、源代码
#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()
    //   将第一个byte更改为0xCC(INT3)
    //   (orginal byte是备份)
    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(INT3)
    if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
    {
        // 如果BP地址为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()的param2,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. 将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是让程序放弃CPU的时间片,让其他程序执行,此时钩取的notepad才能调用WriteFile函数保存内容
            // 否则,下面的WriteProcessMemory立刻会把WriteFile钩取,notepad去执行WriteFile还是被断下
            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;

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

        // 创建Debuggee进程或attach事件
        if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
        {
            OnCreateProcessDebugEvent(&de);
        }
        // 异常事件
        else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
        {
            if( OnExceptionDebugEvent(&de) )
                continue;
        }
        // Debuggee进程终止事件
        else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
        {
            // debuggee结束->debugger结束
            break;
        }

        // 恢复Debuggee的运行
        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;
}

0x03、程序执行流程

程序执行流

0x04、结果分析

运用在windows10 32bit Debug/Release
64bit参数结构、寄存器名字不同,需要修改才能使用
x86_debug

0x05、用到的API
// 使调试器可以附加到活动进程并调试它
BOOL WINAPI DebugActiveProcess(
  __in  DWORD dwProcessId// 要附加进程的PID
);

// 等待调试事件发生
BOOL WINAPI WaitForDebugEvent(
  __out  LPDEBUG_EVENT lpDebugEvent,// 接收DEBUG_EVENT结构,保存被调试程序的信息
  __in   DWORD dwMilliseconds// 等待调试事件的毫秒数。如果此参数为零,则函数测试调试事件并立即返回。如果参数为INFINITE,则函数在发生调试事件之前不会返回。
);


// 继续执行被调试的程序
BOOL WINAPI ContinueDebugEvent(
  __in  DWORD dwProcessId,// 继续执行程序的进程
  __in  DWORD dwThreadId,// 继续执行程序的线程
  __in  DWORD dwContinueStatus// DBG_CONTINUE继续执行;DBG_EXCEPTION_NOT_HANDLED交给SEH处理
);

// 获取线程上下文
BOOL WINAPI GetThreadContext(
  __in     HANDLE hThread,// 线程ID
  __inout  LPCONTEXT lpContext// 保存线程上下文的CONTEXT结构
);

// 设定线程上下文
BOOL WINAPI SetThreadContext(
  __in  HANDLE hThread,// 线程ID
  __in  const CONTEXT* lpContext// 要设置的CONTEXT结构
);


// 从目标进程赋复制nsize大小的数据到lpBuffer中
BOOL WINAPI ReadProcessMemory(
  __in   HANDLE hProcess,// 目标进程
  __in   LPCVOID lpBaseAddress,// 读取数据的起始地址,在读取数据前,系统将先检验该地址的数据是否可读,如果不可读,函数将调用失败
  __out  LPVOID lpBuffer,// 读取的数据保存在的缓冲区
  __in   SIZE_T nSize,// 读取数据大小
  __out  SIZE_T* lpNumberOfBytesRead// 实际被读取数据大小的存放地址。如果被指定为NULL,那么将忽略此参数。
);

// 向目标进程写入nsize大小的数据到lpBaseAddress中
BOOL WINAPI WriteProcessMemory(
  __in   HANDLE hProcess,// 目标进程
  __in   LPVOID lpBaseAddress,// 要写入的地址
  __in   LPCVOID lpBuffer,// 要写入的数据存放的缓冲区
  __in   SIZE_T nSize,// 写入数据大小
  __out  SIZE_T* lpNumberOfBytesWritten// 实际写入数据大小的存放地址。如果被指定为NULL,那么将忽略此参数。
);

0x06用到的结构
// 调试事件发生后,保存被调试程序的信息
typedef struct _DEBUG_EVENT {  
	DWORD dwDebugEventCode;  
	DWORD dwProcessId;  
	DWORD dwThreadId;
	// 每种异常也是一个结构体,说一下CREATE_PROCESS_DEBUG_INFO 
	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 UnloadDll;    
	OUTPUT_DEBUG_STRING_INFO DebugString;    
	RIP_INFO RipInfo;  
	} u;
} DEBUG_EVENT,  *LPDEBUG_EVENT;

typedef struct _CREATE_PROCESS_DEBUG_INFO {  
	HANDLE hFile;  
	HANDLE hProcess;  
	HANDLE hThread;  
	LPVOID lpBaseOfImage;  
	DWORD dwDebugInfoFileOffset;  
	DWORD nDebugInfoSize;  
	LPVOID lpThreadLocalBase;  
	LPTHREAD_START_ROUTINE lpStartAddress;  
	LPVOID lpImageName;  
	WORD fUnicode;
} CREATE_PROCESS_DEBUG_INFO,  *LPCREATE_PROCESS_DEBUG_INFO;

// CONTEXT x86
struct _CONTEXT {
    DWORD ContextFlags;

    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
    //

    FLOATING_SAVE_AREA FloatSave;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_SEGMENTS.
    //

    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_INTEGER.
    //

    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;

    //
    // This section is specified/returned if the
    // ContextFlags word contians the flag CONTEXT_CONTROL.
    //

    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;              // MUST BE SANITIZED
    DWORD   EFlags;             // MUST BE SANITIZED
    DWORD   Esp;
    DWORD   SegSs;

    //
    // This section is specified/returned if the ContextFlags word
    // contains the flag CONTEXT_EXTENDED_REGISTERS.
    // The format and contexts are processor specific
    //

    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;

// CONTEXT x64
typedef struct _CONTEXT {
  DWORD64 P1Home;
  DWORD64 P2Home;
  DWORD64 P3Home;
  DWORD64 P4Home;
  DWORD64 P5Home;
  DWORD64 P6Home;
  DWORD   ContextFlags;
  DWORD   MxCsr;
  WORD    SegCs;
  WORD    SegDs;
  WORD    SegEs;
  WORD    SegFs;
  WORD    SegGs;
  WORD    SegSs;
  DWORD   EFlags;
  DWORD64 Dr0;
  DWORD64 Dr1;
  DWORD64 Dr2;
  DWORD64 Dr3;
  DWORD64 Dr6;
  DWORD64 Dr7;
  DWORD64 Rax;
  DWORD64 Rcx;
  DWORD64 Rdx;
  DWORD64 Rbx;
  DWORD64 Rsp;
  DWORD64 Rbp;
  DWORD64 Rsi;
  DWORD64 Rdi;
  DWORD64 R8;
  DWORD64 R9;
  DWORD64 R10;
  DWORD64 R11;
  DWORD64 R12;
  DWORD64 R13;
  DWORD64 R14;
  DWORD64 R15;
  DWORD64 Rip;
  union {
    XMM_SAVE_AREA32 FltSave;
    NEON128         Q[16];
    ULONGLONG       D[32];
    struct {
      M128A Header[2];
      M128A Legacy[8];
      M128A Xmm0;
      M128A Xmm1;
      M128A Xmm2;
      M128A Xmm3;
      M128A Xmm4;
      M128A Xmm5;
      M128A Xmm6;
      M128A Xmm7;
      M128A Xmm8;
      M128A Xmm9;
      M128A Xmm10;
      M128A Xmm11;
      M128A Xmm12;
      M128A Xmm13;
      M128A Xmm14;
      M128A Xmm15;
    } DUMMYSTRUCTNAME;
    DWORD           S[32];
  } DUMMYUNIONNAME;
  M128A   VectorRegister[26];
  DWORD64 VectorControl;
  DWORD64 DebugControl;
  DWORD64 LastBranchToRip;
  DWORD64 LastBranchFromRip;
  DWORD64 LastExceptionToRip;
  DWORD64 LastExceptionFromRip;
} CONTEXT, *PCONTEXT;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值