首页
社区
课程
招聘
[原创]实现简易ARK工具(2) 切CR3进程读写
发表于: 2025-7-13 22:39 1791

[原创]实现简易ARK工具(2) 切CR3进程读写

2025-7-13 22:39
1791

本文主要讲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读写目标进程常常使用:

OpenProcessVirtualProtectExReadProcessMemory 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. 通过分页机制转换为物理地址
// 32位标准分页模式下的CR3
typedef union _CR3_32 {
    struct {
        ULONG Reserved1 : 3;    // 0-2: 保留位
        ULONG PWT : 1;          // 3: 页级写直达
        ULONG PCD : 1;          // 4: 页级缓存禁止 
        ULONG Reserved2 : 7;    // 5-11: 保留位
        ULONG PageDirectoryBase : 20; // 12-31: 页目录基址
    };
    ULONG AsULong;
} CR3_32, *PCR3_32;
 
// PAE模式下的CR3 
typedef union _CR3_PAE {
    struct {
        ULONG PWT : 1;          // 3: 页级写直达
        ULONG PCD : 1;          // 4: 页级缓存禁止
        ULONG Reserved : 3;     // 5-7: 保留位
        ULONG Reserved2 : 1;    // 8: 必须为0
        ULONG PDPT_Base : 23;   // 9-31: PDPT基址(物理地址高23位,低9位为0)
    };
    ULONG AsULong;
} CR3_PAE, *PCR3_PAE;
// 32位标准分页模式下的CR3
typedef union _CR3_32 {
    struct {
        ULONG Reserved1 : 3;    // 0-2: 保留位
        ULONG PWT : 1;          // 3: 页级写直达
        ULONG PCD : 1;          // 4: 页级缓存禁止 
        ULONG Reserved2 : 7;    // 5-11: 保留位
        ULONG PageDirectoryBase : 20; // 12-31: 页目录基址
    };
    ULONG AsULong;
} CR3_32, *PCR3_32;
 
// PAE模式下的CR3 
typedef union _CR3_PAE {
    struct {
        ULONG PWT : 1;          // 3: 页级写直达
        ULONG PCD : 1;          // 4: 页级缓存禁止
        ULONG Reserved : 3;     // 5-7: 保留位
        ULONG Reserved2 : 1;    // 8: 必须为0
        ULONG PDPT_Base : 23;   // 9-31: PDPT基址(物理地址高23位,低9位为0)
    };
    ULONG AsULong;
} CR3_PAE, *PCR3_PAE;
// PAE模式下的PDPTE结构
typedef union _PDPTE_PAE {
    struct {
        ULONGLONG P : 1;        // 0: 存在位
        ULONGLONG Reserved1 : 2; // 1-2: 保留位(必须为0)
        ULONGLONG PWT : 1;      // 3: 页级写直达
        ULONGLONG PCD : 1;      // 4: 页级缓存禁止
        ULONGLONG Reserved2 : 4; // 5-8: 保留位(必须为0)
        ULONGLONG AVL : 3;      // 9-11: 软件可用位
        ULONGLONG PageDirBase : 24; // 12-35: 页目录基址(物理地址)
        ULONGLONG Reserved3 : 28; // 36-63: 保留位(必须为0)
    };
    ULONGLONG AsULongLong;
} PDPTE_PAE, *PPDPTE_PAE;
// PAE模式下的PDPTE结构
typedef union _PDPTE_PAE {
    struct {
        ULONGLONG P : 1;        // 0: 存在位
        ULONGLONG Reserved1 : 2; // 1-2: 保留位(必须为0)
        ULONGLONG PWT : 1;      // 3: 页级写直达
        ULONGLONG PCD : 1;      // 4: 页级缓存禁止
        ULONGLONG Reserved2 : 4; // 5-8: 保留位(必须为0)
        ULONGLONG AVL : 3;      // 9-11: 软件可用位
        ULONGLONG PageDirBase : 24; // 12-35: 页目录基址(物理地址)
        ULONGLONG Reserved3 : 28; // 36-63: 保留位(必须为0)
    };
    ULONGLONG AsULongLong;
} PDPTE_PAE, *PPDPTE_PAE;
//常规模式下
struct PageDirectoryEntry {
    unsigned P : 1;      // 0: 存在位 (Present)
    unsigned RW : 1;     // 1: 读写位 (0=只读, 1=读写)
    unsigned US : 1;     // 2: 用户/内核位 (0=内核, 1=用户)
    unsigned PWT : 1;    // 3: 写缓存 (Page Write Through)  指TLB 偷懒不写了
    unsigned PCD : 1;    // 4: 页级缓存禁止 (Page Cache Disable)
    unsigned A : 1;      // 5: 访问位 (Accessed)
    unsigned D : 1;      // 6: 脏位 (Dirty) - 仅对页表项有效
    unsigned PS : 1;     // 7: 页大小 (Page Size) - 仅对页目录项有效
    unsigned G : 1;      // 8: 全局位 (Global)
    unsigned AVL : 3;    // 9-11: 软件可用位 (Available)
    unsigned BASE : 20;  // 12-31: 物理页首地址
};
 
