首页
社区
课程
招聘
[原创]开源一个自己写的简易的windows内核hook框架
发表于: 2022-12-6 17:47 19411

[原创]开源一个自己写的简易的windows内核hook框架

2022-12-6 17:47
19411

如图,假设未被hook的代码如黄色图块显示。代码顺序为ABCDE,假设ABC三条指令加起来长度大于14字节,可以放下 ff 25 00 00 00 00 00 00 00 00 00 00 00 00 这个跳转。本框架会自动识别这三条代码的长度,然后将其替换为一个 ff25 jmp。其跳到自己申请的一块空间。跳转完成之后首先进行环境的保存,将所有寄存器保存到栈中。然后call一个C语言写的callback函数。可以在这个函数中进行相应的操作。如果这个函数的返回值是 FALSE ,则跳转回原函数处进行执行。如果为 TRUE ,则直接return,不再执行原函数。如果需要执行原函数,则重新POP所有之前保存的寄存器,然后执行 A B C 三条语句,最后通过一个 ff25 jmp跳到原函数中的下一行处执行(在此示例中是D处)。

第一个参数是需要设置fast_prehandler的hook的编号。第二个参数是自行编写的prehandler的buffer地址,第三个参数是buffer的大小,第四个参数是buffer中 ff25 jmp的地址的偏移。自行编写的代码的格式如下

如果前面cmp判断不需要处理,那么就跳到 @重新运行原来的code,运行原始逻辑,然后跳回到原来位置 。否则通过 ff25 jmp重新跳到原来的hook函数的地址,重新执行原来的hook_handler。

思路如下。将这个短跳的跳转地址改为 jmp 到原函数里面的jx目标地址 代码的地址。最后在执行完 A B C 之后通过一个 EB 短跳跳到原来的 JMP D 处。

其中返回值为FALSE表示执行完本函数后继续执行原来的函数。如果为true则不再执行原始的函数,直接返回。 pcontext 是一个指向之前保存的寄存器的指针。其结构如下

在handler函数中可以通过读取这些寄存器来获取调用的信息,也可以通过修改这些寄存器达到修改调用方调用原函数时的调用参数的目的。

运行结果如下

设置fast_prehandler的代码如下

运行结果如下

这里在fast_prehandler中判断了 rcx 是否等于1,如果不等于1就直接走原流程,因此没有打印出任何信息。我们把他改为 cmp rdx, 0x1000 再试一下

可以看到只有 rdx 为1000的时候才会进入到C语言编写的handler中。否则快速跳回到原流程中执行。

UCHAR buf[] = {
    0x48, 0x83, 0xF9, 0x01, // 00007FF806EA094F |  | cmp rcx,1 |
    0x74, 0x0E, // 00007FF806EA0953 | | je ntdll.7FF806EA0963  |
    0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, //00007FF806EA0955 |  | jmp qword ptr ds : [7FF806EA095B] |
    0x00, 0x00, // 00007FF806EA095B |  | add byte ptr ds : [rax] ,al |
    0x00, 0x00, // 00007FF806EA095D |  | add byte ptr ds : [rax] ,al |
    0x00, 0x00, // 00007FF806EA095F | | add byte ptr ds : [rax] ,al |
    0x00, 0x00, // 00007FF806EA0961 | | add byte ptr ds : [rax] ,al |
    0x90, // 00007FF806EA0963 | | nop
};
 
set_fast_prehandler(number, buf, sizeof buf, 12);
UCHAR buf[] = {
    0x48, 0x83, 0xF9, 0x01, // 00007FF806EA094F |  | cmp rcx,1 |
    0x74, 0x0E, // 00007FF806EA0953 | | je ntdll.7FF806EA0963  |
    0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, //00007FF806EA0955 |  | jmp qword ptr ds : [7FF806EA095B] |
    0x00, 0x00, // 00007FF806EA095B |  | add byte ptr ds : [rax] ,al |
    0x00, 0x00, // 00007FF806EA095D |  | add byte ptr ds : [rax] ,al |
    0x00, 0x00, // 00007FF806EA095F | | add byte ptr ds : [rax] ,al |
    0x00, 0x00, // 00007FF806EA0961 | | add byte ptr ds : [rax] ,al |
    0x90, // 00007FF806EA0963 | | nop
};
 
