Anti HookAPI学习笔记
Hookapi分为2种,ring3 下挂钩 API 基本上也就是修改导入表,和Inline hook 修改前5个字节这几种方法,关于挂钩API 请参见:www.xfocus.net/articles/200403/681.html 
部分引用如下:

引用:

这里对我们比较重要的是.idata部分的导入地址表(IAT)。这个部分包含了导入的相关信息和导入函数的地址。有一点很重要的是我们必须知道PE文件 是如何创建的。当在编程语言里间接调用任意API(这意味着我们是用函数的名字来调用它,而不是用它的地址),编译器并不直接把调用连接到模块,而是用 jmp指令连接调用到IAT,IAT在系统把进程调入内存时时会由进程载入器填满。这就是我们可以在两个不同版本的Windows里使用相同的二进制代码 的原因,虽然模块可能会加载到不同的地址。进程载入器会在程序代码里调用所使用的IAT里填入直接跳转的jmp指令。所以我们能在IAT里找到我们想要挂 钩的指定函数,我们就能很容易改变那里的jmp指令并重定向代码到我们的地址。完成之后每次调用都会执行我们的代码了。
看到了吧,关键点是 进程载入器会在程序代码里调用所使用的IAT里填入直接跳转的jmp指令 hookapi就是修改这个里面写得地址 将其修改成hook函数得地址

最简单得防护方法 调用下层API
比如RegSetValueA 偶得代码其实就是
引用:

77DB6F49 >    8BFF                  mov edi,edi
77DB6F4B   .  55                    push ebp
77DB6F4C   .  8BEC                  mov ebp,esp
77DB6F4E   .  51                    push ecx
77DB6F4F   .  53                    push ebx
77DB6F50   .  33DB                  xor ebx,ebx
77DB6F52   .  817D 08 04000080      cmp dword ptr ss:[ebp+8],80000004
77DB6F59   .  895D FC               mov dword ptr ss:[ebp-4],ebx
77DB6F5C   .  0F84 B7020200         je ADVAPI32.77DD7219
77DB6F62   .  837D 10 01            cmp dword ptr ss:[ebp+10],1
77DB6F66   .  75 78                 jnz short ADVAPI32.77DB6FE0
77DB6F68   .  395D 14               cmp dword ptr ss:[ebp+14],ebx
77DB6F6B   .  74 73                 je short ADVAPI32.77DB6FE0
77DB6F6D   .  56                    push esi
77DB6F6E   .  8D45 FC               lea eax,dword ptr ss:[ebp-4]
77DB6F71   .  50                    push eax
77DB6F72   .  FF75 08               push dword ptr ss:[ebp+8]
77DB6F75   .  E8 46F9FEFF           call ADVAPI32.77DA68C0
77DB6F7A   .  8BF0                  mov esi,eax
77DB6F7C   .  3BF3                  cmp esi,ebx
77DB6F7E   .  8975 08               mov dword ptr ss:[ebp+8],esi
77DB6F81   .  0F84 96020200         je ADVAPI32.77DD721D
77DB6F87   .  8B45 14               mov eax,dword ptr ss:[ebp+14]
77DB6F8A   .  8D50 01               lea edx,dword ptr ds:[eax+1]
77DB6F8D   >  8A08                  mov cl,byte ptr ds:[eax]
77DB6F8F   .  40                    inc eax
77DB6F90   .  3ACB                  cmp cl,bl
77DB6F92   .^ 75 F9                 jnz short ADVAPI32.77DB6F8D
77DB6F94   .  2BC2                  sub eax,edx
77DB6F96   .  57                    push edi
77DB6F97   .  8D78 01               lea edi,dword ptr ds:[eax+1]
77DB6F9A   .  8B45 0C               mov eax,dword ptr ss:[ebp+C]
77DB6F9D   .  3BC3                  cmp eax,ebx
77DB6F9F   .  0F85 80020200         jnz ADVAPI32.77DD7225
77DB6FA5   >  8975 10               mov dword ptr ss:[ebp+10],esi
77DB6FA8   >  57                    push edi                                                       ; /BufSize
77DB6FA9   .  FF75 14               push dword ptr ss:[ebp+14]                                     ; |Buffer
77DB6FAC   .  6A 01                 push 1                                                         ; |ValueType = REG_SZ
77DB6FAE   .  53                    push ebx                                                       ; |Reserved
77DB6FAF   .  53                    push ebx                                                       ; |ValueName
77DB6FB0   .  FF75 10               push dword ptr ss:[ebp+10]                                     ; |hKey
77DB6FB3   .  E8 2F7CFFFF           call ADVAPI32.RegSetValueExA                                   ; \RegSetValueExA
77DB6FB8   .  8BF0                  mov esi,eax
77DB6FBA   .  8B45 10               mov eax,dword ptr ss:[ebp+10]
77DB6FBD   .  3B45 08               cmp eax,dword ptr ss:[ebp+8]
77DB6FC0   .  0F85 7E020200         jnz ADVAPI32.77DD7244
77DB6FC6   >  5F                    pop edi
77DB6FC7   >  395D FC               cmp dword ptr ss:[ebp-4],ebx
77DB6FCA   .  0F85 81020200         jnz ADVAPI32.77DD7251
77DB6FD0   >  8BC6                  mov eax,esi
77DB6FD2   .  5E                    pop esi
77DB6FD3   >  5B                    pop ebx
77DB6FD4   .  C9                    leave
77DB6FD5   .  C2 1400               retn 14


