找到 wrapper.node 中签名函数的:
- 函数名称(如果有导出名称)
- 函数地址
- 偏移值(地址 - 模块基址)
1. 打开 x64dbg (x64)
2. 文件 → 打开
3. 选择: C:\Program Files\Tencent\QQ\resources\app.asar.unpacked\node_modules\wrapper.node
方式 A: 菜单方式
- 符号 → 导出表
- 或按快捷键 Ctrl+E
方式 B: 符号窗口
- 点击底部的 "符号" 标签
- 找到 wrapper.node 模块
- 展开查看导出函数
在导出表中,你会看到类似这样的列表:
序号 地址 名称 模块
---- ------------ --------------------- ----------
1 180001000 node_register_module wrapper
2 180002000 napi_register_module wrapper
3 180A9CE90 ?可疑的签名函数? wrapper
...
寻找目标函数的线索:
-
函数名包含关键字
- sign, Sign, SIGN
- encrypt, Encrypt
- hash, Hash
- token, Token
- crypto, Crypto
-
地址在已知偏移附近
- 如果地址是
180A9CE90,模块基址是180000000 - 偏移 =
A9CE90,这正好是我们要找的!
- 如果地址是
-
C++ mangled name
- 以
?或@开头 - 例如:
?sign@Wrapper@@QEAAHPEBD...
- 以
-
序号导出
- 只有序号,没有名称
- 需要逐个检查
双击可疑的函数名,跳转到该地址,检查:
正确的函数特征:
; 函数序言
wrapper+A9CE90:
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
; 第5个参数 result 在栈上 [rsp+...]函数体特征:
- 大量的位运算(and, or, xor, shl, shr)
- 循环结构(loop, jne, jmp)
- 内存操作(movzx, movsx)
- 可能有加密算法特征(MD5, SHA 的魔数常量)
安装了 Visual Studio 或 Visual Studio Build Tools
-
打开 VS 开发者命令提示符
- 开始菜单 → Visual Studio → Developer Command Prompt
-
运行 dumpbin
cd "C:\Program Files\Tencent\QQ\resources\app.asar.unpacked\node_modules" dumpbin /EXPORTS wrapper.node
-
查看输出
Microsoft (R) COFF/PE Dumper Version ... File Type: DLL Section contains the following exports for wrapper.node ordinal hint RVA name 1 0 00001000 node_register_module 2 1 00A9CE90 ?SignFunction... -
记录信息
- RVA (相对虚拟地址) = 偏移值
- 例如:
00A9CE90就是偏移值0xA9CE90
pip install pefile创建 find_exports.py:
import pefile
import sys
# wrapper.node 路径
dll_path = r"C:\Program Files\Tencent\QQ\resources\app.asar.unpacked\node_modules\wrapper.node"
try:
pe = pefile.PE(dll_path)
print("=" * 60)
print("wrapper.node 导出函数列表")
print("=" * 60)
print()
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
print(f"{'序号':<6} {'RVA':<12} {'名称'}")
print("-" * 60)
for export in pe.DIRECTORY_ENTRY_EXPORT.symbols:
ordinal = export.ordinal
address = hex(export.address) if export.address else "N/A"
name = export.name.decode() if export.name else f"Ordinal_{ordinal}"
print(f"{ordinal:<6} {address:<12} {name}")
# 高亮可疑函数
if export.name:
name_lower = export.name.decode().lower()
if any(keyword in name_lower for keyword in ['sign', 'encrypt', 'hash', 'token', 'crypto']):
print(f" ^^^ 可疑函数!可能是签名函数")
else:
print("未找到导出函数")
print()
print("=" * 60)
except Exception as e:
print(f"错误: {e}")
sys.exit(1)运行:
python find_exports.py1. 打开 IDA Pro (64-bit)
2. File → Open
3. 选择 wrapper.node
4. 等待自动分析完成
1. View → Open subviews → Exports
2. 或按 Shift+F7
3. 查看导出函数列表
1. 双击函数名跳转到函数
2. 按 F5 查看反编译代码
3. 分析函数参数和逻辑
反编译代码示例:
__int64 __fastcall sign_function(
const char *cmd,
const unsigned char *src,
unsigned __int64 src_len,
int seq,
unsigned char *result)
{
// 签名处理逻辑
// ...
return 0;
}例如找到:doSign 或 createSignature
操作:
- 记录函数名和偏移值
- 更新项目代码,添加函数名注释
- 测试该偏移值是否正确
例如:Ordinal_15(没有函数名)
操作:
- 逐个检查序号导出的函数
- 根据函数特征判断(5个参数、复杂逻辑)
- 记录序号和偏移值
例如:?sign@WrapperClass@@QEAAHPEBD@Z
操作:
- 使用 demangler 解码
- 在线工具:https://demangler.com/
- 命令行:
undname "?sign@WrapperClass@@QEAAHPEBD@Z"
- 理解函数的真实名称和参数
- 记录原始 mangled name 和偏移值
可能原因:
- 函数没有导出(内部函数)
- 使用了模糊命名
- 函数通过其他方式调用
解决方案:
- 使用动态调试附加到 QQ 进程
- 设置内存断点捕获签名调用
- 分析调用栈找到函数地址
找到函数后,请记录以下信息:
=== 签名函数信息 ===
QQ 版本: 9.9.20-37051
模块名称: wrapper.node
模块基址: 0x180000000 (示例)
函数名称: [在此填写函数名]
函数地址: 0x180A9CE90 (示例)
偏移值: 0xA9CE90
函数特征:
- 参数数量: 5
- 返回类型: int
- 函数大小: 约 XXX 字节
验证状态:
[ ] 已验证函数序言正确
[ ] 已验证参数使用
[ ] 已在配置文件中测试
[ ] 签名功能正常工作
找到函数信息后:
-
更新 sign.json 配置
{ "version": "9.9.20-37051", "offset": "0xA9CE90" } -
编译并测试
cmake --build --preset=msvc-release start.bat
-
验证服务
http://localhost:8080/ping -
分享给社区
- 在 GitHub Issues 中分享你的发现
- 帮助其他使用相同 QQ 版本的用户