@bingine/cemem 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/PORT-SCOPE.md +169 -0
  2. package/README.md +451 -2
  3. package/dist/cjs/addr/expression.js +650 -0
  4. package/dist/cjs/addr/expression.js.map +1 -0
  5. package/dist/cjs/addresslist/memory-record.js +612 -0
  6. package/dist/cjs/addresslist/memory-record.js.map +1 -0
  7. package/dist/cjs/asm/assembler.js +354 -0
  8. package/dist/cjs/asm/assembler.js.map +1 -0
  9. package/dist/cjs/asm/autoassembler-directives.js +362 -0
  10. package/dist/cjs/asm/autoassembler-directives.js.map +1 -0
  11. package/dist/cjs/asm/autoassembler.js +2163 -0
  12. package/dist/cjs/asm/autoassembler.js.map +1 -0
  13. package/dist/cjs/asm/backend.js +394 -0
  14. package/dist/cjs/asm/backend.js.map +1 -0
  15. package/dist/cjs/asm/disassembler.js +715 -0
  16. package/dist/cjs/asm/disassembler.js.map +1 -0
  17. package/dist/cjs/backend/dbk-backend.js +683 -0
  18. package/dist/cjs/backend/dbk-backend.js.map +1 -0
  19. package/dist/cjs/backend/native-ce-backend.js +667 -0
  20. package/dist/cjs/backend/native-ce-backend.js.map +1 -0
  21. package/dist/cjs/backend/self-backend.js +449 -0
  22. package/dist/cjs/backend/self-backend.js.map +1 -0
  23. package/dist/cjs/backend/types.js +48 -0
  24. package/dist/cjs/backend/types.js.map +1 -0
  25. package/dist/cjs/cheattable/ct-format.js +533 -0
  26. package/dist/cjs/cheattable/ct-format.js.map +1 -0
  27. package/dist/cjs/index.js +219 -0
  28. package/dist/cjs/index.js.map +1 -0
  29. package/dist/cjs/lua/aa.js +230 -0
  30. package/dist/cjs/lua/aa.js.map +1 -0
  31. package/dist/cjs/lua/addressing.js +118 -0
  32. package/dist/cjs/lua/addressing.js.map +1 -0
  33. package/dist/cjs/lua/api.js +736 -0
  34. package/dist/cjs/lua/api.js.map +1 -0
  35. package/dist/cjs/lua/asm.js +220 -0
  36. package/dist/cjs/lua/asm.js.map +1 -0
  37. package/dist/cjs/lua/bridge.js +526 -0
  38. package/dist/cjs/lua/bridge.js.map +1 -0
  39. package/dist/cjs/lua/marshal.js +562 -0
  40. package/dist/cjs/lua/marshal.js.map +1 -0
  41. package/dist/cjs/lua/memory.js +213 -0
  42. package/dist/cjs/lua/memory.js.map +1 -0
  43. package/dist/cjs/lua/objects.js +1070 -0
  44. package/dist/cjs/lua/objects.js.map +1 -0
  45. package/dist/cjs/lua/scan.js +543 -0
  46. package/dist/cjs/lua/scan.js.map +1 -0
  47. package/dist/cjs/lua/types.js +103 -0
  48. package/dist/cjs/lua/types.js.map +1 -0
  49. package/dist/cjs/scanner/matcher.js +781 -0
  50. package/dist/cjs/scanner/matcher.js.map +1 -0
  51. package/dist/cjs/scanner/pointer-scan.js +546 -0
  52. package/dist/cjs/scanner/pointer-scan.js.map +1 -0
  53. package/dist/cjs/scanner/scanner.js +513 -0
  54. package/dist/cjs/scanner/scanner.js.map +1 -0
  55. package/dist/cjs/scanner/shared-layout.js +207 -0
  56. package/dist/cjs/scanner/shared-layout.js.map +1 -0
  57. package/dist/cjs/types.js +190 -0
  58. package/dist/cjs/types.js.map +1 -0
  59. package/dist/cjs/value-codec.js +515 -0
  60. package/dist/cjs/value-codec.js.map +1 -0
  61. package/dist/cjs/win/process.js +475 -0
  62. package/dist/cjs/win/process.js.map +1 -0
  63. package/dist/cjs/win/scan-worker.js +84 -0
  64. package/dist/cjs/win/scan-worker.js.map +1 -0
  65. package/dist/cjs/win/winapi.js +403 -0
  66. package/dist/cjs/win/winapi.js.map +1 -0
  67. package/dist/types/addr/expression.d.ts +98 -0
  68. package/dist/types/addr/expression.d.ts.map +1 -0
  69. package/dist/types/addresslist/memory-record.d.ts +218 -0
  70. package/dist/types/addresslist/memory-record.d.ts.map +1 -0
  71. package/dist/types/asm/assembler.d.ts +106 -0
  72. package/dist/types/asm/assembler.d.ts.map +1 -0
  73. package/dist/types/asm/autoassembler-directives.d.ts +118 -0
  74. package/dist/types/asm/autoassembler-directives.d.ts.map +1 -0
  75. package/dist/types/asm/autoassembler.d.ts +434 -0
  76. package/dist/types/asm/autoassembler.d.ts.map +1 -0
  77. package/dist/types/asm/backend.d.ts +167 -0
  78. package/dist/types/asm/backend.d.ts.map +1 -0
  79. package/dist/types/asm/disassembler.d.ts +194 -0
  80. package/dist/types/asm/disassembler.d.ts.map +1 -0
  81. package/dist/types/backend/dbk-backend.d.ts +353 -0
  82. package/dist/types/backend/dbk-backend.d.ts.map +1 -0
  83. package/dist/types/backend/native-ce-backend.d.ts +213 -0
  84. package/dist/types/backend/native-ce-backend.d.ts.map +1 -0
  85. package/dist/types/backend/self-backend.d.ts +64 -0
  86. package/dist/types/backend/self-backend.d.ts.map +1 -0
  87. package/dist/types/backend/types.d.ts +176 -0
  88. package/dist/types/backend/types.d.ts.map +1 -0
  89. package/dist/types/cheattable/ct-format.d.ts +100 -0
  90. package/dist/types/cheattable/ct-format.d.ts.map +1 -0
  91. package/dist/types/index.d.ts +54 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/lua/aa.d.ts +107 -0
  94. package/dist/types/lua/aa.d.ts.map +1 -0
  95. package/dist/types/lua/addressing.d.ts +41 -0
  96. package/dist/types/lua/addressing.d.ts.map +1 -0
  97. package/dist/types/lua/api.d.ts +46 -0
  98. package/dist/types/lua/api.d.ts.map +1 -0
  99. package/dist/types/lua/asm.d.ts +42 -0
  100. package/dist/types/lua/asm.d.ts.map +1 -0
  101. package/dist/types/lua/bridge.d.ts +129 -0
  102. package/dist/types/lua/bridge.d.ts.map +1 -0
  103. package/dist/types/lua/marshal.d.ts +244 -0
  104. package/dist/types/lua/marshal.d.ts.map +1 -0
  105. package/dist/types/lua/memory.d.ts +61 -0
  106. package/dist/types/lua/memory.d.ts.map +1 -0
  107. package/dist/types/lua/objects.d.ts +210 -0
  108. package/dist/types/lua/objects.d.ts.map +1 -0
  109. package/dist/types/lua/scan.d.ts +106 -0
  110. package/dist/types/lua/scan.d.ts.map +1 -0
  111. package/dist/types/lua/types.d.ts +146 -0
  112. package/dist/types/lua/types.d.ts.map +1 -0
  113. package/dist/types/scanner/matcher.d.ts +122 -0
  114. package/dist/types/scanner/matcher.d.ts.map +1 -0
  115. package/dist/types/scanner/pointer-scan.d.ts +187 -0
  116. package/dist/types/scanner/pointer-scan.d.ts.map +1 -0
  117. package/dist/types/scanner/scanner.d.ts +150 -0
  118. package/dist/types/scanner/scanner.d.ts.map +1 -0
  119. package/dist/types/scanner/shared-layout.d.ts +96 -0
  120. package/dist/types/scanner/shared-layout.d.ts.map +1 -0
  121. package/dist/types/types.d.ts +239 -0
  122. package/dist/types/types.d.ts.map +1 -0
  123. package/dist/types/value-codec.d.ts +134 -0
  124. package/dist/types/value-codec.d.ts.map +1 -0
  125. package/dist/types/win/process.d.ts +190 -0
  126. package/dist/types/win/process.d.ts.map +1 -0
  127. package/dist/types/win/scan-worker.d.ts +51 -0
  128. package/dist/types/win/scan-worker.d.ts.map +1 -0
  129. package/dist/types/win/winapi.d.ts +144 -0
  130. package/dist/types/win/winapi.d.ts.map +1 -0
  131. package/docs/assembler.md +371 -0
  132. package/docs/autoassembler.md +423 -0
  133. package/docs/backend-dbk.md +305 -0
  134. package/docs/backends.md +273 -0
  135. package/docs/evidence/atomics-wait-io.js +20 -0
  136. package/docs/evidence/mini-repro.js +98 -0
  137. package/docs/lua-api.md +718 -0
  138. package/package.json +60 -4
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Windows 原生 API 绑定层。
3
+ *
4
+ * 通过 koffi 直接绑定 kernel32/psapi 等导出函数,替代 Cheat Engine 里的
5
+ * `NewKernelHandler.pas` / `ProcessHandlerUnit.pas`(用户态部分)。
6
+ *
7
+ * 注意:CE 在内核态做的部分(dbk32/DBKKernel 驱动读写物理内存、
8
+ * 绕过 ObRegisterCallbacks 等)在 Node 中不存在实现路径,见 PORT-SCOPE.md。
9
+ * 本模块只覆盖 CE 的"无驱动回退路径"所使用的那套用户态 API。
10
+ */
11
+ import * as koffi from 'koffi';
12
+ declare const PROCESSENTRY32W: koffi.IKoffiCType;
13
+ declare const THREADENTRY32: koffi.IKoffiCType;
14
+ declare const MODULEENTRY32W: koffi.IKoffiCType;
15
+ /** MEMORY_BASIC_INFORMATION (x64)。sizeof = 48。 */
16
+ declare const MEMORY_BASIC_INFORMATION: koffi.IKoffiCType;
17
+ /** SECURITY_ATTRIBUTES,用于 CreateFileMapping 共享内存。 */
18
+ declare const SECURITY_ATTRIBUTES: koffi.IKoffiCType;
19
+ export declare const CreateToolhelp32Snapshot: koffi.KoffiFunction;
20
+ /**
21
+ * 关于 koffi 的结构体出入参约定(实测结论,勿改):
22
+ * - 用 `koffi.pointer(STRUCT)` 作为参数类型,**不要**再套 `koffi.out()`;
23
+ * 套了 out() 会让 koffi 只写不回,传对象时字段读不到(全为 0/undefined)。
24
+ * - 调用时传一块 `Buffer`(首字节按结构体布局写入入参字段)最稳,
25
+ * 也可以传 `koffi.alloc(STRUCT, 1)` 并用 `koffi.decode(ptr, STRUCT)` 取回。
26
+ * - 结构体返回值无法直接用 JS 对象承接,必须走上面两种方式之一。
27
+ */
28
+ export declare const Process32FirstW: koffi.KoffiFunction;
29
+ export declare const Process32NextW: koffi.KoffiFunction;
30
+ export declare const Thread32First: koffi.KoffiFunction;
31
+ export declare const Thread32Next: koffi.KoffiFunction;
32
+ export declare const Module32FirstW: koffi.KoffiFunction;
33
+ export declare const Module32NextW: koffi.KoffiFunction;
34
+ export declare const OpenProcess: koffi.KoffiFunction;
35
+ export declare const CloseHandle: koffi.KoffiFunction;
36
+ export declare const GetCurrentProcess: koffi.KoffiFunction;
37
+ export declare const GetCurrentProcessId: koffi.KoffiFunction;
38
+ export declare const GetProcessId: koffi.KoffiFunction;
39
+ export declare const TerminateProcess: koffi.KoffiFunction;
40
+ export declare const IsWow64Process: koffi.KoffiFunction;
41
+ export declare const ReadProcessMemory: koffi.KoffiFunction;
42
+ export declare const WriteProcessMemory: koffi.KoffiFunction;
43
+ export declare const VirtualQueryEx: koffi.KoffiFunction;
44
+ export declare const VirtualProtectEx: koffi.KoffiFunction;
45
+ export declare const VirtualAllocEx: koffi.KoffiFunction;
46
+ export declare const VirtualFreeEx: koffi.KoffiFunction;
47
+ export declare const OpenThread: koffi.KoffiFunction;
48
+ export declare const SuspendThread: koffi.KoffiFunction;
49
+ export declare const ResumeThread: koffi.KoffiFunction;
50
+ export declare const GetThreadContext: koffi.KoffiFunction;
51
+ export declare const SetThreadContext: koffi.KoffiFunction;
52
+ export declare const CreateFileMappingW: koffi.KoffiFunction;
53
+ export declare const OpenFileMappingW: koffi.KoffiFunction;
54
+ export declare const MapViewOfFile: koffi.KoffiFunction;
55
+ export declare const UnmapViewOfFile: koffi.KoffiFunction;
56
+ export declare const GetLastError: koffi.KoffiFunction;
57
+ /**
58
+ * `CreateFileW`。
59
+ *
60
+ * 用于打开内核驱动暴露的设备对象,例如 `\\.\CEDRIVER73`(CE 的 DBK 驱动)。
61
+ * 句柄返回 `void*`,失败时是 `INVALID_HANDLE_VALUE`。
62
+ */
63
+ export declare const CreateFileW: koffi.KoffiFunction;
64
+ /**
65
+ * `DeviceIoControl`。
66
+ *
67
+ * 输入/输出缓冲区都按 `void*` 传:传 `Buffer` 时用 `bufferAsPointer(buf)` 换指针。
68
+ * 对 `METHOD_BUFFERED` 的 ioctl,输入与输出都经系统缓冲区中转,
69
+ * 因此**同一块 Buffer 同时当 in/out 是合法的** —— CE 的写内存就是这么用的
70
+ * (`deviceiocontrol(hdevice, cc, @ao[0], 512, @ao[0], 512, br, nil)`)。
71
+ */
72
+ export declare const DeviceIoControl: koffi.KoffiFunction;
73
+ /** 读写访问 */
74
+ export declare const GENERIC_READ = 2147483648;
75
+ export declare const GENERIC_WRITE = 1073741824;
76
+ /** 共享读写(CE 打开驱动时就是这么传的) */
77
+ export declare const FILE_SHARE_READ = 1;
78
+ export declare const FILE_SHARE_WRITE = 2;
79
+ /** `CREATE_EXISTING`:只打开已存在的设备,不创建 */
80
+ export declare const CREATE_EXISTING = 3;
81
+ /** `FILE_ATTRIBUTE_NORMAL` */
82
+ export declare const FILE_ATTRIBUTE_NORMAL = 128;
83
+ /** `INVALID_HANDLE_VALUE`(Win32 的 -1),用 bigint 表示避免 32 位截断 */
84
+ export declare const INVALID_HANDLE_VALUE = -1n;
85
+ export declare const GetModuleFileNameExW: koffi.KoffiFunction | null;
86
+ export declare const EnumProcessModules: koffi.KoffiFunction | null;
87
+ export declare function win32ErrorMessage(code: number): string;
88
+ /** 读取一句话的当前错误。 */
89
+ export declare function lastErrorMessage(): string;
90
+ /** 判断句柄是否有效(koffi 返回 bigint 指针)。 */
91
+ export declare function isValidHandle(h: unknown): boolean;
92
+ /** 把 koffi 返回的指针/HANDLE 统一成 bigint。 */
93
+ export declare function handleToBigInt(h: unknown): bigint;
94
+ /**
95
+ * 把 bigint 地址转成 koffi 需要的指针参数。
96
+ *
97
+ * 关键约定(实测):
98
+ * - `koffi.as(x, 'void*')` 只接受"外部指针/缓冲区",传裸地址会抛错;
99
+ * - `koffi.address(x)` 是反方向(外部指针 → 地址),也不要在这里用;
100
+ * - koffi 直接接受 BigInt/number 作为指针参数的地址值,因此原样返回即可。
101
+ */
102
+ export declare function asPointer(addr: bigint): bigint;
103
+ /**
104
+ * 把 JS 缓冲区暴露为指针(用于把 Buffer 当作 OUT 参数传给原生函数)。
105
+ * 与 asPointer 的区别:asPointer 转的是"裸地址",这里转的是"缓冲区句柄"。
106
+ */
107
+ export declare function bufferAsPointer(buf: Buffer): unknown;
108
+ export declare const TH32CS_SNAPPROCESS = 2;
109
+ export declare const TH32CS_SNAPTHREAD = 4;
110
+ export declare const TH32CS_SNAPMODULE = 8;
111
+ export declare const TH32CS_SNAPMODULE32 = 16;
112
+ export declare const TH32CS_SNAPALL: number;
113
+ export declare const THREAD_SUSPEND_RESUME = 2;
114
+ export declare const THREAD_GET_CONTEXT = 8;
115
+ export declare const THREAD_SET_CONTEXT = 16;
116
+ export declare const THREAD_QUERY_INFORMATION = 64;
117
+ export declare const MEM_RELEASE = 32768;
118
+ export declare const MEM_RESERVE_FLAG = 8192;
119
+ export declare const MEM_COMMIT_FLAG = 4096;
120
+ export declare const PAGE_READONLY_FLAG = 2;
121
+ export declare const PAGE_READWRITE_FLAG = 4;
122
+ export declare const PAGE_EXECUTE_FLAG = 16;
123
+ export declare const PAGE_EXECUTE_READ_FLAG = 32;
124
+ export declare const PAGE_EXECUTE_READWRITE_FLAG = 64;
125
+ export declare const PAGE_PROTECTION_MASK = 255;
126
+ export declare const PAGE_GUARD_FLAG = 256;
127
+ export declare const FILE_MAP_ALL_ACCESS = 983071;
128
+ export declare const PAGE_READWRITE_MAP = 4;
129
+ export declare const MEM_STATE_NAMES: Record<number, string>;
130
+ export declare const MEM_TYPE_NAMES: Record<number, string>;
131
+ export declare const PAGE_PROTECT_NAMES: Record<number, string>;
132
+ /** 常用进程访问权限(导出给调用方) */
133
+ export declare const ACCESS: {
134
+ readonly TERMINATE: 1;
135
+ readonly CREATE_THREAD: 2;
136
+ readonly VM_OPERATION: 8;
137
+ readonly VM_READ: 16;
138
+ readonly VM_WRITE: 32;
139
+ readonly QUERY_INFORMATION: 1024;
140
+ readonly QUERY_LIMITED_INFORMATION: 4096;
141
+ readonly SUSPEND_RESUME: 2048;
142
+ };
143
+ export { PROCESSENTRY32W, THREADENTRY32, MODULEENTRY32W, MEMORY_BASIC_INFORMATION, SECURITY_ATTRIBUTES, koffi, };
144
+ //# sourceMappingURL=winapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"winapi.d.ts","sourceRoot":"","sources":["../../../src/win/winapi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,371 @@
1
+ # 反汇编器 / 汇编器
2
+
3
+ 本移植提供 **x86 / x64 / ARM / Thumb / ARM64** 的反汇编与汇编能力,用于内存查看器、
4
+ 代码分析、以及 Auto Assembler(见 [autoassembler.md](autoassembler.md))。
5
+
6
+ ## 一、为什么用外部引擎(而不是移植那 2 万行 Pascal)
7
+
8
+ `Cheat Engine/disassembler.pas` 有 **13,933 行**,`Assemblerunit.pas` 有 **7,218 行**,
9
+ ARM 侧另有 `disassemblerarm64.pas`(4,608)、`disassemblerthumb.pas`(4,414)、
10
+ `assemblerarm.pas`(1,074)等。但如果去看 CE 在 Windows 上的实际路径,会发现:
11
+
12
+ ```pascal
13
+ // disassembler.pas, TDisassembler.disassemble
14
+ if defaultBinutils<>nil then
15
+ begin
16
+ //use this
17
+ LastDisassembleData.address:=offset;
18
+ defaultBinutils.disassemble(LastDisassembleData); // ← 调外部 objdump
19
+ ...
20
+ exit;
21
+ end;
22
+ ```
23
+
24
+ 即 **CE 自己优先调用 binutils 的 `objdump`**(见 `binutils.pas`:
25
+ `p.executable := '"...objdump"'`),自带的 Pascal 实现只是**没有 binutils 时的回退**
26
+ (Android 等场景)。汇编同理走 `as` / `ld`。
27
+
28
+ 所以本移植遵循同一架构,选两个成熟引擎做后端:
29
+
30
+ | 能力 | 后端 | 说明 |
31
+ | --- | --- | --- |
32
+ | 反汇编 | [`capstone-wasm`](https://www.npmjs.com/package/capstone-wasm) | Capstone 官方 WASM 绑定 |
33
+ | 汇编 | [`keystone-wasm`](https://www.npmjs.com/package/keystone-wasm) | Keystone 官方 WASM 绑定(v0.9) |
34
+
35
+ 好处是覆盖面与正确性远胜手抄指令表(SSE/AVX、ARM NEON/SVE 等都能处理),
36
+ 代价是引入两个 WASM 依赖(合计约 3MB)并需要异步初始化。
37
+
38
+ > 也评估过 `iced-x86`(1.21.0):它确实是很好的解码器,但这个 npm 包
39
+ > **只暴露 `Decoder`/`Encoder`/`BlockEncoder`,没有 `Assembler`**(文本→机器码),
40
+ > 且字段名与 C# 版不一致(`length` 而非 `len`)。要做文本汇编仍需自己写 ——
41
+ > 因此最终选了 Capstone + Keystone 的组合。
42
+
43
+ ## 二、支持的架构
44
+
45
+ | 架构名 | 说明 | 指令长度 | 对齐 | 地址宽度 |
46
+ | --- | --- | --- | --- | --- |
47
+ | `x86` | 32 位 x86 | 1–15 字节,可变 | 1 | 8 |
48
+ | `x64` | 64 位 x86(默认) | 1–15 字节,可变 | 1 | 16 |
49
+ | `arm` | ARM A32(ARMv7 及以前) | 固定 4 | 4 | 8 |
50
+ | `thumb` | Thumb / Thumb-2 | 2 或 4 | 2 | 8 |
51
+ | `arm64` | AArch64(ARMv8) | 固定 4 | 4 | 16 |
52
+
53
+ 架构名接受宽松写法(大小写、`_`/`-`/空格都忽略):
54
+
55
+ ```js
56
+ normalizeArchitecture('aarch64') // 'arm64'
57
+ normalizeArchitecture('ARM') // 'arm'
58
+ normalizeArchitecture('thumb2') // 'thumb'
59
+ normalizeArchitecture('amd64') // 'x64'
60
+ normalizeArchitecture('i386') // 'x86'
61
+ normalizeArchitecture(64) // 'x64'
62
+ ```
63
+
64
+ 字节序可选(CE 的 ARM 目标可能是大端):
65
+
66
+ ```js
67
+ assembleToBytes('mov r0, r1', { address: 0x1000n, arch: 'arm', endianness: 'big' });
68
+ // → e1 a0 00 01(小端为 01 00 a0 e1)
69
+ ```
70
+
71
+ ## 三、快速开始
72
+
73
+ ```js
74
+ const {
75
+ ensureAsmBackend, // ← 必须先调用(WASM 异步初始化)
76
+ disassembleOne,
77
+ formatDisassembled,
78
+ assembleToBytes,
79
+ instructionText,
80
+ } = require('node-cheat-engine');
81
+
82
+ (async () => {
83
+ await ensureAsmBackend();
84
+
85
+ // 反汇编(x64)
86
+ const insn = disassembleOne(Buffer.from('488b0510203040', 'hex'), 0x7ff000001000n);
87
+ console.log(insn.opcode); // 'mov'
88
+ console.log(insn.parameters); // 'rax, qword ptr [rip + 0x40302010]'
89
+ console.log(insn.dataSize); // 8
90
+ console.log(formatDisassembled(insn));
91
+ // → '00007FF000001000 - 48 8B 05 10 20 30 40 - mov rax, qword ptr [rip + 0x40302010]'
92
+
93
+ // 汇编(x64)
94
+ const bytes = assembleToBytes('mov rax, 1\nret', { address: 0x1000n });
95
+ console.log(bytes.toString('hex')); // '48c7c001000000c3'
96
+
97
+ // ARM64
98
+ const a64 = assembleToBytes('stp x29, x30, [sp, #-16]!\nret', {
99
+ address: 0x400000n, arch: 'arm64',
100
+ });
101
+ const list = disassemble(a64, 0x400000n, 4, { arch: 'arm64' });
102
+ list.forEach((i) => console.log(instructionText(i)));
103
+ // stp x29, x30, [sp, #-0x10]!
104
+ // ret
105
+ })();
106
+ ```
107
+
108
+ ### 反汇编目标进程的代码
109
+
110
+ ```js
111
+ const { Process, ensureAsmBackend, disassembleAt, formatDisassembled } = require('node-cheat-engine');
112
+
113
+ await ensureAsmBackend();
114
+ const proc = Process.open(1234);
115
+ const mod = proc.modules()[0];
116
+
117
+ let addr = mod.baseAddress;
118
+ for (let i = 0; i < 10; i++) {
119
+ const insn = disassembleAt((a, n) => proc.readMemory(a, n), addr);
120
+ console.log(formatDisassembled(insn));
121
+ addr += BigInt(insn.size); // ARM 定长 4 / Thumb 2|4 / x86 变长
122
+ }
123
+ proc.close();
124
+ ```
125
+
126
+ `disassembleAt` 固定读 16 字节(x86 单指令最长 15 字节,ARM/ARM64 定长 4,
127
+ Thumb 最多 4),一次读取足够覆盖任何架构的单条指令。
128
+
129
+ ## 四、数字进制(重要,容易写错)
130
+
131
+ 实测(Keystone 0.9):
132
+
133
+ | 写法 | 含义 | 例 |
134
+ | --- | --- | --- |
135
+ | `100` | **十进制** 100 | `mov eax, 100` → `B8 64 00 00 00` |
136
+ | `0x100` | 十六进制 0x100 | `mov eax, 0x100` → `B8 00 01 00 00` |
137
+ | `100h` | 十六进制 0x100 | `mov eax, 100h` → `B8 00 01 00 00` |
138
+ | `#100`(ARM) | 十进制 100 | `mov r0, #100` 与 `#0x64` 编码相同 |
139
+
140
+ **与 CE 的一致性**:CE 帮助文档里十六进制一律用 `h` 后缀 ——
141
+ `mov eax, 403045h ; make eax have the value 403045 hex`、
142
+ `mov cl, byte ptr [34h]`;而裸数字的例子(`mov edx, 5006`)按十进制解读。
143
+ CE 在 Windows 上把汇编交给 **GNU as**(binutils),GNU as 的裸数字也是十进制 ——
144
+ 因此本移植与 CE 的实际行为一致。Keystone 也支持 `h` 后缀,CE 脚本可直跑。
145
+
146
+ > 注意:CE 自带的 Pascal 汇编器与 CE 部分文档示例(如 `mov [esi+80], ebx`
147
+ > 里的 `80` 看起来像 0x80)暗示裸数字可能按十六进制解释。
148
+ > 本项目没有 CE 可执行文件可实测,因此**采用 binutils 语义**。
149
+ > 如果你的脚本依赖十六进制写法,请显式写 `0x80` 或 `80h`。
150
+ > 另外 `db`/`dw`/`dd`/`dq` 的裸数字**是十六进制**(`dataHexDefault` 默认 true,
151
+ > 与 CE 一致):`db 89 01` 就是字节 `0x89 0x01`。这两套约定不同,别混。
152
+
153
+ ## 五、API
154
+
155
+ ### 5.1 初始化(必须)
156
+
157
+ ```js
158
+ await ensureAsmBackend(); // 幂等;失败时不缓存失败结果,可重试
159
+ isAsmBackendReady(); // 同步查询是否就绪
160
+ backendVersions(); // { capstone, keystone }
161
+ ```
162
+
163
+ 未初始化就调用其它 API 会抛 `AsmBackendError`,信息里会提示
164
+ `请在调用前 await ensureAsmBackend()`。
165
+
166
+ ### 5.2 反汇编
167
+
168
+ ```js
169
+ disassemble(code: Buffer, address: bigint, maxInstructions = 1, opts?): DisassembledInstruction[]
170
+ disassembleOne(code, address, opts?): DisassembledInstruction
171
+ disassembleAt(readMemory: (addr, size) => Buffer, address, opts?): DisassembledInstruction
172
+ ```
173
+
174
+ - `opts.bitness`:`32 | 64`,默认 64
175
+ - `disassembleOne` 解不出指令时抛错(附上字节内容),不会返回 `null`
176
+ - `disassembleAt` 只读 16 字节(x86 单指令最长 15 字节)
177
+
178
+ `DisassembledInstruction` 的字段对齐 CE 的 `TLastDisassembleData`
179
+ (`lastdisassembledata.pas`):
180
+
181
+ | 字段 | 说明 |
182
+ | --- | --- |
183
+ | `address` | 指令地址(bigint) |
184
+ | `prefix` / `prefixSize` | 前缀文本(`rep`/`lock`)与长度 |
185
+ | `opcode` | 助记符,例如 `mov` |
186
+ | `parameters` | 操作数文本,例如 `rax, qword ptr [rip + 0x40302010]` |
187
+ | `bytes` | 机器码,**每个字节一个 `Buffer`**(对齐 CE 的 `Bytes: array of byte`) |
188
+ | `separators` / `separatorCount` | 字节里的分段下标(前缀/操作码/modrm/sib),供界面高亮 |
189
+ | `modrmValueType` / `modrmValue` | 内存操作数指向的值(`DisasmValueType`) |
190
+ | `parameterValueType` / `parameterValue` | 立即数/位移值 |
191
+ | `dataSize` | 读写的数据宽度(字节) |
192
+ | `isFloat` / `isFloat64` | 是否单/双精度浮点 |
193
+ | `hasSib` / `sibIndex` / `sibScaler` | SIB 信息 |
194
+ | `isJump` / `isConditionalJump` / `isCall` / `isRet` / `isRep` | 流程控制标记 |
195
+ | `ripRelative` | RIP 相对偏移下标;`-1` 表示不是 |
196
+ | `is64Bit` | 是否 64 位模式解码 |
197
+ | `size` | 指令长度 |
198
+
199
+ ### 5.3 格式化与解析
200
+
201
+ ```js
202
+ formatDisassembled(insn, { addressWidth?, showAddress?, showBytes? }): string
203
+ instructionText(insn): string // 含前缀的完整指令文本,用于重新汇编
204
+ splitDisassembledString(text): { address, bytes, opcode, parameters }
205
+ branchTarget(insn): bigint | null // 跳转/调用目标,非流程指令返回 null
206
+ formatBytes(bytes): string // '48 8B 05 10 20 30 40'
207
+ ```
208
+
209
+ **`instructionText` 与 `opcode + parameters` 的区别很重要**:CE 把前缀单独存在
210
+ `prefix` 字段,所以 `opcode + parameters` **不含 `rep`/`lock`**。
211
+ 想往返必须用 `instructionText`:
212
+
213
+ ```js
214
+ const insn = disassembleOne(Buffer.from('f3a4', 'hex'), 0x1000n);
215
+ insn.prefix; // 'rep'
216
+ insn.opcode; // 'movsb'
217
+ instructionText(insn); // 'rep movsb' ← 用这个去汇编
218
+ `${insn.opcode} ${insn.parameters}`; // 'movsb' ← 会丢掉 F3 前缀(不要这样往返)
219
+ ```
220
+
221
+ ### 5.4 汇编
222
+
223
+ ```js
224
+ assemble(source: string, opts): AssembleResult
225
+ assembleLines(source: string, opts): AssembleResult // 逐行,带每行地址
226
+ assembleToBytes(source: string, opts): Buffer
227
+ ```
228
+
229
+ `opts`:
230
+
231
+ | 选项 | 说明 |
232
+ | --- | --- |
233
+ | `address`(必需) | 代码将放置的地址。**相对跳转/相对寻址的编码取决于它** |
234
+ | `arch` | `x86` / `x64`(默认)/ `arm` / `thumb` / `arm64` |
235
+ | `bitness` | `32 \| 64`,兼容旧写法;`arch` 优先(`arm` 与 `thumb` 都是 32 位,光给位数无法区分) |
236
+ | `endianness` | `little`(默认)/ `big`。CE 的 ARM 目标可能是大端 |
237
+ | `symbols` | `Record<string, bigint> \| Map<string, bigint>`,用于解析 `jmp label` 这类引用 |
238
+ | `syntax` | `intel`(默认)`\| att \| nasm \| masm \| gas`;**仅 x86 有效**,ARM 只有一种语法 |
239
+ | `friendlyErrors` | 默认 `true`:把 Keystone 的报错换成更可读的中文说明 |
240
+
241
+ 返回 `AssembleResult`:
242
+
243
+ ```ts
244
+ {
245
+ bytes: Buffer; // 完整机器码
246
+ address: bigint; // 起始地址
247
+ endAddress: bigint; // 编码后的下一个地址
248
+ lines: AssembledLine[]; // 逐行 { source, bytes, address, empty }
249
+ usedSymbols: Record<string, string>; // 本次用到的符号
250
+ }
251
+ ```
252
+
253
+ `assembleLines` 每行都给出地址,这是 Auto Assembler 需要的
254
+ (`alloc`/`label` 之后要把脚本行映射回地址)。它同时识别两种标号写法:
255
+
256
+ ```js
257
+ assembleLines('start:\nmov eax,1\ninline: ret', { address: 0x1000n });
258
+ // 'start:' 行 empty=true 且不占地址;'inline: ret' 记录标号后编码 ret
259
+ ```
260
+
261
+ ### 5.5 符号解析
262
+
263
+ Keystone 的 `KS_OPT_SYM_RESOLVER` 需要传 JS 函数指针,wasm 绑定未暴露,
264
+ 因此本移植用**文本替换**实现符号解析(语义等价、可预测、报错能指出符号名):
265
+
266
+ ```js
267
+ assemble('mov eax, dword ptr [myAddr]', {
268
+ address: 0x1000n,
269
+ symbols: { myAddr: 0x7ff000002000n },
270
+ });
271
+ ```
272
+
273
+ 替换时按名字长度倒序、用词边界匹配,不会误伤 `0xAB` 这类十六进制字面量
274
+ 或寄存器名。未在符号表里的标识符会被识别并给出:
275
+
276
+ ```
277
+ 汇编失败:未定义的符号 "someLabel"。请先用 label/define/registersymbol/aobscan 定义,
278
+ 或通过 symbols 选项传入。
279
+ ```
280
+
281
+ ## 六、实测得到的关键行为(都是踩过的坑)
282
+
283
+ 这几条都写进了代码注释,改动前请先读对应注释:
284
+
285
+ 1. **`int 3` 修正**:Keystone 0.9 把 `int 3` 编成 `CD 03`(两字节),
286
+ 而 x86 惯例与 CE 都用单字节 `CC`。本移植在整段输出恰为 `CD 03` 时替换成 `CC`。
287
+
288
+ 2. **`qword ptr` 不能被 `word ptr` 抢先匹配**:数据宽度推断最初按
289
+ `byte ptr` → `word ptr` → `dword ptr` → `qword ptr` 的顺序逐个 `match`,
290
+ 结果 `qword ptr` 里的子串命中了 `word ptr`,宽度被误判成 2 字节。
291
+ 现在用一次性分组匹配(等价"最长优先")。
292
+
293
+ 3. **Capstone 不接受 bigint 地址**:传 bigint 会在 WASM 边界抛类型错误。
294
+ 统一转 `number`,并在超过 `Number.MAX_SAFE_INTEGER` 时**明确报错**
295
+ (`toSafeNumberAddress`),绝不静默截断。
296
+
297
+ 4. **`disasm()` 返回的 `address` 是缓冲区相对偏移**,不是绝对地址。
298
+ `backend.ts` 负责加回基址。若忘了这一步,多指令反汇编的地址会从 0 开始。
299
+
300
+ 5. **`disasm()` 的参数是选项对象**,不是位置参数:
301
+ `engine.disasm(code, { address, count })`。传三个位置参数会静默失败。
302
+
303
+ 6. **Capstone 的构造器只有 `(arch, mode)`**,语法要用
304
+ `setOption(CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL)` 设置。
305
+
306
+ 7. **前缀必须靠 `instructionText` 拼回**:只用 `opcode + parameters` 往返会丢掉
307
+ `F3`/`F2` 前缀(`rep movsb` 退化成 `movsb`)。
308
+
309
+ ## 七、正确性判定标准:语义往返 ≠ 字节往返
310
+
311
+ x86 允许**语义等价但字节不同**的多种编码,例如:
312
+
313
+ | 指令 | 形式 A | 形式 B |
314
+ | --- | --- | --- |
315
+ | `mov rsi, rdx` | `48 89 D6`(89 /r) | `48 8B F2`(8B /r) |
316
+ | `xor r14d, r14d` | `45 33 F6`(33 /r) | `45 31 F6`(31 /r) |
317
+
318
+ 反汇编器给出助记符形式,重新汇编时汇编器会挑它自己的编码,因此
319
+ **字节不必相同**。CE 也如此(objdump 反汇编 / as 汇编)。
320
+
321
+ 所以本移植的验证标准是**语义往返**:`反汇编 → 重新汇编 → 再反汇编`,
322
+ 结果必须与原指令的助记符与操作数**逐字段一致**。
323
+
324
+ 实测(`test/asm.test.js`,对目标进程真实映像段抽样):
325
+
326
+ ```
327
+ [参考] 检查 360 条:语义往返 100%,字节级一致 88.1%
328
+ ```
329
+
330
+ 字节级 88.1% 是正常的;测试里只把它当作"汇编器是否整体退化"的信号
331
+ (断言 > 75%),真正的断言是语义往返 100%。
332
+
333
+ ## 八、测试
334
+
335
+ ```bash
336
+ npm run test:asm # 或 node --test test/asm.test.js
337
+ ```
338
+
339
+ 26 → **39 个用例**,五层验证:
340
+
341
+ | 层次 | 内容 |
342
+ | --- | --- |
343
+ | 编码正确性 | 20 条指令与手册/Keystone 的期望字节逐一比对(x64 + x86 + ARM + Thumb + ARM64) |
344
+ | 字段正确性 | 数据宽度、浮点标记、流程控制标记、条件码、PC 相对、多条推进 |
345
+ | **动态语义往返** | 目标进程真实映像段抽样 360 条指令,语义往返必须 100% |
346
+ | 端到端 | 汇编 → 写入目标进程 → 读回 → 反汇编 → 重新汇编,逐条比对 |
347
+ | 多架构往返 | 5 种架构各一组指令,汇编 → 反汇编 → 重新汇编,语义一致 |
348
+
349
+ ## 九、已知限制
350
+
351
+ - **Capstone 不暴露 `detail`**(`capstone-wasm` 的 `readInsn` 是私有的),
352
+ 因此拿不到结构化的操作数类型/寄存器使用信息。本移植改用**助记符表**判定
353
+ 流程控制与数据宽度,对主流指令足够;冷门指令可能被标成 `isjump=false`
354
+ (宁可少标,不错标)。
355
+ - **`separators` 是保守近似**:没有完整编码器结构,无法精确给出
356
+ 前缀/操作码/modrm/sib 的字节分界,只保证单调递增且不越界(界面分段不会错位)。
357
+ - **`ripRelative` 是近似标记**:x86 上返回固定下标 3;ARM64 上只要有 PC 相对语义
358
+ (`adrp`/`adr`/`b`/`bl` 或操作数里出现 `pc`)就置 0。都不是精确字节位置。
359
+ - **`hasSib` 用正则判断**,极端寻址写法可能误判。
360
+ - **ARM 的"条件执行"与"条件跳转"分开处理**:`moveq` 只填 `conditionCode`、
361
+ 不置 `isConditionalJump`(它是条件执行不是跳转);`beq` 才置
362
+ `isConditionalJump`。这与 CE 的处理一致,但用的时候要留意区别。
363
+ - **ARM 的数据宽度靠助记符/寄存器推断**:`ldrb`→1、`ldrh`→2、`ldrd`→8、
364
+ `s0`→4、`d0`→8;`push`/`pop`/`ldm`/`stm` 是块操作,返回 0(没有单一宽度)。
365
+ - **ARM64 的 `str w0, [x1, #-4]` 可能被 Capstone 解码成 `stur w0, ...`**:
366
+ 这是同一指令的"非 scaled 形式",属正常现象,不影响往返。
367
+ - **不支持 ARM 的 ThumbEE / Jazelle、ARM64 的 SVE2 全指令集**:
368
+ Keystone/Capstone 支持到哪就是哪。
369
+ - **`dataSize` 对部分指令返回 0**(例如无操作数的 `ret`/`nop`),这是预期行为。
370
+ - 汇编语法以 Keystone/NASM 为准。CE 汇编器里那些针对旧语法的兼容分支
371
+ (`$` 前缀十六进制、`offset` 关键字等)**未实现**,遇到时 Keystone 会报错。