3.2 Windows驱动开发:内核CR3切换读写内存

CR3是一种控制寄存器,它是CPU中的一个专用寄存器,用于存储当前进程的页目录表的物理地址。在x86体系结构中,虚拟地址的翻译过程需要借助页表来完成。页表是由页目录表和页表组成的,页目录表存储了页表的物理地址,而页表存储了实际的物理页框地址。因此,页目录表的物理地址是虚拟地址翻译的关键之一。

在操作系统中,每个进程都有自己的地址空间,地址空间中包含了进程的代码、数据和堆栈等信息。为了实现进程间的隔离和保护,操作系统会为每个进程分配独立的地址空间。在这个过程中,操作系统会将每个进程的页目录表的物理地址存储在它自己的CR3寄存器中。当进程切换时,操作系统会修改CR3寄存器的值,从而让CPU使用新的页目录表来完成虚拟地址的翻译。

利用CR3寄存器可以实现强制读写特定进程的内存地址,这种操作需要一定的权限和技术知识。在实际应用中,这种操作主要用于调试和漏洞挖掘等方面。同时,由于CR3寄存器的读写属于有痕读写,因此许多驱动保护都会禁止或者修改CR3寄存器的值,以提高系统的安全性,此时CR3读写就失效了,当然如果能找到CR3的正确地址,此方式也是靠谱的一种读写机制。

在读写进程的时候,我们需要先找到目标进程的PEPROCESS结构。PEPROCESS结构是Windows操作系统中描述进程的一个重要数据结构,它包含了进程的各种属性和状态信息,如进程ID、进程优先级、内存布局等等。对于想要读写目标进程的内存,我们需要获得目标进程的PEPROCESS结构,才能进一步访问和操作进程的内存。

每个进程都有一个唯一的进程ID(PID),我们可以通过遍历系统中所有进程的方式来查找目标进程。对于每个进程,我们可以通过读取进程对象的名称来判断它是否是目标进程。如果找到了目标进程,我们就可以从其进程对象中获得指向PEPROCESS结构的指针。

具体的查找过程可以分为以下几个步骤:

  • 遍历系统中所有进程,获取每个进程的句柄(handle)和进程对象(process object)。
  • 从进程对象中获取进程的名称,并与目标进程的名称进行比较。
  • 如果找到了目标进程,从进程对象中获取指向PEPROCESS结构的指针。

需要注意的是,查找进程的过程可能会受到安全策略和权限的限制,因此需要在合适的上下文中进行。在实际应用中,需要根据具体的场景和要求进行合理的安全措施和权限管理。

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

NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process);

// 定义全局EProcess结构
PEPROCESS Global_Peprocess = NULL;

// 根据进程名获得EPROCESS结构
NTSTATUS GetProcessObjectByName(char *name)
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    SIZE_T i;

    __try
    {
        for (i = 100; i<20000; i += 4)
        {
            NTSTATUS st;
            PEPROCESS ep;
            st = PsLookupProcessByProcessId((HANDLE)i, &ep);
            if (NT_SUCCESS(st))
            {
                char *pn = PsGetProcessImageFileName(ep);
                if (_stricmp(pn, name) == 0)
                {
                    Global_Peprocess = ep;
                }
            }
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        return Status;
    }
    return Status;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    NTSTATUS nt = GetProcessObjectByName("Tutorial-i386.exe");

    if (NT_SUCCESS(nt))
    {
        DbgPrint("[+] eprocess = %x \n", Global_Peprocess);
    }

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

以打开Tutorial-i386.exe为例,打开后即可返回他的Proces,当然也可以直接传入进程PID同样可以得到进程Process结构地址。

// 根据PID打开进程
PEPROCESS Peprocess = NULL;
DWORD PID = 6672;
NTSTATUS nt = PsLookupProcessByProcessId((HANDLE)PID, &Peprocess);

通过CR3读取内存Tutorial-i386.exe里面的0x0009EDC8这段内存,读出长度是4字节,核心读取函数CR3_ReadProcessMemory其实现原理可概括为以下4步;

  • 首先,函数的输入参数包括目标进程的PEPROCESS结构指针Process、要读取的内存地址Address、读取的字节数Length以及输出缓冲区的指针Buffer。函数的返回值为布尔类型,表示读取操作是否成功。

  • 接着,函数使用了CheckAddressVal函数获取了目标进程页目录表的物理地址,并将其保存在变量pDTB中。如果获取页目录表的物理地址失败,函数直接返回FALSE,表示读取操作失败。

  • 接下来,函数使用了汇编指令_disable()来禁用中断,然后调用__readcr3()函数获取当前系统的CR3寄存器的值,保存在变量OldCr3中。接着,函数使用__writecr3()函数将CR3寄存器的值设置为目标进程的页目录表的物理地址pDTB。这样就切换了当前系统的地址空间到目标进程的地址空间。

  • 然后,函数使用了MmIsAddressValid()函数来判断要读取的内存地址是否可访问。如果可访问,函数就调用RtlCopyMemory()函数将目标进程内存中的数据复制到输出缓冲区中。函数最后打印了读入数据的信息,并返回TRUE表示读取操作成功。如果要读取的内存地址不可访问,函数就直接返回FALSE表示读取操作失败。

最后,函数使用了汇编指令_enable()来恢复中断,并使用__writecr3()函数将CR3寄存器的值设置为原来的值OldCr3,从而恢复了当前系统的地址空间。

需要注意的是,这段代码仅仅实现了对目标进程内存的读取操作,如果需要进行写操作,还需要在适当的情况下使用类似的方式切换地址空间,并使用相关的内存操作函数进行写操作。另外,这种方式的内存读取操作可能会受到驱动保护的限制,需要谨慎使用。

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

#define DIRECTORY_TABLE_BASE 0x028

#pragma  intrinsic(_disable)
#pragma  intrinsic(_enable)

NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process);

