• 1281阅读
  • 2回复

一种躲避运行时代码校验的方法(Reload And Run) [复制链接]

上一主题 下一主题
离线啊冲
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-02-04





   我们有时候需要对运行中的程序打内存补丁,或者对它的代码挂一些钩子之类的工作。但是现在相当多软件进行了运行时的代码检测。一旦发现内存中的代码被修改掉,就会进行处理。本文介绍了一种比较特别的办法,用于通过这些检测。    首先需要说一下做运行时代码校验的方法。一般来说,校验者需要取得当前模块的基地址,通过分析PE结构,获得代码节的偏移和大小,然后对内存中的代码进行CRC或者其他的一些校验。
这其中有个很大的问题,校验者默认了通过这种方式取得的代码节就是当前被使用到的代码,但是事实却不一定如此。一般编译器正常生成的代码,绝大部分跳转和call语句都使用相对地址,因此,我们完全可以把代码节或者整个exe文件映像复制到内存其他地方,并操作进程内的所有线程,使得它执行在新复制的那份代码中。这样,校验者仍然在扫描旧的地址,新的那份我们就可以随意修改了。
    由于一旦进程开始执行,并且创建其他线程之后,我们通过取得线程Context获得的EIP,多半在系统代码中间,所以难以改变。唯一的方法就是,在exe的EntryPoint被执行前,将EntryPoint重定向到新的代码,并Hook CreateThread,将新线程也重新定位。可以通过下面几个步骤来实现:
1、  如果想处理的进程为a.exe,并且a.exe是由b.exe创建的,那么我们需要hook掉b.exe的进程创建函数,一般是CreateProcess。
2、  在Hook的CreateProcess中,以CREATE_SUSPENDED标志创建a.exe。并向a.exe中注入我们的dll,等待这个dll完成处理之后才ResumeThread a.exe的主线程。
3、  注入的dll需要完成几件事。首先要获得主模块的基地址和大小,并分配足够的空间,将原始映像复制过去。然后Hook掉EntryPoint,并Hook CreateThread,最后恢复a.exe主线程。
4、  Hook掉的EntryPoint中,恢复被Hook的EntryPoint代码,防止在后面被检查出来,然后jmp到新分配的代码区域即可。
5、  Hook的CreateThread中,重新计算代码线程函数地址,并修改后创建。这样,所有线程就都在新分配的代码中执行了。

