学习一下利用修改物理内存来跨进程内存读写
系统: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。
注意,并不是取出来的值就直接指向一下一个页表,个人PC上一般是取值的12-35bit的值,其他置0。具体的后面见代码,或参考看雪的文章
使用windbg可以先查看进程对应的DirBase地址,然后再使用!vtop Dirbase地址 虚拟地址
查看虚拟地址对应的物理地址,如下。
上面得到DirBase的值是235ae6000
,然后需要查看物理地址的虚拟地址是0x0000A6E334FB00
,就使用命令
得到最后对应的物理地址是0x11b10cb00。
简单例子代码如下:
运行后可以打印出来字符串的虚拟地址0000A6E334FB00
,然后通过上述步骤得到物理地址。
我们尝试看看物理内存中的字符串,现在已经确定物理内存的地址是0xD0000147
,使用!db 0xD0000147
来查看物理内存,记住要!
,没有感叹号的是查看虚拟内存的
可以看到物理内存上的字符串内容。
DirBase地址除了通过上述windbg直接得到这个值以外,还可以通过EPROCESS来得到,这个是代码比较需要的
DirectoryTableBase的值就是DirBase地址了,实际上就是EPROCESS + 0x28的偏移
还可以通过获取CR3寄存器的值,CR3寄存器中的值就是页目录表的物理地址,也就是DirBase
目的:进程B可以通过修改物理内存的内容来修改进程A内存中的数据
实验设置:进程A泄露一个变量地址,然后等待进程B修改,修改后再回复执行,打印变量值看是否修改成功
内核部分思路:
这里写一个例子来充当被攻击(修改内存)的进程。主要就是打印变量内容和地址,然后暂停程序等待一段时间(等待被驱动修改),然后再打印变量内容,看看是否被驱动修改内存成功。
这里就是主要逻辑,通过驱动代码取修改目标进程的内存内容,做到跨进程内存读取,修改。
定义一个读取物理内存函数
再定义一个写入物理内存的函数
我们需要将虚拟地址转换成物理地址,那么首先需要线性地址+DirBase 地址,DirBase地址获取是通过PEPROCESS+0x28偏移读取的
得到DirBase后,就可以虚拟地址转换物理地址。
传入虚拟地址后,取后48bit,然后将这48bit分成4个9bit和最后12bit,分别是PML4,PDPE,PDE,PTE和页内偏移offset。需要注意的是DirBase就已经是物理内存了,所以读取DirBase内容并且一层一层读取都要用自定义函数ReadPhysicalAddress 。
每一层都是基地址+8*偏移 ,读取的内容,取12-35bit 就是下一层的基地址
最后再主函数中定义一下逻辑。这里直接手动指定进程号和目标进程打印出来的变量地址,然后将虚拟地址转化成物理地址,读取物理地址上的内容并打印出来看看是否正确。再修改物理地址上的内容。
目标进程
驱动
可以看到目标进程的指定内存被修改,同时驱动也跨进程读取,修改内存成功
使用CR3切换实现读取指定进程内存数据 | pnpon.com
c/c++/易语言驱动内存无痕读写源码 | csdn.net
将虚拟地址转换为物理地址 | learn.microsoft.com
X64下的虚拟地址到物理地址的转换 | bbs.kanxue.com
几种挖掘任意读写驱动的方法 | myzxcg.com/
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.
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.
!vtop 235ae6000 0000A6E334FB00
!vtop 235ae6000 0000A6E334FB00
#include <stdio.h>
#include <stdlib.h>
int
main() {
char
flag[] = {
"flag{b7285d748dd042a4929d3dbec778e637}"
};
printf
(
"value addr: %p"
, flag);
getchar
();
return
0;
}
#include <stdio.h>
#include <stdlib.h>
int
main() {
char
flag[] = {
"flag{b7285d748dd042a4929d3dbec778e637}"
};
printf
(
"value addr: %p"
, flag);
getchar
();
return
0;
}
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......
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......
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
]
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
]
#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;
}
#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;
}
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);
}
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);
}
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;
}
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;
}
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;
}
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;
}
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 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 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);
WritePhysicalAddress((PVOID64)uAddr, example, 40, &BytesTransferred);
kprintf(
"[+] Write end\r\n"
);
return
STATUS_SUCCESS;
}
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;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)