// 关闭写保护
KIRQL Open()
{
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    UINT64 cr0 = __readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);
    _disable();
    return irql;
}

// 开启写保护
void Close(KIRQL irql)
{
    UINT64 cr0 = __readcr0();
    cr0 |= 0x10000;
    _enable();
    __writecr0(cr0);
    KeLowerIrql(irql);
}

// 检查内存
ULONG64 CheckAddressVal(PVOID p)
{
    if (MmIsAddressValid(p) == FALSE)
        return 0;
    return *(PULONG64)p;
}

// CR3 寄存器读内存
BOOLEAN CR3_ReadProcessMemory(IN PEPROCESS Process, IN PVOID Address, IN UINT32 Length, OUT PVOID Buffer)
{
    ULONG64 pDTB = 0, OldCr3 = 0, vAddr = 0;
    pDTB = CheckAddressVal((UCHAR*)Process + DIRECTORY_TABLE_BASE);
    if (pDTB == 0)
    {
        return FALSE;
    }

    _disable();
    OldCr3 = __readcr3();
    __writecr3(pDTB);
    _enable();

    if (MmIsAddressValid(Address))
    {
        RtlCopyMemory(Buffer, Address, Length);
        DbgPrint("读入数据: %ld", *(PDWORD)Buffer);
        return TRUE;
    }

    _disable();
    __writecr3(OldCr3);
    _enable();
    return FALSE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    // 根据PID打开进程
    PEPROCESS Peprocess = NULL;
    DWORD PID = 6672;
    NTSTATUS nt = PsLookupProcessByProcessId((HANDLE)PID, &Peprocess);

    DWORD buffer = 0;

    BOOLEAN bl = CR3_ReadProcessMemory(Peprocess, (PVOID)0x0009EDC8, 4, &buffer);

    DbgPrint("readbuf = %x \n", buffer);
    DbgPrint("readbuf = %d \n", buffer);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

读出后输出效果如下:

如下所示是一个在Windows操作系统下写入目标进程内存的函数,函数CR3_WriteProcessMemory()使用了CR3寄存器的切换来实现对特定进程内存地址的强制写操作。

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

#define DIRECTORY_TABLE_BASE 0x028

#pragma  intrinsic(_disable)
#pragma  intrinsic(_enable)

NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);
NTKERNELAPI CHAR* PsGetProcessImageFileName(PEPROCESS Process);

// 关闭写保护
KIRQL Open()
{
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    UINT64 cr0 = __readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);
    _disable();
    return irql;
}

// 开启写保护
void Close(KIRQL irql)
{
    UINT64 cr0 = __readcr0();
    cr0 |= 0x10000;
    _enable();
    __writecr0(cr0);
    KeLowerIrql(irql);
}

// 检查内存
ULONG64 CheckAddressVal(PVOID p)
{
    if (MmIsAddressValid(p) == FALSE)
        return 0;
    return *(PULONG64)p;
}

