驱动断链
实验环境:Win7_x32
打开PCHunter32,点开驱动模块可以看到所有的加载的驱动基本上都在这里。包括PCHunter本身加载的驱动都被罗列出来了
这个PCHunter的遍历驱动列表的方式和我们常规的遍历方式不太一样,他这个遍历是通过查询模块,然后还查了目录
查的就是Driver这个目录,这里面有所有的驱动,还有FileSystem也去遍历了。他是由这两个目录组合起来的。
如果我们去遍历,铁定没有他这么多。
这里我们来认识一个结构:DRIVER_OBJECT
我们在windbg下一个断点。然后dt
命令查看一下。
1: kd> dt nt!_DRIVER_OBJECT
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 DeviceObject : Ptr32 _DEVICE_OBJECT
+0x008 Flags : Uint4B
+0x00c DriverStart : Ptr32 Void
+0x010 DriverSize : Uint4B
+0x014 DriverSection : Ptr32 Void
+0x018 DriverExtension : Ptr32 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING
+0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
+0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH
+0x02c DriverInit : Ptr32 long
+0x030 DriverStartIo : Ptr32 void
+0x034 DriverUnload : Ptr32 void
+0x038 MajorFunction : [28] Ptr32 long
他一共有这么多成员,我们主要关注DriverSection
这个成员,他是一个类似三环下PEB的链表。
但是结构没有显示出来,我们来弄一个结构出来
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY exp;
ULONG un;
ULONG NonPagedDebugInfo;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG __Undefined6;
ULONG CheckSum;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
我们先不着急断链,我们先把指向改过去。
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pReg) {
DbgBreakPoint();
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
我们下个断点来看看
加载驱动断下。我们走到ldr创建完成,我们去命令窗口dt一下ldr
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x83f8c850 - 0x87df01b0 ]
这个是一个加载的双向循环链表,整个都是串起来的+0x018 DllBase : 0x8bd62000
这里就是是这个模块地址。+0x01c EntryPoint : 0x8bd66000
这个就是入口点
我们再去看一下:dt pDriver
。
在驱动结构中:
+0x00c DriverStart : 0x8bd62000 Void
在驱动结构中这个是起始地址
有没有发现这个DriverStart
和DllBase
是一样的。
我们再来观察一下我们驱动的名字:FullDllName : _UNICODE_STRING "\??\C:\Users\Catsay\Desktop\驱动断链.sys"
这里显示的是我们只被加载了一次
我们来看一下这个双向循环链表:
dt _KLDR_DATA_TABLE_ENTRY 0x83f8c850
windbg运行一下命令可以看到
2: kd> dt _KLDR_DATA_TABLE_ENTRY 0x83f8c850
____!_KLDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x86342c98 - 0x8674d318 ]
+0x008 exp : _LIST_ENTRY [ 0x7 - 0xcde ]
+0x010 un : 0x83e0b000
+0x014 NonPagedDebugInfo : 0x83e42000
+0x018 DllBase : 0x84254000
+0x01c EntryPoint : 0
+0x020 SizeOfImage : 0
+0x024 FullDllName : _UNICODE_STRING ""
+0x02c BaseDllName : _UNICODE_STRING "은菸좀菸"
+0x034 Flags : 0x83f7e800
+0x038 LoadCount : 0x63d0
+0x03a __Undefined5 : 0x8791
+0x03c __Undefined6 : 0
+0x040 CheckSum : 0
+0x044 TimeDateStamp : 0
这里是啥都没有,我们继续往上看一个节点:
2: kd> dt _KLDR_DATA_TABLE_ENTRY 0x86342c98
____!_KLDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x86342c20 - 0x83f8c850 ]
+0x008 exp : _LIST_ENTRY [ 0x83eba544 - 0x12 ]
+0x010 un : 0
+0x014 NonPagedDebugInfo : 0
+0x018 DllBase : 0x83e42000
+0x01c EntryPoint : 0x83f614d8
+0x020 SizeOfImage : 0x412000
+0x024 ReadVirtual: 8b400fb8 not properly sign extended
FullDllName : _UNICODE_STRING "\SystemRoot\system32\ntkrnlpa.exe"
+0x02c ReadVirtual: 86342cf4 not properly sign extended
BaseDllName : _UNICODE_STRING "ntoskrnl.exe"
+0x034 Flags : 0x8004000
+0x038 LoadCount : 0x6e
+0x03a __Undefined5 : 0
+0x03c __Undefined6 : 0
+0x040 CheckSum : 0x3c88ac
+0x044 TimeDateStamp : 0
我们看到了:ntkrnlpa.exe
继续往前,就看到了:
2: kd> dt _KLDR_DATA_TABLE_ENTRY 0x86342c20
____!_KLDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x86342ba0 - 0x86342c98 ]
+0x008 exp : _LIST_ENTRY [ 0x83e0cd50 - 0x3 ]
+0x010 un : 0
+0x014 NonPagedDebugInfo : 0
+0x018 DllBase : 0x83e0b000
+0x01c EntryPoint : 0x83e0b000
+0x020 SizeOfImage : 0x37000
+0x024 ReadVirtual: 8b400f68 not properly sign extended
FullDllName : _UNICODE_STRING "\SystemRoot\system32\halmacpi.dll"
+0x02c ReadVirtual: 86342c7c not properly sign extended
BaseDllName : _UNICODE_STRING "hal.dll"
+0x034 Flags : 0x8004000
+0x038 LoadCount : 0x57
+0x03a __Undefined5 : 0
+0x03c __Undefined6 : 0
+0x040 CheckSum : 0x37fb1
+0x044 TimeDateStamp : 0
也就是说我们通过遍历这个链表就可以把所有驱动都遍历出来
我们先来写代码把所有驱动遍历出来。
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pReg) {
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
PKLDR_DATA_TABLE_ENTRY pre = (PKLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY next = (PKLDR_DATA_TABLE_ENTRY)pre->InLoadOrderLinks.Flink;
while (next!= pre)
{
DbgPrintEx(77, 0, "[db]:driver name = %wZ\r\n", &next->FullDllName);
next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
这时候我们可以看到已经遍历出来了。那么我们现在想断链某个驱动就很简单了。我们找到名字然后找到对应的驱动,把它从这个链中摘掉。
我们去把PCHunter32aq.sys给摘掉。
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pReg) {
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
PKLDR_DATA_TABLE_ENTRY pre = (PKLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY next = (PKLDR_DATA_TABLE_ENTRY)pre->InLoadOrderLinks.Flink;
UNICODE_STRING driverName = {0};
RtlInitUnicodeString(&driverName, L"PCHunter32aq.sys");
while (next!= pre)
{
if (next->BaseDllName.Length!=0&&RtlCompareUnicodeString(&driverName, &next->BaseDllName,TRUE)==0)
{
DbgPrintEx(77, 0, "[db]:driver name = %wZ\r\n", &next->FullDllName);
RemoveEntryList(&next->InLoadOrderLinks);
break;
}
next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
断掉之后发现PCHunter卡死了
我们来看看他为啥卡死了
windbg执行:!process 0 0
打印出所有进程信息。然后找到PCHunter:
PROCESS 86694288 SessionId: 1 Cid: 0f40 Peb: 7ffdd000 ParentCid: 09a8
DirBase: 7f045480 ObjectTable: 99651880 HandleCount: 282.
Image: PCHunter32.exe
然后!process 86694288
,把它里面所有的线程都打印出来看看。
应该就是卡死在这附近。那我们就换一个断掉。我们去断HTTP.sys
我们现在的断链是已经成功了,但是被PCHunter检测出来了。
这时候我们需要用到一个没有导出的函数,我们用ida扣一下这个函数。
_ObReferenceObjectByName
就是这个函数,把它文档化一下:
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
__in PUNICODE_STRING ObjectName,
__in ULONG Attributes,
__in_opt PACCESS_STATE AccessState,
__in_opt ACCESS_MASK DesiredAccess,
__in POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE AccessMode,
__inout_opt PVOID ParseContext,
__out PVOID *Object
);
然后补全驱动代码:
#include <ntifs.h>
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY exp;
ULONG un;
ULONG NonPagedDebugInfo;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG __Undefined6;
ULONG CheckSum;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
__in PUNICODE_STRING ObjectName,
__in ULONG Attributes,
__in_opt PACCESS_STATE AccessState,
__in_opt ACCESS_MASK DesiredAccess,
__in POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE AccessMode,
__inout_opt PVOID ParseContext,
__out PVOID *Object
);
VOID DriverUnload(PDRIVER_OBJECT pDriver) {
}
extern POBJECT_TYPE * IoDriverObjectType;
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pReg) {
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
PKLDR_DATA_TABLE_ENTRY pre = (PKLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY next = (PKLDR_DATA_TABLE_ENTRY)pre->InLoadOrderLinks.Flink;
UNICODE_STRING driverName = {0};
RtlInitUnicodeString(&driverName, L"HTTP.sys");
UNICODE_STRING driverName1 = { 0 };
RtlInitUnicodeString(&driverName1, L"\\driver\\HTTP");
while (next!= pre)
{
DbgPrintEx(77, 0, "[db]:driver name = %wZ\r\n", &next->FullDllName);
if (next->BaseDllName.Length!=0&&RtlCompareUnicodeString(&driverName, &next->BaseDllName,TRUE)==0)
{
PDRIVER_OBJECT httpDriver = NULL;
NTSTATUS status = ObReferenceObjectByName(&driverName1, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &httpDriver);
if (NT_SUCCESS(status))
{
RemoveEntryList(&next->InLoadOrderLinks);
httpDriver->DriverInit = NULL;
httpDriver->DriverSection = NULL;
}
break;
}
next = (PKLDR_DATA_TABLE_ENTRY)next->InLoadOrderLinks.Flink;
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
我们就可以发现已经隐藏掉了,通过PCHunter也检测不到了。
那么我们用这一份代码来隐藏自己可以吗?
不可以。会直接蓝屏。
我们在入口点隐藏自己,想一下我们在做PE加载的时候,我们去调用入口点调用完之后是不是还会有可能拿入口点和PE头做一些其他事情。
所以我们在这不能够把自己头抹掉。我们想要把自己隐藏需要把握一下时机。我们要让他调用完自己之后再把自己抹掉。
#include <ntifs.h>
typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY exp;
ULONG un;
ULONG NonPagedDebugInfo;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Undefined5;
ULONG __Undefined6;
ULONG CheckSum;
ULONG TimeDateStamp;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
NTKERNELAPI NTSTATUS ObReferenceObjectByName(
__in PUNICODE_STRING ObjectName,
__in ULONG Attributes,
__in_opt PACCESS_STATE AccessState,
__in_opt ACCESS_MASK DesiredAccess,
__in POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE AccessMode,
__inout_opt PVOID ParseContext,
__out PVOID *Object
);
extern POBJECT_TYPE * IoDriverObjectType;
void DriverHide(PWCH ObjName)
{
//延时
LARGE_INTEGER in = { 0 };
in.QuadPart = -10000 * 5000;
KeDelayExecutionThread(KernelMode, FALSE, &in);
UNICODE_STRING driverName1 = { 0 };
RtlInitUnicodeString(&driverName1, ObjName);
PDRIVER_OBJECT httpDriver = NULL;
NTSTATUS status = ObReferenceObjectByName(&driverName1, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &httpDriver);
if (NT_SUCCESS(status))
{
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)httpDriver->DriverSection;
DbgPrintEx(77, 0, "[db]: driver name = %wZ\r\n", &ldr->FullDllName);
httpDriver->DriverSection = ldr->InLoadOrderLinks.Flink;
RemoveEntryList(&ldr->InLoadOrderLinks);
httpDriver->DriverInit = NULL;
ObDereferenceObject(httpDriver);
}
return;
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
//启动一个线程
HANDLE hThread = NULL;
NTSTATUS status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, DriverHide, L"\\driver\\day");
if (NT_SUCCESS(status))
{
NtClose(hThread);
}
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
我们创建一个线程去操作。
断了之后就没有了。我们再去使用这份代码去断掉PCHunter。
可以看到PCHunter的驱动也已经隐藏掉了