下面是注入的dll的实现代码:
[cpp] view plain copy
  1. pfnCreateThread g_pCreateThread = ::CreateThread;  
  2. PBYTE  g_pbyNewImage = NULL;  
  3.   
  4. #pragma pack(push,1)  
  5. typedef struct _PUSH_RETN  
  6. {  
  7.   BYTE byOpcodePush;//0x68  
  8.   DWORD dwRetnAddr;  
  9.   BYTE byOpcodeRetn;//0xC3  
  10. }PUSH_RETN, *PPUSH_RETN;  
  11. #pragma pack(pop)  
  12.   
  13. BYTE g_abyOldEntry[6] = {0};  
  14. PBYTE g_pbyOldEntry = 0;  
  15. PBYTE g_pbyNewEntry = 0;  
  16.   
  17. //这里使用Detours库Hook掉CreateThread。  
  18. BOOL HookThreadCreate()  
  19. {  
  20.   DetourTransactionBegin();  
  21.   DetourUpdateThread( GetCurrentThread());  
  22.   
  23.   
  24.   if( DetourAttach( &(PVOID&)g_pCreateThread, Hook_CreateThread) != NO_ERROR)  
  25.   {  
  26.     DebugOut( TEXT( "Hook CreateThread fail\r\n"));  
  27.   }  
  28.   
  29.   if( DetourTransactionCommit() != NO_ERROR)  
  30.   {  
  31.     DebugOut( TEXT( "Hook fail\r\n"));  
  32.     return FALSE;  
  33.   }  
  34.   else  
  35.   {  
  36.     DebugOut( TEXT( "Hook ok\r\n"));  
  37.     return TRUE;  
  38.   }  
  39. }  
  40.   
  41. //Hook的CreateThread里面,重新计算lpStartAddress地址,并按这个地址来创建  
  42. HANDLE WINAPI Hook_CreateThread(  
  43.                 LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  44.                 SIZE_T dwStackSize,  
  45.                 LPTHREAD_START_ROUTINE lpStartAddress,  
  46.                 LPVOID lpParameter,  
  47.                 DWORD dwCreationFlags,  
  48.                 LPDWORD lpThreadId  
  49.                 )  
  50. {  
  51.   PBYTE pfn = (PBYTE)lpStartAddress;  
  52.   HMODULE hMod = ::GetModuleHandle( NULL);  
  53.   pfn = g_pbyNewImage + (pfn - (PBYTE)hMod);  
  54.   HANDLE hThread = g_pCreateThread( lpThreadAttributes, dwStackSize, (LPTHREAD_START_ROUTINE)pfn, lpParameter, dwCreationFlags, lpThreadId);  
  55.   return hThread;  
  56. }  
  57.   
  58. //Hook掉的入口点,恢复旧代码并跳转到新分配的代码空间  
  59. __declspec( naked ) VOID Hook_EntryPoint()  
  60. {  
  61.   //_asm int 3  
  62.   for ( DWORD i = 0; i < sizeof(g_abyOldEntry); i++)  
  63.   {//恢复旧的代码  
  64.     g_pbyOldEntry = g_abyOldEntry;  
  65.   }  
  66.   _asm jmp g_pbyNewEntry;  
  67. }  
  68.   
  69. //Hook掉入口点  
  70. VOID HookEntryPoint()  
  71. {  
  72.   DebugOut( _T( "In HookEntryPoint\r\n"));  
  73.   
  74.   HMODULE hMod = ::GetModuleHandle( NULL);  
  75.   PIMAGE_DOS_HEADER pstDosHeader = (PIMAGE_DOS_HEADER)hMod;  
  76.   PIMAGE_NT_HEADERS pstHeader = (PIMAGE_NT_HEADERS)((PBYTE)hMod + pstDosHeader->e_lfanew);  
  77.   DWORD dwEntryRVA = pstHeader->OptionalHeader.AddressOfEntryPoint;  
  78.   PBYTE pbyRVA = (PBYTE)(hMod) + dwEntryRVA;  
  79.   PPUSH_RETN pstHook = (PPUSH_RETN)pbyRVA;  
  80.   memcpy( g_abyOldEntry, pbyRVA, sizeof(PUSH_RETN));  
  81.   pstHook->byOpcodePush = 0x68;  
  82.   pstHook->dwRetnAddr = (DWORD)Hook_EntryPoint;  
  83.   pstHook->byOpcodeRetn = 0xC3;  
  84.   
  85.   g_pbyOldEntry = pbyRVA;  
  86.   g_pbyNewEntry = g_pbyNewImage + dwEntryRVA;  
  87.   DebugOut( _T("New image base = 0x%X, New EntryPoint = 0x%X\r\n \  
  88.         Old image base = 0x%X, Old EntryPoint = 0x%X\r\n"), g_pbyNewImage, g_pbyNewEntry, hMod, g_pbyOldEntry);  
  89.   DebugOut( _T( "HookEntryPoint OK\r\n"));  
  90. }  
  91.   
  92. //在DllMain里面Process Attach的时候调用  
  93. VOID OnAttachProcess()  
  94. {  
  95.   HMODULE hMod = ::GetModuleHandle( NULL);  
  96.   DWORD dwSize = GetImageSize();  
  97.   g_pbyNewImage = new BYTE[dwSize];  
  98.   
  99.   DWORD dwOldProtect = 0;  
  100.   VirtualProtect( g_pbyNewImage, dwSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);  
  101.   VirtualProtect( (LPVOID)hMod, dwSize, PAGE_READWRITE, &dwOldProtect);  
  102.   memcpy( g_pbyNewImage, (LPVOID)hMod, dwSize);  
  103.   
  104.   HookEntryPoint();  
  105.   HookThreadCreate();  
  106. }  
  107.   
  108. //获得主模块映像大小  
  109. DWORD GetImageSize()  
  110. {  
  111.   HANDLE hShot = NULL;  
  112.   BOOL blResult = FALSE;  
  113.   MODULEENTRY32 stInfo = {0};  
  114.   stInfo.dwSize = sizeof( MODULEENTRY32);  
  115.   TCHAR tszTmp[MAX_PATH] = {0};  
  116.   
  117.   ::GetModuleFileName( GetModuleHandle(NULL), tszTmp, MAX_PATH);  
  118.   _tcslwr( tszTmp);  
  119.   size_t s = _tcslen(tszTmp);  
  120.   for ( DWORD i = 0; i < s; i++)  
  121.   {  
  122.     if ( tszTmp[s - i] == '\\')  
  123.     {  
  124.       s = s - i + 1;  
  125.       break;  
  126.     }  
  127.   }  
  128.   hShot = ::CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, ::GetCurrentProcessId());  
  129.   if ( INVALID_HANDLE_VALUE == hShot)  
  130.   {  
  131.     DebugOut( _T( "CreateToolhelp32Snapshot fail.Err=%d\r\n"), GetLastError());  
  132.     return 0;  
  133.   }  
  134.   
  135.   blResult = ::Module32First( hShot, &stInfo);  
  136.   while ( blResult)  
  137.   {  
  138.     _tcslwr( stInfo.szModule);  
  139.     if ( _tcscmp( stInfo.szModule, tszTmp + s) == 0)  
  140.     {  
  141.       CloseHandle( hShot);  
  142.       return stInfo.modBaseSize;  
  143.     }  
  144.     blResult = ::Module32Next( hShot, &stInfo);  
  145.   }  
  146.   CloseHandle( hShot);  
  147.   return 0;  
  148. }  

     这种方法对加壳之后的exe作用有限,因为Hook的并不是真实的OEP。这样做常常会造成壳执行过程中,或者跳转到OEP之后迅速崩溃。本文仅仅是介绍一种思想,有时候巧妙的构思可以使得复杂问题很快得到解决。
    改良之后,这个技巧可以用到不少地方,呵呵


善者 慈悲心常在 无怨无恨 以苦为乐
默认压缩密码www.hifyl.com
文件分享密码问题:http://www.hifyl.com/read-htm-tid-4444.html
离线htr78

只看该作者 沙发  发表于: 2016-02-17
收藏了,我得加紧学习,争取2年内动手,因为我没api基础
离线v2680267313

只看该作者 板凳  发表于: 2016-04-30
用户被禁言,该主题自动屏蔽!
快速回复
限100 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
上一个 下一个