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

线程监控&保护

PsSetCreateThreadNotifyRoutine

线程监控使用到的API相对于进程监控简单,使用到PsSetCreateThreadNotifyRoutine,而这个值并不能像进程操作的API一样进行操作,这里我们首先先使用这个API来进行线程的监控

 

NotifyRoutine指向PCREATE_THREAD_NOTIFY_ROUTINE

PcreateThreadNotifyRoutine的结构如下,第一个参数为PID,第二个参数为TID,第三个参数表示是创建线程还是删除线程,创建线程则为TRUE,删除线程则为FALSE

那么我们这里就可以写出CREATE_THREAD_NOTIFY_ROUTINE函数

这里如果要将函数摘除,就需要用到PsRemoveCreateThreadNotifyRoutine函数,定义如下

我们如果想让进行线程的保护该如何操作呢?我们想要阻止线程的创建,就首先要找到对应的进程,再去进行拦截,因为在64位下都是通过回调的方式实现,那么我们就可以通过找到线程回调的地址,然后直接改为ret即可起到拦截线程创建的效果

1.通过PID找到EPROCESS

2.通过TID找到ETHREAD

3.通过EPROCESS得到进程路径

4.通过进程路径对应进程名

5.判断进程名是否相同

<1>若相同则找到线程回调函数的地址修改内容为ret

<2>若不相同则退出

那么我们该如何找到线程回调函数的地址呢?这里查阅资料后发现,3环将回调函数的地址放在了ETHREAD + 0x410偏移的Win32StartAddress里面

那么这里我们就能进行函数的编写,首先我们通过PsSetCreateThreadNotifyRoutine注册一个线程回调函数

然后我们再去写CreateThreadNotify这个回调函数,首先获取EPROCESSETHREAD,然后通过EPROCESS获取进程名

然后再判断进程名是否为我们要保护的线程

定位到回调函数的地址判断内存空间是否可用

这里的话定位到了回调函数的地址,如果我们要修改回调函数的值就要修改页保护属性,但是在64位下是不允许使用内联汇编的,这里的话就需要使用到汇编生成.obj文件来使用

修改完成之后这里我们使用ObDereferenceObject ,减少引用计数

这里因为我在win10 x64上做的实验,这里在关闭保护属性的时候一直报错导致0xC3一直修改不成功,这里就不放图了

模块监控&保护

PsSetLoadImageNotifyRoutine

和之前的函数一样都是指向一个结构,这里是LOAD_IMAGE_NOTIFY_ROUTINE

PLOAD_IMAGE_NOTIFY_ROUTINE

这里第一个参数是指向缓冲的Unicode字符串的指针,用于标识可执行映像文件,第二个参数表示PID,第三个参数指向IMAGE_INFO

IMAGE_INFO

具体成员的作用如下

  • Properties ImageAddressingMode 始终设置为IMAGE_ADDRESSING_MODE_32BIT。

  • SystemModeImage 设置为一个用于新加载的内核模式组件(如驱动程序),或者对于映射到用户空间的映像设置为 0。

  • ImageMappedToAllPids 始终设置为0。

  • ExtendedInfoPresent 如果设置了ExtendedInfoPresent标志,则IMAGE_INFO结构是图像信息结构的较大扩展版本的一部分(请参阅IMAGE_INFO_EX)。在Windows Vista中添加。有关详细信息,请参阅本备注部分的“扩展版本的图像信息结构”。

  • MachineTypeMismatch 始终设置为 0。在Windows 8 / Windows Server 2012中添加。

  • ImageSignatureLevel 代码完整性标记为映像的签名级别。该值是ntddk.h中的#define SESIGNING_LEVEL *常量之一。在Windows 8.1 / Windows Server 2012 R2中添加。

  • ImageSignatureType 代码完整性标记为映像的签名类型。该值是在ntddk.h中定义的SE_IMAGE_SIGNATURE_TYPE枚举值。在Windows 8.1 / Windows Server 2012 R2中添加。

  • ImagePartialMap 如果调用的映像视图是不映射整个映像的部分视图,则该值不为零; 0如果视图映射整个图像。在Windows 10 / Windows Server 2016中添加。

  • Reserved 始终设置为 0。

  • ImageBase 设置为映像的虚拟基地址。

  • ImageSelector 始终设置为 0。

  • ImageSize 映像的虚拟大小(以字节为单位)。

  • ImageSectionNumber 始终设置为 0。

那么我们首先还是定义一下回调函数

我们的回调函数在接收到消息的时候模块已经加载完成了,那么这里我们就只能够进行模块的卸载操作,在模块的ImageInfo 结构里面提供了加载的ImageBase,那么我们只需要找到OEP,即可计算得到DriverEntry的地址。那么我们找到入口点函数的地址之后,就可以修改错误码为STATUS_ACCESS_DENIED 0xC0000022,就能够达到卸载驱动模块的效果

对应的硬编码为B8 22 00 00 C0 C3

那么这里我们就可以进行卸载驱动模块函数的编写,首先定义指针指向OEP

PIMAGE_DOS_HEADER pDosHeader = pLoadImageBase;
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PCHAR)pDosHeader + pDosHeader->e_lfanew);
PVOID pAddressOfEntryPoint = (PVOID)((PCHAR)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint);

然后写入shellcode,通过MmCreateMdl写入,我们知道在内核里面是不能够随便进行读写操作的,这里就可以通过MDL写入的方式映射到虚拟内存实现shellcode的写入

使用MmBuildMdlForNonPagedPool更新MDL对物理内存的描述并映射到虚拟内存,然后写入数据,释放MDL

然后我们再尝试对DLL模块进行卸载,这里的话就不能像卸载驱动模块直接在入口点返回,因为DLL的入口点函数的返回值并不能够确定DLL能否加载成功。这里windows提供了一个未文档化的函数MmUnmapViewOfSection用来卸载进程中已经加载的模块

那么我们要想卸载模块,首先就肯定要获取所有的模块,使用到PsSetLoadImageNotifyRoutine设置回调函数来获取模块的加载信息

windows为了避免死锁,在进行模块加载回调函数的时候不能够进行其他操作,也就是说我们想要卸载DLL模块则需要等所有模块加载完毕之后才能进行卸载操作

这里来进行函数的编写

实现效果

这里要实现的效果就是阻止DriverTest.sys的加载

首先启动我们的监控驱动,然后加载DriverTest.sys可以看到拒绝访问

然后再卸载我们的监控驱动之后DriverTest.sys加载成功

然后我们再尝试注入Test.dll,可以看到注入失败

我们再去xp上尝试一下,首先加载驱动

当我们打开一个程序的时候都会打印出当前进程加载的dll模块

然后注入DLL,也是被拦截

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值