通过修改物理内存实现跨进程内存读写

习一下利用修改物理内存来跨进程内存读写

系统:win10 21h1 x64

编译环境: vs2022 详情见附录

基础

虚拟地址转物理地址

虚拟地址也称线性地址,一个线性地址+进程的DirBase地址可以转换成物理地址。先来看线性地址的含义

在x64体系中只实现了48位的virtual address,高16位被用作符号扩展,这高16位要么全是0,要么全是1。
不同于x86体系结构,每级页表寻址长度变成9位,由于在x64体系结构中,普通页大小仍为4KB,然而数据却表示64位长,因此一个4KB页在x64体系结构下只能包含512项内容,所以为了保证页对齐和以页为单位的页表内容换入换出,在x64下每级页表寻址部分长度定位9位。

 

从Page Map Level 4(PML4)开始到最后的物理地址,每一个都可以理解成一层页表的索引,索引值就是线性地址上不同的部分,分别缩写是PML4, PDPE, PDE,PTE。 

 

使用windbg可以先查看进程对应的DirBase地址,然后再使用!vtop Dirbase地址 虚拟地址查看虚拟地址对应的物理地址,如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

3: kd> !process 258c 0

Searching for Process with Cid == 258c

PROCESS ffffc40d2ab48340

    SessionId: 1  Cid: 258c    Peb: a6e35cd000  ParentCid: 1250

    DirBase: 235ae6000  ObjectTable: ffff998138d4ee00  HandleCount:  38.

    Image: test.exe

3: kd> !vtop 235ae6000 0000A6E334FB00

Amd64VtoP: Virt 000000a6e334fb00, pagedir 0000000235ae6000

Amd64VtoP: PML4E 0000000235ae6008

Amd64VtoP: PDPE 00000001087fb4d8

Amd64VtoP: PDE 000000010f7fc8c8

Amd64VtoP: PTE 00000000ad207a78

Amd64VtoP: Mapped phys 000000011b10cb00

Virtual address a6e334fb00 translates to physical address 11b10cb00.

上面得到DirBase的值是235ae6000,然后需要查看物理地址的虚拟地址是0x0000A6E334FB00,就使用命令

1

!vtop 235ae6000 0000A6E334FB00

得到最后对应的物理地址是0x11b10cb00。

简单例子代码如下:

1

2

3

4

5

6

7

8

9

10

11

#include <stdio.h>

#include <stdlib.h>

int main() {

    char flag[] = {"flag{b7285d748dd042a4929d3dbec778e637}"};

    printf("value addr: %p", flag);

    getchar();

    return 0;

}

运行后可以打印出来字符串的虚拟地址0000A6E334FB00,然后通过上述步骤得到物理地址。

我们尝试看看物理内存中的字符串,现在已经确定物理内存的地址是0xD0000147,使用!db 0xD0000147来查看物理内存,记住要!,没有感叹号的是查看虚拟内存的

1

2

3

4

5

6

7

8

9

3: kd> !db 0x11b10cb00

#11b10cb00 66 6c 61 67 7b 62 37 32-38 35 64 37 34 38 64 64 flag{b7285d748dd

#11b10cb10 30 34 32 61 34 39 32 39-64 33 64 62 65 63 37 37 042a4929d3dbec77

#11b10cb20 38 65 36 33 37 7d 00 00-f8 82 20 82 f7 7f 00 00 8e637}.... .....

#11b10cb30 00 00 00 00 00 00 00 00-20 13 1f 82 f7 7f 00 00 ........ .......

#11b10cb40 00 00 00 00 00 00 00 00-99 13 1f 82 f7 7f 00 00 ................

#11b10cb50 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

#11b10cb60 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

#11b10cb70 00 00 00 00 00 00 00 00-44 73 d3 08 fe 7f 00 00 ........Ds......

可以看到物理内存上的字符串内容。

DirBase地址获取

DirBase地址除了通过上述windbg直接得到这个值以外,还可以通过EPROCESS来得到,这个是代码比较需要的

1

2

3

4

5

6

7

8

9

10

11

12

13

3: kd> dt _eprocess ffffc40d2ab48340