最终进入得是RegSetValueExA 其实还会继续进去RegSetValueExW。。。。
2.对于部分API有效得防护方法 使用汇编代码模拟
例如; GetVersion 其实可以自己写汇编代码实现
引用:

7C8111DA >  64:A1 18000000          mov eax,dword ptr fs:[18]
7C8111E0    8B48 30                 mov ecx,dword ptr ds:[eax+30]
7C8111E3    8B81 B0000000           mov eax,dword ptr ds:[ecx+B0]
7C8111E9    0FB791 AC000000         movzx edx,word ptr ds:[ecx+AC]
7C8111F0    83F0 FE                 xor eax,FFFFFFFE
7C8111F3    C1E0 0E                 shl eax,0E
7C8111F6    0BC2                    or eax,edx
7C8111F8    C1E0 08                 shl eax,8
7C8111FB    0B81 A8000000           or eax,dword ptr ds:[ecx+A8]
7C811201    C1E0 08                 shl eax,8
7C811204    0B81 A4000000           or eax,dword ptr ds:[ecx+A4]
7C81120A    C3                      retn

GetCurrentProcessId其实就是
引用:

7C809920 >  64:A1 18000000          mov eax,dword ptr fs:[18]
7C809926    8B40 20                 mov eax,dword ptr ds:[eax+20]
7C809929    C3                      retn

当然 还有个偶们最熟悉得 IsDebuggerPresent
引用:

7C813093 >  64:A1 18000000          mov eax,dword ptr fs:[18]
7C813099    8B40 30                 mov eax,dword ptr ds:[eax+30]
7C81309C    0FB640 02               movzx eax,byte ptr ds:[eax+2]
7C8130A0    C3                      retn

其实代码就3句而已 HOHO

3.以上所说得方法都是属于治标不知本或者是有局限性得,真正得对付修改IAT地址得手段得方法是自己用GetProcAddress函数获取
注意了!使用GetProcAddress函数获取最好是自己写GetProcAddress函数去获取,而不是直接调用系统得GetProcAddress函数,什么原因呢?因为系统得GetProcAddress函数可是已经杯做过手脚得了
给出一段代码 
lifeengines(netsowell)大侠得
引用:

