本文主要讲32位分页的一些格式和内核附加读写的方法
分页"废除了"分段
来看windbg拿到的GDT表,没学过的看下前文 实现简易ARK工具(1) GDT表

前面四个段实现了windows的平坦内存模型,他们的共性:
此时看到的虚拟地址就是线性地址,选择子CS已经是0 ,分段变得"透明",所以我们平时写软件只需要关心虚拟内存就可以了。
Windows有多种分页机制:
1. 32位分页
2. PAE分页(物理地址扩展)
3. IA-32e分页(64位)
本文涉及前两种,也就是XP,win7 相关,欸其他的等我学了再说(狗头
分页由寄存器中的三个标志位控制:
由此排列组合有六种情况

数据结构
CR3寄存器:
PDPTE
PDE:

PTE:

死去的408在攻击我(呕呕呕 ) 分页情况太多,直接看Intel手册卷3吧,我这块基本都删了,写的没啥营养,这是网上的中文版卷3链接IA-32架构软件开发人员指南:卷3
还有前辈文章: https://bbs.kanxue.com/thread-270490.htm
转换需要用到页目录表 (Page Directory Table)和页表 (Page Table)。

为什么要设计两张表?
转换为物理地址后,如何读写物理内存
与设备通讯或者使用内核API。
早期可以通过这个设备,和他通讯就可以操作物理内存,在XP中R3也可以打开,win10修复了。
XP内核四个版本
XP有四种内核模式,可以将这些文件拖入ida分析内核API
附加读进程的本质是切换CR3到目标进程的页目录基址然后查它的页表
相关结构体与API:
通过PID获取进程的EPROCESS结构指针,可以拿到进程的页目录表基址。
EPROCESS结构体层次关系:
注意这个函数会增加引用计数。

思路
可以过早期的保护?(在今天看是不是也不管用了)
常规的R3读写目标进程常常使用:
OpenProcess、 VirtualProtectEx、ReadProcessMemory CreateRemoteThread``SetWindowsHookEx等API。实际这些api最后会进ntdll调内核函数。
R0常常使用ZwReadVirtualMemory ZwWriteVirtualMemory MmCopyVirtualMemory KeStackAttachProcess RtlCopyMemory KeUnstackDetachProcess等API。
安全软件会把大部分内核函数全部hook一遍,上述方法都会被监控。
附加读写+物理内存映射的优势:首先可以不管读写权限(自己映射可读可写),其次能绕过一些被监控的API。
MmMapIoSpace由于系统大量使用,hook它会极大影响性能,可能会让系统卡顿,相对不容易被监控。
NyMmGetPhysicalAddress可以自己实现。(偷懒
一些问题
问1:修改CR3把页目录表切为目标进程的,那怎么跑后续自己的程序代码?
答:我们的代码在内核,不分进程,我们切换的是低2G的内存。所以读写的时候缓冲区地址一定要给内核地址(放心用systembuffer)。
问2:本章的思路是附加进程拿到CR3,为对方的物理内存映射虚拟内存,对虚拟内存进行内存拷贝,然后切回自己的CR3,如果KeStackAttachProcess之后的操作中被切了线程,会不会寄?
答:需要提升中断级别 或 屏蔽中断:CLi SLi指令来阻止线程切换,注意Cli只能防止当前核心切换。
问3:假设我们现在想要读0x00401000,但是目标进程没有这个虚拟内存,映射到的物理地址是0x00000000,那么会将内核地址0地址映射为一块虚拟地址。此时UI上显示的还是我们传入的00401000,这会误导使用者。
答:可以在驱动验证一下映射的物理地址是否为0:if (PhysicalAddress.QuadPart == 0)
在32位Windows中,内核地址空间通常在 0x80000000 以上, 内核虚拟地址会被正确映射到物理地址。MmGetPhysicalAddress结果为0表示虚拟地址没有被映射,可能是无效虚拟地址或页面被换出(缺页)。
Imgui有开源的内存编辑器控件,直接拿来用了。遍历进程的文章之后再写。
读个EditPlus,写内存把dos头抹了打不开了,说明读写成功。(为什么会改到文件,是不是用了映射)

代码
待续:如何在内核中遍历进程...
Sel Base Limit Type P Si Gr Pr Lo Flags
---- -------- -------- ---------- - -- -- -- -- --------
//关注前面四个"平坦化的段"
0008 00000000 ffffffff Code RE Ac 0 Bg Pg P Nl 00000c9b ; 内核代码段
0010 00000000 ffffffff Data RW Ac 0 Bg Pg P Nl 00000c93 ; 内核数据段
0018 00000000 ffffffff Code RE Ac 3 Bg Pg P Nl 00000cfb ; 用户代码段
0020 00000000 ffffffff Data RW Ac 3 Bg Pg P Nl 00000cf3 ; 用户数据段
//后面还有一些特殊功能段 我也不懂~ 无视吧
0028 80b93c00 000020ab TSS32 Busy 0 Nb By P Nl 0000008b ; 当前TSS
0050 80b95d20 00000067 TSS32 Avl 0 Nb By P Nl 00000089 ; 备用TSS
0058 80b95cb0 00000067 TSS32 Avl 0 Nb By P Nl 00000089 ; 备用TSS
0030 84d26000 00004fff Data RW Ac 0 Bg By P Nl 00000493 ; 处理器控制区域段 (PCR)
0038 00000000 0000ffff Data RW Ac 3 Bg By P Nl 000004f3 ; 16位兼容段
0040 00000400 0000ffff Data RW 3 Nb By P Nl 000000f2 ; DOS兼容段
0070 80b93800 0000003f Data RW 0 Nb By P Nl 00000092 ; 内核数据结构
Sel Base Limit Type P Si Gr Pr Lo Flags
---- -------- -------- ---------- - -- -- -- -- --------
//关注前面四个"平坦化的段"
0008 00000000 ffffffff Code RE Ac 0 Bg Pg P Nl 00000c9b ; 内核代码段
0010 00000000 ffffffff Data RW Ac 0 Bg Pg P Nl 00000c93 ; 内核数据段
0018 00000000 ffffffff Code RE Ac 3 Bg Pg P Nl 00000cfb ; 用户代码段
0020 00000000 ffffffff Data RW Ac 3 Bg Pg P Nl 00000cf3 ; 用户数据段
//后面还有一些特殊功能段 我也不懂~ 无视吧
0028 80b93c00 000020ab TSS32 Busy 0 Nb By P Nl 0000008b ; 当前TSS
0050 80b95d20 00000067 TSS32 Avl 0 Nb By P Nl 00000089 ; 备用TSS
0058 80b95cb0 00000067 TSS32 Avl 0 Nb By P Nl 00000089 ; 备用TSS
0030 84d26000 00004fff Data RW Ac 0 Bg By P Nl 00000493 ; 处理器控制区域段 (PCR)
0038 00000000 0000ffff Data RW Ac 3 Bg By P Nl 000004f3 ; 16位兼容段
0040 00000400 0000ffff Data RW 3 Nb By P Nl 000000f2 ; DOS兼容段
0070 80b93800 0000003f Data RW 0 Nb By P Nl 00000092 ; 内核数据结构
开启分页后:
1. 程序使用32位虚拟地址 (如: 0x00401000)
2. CPU自动加上段基址: 0x00000000 + 0x00401000 = 0x00401000
3. 得到线性地址: 0x00401000
4. 通过分页机制转换为物理地址
开启分页后:
1. 程序使用32位虚拟地址 (如: 0x00401000)
2. CPU自动加上段基址: 0x00000000 + 0x00401000 = 0x00401000
3. 得到线性地址: 0x00401000
4. 通过分页机制转换为物理地址
typedef union _CR3_32 {
struct {
ULONG Reserved1 : 3;
ULONG PWT : 1;
ULONG PCD : 1;
ULONG Reserved2 : 7;
ULONG PageDirectoryBase : 20;
};
ULONG AsULong;
} CR3_32, *PCR3_32;
typedef union _CR3_PAE {
struct {
ULONG PWT : 1;
ULONG PCD : 1;
ULONG Reserved : 3;
ULONG Reserved2 : 1;
ULONG PDPT_Base : 23;
};
ULONG AsULong;
} CR3_PAE, *PCR3_PAE;
typedef union _CR3_32 {
struct {
ULONG Reserved1 : 3;
ULONG PWT : 1;
ULONG PCD : 1;
ULONG Reserved2 : 7;
ULONG PageDirectoryBase : 20;
};
ULONG AsULong;
} CR3_32, *PCR3_32;
typedef union _CR3_PAE {
struct {
ULONG PWT : 1;
ULONG PCD : 1;
ULONG Reserved : 3;
ULONG Reserved2 : 1;
ULONG PDPT_Base : 23;
};
ULONG AsULong;
} CR3_PAE, *PCR3_PAE;
typedef union _PDPTE_PAE {
struct {
ULONGLONG P : 1;
ULONGLONG Reserved1 : 2;
ULONGLONG PWT : 1;
ULONGLONG PCD : 1;
ULONGLONG Reserved2 : 4;
ULONGLONG AVL : 3;
ULONGLONG PageDirBase : 24;
ULONGLONG Reserved3 : 28;
};
ULONGLONG AsULongLong;
} PDPTE_PAE, *PPDPTE_PAE;
typedef union _PDPTE_PAE {
struct {
ULONGLONG P : 1;
ULONGLONG Reserved1 : 2;
ULONGLONG PWT : 1;
ULONGLONG PCD : 1;
ULONGLONG Reserved2 : 4;
ULONGLONG AVL : 3;
ULONGLONG PageDirBase : 24;
ULONGLONG Reserved3 : 28;
};
ULONGLONG AsULongLong;
} PDPTE_PAE, *PPDPTE_PAE;
struct PageDirectoryEntry {
unsigned P : 1;
unsigned RW : 1;
unsigned US : 1;
unsigned PWT : 1;
unsigned PCD : 1;
unsigned A : 1;
unsigned D : 1;
unsigned PS : 1;
unsigned G : 1;
unsigned AVL : 3;
unsigned BASE : 20;
};
typedef union _PDE_PAE {
struct {
ULONGLONG P : 1;
ULONGLONG RW : 1;
ULONGLONG US : 1;
ULONGLONG PWT : 1;
ULONGLONG PCD : 1;
ULONGLONG A : 1;
ULONGLONG D : 1;
ULONGLONG PS : 1;
ULONGLONG G : 1;
ULONGLONG AVL : 3;
ULONGLONG PageTableBase : 24;
ULONGLONG Reserved : 28;
};
ULONGLONG AsULongLong;
} PDE_PAE, *PPDE_PAE;
struct PageDirectoryEntry {
unsigned P : 1;
unsigned RW : 1;
unsigned US : 1;
unsigned PWT : 1;
unsigned PCD : 1;
unsigned A : 1;
unsigned D : 1;
unsigned PS : 1;
unsigned G : 1;
unsigned AVL : 3;
unsigned BASE : 20;
};
typedef union _PDE_PAE {
struct {
ULONGLONG P : 1;
ULONGLONG RW : 1;
ULONGLONG US : 1;
ULONGLONG PWT : 1;
ULONGLONG PCD : 1;
ULONGLONG A : 1;
ULONGLONG D : 1;
ULONGLONG PS : 1;
ULONGLONG G : 1;
ULONGLONG AVL : 3;
ULONGLONG PageTableBase : 24;
ULONGLONG Reserved : 28;
};
ULONGLONG AsULongLong;
} PDE_PAE, *PPDE_PAE;
struct PageTableEntry {
unsigned P : 1;
unsigned RW : 1;
unsigned US : 1;
unsigned PWT : 1;
unsigned PCD : 1;
unsigned A : 1;
unsigned D : 1;
unsigned PAT : 1;
unsigned G : 1;
unsigned AVL : 3;
unsigned BASE : 20;
};
struct PageTableEntry {
unsigned P : 1;
unsigned RW : 1;
unsigned US : 1;
unsigned PWT : 1;
unsigned PCD : 1;
unsigned A : 1;
unsigned D : 1;
unsigned PAT : 1;
unsigned G : 1;
unsigned AVL : 3;
unsigned BASE : 20;
};
普通情况举例
虚拟地址: |高20位:页号| 低12位:页内偏移 |
找到物理页 在页内的具体位置
1)看图先确认 低12bits是映射到某个物理页后在页内的偏移,虚拟地址和物理地址低12位是一致的
,那么重点在于如何通过查表找到物理页。
2)页表需要描述:地址+属性
已知物理地址32位,而一个页表项描述的是页首地址,后12bits一定为0,所以地址只需要存储高20位。
举例:
物理页首地址: 0x12345000 (后12位必为0)
页表项只需存储: 0x12345 (高20位)
节省空间: 32位 → 20位地址 + 12位属性
3)假设用1张表存储页表项
段大小: 4GB = 2^32 字节
页大小: 4KB = 2^12 字节
页数量: 4GB/4KB = 2^32/2^12 = 2^20 = 1M 页
页表项: 1M个 × 4字节 = 4MB
问题: 每个进程都需要4MB的页表,这样的开销在win32的512MB内存年代极不合理 所以设计了二级页表
4)二级页表
PDE (Page Directory Entry): 页目录索引,2^10 = 1024项
PTE (Page Table Entry): 页表索引,2^10 = 1024项
通过2级寻址 也可以描述2^20数量的页表项 大大节省空间
普通情况举例
虚拟地址: |高20位:页号| 低12位:页内偏移 |
找到物理页 在页内的具体位置
1)看图先确认 低12bits是映射到某个物理页后在页内的偏移,虚拟地址和物理地址低12位是一致的
,那么重点在于如何通过查表找到物理页。
2)页表需要描述:地址+属性
已知物理地址32位,而一个页表项描述的是页首地址,后12bits一定为0,所以地址只需要存储高20位。
举例:
物理页首地址: 0x12345000 (后12位必为0)
页表项只需存储: 0x12345 (高20位)
节省空间: 32位 → 20位地址 + 12位属性
3)假设用1张表存储页表项
段大小: 4GB = 2^32 字节
页大小: 4KB = 2^12 字节
页数量: 4GB/4KB = 2^32/2^12 = 2^20 = 1M 页
页表项: 1M个 × 4字节 = 4MB
问题: 每个进程都需要4MB的页表,这样的开销在win32的512MB内存年代极不合理 所以设计了二级页表
4)二级页表
PDE (Page Directory Entry): 页目录索引,2^10 = 1024项
PTE (Page Table Entry): 页表索引,2^10 = 1024项
通过2级寻址 也可以描述2^20数量的页表项 大大节省空间
EPROCESS {
KPROCESS Pcb;
};
KPROCESS {
ULONG DirectoryTableBase[2];
};
CR3_VALUE = EPROCESS->Pcb.DirectoryTableBase[0];
EPROCESS {
KPROCESS Pcb;
};
KPROCESS {
ULONG DirectoryTableBase[2];
};
CR3_VALUE = EPROCESS->Pcb.DirectoryTableBase[0];
用户态应用
↓ DeviceIoControl(CTL_ATTACH_MEM_READ/WRITE)
↓
┌─────────────────────────────────────────────────────────────┐
│ 内核驱动处理流程 │
│ │
│ 1. 获取目标进程对象 │
│ ├─ PsLookupProcessByProcessId(ProcessId) │
│ └─ 返回 EPROCESS 结构指针 用它拿进程页目录基址 │
│ │
│ 2. 准备进程切换 │
│ ├─ KeRaiseIrql(DISPATCH_LEVEL) 提升中断级别 避免数据拷贝的时候被切CR3│
│ └─ 准备 KAPC_STATE 结构 │
│ │
│ 3. 切换到目标进程地址空间 │
│ ├─ KeStackAttachProcess(Process, &ApcState) │
│ └─ 切换 CR3 寄存器到目标进程页目录 │
│ │
│ 4. 虚拟地址转物理地址 │
│ ├─ MmGetPhysicalAddress(VirtualAddress) │
│ └─ 使用目标进程的页表进行地址转换 │
│ │
│ 5. 物理内存映射 │
│ ├─ MmMapIoSpace(PhysicalAddress, Size, CacheType) │
│ └─ 将物理地址映射到当前内核虚拟地址空间 可以避免内存权限问题 │
│ │
│ 6. 执行读写操作 │
│ ├─ 读取: RtlCopyMemory(Buffer, MappedAddr, Size) │
│ └─ 写入: RtlCopyMemory(MappedAddr, Buffer, Size) │
│ │
│ 7. 清理和恢复 │
│ ├─ MmUnmapIoSpace(MappedAddr, Size) │
│ ├─ KeUnstackDetachProcess(&ApcState) │
│ ├─ KeLowerIrql(OldIrql) │
│ └─ ObDereferenceObject(Process) │
│ │
└─────────────────────────────────────────────────────────────┘
用户态应用
↓ DeviceIoControl(CTL_ATTACH_MEM_READ/WRITE)
↓
┌─────────────────────────────────────────────────────────────┐
│ 内核驱动处理流程 │
│ │
│ 1. 获取目标进程对象 │
│ ├─ PsLookupProcessByProcessId(ProcessId) │
│ └─ 返回 EPROCESS 结构指针 用它拿进程页目录基址 │
│ │
│ 2. 准备进程切换 │
│ ├─ KeRaiseIrql(DISPATCH_LEVEL) 提升中断级别 避免数据拷贝的时候被切CR3│
│ └─ 准备 KAPC_STATE 结构 │
│ │
│ 3. 切换到目标进程地址空间 │
│ ├─ KeStackAttachProcess(Process, &ApcState) │
│ └─ 切换 CR3 寄存器到目标进程页目录 │
│ │
│ 4. 虚拟地址转物理地址 │
│ ├─ MmGetPhysicalAddress(VirtualAddress) │
│ └─ 使用目标进程的页表进行地址转换 │
│ │
│ 5. 物理内存映射 │
│ ├─ MmMapIoSpace(PhysicalAddress, Size, CacheType) │
│ └─ 将物理地址映射到当前内核虚拟地址空间 可以避免内存权限问题 │
│ │
│ 6. 执行读写操作 │
│ ├─ 读取: RtlCopyMemory(Buffer, MappedAddr, Size) │
│ └─ 写入: RtlCopyMemory(MappedAddr, Buffer, Size) │
│ │
│ 7. 清理和恢复 │
│ ├─ MmUnmapIoSpace(MappedAddr, Size) │
│ ├─ KeUnstackDetachProcess(&ApcState) │
│ ├─ KeLowerIrql(OldIrql) │
│ └─ ObDereferenceObject(Process) │
│ │
└─────────────────────────────────────────────────────────────┘
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
ReadProcessMemory(hProcess, addr, buffer, size, NULL);
WriteProcessMemory(hProcess, addr, buffer, size, NULL);
VirtualProtectEx(hProcess, addr, size, PAGE_EXECUTE_READWRITE, &old);
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)addr, param, 0, NULL);
SetWindowsHookEx(WH_GETMESSAGE, HookProc, hMod, threadId);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
ReadProcessMemory(hProcess, addr, buffer, size, NULL);
WriteProcessMemory(hProcess, addr, buffer, size, NULL);
VirtualProtectEx(hProcess, addr, size, PAGE_EXECUTE_READWRITE, &old);
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)addr, param, 0, NULL);
SetWindowsHookEx(WH_GETMESSAGE, HookProc, hMod, threadId);
ZwReadVirtualMemory(hProcess, addr, buffer, size, NULL);
ZwWriteVirtualMemory(hProcess, addr, buffer, size, NULL);
MmCopyVirtualMemory(srcProcess, srcAddr, dstProcess, dstAddr, size,
KernelMode, &bytesRead);
KeStackAttachProcess(targetProcess, &apcState);
RtlCopyMemory(buffer, targetAddr, size);
KeUnstackDetachProcess(&apcState);
ZwReadVirtualMemory(hProcess, addr, buffer, size, NULL);
ZwWriteVirtualMemory(hProcess, addr, buffer, size, NULL);
MmCopyVirtualMemory(srcProcess, srcAddr, dstProcess, dstAddr, size,
KernelMode, &bytesRead);
KeStackAttachProcess(targetProcess, &apcState);
RtlCopyMemory(buffer, targetAddr, size);
KeUnstackDetachProcess(&apcState);
PHYSICAL_ADDRESS MyMmGetPhysicalAddress(PVOID VirtualAddress) {
ULONG_PTR cr3 = __readcr3();
}
PHYSICAL_ADDRESS MyMmGetPhysicalAddress(PVOID VirtualAddress) {
ULONG_PTR cr3 = __readcr3();
}
//R3的
//读写目标进程内存
typedef struct PROCESS_MEM_REQ {
HANDLE ProcessId; // 目标进程ID
PVOID VirtualAddress; // 虚拟地址
unsigned Size; // 数据大小
//写到systembuffer 不需要在结构体定义缓冲区
}*PPROCESS_MEM_REQ;
PVOID memBuffer_;
DWORD memBufferSize_;
DWORD memDataSize_;
BOOL EnsureBufferSize(DWORD requiredSize);//可以不调
BOOL AttachReadMem(DWORD ProcessId, ULONG VirtualAddress, DWORD Size); //附加读
BOOL AttachWriteMem(DWORD ProcessId, ULONG VirtualAddress, DWORD Size);//附加写
void ClearBuffer(); //清空内存读写的缓冲区
void ArkR3::ClearBuffer()
{
if (memBuffer_ && memBufferSize_ > 0) {
memset(memBuffer_, 0, memBufferSize_);
}
memDataSize_ = 0;
}
BOOL ArkR3::EnsureBufferSize(DWORD requiredSize) //AI写的
{
if (requiredSize > 0x100000) { // 限制最大1MB
Log("EnsureBufferSize: Size too large (%d bytes)", requiredSize);
return FALSE;
}
if (memBufferSize_ >= requiredSize) {
return TRUE; // 当前缓冲区已足够
}
// 计算新的缓冲区大小(向上取整到4KB边界)
DWORD newSize = ((requiredSize + 4095) / 4096) * 4096;
PVOID newBuffer = realloc(memBuffer_, newSize);
if (!newBuffer) {
Log("EnsureBufferSize: Failed to allocate %d bytes", newSize);
return FALSE;
}
memBuffer_ = newBuffer;
memBufferSize_ = newSize;
Log("EnsureBufferSize: Buffer resized to %d bytes", newSize);
return TRUE;
}
//读取
//R3 : [PROCESS_MEM_REQ] → R0 → R0 : [读取数据] → R3
//发送12字节请求 接收Size字节
BOOL ArkR3::AttachReadMem(DWORD ProcessId, ULONG VirtualAddress, DWORD Size)
{
// 参数验证
if (ProcessId == 0 || Size == 0) {
LogErr("AttachReadMem: Invalid args");
return FALSE;
}
if (!EnsureBufferSize(Size)) {
LogErr("AttachReadMem: Failed to ensure buffer size");
return FALSE;
}
PROCESS_MEM_REQ req;
req.ProcessId = (HANDLE)ProcessId;
req.VirtualAddress = (PVOID)VirtualAddress;
req.Size = Size;
DWORD dwRetBytes = 0;
BOOL bResult = DeviceIoControl(m_hDriver, CTL_ATTACH_MEM_READ,
&req, sizeof(PROCESS_MEM_REQ), // 输入:请求结构体
memBuffer_, Size, // 输出:直接写入内部缓冲区
&dwRetBytes, NULL);
if (bResult) {
memDataSize_ = Size;
Log("AttachReadMem: PID=%d, Addr=0x%08X, Size=%d - Success",
ProcessId, VirtualAddress, Size);
return TRUE;
}
else {
Log("AttachReadMem: DeviceIoControl failed, PID=%d, Addr=0x%08X, Size=%d",
ProcessId, VirtualAddress, Size);
return FALSE;
}
}
//写入:
//R3 : [PROCESS_MEM_REQ] [写入数据] → R0 → 处理完成
//发送12 + Size字节 不需要返回数据
BOOL ArkR3::AttachWriteMem(DWORD ProcessId, ULONG VirtualAddress, DWORD Size)
{
if (ProcessId == 0 || VirtualAddress == 0 || Size == 0) {
Log("AttachWriteMem: Invalid parameters");
return FALSE;
}
if (memDataSize_ < Size) {
Log("AttachWriteMem: Not enough data in buffer, available: %d, required: %d",
memDataSize_, Size);
return FALSE;
}
DWORD totalSize = sizeof(PROCESS_MEM_REQ) + Size;
PVOID pBuffer = malloc(totalSize);
if (!pBuffer) {
Log("AttachWriteMem: Failed to allocate buffer, size: %d", totalSize);
return FALSE;
}
PPROCESS_MEM_REQ req = (PPROCESS_MEM_REQ)pBuffer;
req->ProcessId = (HANDLE)ProcessId;
req->VirtualAddress = (PVOID)VirtualAddress;
req->Size = Size;
// 从内部缓冲区复制数据到请求缓冲区
memcpy((PUCHAR)pBuffer + sizeof(PROCESS_MEM_REQ), memBuffer_, Size);
DWORD dwRetBytes = 0;
BOOL bResult = DeviceIoControl(m_hDriver, CTL_ATTACH_MEM_WRITE,
pBuffer, totalSize,
NULL, 0,
&dwRetBytes, NULL);
if (bResult) {
Log("AttachWriteMem: PID=%d, Addr=0x%08X, Size=%d - Success",
ProcessId, VirtualAddress, Size);
} else {
Log("AttachWriteMem: PID=%d, Addr=0x%08X, Size=%d (dwRetBytes=%d)",
ProcessId, VirtualAddress, Size, dwRetBytes);
}
free(pBuffer);
return bResult;
}
//R3的
//读写目标进程内存
typedef struct PROCESS_MEM_REQ {
HANDLE ProcessId; // 目标进程ID
PVOID VirtualAddress; // 虚拟地址
unsigned Size; // 数据大小
//写到systembuffer 不需要在结构体定义缓冲区
}*PPROCESS_MEM_REQ;
PVOID memBuffer_;
DWORD memBufferSize_;
DWORD memDataSize_;
BOOL EnsureBufferSize(DWORD requiredSize);//可以不调
BOOL AttachReadMem(DWORD ProcessId, ULONG VirtualAddress, DWORD Size); //附加读
BOOL AttachWriteMem(DWORD ProcessId, ULONG VirtualAddress, DWORD Size);//附加写
void ClearBuffer(); //清空内存读写的缓冲区
void ArkR3::ClearBuffer()
{
if (memBuffer_ && memBufferSize_ > 0) {
memset(memBuffer_, 0, memBufferSize_);
}
memDataSize_ = 0;
}
BOOL ArkR3::EnsureBufferSize(DWORD requiredSize) //AI写的
{
if (requiredSize > 0x100000) { // 限制最大1MB
Log("EnsureBufferSize: Size too large (%d bytes)", requiredSize);
return FALSE;
}
if (memBufferSize_ >= requiredSize) {
return TRUE; // 当前缓冲区已足够
}
// 计算新的缓冲区大小(向上取整到4KB边界)
DWORD newSize = ((requiredSize + 4095) / 4096) * 4096;
PVOID newBuffer = realloc(memBuffer_, newSize);
if (!newBuffer) {
Log("EnsureBufferSize: Failed to allocate %d bytes", newSize);
return FALSE;
}
memBuffer_ = newBuffer;
memBufferSize_ = newSize;
Log("EnsureBufferSize: Buffer resized to %d bytes", newSize);
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-7-31 22:47
被X66iaM编辑
,原因: