内核编程笔记

内核编程笔记

内核编程学习记录,拒绝做脚本小子!!!

涉及到的未公开数据结构的查询网站

WRK部分开源的KernelCode

火哥6期


宏定义

kPrint

1
#define kPrint(X,...) DbgPrintEx(77,0,X,__VA_ARGS__)

字符串操作

主要是对UnicodeString数据类型的操作,包括格式化输出、ASCII字符串和UNICODE字符串的比较等。

了解UNICODE_SRING类型的内存布局

1
2
3
4
5
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

0x01 格式化输出

1
2
3
4
5
6
7
8
9
//0x01 初始化UNICODE_STRING类型,并格式化输出
kPrint("[kv]:开始测试格式化输出\r\n");
UNICODE_STRING uString;
RtlInitUnicodeString(&uString, L"I am unicodeString");
//kPrint("[kv]:%ls\r\n", uString);
//格式化输出UnicodeString,wZ:是宽字符/UnicodeString,对应的参数是PUNICODE_STRING类型,ws:宽字符/字符串,ls和ws一致
kPrint("[kv]:%wZ\r\n", &uString);
kPrint("[kv]:%ws\r\n", uString.Buffer);
kPrint("[kv]:%ls\r\n", uString.Buffer);

0x02 宽字符串和UNICODE字符串比较

直接用RtlInitUnicodeString初始化一个UNICODE字符串,然后用RtlEqualUnicodeString比较

1
2
3
4
5
6
7
8
9
10
11
12
13
//0x02 和ascii码字符串比较,宽字符
const wchar_t cmpAstring[] = L"I am unicodeString";
UNICODE_STRING cmpUstring;
RtlInitUnicodeString(&cmpUstring, cmpAstring);
if (RtlEqualUnicodeString(&uString, &cmpUstring, 0) == TRUE)//是否忽略大小写,这里写否
{
kPrint("[kv]:方式一成功\r\n");
}
if (!wcscmp(uString.Buffer, cmpAstring))
{
kPrint("[kv]:方式二成功\r\n");
}
ExFreePoolWithTag(unicodeBuffer, 'tag1');

0x03 非宽字符和UNICODE字符串比较

RtlMultiByteToUnicodeSize获取生成UNICODE字符串的长度,然后用RtlMultiByteToUnicodeN转换成宽字符串和UNICODESTRING的buffer比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//0x03 和ascii码字符串比较,非宽字符
const char cmpAstring[] = "I am unicodeString";
ULONG bytesInUnicodeString = 0;
RtlMultiByteToUnicodeSize(&bytesInUnicodeString, cmpAstring, strlen(cmpAstring));
//分配UNICODE_STRING结构的内存缓冲区
PWSTR unicodeBuffer = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, bytesInUnicodeString, 'tag1');
if (!unicodeBuffer)
{
kPrint("Memory allocation failed.\n");
return;
}
UNICODE_STRING cmpUstring;
cmpUstring.Buffer = unicodeBuffer;
cmpUstring.Length = 0;
cmpUstring.MaximumLength = (USHORT)bytesInUnicodeString;
RtlMultiByteToUnicodeN(cmpUstring.Buffer, bytesInUnicodeString, &cmpUstring.Length, cmpAstring, strlen(cmpAstring));
DbgBreakPoint();
if (RtlEqualUnicodeString(&uString, &cmpUstring, 0) == TRUE)//是否忽略大小写,这里写否
{
kPrint("[kv]:方式一成功\r\n");
}
if (!wcsncmp(uString.Buffer,cmpUstring.Buffer,uString.Length/sizeof(WCHAR)))//wcsncmp第三个参数对应的是字符长度,而不是字节长度
{
kPrint("[kv]:方式二成功\r\n");
}
ExFreePoolWithTag(unicodeBuffer, 'tag1');

0x04 封装

KvCmpUnicodeStringA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOLEAN KvCmpUnicodeStringA(PCHAR AString,UNICODE_STRING UString) //对于非宽字符串和UNICODE字符串的比较
{
ULONG bytesInUnicodeString = 0;
RtlMultiByteToUnicodeSize(&bytesInUnicodeString, AString, strlen(AString));
PWSTR unicodeBuffer = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, bytesInUnicodeString, 'tag1');
if (!unicodeBuffer)
{
kPrint("%s:ExAllocatePoolWithTag Failed\r\n", __FUNCTION__);
return FALSE;
}
RtlMultiByteToUnicodeN(unicodeBuffer, bytesInUnicodeString, NULL, AString, strlen(AString));
if (!wcsncmp(UString.Buffer, unicodeBuffer, UString.Length / sizeof(WCHAR)))//wcsncmp第三个参数对应的是字符长度,而不是字节长度
{
ExFreePoolWithTag(unicodeBuffer, 'tag1');
return TRUE;
}
ExFreePoolWithTag(unicodeBuffer, 'tag1');
return FALSE;
}

