基础驱动读写

忙里偷闲 尝试驱动读写

忙里偷闲敲会驱动玩玩,第一种驱动读内存方法,注册进程回调去读内存,看注释大概就懂了

extern “C” {

#include<ntifs.h>
#include<windef.h>

    NTKERNELAPI
        PVOID
        PsGetProcessSectionBaseAddress(
            __in PEPROCESS Process
        );
    //PsGetProcessSectionBaseAddress可以获得当前进程的基址
    //EPROCESS +0x128偏移处 为SectionBaseAddress

    BYTE Code[8] = { 0 };
    ULONG offset = 0x10;



    VOID MyProcessNotify(HANDLE ParentID, HANDLE ProcessID, BOOLEAN Create) {
        if (Create) {
            PEPROCESS process = NULL;
            NTSTATUS status = STATUS_SUCCESS;
            PUCHAR addr = NULL;
            KAPC_STATE temp_stack = { 0 };

            status = PsLookupProcessByProcessId(ProcessID, &process);
            //通过pid获得PEPROCESS结构体
            if (process) {
                ObDereferenceObject(process);
                addr = (PUCHAR)PsGetProcessSectionBaseAddress(process);
                if (!addr) {
                    DbgPrint("Get addr failed \n");
                    return;
                }

                RtlZeroMemory(Code, sizeof(Code));
                KeStackAttachProcess(process, &temp_stack);
                __try {
                    ProbeForRead((addr + offset), sizeof(Code), 1);
                    //最好使用这个函数可以用来测试addr+offset这个地址的合法性
                    //一般使用MmIsAddress这类的函数结果可能不准确(由于缺页异常)
                    RtlCopyMemory((PVOID)Code, (PVOID)(addr + offset), sizeof(Code));

                }
                __except (1) {
                    DbgPrint("illegal addr\n");
                }
                KeUnstackDetachProcess(&temp_stack);
                for (int i = 0; i < sizeof(Code); i++) {
                    DbgPrint("read %x", Code[i]);
                }
                return;
            }

        }
    }

    VOID Unload(PDRIVER_OBJECT driver) {
        PsSetCreateProcessNotifyRoutine(MyProcessNotify, TRUE);
        //驱动卸载前必须移除全部回调
        DbgPrint("unloading ...\n");
    }

    NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) {
        driver->DriverUnload = Unload;
        NTSTATUS status = STATUS_SUCCESS;
        status = PsSetCreateProcessNotifyRoutine(MyProcessNotify, FALSE);
        //创建进程回调,FALSE和true代表是否插入全局回调表中
        //A driver must remove any callbacks that it registers before it unloads.
        //You can remove the callback by calling PsSetCreateProcessNotify with Remove = TRUE.

        return STATUS_SUCCESS;

    }
}

运行截图
image_1dasudsnk1tq91g6e1dnqc3n13rp19.png-287.3kB

可以看到成功的读到了刚刚创建的进程中的,addr+offset处的值,但是想要写的话,直接写可能会出问题,addr+0x1000往后是数据代码段,代码段一般是不可写的,两种放法写,一种是通过修改cr0寄存器的保护位强行写,另一种就是通过mdl间接写入内存。

NTSTATUS ReadAndWriteMem(HANDLE processID) {
        NTSTATUS status = STATUS_SUCCESS;
        HANDLE pid = NULL;
        PEPROCESS process = NULL;
        KAPC_STATE temp_stack = { 0 };
        PVOID addr=0;
        PMDL tempmdl = NULL;
        PUCHAR pmapped = NULL;

        status = PsLookupProcessByProcessId(pid, &process);
        //同样获取process结构体
        if (!NT_SUCCESS(status)) {
            DbgPrint("error...\n");
            return status;
        }
        ObDereferenceObject(process);
        KeStackAttachProcess(process, &temp_stack);
        //attach上目标进程实现读写
        tempmdl = IoAllocateMdl(addr, 4, 0, 0, NULL);
        //分配一块mdl指向的是目标地址
        if (!tempmdl) {
            DbgPrint("allocate failed...\n");
            KeUnstackDetachProcess(&temp_stack);
            return STATUS_UNSUCCESSFUL;
        }
        MmBuildMdlForNonPagedPool(tempmdl);
        //为mdl创建非分页内存
        __try {
            pmapped = (PUCHAR)MmMapLockedPages(tempmdl, KernelMode);
            //映射mdl内存
        }
        __except (1) {
            DbgPrint("映射失败...\n");
            IoFreeMdl(tempmdl);
            return STATUS_UNSUCCESSFUL;
        }
        for (int i = 0; i<4; i++)
            DbgPrint("read %x\n", *(pmapped + i));
        //先读出来

        BYTE code[] = { 0xcc };
        for (int i = 0; i<4; i++)
            RtlCopyMemory(pmapped + i, code, 1);
        //写入内存

        for (int i = 0; i<4; i++)
            DbgPrint("read after write %x\n", *(pmapped + i));

        MmUnmapLockedPages((PVOID)pmapped, tempmdl);
        IoFreeMdl(tempmdl);
        KeUnstackDetachProcess(&temp_stack);
        return STATUS_SUCCESS;
    }

这种mdl读取内存的方式可以绕过代码段的写保护,当然更暴力一点的就是直接改cr0寄存器啦

void WPOFF()
{
    __asm
    {
        cli;//将处理器标志寄存器的中断标志位清0,不允许中断
        mov eax, cr0
        and eax, not 0x10000
        mov cr0, eax
    }
}


void WPON() {
    __asm
    {
        mov  eax, cr0
        or eax, 0x10000
        mov  cr0, eax
        sti;//将处理器标志寄存器的中断标志置1,允许中断
    }
}