找回密码
 立即注册

Windows驱动开发基础-ring3和ring0通信

2024-11-21 20:24| 发布者: admin| 查看: 299| 评论: 0|来自: 知乎

摘要: 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", GENER ...
 

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然后左移四位即可获取偏移量.

路过

雷人

握手

鲜花

鸡蛋

相关分类

QQ|Archiver|手机版|小黑屋|软件开发编程门户 ( 陇ICP备2024013992号-1|甘公网安备62090002000130号 )

GMT+8, 2025-1-18 09:57 , Processed in 0.044370 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

返回顶部