KvCmpUnicodeStringW

1
2
3
4
5
6
7
8
9
10
BOOLEAN KvCmpUnicodeStringW(PWCHAR AString, UNICODE_STRING UString)//对于宽字符串和UNICODE字符串的比较
{
UNICODE_STRING cmpUstring;
RtlInitUnicodeString(&cmpUstring, AString);
if (RtlEqualUnicodeString(&UString, &cmpUstring, 0) == TRUE)//是否忽略大小写,这里写否
{
return TRUE;
}
return FALSE;
}

驱动断链

0x01 数据结构

DRIVER_OBJECT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//0xa8 bytes (sizeof)
struct _DRIVER_OBJECT
{
SHORT Type; //0x0
SHORT Size; //0x2
struct _DEVICE_OBJECT* DeviceObject; //0x4
ULONG Flags; //0x8
VOID* DriverStart; //0xc //驱动基址 = DllBase
ULONG DriverSize; //0x10 //驱动大小 = SizeOfImage
VOID* DriverSection; //0x14 //指向_KLDR_DATA_TABLE_ENTRY
struct _DRIVER_EXTENSION* DriverExtension; //0x18
struct _UNICODE_STRING DriverName; //0x1c
struct _UNICODE_STRING* HardwareDatabase; //0x24
struct _FAST_IO_DISPATCH* FastIoDispatch; //0x28
LONG (*DriverInit)(struct _DRIVER_OBJECT* arg1, struct _UNICODE_STRING* arg2); //0x2c 驱动入口点=EntryPoint
VOID (*DriverStartIo)(struct _DEVICE_OBJECT* arg1, struct _IRP* arg2); //0x30
VOID (*DriverUnload)(struct _DRIVER_OBJECT* arg1); //0x34
LONG (*MajorFunction[28])(struct _DEVICE_OBJECT* arg1, struct _IRP* arg2); //0x38
};

_KLDR_DATA_TABLE_ENTRY

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
typedef struct _NON_PAGED_DEBUG_INFO {
USHORT Signature;
USHORT Flags;
ULONG Size;
USHORT Machine;
USHORT Characteristics;
ULONG TimeDateStamp;
ULONG CheckSum;
ULONG SizeOfImage;
ULONGLONG ImageBase;
} NON_PAGED_DEBUG_INFO, * PNON_PAGED_DEBUG_INFO;

typedef struct _KLDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks; //驱动双向链表
PVOID ExceptionTable;
ULONG ExceptionTableSize;
// ULONG padding on IA64
PVOID GpValue;
PNON_PAGED_DEBUG_INFO NonPagedDebugInfo;
PVOID DllBase; //驱动基址
PVOID EntryPoint; //驱动入口点
ULONG SizeOfImage; //驱动大小
UNICODE_STRING FullDllName; //完整驱动路径
UNICODE_STRING BaseDllName; // 驱动名称
ULONG Flags;
USHORT LoadCount;
USHORT __Unused5;
PVOID SectionPointer;
ULONG CheckSum;
// ULONG padding on IA64
PVOID LoadedImports;
PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, * PKLDR_DATA_TABLE_ENTRY;

0x02 驱动枚举

通过DriverSection找到双向链表,遍历双向链表针对BaseDllName属性进行比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kPrint("驱动枚举操作\r\n");
//从DriverSection获取系统的驱动模块双向链表
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
PKLDR_DATA_TABLE_ENTRY preLdr = ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY nxtLdr = ldr->InLoadOrderLinks.Blink;
ULONG sum = 1;
kPrint("[kv]:%d-%wZ\r\n", sum, &ldr->BaseDllName); // 别省略了当前驱动
while (preLdr != nxtLdr)
{
sum++;
UNICODE_STRING baseName = preLdr->BaseDllName;
kPrint("[kv]:%d-%wZ\r\n", sum,&baseName);
preLdr = preLdr->InLoadOrderLinks.Flink;
}

0x03 驱动断链

浅层断链

找到指定驱动后用RemoveEntryList函数断链,还是会被PCHunter找到并列入隐藏驱动名单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
PKLDR_DATA_TABLE_ENTRY preLdr = ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY nxtLdr = ldr->InLoadOrderLinks.Blink;
UNICODE_STRING target;
RtlInitUnicodeString(&target, L"HTTP.sys");
while (preLdr != nxtLdr)
{
UNICODE_STRING baseName = preLdr->BaseDllName;
if (RtlEqualUnicodeString(&target, &baseName, TRUE))
{
//找到指定驱动
RemoveEntryList(&preLdr->InLoadOrderLinks);
kPrint("[kv]:find %wZ\r\n", &baseName);
break;
}
preLdr = preLdr->InLoadOrderLinks.Flink;
}

