Skip to content

Latest commit

 

History

History
376 lines (273 loc) · 10.2 KB

File metadata and controls

376 lines (273 loc) · 10.2 KB

x64dbg 查找模块基址和内存地址 - 实战指南

🎯 目标

找到 wrapper.node 中签名函数的:

  1. 模块基址 (Module Base Address)
  2. 内存地址 (Memory Address)

📝 准备工作

工具

  • x64dbg (64位版本)
  • Windows 计算器(程序员模式)

文件路径

9.9.12-25493: C:\Users\admin\Documents\qqnt\9912\resources\app\versions\9.9.12-25493\wrapper.node
9.9.20-37051: C:\Program Files\Tencent\QQNT\versions\9.9.20-37051\resources\app\wrapper.node

已知偏移值

9.9.12-25493: 0xA996E0
9.9.20-37051: 0xA9CE90 (待验证)

方法一:启动时自动显示 ⭐ 最简单

Step 1: 打开 wrapper.node

1. 启动 x64dbg (x64)
2. 文件 → 打开
3. 浏览到 wrapper.node 并打开

Step 2: 查看顶部状态栏

加载完成后,立即查看窗口顶部:

看这里 ↓

┌─────────────────────────────────────────────────┐
│ wrapper - 基址: 180000000   [运行] [暂停] ...  │  ← 模块基址在这里!
├─────────────────────────────────────────────────┤
│ 地址          │ 十六进制                        │
│ 180000000     │ 4D 5A 90 00 ...                │
└─────────────────────────────────────────────────┘

记录: 模块基址 = 180000000

Step 3: 计算内存地址

使用 Windows 计算器:

模块基址:    180000000
偏移地址:  +     A996E0
──────────────────────
内存地址:    180A996E0

方法二:内存映射窗口 ⭐ 最准确

Step 1: 打开内存映射

方式 A: 菜单
  调试 → 内存映射

方式 B: 快捷键
  Alt+M

Step 2: 找到 wrapper.node

在内存映射表中找到 wrapper 模块:

┌──────────────────────────────────────────────────────────┐
│ 基址         │ 大小    │ 权限 │ 模块     │ 路径           │
├──────────────────────────────────────────────────────────┤
│ 180000000 ⭐ │ 1000    │ ER-- │ wrapper  │ C:\...\wrap... │
│ 180001000    │ 2000    │ -R-- │ wrapper  │ C:\...\wrap... │
│ 180003000    │ F000    │ -RW- │ wrapper  │ C:\...\wrap... │
│ ...          │ ...     │ ...  │ ...      │ ...            │
└──────────────────────────────────────────────────────────┘
                ↑
        这就是模块基址!

记录: 模块基址 = 180000000

Step 3: 右键查看详细信息

在 wrapper 行上右键 → 属性/详细信息

会显示:

模块名称: wrapper.node
基址: 180000000
大小: XXXXX
路径: C:\...\wrapper.node

方法三:符号窗口

Step 1: 打开符号窗口

方式 A: 底部标签
  点击 "符号" 标签

方式 B: 菜单
  查看 → 符号

Step 2: 展开 wrapper.node

符号窗口显示:
├─ 📦 kernel32.dll (基址: 7FF900000000)
├─ 📦 ntdll.dll (基址: 7FFA00000000)
├─ 📦 wrapper.node (基址: 180000000) ⭐ 看这里
│  ├─ 导出函数
│  │  ├─ 函数1 (RVA: 00001000)
│  │  ├─ 函数2 (RVA: 00A996E0) ⭐ 可能是签名函数
│  │  └─ ...
│  └─ ...
└─ ...

记录:

  • 模块基址 = 180000000
  • 如果找到函数的 RVA = A996E0

🎯 查找特定函数的内存地址

方法 A: 使用跳转功能(最快)

  1. Ctrl+G 打开跳转对话框

  2. 输入以下任一格式:

    格式 1: 模块名+偏移

    wrapper+A996E0
    

    格式 2: 完整内存地址

    180A996E0
    

    ⚠️ 注意:不要加 0x 前缀!

  3. 按回车跳转

  4. 查看当前地址

    跳转后,CPU 窗口顶部显示:

    ┌─────────────────────────────────────────────────┐
    │ 180A996E0  ← 这就是内存地址                      │
    ├─────────────────────────────────────────────────┤
    │ 180A996E0 │ 48 89 5C 24 08  mov [rsp+8],rbx    │
    │ 180A996E5 │ 48 89 6C 24 10  mov [rsp+10],rbp   │
    │ ...                                             │
    └─────────────────────────────────────────────────┘
    

方法 B: 查看导出表

  1. Ctrl+E 打开导出表

  2. 查看列表

    ┌───────────────────────────────────────────────────┐
    │ 序号 │ RVA      │ 地址      │ 名称               │
    ├───────────────────────────────────────────────────┤
    │ 1    │ 00001000 │ 180001000 │ node_module_init  │
    │ 2    │ 00A996E0 │ 180A996E0 │ ??? ⭐            │
    │ ...  │ ...      │ ...       │ ...               │
    └───────────────────────────────────────────────────┘
            ↑          ↑
        偏移地址   内存地址
    
  3. 双击函数 可直接跳转到该内存地址