// CR3 寄存器写内存
BOOLEAN CR3_WriteProcessMemory(IN PEPROCESS Process, IN PVOID Address, IN UINT32 Length, IN PVOID Buffer)
{
    ULONG64 pDTB = 0, OldCr3 = 0, vAddr = 0;

    // 检查内存
    pDTB = CheckAddressVal((UCHAR*)Process + DIRECTORY_TABLE_BASE);
    if (pDTB == 0)
    {
        return FALSE;
    }

    _disable();

    // 读取CR3
    OldCr3 = __readcr3();

    // 写CR3
    __writecr3(pDTB);
    _enable();

    // 验证并拷贝内存
    if (MmIsAddressValid(Address))
    {
        RtlCopyMemory(Address, Buffer, Length);
        return TRUE;
    }
    _disable();

    // 恢复CR3
    __writecr3(OldCr3);
    _enable();
    return FALSE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark \n");

    // 根据PID打开进程
    PEPROCESS Peprocess = NULL;
    DWORD PID = 6672;
    NTSTATUS nt = PsLookupProcessByProcessId((HANDLE)PID, &Peprocess);

    DWORD buffer = 999;

    BOOLEAN bl = CR3_WriteProcessMemory(Peprocess, (PVOID)0x0009EDC8, 4, &buffer);
    DbgPrint("写出状态: %d \n", bl);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

写出后效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/197533.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数字阅读用户规模持续增长 5.3亿人享受数字化阅读便利

近日,鲁迅长孙周令飞在接受采访时表示,自己“现在90%的时间刷视频,10%的时间看书”,引发网友热议。不少网友表示,鲁迅的孙子都花90%的时间刷视频,难怪现在没人看书了,其实这并不奇怪,也并不表明没人看书,而是读屏与读书并重的时代,纸质阅读与数字阅读共同构成了日常的阅读模式。…

机器学习常用距离度量方法

机器学习常用距离度量方法 前言一、前期准备二、距离度量方法1. 欧氏距离2.曼哈顿距离3.切比雪夫距离4. 闵可夫斯基距离 总结 前言 机器学习中往往通过度量来研究不同样本或数据集之间的差异性&#xff0c;合适的度量方式可以显著提高算法的准确率&#xff0c;因此在接下来的内…

js逆向-某赞滑块

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 如果觉得文章对你有所帮助&#xff0c;可以给博主点击关注和收藏哦&#xff01; 前言 目标网站&#xff1a;aHR0cHM6Ly9hY2NvdW50LnlvdXphbi5j…

科普 | 隧道代理IP,简化操作提升安全性

随着数字化时代的深入发展&#xff0c;企业对网络数据的依赖日益增强。在这样的背景下&#xff0c;隧道代理IP正在以其独特的优势改变传统的网络代理模式&#xff0c;为企业级数据采集领域带来革命性的变革。 隧道代理IP技术简介 隧道代理IP通过云端服务器实现自动化的HTTP代理…

检验科LIS系统源码,LIS系统,检验数据分析,生成检验报告

检验科LIS系统源码&#xff0c;全套LIS系统商业项目源码 LIS是HIS系统的一个重要的组成部分&#xff0c;其主要功能是将检验的实验仪器传出的检验数据经分析&#xff0c;生成检验报告&#xff0c;通过网络存储在数据库中&#xff0c;这样医生能够方便、及时的看到患者的检验结果…

96.STL-遍历算法 transform

目录 transform 语法&#xff1a; 功能描述&#xff1a; 函数原型&#xff1a; 代码示例&#xff1a; transform 是 C 标准模板库&#xff08;STL&#xff09;中的一个算法&#xff0c;用于对一个范围内的元素进行转换并将结果存储到另一个范围。以下是简要解释和一个示例…

leetcode 202.快乐数

代码&#xff1a; class Solution {//计算 n 每个位置上的数字的平方和public int quadraticSum(int n){int sum0;while (n>0){int in%10;sumi*i;n/10;}return sum;}public boolean isHappy(int n) {//慢指针int slown;//快指针int fastquadraticSum(n);while (slow!fast){…

JVM 参数介绍

在一些规模稍大的应用中&#xff0c;Java虚拟机&#xff08;JVM&#xff09;的内存设置尤为重要&#xff0c;想在项目中取得好的效率&#xff0c;GC&#xff08;垃圾回收&#xff09;的设置是第一步。 PermGen space&#xff1a;全称是Permanent Generation space.就是说是永久…

音视频学习(十九)——rtsp收流(tcp方式)

前言 本文主要介绍以tcp方式实现rtsp拉流。 流程图 流程说明: 客户端发起tcp请求&#xff0c;如向真实相机设备请求&#xff0c;端口一般默认554&#xff1b;tcp连接成功&#xff0c;客户端与服务端开始rtsp信令交互&#xff1b;客户端收到play命令响应后&#xff0c;开启线…

esp32 esp-idf V5.1.1版本看门狗配置

idf.py menuconfig打开配置窗口 选择Component config选项

LLM能力与应用全解析

一、简介 经过几年时间的发展&#xff0c;大语言模型&#xff08;LLM&#xff09;已经从新兴技术发展为主流技术。而以大模型为核心技术的产品将迎来全新迭代。大模型除了聊天机器人应用外&#xff0c;能否在其他领域产生应用价值&#xff1f;在回答这个问题前&#xff0c;需要…

【Docker项目实战】使用Docker部署Plik临时文件上传系统

【Docker实战项目】使用Docker部署Plik 临时文件上传系统 一、Plik介绍1.1 Plik简介1.2 Plik特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Plik镜像五、部署Plik临时…

手机爬虫用Fiddler详细教程

如果你正在进行手机爬虫的工作&#xff0c;那么一款强大而又实用的网络调试工具Fiddler将会是你的好帮手。今天&#xff0c;我将和大家分享一份详细的Fiddler教程&#xff0c;教你如何使用它来轻松捕获和分析手机App的网络请求。让我们一起来探索Fiddler的功能和操作&#xff0…

P25 C++ const关键字

前言 本期我们要讲的是 C 中的 const 关键字。 const 在改变生成代码方面做不了什么&#xff0c;它有点像类和结构体的可见性&#xff0c;这是一个机制&#xff0c;可以让我们的代码更加干净&#xff0c;并对开发人员写代码强制特定的规则。 const 就像你做出的承诺&#xf…

双馈风机频率二次跌落,永磁风机一次调频火电水电光伏储能直流一次调频,虚拟惯性下垂控制,虚拟同步机VSG控制,二次调频也可继续深入研究

双馈风机(永磁同步风机)虚拟惯性控制下垂控制参与系统一次调频的Matlab/Simulink模型&#xff0c;调频结束后转速回复&#xff0c;造成频率二次跌落SFD。 系统为三机九节点模型&#xff08;可更换为四机两区域&#xff0c;十机39节点&#xff0c;IEEE39节点&#xff0c;IEEE11…

idea 2023使用技巧(一)

IntelliJ IDEA在业界被公认为最好的java开发工具之一。它能给你良好的开发体验。 idea版本号为2023.2.5。 1 基础操作 1.1索引 idea首次加载项目时&#xff0c;都会创建索引&#xff0c;创建索引的时间跟项目的文件多少成正比。idea的缓存和索引主要是用来加快文件查询&…

Gossip协议理解

概述 Gossip协议&#xff0c;又称epidemic协议&#xff0c;基于流行病传播方式的节点或进程之间信息交换的协议&#xff0c;在分布式系统中被广泛使用。 在1987年8月由施乐-帕洛阿尔托研究中心发表ACM上的论文《Epidemic Algorithms for Replicated Database Maintenance》中…

Python 和 Node.js 之间通信 JSON 数据

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在实际应用中&#xff0c;不同编程语言之间的通信是常见的需求。Python和Node.js是两个流行且功能强大的编程语言&#xff0c;它们之间使用JSON格式进行数据交换是一种高效和灵活的方式。本文将详细介绍如何在Py…

Java线程通信

线程通信 案例 package com.itheima.d4;public class ThreadTest {public static void main(String[] args) {Desk desk new Desk();//创建3个生产者线程new Thread(() -> {while (true) {desk.put();}}, "厨师1").start();new Thread(() -> {while (true) {…

Gee教程3.实现前缀树路由

需要完成的目标 使用 Trie 树实现动态路由(dynamic route)解析。支持两种模式:name和*filepath&#xff0c;(开头带有:或者*) 这里前缀树的实现修复了Go语言动手写Web框架 - Gee第三天 前缀树路由Router | 极客兔兔​​​​​​ 中路由冲突的bug。 Trie树简介 之前&#xff0…