// PAE模式下的PDE结构(64位)
typedef union _PDE_PAE {
    struct {
        ULONGLONG P : 1;        // 0: 存在位
        ULONGLONG RW : 1;       // 1: 读写位
        ULONGLONG US : 1;       // 2: 用户/内核位
        ULONGLONG PWT : 1;      // 3: 页级写直达
        ULONGLONG PCD : 1;      // 4: 页级缓存禁止
        ULONGLONG A : 1;        // 5: 访问位
        ULONGLONG D : 1;        // 6: 脏位
        ULONGLONG PS : 1;       // 7: 页大小(0=4KB, 1=2MB)
        ULONGLONG G : 1;        // 8: 全局位
        ULONGLONG AVL : 3;      // 9-11: 软件可用位
        ULONGLONG PageTableBase : 24; // 12-35: 页表基址或物理页基址
        ULONGLONG Reserved : 28; // 36-63: 保留位(必须为0)
    };
    ULONGLONG AsULongLong;
} PDE_PAE, *PPDE_PAE;
//常规模式下
struct PageDirectoryEntry {
    unsigned P : 1;      // 0: 存在位 (Present)
    unsigned RW : 1;     // 1: 读写位 (0=只读, 1=读写)
    unsigned US : 1;     // 2: 用户/内核位 (0=内核, 1=用户)
    unsigned PWT : 1;    // 3: 写缓存 (Page Write Through)  指TLB 偷懒不写了
    unsigned PCD : 1;    // 4: 页级缓存禁止 (Page Cache Disable)
    unsigned A : 1;      // 5: 访问位 (Accessed)
    unsigned D : 1;      // 6: 脏位 (Dirty) - 仅对页表项有效
    unsigned PS : 1;     // 7: 页大小 (Page Size) - 仅对页目录项有效
    unsigned G : 1;      // 8: 全局位 (Global)
    unsigned AVL : 3;    // 9-11: 软件可用位 (Available)
    unsigned BASE : 20;  // 12-31: 物理页首地址
};
 
// PAE模式下的PDE结构(64位)
typedef union _PDE_PAE {
    struct {
        ULONGLONG P : 1;        // 0: 存在位
        ULONGLONG RW : 1;       // 1: 读写位
        ULONGLONG US : 1;       // 2: 用户/内核位
        ULONGLONG PWT : 1;      // 3: 页级写直达
        ULONGLONG PCD : 1;      // 4: 页级缓存禁止
        ULONGLONG A : 1;        // 5: 访问位
        ULONGLONG D : 1;        // 6: 脏位
        ULONGLONG PS : 1;       // 7: 页大小(0=4KB, 1=2MB)
        ULONGLONG G : 1;        // 8: 全局位
        ULONGLONG AVL : 3;      // 9-11: 软件可用位
        ULONGLONG PageTableBase : 24; // 12-35: 页表基址或物理页基址
        ULONGLONG Reserved : 28; // 36-63: 保留位(必须为0)
    };
    ULONGLONG AsULongLong;
} PDE_PAE, *PPDE_PAE;
// 页表项结构相同,但第7位含义不同
struct PageTableEntry {
    unsigned P : 1;      // 0: 存在位
    unsigned RW : 1;     // 1: 读写位
    unsigned US : 1;     // 2: 用户/内核位
    unsigned PWT : 1;    // 3: 写缓存
    unsigned PCD : 1;    // 4: 页级缓存禁止
    unsigned A : 1;      // 5: 访问位
    unsigned D : 1;      // 6: 脏位 (数据已修改)
    unsigned PAT : 1;    // 7: 页属性表索引 (Page Attribute Table)
    unsigned G : 1;      // 8: 全局位
    unsigned AVL : 3;    // 9-11: 软件可用位
    unsigned BASE : 20;  // 12-31: 物理页首地址
};
// 页表项结构相同,但第7位含义不同
struct PageTableEntry {
    unsigned P : 1;      // 0: 存在位
    unsigned RW : 1;     // 1: 读写位
    unsigned US : 1;     // 2: 用户/内核位
    unsigned PWT : 1;    // 3: 写缓存
    unsigned PCD : 1;    // 4: 页级缓存禁止
    unsigned A : 1;      // 5: 访问位
    unsigned D : 1;      // 6: 脏位 (数据已修改)
    unsigned PAT : 1;    // 7: 页属性表索引 (Page Attribute Table)
    unsigned G : 1;      // 8: 全局位
    unsigned AVL : 3;    // 9-11: 软件可用位
    unsigned BASE : 20;  // 12-31: 物理页首地址
};
普通情况举例
虚拟地址: |高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;           // 内核进程控制块(偏移0x0)
    // ...
};
 
