首页
社区
课程
招聘
[原创]Windows平台hook工具
发表于: 4天前 1095

[原创]Windows平台hook工具

4天前
1095

项目地址:2e1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6A6L8X3y4D9N6h3c8W2x3U0l9I4y4W2)9J5c8W2g2K6k6i4u0y4L8$3c8W2d9r3!0G2K9@1S2W2L8s2m8W2M7R3`.`.


工具下载地址:ad6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6A6L8X3y4D9N6h3c8W2x3U0l9I4y4W2)9J5c8W2g2K6k6i4u0y4L8$3c8W2d9r3!0G2K9@1S2W2L8s2m8W2M7W2)9J5c8Y4u0W2L8r3g2S2M7$3g2K6i4K6u0r3N6r3q4Y4i4K6u0r3N6U0q4Q4x3X3f1I4i4K6u0W2x3R3`.`.


inline hook,支持x64/x86


驱动没有签名,需要关闭签名校验


首先尝试使用如下命令关闭:


bcdedit /set testsigning on

然后重启,如果不起作用,参考下图:



如上图所示进行操作即可,第一步重启的时候需要按住shift,最后一步是根据提示看关闭签名校验是第几个选项,一般是第7个,按F7即可


hook指定进程的某个位置


假设说你要hook notepad.exe的createfilew,你可以编写如下hookseq文件:


# UserModeHookHelper hook sequence file
[hook]
module=kernel32.dll
offset=0x24e70
dllPath=C:\Users\x\Desktop\project\UserModeHookHelper-main\x64\Debug\VbsObjectCheck.dll
export=HookNotepadCreateFileW


0x24e70是createfilew函数相对于kernel32.dll的偏移


C:\Users\x\Desktop\project\UserModeHookHelper-main\x64\Debug\VbsObjectCheck.dll是包含了你的hook逻辑代码的dll

HookNotepadCreateFileW是你的hook逻辑代码所在的导出函数


dll的编写请参考示例


如果你要hook 32位的进程,请在hook code的头部添加一行代码

PROLOGWin32(esp);

如果是64位进程,请添加

PROLOGX64(rsp);


这两个东西的作用是帮你恢复cpu执行到hook点时原始寄存器的值,你可以在你的hook code中直接引用这些寄存器,也可以修改寄存器的值


PROLOGX64的实现代码:


#define PROLOGX64(rsp)                                                         \
    if (!(rsp)) {                                                            \
        Log(L"Fatal Error, RSP==NULL\n");                                    \
        return;                                                              \
    }                                                                        \
    PVOID original_rsp = (PVOID)((DWORD64)(rsp) + 0x80);                     \
    PVOID r15 = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x0);          \
    PVOID r14 = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x8);          \
    PVOID r13 = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x10);         \
    PVOID r12 = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x18);         \
    PVOID r11 = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x20);         \
    PVOID r10 = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x28);         \
    PVOID rbp = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x40);         \
    PVOID rdi = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x48);         \
    PVOID rsi = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x50);         \
    PVOID rbx = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x68);         \
    PVOID rax = (PVOID)*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x70);


比如说你想要修改rax寄存器的值,执行如下代码即可:


*(DWORD64*)((UCHAR*)(ULONG_PTR)(rsp) + 0x70) = new_rax_value;



添加hook list


首先需要将目标进程的路径添加到hooklist,有两种添加方式,第一种是在进程已经启动的情况下,右键添加,然后在进程下次启动的时候就会自动注入我们的master dll,以便进行后续的hookseq文件加载操作




另一种是通过菜单栏直接添加目标进程的路径:



instant hook 即刻hook


在上面的演示动画中,我们是在进程启动之后才加载hookseq文件对指定位置进行hook,但是某些场景下我们可能需要在某个dll加载后立刻hook其某个位置的代码,这个时候再使用上面的hook流程就有点太晚了,因此我引入instant hook功能,在目标dll加载后立刻对其目标代码进行hook


假设说我们现在要hook powershell进程的amsi.dll的AmsiScanBuffer函数,为了演示对x86的支持,我们使用32位的powershell进程来演示


我们的hook code的逻辑代码:


extern "C" __declspec(dllexport) VOID HookAmsiScanBuffer(ULONG esp) {
	PROLOGWin32(esp);

	// WRITE YOUR CODE HERE
	Log(L"amsi scan buffer file path: %s\n", (WCHAR*)ecx);
	// HOOK CODE END
	
	return;
}


hookseq文件内容:


# UserModeHookHelper hook sequence file
[hook]
module=amsi.dll
offset=0x5960
dllPath=C:\Users\x\Desktop\project\UserModeHookHelper-main\Release\VbsObjectCheck.dll
export=HookAmsiScanBuffer



这里简单介绍一下instant hook的原理,我们使用内核驱动在目标进程启动的时候通过APC将master dll注入进去,master dll会检查当前进程是否应用了instant hook,如果是,master dll就会hook ntdll!ldrloaddll函数的返回位置,在此处检查加载的dll的路径以及加载是否成功(eax/rax寄存器的值),在满足特定的条件之后,master dll就认为目标dll已经加载完毕,此时通过事件通知UMController(主控程序)根据hookseq文件对该dll的特定偏移进行注入,此时master dll会处于阻塞状态,目标dll将处于加载完成但未执行任何代码的状态,主控程序完成注入后会通过事件通知master dll注入已完成,此时master dll解除阻塞,ldrloaddll被放行,至此,整个instant hook流程就完成了


