首页
社区
课程
招聘
“可过PG”的驱动伪装与隐藏
发表于: 2023-4-19 01:07 31379

“可过PG”的驱动伪装与隐藏

2023-4-19 01:07
31379

潜水太久啦,来水个帖。

非常简单,手写个驱动,在DriverEntry上加上一句:

手动加载,调用栈如下:

发现华点:

IopLoadDriver基本上只有第一个参数没确定意义了。

使用IDA对IopLoadUnloadDriver进行引用分析。来自IopLoadDriverImage

再对IopLoadDriverImage引用分析。来自NtLoadDriver

这唯一的一个参数,就是注册表对应的服务路径啦,例:

\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Test_Drv

这个注册表是在你 Ring3 调用 CreateServiceW的时候就填好的。

那么调用路径就清晰啦:

这个函数是基本算是最重要的函数啦,毕竟是它调用的DriverEntry。

而分析上述调用路径的唯一作用,就是为了得到 IopLoadDriver参数的意义。(注册表的 Handle)

由于这个函数比较大,仅分析重要部分,伪代码如下:

这个函数非常强大,传入需要加载的驱动的路径,就可以直接加载一个可直接运行的无DriverObject驱动。(请读者自行测试)

功能包括:

创建DriverSection、映射内存、修复重定位、将DriverSection添加入PsLoadedModuleList、填充IAT、填充_security_cookie、...(这几个功能已经够用了)

没错,上述的 DriverSection 就是 驱动模块链表 连接的结构体 :_LDR_DATA_TABLE_ENTRY。

同样,这个函数比较大,这里仅列出重要部分伪代码:

至此,一个 可在驱动模块链表(PsLoadedModuleList)可以找到的驱动就加载完成了。

如果你需要隐藏或伪装的驱动本身有签名,就不用patch了,如果硬要加载一个无签名的,那才需要patch,最好不要啦,因为有蓝屏几率。

在通过MiObtainSectionForDriver获取DriverSection的时候,Windows就会进行数字签名认证啦!调用栈如下:

通过 SeValidateImageHeader进入 CI.DLL 进行签名校验,CL.DLL 部分就不再继续跟了,我们主要看如何进入CI.DLL:

网上一般通过Patch CI!g_CiOptions实现绕过数字签名校验,这边就不搞这么复杂了,我们 patch qword_14040EF40,换成我们的函数,返回 STATUS_SUCCESS 就可以通过签名校验了,虽然 qword_14040EF40受PG保护,但是只修改一会,加载完驱动就改回去,PG就反应不过来了。(我试过很多次,基本上改了 qword_14040EF40 后几个小时PG才能反应过来)。定位:

win10、win11 我都看过, 都是在 &SeCiCallbacks + 0x20 的位置,可能有些版本不同。

操作很简单,通过调用上面说到过的未导出函数,就能实现。

需要隐藏的驱动或伪装的驱动代码如下:(主要是创建一个 名为 ICEY_Device的设备,创建成功说明 驱动加载成功)

编译出来的 驱动文件 名字为 : Test_Drv.sys

这里就贴一下伪代码,实际代码请查看项目:

对就这么简单,就加载了一个隐藏驱动(无DriverObject 、无DriverSection)

由于没有这两个结构体,所以这个驱动一些函数是无法调用的,如何解决这里不赘述了。

在项目中我采取了IO劫持的方式,让我的隐藏驱动创建设备,原理就是给隐藏驱动的 DriverEntry第一个参数传进一个没有设备的DriverObject,详情请看代码。

ARK工具找不到:

WinObj 双击 设备名字测试:(成功输出 “Create File Finsh!”)

同上,只贴伪代码,实际代码请查看项目:

将 Test_Drv.sys 伪装成 360AntiHacker64.sys 。

ARK签名检测:

火绒剑检测:

不保证100%可过 PG,只是在我测试 40+小时以上,win10 win11 均未被PG检测。

项目用到大量未导出函数,需要自己想办法获取函数地址。进入 Kernel_PDB.c InitAllOffSet() 进行修改