set_fast_prehandler(number, buf, sizeof buf, 12);
// prehandler格式类似如下
// cmp XXX
// jnz 重新运行原来的code,运行原始逻辑,然后跳回到原来位置  ; 对一些参数进行判断
// jmp [eip]  ; 一个ff25 jmp,offset填0
// 00 00
// 00 00
// 00 00
// 00 00
// @重新运行原来的code,运行原始逻辑,然后跳回到原来位置
// ; 这后面的原始逻辑由后面的代码自动填入,不用手动写。
// prehandler格式类似如下
// cmp XXX
// jnz 重新运行原来的code,运行原始逻辑,然后跳回到原来位置  ; 对一些参数进行判断
// jmp [eip]  ; 一个ff25 jmp,offset填0
// 00 00
// 00 00
// 00 00
// 00 00
// @重新运行原来的code,运行原始逻辑,然后跳回到原来位置
// ; 这后面的原始逻辑由后面的代码自动填入,不用手动写。
BOOLEAN NtOpenProcess_callback(PGuestContext pcontext)
BOOLEAN NtOpenProcess_callback(PGuestContext pcontext)
typedef struct _GuestContext
{
    ULONG64 mRflags;
    ULONG64 mRax;
    ULONG64 mRcx;
    ULONG64 mRdx;
    ULONG64 mRbx;
    ULONG64 mRsp;
    ULONG64 mRbp;
    ULONG64 mRsi;
    ULONG64 mRdi;
    ULONG64 mR8;
    ULONG64 mR9;
    ULONG64 mR10;
    ULONG64 mR11;
    ULONG64 mR12;
    ULONG64 mR13;
    ULONG64 mR14;
    ULONG64 mR15;
}GuestContext, * PGuestContext;
typedef struct _GuestContext
{
    ULONG64 mRflags;
    ULONG64 mRax;
    ULONG64 mRcx;
    ULONG64 mRdx;
    ULONG64 mRbx;
    ULONG64 mRsp;
    ULONG64 mRbp;
    ULONG64 mRsi;
    ULONG64 mRdi;
    ULONG64 mR8;
    ULONG64 mR9;
    ULONG64 mR10;
    ULONG64 mR11;
    ULONG64 mR12;
    ULONG64 mR13;
    ULONG64 mR14;
    ULONG64 mR15;
}GuestContext, * PGuestContext;
#include<ntifs.h>
#include <ntddk.h>
#include <ntstrsafe.h>
#include "hook.h"
 
ULONG64 num = 0;
 
VOID DRIVERUNLOAD(_In_ struct _DRIVER_OBJECT* DriverObject)
{
    KdPrintEx((77, 0, "unload\r\n"));
    reset_hook(num);
}
 
BOOLEAN NtOpenProcess_callback(PGuestContext pcontext)
{
    KdPrintEx((77, 0, "参数为 %llx %llx %llx %llx\r\n", pcontext->mRcx, pcontext->mRdx, pcontext->mR8, pcontext->mR9));
    return FALSE; // RETURN FALSE表示执行完本函数后继续执行原始的ntopenprocess函数。如果return true则不再执行原始的openprocess函数,直接返回。
}
 
 
VOID hook_NtOpenProcess()
{
    UNICODE_STRING unName = { 0 };
    RtlInitUnicodeString(&unName, L"NtOpenProcess");
    PUCHAR funcAddr = MmGetSystemRoutineAddress(&unName);
 
    hook_by_addr(funcAddr, NtOpenProcess_callback, &num);
}
 
 
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
    KdPrintEx((77, 0, "entry\r\n"));
    hook_NtOpenProcess();
    pDriver->DriverUnload = DRIVERUNLOAD;
    return STATUS_SUCCESS;
}
#include<ntifs.h>
#include <ntddk.h>
#include <ntstrsafe.h>
#include "hook.h"
 
ULONG64 num = 0;
 
VOID DRIVERUNLOAD(_In_ struct _DRIVER_OBJECT* DriverObject)
{
    KdPrintEx((77, 0, "unload\r\n"));
    reset_hook(num);
}
 
BOOLEAN NtOpenProcess_callback(PGuestContext pcontext)
{
    KdPrintEx((77, 0, "参数为 %llx %llx %llx %llx\r\n", pcontext->mRcx, pcontext->mRdx, pcontext->mR8, pcontext->mR9));
    return FALSE; // RETURN FALSE表示执行完本函数后继续执行原始的ntopenprocess函数。如果return true则不再执行原始的openprocess函数,直接返回。
}
 
 
VOID hook_NtOpenProcess()
{
    UNICODE_STRING unName = { 0 };
    RtlInitUnicodeString(&unName, L"NtOpenProcess");
    PUCHAR funcAddr = MmGetSystemRoutineAddress(&unName);
 
    hook_by_addr(funcAddr, NtOpenProcess_callback, &num);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-12-14 17:43 被smallzhong_编辑 ,原因:
收藏
免费 6
支持
分享
最新回复 (6)
雪    币: 2134
活跃值: (3911)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
2
资次
2022-12-6 18:20
0
雪    币: 246
活跃值: (4507)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
3
zydis4他的这个zydis.c zydis.h可以直接放在内核项目里?
2022-12-6 20:59
0
雪    币: 2999
活跃值: (5368)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
4
ookkaa zydis4他的这个zydis.c zydis.h可以直接放在内核项目里?
可以
2022-12-6 23:27
0
雪    币: 6357
活跃值: (3887)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
这个不蓝屏吗?
然后,以OpenProcess为例,能不能在callback里面,去改变R9里面的PID呢?比如原本PID是100,我改成101,然后执行原始的OpenProcess?
2022-12-7 09:39
0
雪    币: 2999
活跃值: (5368)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
6
大鲤鱼 这个不蓝屏吗? 然后,以OpenProcess为例,能不能在callback里面,去改变R9里面的PID呢?比如原本PID是100,我改成101,然后执行原始的OpenProcess?
可以,这就是写这个框架的目的
至于蓝屏,自己过PG咯,这代码只做inlinehook的工作,不干别的事。
2022-12-7 14:40
0
雪    币: 1825
活跃值: (5354)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
smallzhong_ 可以,这就是写这个框架的目的 至于蓝屏,自己过PG咯,这代码只做inlinehook的工作,不干别的事。
你能自己过pg?
2022-12-14 08:51
0
游客
登录 | 注册 方可回帖
返回
//