• 2009-03-13

    API HOOK 之 DLL代码修改 再之 放弃

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://wolfhead.blogbus.com/logs/36511018.html

    之前使用修改PE头IMAGE_IMPORT_DISCRIPTOR的方法进行API HOOK网上资料大多都是这个,对于要遍历每一个模块很是不满。。。所以决定直接修改DLL代码,思想类似于THUNK,总的来说,仅仅对于HOOK API来说这是可行的

    #pragma pack(push,1)
    typedef struct _ThunkCode
    {
     unsigned char JMP;
     DWORD Dest;
    }ThunkCode;
    #pragma pack(pop)

    int _stdcall A()
    {
     int i = 0;
     ++i;
     return 0;
    }

    int _tmain(int argc, _TCHAR* argv[])
    {

     HMODULE hModule = LoadLibrary(TEXT("user32.dll"));
     if (NULL == hModule)
     {
      printf("LoadLibrary fail\n");
      return 0;
     }

     void* ProcAddress = GetProcAddress(hModule,"MessageBoxW");
     if (NULL == ProcAddress)
     {
      printf("GetProcAddress fail\n");
      return 0;
     }

     printf("proc address %p\n",ProcAddress);

     void * Dest = A;
     ThunkCode OriginalCode;
     ThunkCode thunkCode;
     thunkCode.JMP = 0xe9;
     thunkCode.Dest = (int)Dest - (int)ProcAddress - sizeof(DWORD) - 1;
     printf("%d\n",sizeof(thunkCode));
     DWORD OriginalProtect;
     ::VirtualProtect(ProcAddress,sizeof(thunkCode),PAGE_EXECUTE_READWRITE,&OriginalProtect);
     DWORD BytesRead;
     ::ReadProcessMemory(GetCurrentProcess(),ProcAddress,&OriginalCode,sizeof(OriginalCode),&BytesRead);
     ::WriteProcessMemory(GetCurrentProcess(),ProcAddress,&thunkCode,sizeof(thunkCode),&BytesRead);

     MessageBoxW(NULL,NULL,NULL,0);//断点断点

     while(1);

     return 0;
    }

    在断点处反汇编:

    011715C8  mov         esi,esp
    011715CA  push        0   
    011715CC  push        0   
    011715CE  push        0   
    011715D0  push        0   
    011715D2  call        dword ptr [__imp__MessageBoxW@16 (1178370h)]

    JUMP TO----》

    7646D667  jmp         A (1171104h)

    JUMP TO----》

    int _stdcall A()
    {
    01171420  push        ebp 
    01171421  mov         ebp,esp
    01171423  sub         esp,0CCh
    01171429  push        ebx 
    0117142A  push        esi 
    0117142B  push        edi 
    0117142C  lea         edi,[ebp-0CCh]
    01171432  mov         ecx,33h
    01171437  mov         eax,0CCCCCCCCh
    0117143C  rep stos    dword ptr es:[edi]
     int i = 0;
    0117143E  mov         dword ptr [i],0
     ++i;
    01171445  mov         eax,dword ptr [i]
    01171448  add         eax,1
    0117144B  mov         dword ptr [i],eax
     return 0;
    0117144E  xor         eax,eax
    }

    大家看到了,是很成功的,但是我还是放弃了这种做法,为什么?

    好了首先我进行试验验证DLL的代码段在系统中可能不只存在一份的,好,因此我写如下代码,调试:


    int _tmain(int argc, _TCHAR* argv[])
    {
     MessageBoxW(NULL,NULL,NULL,0);
     return 0;
    }

    反汇编:

     MessageBoxW(NULL,NULL,NULL,0);
    00C913BE  mov         esi,esp
    00C913C0  push        0   
    00C913C2  push        0   
    00C913C4  push        0   
    00C913C6  push        0   
    00C913C8  call        dword ptr [__imp__MessageBoxW@16 (0C98338h)]

    -----》

    7646D667  mov         edi,edi
    7646D669  push        ebp 
    7646D66A  mov         ebp,esp
    7646D66C  cmp         dword ptr ds:[76479CA8h],0

    !并无变化,为什么?在运行这一段程序时,之前修改API的程序并未退出。好原因就是Copy on write:

    The MIcrosoft Windows NT, Windows 2000, or Windows XP's Copy on Write page protection is a concept that allows multiple applications to map their virtual address spaces to share the same physical pages, until an application needs to modify the page and have its own instance copy. This is part of a technique called Lazy Evaluation, which allows the system to not waste time by committing resources, time, or execution until/unless absolutely necessary. Copy on Write allows the virtual memory manager to save memory and execution time.

    Copy on Write works as follows: In generic terms, an application can load something into its virtual memory (for example, a code section or DLL code). Virtual memory is mapped to physical memory. Another process may want to load the same thing into its virtual memory. As long as neither process writes to this memory, they can map to, and share, the same physical pages.

    If either process needs to write to this memory, because the memory is marked as Copy on Write, the physical page frame will be copied somewhere else in physical memory. Fixups are made for the virtual memory mapping of the writing process. Both applications now have their own instance of the memory contents. In short, applications can share the same physical memory with Copy on Write, until one of the applications has to modify the contents. At that point, a new copy of the contents is made, and the writing process has its own copy.


    也就是说,除非我可以将修改写入物理内存并且通知其他mapping的程序才有可能让修改对所有调用者起作用。同时如果需要全局的对API进行修改那么必然每个进程都会拥有一份DLL副本,问题是很严重的。

     

    同时,使用这个方法的问题在于我必须在调用原始API版本前恢复API前端5bytes的数据。形如:

    void MySleep(int)

    {

     DisHook(Sleep);

     Sleep(1000);

     Hook(Sleep);

    }

    好Sleep用在这里是一个很好的例子,由于DLL对于Thread来说是共享的因此当多线程的情况下:

    Thread1:  MySleep {DisHook  -》 Sleep(1000) ------------------》 Hook}

    Thread2: 100ms-------》MySleep{//!!!DisHooked by Thread1!!} == Sleep()

    好了,这是严重的同步问题,而且怎么解决呢?似乎很难,除非使用同步机制,那就明显很有可能的使系统表现出异样的运行状态。

    同步的问题并没有真正写代码予以验证,不过相信这是不会有问题的。

    有感于今天敏锐的嗅觉和昨天的愚钝。


    收藏到:Del.icio.us




    评论

  • 布银布舒服斯基