DWORD GetFunctionAddress( HMODULE phModule,char* pProcName )
{
        if (!phModule)
                return        0;
        PIMAGE_DOS_HEADER        pimDH        =        (PIMAGE_DOS_HEADER)phModule;
        PIMAGE_NT_HEADERS        pimNH        =        (PIMAGE_NT_HEADERS)((char*)phModule+pimDH->e_lfanew);
        PIMAGE_EXPORT_DIRECTORY        pimED        =        (PIMAGE_EXPORT_DIRECTORY)((DWORD)phModule+pimNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
        DWORD        pExportSize        =        pimNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
        DWORD        pResult        =        0;

        
        if ((DWORD)pProcName < 0x10000)
        {
                if ((DWORD)pProcName        >=        pimED->NumberOfFunctions+pimED->Base || (DWORD)pProcName < pimED->Base)
                        return        0;
                pResult        =        (DWORD)phModule+((DWORD*)((DWORD)phModule+pimED->AddressOfFunctions))[(DWORD)pProcName-pimED->Base];
        }else
        {
                DWORD*        pAddressOfNames        =        (DWORD*)((DWORD)phModule+pimED->AddressOfNames);
                for (int i=0;i<pimED->NumberOfNames;i++)
                {
                        char*        pExportName        =        (char*)(pAddressOfNames[i]+(DWORD)phModule);
                        if (strcmp(pProcName,pExportName) == 0)
                        {
                                WORD*        pAddressOfNameOrdinals        =        (WORD*)((DWORD)phModule+pimED->AddressOfNameOrdinals);
                                pResult                =        (DWORD)phModule+((DWORD*)((DWORD)phModule+pimED->AddressOfFunctions))[pAddressOfNameOrdinals[i]];
                                break;
                        }
                }
        }
        if  (pResult != 0 && pResult        >=        (DWORD)pimED        &&        pResult <        (DWORD)pimED+pExportSize)
        {
                char*        pDirectStr        =        (char*)pResult;
                bool        pstrok        =        false;
                while (*pDirectStr)
                {
                        if (*pDirectStr == '.')
                        {
                                pstrok        =        true;
                                break;
                        }
                        pDirectStr++;
                }
                if (!pstrok)
                        return        0;
                char        pdllname[MAX_PATH];
                int                pnamelen        =        pDirectStr-(char*)pResult;
                if (pnamelen <= 0)
                        return        0;
                memcpy(pdllname,(char*)pResult,pnamelen);
                pdllname[pnamelen] = 0;
                HMODULE        phexmodule        =        GetModuleHandle(pdllname);
                pResult        =        GetFunctionAddress(phexmodule,pDirectStr+1);
        }

        return pResult;
}

二:在lifeengines大侠得帮助下,偶们就可以对付几乎所有得修改IAT地址得hook手段了,但是不要高兴得太早了,偶们还有一种人要对付,他们是Inline hook 修改前5个字节得人,上面得就是你自己取得了真实地址,但是开始代码杯改成了jmp xxxxxxxx,一样还是走到了hook函数里面去了,偶们必须在调用之前先恢复入口得5个字节
来自winroot大牛得 对付API-splicing的一种简单方法 [PSI_H]得文章中给出了一种方法


相关代码引用如下
引用:

首先脑子里想到的是,使用LoadLibrary/GetProcAddress函数来获取被拦截函数的原始代码,之后用它在内存里替换掉以前的代码,这 样就摘掉了对函数的HOOK。因为调用LoadLibrary将返回指向已加载模块的指针,所以必须将文件拷贝并加载此拷贝。下面的代码去除了对 ZwWriteVirtualMemory函数的拦截:
1.  // 将NTDLL.DLL文件拷入TEMP文件夹
2.   
3.          char szTemp[MAX_PATH];
4.   
5.          GetTempPath(MAX_PATH, szTemp);
6.   
7.          strcat(szTemp, "ntdll2.dll");
8.   
9.          CopyFile("C:\\Windows\\System32\\ntdll.dll", szTemp, TRUE);
10.          
11.          // 取得指向原始函数的指针
12.      HMODULE hMod = LoadLibrary(szTemp);
13.          void* ptr_orig = GetProcAddress(hMod, "ZwWriteVirtualMemory");    
14.          
15.          // 取得指向当前函数的指针
16.          void* ptr_new = GetProcAddress (LoadLibrary("ntdll.dll"), "ZwWriteVirtualMemory");
17.          
18.          // 设置内存访问权限
19.          DWORD dwOldProtect;
20.          VirtualProtect(ptr_new, 10, PAGE_EXECUTE_READWRITE, &dwOldProtect);
21.   
22.          // 替换函数的前10个(为保险起见)字节
23.          memcpy(ptr_new, ptr_orig, 10);
24.          
25.          FreeLibrary(hMod);
26.          DeleteFile(szTemp);
尽管这里给出的摘除HOOK的方法完全奏效,但需要加载新的dll模块,这可能会引起防火墙的暴怒。我所认为的更为优雅的办法就是只需从文件中读取所需要的字节。下面这个函数的代码恢复了API的原始的起始部分。
1.  bool RemoveFWHook(char* szDllPath, char* szFuncName) // szDllPath为DLL的完整路径 !
2.      {
3.          // 取得指向函数的指针
4.          HMODULE lpBase = LoadLibrary(szDllPath);
5.          LPVOID lpFunc = GetProcAddress(lpBase, szFuncName);
6.          if(!lpFunc)
7.              return false;
8.          // 取得RVA
9.          DWORD dwRVA = (DWORD)lpFunc-(DWORD)lpBase;
10.   
11.          // 将文件映射入内存
12.          HANDLE hFile = CreateFile(szDllPath,GENERIC_READ, FILE_SHARE_READ,
13.                              NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);
14.          if(INVALID_HANDLE_VALUE == hFile)
15.              return false;
16.   
17.          DWORD dwSize = GetFileSize(hFile, NULL);
18.   
19.          HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY|SEC_IMAGE, 0, dwSize, NULL);
20.   
21.          LPVOID lpBaseMap = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwSize);
22.          
23.      // 指向当前函数的指针
24.          LPVOID lpRealFunc = (LPVOID)((DWORD)lpBaseMap+dwRVA);
25.   
26.          // 修改访问权限并拷贝
27.          DWORD dwOldProtect;
28.          BOOL bRes=true;
29.          if(VirtualProtect(lpFunc, 10, PAGE_EXECUTE_READWRITE, &dwOldProtect))
30.          {
31.              memcpy(lpFunc, lpRealFunc, 10);
32.          }else{
33.              bRes=false;
34.          }
35.   
36.          UnmapViewOfFile(lpBaseMap);
37.   
38.          CloseHandle(hMapFile);
39.          CloseHandle(hFile);
40.   
41.          return bRes;
42.   
43.  }

