天空闲话 发表于 2023-5-23 14:53:50

驱动开发:内核实现进程汇编与反汇编

在笔者上一篇文章《驱动开发:内核MDL读写进程内存》简单介绍了如何通过MDL映射的方式实现进程读写操作,本章将通过如上案例实现远程进程反汇编功能,此类功能也是ARK工具中最常见的功能之一,通常此类功能的实现分为两部分,内核部分只负责读写字节集,应用层部分则配合反汇编引擎对字节集进行解码,此处我们将运用capstone引擎实现这个功能。
https://img2023.cnblogs.com/blog/1379525/202302/1379525-20230222191230962-634665817.png
首先是实现驱动部分,驱动程序的实现是一成不变的,仅仅只是做一个读写功能即可,完整的代码如下所示;
// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#include <ntifs.h>
#include <windef.h>

#define READ_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define WRITE_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)

#define DEVICENAME L"\\Device\\ReadWriteDevice"
#define SYMBOLNAME L"\\??\\ReadWriteSymbolName"

typedef struct
{
        DWORD pid;       // 进程PID
        UINT64 address;// 读写地址
        DWORD size;      // 读写长度
        BYTE* data;      // 读写数据集
}ProcessData;

// MDL读取封装
BOOLEAN ReadProcessMemory(ProcessData* ProcessData)
{
        BOOLEAN bRet = TRUE;
        PEPROCESS process = NULL;

        // 将PID转为EProcess
        PsLookupProcessByProcessId(ProcessData->pid, &process);
        if (process == NULL)
        {
                return FALSE;
        }

        BYTE* GetProcessData = NULL;
        __try
        {
                // 分配堆空间 NonPagedPool 非分页内存
                GetProcessData = ExAllocatePool(NonPagedPool, ProcessData->size);
        }
        __except (1)
        {
                return FALSE;
        }

        KAPC_STATE stack = { 0 };
        // 附加到进程
        KeStackAttachProcess(process, &stack);

        __try
        {
                // 检查进程内存是否可读取
                ProbeForRead(ProcessData->address, ProcessData->size, 1);

                // 完成拷贝
                RtlCopyMemory(GetProcessData, ProcessData->address, ProcessData->size);
        }
        __except (1)
        {
                bRet = FALSE;
        }

        // 关闭引用
        ObDereferenceObject(process);

        // 解除附加
        KeUnstackDetachProcess(&stack);

        // 拷贝数据
        RtlCopyMemory(ProcessData->data, GetProcessData, ProcessData->size);

        // 释放堆
        ExFreePool(GetProcessData);
        return bRet;
}

// MDL写入封装
BOOLEAN WriteProcessMemory(ProcessData* ProcessData)
{
        BOOLEAN bRet = TRUE;
        PEPROCESS process = NULL;

        // 将PID转为EProcess
        PsLookupProcessByProcessId(ProcessData->pid, &process);
        if (process == NULL)
        {
                return FALSE;
        }

        BYTE* GetProcessData = NULL;
        __try
        {
                // 分配堆
                GetProcessData = ExAllocatePool(NonPagedPool, ProcessData->size);
        }
        __except (1)
        {
                return FALSE;
        }

        // 循环写出
        for (int i = 0; i < ProcessData->size; i++)
        {
                GetProcessData = ProcessData->data;
        }

        KAPC_STATE stack = { 0 };

        // 附加进程
        KeStackAttachProcess(process, &stack);

        // 分配MDL对象
        PMDL mdl = IoAllocateMdl(ProcessData->address, ProcessData->size, 0, 0, NULL);
        if (mdl == NULL)
        {
                return FALSE;
        }

        MmBuildMdlForNonPagedPool(mdl);

        BYTE* ChangeProcessData = NULL;

        __try
        {
                // 锁定地址
                ChangeProcessData = MmMapLockedPages(mdl, KernelMode);

                // 开始拷贝
                RtlCopyMemory(ChangeProcessData, GetProcessData, ProcessData->size);
        }
        __except (1)
        {
                bRet = FALSE;
                goto END;
        }

        // 结束释放MDL关闭引用取消附加
END:
        IoFreeMdl(mdl);
        ExFreePool(GetProcessData);
        KeUnstackDetachProcess(&stack);
        ObDereferenceObject(process);

        return bRet;
}

