我们在软件破解学习过程中经常遇到一些时间验证、注册码验证、网络数据抓取、USB狗数据抓取。要完成操作我们可以通过Hook技术来完成时间欺骗,验证码欺骗,数据抓取及欺骗。
注:钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
以上是微软对Hook的定义。通过SetWindowsHookEx 来设置Hook的函数。如果你认为这就是我要讲解的Hook技术,只能说图样图森破!这种Hook本质为Windows系统内核在调用正常流程前的一次函数回调。
今天要讲的Hook技术是程序汇编级的Hook调用。
如一个软件要对时间进行判断,当发现时间过期后自动退出。我们可以修改系统获取时间为一个早期时间,这样程序永远不会发送过期。
首先我们构造一个获取时间的函数。该函数主要通过获取系统时间,并修改系统时间的年份返回给调用者。其中临界区主要用于线程安全。ReleaseBase/ MonitorBase 主要是恢复代码和监控代码。这在下边详细讲解。
注:钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
以上是微软对Hook的定义。通过SetWindowsHookEx 来设置Hook的函数。如果你认为这就是我要讲解的Hook技术,只能说图样图森破!这种Hook本质为Windows系统内核在调用正常流程前的一次函数回调。
今天要讲的Hook技术是程序汇编级的Hook调用。
如一个软件要对时间进行判断,当发现时间过期后自动退出。我们可以修改系统获取时间为一个早期时间,这样程序永远不会发送过期。
首先我们构造一个获取时间的函数。该函数主要通过获取系统时间,并修改系统时间的年份返回给调用者。其中临界区主要用于线程安全。ReleaseBase/ MonitorBase 主要是恢复代码和监控代码。这在下边详细讲解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void WINAPI MonFunc(LPSYSTEMTIME p) { //Thread safety EnterCriticalSection(&g_cs); //Restore the original API before calling it ReleaseBase(); GetSystemTime(p); p->wYear = 2014; MonitorBase(); //You can do anything here, and you can call the UninstallMonitor //when you want to leave. //Thread safety LeaveCriticalSection(&g_cs); return ; } |
如下所示,在ReleaseBase/ MonitorBase主要完成向内存写入特定的数据。这些数据就是指令代码。一个是修改前的原始指令代码,一个是修改后的新的指令代码。比如程序原来的代码为调用函数B。但我修改函数B开始字节为新字节(功能:跳转到新的函数代码C中)。当程序执行到函数C时,恢复函数B的开始字节,然后进入原来函数B的调用,当函数B执行完毕后返回到C中恢复B开始字节。这样当下次调用函数B时,程序仍然会进入到函数C中执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | __inline BOOL MonitorBase(void) { // Modify the heading 6 bytes opcode in target API to jmp instruction, // the jmp instruction will lead the EIP to our fake function ReadProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aBackup)/ sizeof(g_aBackup[0]), NULL); printf("Do Mon\n"); return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aOpcode), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL); } __inline BOOL ReleaseBase(void) { // Restore the heading 6 bytes opcode of target API. printf("Do NoMon\n"); return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL); } |
这就是在函数体B中一会是进入函数C的代码,一会是执行自己的原本代码。进入函数C的汇编本质就是“jmp xxx”。这些可以在初始化(InstallMonitor)时计算出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //Install Monitor BOOL InstallMonitor(void) { //Get handle of current process g_hProc = GetCurrentProcess(); g_aOpcode[0] = 0xE9; //JMP Procudure *(DWORD*)(&g_aOpcode[1]) = (DWORD)MonFunc - g_dwApiFunc - 5; InitializeCriticalSection(&g_cs); printf("OP: %.2X %.2x %.2x %.2x %.2x %.8x %.8x\n",g_aOpcode[0],g_aOpcode[1],g_aOpcode[2],g_aOpcode[3],g_aOpcode[4],(DWORD)MonFunc,g_dwApiFunc); //Start monitor return MonitorBase(); } |
其内存汇编指令变化如下:
原始指令:
原始指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | funcA: Push xxxx Push xxxx Call B Cmp eax,eax funcB: Push ebp Mov ebp, esp "...... funcC: "恢复为函数B Push xxxx Push xxxx Call B "设置函数B为跳转到函数C |
初始化后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | funcA: Push xxxx Push xxxx Call B Cmp eax,eax funcB: Jmp C "...... funcC: "恢复为函数B Push xxxx Push xxxx Call B "设置函数B为跳转到函数C |
当程序执行函数B时将跳转到函数C执行。执行完 恢复函数 B后,函数B恢复到之前状态。紧接着在函数C中调用函数B,函数B 可正常执行。
执行完后,设置函数B为跳转代码。这样,当程序下一次调用函数B后会继续执行函数C,如此循环。
截获设置时间的完整cpp代码如下。其生成动态库,可以通过远线程注入注入到目标程序中,以达到篡改时间的目的。当然也可以修改目标程序的导入目录,让程序自动加载该动态库。
执行完后,设置函数B为跳转代码。这样,当程序下一次调用函数B后会继续执行函数C,如此循环。
截获设置时间的完整cpp代码如下。其生成动态库,可以通过远线程注入注入到目标程序中,以达到篡改时间的目的。当然也可以修改目标程序的导入目录,让程序自动加载该动态库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | // InjGetSysTime.cpp : 定义 DLL 应用程序的导出函数。 // #include "stdafx.h" #include #include #include #include "Shlwapi.h" #include using namespace std; #pragma comment(lib,"Shlwapi.lib") /*需要加上此行才可以正确link,VC6.0*/ //Handle of current process HANDLE g_hProc; //Backup of orignal code of target api BYTE g_aBackup[6]; BYTE g_aOpcode[6]; //Critical section, prevent concurrency of calling the monitor CRITICAL_SECTION g_cs; //Base address of target API in DWORD DWORD g_dwApiFunc = (DWORD)GetSystemTime; //Hook the target API __inline BOOL MonitorBase(void) { // Modify the heading 6 bytes opcode in target API to jmp instruction, // the jmp instruction will lead the EIP to our fake function ReadProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aBackup)/ sizeof(g_aBackup[0]), NULL); printf("Do Mon\n"); return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aOpcode), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL); } //Unhook the target API __inline BOOL ReleaseBase(void) { // Restore the heading 6 bytes opcode of target API. printf("Do NoMon\n"); return WriteProcessMemory(g_hProc, LPVOID(g_dwApiFunc), LPVOID(g_aBackup), sizeof(g_aOpcode) / sizeof(g_aOpcode[0]), NULL); } //Pre-declare BOOL UninstallMonitor(void); //Monitor Function void WINAPI MonFunc(LPSYSTEMTIME p) { //Thread safety EnterCriticalSection(&g_cs); //Restore the original API before calling it ReleaseBase(); GetSystemTime(p); p->wYear = 2014; MonitorBase(); //You can do anything here, and you can call the UninstallMonitor //when you want to leave. //Thread safety LeaveCriticalSection(&g_cs); return ; } //Install Monitor BOOL InstallMonitor(void) { //Get handle of current process g_hProc = GetCurrentProcess(); g_aOpcode[0] = 0xE9; //JMP Procudure *(DWORD*)(&g_aOpcode[1]) = (DWORD)MonFunc - g_dwApiFunc - 5; InitializeCriticalSection(&g_cs); printf("OP: %.2X %.2x %.2x %.2x %.2x %.8x %.8x\n",g_aOpcode[0],g_aOpcode[1],g_aOpcode[2],g_aOpcode[3],g_aOpcode[4],(DWORD)MonFunc,g_dwApiFunc); //Start monitor return MonitorBase(); } BOOL UninstallMonitor(void) { //Release monitor if (!ReleaseBase()) return FALSE; DeleteCriticalSection(&g_cs); CloseHandle(g_hProc); //Synchronize to main application, release semaphore to free injector HANDLE hSema = OpenSemaphore(EVENT_ALL_ACCESS, FALSE, _T("Global\\InjHack")); if (hSema == NULL) return FALSE; return ReleaseSemaphore(hSema, 1, (LPLONG)g_hProc); } extern "C" void __declspec(dllexport) Test() { } BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved) { TCHAR buf[MAX_PATH]={0}; string str; switch (fdwReason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hInstDll); GetModuleFileName(hInstDll, buf, MAX_PATH); str = buf; str = str.substr(0, str.rfind("\\") ); str += "\\Debug.Info"; if ( ::PathFileExists( str.c_str() ) ) { ::AllocConsole(); freopen("CONOUT$","w",stdout); printf("Prin On\n"); } InstallMonitor(); break; case DLL_PROCESS_DETACH: ReleaseBase(); printf("Exit\n"); break; } return TRUE; } |
注:网络配图