请仔细看以上代码 以上代码实现了恢复函数入口10字节得功能,这样做,对付绝大多数得APIhook手段是完全奏效得,但是别忘记了,还有1位大侠得办法可以穿透他,HOOK API Lib by xIkUg
看代码 由于偶更加熟悉C代码一点。这里用得是海风月影得Hook api lib 0.2 for C
引用:

BYTE   BeforeStub[94] ={
    0x58,                                // 0 pop     eax
    0xEB, 0x08,                          // 1 jmp     short 0040100B
    0x00, 0x00, 0x00, 0x00,              // 3 dd      00000000
    0x00, 0x00, 0x00, 0x00,              // 7 dd      00000000
    0xE8, 0x00, 0x00, 0x00, 0x00,        // 11 call    00401010
    0x59,                                // 16 pop     ecx
    0x81, 0xE9, 0x10, 0x10, 0x40, 0x00,  // 17 sub     ecx, 00401010
    0x89, 0xA1, 0x03, 0x10, 0x40, 0x00,  // 23 mov     [ecx+401003], esp
    0x89, 0x81, 0x07, 0x10, 0x40, 0x00,  // 29 mov     [ecx+401007], eax
    0xE8, 0x36, 0x01, 0x00, 0x00,        // 35 call    HookProc 这里动态改变地址
    0x8B, 0x44, 0x24, 0xFC,              // 40 mov     eax, [esp - 4]
    0xE8, 0x00, 0x00, 0x00, 0x00,        // 44 call    0040102D
    0x59,                                // 49 pop     ecx
    0x89, 0x44, 0x24, 0xFC,              // 50 mov [esp - 4], eax
    0x81, 0xE9, 0x31, 0x10, 0x40, 0x00,  // 54 sub     ecx, 0040102D
    0x8B, 0xA1, 0x03, 0x10, 0x40, 0x00,  // 60 mov     esp, [ecx+401003]
    0x8B, 0x81, 0x07, 0x10, 0x40, 0x00,  // 66 mov     eax, [ecx+401007]
    0x50,                                // 72 push    eax
    0x90, 0x90, 0x90, 0x90,              // 73 保存入口处代码 16字节
    0x90, 0x90, 0x90, 0x90,                 
    0x90, 0x90, 0x90, 0x90,
    0x90, 0x90, 0x90, 0x90,   
    0xE9, 0x18, 0x01, 0x00, 0x00         // 89 jmp     HookedApi
};

如果偶们强制恢复前面10字节会出错得,偶们得手段应该先使用GetOpCodeSize获得准确得字节大小予以恢复,不过偶选择1个比较偷懒得办法,直接恢复200个字节(对A系列得API基本有效)这种做法也对那些在函数返回ret处下断点得有效,
参考文献:
西裤哥的 Hook api lib 0.2 for C by 海风月影 http://www.unpack.cn/viewthread.php?tid=11189
又一次写GetProcAddress函数 by lifeengines http://www.unpack.cn/thread-14189-1-1.html

  • 标 题: 答复
  • 作 者:zhuwg
  • 时 间:2007-07-06 18:30
  • 附 件:Anti HookAPI.rar

word格式得文档传上来一份 方便观看