KPROCESS {
    ULONG DirectoryTableBase[2];  // 页目录表基址(CR3值)
    // DirectoryTableBase[0]: 用户态页目录基址
    // DirectoryTableBase[1]: 内核态页目录基址(PAE模式)
    // ...
};
 
// 获取页目录基址:
CR3_VALUE = EPROCESS->Pcb.DirectoryTableBase[0];
// 简化的结构体关系
EPROCESS {
    KPROCESS Pcb;           // 内核进程控制块(偏移0x0)
    // ...
};
 
KPROCESS {
    ULONG DirectoryTableBase[2];  // 页目录表基址(CR3值)
    // DirectoryTableBase[0]: 用户态页目录基址
    // DirectoryTableBase[1]: 内核态页目录基址(PAE模式)
    // ...
};
 
// 获取页目录基址:
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)                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
// 1. 标准API方式
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
ReadProcessMemory(hProcess, addr, buffer, size, NULL);
WriteProcessMemory(hProcess, addr, buffer, size, NULL);
 
// 2. 修改内存保护
VirtualProtectEx(hProcess, addr, size, PAGE_EXECUTE_READWRITE, &old);
 
// 3. 远程线程注入
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)addr, param, 0, NULL);
 
// 4. DLL注入
SetWindowsHookEx(WH_GETMESSAGE, HookProc, hMod, threadId);
// 1. 标准API方式
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
ReadProcessMemory(hProcess, addr, buffer, size, NULL);
WriteProcessMemory(hProcess, addr, buffer, size, NULL);
 
// 2. 修改内存保护
VirtualProtectEx(hProcess, addr, size, PAGE_EXECUTE_READWRITE, &old);
 
// 3. 远程线程注入
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)addr, param, 0, NULL);
 
// 4. DLL注入
SetWindowsHookEx(WH_GETMESSAGE, HookProc, hMod, threadId);
// 1. 内核标准API
ZwReadVirtualMemory(hProcess, addr, buffer, size, NULL);
ZwWriteVirtualMemory(hProcess, addr, buffer, size, NULL);
 
// 2. 内核内存复制
MmCopyVirtualMemory(srcProcess, srcAddr, dstProcess, dstAddr, size,
                   KernelMode, &bytesRead);
 
// 3. 进程attach方式
KeStackAttachProcess(targetProcess, &apcState);
// 直接内存访问
RtlCopyMemory(buffer, targetAddr, size);
KeUnstackDetachProcess(&apcState);
// 1. 内核标准API
ZwReadVirtualMemory(hProcess, addr, buffer, size, NULL);
ZwWriteVirtualMemory(hProcess, addr, buffer, size, NULL);
 
// 2. 内核内存复制
MmCopyVirtualMemory(srcProcess, srcAddr, dstProcess, dstAddr, size,
                   KernelMode, &bytesRead);
 
// 3. 进程attach方式
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编辑 ,原因:
收藏
免费 24
支持
分享
最新回复 (10)
雪    币: 961
活跃值: (1826)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2

分页体系好庞大,讲的废话有点多了,对照intel卷三辩证的看 

最后于 2025-7-14 04:34 被X66iaM编辑 ,原因:
上传的附件:
2025-7-13 23:00
0
雪    币: 0
活跃值: (728)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2025-7-14 21:41
0
雪    币: 25
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2025-7-15 17:20
0
雪    币: 10
活跃值: (1304)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2025-7-16 16:49
0
雪    币: 414
活跃值: (227)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
谢谢分享
2025-7-23 13:42
0
雪    币: 8
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
学习学习 正好要用的
2025-8-22 11:59
0
雪    币: 201
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
66666666
2025-9-4 21:07
0
雪    币: 2653
活跃值: (7246)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
学习 谢谢分享
2025-9-19 07:49
0
雪    币: 528
活跃值: (1629)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
666666666
2025-10-26 11:15
0
雪    币: 205
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
谢谢分享
2天前
0
游客
登录 | 注册 方可回帖
返回