image-20241023200347076

深层断链

对驱动程序对象的部分属性进行抹除即可让PCHunter检测不到

  1. 引入非导出的函数ObReferenceObjectByName
1
2
3
4
5
6
7
8
9
10
11
12
13
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
);//手动导入ObReferenceObjectByName函数
extern POBJECT_TYPE * IoDriverObjectType;//声明一个外部变量 IoDriverObjectType,这个变量是指向对象类型的指针,在内核中用于引用驱动程序对象。
  1. 实现深层断链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
PKLDR_DATA_TABLE_ENTRY preLdr = ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY nxtLdr = ldr->InLoadOrderLinks.Blink;
UNICODE_STRING target;
RtlInitUnicodeString(&target, L"HTTP.sys");
UNICODE_STRING objectName;
RtlInitUnicodeString(&objectName, L"\\Driver\\http"); //这个设备名称字符串格式要了解,大小写不敏感,后面的http不带.sys后缀
while (preLdr != nxtLdr)
{
UNICODE_STRING baseName = preLdr->BaseDllName;
//打开设备对象
if (RtlEqualUnicodeString(&target, &baseName, TRUE))
{
//找到指定驱动
PDRIVER_OBJECT driverObject = NULL;
NTSTATUS st = ObReferenceObjectByName(&objectName, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &driverObject);//IoDriverObjectType设备类型要填驱动对象,IoDeviceObjectType是设备对象
if (!NT_SUCCESS(st))
{
kPrint("[kv]:ObReferenceObjectByName faild st:0x%x\r\n", st);
return;

RemoveEntryList(&preLdr->InLoadOrderLinks);
driverObject->DriverSection = 0;//抹除驱动特征1
DriverObject->DriverInit = 0;//抹除驱动特征2
kPrint("[kv]:断链%wZ成功\r\n", &baseName);
break;
}
preLdr = preLdr->InLoadOrderLinks.Flink;
}

效果图

image-20241023213403159

被列入可疑驱动对象名单

image-20241023213435546

自我断链

在驱动主要功能实现完毕即将返回前,创建一个延迟线程(KeDelayExecutionThread),等驱动返回之后抹除驱动特征(DriverInit、DriverSection),直接抹除会因为DriverEntry结束之后继续调用驱动特征属性导致蓝屏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
typedef struct _DriverHideContext
{
PWCH objectName;
PDRIVER_OBJECT DriverObject;
}DriverHideContext,*PDriverHideContext; //线程参数结构体
NTSTATUS DriverHide(PDriverHideContext context)
{
//定义延迟时间,为了给KeDelayExecutionThread传参
LARGE_INTEGER interval = { 0 };
interval.QuadPart = -10000 * 5000;
KeDelayExecutionThread(KernelMode, FALSE,&interval);
// 找到驱动的双向链表,断链
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)context->DriverObject->DriverSection;
//找到驱动对象抹除驱动特征
UNICODE_STRING target;
RtlInitUnicodeString(&target,context->objectName);
PDRIVER_OBJECT driverObject;
NTSTATUS st = ObReferenceObjectByName(&target, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &driverObject);
if (!NT_SUCCESS(st))
{
ExFreePool(context);
kPrint("[kv]:%s-ObReferenceObjectByName failed 0x%x\r\n", __FUNCTION__, st);
return st;
}
RemoveEntryList(&ldr->InLoadOrderLinks);
driverObject->DriverInit = 0;
driverObject->DriverSection = 0;
ExFreePool(context);
//用完之后减少引用计数,防止泄露
ObDereferenceObject(driverObject);
return STATUS_SUCCESS;
}
VOID mainTest()
{
HANDLE threadHandle;
//放到池内存或定义为全局变量,因为栈帧会被破坏
PDriverHideContext context = (PDriverHideContext)ExAllocatePool(NonPagedPool, sizeof(PDriverHideContext));
context->objectName = L"\\Driver\\driverStudy"; //找到驱动对象抹除驱动特征
context->DriverObject = DriverObject; // 找到驱动的双向链表,断链
NTSTATUS st = PsCreateSystemThread(&threadHandle, THREAD_ALL_ACCESS, NULL, NULL,NULL, DriverHide, context);
if (NT_SUCCESS(st))
{
NtClose(threadHandle);
}
}

效果图

image-20241023224314467

0x04 封装

KvEnumAllKernelModules

