必備絕技--Hook大法( 上 )

標 題: 【原創】必備絕技--Hook大法( 上 )
作 者: Lvg
時 間: 2007-04-08,22:23
鏈 接: http://bbs.pediy.com/showthread.php?t=42362

【文章標題】: 必備絕技--Hook大法(上)
【文章作者】: LvG
【作者郵箱】: [email protected]
【作者聲明】: 這沒有什麼新鮮東西,其內容全部來自於前輩,姑且當作學習筆記。文字用自己的話寫出,四段代碼均出自別人(知道作者的,以註明),但短小精悍,就寫在一起了,便於察看。歡迎指正。
--------------------------------------------------------------------------------
【詳細過程】
  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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章