邪恶八进制信息安全团队技术讨论组's Archiver

zhuwg 2007-4-13 21:00

[转载]必备绝技--Hook大法( 上 )

标 题: 【原创】必备绝技--Hook大法( 上 )
作 者: Lvg
时 间: 2007-04-08,22:23
链 接: [url]http://bbs.pediy.com/showthread.php?threadid=42362[/url]

【文章标题】: 必备绝技--Hook大法(上)
【文章作者】: LvG
【作者邮箱】: [email]LvG2008@gmail.com[/email]
【作者声明】: 这没有什么新鲜东西,其内容全部来自于前辈,姑且当作学习笔记。文字用自己的话写出,四段代码均出自别人(知道作者的,以注明),但短小精悍,就写在一起了,便于察看。欢迎指正。
--------------------------------------------------------------------------------
【详细过程】
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格式的详细知识可参见:[url]http://bbs.pediy.com/showthread.php?t=31840[/url]及<<加密与解密>>。
(二)大体框架:这里用伪码给个一般框架,以便有个大体印象。
文件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&#39;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&#39;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&#39;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&#39;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&#39;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] == &#39;,&#39;)
    {
     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 罗云彬, [url]http://asm.yeah.net[/url]
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 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 罗云彬, [url]http://asm.yeah.net[/url]
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 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 &#39;User32.dll&#39;,0
_szMessageBox db &#39;MessageBox&#39;,0
_szCaption db &#39;A rootkit !&#39;,0
_szText  db &#39;Hello,im LvG,but you cant find me!&#39;,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 &#39;无法打开远程线程!&#39;,0
szDesktopClass  db &#39;Progman&#39;,0
szDesktopWindow  db &#39;Program Manager&#39;,0
szDllKernel  db &#39;Kernel32.dll&#39;,0
szLoadLibrary  db &#39;LoadLibraryA&#39;,0
szGetProcAddress db &#39;GetProcAddress&#39;,0
szGetModuleHandle db &#39;GetModuleHandleA&#39;,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

页: [1]
© 1999-2008 EvilOctal Security Team