nt!_EPROCESS

   +0x000 Pcb              : _KPROCESS

   +0x438 ProcessLock      : _EX_PUSH_LOCK

   +0x440 UniqueProcessId  : 0x00000000`0000258c Void

   +0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffffc40d`2cb43788 - 0xffffc40d`2cd444c8 ]

   +0x458 RundownProtect   : _EX_RUNDOWN_REF

   .....

3: kd> dx -id 0,0,ffffc40d23c95040 -r1 (*((ntkrnlmp!_KPROCESS *)0xffffc40d2ab48340))

(*((ntkrnlmp!_KPROCESS *)0xffffc40d2ab48340))                 [Type: _KPROCESS]

    [+0x000] Header           [Type: _DISPATCHER_HEADER]

    [+0x018] ProfileListHead  [Type: _LIST_ENTRY]

    [+0x028] DirectoryTableBase : 0x235ae6000 [Type: unsigned __int64]

DirectoryTableBase的值就是DirBase地址了,实际上就是EPROCESS + 0x28的偏移

还可以通过获取CR3寄存器的值,CR3寄存器中的值就是页目录表的物理地址,也就是DirBase

思路

目的:进程B可以通过修改物理内存的内容来修改进程A内存中的数据

实验设置:进程A泄露一个变量地址,然后等待进程B修改,修改后再回复执行,打印变量值看是否修改成功

内核部分思路:

  • 将R3的虚拟地址转换为物理地址
  • 使用MmCopyMemory复制物理地址内容
  • 修改内容
  • 使用mmMapIoSpaceEx将修改后的内容映射回物理地址

代码实现

被修改进程代码

这里写一个例子来充当被攻击(修改内存)的进程。主要就是打印变量内容和地址,然后暂停程序等待一段时间(等待被驱动修改),然后再打印变量内容,看看是否被驱动修改内存成功。

1

2

3

4

5

6

7

8

9

10

11

12

13

#include <stdio.h>

#include <stdlib.h>

int main() {

    char flag[] = {"flag{b7285d748dd042a4929d3dbec778e637}"};

    printf("value addr: %p\r\n", flag);

    printf("flag data: %s\r\n", flag);

    getchar();

    printf("flag data Now: %s\r\n", flag);

    return 0;

}

驱动代码

这里就是主要逻辑,通过驱动代码取修改目标进程的内存内容,做到跨进程内存读取,修改。

定义一个读取物理内存函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/// @brief 读取物理地址的内存内容

/// @param address 物理地址

/// @param buffer 复制内存地址到buffer

/// @param size 复制大小

/// @param BytesTransferred 读取的字节数

/// @return

NTSTATUS ReadPhysicalAddress(IN PVOID64 address, OUT PVOID64 buffer,

                             IN SIZE_T size, OUT SIZE_T* BytesTransferred)

{

    MM_COPY_ADDRESS Read          = {0};

    Read.PhysicalAddress.QuadPart = (LONG64)address;

    return MmCopyMemory(

        buffer, Read, size, MM_COPY_MEMORY_PHYSICAL, BytesTransferred);

}

再定义一个写入物理内存的函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

/// @brief 写入指定内容到物理内存中

/// @param address 被写入的物理地址

/// @param buffer 需要写入的缓冲区指针

/// @param size 需要写入的大小

/// @param BytesTransferred 写入成功后的大小

/// @return

NTSTATUS WritePhysicalAddress(IN PVOID64 address, IN PVOID64 buffer,

                              IN SIZE_T size, OUT SIZE_T* BytesTransferred)

{

    PVOID            map;

    PHYSICAL_ADDRESS Write = {0};

    if (!address) {

        kprintf("Address value error. \r\n");

        return STATUS_UNSUCCESSFUL;

    }

    Write.QuadPart = (LONG64)address;

    map            = MmMapIoSpaceEx(Write, size, PAGE_READWRITE);

    if (!map) {

        kprintf("Write Memory faild.\r\n");

        return STATUS_UNSUCCESSFUL;

    }

    RtlCopyMemory(map, buffer, size);

    *BytesTransferred = size;

    MmUnmapIoSpace(map, size);

    return STATUS_SUCCESS;

}

我们需要将虚拟地址转换成物理地址,那么首先需要线性地址+DirBase地址,DirBase地址获取是通过PEPROCESS+0x28偏移读取的

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

/// @brief 通过EPROCESS获取DirBase值

/// @param pid 进程PID

/// @param pDirbase 一个UINT64指针,获取成功后返回值

/// @return

NTSTATUS GetDirBaseByEprocess(IN UINT64 pid, OUT PUINT64 pDirbase)

{

    PEPROCESS pEprocess;

    NTSTATUS  status;

    status = PsLookupProcessByProcessId((HANDLE)pid, &pEprocess);

    if (!NT_SUCCESS(status)) {

        kprintf("[!] Get Pid=%d _EPROCESS failed!", pid);

        return STATUS_UNSUCCESSFUL;

    }

    *pDirbase =

        *(PUINT64)((PUCHAR)pEprocess + WIN10_21H1_EPROCESS2DIRBASE_OFFSET);

    kprintf("[+] uDirBase ==> %llx\r\n", *pDirbase);

    return STATUS_SUCCESS;

}

得到DirBase后,就可以虚拟地址转换物理地址。

传入虚拟地址后,取后48bit,然后将这48bit分成4个9bit和最后12bit,分别是PML4,PDPE,PDE,PTE和页内偏移offset。需要注意的是DirBase就已经是物理内存了,所以读取DirBase内容并且一层一层读取都要用自定义函数ReadPhysicalAddress

每一层都是基地址+8*偏移,读取的内容,取12-35bit就是下一层的基地址

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

/// @brief 传入DirBase值和虚拟地址后,回转化成一个物理地址返回

/// @param DirBase DirBase地址,传入一个UINT64值

/// @param addr 传入一个指向虚拟地址的指针,转化成物理地址后会修改这个指针的值

/// @return

NTSTATUS TranslateAddress(IN UINT64 DirBase, _Inout_ PUINT64 addr)

{

    UINT16   PML4, PDPE, PDE, PTE, offset;

    UINT64   mask = 0x7fffff000;

    UINT64   uTmp;

    SIZE_T   BytesTransferred;

    NTSTATUS status;

    offset = *addr & 0xfff;

    PTE    = (*addr >> 12) & 0x1ff;

    PDE    = (*addr >> (12 + 9)) & 0x1ff;

    PDPE   = (*addr >> (9 * 2 + 12)) & 0x1ff;

    PML4   = (*addr >> (9 * 3 + 12)) & 0x1ff;

    status = ReadPhysicalAddress(

        (PVOID64)(DirBase + PML4 * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

    uTmp &= mask;

    kprintf("[+] PML4(%x) ==> %llx\r\n", PML4, uTmp);

    status = ReadPhysicalAddress(

        (PVOID64)(uTmp + PDPE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

    uTmp &= mask;

    kprintf("[+] PDPE(%x) ==> %llx\r\n", PDPE, uTmp);

    status = ReadPhysicalAddress(

        (PVOID64)(uTmp + PDE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

    uTmp &= mask;

    kprintf("[+] PDE(%x) ==> %llx\r\n", PDE, uTmp);

    status = ReadPhysicalAddress(

        (PVOID64)(uTmp + PTE * 8), &uTmp, sizeof(uTmp), &BytesTransferred);

    uTmp &= mask;

    kprintf("[+] PTE(%x) ==> %llx\r\n", PTE, uTmp);

    *addr = uTmp + offset;

    kprintf("[+] physical address: %llx\r\n", *addr);

    return STATUS_SUCCESS;

}

最后再主函数中定义一下逻辑。这里直接手动指定进程号和目标进程打印出来的变量地址,然后将虚拟地址转化成物理地址,读取物理地址上的内容并打印出来看看是否正确。再修改物理地址上的内容。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING path)

{

    NTSTATUS status;

    UINT64   pid, uAddr, uDirBase;

    SIZE_T   BytesTransferred;

    UCHAR    charArry[40] = {0};

    UCHAR    example[40] = {"Yes I change memory by physical"};

    pid   = 10276;

    uAddr = 0x3629FAFB80;

    pDriver->DriverUnload = DriverUnload;

    // 手动指定进程号

    status = GetDirBaseByEprocess(pid, &uDirBase);

    if (!NT_SUCCESS(status)) {

        kprintf("[!] Get DirBase address failed!\r\n");

        return STATUS_UNSUCCESSFUL;

    }

    // 将虚拟地址转化成物理地址

    status = TranslateAddress(uDirBase, &uAddr);

    if (!NT_SUCCESS(status)) {

        kprintf("[!] Translate address failed!\r\n");

        return STATUS_UNSUCCESSFUL;

    }

    // 读取物理地址内容, 然后修改内容

    ReadPhysicalAddress((PVOID64)uAddr, charArry, 40, &BytesTransferred);

    kprintf("[+] data is %s\r\n", charArry);

    // 将example字符串写入物理内存

    WritePhysicalAddress((PVOID64)uAddr, example, 40, &BytesTransferred);

    kprintf("[+] Write end\r\n");

    return STATUS_SUCCESS;

}

结果

目标进程

 

可以看到目标进程的指定内存被修改,同时驱动也跨进程读取,修改内存成功 

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

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

相关文章

力扣算法之627. 变更性别

仅作为个人解题思路记录 题解: 1.要求将m换成f&#xff0c;f换成m 2.要求使用一句update语句 我的解 UPDATE Salary SET sex (CASE WHEN Salary.sexm THEN f WHEN Salary.sexf THEN m end) 我的解注解 sql server中update中允许使用case以及iif

代码随想录算法训练营第十七天(py)| 二叉树 | 110.平衡二叉树、 257. 二叉树的所有路径、404.左叶子之和

110.平衡二叉树 力扣链接 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树&#xff08;所有节点的左右子树深度不会超过1&#xff09; 思路 后序遍历 如果判断到子树不是平衡二叉树&#xff0c;就返回-1&#xff0c;这个-1会一路向上返回到根节点 class Solution:def i…

【机器学习-08】 | Scikit-Learn工具包进阶指南:Scikit-Learn工具包之决策树算法实战分析

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

(3)医疗图像处理:MRI磁共振成像-快速采集--(杨正汉)

目录 一、磁共振快速采集技术基础 1.K空间的基本特点 2.快速成像的理由&#xff1a; 3.快速成像的硬件要求&#xff1a; 二、磁共振快速采集技术 1.采集更少的相位编码线 2.平行采集技术PAT 3.其他与快速采集有关的技术 1&#xff09;部分回波技术 2&#xff09;频率…

四川音盛佳云电子商务有限公司正规吗?靠谱吗?

在数字化浪潮席卷全球的今天&#xff0c;电子商务已成为推动经济发展的重要引擎。四川音盛佳云电子商务有限公司&#xff0c;作为抖音电商服务的佼佼者&#xff0c;正以其独特的视角和创新的策略&#xff0c;引领着抖音电商的新潮流&#xff0c;开启着电商服务的新篇章。 四川…

单例模式中的 双判断锁 问题、单例模式的资源问题

》》》Lazy 不存在高并发问题&#xff0c;lazy已经解决了。 CLR 类执行的顺序 静态变量初始化 1次静态构造函数 1次实例变量初始化基类静态变量初始化 1次基类静态构造函数 1次基类实例变量初始化基类实例构造函数实例构造函数 》》》 创建单例模式 好多种 1&#xff0c;静态…

Linux x86_64 UEFI 启动

文章目录 前言一、UEFI二、Disk device compatibility2.1 GPT 磁盘分区表2.1.1 简介2.1.2 Linux 2.2 ESP&#xff08;EFI&#xff09; 文件系统2.2.1 简介2.2.2 LinuxLinux Kernel EFI Boot Stub 三、UEFI GPT grub23.1 简介3.2 引导方式 3.3 BOOTX64.EFI3.4 shimx64.efi3.5 …

4.双指针+递归

一、双指针编程技巧 方法参数传递数组 将数组通过方法参数传递&#xff0c;方法操作的数组和main方法中的数组指向同一块内存区域&#xff0c;意味着方法操作数组&#xff0c;同时会引起main方法中数组的改变以引用的方式作为方法参数进行传递的 元素交换 定义临时变量temp&a…

代码随想录算法训练营第16天 |● 104.二叉树的最大深度 559.n叉树的最大深度 ● 111.二叉树的最小深度 ● 222.完全二叉树的节点个数

文章目录 前言104.二叉树的最大深度思路知识点 方法一 递归法方法二 迭代法 559. n叉树的最大深度111.二叉树的最小深度思路方法一 后向遍历递归法方法二 迭代法 222.完全二叉树的节点个数思路方法一 当成普通二叉树来做方法二 利用完全二叉树的特性 总结 前言 所有的题目一刷…

带你玩转OpenHarmony AI:打造智能语音子系统

简介 AI时代&#xff0c;智者当先&#xff0c;判断一个终端设备是否智能&#xff0c;语音能力是必不可缺的。智能家居、智慧厨房、智能汽车等等&#xff0c;一切衣食住行都在往智能方向发展&#xff0c;那我们该如何在OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&am…

MySQL进阶 日志结尾以及8.0新特性

日志结尾 前面我们聊了mysql的undo日志,redo日志,binlog等等,也从一条update语句来分析了一下日志的执行思路以及版本控制是怎么回事,四大特性是怎么实现的等等 今天我们来说说最后一个错误日志 其实用处不大 因为对我们开发人员来说基本上是没有权限来查看错误日志的 一般…

c++读取文本文件出现乱码问题

else if (type 2) { //教师身份验证 int fId; //从文件中获取的id号 string fName; //从文件中获取的姓名 string fPwd; //从文件中获取的密码 while (ifs >> fId && ifs >> fName && ifs >> fPwd) { cout…

windows Oracle 11g服务器端和客户端安装 SQLark连接ORACLE

1 从ORACLE官网下载数据库安装包 https://edelivery.oracle.com/osdc/faces/SoftwareDelivery 2:安装数据库 注意&#xff1a;在加载组件的这一步&#xff0c;如果你的电脑里面有杀毒软件&#xff0c;首先把安装目录加入白名单&#xff0c;要不然可能会一直加载组件失败。…

面向对象的理解

1.结构化程序设计(面向过程) 结构化程序主张按功能来分析系统需求&#xff0c;结构化的主要原则&#xff1a; 自顶向下 逐步求精 模块化设计 结构化程序会按功能把程序分为一个个的单独的文件&#xff0c;例如&#xff1a;让灯亮这个功能&#xff0c;就会由多个函数构成一…

银行总部文件自动下发,如何保证不影响专线网络使用?

银行在我国金融体系中占据重要地位&#xff0c;是我国市场经济的重要组成部分。我国商业银行随着自身不断发展&#xff0c;规模日益扩大&#xff0c;形成了“总行-分行-支行-营业网点”的典型层级管理模式。在日常中&#xff0c;银行总部存在文件下发的场景&#xff1a; 银行总…

c4d云渲染是工程文件会暴露吗?

在数字创意产业飞速发展的今天&#xff0c;C4D云渲染因其高效便捷而备受欢迎。然而&#xff0c;随着技术应用的深入&#xff0c;人们开始关注一个核心问题&#xff1a;在享受云渲染带来的便利的同时&#xff0c;C4D工程文件安全吗&#xff1f;是否会有暴露的风险&#xff1f;下…

常见的字符编码

字符&#xff1a;各种文字和符号的总称&#xff0c;包括各个国家的文字&#xff0c;标点符号&#xff0c;图形符号&#xff0c;数字等 字符集&#xff1a;字符集是多个符号的集合&#xff0c;每个字符集包含的字符个数不同 字符编码&#xff1a;字符集只是规定了有哪些字符&a…

openlayers绘制经纬网格,有添加或者移除功能

项目需要在地图中添加经纬网格&#xff0c;然后看了一下官网有相关的介绍 官网 我的项目是vue写的&#xff0c;当点击多选框显示隐藏经纬网格&#xff0c;下面直接写代码 这是绘制经纬网格方法 //引入 import TileArcGISRest from ol/source/TileArcGISRest import "ol/o…

k近邻和kd树

K近邻 选取k值的时候可以采用交叉验证的方法 一般采用欧氏距离 kd树 采用树这个特殊的数据结构来实现k近邻算法 先假设是二维的情况 下面讲解kd树的完整构造过程 找这个中位数是按照每棵子树来创建的 前提是已经有了一棵kd树,然后来一个实例点

Ai编码的助手,现在我用这个

给你分享一个AI编码助手—百度Comate&#xff01; https://comate.baidu.com/zh/shopping?inviteCodeaz5z518a 记得在你的vscode 或 jetbrains编码工具里体验体验哦