管安装不管卸载,重启即可卸载哈哈哈哈。

Drv_Hide_And_Camouflage

DbgBreakPoint();
DbgBreakPoint();
 
 
ExpWorkerThread -> IopLoadUnloadDriver -> IopLoadDriver -> DriverEntry
ExpWorkerThread -> IopLoadUnloadDriver -> IopLoadDriver -> DriverEntry
 
 
 
 
 
 
 
 
 
 
添加注册项 -> NtLoadDriver -> IopLoadDriverImage -> IopLoadUnloadDriver -> IopLoadDriver -> DriverEntry
添加注册项 -> NtLoadDriver -> IopLoadDriverImage -> IopLoadUnloadDriver -> IopLoadDriver -> DriverEntry
 
 
//获取注册表项基本信息
status = NtQueryKey(KeyHandle, KeyBasicInformation, P, Length, &Length);
 
//从注册表键中获取驱动路径 放入 Destination
//例:\??\C:\Users\nihao\Desktop\Kernel_PDB_Frame_R0.sys
status = IopBuildFullDriverPath(&v38, KeyHandle, &Destination);
 
//这个函数非常重要!返回DriverObject.DriverSection 和 驱动基地址 BaseAddress
//通过这个函数就能直接安装一个可运行的驱动了,待会分析
status = MmLoadSystemImageEx(&Destination, 0, 0, 0, &DriverSection, &BaseAddress);
 
//创建一个新的DriverObject
OBJECT_ATTRIBUTES Obj_Att = { 0 };
Obj_Att.Length = 0x30;
Obj_Att.Attributes = 0x250;
Obj_Att.ObjectName = &ObjectName;// L"\\Driver\\Test_Drv" "\\Driver\\"串接 服务名
status = ObCreateObjectEx(KeGetCurrentThread()->PreviousMode,
                              IoDriverObjectType,
                             &Obj_Att,// POBJECT_ATTRIBUTES
                             0,
                             &out,//这个是一个ULONG64 类型 没啥用
                             0x1A0,
                             0,
                             0,
                             &DriverObject,//返回新的 DriverObject
                             0i64);
 
 
//填充DriverObject
memset(DriverObject, 0, 0x1A0ui64);
DriverObject->DriverExtension = &DriverObject[1];// 准备填充 Driver_Object
*&DriverObject[1].Type = DriverObject;
ReturnLength = 28;
memset64(DriverObject->MajorFunction, IopInvalidDeviceRequest, 0x1Cui64);
v18 = BaseAddress;//MmLoadSystemImageEx返回的基地址
v19 = BaseAddress;
*&DriverObject->Type = 0x1500004; // Type = 4,Size = 0x150
v20 = RtlImageNtHeader(v19);
*v40 = v20->OptionalHeader.MinorImageVersion | (v20->OptionalHeader.MajorImageVersion << 16);
v21 = &v18[v20->OptionalHeader.AddressOfEntryPoint];
if ( (v20->OptionalHeader.DllCharacteristics & 0x2000) == 0 )
    DriverObject->Flags |= 2u;
DriverObject->DriverInit = v21;//DriverEntry 啦
DriverObject->DriverSection = DriverSection;//MmLoadSystemImageEx 返回的DriverSection
DriverObject->DriverStart = v18;//基地址
DriverObject->DriverSize = v20->OptionalHeader.SizeOfImage;
 
//将DriverObject添加入全局Object
status = ObInsertObjectEx(DriverObject, 0i64, 1i64, 0, 0, 0i64, &Handle);// 加入Object
 
//继续填充DriverObject
status = ObReferenceObjectByHandle(Handle,0,IoDriverObjectType,
                        KeGetCurrentThread()->PreviousMode,
                        &DriverObject,
                        0i64);//测试一下能不能通过Handle获得DriverObject ?!