instant patch 即刻patch


在某些场景下,比如软件的试用期到了,弹窗让我们登录或者填写注册码,但是由于网络环境的限制(比如内网开发环境,没办法登录)导致我们无法正常使用,我这里就有一个现实的案例,内网开发环境安装vs2017,30天之后弹窗让我登录microsoft账户,但是由于我无法联网,登录不了,就想着看有没有办法不让他弹窗,挂上调试器在窗口创建函数下断点,最后找到激活窗口的创建位置是Microsoft_VisualStudio_ProductKeyDialog_ni+0x8a465,那么对于这种场景,instant patch就派上了用场


使用instant patch,我们可以直接把这个原本是call指令的地址patch成对应长度的nop指令,这个操作需要在Microsoft_VisualStudio_ProductKeyDialog_ni.dll加载之后立刻执行


编写如下patchseq文件:




[patch]
module=Microsoft.VisualStudio.ProductKeyDialog.ni.dll
offset=0x8a455
patch=909090909090



就是下面这种场景,导致我的vs2017无法使用:





使用instant patch绕过激活窗口:



kernel hook


除了用户模式的hook功能外,本工具还支持内核模式代码的hook,该功能以插件的形式提供,可以通过plugins菜单的KrnlModeHookHlp进入



和用户模式的hook一样,内核模式的hook也是通过读取hookseq文件来完成的




我们假设出来一个场景,我们需要在just_a_test.exe进程启动的时候断到调试器中,那么我们就可以通过hook进程创建的必经内核函数来实现这个操作,最先想到的必然是进程创建回调函数,我们用WinObjEx看一下有哪些已经注册了的回调函数:



那我们就使用第一个作为目标函数


需要注意的是,hook点不能包含带有间接寻址的非跳转指令,比如这里第一个回调函数:


0: kd> u nt+0x325710
nt!ViCreateProcessCallback:
fffff807`24125710 4883ec28        sub     rsp,28h
fffff807`24125714 833dad798f0000  cmp     dword ptr [nt!ViVerifierEnabled (fffff807`24a1d0c8)],0
fffff807`2412571b 488bc2          mov     rax,rdx
fffff807`2412571e 0f85942e1500    jne     nt!ViCreateProcessCallback+0x152ea8 (fffff807`242785b8)
fffff807`24125724 4883c428        add     rsp,28h
fffff807`24125728 c3              ret

可以看到第二条cmp指令里面有一个简介寻址,因此我们没办法在这个地方hook,不过我们可以选择第三条指令作为hook点,我们支持包含间接寻址的控制流类型指令



参考DriverHookCodeTemplate编写如下hook code:


VOID __declspec(dllexport) HookProcessCreate(PVOID rcx, PVOID rdx, PVOID r8, PVOID r9, PVOID rsp) {

	PROLOGX64(rsp);

	PUNICODE_STRING new_process_image_path = NULL;
	NTSTATUS st=DRVHCLIB_NT_LocateImageByPID((HANDLE)rdx, &new_process_image_path);

	if (0 == st && new_process_image_path!=NULL) {
		UNICODE_STRING a;
		RtlInitUnicodeString(&a, L"just_a_test.exe");

		if (DRVHCLIB_STR_RtlSuffixUnicodeString(&a, new_process_image_path, TRUE)) {
			DLog(L"new process image path: %wZ\n", new_process_image_path);
			DbgBreakPoint();
		}

		ExFreePool(new_process_image_path);
	}
	else {
		DLog(L"failed to get iamge path for pid=0x%x, status=0x%x\n", (HANDLE)rdx, st);
	}

	return;
}


然后编写如下hookseq:

# UserModeHookHelper hook sequence file
[hook]
module=ntoskrnl.exe
offset=0x32571B
dllPath=C:\Users\user\Desktop\DriverHookCodeTemplate.sys
export=HookProcessCreate


实际效果:



调试受保护进程


禁用PPL和进程对象回调


在我们的逆向工作中,经常需要动态调试,而有时被调试的进程可能是PPL进程且受到驱动保护,一般情况下驱动对进程的保护是通过ObProcess的pre回调来完成的,而PPL只需要修改对应进程EPROCESS的特定标志位即可绕过,如下:




禁用minifilter的PreCreateSection回调


某些安全厂商的驱动会注册这个回调函数以禁止不受信任的dll注入到自己的进程中,加载dll肯定会触发系统调用NtCreateSection,进而触发这个minifilter回调函数,内核驱动会在此处进行检查以拒绝不受信的dll被加载





强制注入dll



如果你临时想向一个进程中注入任意dll,可以右键选择force inject,然后再右键inject dll选择要注入的dll:



内核内存读写插件

KrnlMemWriter插件支持对任意内核内存进行指定长度的读写操作



early break


在某些场景下,你可能需要调试一个进程在启动阶段的代码,此时你就可以使用early break功能,首先将目标进程标记为early break,重启目标进程或者重启机器,我们的工具会在目标进程启动的时候中断到内核调试器中:



此时放行后目标进程会被umhh.dll中的sleep卡住,需要右键wake up来继续执行流,此时我们有时间使用用户态windbg来attach并进行调试



使用此功能有两种方式,一种是将目标进程添加至hook list,然后重启目标进程,另一种是开启Global Hook Mode然后重启计算机



[招生]科锐逆向工程师培训(2026年7月3日实地,远程教学同时开班, 第56期)!

最后于 23小时前 被12138编辑 ,原因:
收藏
免费 0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回