百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 软件资讯 > 正文

r0下的进程保护

dezehang 2024-11-22 13:03 1 浏览

前言

进程保护是众多AV或者病毒都要所具备的基础功能,本文就0环下通过SSDT来对进程进行保护进行探究,SSDT也不是什么新技术,但作为学习,老的技术我们同样需要掌握。

什么是SSDT

SSDT 的全称是System Services Descriptor Table,系统服务描述符表。

首先要明确的是他是一张表,通过windbg查看这张表。

dd  KeServiceDescriptorTable

这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来。

当我们在r 3调用一个API时,实际上调用的是一个接口,这里拿ReadProcessMemory举例。

ReadProcessMemory函数在kernel32.dll中导出,通过断点可以找到对应的反汇编代码。在汇编代码中,可以看到ReadProcessMemory调用了ntdll.dll中的ZwReadVirtualMemory函数。

在ZwReadVirtualMemory函数开始的地方下断点。

bp ZwReadVirtualMemory

实际上功能代码也没有在ZwReadVirtualMemory函数中实现,只是拿着一个索引号并跳转到一个地址。

这个索引号实际上就是SSDT表中的索引号,回到windbg,我们现在拿到索引号0xBA去SSDT表中找。

kd> dd  KeServiceDescriptorTable
80553fa0  80502b8c 00000000 0000011c 80503000
80553fb0  00000000 00000000 00000000 00000000
80553fc0  00000000 00000000 00000000 00000000
80553fd0  00000000 00000000 00000000 00000000
80553fe0  00002710 bf80c0b6 00000000 00000000
80553ff0  f8b67a80 f82e7b60 821bfa90 806e2f40
80554000  00000000 00000000 22bc349b 00000001
80554010  afa8a15b 01d7eb4f 00000000 00000000
kd> dd 80502b8c + 0xba*4
80502e74  805aa712 805c99e0 8060ea76 8060c43c
80502e84  8056f0d2 8063ab56 8061aca8 8061d332
80502e94  8059b804 8059c7cc 8059c1d4 8059baee
80502ea4  805bf456 80598d62 8059908e 805bf264
80502eb4  806064b6 8051ee82 8061cc3e 805cbd40
80502ec4  805cbc22 8061cd3a 8061ce20 8061cf48
80502ed4  8059a07c 8060db50 8060db50 805c892a
80502ee4  8063d80e 8060be28 80607fb8 8060882a
kd> u 805aa712

可以看到在0环调用的是NtReadVirtualMemory,这实际上才是真正实现功能的地方。而SSDT将r 3和r 0联系到一起。

SSDT结构

在 NT 4.0 以上的 Windows 操作系统中,默认就存在两个系统服务描述表,这两个调度表对应了两类不同的系统服务,

这两个调度表为:KeServiceDescriptorTable 和 KeServiceDescriptorTableShadow,

其中 KeServiceDescriptorTable 主要是处理来自 Ring3 层 Kernel32.dll 中的系统调用,

而 KeServiceDescriptorTableShadow 则主要处理来自 User32.dll 和 GDI32.dll 中的系统调用,

并且 KeServiceDescriptorTable 在 ntoskrnl.exe(Windows 操作系统内核文件,包括内核和执行体层)是导出的,

而 KeServiceDescriptorTableShadow 则是没有被 Windows 操作系统所导出,

而关于 SSDT 的全部内容则都是通过 KeServiceDescriptorTable 来完成的。

SSDT表的结构通过结构体表示为如下:

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
    KSYSTEM_SERVICE_TABLE   ntoskrnl;  // ntoskrnl.exe 的服务函数
    KSYSTEM_SERVICE_TABLE   win32k;    // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)
    KSYSTEM_SERVICE_TABLE   notUsed1;
    KSYSTEM_SERVICE_TABLE   notUsed2;
} KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;

其中每一项又是一个结构体:KSYSTEM_SERVICE_TABLE。通过结构体表示为如下:

typedef struct _KSYSTEM_SERVICE_TABLE
{
    PULONG  ServiceTableBase;          // SSDT (System Service Dispatch Table)的基地址
    PULONG  ServiceCounterTableBase;   // 用于 checked builds, 包含 SSDT 中每个服务被调用的次数
    ULONG   NumberOfService;           // 服务函数的个数, NumberOfService * 4 就是整个地址表的大小
    ULONG   ParamTableBase;            // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;

通过看图形化界面可以更加直观,下图是ntoskrnl.exe和win32k.sys的服务函数结构。

HOOK SSDT

有了上面的知识储备,理解SSDT HOOK就很容易了。

当3环程序执行后,操作系统拿着索引去SSDT表中找对应的0环程序,这时我们就可以在SSDT表中做点手脚,将某一个api函数的指针改成我们自己函数的指针,这样执行的将会是我们自己的代码。

首先需要定义我们自己的函数

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。

比如这里就保护Dbgview.exe。

这里函数准备好以后,就要将该函数的指针覆盖原来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分页 %p\n",RCR4));
    KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));
    *(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02; 
    KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));
}
else
{//说明是10-10-12分页
    KdPrint(("10-10-12分页\n"));
    KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));
    *(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;
    KdPrint(("PTE2 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));
}

还有一种方式就是通过Cr0寄存器。CR0寄存器的第16位叫做保护属性位,控制着页的读或写属性。

WP为1 时, 不能修改只读的内存页 , WP为0 时, 可以修改只读的内存页。那么我们就可以将WP位置为0,暂时关闭可读属性。

VOID PageProtectOn()
{
    __try
    {
        _asm
        {
            mov eax, cr0
            or eax, 10000h
            mov cr0, eax
            sti
        }
    }
    __except (1)
    {
        DbgPrint("PageProtectOn执行失败!");
    }
}
VOID PageProtectOff()
{
    __try
    {
        _asm
        {
            cli
            mov eax, cr0
            and eax, not 10000h //and eax,0FFFEFFFFh
            mov 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工具去看。

然后就是卸载钩子,用于驱动卸载的时候使用。

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时会被拒绝。

如果通过taskkill同样不行。

后记

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

可以看到NtOpenProcess赫然在列。实际上SSDT已作为基础需要被了解。

本文由SD原创发布
转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/262577
安全客 - 有思想的安全新媒体

相关推荐

WIN10系统如何安装UG10.0

随着科技的不断进步与更新,现在有很多公司己经安装上了WIN10的系统以及使用UG10.0了,但很多人反映WIN10系统安装UG10.0不好装,以下详细介绍一下1如果WIN10系统没有自带有JAVA需...

自学UG编程的心得分享

为什么有的人3个月学会基本的UG建模画图编程,有的断断续续3——5年才学会,还有的人干了7年的加工中心还不会电脑画图编程。这是什么原因?1.顾虑太多,什么都想得到,什么都想一起抓,总是上班加班没时间,...

UG/NX 绘制一个捞笊(zhào)模型,或者也可以叫它漏勺?

今天我们来看看这个模型,起因是群里有小伙伴说要做一个捞笊的模型,看见这名字直接给我整懵了,然后他发了张家里漏勺的图片才知道原来这玩意还有个这种名字。这东西相信每个小伙伴家里都有吧,它的建模方法也比较...

再也不用为学UG编程发愁了!380集最新UG资料免费送

上期发的UG教程很多粉丝都领到了,收获越来越多的好评!有你们一直陪伴真的很高兴,谢谢各位粉丝!为了给大家提供更优质的资源,这两个月都在整理你们最关心的UG资源,都是多位编程工厂老师傅的工厂实战精华,真...

优胜原创UG_3-4-5轴后处理下载

反复上机调试,安全稳定可靠,请放心使用2020.11.21,修复YSUG4-5轴后处理锁轴输出...

青华模具学院-UG10.0安装文件说明

青华模具学院分享:今天我们来跟大家一起学习NX10.0版本的安装方法,网上有很多这个版本的安装视频以及方法图文,但到最终安装软件时仍有很多新手对安装仍然感到头痛,基于这样的情况,我们特别就NX10.0...

UGnx10安装说明

温馨提示,安装前,请退出杀毒软件,关闭防火墙,因为这些软件可能阻断NX主程序和许可程序间的通信,导致安装后,软件无法启动。1、解压下载后的压缩包,右键,选择‘’解压到UGNX10_64位正式版(csl...

正版UG软件,正版UG代理,正版软件和盗版软件的区别

大家都知道,UG软件是制造业必不可少的一款三维软件,广泛应用于:CAE(有限元分析),CAD(产品设计/模具设计),CAM(计算机辅助制造编程),那么有人不禁要问了,正版软件和盗版软件在使用上有明显区...

非常全面的UG加工模块中英对照(图标注释)

大家好,我是粥粥老师,听说很多同学都在学习UG但是没有学习资料和安装包,今天粥粥老师就全部打包好免费发放给你们,那么怎么获取全套资料图档安装包呢领取途径①关注②评论、点赞、转发③私信“UG或者...

腾讯自研Git客户端 UGit|Git 图形界面客户端

支持平台:#Windows#macOS腾讯推出的一款Git图形界面客户端,简化了Git的使用流程,特别适合处理大型项目和文件。支持直接提交和推送操作,避免在大规模项目中由于远程频繁变更而导致...

经典收藏:UG重用库的一些不为人知小技巧

免费领取UG产品编程、UG多轴UG模具编程、安装包安装教程图档资料关注私信我“领取资料”,即可免费领取完整版,感谢支持,爱你们哟,么么主题:UG后处理+仿真+外挂UG重用库的正确使用方法:首先有...

UG编程常用指令G、M代码,快收藏好

今天给大家分享数控编程常用的指令代码,希望对正在学习路上的你带来一丝丝帮助。最好的方法就是转发到自己空间,方便以后学习。对了,如果你还需要其他UG教程学习资料,CNC加工中心的一些参数,以及UG画图,...

UG NX7.0中文版从入门到精通

Unigraphics(简称UG)是一套功能强大的CAD/CAE/CAM应用软件,UGNX7是其最新版本。《UGNX7从入门到精通(中文版)》以UGNX7为平台,从工程应用的角度出发,通过基...

经典UG建模基础练习图纸

UG是目前工作中比较优秀拥有大量用户的一款机械模具产品行业三维设计软件,cam加工丶软件支持全中文汉化;能够带给用户更为非凡的设计与加工新体验。很多朋友私信小编问有没有UG建模练习图纸,今天给大家分享...

UG NC软件基础操作,如何设置UG草图精度

默认情况下我们绘制草图一般只保留一位小数,即使你输入多位小数软件也会自动四舍五入,这个你做一些国标的图还好,国标以毫米为单位,一般保留小数点后一位就够了,但如果你做的图是英制单位,那么保留一位小数肯定...