其他
通过修改物理内存实现跨进程内存读写
一
环境搭建
系统:win10 21h1 x64 编译环境: vs2022 详情见附录
一
基础
虚拟地址转物理地址
在x64体系中只实现了48位的virtual address,高16位被用作符号扩展,这高16位要么全是0,要么全是1。
不同于x86体系结构,每级页表寻址长度变成9位,由于在x64体系结构中,普通页大小仍为4KB,然而数据却表示64位长,因此一个4KB页在x64体系结构下只能包含512项内容,所以为了保证页对齐和以页为单位的页表内容换入换出,在x64下每级页表寻址部分长度定位9位。
注意,并不是取出来的值就直接指向一下一个页表,个人PC上一般是取值的12-35bit的值,其他置0。具体的后面见代码,或参考看雪的文章(https://bbs.kanxue.com/thread-203391.htm)。
!vtop Dirbase地址 虚拟地址
查看虚拟地址对应的物理地址,如下: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.
235ae6000
,然后需要查看物理地址的虚拟地址是0x0000A6E334FB00
,就使用命令#include <stdlib.h>
int main() {
char flag[] = {"flag{b7285d748dd042a4929d3dbec778e637}"};
printf("value addr: %p", flag);
getchar();
return 0;
}
0000A6E334FB00
,然后通过上述步骤得到物理地址。0xD0000147
,使用!db 0xD0000147
来查看物理内存,记住要!
,没有感叹号的是查看虚拟内存的。#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地址获取
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]
二
思路
三
代码实现
被修改进程代码
#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;
}
驱动代码
/// @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);
}
/// @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;
}
/// @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;
}
/// @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;
}
{
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;
}
四
结果
五
参考
(https://myzxcg.com/2024/03/%E5%87%A0%E7%A7%8D%E6%8C%96%E6%8E%98%E4%BB%BB%E6%84%8F%E8%AF%BB%E5%86%99%E9%A9%B1%E5%8A%A8%E7%9A%84%E6%96%B9%E6%B3%95/)
六
附录
驱动全部代码
#include <ntddk.h>
#include <intrin.h>
#include <ntdef.h>
#define WIN10_21H1_EPROCESS2DIRBASE_OFFSET 0x28
#define kprintf(...) \
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__))
NTSTATUS GetDirBaseByEprocess(IN UINT64, OUT PUINT64);
NTSTATUS TranslateAddress(IN UINT64, _Inout_ PUINT64);
NTSTATUS ReadPhysicalAddress(IN PVOID64, OUT PVOID64, IN SIZE_T, OUT SIZE_T*);
NTSTATUS WritePhysicalAddress(IN PVOID64, IN PVOID64, IN SIZE_T, OUT SIZE_T*);
NTKERNELAPI PPEB NTAPI PsGetProcessPeb(IN PEPROCESS Process);
NTKERNELAPI PVOID NTAPI PsGetProcessWow64Process(IN PEPROCESS Process);
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
kprintf("驱动已卸载.\r\n");
}
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;
}
/// @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);
}
/// @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;
}
/// @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;
}
/// @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;
}
编译环境
Version 17.9.6
VisualStudio.17.Release/17.9.6+34728.123
Microsoft .NET Framework
Version 4.8.09032
Installed Version: Community
Visual C++ 2022 00482-90000-00000-AA134
Microsoft Visual C++ 2022
ASP.NET and Web Tools 17.9.199.22661
ASP.NET and Web Tools
Azure App Service Tools v3.0.0 17.9.199.22661
Azure App Service Tools v3.0.0
C# Tools 4.9.0-3.24121.1+a98c90d56455379836dd5c845b35fa932b00cfa3
C# components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.
Debugging Tools for Windows 10.0.26090.1
Integrates the Windows Debugger functionality (http://go.microsoft.com/fwlink/?linkid=223405) in Visual Studio.
Microsoft JVM Debugger 1.0
Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines
NuGet Package Manager 6.9.2
NuGet Package Manager in Visual Studio. For more information about NuGet, visit https://docs.nuget.org/
Test Adapter for Boost.Test 1.0
Enables Visual Studio's testing tools with unit tests written for Boost.Test. The use terms and Third Party Notices are available in the extension installation directory.
Test Adapter for Google Test 1.0
Enables Visual Studio's testing tools with unit tests written for Google Test. The use terms and Third Party Notices are available in the extension installation directory.
TypeScript Tools 17.0.30103.2001
TypeScript Tools for Microsoft Visual Studio
Visual Basic Tools 4.9.0-3.24121.1+a98c90d56455379836dd5c845b35fa932b00cfa3
Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.
Visual Studio IntelliCode 2.2
AI-assisted development for Visual Studio.
Windows Driver Kit 10.0.26090.1
Headers, libraries, and tools needed to develop, debug, and test Windows drivers (msdn.microsoft.com/en-us/windows/hardware/gg487428.aspx)
看雪ID:mi1itray.axe
https://bbs.kanxue.com/user-home-900501.htm
# 往期推荐
1、CVE-2020-9802:Incorrect CSE for ArithNegate 导致的越界访问2、通过BLECTF入门BLE3、InfinityHook 可兼容最新版windows4、CVE-2023-4427:ReduceJSLoadPropertyWithEnumeratedKey 中的越界访问5、Windows 主机入侵检测与防御内核技术深入解析球分享
球点赞
球在看
点击阅读原文查看更多