r0下的进程保护( 二 )


首先需要定义我们自己的函数
ULONG g_Pid = 568;NTSTATUS NTAPI MyOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess,POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId){NTSTATUS status;status = STATUS_SUCCESS;//当此进程为要保护的进程时if (ClientId->UniqueProcess == (HANDLE)g_Pid){//设为拒绝访问DesiredAccess = 0;}return NtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);}g_Pid定义为全局的,我们想保护哪个进程就将该进程的pid赋值给g_Pid 。
【r0下的进程保护】比如这里就保护Dbgview.exe 。

r0下的进程保护

文章插图
 
这里函数准备好以后,就要将该函数的指针覆盖原来NtOpenProcess的指针 。但是需要注意的是:我们自己改自己的代码是不用管权限的,改别人的代码很有可能这块内存是只读的,并不可写 。
那么本质上就是SSDT对应的物理页是只读的,这里有两种办法,我们都知道物理页的内存R/W位的属性是由PDE和PTE相与而来的,那么我们就可以改变SSDT对应的PDE和PTE的R/W属性,将物理页设置为可读可写的 。通过CR4寄存器判断是2-9-9-12分页还是10-10-12分页 。
if(RCR4 & 0x00000020){//说明是2-9-9-12分页KdPrint(("2-9-9-12分页 %pn",RCR4));KdPrint(("PTE1 %pn",*(Dword*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));*(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02;KdPrint(("PTE1 %pn",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));}else{//说明是10-10-12分页KdPrint(("10-10-12分页n"));KdPrint(("PTE1 %pn",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;KdPrint(("PTE2 %pn",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));}还有一种方式就是通过Cr0寄存器 。CR0寄存器的第16位叫做保护属性位,控制着页的读或写属性 。
r0下的进程保护

文章插图
 
WP为1 时, 不能修改只读的内存页 , WP为0 时, 可以修改只读的内存页 。那么我们就可以将WP位置为0,暂时关闭可读属性 。
VOID PageProtectOn(){__try{_asm{mov eax, cr0or eax, 10000hmov cr0, eaxsti}}__except (1){DbgPrint("PageProtectOn执行失败!");}}VOID PageProtectOff(){__try{_asm{climov eax, cr0and eax, not 10000h //and eax,0FFFEFFFFhmov cr0, eax}}__except (1){DbgPrint("PageProtectOff执行失败!");}}可以修改SSDT表后,就要写函数来修改NtOpenProcess指针,也就是我们的HOOK函数 。
NTSTATUS _hook(){NTSTATUS status;status = STATUS_SUCCESS;PageProtectOff();PoldAddress = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a];KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a] = (ULONG)MyOpenProcess;PageProtectOn();return status;}在修改SSDT表前先关闭物理页保护,修改完后要开启物理页保护,保证其他任务能够顺利完成 。这里的索引可以通过ida或者debug工具去看 。
r0下的进程保护

文章插图
 
然后就是卸载钩子,用于驱动卸载的时候使用 。
NTSTATUS _unhook(){NTSTATUS status;status = STATUS_SUCCESS;PageProtectOff();KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a] = PoldAddress;PageProtectOn();return status;}最后是入口和卸载函数
VOID DriverUnload(PDRIVER_OBJECT driver){_unhook();DbgPrint("卸载了 。。。。。n");}NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path){_hook();DbgPrint("跑起来了 。。。n");driver->DriverUnload = DriverUnload;return STATUS_SUCCESS;}最后编译,加载驱动,当我们尝试用任务管理器杀死Dbgview时会被拒绝 。
r0下的进程保护

文章插图
 
如果通过taskkill同样不行 。
r0下的进程保护

文章插图
 
后记在SSDT上写钩子,在0环是最低级的方式,可以看到编写代码十分简单,但是也是非常容易被检测的,比如我们通过PChunter这样的内核工具去看一下 。
r0下的进程保护

文章插图
 
可以看到NtOpenProcess赫然在列 。实际上SSDT已作为基础需要被了解 。
本文由SD原创发布
转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/262577
安全客 - 有思想的安全新媒体


推荐阅读