ZwClose(Handle);
DriverObject->HardwareDatabase = PCmRegistryMachineHardwareDescriptionSystemName;
DriverObject->DriverName.Buffer = ExAllocatePool(NonPagedPool, ObjectName.MaximumLength);
DriverObject->DriverName.Length = ObjectName.Length;
DriverObject->DriverName.MaximumLength = ObjectName.MaximumLength;
memcpy(DriverObject->DriverName.Buffer, ObjectName.Buffer, ObjectName.MaximumLength);
//这里的 ObjectName 就是 L"\\Driver\\Test_Drv"  "\\Driver\\"串接 服务名
 
//调用DriverEntry
status = ZwQueryObject(HRegistry, 1, PSTR, 0x1000, &NtQueryObjReturnLen);//拿到注册表路径
DriverObject->DriverInit(DriverObject,PSTR);//刚好对应DriverEntry的两个参数
 
//提交驱动申请的设备(如果驱动有注册设备)
IopReadyDeviceObjects(DriverObject);//不调用这个函数的话,设备无法打开
 
//收尾工作
//获取注册表项基本信息
status = NtQueryKey(KeyHandle, KeyBasicInformation, P, Length, &Length);
 
//从注册表键中获取驱动路径 放入 Destination
//例:\??\C:\Users\nihao\Desktop\Kernel_PDB_Frame_R0.sys
status = IopBuildFullDriverPath(&v38, KeyHandle, &Destination);
 
//这个函数非常重要!返回DriverObject.DriverSection 和 驱动基地址 BaseAddress
//通过这个函数就能直接安装一个可运行的驱动了,待会分析
status = MmLoadSystemImageEx(&Destination, 0, 0, 0, &DriverSection, &BaseAddress);
 
//创建一个新的DriverObject
OBJECT_ATTRIBUTES Obj_Att = { 0 };
Obj_Att.Length = 0x30;
Obj_Att.Attributes = 0x250;
Obj_Att.ObjectName = &ObjectName;// L"\\Driver\\Test_Drv" "\\Driver\\"串接 服务名
status = ObCreateObjectEx(KeGetCurrentThread()->PreviousMode,
                              IoDriverObjectType,
                             &Obj_Att,// POBJECT_ATTRIBUTES
                             0,
                             &out,//这个是一个ULONG64 类型 没啥用
                             0x1A0,
                             0,
                             0,
                             &DriverObject,//返回新的 DriverObject
                             0i64);
 
 
//填充DriverObject
memset(DriverObject, 0, 0x1A0ui64);
DriverObject->DriverExtension = &DriverObject[1];// 准备填充 Driver_Object
*&DriverObject[1].Type = DriverObject;
ReturnLength = 28;
memset64(DriverObject->MajorFunction, IopInvalidDeviceRequest, 0x1Cui64);
v18 = BaseAddress;//MmLoadSystemImageEx返回的基地址
v19 = BaseAddress;
*&DriverObject->Type = 0x1500004; // Type = 4,Size = 0x150
v20 = RtlImageNtHeader(v19);
*v40 = v20->OptionalHeader.MinorImageVersion | (v20->OptionalHeader.MajorImageVersion << 16);
v21 = &v18[v20->OptionalHeader.AddressOfEntryPoint];
if ( (v20->OptionalHeader.DllCharacteristics & 0x2000) == 0 )
    DriverObject->Flags |= 2u;
DriverObject->DriverInit = v21;//DriverEntry 啦
DriverObject->DriverSection = DriverSection;//MmLoadSystemImageEx 返回的DriverSection
DriverObject->DriverStart = v18;//基地址
DriverObject->DriverSize = v20->OptionalHeader.SizeOfImage;
 
//将DriverObject添加入全局Object
status = ObInsertObjectEx(DriverObject, 0i64, 1i64, 0, 0, 0i64, &Handle);// 加入Object
 
//继续填充DriverObject
status = ObReferenceObjectByHandle(Handle,0,IoDriverObjectType,
                        KeGetCurrentThread()->PreviousMode,
                        &DriverObject,
                        0i64);//测试一下能不能通过Handle获得DriverObject ?!
