r3/r0通信
用户态
#include <Windows.h>
#include <stdio.h>
#define SENDSTR CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
void main() {
HANDLE device = CreateFileW(L"\\\\.\\cc", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if (device == INVALID_HANDLE_VALUE) {
printf("获取驱动句柄失败! 错误:%d\n", GetLastError());
getchar();
return;
}
const char* msg = "hello cccccccccc";
char response[256] = { 0 };
DWORD size = 0;
DWORD bytesReturned = 0;
if (!DeviceIoControl(device, SENDSTR, (LPVOID)msg, strlen(msg) + 1, response, sizeof(response), &bytesReturned, 0)) {
printf("发送消息失败! 错误:%d\n", GetLastError());
}
else {
printf("发送消息成功! 输出缓冲区大小:%d\n", bytesReturned);
printf("从内核收到的回复: %s\n", response);
}
getchar();
CloseHandle(device);
}
内核态
#include "ntddk.h"
#define SYMBOLLINK L"\\??\\cc"
//生成一个自己设备控制请求功能号 0-7ff 被微软保留,只能用比这大的
#define SENDSTR CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
PDEVICE_OBJECT dev = NULL; //控制设备
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
if (DriverObject != NULL)
{
UNICODE_STRING SymbolName;//符号链接
RtlInitUnicodeString(&SymbolName, SYMBOLLINK);
IoDeleteSymbolicLink(&SymbolName);//删除符号链接
if (dev != NULL)
{
IoDeleteDevice(dev);
}
DbgPrint("删除设备和符号链接成功");
}
}
NTSTATUS CreateDevice(PDRIVER_OBJECT DriverObject) {
NTSTATUS Status; //返回状态
UNICODE_STRING DeviceName; //设备名称
UNICODE_STRING SymbolName;//符号链接
RtlInitUnicodeString(&DeviceName, L"\\Device\\cc");
Status = IoCreateDevice(
DriverObject,
0,
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE, //是否是独占设备,安全软件一般都是独占,由某个进程打开着永不关闭
&dev
);
do
{
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_COLLISION)
{
DbgPrint("设备名称冲突");
}
DbgPrint("创建失败");
break;
}
//初始化符号链接 设备名称应用程序是不可见的,因此驱动要暴露一个符号链接给应用层
RtlInitUnicodeString(&SymbolName, SYMBOLLINK);
Status = IoCreateSymbolicLink(&SymbolName, &DeviceName);
if (!NT_SUCCESS(Status)) { //不等于0
IoDeleteDevice(dev); //删除设备
DbgPrint("删除设备成功");
break;
}
else {
DbgPrint("创建符号链接成功");
}
} while (FALSE);//仅执行一次的经典写法,为内核态的跳出格式
return Status;
}
NTSTATUS fDispatch(PDEVICE_OBJECT pdev, PIRP irp) {
UNREFERENCED_PARAMETER(pdev);
NTSTATUS Status = STATUS_SUCCESS; //返回状态
ULONG len = 0;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(irp);
ULONG inBufferLength = stack->Parameters.DeviceIoControl.InputBufferLength;
ULONG outBufferLength = stack->Parameters.DeviceIoControl.OutputBufferLength;
PVOID inBuffer = (PCHAR)irp->AssociatedIrp.SystemBuffer;
if (stack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
{
//处理DeviceIoControl
switch (stack->Parameters.DeviceIoControl.IoControlCode)
{
case SENDSTR:
if (inBufferLength > 0 && inBuffer != NULL) {
DbgPrint("Received message from user: %s\n",(char*) inBuffer);
char response[10] = "coleak";
ULONG responseLength = (ULONG)strlen(response) + 1;
if (outBufferLength >= responseLength) {
RtlZeroMemory(inBuffer, outBufferLength);
RtlCopyMemory(inBuffer, response, responseLength);
len = responseLength;
}
else {
Status = STATUS_BUFFER_TOO_SMALL;
irp->IoStatus.Information = 0;
}
break;
}
default:
//到这里的请求都是不接受的请求,返回参数错误
Status = STATUS_INVALID_PARAMETER;
break;
}
}
irp->IoStatus.Information = len;
irp->IoStatus.Status = Status;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return Status;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
//KdBreakPoint();
if (RegistryPath != NULL)
{
DbgPrint("[%ws]所在注册表位置:%wZ\n", __FUNCTIONW__, RegistryPath);
}
if (DriverObject != NULL)
{
DbgPrint("[%ws]驱动对象地址:%p\n", __FUNCTIONW__, DriverObject);
//创建控制设备
CreateDevice(DriverObject);
//设置分发函数
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = fDispatch;
}
DriverObject->DriverUnload = DriverUnload;
DbgPrint("驱动加载成功");
}
return STATUS_SUCCESS;
}
浅记
- CreateFileW的dwFlagsAndAttributes为FILE_ATTRIBUTE_SYSTEM,该文件是操作系统的一部分或由操作系统独占使用
- DeviceIoControl设置功能号和r0进行通信,r0应该设置对应的的功能号以便判断IoControlCode后处理该请求
- 用户态的链接符号格式为
\\\\.\\链接名
,内核态链接符号格式为\\??\\链接名
- UNREFERENCED_PARAMETER可以忽略参数未使用的报错
- METHOD_BUFFERED通信中内核的输入和输出缓冲区均为irp->AssociatedIrp.SystemBuffer
x64 HOOK
windbg调试下结构
cmd下:bcdedit -debug on
.sympath srv*C:\symbols\microsoft*https://msdl.microsoft.com/download/symbols
.reload
rdmsr C0000082
u fffff807`6502b800 (nt!KiSystemCall64)
u...
找到如下
nt!KiSystemServiceRepeat:
fffff807`6502bb64 4c8d15555d9d00 lea r10,[nt!KeServiceDescriptorTable (fffff807`65a018c0)]
fffff807`6502bb6b 4c8d1d8e258f00 lea r11,[nt!KeServiceDescriptorTableShadow (fffff807`6591e100)]
特征码:4c8d15
这里的fffff807`65a018c0=fffff807`6502bb6b+9d5d55
接下来找sysnumber
u zwopenprocess
nt!ZwOpenProcess+0x14:
fffff800`72c13414 b826000000 mov eax,26h
fffff800`72c13419 e962830100 jmp nt!KiServiceInternal (fffff800`72c2b780)
这里的26h也就是表中的38
dd KeServiceDescriptorTable
fffff800`736018c0 728cfb80 fffff800 00000000 00000000
fffff800`736018d0 000001e6 00000000 728d031c fffff800
这里主要找四个字节的偏移量ServiceTableBase
内核态
#include "ntddk.h"
#include<intrin.h>
#include "c.h"
PVOID oldfun = NULL;
ULONG funoffset = 0;
typedef NTSTATUS(*pNtOpenProcess)(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
);
KIRQL WPOFFx64()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}
void WPONx64(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}
typedef struct _SERVICE_DESCRIPTOR_TABLE
{
PVOID ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfService;//SSDT表中服务函数的总数
PVOID ParamTableBase;
} SSDTEntry, * PSSDTEntry;
NTSTATUS myNtOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
) {
DbgPrint("打开了一个进程:ClientId---%ld", ClientId->UniqueProcess);
return ((pNtOpenProcess)oldfun)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
ULONGLONG Get_SSDT_Base()
{
PUCHAR Base = (PUCHAR)__readmsr(0xC0000082); // 读取C0000082寄存器
PUCHAR Address = Base + 0x500; // 相加偏移
PUCHAR i = NULL;
UCHAR b1 = 0, b2 = 0, b3 = 0; // 保存特征码
ULONG templong = 0;
ULONGLONG addr = 0; // 最后获取到的地址
for (i = Base; i < Address; i++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
{
b1 = *i; b2 = *(i + 1); b3 = *(i + 2);
if (b1 == 0x4c && b2 == 0x8d && b3 == 0x15) // 判断是否=4c8d15
{
memcpy(&templong, i + 3, 4); // 在i+3位置拷贝,拷贝4字节
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return addr;
}
VOID unhook() {
PSSDTEntry ssdt = (PSSDTEntry)Get_SSDT_Base();
PULONG stb = (PULONG)(ssdt->ServiceTableBase);
KIRQL irql = WPOFFx64();//关保护
stb[38] = funoffset;
WPONx64(irql);
DbgPrint("unhook");
}
ULONGLONG GetSSDTFunction(ULONG Index)
{
PSSDTEntry ssdt = (PSSDTEntry)Get_SSDT_Base();
PULONG stb = (PULONG)(ssdt->ServiceTableBase);
LONG qwTemp = stb[Index];
qwTemp = qwTemp >> 4;
return (ULONGLONG)stb + (ULONGLONG)qwTemp;
}
ULONG getOffset(ULONGLONG addrFunction)
{
PSSDTEntry ssdt = (PSSDTEntry)Get_SSDT_Base();
PULONG stb = (PULONG)(ssdt->ServiceTableBase);
LONG tmp = (LONG)(addrFunction - (ULONGLONG)stb);
tmp = tmp << 4;
return tmp;
}
VOID initKeBugCheckEx()
{
/*
48 B8 xxxx mov rax,XXXh;
FF E0 jmp rax
*/
UCHAR jmpCode[] = "\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0";
ULONGLONG proxyFunction = (ULONGLONG)myNtOpenProcess; //自己的NtOpenProess
memcpy(jmpCode + 2, &proxyFunction, 8);
KIRQL irql = WPOFFx64();//关保护
memcpy((void*)KeBugCheckEx, jmpCode, 12);
WPONx64(irql);
}
VOID hookSSDT()
{
oldfun = (PVOID)GetSSDTFunction(38);
initKeBugCheckEx();
PSSDTEntry ssdt = (PSSDTEntry)Get_SSDT_Base();
PULONG stb = (PULONG)(ssdt->ServiceTableBase);
funoffset = stb[38];
KIRQL irql = WPOFFx64();//关保护
stb[38] = getOffset((ULONGLONG)KeBugCheckEx);
WPONx64(irql);
DbgPrint("SSDT hooked");
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
unhook();
DbgPrint("DriverUnload");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("DriverEntry");
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
hookSSDT();
return STATUS_SUCCESS;
}
浅记
- 通过修改
cr0
寄存器的WP位来关闭写保护,并提高IRQL到DPC级别 - 通过读取MSR寄存器
0xC0000082
,并向下查找特征码4c 8d 15
以定位SSDT的基地址 - ServiceTableBase+偏移量(stb[Index])可以和获取函数的地址
- 构造jmpCode修改KeBugCheckEx前12位进行jmp到我们的函数
- 函数地址-ServiceTableBase然后左移四位即可获取偏移量.