方法 C: 在反汇编窗口中查看

  1. 跳转到函数位置

  2. 查看地址栏

    左侧列显示的就是内存地址:

    地址        │ 十六进制      │ 反汇编
    ────────────┼───────────────┼────────────────
    180A996E0 ⭐│ 48 89 5C ... │ mov [rsp+8],rbx
    180A996E5   │ 48 89 6C ... │ mov [rsp+10],rbp
    

🧮 地址计算实战

示例 1: 已知偏移,求内存地址

已知:

  • 模块基址:180000000
  • 偏移地址:A996E0

计算:

使用 Windows 计算器(程序员模式,HEX):

步骤:
1. 输入:180000000
2. 点击 +
3. 输入:A996E0
4. 点击 =
5. 结果:180A996E0  ← 内存地址

验证: 在 x64dbg 中按 Ctrl+G,输入 wrapper+A996E0,应该跳转到 180A996E0

示例 2: 已知内存地址,求偏移

已知:

  • 内存地址:180A996E0
  • 模块基址:180000000

计算:

使用 Windows 计算器(程序员模式,HEX):

步骤:
1. 输入:180A996E0
2. 点击 -
3. 输入:180000000
4. 点击 =
5. 结果:A996E0  ← 偏移地址

用途: 这个偏移值就是我们要存储在代码中的!


🔍 验证函数是否正确

跳转到地址后,检查代码特征

正确的签名函数应该有:

; 函数序言
wrapper+A996E0:
48 89 5C 24 08    mov qword ptr [rsp+8], rbx     ; 保存寄存器
48 89 6C 24 10    mov qword ptr [rsp+10], rbp
48 89 74 24 18    mov qword ptr [rsp+18], rsi
57                push rdi
48 83 EC 50       sub rsp, 50                     ; 分配栈空间

; 参数处理
48 8B F1          mov rsi, rcx                    ; 第1个参数 (cmd)
48 8B EA          mov rbp, rdx                    ; 第2个参数 (src)
4C 8B F0          mov r14, r8                     ; 第3个参数 (src_len)
44 8B F9          mov r15d, r9d                   ; 第4个参数 (seq)

特征检查清单:

  • ✅ 有标准的函数序言(push/mov/sub rsp)
  • ✅ 使用了 5 个参数(rcx, rdx, r8, r9, 栈参数)
  • ✅ 函数体较大(通常 > 200 字节)
  • ✅ 包含循环和位运算
  • ✅ 有函数结尾(ret 指令)

📊 快速参考表

需求 工具/方法 位置
查看模块基址 x64dbg 顶部状态栏 加载后自动显示
查看模块基址 内存映射窗口 Alt+M → 找 wrapper
查看内存地址 跳转功能 Ctrl+G → wrapper+偏移
查看导出函数 导出表 Ctrl+E
计算地址 Windows 计算器 程序员模式 + HEX

💡 常见问题

Q: 为什么我的模块基址和你的不一样?

A: 这很正常!模块基址每次加载都可能不同(ASLR)。

你的可能是: 180000000
我的可能是: 7FF800000000
别人的:    1A0000000

重要: 偏移地址才是固定的!

Q: 输入地址时要不要加 0x?

A: 在 x64dbg 中 不要加 0x

❌ 错误:0x180A996E0
✅ 正确:180A996E0
✅ 正确:wrapper+A996E0

Q: 如何知道跳转后的地址是否正确?

A: 查看汇编代码:

  1. 应该看到有意义的指令(mov, push, call 等)
  2. 不应该是全 00 或 CC(int3)
  3. 应该有函数序言和结尾

🎯 实战任务清单

完成以下任务来练习:

任务 1: 找到 9.9.12-25493 的模块基址

  • 用 x64dbg 打开 wrapper.node
  • 记录模块基址:__________
  • 验证:应该是 8 位或 12 位十六进制数

任务 2: 计算签名函数的内存地址

  • 已知偏移:0xA996E0
  • 模块基址:__________ (任务1的结果)
  • 计算内存地址:__________

任务 3: 跳转并验证

  • 按 Ctrl+G
  • 输入:wrapper+A996E0
  • 验证:看到函数代码而非乱码

任务 4: 对比 9.9.20 版本

  • 打开 9.9.20 的 wrapper.node
  • 记录模块基址:__________
  • 跳转到 wrapper+A9CE90
  • 检查是否是有效的函数代码

🚀 下一步

找到地址后:

  1. 记录偏移值 到 sign.cpp 的 addrMap
  2. 创建配置文件 sign.json
  3. 编译项目 生成 DLL
  4. 运行测试 验证偏移值是否正确

祝你成功喵~ 🐾