NTSTATUS DriverIrpCtl(PDEVICE_OBJECT device, PIRP pirp)
{
        PIO_STACK_LOCATION stack;
        stack = IoGetCurrentIrpStackLocation(pirp);
        ProcessData* ProcessData;

        switch (stack->MajorFunction)
        {

        case IRP_MJ_CREATE:
        {
                break;
        }

        case IRP_MJ_CLOSE:
        {
                break;
        }

        case IRP_MJ_DEVICE_CONTROL:
        {
                // 获取应用层传值
                ProcessData = pirp->AssociatedIrp.SystemBuffer;

                DbgPrint("进程ID: %d | 读写地址: %p | 读写长度: %d \n", ProcessData->pid, ProcessData->address, ProcessData->size);

                switch (stack->Parameters.DeviceIoControl.IoControlCode)
                {
                // 读取函数
                case READ_PROCESS_CODE:
                {
                        ReadProcessMemory(ProcessData);
                        break;
                }
                // 写入函数
                case WRITE_PROCESS_CODE:
                {
                        WriteProcessMemory(ProcessData);
                        break;
                }

                }

                pirp->IoStatus.Information = sizeof(ProcessData);
                break;
        }

        }

        pirp->IoStatus.Status = STATUS_SUCCESS;
        IoCompleteRequest(pirp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
        if (driver->DeviceObject)
        {
                UNICODE_STRING SymbolName;
                RtlInitUnicodeString(&SymbolName, SYMBOLNAME);

                // 删除符号链接
                IoDeleteSymbolicLink(&SymbolName);
                IoDeleteDevice(driver->DeviceObject);
        }
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
        NTSTATUS status = STATUS_SUCCESS;
        PDEVICE_OBJECT device = NULL;
        UNICODE_STRING DeviceName;

        DbgPrint(" hello lyshark.com \n");

        // 初始化设备名
        RtlInitUnicodeString(&DeviceName, DEVICENAME);

        // 创建设备
        status = IoCreateDevice(Driver, sizeof(Driver->DriverExtension), &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device);
        if (status == STATUS_SUCCESS)
        {
                UNICODE_STRING SymbolName;
                RtlInitUnicodeString(&SymbolName, SYMBOLNAME);

                // 创建符号链接
                status = IoCreateSymbolicLink(&SymbolName, &DeviceName);

                // 失败则删除设备
                if (status != STATUS_SUCCESS)
                {
                        IoDeleteDevice(device);
                }
        }

        // 派遣函数初始化
        Driver->MajorFunction = DriverIrpCtl;
        Driver->MajorFunction = DriverIrpCtl;
        Driver->MajorFunction = DriverIrpCtl;

        // 卸载驱动
        Driver->DriverUnload = UnDriver;

        return STATUS_SUCCESS;
}上方的驱动程序很简单关键部分已经做好了备注,此类驱动换汤不换药没啥难度,接下来才是本节课的重点,让我们开始了解一下Capstone这款反汇编引擎吧,Capstone是一个轻量级的多平台、多架构的反汇编框架。Capstone旨在成为安全社区中二进制分析和反汇编的终极反汇编引擎,该引擎支持多种平台的反汇编,非常推荐使用。

[*]反汇编引擎下载地址:https://cdn.lyshark.com/sdk/capstone_msvc12.zip
这款反汇编引擎如果你想要使用它则第一步就是调用cs_open()官方对其的解释是打开一个句柄,这个打开功能其中的参数如下所示;

[*]参数1:指定模式 CS_ARCH_X86 表示为Windows平台
[*]参数2:执行位数 CS_MODE_32为32位模式,CS_MODE_64为64位
[*]参数3:打开后保存的句柄&dasm_handle
第二步也是最重要的一步,调用cs_disasm()反汇编函数,该函数的解释如下所示;

[*]参数1:指定dasm_handle反汇编句柄
[*]参数2:指定你要反汇编的数据集或者是一个缓冲区
[*]参数3:指定你要反汇编的长度 64
[*]参数4:输出的内存地址起始位置 0x401000
[*]参数5:默认填充为0
[*]参数6:用于输出数据的一个指针
这两个函数如果能搞明白,那么如下反汇编完整代码也就可以理解了。
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
#include <inttypes.h>
#include <capstone/capstone.h>

#pragma comment(lib,"capstone64.lib")

#define READ_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define WRITE_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)

typedef struct
{
        DWORD pid;
        UINT64 address;
        DWORD size;
        BYTE* data;
}ProcessData;

int main(int argc, char* argv[])
{
        // 连接到驱动
        HANDLE handle = CreateFileA("\\??\\ReadWriteSymbolName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

        ProcessData data;
        DWORD dwSize = 0;

        // 指定需要读写的进程
        data.pid = 6932;
        data.address = 0x401000;
        data.size = 64;

        // 读取机器码到BYTE字节数组
        data.data = new BYTE;
        DeviceIoControl(handle, READ_PROCESS_CODE, &data, sizeof(data), &data, sizeof(data), &dwSize, NULL);
        for (int i = 0; i < data.size; i++)
        {
                printf("0x%02X ", data.data);
        }

        printf("\n");

        // 开始反汇编
        csh dasm_handle;
        cs_insn *insn;
        size_t count;

        // 打开句柄
        if (cs_open(CS_ARCH_X86, CS_MODE_32, &dasm_handle) != CS_ERR_OK)
        {
                return 0;
        }

        // 反汇编代码
        count = cs_disasm(dasm_handle, (unsigned char *)data.data, data.size, data.address, 0, &insn);

        if (count > 0)
        {
                size_t index;
                for (index = 0; index < count; index++)
                {
                        /*
                        for (int x = 0; x < insn.size; x++)
                        {
                                printf("机器码: %d -> %02X \n", x, insn.bytes);
                        }
                        */

                        printf("地址: 0x%"PRIx64" | 长度: %d 反汇编: %s %s \n", insn.address, insn.size, insn.mnemonic, insn.op_str);
                }
                cs_free(insn, count);
        }
        cs_close(&dasm_handle);

        getchar();
        CloseHandle(handle);
        return 0;
}通过驱动加载工具加载WinDDK.sys然后在运行本程序,你会看到正确的输出结果,反汇编当前位置处向下64字节。
https://img2023.cnblogs.com/blog/1379525/202302/1379525-20230222195434976-1374475496.png
说完了反汇编接着就需要讲解如何对内存进行汇编操作了,汇编引擎这里采用了XEDParse该引擎小巧简洁,著名的x64dbg就是在运用本引擎进行汇编替换的,本引擎的使用非常简单,只需要向XEDParseAssemble()函数传入一个规范的结构体即可完成转换,完整代码如下所示。

[*]汇编引擎下载地址:https://cdn.lyshark.com/sdk/XEDParse.zip
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>

extern "C"
{
#include "D:/XEDParse/XEDParse.h"
#pragma comment(lib, "D:/XEDParse/XEDParse_x64.lib")
}

using namespace std;

#define READ_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)
#define WRITE_PROCESS_CODE CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)

typedef struct
{
        DWORD pid;
        UINT64 address;
        DWORD size;
        BYTE* data;
}ProcessData;

int main(int argc, char* argv[])
{
        // 连接到驱动
        HANDLE handle = CreateFileA("\\??\\ReadWriteSymbolName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

        ProcessData data;
        DWORD dwSize = 0;

        // 指定需要读写的进程
        data.pid = 6932;
        data.address = 0x401000;
        data.size = 0;

        XEDPARSE xed = { 0 };
        xed.x64 = FALSE;

        // 输入一条汇编指令并转换
        scanf_s("%llx", &xed.cip);
        gets_s(xed.instr, XEDPARSE_MAXBUFSIZE);
        if (XEDPARSE_OK != XEDParseAssemble(&xed))
        {
                printf("指令错误: %s\n", xed.error);
        }

        // 生成堆
        data.data = new BYTE;

        // 设置长度
        data.size = xed.dest_size;

        for (size_t i = 0; i < xed.dest_size; i++)
        {
                // 替换到堆中
                printf("%02X ", xed.dest);
                data.data = xed.dest;
        }

        // 调用控制器,写入到远端内存
        DeviceIoControl(handle, WRITE_PROCESS_CODE, &data, sizeof(data), &data, sizeof(data), &dwSize, NULL);

        printf(" 指令集已替换. \n");
        getchar();
        CloseHandle(handle);
        return 0;
}通过驱动加载工具加载WinDDK.sys然后在运行本程序,你会看到正确的输出结果,可打开反内核工具验证是否改写成功。
https://img2023.cnblogs.com/blog/1379525/202302/1379525-20230222202411803-95027760.png
打开反内核工具,并切换到观察是否写入了一条mov eax,1的指令集机器码,如下图已经完美写入。
https://img2023.cnblogs.com/blog/1379525/202302/1379525-20230222202052101-321659965.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 驱动开发:内核实现进程汇编与反汇编