@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,715 @@
1
+ "use strict";
2
+ /**
3
+ * 反汇编器(x86 / x64 / ARM / Thumb / ARM64)—— 输出对齐 Cheat Engine 的
4
+ * `TLastDisassembleData`。
5
+ *
6
+ * 后端是 Capstone(见 `backend.ts` 里关于"CE 自己也用 binutils 而非手写解码器"的说明)。
7
+ * 本文件负责把 Capstone 的原始结果加工成 CE 需要的那套字段与文本格式。
8
+ *
9
+ * CE 的字段定义见 `lastdisassembledata.pas` 的 `TLastDisassembleData`:
10
+ * address / prefix / opcode / parameters / description / Bytes /
11
+ * SeperatorCount+Seperators / modrmValue / parameterValue /
12
+ * datasize / isfloat / isfloat64 / hasSib / sibIndex / sibScaler /
13
+ * isjump / iscall / isret / isrep / isconditionaljump / riprelative
14
+ * / Disassembler(dcX86 / dcArm / dcArm64 / dcThumb)
15
+ *
16
+ * ## 文本格式
17
+ * CE 在 binutils 分支下产出 `"地址 - 字节 - 操作码 参数"`。
18
+ * 本模块的 `formatDisassembled` 产同样形状的字符串,同时把 opcode / parameters
19
+ * 分开保留(CE 内部也是分开存的,显示时才拼)。
20
+ *
21
+ * ## 多架构差异(这里是最容易出错的地方)
22
+ *
23
+ * | 项目 | x86/x64 | ARM/Thumb | ARM64 |
24
+ * | --- | --- | --- | --- |
25
+ * | 指令长度 | 1–15 字节,可变 | 4(Thumb 2/4) | 4 |
26
+ * | 前缀 | `rep`/`lock`/段前缀 | 无 | 无 |
27
+ * | 条件执行 | `jcc` 助记符(7x/0F 8x) | **指令后缀**(`moveq`/`addne`) | `b.eq` 助记符 |
28
+ * | 返回 | `ret`/`retf`/`iret` | `bx lr`/`pop {...,pc}`/`mov pc,lr` | `ret` |
29
+ * | 调用 | `call` | `bl`/`blx` | `bl`/`blr` |
30
+ * | 寄存器 | `rax`/`eax`/`al`… | `r0`–`r15`、`sp`/`lr`/`pc` | `x0`–`x30`、`w0`–`w30`、`sp`/`xzr`/`wzr` |
31
+ * | RIP 相对 | 有(`[rip+disp]`) | 无 | 有(`adrp`/`ldr xN, [pc,...]`、`b`/`bl` 的 PC 相对) |
32
+ *
33
+ * ## 已知限制
34
+ * `capstone-wasm` 不暴露 detail(`readInsn` 是私有的),因此拿不到
35
+ * "操作数类型/寄存器使用/内存操作数宽度"等结构信息。
36
+ * 本模块改用**助记符表**判定流程控制与数据宽度,对主流指令足够;
37
+ * 冷门指令会被标成 `isjump=false`(宁可少标,不要错标)。
38
+ */
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.DisassemblerClass = exports.DisasmValueType = void 0;
41
+ exports.disassemble = disassemble;
42
+ exports.disassembleOne = disassembleOne;
43
+ exports.disassembleAt = disassembleAt;
44
+ exports.alignmentOf = alignmentOf;
45
+ exports.formatBytes = formatBytes;
46
+ exports.formatDisassembled = formatDisassembled;
47
+ exports.instructionText = instructionText;
48
+ exports.splitDisassembledString = splitDisassembledString;
49
+ exports.branchTarget = branchTarget;
50
+ const backend_1 = require("./backend");
51
+ /* ------------------------------------------------------------------ *
52
+ * 输出类型
53
+ * ------------------------------------------------------------------ */
54
+ /** 值类型提示,对齐 CE 的 TDisAssemblerValueType */
55
+ var DisasmValueType;
56
+ (function (DisasmValueType) {
57
+ DisasmValueType[DisasmValueType["None"] = 0] = "None";
58
+ DisasmValueType[DisasmValueType["Address"] = 1] = "Address";
59
+ DisasmValueType[DisasmValueType["Value"] = 2] = "Value";
60
+ })(DisasmValueType || (exports.DisasmValueType = DisasmValueType = {}));
61
+ /** 反汇编器类型,对齐 CE 的 TDisassemblerClass */
62
+ var DisassemblerClass;
63
+ (function (DisassemblerClass) {
64
+ DisassemblerClass["X86"] = "dcX86";
65
+ DisassemblerClass["Arm"] = "dcArm";
66
+ DisassemblerClass["Arm64"] = "dcArm64";
67
+ DisassemblerClass["Thumb"] = "dcThumb";
68
+ })(DisassemblerClass || (exports.DisassemblerClass = DisassemblerClass = {}));
69
+ /* ------------------------------------------------------------------ *
70
+ * 各架构的流程控制判定表
71
+ *
72
+ * Capstone-wasm 不给分组,所以按助记符判定。宁可少标也不要错标。
73
+ * ------------------------------------------------------------------ */
74
+ /* —— x86 —— */
75
+ const X86_UNCONDITIONAL_JUMPS = new Set(['jmp', 'ljmp', 'jmpf']);
76
+ const X86_CONDITIONAL_JUMPS = new Set([
77
+ 'ja', 'jae', 'jb', 'jbe', 'jc', 'je', 'jg', 'jge', 'jl', 'jle', 'jna', 'jnae',
78
+ 'jnb', 'jnbe', 'jnc', 'jne', 'jng', 'jnge', 'jnl', 'jnle', 'jno', 'jnp', 'jns',
79
+ 'jnz', 'jo', 'jp', 'jpe', 'jpo', 'js', 'jz',
80
+ 'jcxz', 'jecxz', 'jrcxz',
81
+ 'loop', 'loope', 'loopne', 'loopz', 'loopnz',
82
+ ]);
83
+ const X86_CALLS = new Set(['call', 'lcall', 'callf']);
84
+ const X86_RETURNS = new Set(['ret', 'retf', 'iret', 'iretd', 'iretq', 'sysret', 'sysexit']);
85
+ const X86_REP_PREFIXES = new Set(['rep', 'repe', 'repz', 'repne', 'repnz']);
86
+ /* —— ARM(A32 / Thumb)——
87
+
88
+ * ARM 的"条件跳转"有两条完全不同的路:
89
+ * 1. 专门的跳转指令 `b` + 条件后缀(`beq`/`bne`…)
90
+ * 2. **几乎所有指令都带条件后缀**(`moveq r0,r1`)—— 严格说这是"条件执行"而非跳转,
91
+ * 但 CE 把它归到 isconditionaljump(`disassembler.pas` 对 ARM 的处理)。
92
+ * 因此这里只把**跳转类**带后缀的算作 isConditionalJump,
93
+ * 其它带后缀的只填 `conditionCode`,不置 isConditionalJump ——
94
+ * 否则 `moveq` 会被误报成条件跳转,界面会乱。
95
+ */
96
+ const ARM_CONDITION_SUFFIXES = [
97
+ 'eq', 'ne', 'cs', 'hs', 'cc', 'lo', 'mi', 'pl', 'vs', 'vc',
98
+ 'hi', 'ls', 'ge', 'lt', 'gt', 'le', 'al',
99
+ ];
100
+ /** ARM 跳转族基名(去掉条件后缀后判断) */
101
+ const ARM_JUMP_BASES = new Set(['b', 'bx', 'bl', 'blx', 'cbz', 'cbnz', 'tbb', 'tbh']);
102
+ /** ARM 无条件返回/跳转(按完整助记符) */
103
+ const ARM_RETURNS = new Set(['bx', 'bxeq', 'bxne', 'bxls', 'bxge', 'bxlt', 'bxgt', 'bxle']);
104
+ /* —— ARM64 —— */
105
+ const ARM64_UNCONDITIONAL_JUMPS = new Set(['b', 'br']);
106
+ const ARM64_CONDITIONAL_JUMPS = new Set(['cbz', 'cbnz', 'tbz', 'tbnz']);
107
+ const ARM64_CALLS = new Set(['bl', 'blr']);
108
+ const ARM64_RETURNS = new Set(['ret', 'eret']);
109
+ /** ARM64 的 `b.eq` 之类:Capstone 输出 `b.eq`,基名是 `b` */
110
+ const ARM64_COND_SUFFIX_RE = /\.(eq|ne|cs|hs|cc|lo|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al)$/;
111
+ /* —— 浮点提示 —— */
112
+ /** x86 单精度 */
113
+ const X86_FLOAT32_HINT = /\b(movss|addss|subss|mulss|divss|sqrtss|minss|maxss|cmpss|comiss|ucomiss)\b/i;
114
+ /** x86 双精度 */
115
+ const X86_FLOAT64_HINT = /\b(movsd|addsd|subsd|mulsd|divsd|sqrtsd|minsd|maxsd|cmpsd|comisd|ucomisd)\b/i;
116
+ /** ARM/ARM64 单精度:寄存器 `s0`–`s31` */
117
+ const ARM_FLOAT32_HINT = /\bs\d+\b/i;
118
+ /** ARM/ARM64 双精度:寄存器 `d0`–`d31` */
119
+ const ARM_FLOAT64_HINT = /\bd\d+\b/i;
120
+ /* ------------------------------------------------------------------ *
121
+ * 主入口
122
+ * ------------------------------------------------------------------ */
123
+ /**
124
+ * 反汇编内存中的一段代码。
125
+ *
126
+ * @param code 机器码缓冲区
127
+ * @param address 该缓冲区在目标进程中的地址
128
+ * @param maxInstructions 最多解多少条,默认 1
129
+ * @param options 架构/字节序
130
+ */
131
+ function disassemble(code, address, maxInstructions = 1, options = {}) {
132
+ if (code.length === 0) {
133
+ throw new backend_1.AsmBackendError('反汇编失败:给定的代码缓冲区为空');
134
+ }
135
+ const arch = options.arch ?? (0, backend_1.archFromBitness)(options.bitness);
136
+ const endianness = options.endianness ?? 'little';
137
+ const raws = (0, backend_1.disassembleRaw)(code, { address, arch, endianness, maxInstructions });
138
+ return raws.map((raw) => convert(raw, arch));
139
+ }
140
+ /** 反汇编一条指令 */
141
+ function disassembleOne(code, address, options = {}) {
142
+ const list = disassemble(code, address, 1, options);
143
+ if (list.length === 0) {
144
+ const arch = options.arch ?? (0, backend_1.archFromBitness)(options.bitness);
145
+ throw new backend_1.AsmBackendError(`反汇编失败:在地址 0x${address.toString(16)} 处无法解码出 ${arch} 指令` +
146
+ `(字节 ${code.subarray(0, 16).toString('hex')})`);
147
+ }
148
+ return list[0];
149
+ }
150
+ /**
151
+ * 从目标进程读内存并反汇编一条。
152
+ *
153
+ * 读取长度按架构取:x86 最长指令 15 字节(读 16 留余量),
154
+ * ARM/ARM64 定长 4 字节(读 16 同样够用,且便于 Thumb 的 4 字节指令)。
155
+ */
156
+ function disassembleAt(readMemory, address, options = {}) {
157
+ const buf = readMemory(address, 16);
158
+ if (buf.length === 0) {
159
+ throw new backend_1.AsmBackendError(`反汇编失败:无法读取地址 0x${address.toString(16)} 的内存`);
160
+ }
161
+ return disassembleOne(buf, address, options);
162
+ }
163
+ /** 按架构取"对齐步长"(用于在解不出指令时安全推进) */
164
+ function alignmentOf(arch) {
165
+ return (0, backend_1.architectureInfo)(arch).alignment;
166
+ }
167
+ /* ------------------------------------------------------------------ *
168
+ * 转换
169
+ * ------------------------------------------------------------------ */
170
+ function convert(raw, arch) {
171
+ const info = (0, backend_1.architectureInfo)(arch);
172
+ const mnemonic = raw.mnemonic.toLowerCase();
173
+ const operands = raw.operands.trim();
174
+ if (info.family === 'x86')
175
+ return convertX86(raw, arch, info, mnemonic, operands);
176
+ if (info.family === 'arm')
177
+ return convertArm(raw, arch, info, mnemonic, operands);
178
+ return convertArm64(raw, arch, info, mnemonic, operands);
179
+ }
180
+ /* —— x86 —— */
181
+ function convertX86(raw, arch, info, mnemonic, operands) {
182
+ // 前缀与操作码分离:Capstone 把 rep/lock 之类并进 mnemonic,这里拆到 prefix
183
+ let prefix = '';
184
+ let opcode = mnemonic;
185
+ const spaceIdx = mnemonic.indexOf(' ');
186
+ if (spaceIdx > 0) {
187
+ prefix = mnemonic.slice(0, spaceIdx);
188
+ opcode = mnemonic.slice(spaceIdx + 1);
189
+ }
190
+ const isRep = X86_REP_PREFIXES.has(prefix) || X86_REP_PREFIXES.has(mnemonic);
191
+ const isJump = X86_UNCONDITIONAL_JUMPS.has(opcode) || X86_CONDITIONAL_JUMPS.has(opcode);
192
+ const isConditionalJump = X86_CONDITIONAL_JUMPS.has(opcode);
193
+ const isCall = X86_CALLS.has(opcode);
194
+ const isRet = X86_RETURNS.has(opcode);
195
+ const dataSize = inferX86DataSize(operands, opcode);
196
+ const isFloat = X86_FLOAT32_HINT.test(opcode) || X86_FLOAT32_HINT.test(operands);
197
+ const isFloat64 = X86_FLOAT64_HINT.test(opcode) || X86_FLOAT64_HINT.test(operands);
198
+ const ripRelative = detectRipRelative(operands);
199
+ const hasSib = /\[[^\]]*(\*|\+ *r[a-z0-9]+ *\+)/i.test(operands);
200
+ const parameterValue = extractDisplacement(operands);
201
+ return {
202
+ address: raw.address,
203
+ prefix,
204
+ prefixSize: prefix ? prefix.length : 0,
205
+ opcode,
206
+ suffix: '',
207
+ parameters: operands,
208
+ description: '',
209
+ bytes: splitBytes(raw.bytes),
210
+ separators: computeSeparators(raw.bytes.length, prefix, hasSib),
211
+ separatorCount: 0,
212
+ modrmValueType: parameterValue !== null ? DisasmValueType.Address : DisasmValueType.None,
213
+ modrmValue: parameterValue ?? 0n,
214
+ parameterValueType: parameterValue !== null ? DisasmValueType.Value : DisasmValueType.None,
215
+ parameterValue: parameterValue ?? 0n,
216
+ dataSize,
217
+ isFloat: isFloat && !isFloat64,
218
+ isFloat64,
219
+ hasSib,
220
+ sibIndex: -1,
221
+ sibScaler: 0,
222
+ isJump,
223
+ isCall,
224
+ isRet,
225
+ isRep,
226
+ isConditionalJump,
227
+ conditionCode: x86ConditionCode(opcode),
228
+ ripRelative,
229
+ is64Bit: info.is64Bit,
230
+ arch,
231
+ disassemblerClass: DisassemblerClass.X86,
232
+ mnemonic,
233
+ size: raw.size,
234
+ };
235
+ }
236
+ /** 从 x86 的 jcc 助记符里取条件码(`je`→`e`、`jne`→`ne`) */
237
+ function x86ConditionCode(opcode) {
238
+ if (!X86_CONDITIONAL_JUMPS.has(opcode))
239
+ return '';
240
+ const cc = opcode.replace(/^j/, '').replace(/^loop/, '');
241
+ return cc === opcode ? '' : cc;
242
+ }
243
+ /** 从操作数文本推断 x86 数据宽度(字节) */
244
+ function inferX86DataSize(operands, opcode) {
245
+ // 注意顺序与边界:`qword ptr` 里包含子串 `word ptr`,
246
+ // 若先匹配 `word ptr` 会把 qword 误判成 2 字节(踩过)。
247
+ // 因此用一次性统一匹配,长的放前面,等价于"最长优先"。
248
+ const ptrMatch = operands.match(/\b(byte|word|dword|qword|xmmword|ymmword|zmmword|tbyte)\s+ptr\b/i);
249
+ if (ptrMatch) {
250
+ switch (ptrMatch[1].toLowerCase()) {
251
+ case 'byte':
252
+ return 1;
253
+ case 'word':
254
+ return 2;
255
+ case 'dword':
256
+ return 4;
257
+ case 'qword':
258
+ return 8;
259
+ case 'tbyte':
260
+ return 10;
261
+ case 'xmmword':
262
+ return 16;
263
+ case 'ymmword':
264
+ return 32;
265
+ case 'zmmword':
266
+ return 64;
267
+ }
268
+ }
269
+ if (/\b(r\d+b|spl|bpl|sil|dil|al|ah|bl|bh|cl|ch|dl|dh)\b/i.test(operands))
270
+ return 1;
271
+ if (/\b(r\d+w|ax|bx|cx|dx|si|di|bp|sp)\b/i.test(operands))
272
+ return 2;
273
+ if (/\b(r\d+d|eax|ebx|ecx|edx|esi|edi|ebp|esp)\b/i.test(operands))
274
+ return 4;
275
+ if (/\b(r\d+|rax|rbx|rcx|rdx|rsi|rdi|rbp|rsp)\b/i.test(operands))
276
+ return 8;
277
+ if (/\bxmm\d+\b/i.test(operands))
278
+ return 16;
279
+ if (/\bymm\d+\b/i.test(operands))
280
+ return 32;
281
+ const suffix = opcode.match(/(b|w|d|q)$/i);
282
+ if (suffix) {
283
+ switch (suffix[1].toLowerCase()) {
284
+ case 'b':
285
+ return 1;
286
+ case 'w':
287
+ return 2;
288
+ case 'd':
289
+ return 4;
290
+ case 'q':
291
+ return 8;
292
+ }
293
+ }
294
+ return 0;
295
+ }
296
+ /* —— ARM(A32 / Thumb)—— */
297
+ function convertArm(raw, arch, info, mnemonic, operands) {
298
+ // ARM 的条件码是助记符后缀,且**不是**所有后缀都是条件码
299
+ // (例如 `b` 是跳转、`bl` 是调用、`blx`、`bx`、`cbz` 这些带 b/l/x 的都不是条件码)。
300
+ const { base, cond } = splitArmCondition(mnemonic);
301
+ const isJump = ARM_JUMP_BASES.has(base) && base !== 'bl' && base !== 'blx';
302
+ const isCall = base === 'bl' || base === 'blx';
303
+ const isRet = detectArmReturn(base, cond, operands);
304
+ const isConditionalJump = isJump && cond !== '' && cond !== 'al';
305
+ const dataSize = inferArmDataSize(operands, base);
306
+ const isFloat = ARM_FLOAT32_HINT.test(operands) && !ARM_FLOAT64_HINT.test(operands);
307
+ const isFloat64 = ARM_FLOAT64_HINT.test(operands);
308
+ const parameterValue = extractDisplacement(operands);
309
+ return {
310
+ address: raw.address,
311
+ prefix: '',
312
+ prefixSize: 0,
313
+ opcode: mnemonic,
314
+ suffix: cond ? `.${cond}` : '',
315
+ parameters: operands,
316
+ description: '',
317
+ bytes: splitBytes(raw.bytes),
318
+ separators: [0],
319
+ separatorCount: 0,
320
+ modrmValueType: parameterValue !== null ? DisasmValueType.Address : DisasmValueType.None,
321
+ modrmValue: parameterValue ?? 0n,
322
+ parameterValueType: parameterValue !== null ? DisasmValueType.Value : DisasmValueType.None,
323
+ parameterValue: parameterValue ?? 0n,
324
+ dataSize,
325
+ isFloat,
326
+ isFloat64,
327
+ hasSib: false,
328
+ sibIndex: -1,
329
+ sibScaler: 0,
330
+ isJump,
331
+ isCall,
332
+ isRet,
333
+ isRep: false,
334
+ isConditionalJump,
335
+ conditionCode: cond,
336
+ // ARM 无 RIP 相对寻址;PC 相对字面量由 Capstone 直接给出绝对地址,故记 -1
337
+ ripRelative: -1,
338
+ is64Bit: info.is64Bit,
339
+ arch,
340
+ disassemblerClass: arch === 'thumb' ? DisassemblerClass.Thumb : DisassemblerClass.Arm,
341
+ mnemonic,
342
+ size: raw.size,
343
+ };
344
+ }
345
+ /**
346
+ * 拆出 ARM 的条件后缀。
347
+ *
348
+ * 难点:`bl`、`bx`、`blx`、`cbz` 里的 `b`/`l`/`x`/`z` 不是条件码。
349
+ * 做法是先从**已知的条件后缀表**里匹配去掉后缀后的基名,
350
+ * 并且要求基名非空。
351
+ */
352
+ function splitArmCondition(mnemonic) {
353
+ // 先从完整助记符里试去掉条件后缀
354
+ for (const cond of ARM_CONDITION_SUFFIXES) {
355
+ if (mnemonic.length > cond.length && mnemonic.endsWith(cond)) {
356
+ const base = mnemonic.slice(0, -cond.length);
357
+ // 排除把 `bx`/`bl`/`cbz` 误拆的情况:基名必须以字母结尾且不在已知非条件集合里
358
+ if (base.length > 0 && !NON_CONDITION_BASES.has(base)) {
359
+ return { base, cond };
360
+ }
361
+ }
362
+ }
363
+ return { base: mnemonic, cond: '' };
364
+ }
365
+ /**
366
+ * 这些基名的结尾字母看起来像条件码,但**拆掉后剩下的基名**是指令本身的一部分,
367
+ * 不能被 `splitArmCondition` 当成条件后缀处理。
368
+ *
369
+ * 注意:**不能把 `b` 放进来**。`b` 是合法的跳转基名,
370
+ * `beq` 正是 `b` + 条件码 `eq`,把它排除会导致 `beq`/`bne` 全都被当成未知指令
371
+ * (isJump=false、条件码为空)—— 这是踩过的 bug。
372
+ * 这里列的是"拆完之后不该被接受"的基名,例如 `bxq` 拆成 `bx`+`q` 要拒绝。
373
+ */
374
+ const NON_CONDITION_BASES = new Set([
375
+ 'bl', 'bx', 'blx', 'cbz', 'cbnz', 'tbb', 'tbh',
376
+ 'add', 'sub', 'and', 'orr', 'eor', 'mul', 'mla', 'mls',
377
+ 'ldr', 'ldrb', 'ldrh', 'ldrsb', 'ldrsh', 'ldrd',
378
+ 'str', 'strb', 'strh', 'strd',
379
+ 'push', 'pop', 'svc', 'nop', 'bkpt', 'clz', 'rev', 'sbfx', 'ubfx',
380
+ ]);
381
+ /**
382
+ * 判断 ARM 指令是否为返回。
383
+ *
384
+ * ARM 没有 x86 那样的 `ret`,返回是靠三种惯用写法:
385
+ * - `bx lr` / `bx<cond> lr`
386
+ * - `pop {..., pc}`(函数尾弹栈返回)
387
+ * - `mov pc, lr`
388
+ */
389
+ function detectArmReturn(base, cond, operands) {
390
+ const full = cond ? `${base}${cond}` : base;
391
+ if (full === 'bx' && /(^|[\s,{])lr([\s,}]|$)/i.test(operands))
392
+ return true;
393
+ if (full === 'mov' && /^\s*pc\s*,\s*lr\s*$/i.test(operands))
394
+ return true;
395
+ if (full === 'pop' && /\bpc\b/i.test(operands))
396
+ return true;
397
+ if (base === 'ldm' && /\bpc\b/i.test(operands))
398
+ return true;
399
+ return false;
400
+ }
401
+ /** 从操作数推断 ARM 数据宽度 */
402
+ function inferArmDataSize(operands, base) {
403
+ // 加载/存储指令的宽度由助记符后缀决定(arm 的惯例)
404
+ if (/^(ldr|str)b$/.test(base))
405
+ return 1;
406
+ if (/^(ldr|str)h$/.test(base))
407
+ return 2;
408
+ if (/^(ldr|str)sb$/.test(base))
409
+ return 1;
410
+ if (/^(ldr|str)sh$/.test(base))
411
+ return 2;
412
+ if (/^(ldr|str)d$/.test(base))
413
+ return 8;
414
+ // ldm/stm 是块操作,没有单一宽度
415
+ if (/^(ldm|stm|push|pop)/.test(base))
416
+ return 0;
417
+ // 浮点寄存器宽度
418
+ if (/\bs\d+\b/i.test(operands))
419
+ return 4;
420
+ if (/\bd\d+\b/i.test(operands))
421
+ return 8;
422
+ if (/\bq\d+\b/i.test(operands))
423
+ return 16;
424
+ // 普通寄存器
425
+ if (/\b(r\d+|sp|lr|pc)\b/i.test(operands))
426
+ return 4;
427
+ // 纯立即数运算(如 `add r0, r0, #1`):按目标寄存器走,上面已覆盖
428
+ return 0;
429
+ }
430
+ /* —— ARM64(AArch64)—— */
431
+ function convertArm64(raw, arch, info, mnemonic, operands) {
432
+ // Capstone 输出 `b.eq` 这种带点后缀;为了与 x86 的 jcc 形态统一,
433
+ // 这里拆成 opcode='b' + suffix='.eq',并据此标记条件跳转。
434
+ let base = mnemonic;
435
+ let suffix = '';
436
+ const dot = mnemonic.indexOf('.');
437
+ if (dot > 0) {
438
+ base = mnemonic.slice(0, dot);
439
+ suffix = mnemonic.slice(dot);
440
+ }
441
+ const condMatch = suffix.match(ARM64_COND_SUFFIX_RE);
442
+ const cond = condMatch ? condMatch[1] : '';
443
+ const isJump = ARM64_UNCONDITIONAL_JUMPS.has(base) ||
444
+ ARM64_CONDITIONAL_JUMPS.has(base) ||
445
+ (base === 'b' && cond !== '');
446
+ const isConditionalJump = ARM64_CONDITIONAL_JUMPS.has(base) || (base === 'b' && cond !== '' && cond !== 'al');
447
+ const isCall = ARM64_CALLS.has(base);
448
+ const isRet = ARM64_RETURNS.has(base);
449
+ const dataSize = inferArm64DataSize(operands, base);
450
+ const isFloat = ARM_FLOAT32_HINT.test(operands) && !ARM_FLOAT64_HINT.test(operands);
451
+ const isFloat64 = ARM_FLOAT64_HINT.test(operands);
452
+ // ARM64 的 PC 相对:adrp / adr / ldr xN,[pc,...] / b / bl
453
+ const isPcRelative = base === 'adrp' ||
454
+ base === 'adr' ||
455
+ base === 'b' ||
456
+ base === 'bl' ||
457
+ /\bpc\b/i.test(operands);
458
+ const parameterValue = extractDisplacement(operands);
459
+ return {
460
+ address: raw.address,
461
+ prefix: '',
462
+ prefixSize: 0,
463
+ opcode: base,
464
+ suffix,
465
+ parameters: operands,
466
+ description: '',
467
+ bytes: splitBytes(raw.bytes),
468
+ separators: [0],
469
+ separatorCount: 0,
470
+ modrmValueType: parameterValue !== null ? DisasmValueType.Address : DisasmValueType.None,
471
+ modrmValue: parameterValue ?? 0n,
472
+ parameterValueType: parameterValue !== null ? DisasmValueType.Value : DisasmValueType.None,
473
+ parameterValue: parameterValue ?? 0n,
474
+ dataSize,
475
+ isFloat,
476
+ isFloat64,
477
+ hasSib: false,
478
+ sibIndex: -1,
479
+ sibScaler: 0,
480
+ isJump,
481
+ isCall,
482
+ isRet,
483
+ isRep: false,
484
+ isConditionalJump,
485
+ conditionCode: cond,
486
+ // PC 相对的目标由 Capstone 直接给出绝对地址;下标按"操作数里出现 pc/相对"记 0
487
+ ripRelative: isPcRelative ? 0 : -1,
488
+ is64Bit: info.is64Bit,
489
+ arch,
490
+ disassemblerClass: DisassemblerClass.Arm64,
491
+ mnemonic,
492
+ size: raw.size,
493
+ };
494
+ }
495
+ /** 从操作数推断 ARM64 数据宽度 */
496
+ function inferArm64DataSize(operands, base) {
497
+ // 浮点/SIMD 寄存器优先(`s0`/`d0`/`q0`/`v0`)
498
+ if (/\bs\d+\b/i.test(operands))
499
+ return 4;
500
+ if (/\bd\d+\b/i.test(operands))
501
+ return 8;
502
+ if (/\bq\d+\b/i.test(operands))
503
+ return 16;
504
+ if (/\bv\d+\.\d*b\b/i.test(operands))
505
+ return 8;
506
+ // 栈对操作 stp/ldp 的宽度由寄存器前缀决定
507
+ if (/\b[xw]\d+\b/i.test(operands)) {
508
+ // 取第一个寄存器判断
509
+ const m = operands.match(/\b([xw])(\d+)\b/i);
510
+ if (m)
511
+ return m[1].toLowerCase() === 'x' ? 8 : 4;
512
+ }
513
+ if (/\b(w|x)zr\b/i.test(operands))
514
+ return operands.includes('xzr') ? 8 : 4;
515
+ if (/\bsp\b/i.test(operands)) {
516
+ // `stp x29, x30, [sp, #-16]!` 之类:看另一个寄存器
517
+ if (/\bx\d+\b/i.test(operands))
518
+ return 8;
519
+ if (/\bw\d+\b/i.test(operands))
520
+ return 4;
521
+ }
522
+ // 无寄存器信息的加载/存储(少见):按 ldrb/strb 之类后缀猜
523
+ if (/b$/.test(base) && /^(ldr|str)/.test(base))
524
+ return 1;
525
+ if (/h$/.test(base) && /^(ldr|str)/.test(base))
526
+ return 2;
527
+ return 0;
528
+ }
529
+ /* ------------------------------------------------------------------ *
530
+ * 公共小工具
531
+ * ------------------------------------------------------------------ */
532
+ /** CE 的 Bytes 是"每个字节一个元素的数组",这里按字节拆开 */
533
+ function splitBytes(bytes) {
534
+ const out = [];
535
+ for (const b of bytes)
536
+ out.push(Buffer.from([b]));
537
+ return out;
538
+ }
539
+ /**
540
+ * 计算字节分隔点。
541
+ *
542
+ * x86:CE 用 `Seperators` 记录 前缀/操作码/modrm/sib/额外 的边界下标。
543
+ * 本移植没有完整编码结构,给出**保守**分界(单调递增、不越界),界面分段不会错位。
544
+ * ARM/ARM64:指令是整体编码,只给 `[0]`。
545
+ */
546
+ function computeSeparators(size, prefix, hasSib) {
547
+ const prefixBytes = prefix ? (prefix.length > 3 ? 2 : 1) : 0;
548
+ const seps = [0];
549
+ if (prefixBytes > 0 && prefixBytes < size)
550
+ seps.push(prefixBytes);
551
+ const opcodeAt = Math.min(prefixBytes + 1, size);
552
+ if (!seps.includes(opcodeAt))
553
+ seps.push(opcodeAt);
554
+ if (hasSib) {
555
+ const sibAt = Math.min(size - 1, opcodeAt + 1);
556
+ if (sibAt > seps[seps.length - 1])
557
+ seps.push(sibAt);
558
+ }
559
+ return seps;
560
+ }
561
+ /**
562
+ * 找出 RIP 相对操作数在字节数组里的下标;不是则返回 -1。
563
+ *
564
+ * 说明:Capstone 的 MODRM 字节位置随前缀长度变化,本移植不做完整指令编码解析,
565
+ * 因此返回"相对指令起点"的近似下标(x64 下 RIP 相对的 ModRM 通常紧跟操作码)。
566
+ */
567
+ function detectRipRelative(operands) {
568
+ if (operands.toLowerCase().indexOf('rip') < 0)
569
+ return -1;
570
+ return 3;
571
+ }
572
+ /**
573
+ * 抽取操作数里的位移/绝对地址值(十六进制 `0x...`、十进制,或 `#0x...`)。
574
+ *
575
+ * ARM 的立即数写作 `#1` / `#0x10`,偏移写作 `[sp, #-16]`,这里都覆盖。
576
+ */
577
+ function extractDisplacement(operands) {
578
+ // 优先取括号内的值(内存操作数)
579
+ const inBracket = operands.match(/\[[^\]]*?(#?-?(?:0x)?[0-9a-f]+)\s*\]?/i);
580
+ if (inBracket) {
581
+ const v = parseNumeric(inBracket[1]);
582
+ if (v !== null)
583
+ return v;
584
+ }
585
+ // ARM 的立即数 `#0x1234`
586
+ const hash = operands.match(/#(-?(?:0x)?[0-9a-f]+)\b/i);
587
+ if (hash) {
588
+ const v = parseNumeric(hash[1]);
589
+ if (v !== null)
590
+ return v;
591
+ }
592
+ // 跳转/调用的目标 `0x...`
593
+ const target = operands.match(/\b(0x[0-9a-f]+)\b/i);
594
+ if (target)
595
+ return parseNumeric(target[1]);
596
+ return null;
597
+ }
598
+ function parseNumeric(text) {
599
+ const t = text.replace(/^#/, '').trim();
600
+ try {
601
+ if (/^-?0x[0-9a-f]+$/i.test(t)) {
602
+ const neg = t.startsWith('-');
603
+ const v = BigInt(neg ? t.slice(1) : t);
604
+ return neg ? -v : v;
605
+ }
606
+ if (/^-?\d+$/.test(t))
607
+ return BigInt(t);
608
+ return null;
609
+ }
610
+ catch {
611
+ return null;
612
+ }
613
+ }
614
+ /* ------------------------------------------------------------------ *
615
+ * 格式化(CE 风格)
616
+ * ------------------------------------------------------------------ */
617
+ /** 把字节数组格式化成 `48 8B 05 10 20 30 40`(大写、空格分隔) */
618
+ function formatBytes(bytes) {
619
+ const arr = Buffer.isBuffer(bytes) ? [bytes] : bytes;
620
+ return arr
621
+ .map((b) => b.toString('hex').toUpperCase().padStart(2, '0'))
622
+ .join(' ');
623
+ }
624
+ /**
625
+ * 格式化为 CE 风格的完整行:`地址 - 字节 - 操作码 参数`。
626
+ *
627
+ * 对齐 `disassembler.pas` 里 binutils 分支的写法。注意 CE 在字节与 ` - `
628
+ * 之间**多一个空格**(逐字节拼接时末尾留了空格),本函数保留这一细节。
629
+ */
630
+ function formatDisassembled(insn, options = {}) {
631
+ const width = options.addressWidth ?? (0, backend_1.architectureInfo)(insn.arch).addressWidth;
632
+ const parts = [];
633
+ if (options.showAddress !== false) {
634
+ parts.push(insn.address.toString(16).toUpperCase().padStart(width, '0'));
635
+ }
636
+ if (options.showBytes !== false) {
637
+ const bytes = formatBytes(insn.bytes);
638
+ parts.push(bytes.length > 0 ? bytes + ' ' : '');
639
+ }
640
+ const head = insn.suffix && insn.suffix.startsWith('.') && insn.opcode === 'b'
641
+ ? `${insn.opcode}${insn.suffix}`
642
+ : insn.opcode;
643
+ const text = insn.parameters ? `${head} ${insn.parameters}` : head;
644
+ parts.push(text);
645
+ return parts.join(' - ');
646
+ }
647
+ /**
648
+ * 取出一条指令的**完整文本**(含前缀与后缀),可用于重新汇编。
649
+ *
650
+ * 为什么需要单独的函数:
651
+ * - x86:CE 把 `rep`/`lock` 前缀单独存在 `prefix` 字段,
652
+ * 只用 `opcode + parameters` 会丢掉 `F3`/`F2`。
653
+ * - ARM:条件码在助记符里,本模块已把它并回 `opcode`,
654
+ * 但 ARM64 的 `.eq` 后缀需要补上。
655
+ */
656
+ function instructionText(insn) {
657
+ let head;
658
+ if (insn.arch === 'arm64' && insn.suffix) {
659
+ head = `${insn.opcode}${insn.suffix}`;
660
+ }
661
+ else if (insn.prefix) {
662
+ head = `${insn.prefix} ${insn.opcode}`;
663
+ }
664
+ else {
665
+ head = insn.opcode;
666
+ }
667
+ return insn.parameters ? `${head} ${insn.parameters}` : head;
668
+ }
669
+ /**
670
+ * 把 CE 风格的整行拆回各段(对齐 `disassembler.pas` 的 `splitDisassembledString`)。
671
+ */
672
+ function splitDisassembledString(disassembled) {
673
+ const parts = disassembled.split(' - ');
674
+ if (parts.length >= 3) {
675
+ const [address, bytes, ...rest] = parts;
676
+ const text = rest.join(' - ').trim();
677
+ const sp = text.indexOf(' ');
678
+ return {
679
+ address: address.trim(),
680
+ bytes: bytes.trim(),
681
+ opcode: sp >= 0 ? text.slice(0, sp) : text,
682
+ parameters: sp >= 0 ? text.slice(sp + 1).trim() : '',
683
+ };
684
+ }
685
+ const text = disassembled.trim();
686
+ const sp = text.indexOf(' ');
687
+ return {
688
+ address: '',
689
+ bytes: '',
690
+ opcode: sp >= 0 ? text.slice(0, sp) : text,
691
+ parameters: sp >= 0 ? text.slice(sp + 1).trim() : '',
692
+ };
693
+ }
694
+ /**
695
+ * 计算跳转/调用的目标地址(非流程指令返回 null)。
696
+ *
697
+ * 覆盖形态:
698
+ * - x86:`0x...`
699
+ * - ARM:`#0x...`(Capstone 对 ARM 分支通常直接给绝对地址)
700
+ * - ARM64:`0x...`
701
+ */
702
+ function branchTarget(insn) {
703
+ if (!insn.isJump && !insn.isCall)
704
+ return null;
705
+ const m = insn.parameters.match(/#?(0x[0-9a-f]+)/i);
706
+ if (!m)
707
+ return null;
708
+ try {
709
+ return BigInt(m[1]);
710
+ }
711
+ catch {
712
+ return null;
713
+ }
714
+ }
715
+ //# sourceMappingURL=disassembler.js.map