遍历双向链表枚举内核模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
VOID KvEnumAllKernelModules(PDRIVER_OBJECT  DriverObject) //枚举所有内核模块
{
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
PKLDR_DATA_TABLE_ENTRY preLdr = ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY nxtLdr = ldr->InLoadOrderLinks.Blink;

ULONG sum = 1;
kPrint("[kv]:%d-%wZ\r\n", sum, &ldr->BaseDllName);
while (preLdr != nxtLdr)
{
sum++;
UNICODE_STRING baseName = preLdr->BaseDllName;
kPrint("[kv]:%d-%wZ\r\n", sum, &baseName);
preLdr = preLdr->InLoadOrderLinks.Flink;
}
}

KvChainBreak

对指定名称的驱动进行断链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
NTSTATUS KvChainBreak(PDRIVER_OBJECT  DriverObject,PWCH targetDriverName)//L"HTTP.sys"
{
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
PKLDR_DATA_TABLE_ENTRY preLdr = ldr->InLoadOrderLinks.Flink;
PKLDR_DATA_TABLE_ENTRY nxtLdr = ldr->InLoadOrderLinks.Blink;
UNICODE_STRING target;
RtlInitUnicodeString(&target, targetDriverName);
PWCH prefix = L"\\Driver\\";
ULONG totalLen = 8 + wcslen(targetDriverName) - 4;
PWCH totalStr = (PWCH)ExAllocatePool(NonPagedPool, sizeof(WCHAR)*(totalLen+1));
wcscpy(totalStr,prefix);
wcsncat(totalStr + 8, targetDriverName, wcslen(targetDriverName) - 4);
totalStr[totalLen] = NULL;
UNICODE_STRING objectName;
RtlInitUnicodeString(&objectName, totalStr);
while (preLdr != nxtLdr)
{
UNICODE_STRING baseName = preLdr->BaseDllName;
//打开设备对象
if (RtlEqualUnicodeString(&target, &baseName, TRUE))
{
//找到指定驱动
PDRIVER_OBJECT driverObject = NULL;
NTSTATUS st = ObReferenceObjectByName(&objectName, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &driverObject);
if (!NT_SUCCESS(st))
{
kPrint("[kv]:ObReferenceObjectByName faild st:0x%x\r\n", st);
ExFreePool(totalStr);
return st;
}

RemoveEntryList(&preLdr->InLoadOrderLinks);
driverObject->DriverSection = 0;
DriverObject->DriverInit = 0;
ExFreePool(totalStr);
return STATUS_SUCCESS;
}
preLdr = preLdr->InLoadOrderLinks.Flink;
}
ExFreePool(totalStr);
return STATUS_NOT_FOUND;
}

KvChainBreakSelfThread

线程函数,用于驱动自我断链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
VOID KvChainBreakSelfThread(PDriverHideContext context)
{
LARGE_INTEGER interval = { 0 };
interval.QuadPart = 10000000 * context->delayTime;//转换为以秒为单位
KeDelayExecutionThread(KernelMode, FALSE, &interval);
PKLDR_DATA_TABLE_ENTRY ldr = (PKLDR_DATA_TABLE_ENTRY)context->DriverObject->DriverSection;
UNICODE_STRING target;
RtlInitUnicodeString(&target, context->objectName);
PDRIVER_OBJECT driverObject;
NTSTATUS st = ObReferenceObjectByName(&target, FILE_ALL_ACCESS, 0, 0, *IoDriverObjectType, KernelMode, NULL, &driverObject);
if (!NT_SUCCESS(st))
{
ExFreePool(context);
kPrint("[kv]:%s-ObReferenceObjectByName failed 0x%x\r\n", __FUNCTION__, st);
return;
}
RemoveEntryList(&ldr->InLoadOrderLinks);
driverObject->DriverInit = 0;
driverObject->DriverSection = 0;
ExFreePool(context);
ObDereferenceObject(driverObject);//用完之后减少引用计数,防止泄露
}

KvChainBreakSelf

驱动自我断链函数,调用KvChainBreakSelfThread线程函数抹除驱动特征

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
VOID KvChainBreakSelf(PDRIVER_OBJECT  DriverObject,PWCH objectName, ULONG delayTime)
//objectName:格式 L"\\Driver\\driverStudy"
//delayTime:以秒为单位,必须为负数(相对时间)
{
HANDLE threadHandle;
PDriverHideContext conText = (PDriverHideContext)ExAllocatePool(NonPagedPool, sizeof(DriverHideContext));
conText->delayTime = -10000 * 1000 * delayTime;
conText->DriverObject = DriverObject;
conText->objectName = objectName;
NTSTATUS nt = PsCreateSystemThread(&threadHandle, FILE_ALL_ACCESS, NULL, NULL, NULL, KvChainBreakSelfThread, conText);
if (NT_SUCCESS(nt))
{
NtClose(threadHandle);
}
ExFreePool(conText);
}