ZwClose(Handle);
DriverObject->HardwareDatabase = PCmRegistryMachineHardwareDescriptionSystemName;
DriverObject->DriverName.Buffer = ExAllocatePool(NonPagedPool, ObjectName.MaximumLength);
DriverObject->DriverName.Length = ObjectName.Length;
DriverObject->DriverName.MaximumLength = ObjectName.MaximumLength;
memcpy(DriverObject->DriverName.Buffer, ObjectName.Buffer, ObjectName.MaximumLength);
//这里的 ObjectName 就是 L"\\Driver\\Test_Drv"  "\\Driver\\"串接 服务名
 
//调用DriverEntry
status = ZwQueryObject(HRegistry, 1, PSTR, 0x1000, &NtQueryObjReturnLen);//拿到注册表路径
DriverObject->DriverInit(DriverObject,PSTR);//刚好对应DriverEntry的两个参数
 
//提交驱动申请的设备(如果驱动有注册设备)
IopReadyDeviceObjects(DriverObject);//不调用这个函数的话,设备无法打开
 
//收尾工作
 
 
 
 
//准备一下接下来需要用到的字符串
status = MiGenerateSystemImageNames(DriverPath, zero1, zero2, &v69, v73, &String1);
//string1同DriverPath \??\C:\Users\nihao\Desktop\Kernel_PDB_Frame_R0.sys
 
//创建DriverSection (仅创建、不插入PsLoadedModuleList)
status = MiObtainSectionForDriver(&String1, DriverPath, 0, 0, &DriverSection);
 
//申请映射地址
PUCHAR Section = DriverSection + 0x70;
int out_uk;
DllBase = MiGetSystemAddressForImage(Section, 0, &out_uk);//此时还未映射
 
//进行映射
KIRQL OldIrql;
KeRaiseIrql(1, &OldIrql);
status = MiMapSystemImage(Section, DllBase);//一定要是 irql = 1 才能映射成功
KeLowerIrql(OldIrql);//映射好的驱动是已经修好了重定位的了,非常nice
 
//填充DriverSection信息并插入PsLoadedModuleList
DriverSection->SizeOfImage = DllSize;
DriverSection->DllBase = DllBase;
status = MiConstructLoaderEntry(DriverSection, &OutU, &AString, 0, 1, &NewDriverSection);
//此时可以Free旧的DriverSection,新的 NewDriverSection 才是真正有效的。
// &OutU和&AString都是没用的输出值,不用管,给个空间就行了
 
//修复IAT
 
//填充_security_cookie
//准备一下接下来需要用到的字符串
status = MiGenerateSystemImageNames(DriverPath, zero1, zero2, &v69, v73, &String1);
//string1同DriverPath \??\C:\Users\nihao\Desktop\Kernel_PDB_Frame_R0.sys
 
//创建DriverSection (仅创建、不插入PsLoadedModuleList)
status = MiObtainSectionForDriver(&String1, DriverPath, 0, 0, &DriverSection);
 
//申请映射地址
PUCHAR Section = DriverSection + 0x70;
int out_uk;
DllBase = MiGetSystemAddressForImage(Section, 0, &out_uk);//此时还未映射
 
//进行映射
KIRQL OldIrql;
KeRaiseIrql(1, &OldIrql);
status = MiMapSystemImage(Section, DllBase);//一定要是 irql = 1 才能映射成功
KeLowerIrql(OldIrql);//映射好的驱动是已经修好了重定位的了,非常nice
 
//填充DriverSection信息并插入PsLoadedModuleList
DriverSection->SizeOfImage = DllSize;
DriverSection->DllBase = DllBase;
status = MiConstructLoaderEntry(DriverSection, &OutU, &AString, 0, 1, &NewDriverSection);
//此时可以Free旧的DriverSection,新的 NewDriverSection 才是真正有效的。
// &OutU和&AString都是没用的输出值,不用管,给个空间就行了
 
//修复IAT
 
