找到 wrapper.node 中签名函数的:
- 模块基址 (Module Base Address)
- 内存地址 (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 (待验证)
1. 启动 x64dbg (x64)
2. 文件 → 打开
3. 浏览到 wrapper.node 并打开
加载完成后,立即查看窗口顶部:
看这里 ↓
┌─────────────────────────────────────────────────┐
│ wrapper - 基址: 180000000 [运行] [暂停] ... │ ← 模块基址在这里!
├─────────────────────────────────────────────────┤
│ 地址 │ 十六进制 │
│ 180000000 │ 4D 5A 90 00 ... │
└─────────────────────────────────────────────────┘
记录: 模块基址 = 180000000
使用 Windows 计算器:
模块基址: 180000000
偏移地址: + A996E0
──────────────────────
内存地址: 180A996E0
方式 A: 菜单
调试 → 内存映射
方式 B: 快捷键
Alt+M
在内存映射表中找到 wrapper 模块:
┌──────────────────────────────────────────────────────────┐
│ 基址 │ 大小 │ 权限 │ 模块 │ 路径 │
├──────────────────────────────────────────────────────────┤
│ 180000000 ⭐ │ 1000 │ ER-- │ wrapper │ C:\...\wrap... │
│ 180001000 │ 2000 │ -R-- │ wrapper │ C:\...\wrap... │
│ 180003000 │ F000 │ -RW- │ wrapper │ C:\...\wrap... │
│ ... │ ... │ ... │ ... │ ... │
└──────────────────────────────────────────────────────────┘
↑
这就是模块基址!
记录: 模块基址 = 180000000
在 wrapper 行上右键 → 属性/详细信息
会显示:
模块名称: wrapper.node
基址: 180000000
大小: XXXXX
路径: C:\...\wrapper.node
方式 A: 底部标签
点击 "符号" 标签
方式 B: 菜单
查看 → 符号
符号窗口显示:
├─ 📦 kernel32.dll (基址: 7FF900000000)
├─ 📦 ntdll.dll (基址: 7FFA00000000)
├─ 📦 wrapper.node (基址: 180000000) ⭐ 看这里
│ ├─ 导出函数
│ │ ├─ 函数1 (RVA: 00001000)
│ │ ├─ 函数2 (RVA: 00A996E0) ⭐ 可能是签名函数
│ │ └─ ...
│ └─ ...
└─ ...
记录:
- 模块基址 =
180000000 - 如果找到函数的 RVA =
A996E0
-
按
Ctrl+G打开跳转对话框 -
输入以下任一格式:
格式 1: 模块名+偏移
wrapper+A996E0格式 2: 完整内存地址
180A996E0⚠️ 注意:不要加 0x 前缀! -
按回车跳转
-
查看当前地址
跳转后,CPU 窗口顶部显示:
┌─────────────────────────────────────────────────┐ │ 180A996E0 ← 这就是内存地址 │ ├─────────────────────────────────────────────────┤ │ 180A996E0 │ 48 89 5C 24 08 mov [rsp+8],rbx │ │ 180A996E5 │ 48 89 6C 24 10 mov [rsp+10],rbp │ │ ... │ └─────────────────────────────────────────────────┘
-
按
Ctrl+E打开导出表 -
查看列表
┌───────────────────────────────────────────────────┐ │ 序号 │ RVA │ 地址 │ 名称 │ ├───────────────────────────────────────────────────┤ │ 1 │ 00001000 │ 180001000 │ node_module_init │ │ 2 │ 00A996E0 │ 180A996E0 │ ??? ⭐ │ │ ... │ ... │ ... │ ... │ └───────────────────────────────────────────────────┘ ↑ ↑ 偏移地址 内存地址 -
双击函数 可直接跳转到该内存地址
-
跳转到函数位置
-
查看地址栏
左侧列显示的就是内存地址:
地址 │ 十六进制 │ 反汇编 ────────────┼───────────────┼──────────────── 180A996E0 ⭐│ 48 89 5C ... │ mov [rsp+8],rbx 180A996E5 │ 48 89 6C ... │ mov [rsp+10],rbp
已知:
- 模块基址:
180000000 - 偏移地址:
A996E0
计算:
使用 Windows 计算器(程序员模式,HEX):
步骤:
1. 输入:180000000
2. 点击 +
3. 输入:A996E0
4. 点击 =
5. 结果:180A996E0 ← 内存地址
验证:
在 x64dbg 中按 Ctrl+G,输入 wrapper+A996E0,应该跳转到 180A996E0
已知:
- 内存地址:
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 |
A: 这很正常!模块基址每次加载都可能不同(ASLR)。
你的可能是: 180000000
我的可能是: 7FF800000000
别人的: 1A0000000
重要: 偏移地址才是固定的!
A: 在 x64dbg 中 不要加 0x!
❌ 错误:0x180A996E0
✅ 正确:180A996E0
✅ 正确:wrapper+A996E0
A: 查看汇编代码:
- 应该看到有意义的指令(mov, push, call 等)
- 不应该是全 00 或 CC(int3)
- 应该有函数序言和结尾
完成以下任务来练习:
- 用 x64dbg 打开 wrapper.node
- 记录模块基址:__________
- 验证:应该是 8 位或 12 位十六进制数
- 已知偏移:0xA996E0
- 模块基址:__________ (任务1的结果)
- 计算内存地址:__________
- 按 Ctrl+G
- 输入:wrapper+A996E0
- 验证:看到函数代码而非乱码
- 打开 9.9.20 的 wrapper.node
- 记录模块基址:__________
- 跳转到 wrapper+A9CE90
- 检查是否是有效的函数代码
找到地址后:
- 记录偏移值 到 sign.cpp 的 addrMap
- 创建配置文件 sign.json
- 编译项目 生成 DLL
- 运行测试 验证偏移值是否正确
祝你成功喵~ 🐾