64位下使用回调函数实现监控(上)

前言

在32位的系统下,我们想要实现某些监控十分简单,只需要找到对应的API实现挂钩操作即可检测进程。但在64位系统下随着Patch Guard的引入,导致我们如果继续使用挂钩API的方式进行监控会出现不可控的情况发生。微软也考虑到了用户程序的开发,所以开放了方便用户调用的系统回调API函数,在64位系统下的监控,使用系统回调相对于直接hook的方式往往是更值得青睐的一方。

进程监控&保护

PsSetCreateProcessNotifyRoutineEx

这个函数主要是设置进程回调监控进程创建与退出

 

PsSetCreateProcessNotifyRoutineEx这个函数并不是随便就能够使用的,微软为了确保安全性要求拥有数字签名的驱动才能够使用此函数。这里微软如何检测是否有数字签名呢?这里就使用到了强制完整性检查

强制完整性检查是一种确保正在加载的二进制文件在加载前需要使用签名的策略,IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY标志在链接时通过使用/integritycheck链接器标志在PE头中进行设置,让正在加载的二进制文件必须签名,这个标志使windows内存管理器在加载时对二进制文件进行签名检查

那么微软就是通过加载二进制文件时是否存在标志来确认驱动的发布者身份是否为已知状态,这就是强制完整性检查

这里在内核里面,windows使用到MmVerifyCallbackFunction 这个内核函数来判断

到IDA里面继续跟MmVerifyCallbackFunction这个函数,发现其逻辑就是通过比较[rax+68h]是否包含了0x20来判断是否拥有正确的数字签名

这里的rax表示DriverSection,而DriverSection指向的是_LDR_DATA_TABLE_ENTRY结构,那么[rax + 0x68]指向的就是ProcessStaticImport

那么如果我们要使用PsSetCreateProcessNotifyRoutineEx这个函数就需要拥有数字签名,这里我们就可以将DriverObject->DriverSection->Flags的值与0x20按位或即可

这里我们就可以编写一个绕过强制完整性检查的函数,注意一下在32位和64位结构体的定义不同,需要分开定义

BOOLEAN bypass_signcheck(PDRIVER_OBJECT pDriverObject)
{
#ifdef _WIN64
 typedef struct _KLDR_DATA_TABLE_ENTRY
 {
  LIST_ENTRY listEntry;
  ULONG64 __Undefined1;
  ULONG64 __Undefined2;
  ULONG64 __Undefined3;
  ULONG64 NonPagedDebugInfo;
  ULONG64 DllBase;
  ULONG64 EntryPoint;
  ULONG SizeOfImage;
  UNICODE_STRING path;
  UNICODE_STRING name;
  ULONG   Flags;
  USHORT  LoadCount;
  USHORT  __Undefined5;
  ULONG64 __Undefined6;
  ULONG   CheckSum;
  ULONG   __padding1;
  ULONG   TimeDateStamp;
  ULONG   __padding2;
 } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
 typedef struct _KLDR_DATA_TABLE_ENTRY
 {
  LIST_ENTRY listEntry;
  ULONG unknown1;
  ULONG unknown2;
  ULONG unknown3;
  ULONG unknown4;
  ULONG unknown5;
  ULONG unknown6;
  ULONG unknown7;
  UNICODE_STRING path;
  UNICODE_STRING name;
  ULONG   Flags;
 } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#endif
 PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
 pLdrData->Flags = pLdrData->Flags | 0x20;

 return TRUE;

}

到这里我们就已经绕过了微软的强制完整性检查,能够调用PsSetCreateProcessNotifyRoutineEx函数,可以看到PsSetCreateProcessNotifyRoutineEx的第一个参数指向CREATE_PROCESS_NOTIFY_ROUTINE_EX,来执行我们需要执行的回调函数

PCREATE_PROCESS_NOTIFY_ROUTINE_EX

第一个参数是Process,指向EPROCESS结构,第二个参数ProcessId就是PID,第三个参数CreateInfo是一个指向PS_CREATE_NOTIFY_INFO的指针,当它为NULL时表明进程退出,不为NULL时表明进程创建,里面存储着要创建的进程信息

msdn的定义如下

然后我们再去看一下PS_CREATE_NOTIFY_INFO

PS_CREATE_NOTIFY_INFO

msdn定义如下

里的话我们要注意两个值,一个是ImageFileName即要创建的进程名,一个是CreationStatus,我们可以看到msdn里面说驱动程序可以将此值修改为错误代码以防止创建进程,这里我们如果想阻止进程创建就可以把这个值设置为STATUS_UNSUCCESSFUL

我们去WRK里面看一下实现,这个API是64位才有的,所以在WRK里面是没有PsSetCreateProcessNotifyRoutineEx这个函数的,但是在32位下有一个PsSetCreateProcessNotifyRoutine,我们看一下

通过源码可以发现是操作数组,这个数组里面存放的是我们填写的回调,而操作系统会依次调用回调,那我们跟随数组查看发现是个定长数组,里面只有8项,在64位系统下,这个数组的长度变为了64项

根据PCREATE_PROCESS_NOTIFY_ROUTINE_EX的结构定义回调函数

那么我们这里通过PsSetCreateProcessNotifyRoutineEx设置回调函数,通过判断status的返回值判断回调函数是否设置成功

NTSTATUS SetReFunction()
{
 NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)CreateProcessNotifyEx, FALSE);
    if (!NT_SUCCESS(status))
    {
        DbgPrint("回调函数设置失败, status=%X", status);
    }
    else
    {
        DbgPrint("进程监控已开启\r\n");
    }
}

然后进行回调函数的实现

VOID CreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)

首先判断CreateInfo的值,如果为NULL则表示进程退出,如果不为NULL才为进程的创建

那么这里通过PsGetProcessImageFileName获取进程名之后进行判断,如果是我们想要拦截的进程就通过设置CreationStatus的值为STATUS_UNSUCCESSFUL来阻止进程的创建

这里我们的回调函数就已经完成,这里需要注意,在卸载驱动的时候就需要将回调函数摘除,否则新创建或者退出的进程会因为找不到回调函数而导致蓝屏

VOID DriverUnload(IN PDEVICE_OBJECT driverObject)
{
    NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)CreateProcessNotifyEx, TRUE);
 
    if (!NT_SUCCESS(status))
    {
        DbgPrint("回调函数删除失败\r\n status=%X", status);
    }
    else
    {
        DbgPrint("回调函数成功删除\r\n");
    }
    DbgPrint("驱动卸载完成\r\n");
}

实现效果

首先注册一下驱动

然后这里首先执行一下我们的exe

然后加载我们的驱动可以看到这里test.exe已经不能够运行

那么这里我们再卸载一下驱动可以发现又可以运行成功

这里可能有点不太明显,我们将拦截的exe改成notepad.exe看下效果

启动驱动可以看到这里启动失败

卸载驱动即可启动成功

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值