//填充_security_cookie
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-4-26 11:22 被icey_编辑 ,原因:
收藏
免费 15
支持
分享
最新回复 (22)
雪    币: 7314
活跃值: (4512)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
PG是什么意思?
2023-4-19 07:10
0
雪    币: 7519
活跃值: (5352)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
nekaxi PG是什么意思?
屁股
2023-4-19 07:29
0
雪    币: 187
活跃值: (863)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
没看出来跟屁股的关系
2023-4-19 09:04
0
雪    币: 2948
活跃值: (30846)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
tql
2023-4-19 09:06
1
雪    币: 1230
活跃值: (1765)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
国外17年玩过了...
2023-4-19 09:11
0
雪    币: 10946
活跃值: (2900)
能力值: ( LV5,RANK:71 )
在线值:
发帖
回帖
粉丝
7

干货满满,感谢分享

最后于 2023-4-19 09:23 被joker陈编辑 ,原因:
2023-4-19 09:22
0
雪    币: 1795
活跃值: (3995)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
感谢分享
2023-4-19 11:01
0
雪    币: 7314
活跃值: (4512)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
lononan 屁股
2023-4-20 06:03
0
雪    币: 0
活跃值: (236)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
和 kdmapper区别是什么
2023-4-23 21:20
0
雪    币: 168
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
大佬,我在使用MiGetSystemAddressForImage映射后,dllbase的地址错误,全是??。这是为什么
2023-5-19 12:42
0
雪    币: 1246
活跃值: (1978)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
12
mb_jglkxfsm 大佬,我在使用MiGetSystemAddressForImage映射后,dllbase的地址错误,全是??。这是为什么
MiGetSystemAddressForImage只是向系统申请一个空间,它返回一个地址,还没映射。把返回的地址通过MiMapSystemImage映射,才有内容。
2023-5-19 13:09
0
雪    币: 168
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13

在填充IAT过程中,如果我直接不下段F11进去单步步过就会报错,单步步过可以通过,但是之后再修复WDF这里  申请内存的时候又会报错,求解,大佬

2023-5-19 17:12
0
雪    币: 15
活跃值: (105)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
想问一个很小白的问题,有不触发PG的进程隐藏源码吗?
2023-5-19 22:31
0
雪    币: 401
活跃值: (1186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
lononan 屁股
你牛 哈哈
2023-5-20 18:10
0
雪    币: 30
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
竟然没看懂
2023-5-22 08:59
0
雪    币: 1795
活跃值: (3995)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
学习,感谢分享
2023-5-27 22:39
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
qqxiaobing 在填充IAT过程中,如果我直接不下段F11进去单步步过就会报错,单步步过可以通过,但是之后再修复WDF这里&nbsp; 申请内存的时候又会报错,求解,大佬
我给你看看
2023-6-6 02:41
0
雪    币: 0
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
Prompto 想问一个很小白的问题,有不触发PG的进程隐藏源码吗?
隐藏进程可以不pg的
2023-7-19 22:11
0
雪    币: 1484
活跃值: (1135)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
楼主试过能过 大黄蜂等翻录软件的驱动隐藏进程的检测吗?
2023-7-27 14:56
0
雪    币: 0
活跃值: (30)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21

PTDrvObj->DriverExtension = &PTDrvObj[1];
*(PULONG64)(&PTDrvObj[1]) = &PTDrvObj[0];这是两步是做什么的啊,&PTDrvObj[1]这样不就是 pObj+sizeof(obj) ObCreateObjectEx返回的obj地址是连续的吗?

最后于 2023-7-30 21:13 被z张大侠编辑 ,原因:
2023-7-30 21:11
0
雪    币: 207
活跃值: (134)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
问一个小白的问题,CI.DLL那里,楼主是怎么知道qword_1403A5C60这个变量是代表进入CI.DL里的函数的呢?我在IDA中对这个变量查询引用,发现没有地方初始化它
2023-11-7 19:47
0
雪    币: 207
活跃值: (134)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
qqxiaobing 在填充IAT过程中,如果我直接不下段F11进去单步步过就会报错,单步步过可以通过,但是之后再修复WDF这里&nbsp; 申请内存的时候又会报错,求解,大佬
解决了吗?我也是MakeIAT这里报错,在里面的MiSnapThunk报了分页错误,提示访问了只读内存
2023-11-9 11:27
0
游客
登录 | 注册 方可回帖
返回
//