@specy/x86 1.0.4 → 1.0.6
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.
- package/{src/instructions.ts → dist/index.d.ts} +1558 -1346
- package/dist/index.js +32 -0
- package/package.json +6 -3
- package/.idea/inspectionProfiles/Project_Default.xml +0 -13
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/.idea/x86-js.iml +0 -12
- package/src/index.ts +0 -443
- package/src/test.ts +0 -17
- package/src/utils.ts +0 -14
- package/src/x86/deps/capstone-x86.min.js +0 -7180
- package/src/x86/deps/keystone-x86.min.js +0 -287
- package/src/x86/deps/unicorn-x86.min.js +0 -274717
- package/tsconfig.json +0 -111
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specy/x86",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "An x86 interpreter for Javascript",
|
|
5
5
|
"homepage": "https://github.com/Specy/x86-js#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/Specy/x86-js.git"
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
13
16
|
"license": "GPL-2.0",
|
|
14
17
|
"author": "Specy",
|
|
15
18
|
"type": "module",
|
|
16
|
-
"module": "
|
|
17
|
-
"types": "
|
|
19
|
+
"module": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
18
21
|
"scripts": {
|
|
19
22
|
"build": "tsup src/index.ts --format esm --dts --clean --minify",
|
|
20
23
|
"start": "npx tsx src/index.ts",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<component name="InspectionProjectProfileManager">
|
|
2
|
-
<profile version="1.0">
|
|
3
|
-
<option name="myName" value="Project Default" />
|
|
4
|
-
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
|
5
|
-
<option name="ignoredErrors">
|
|
6
|
-
<list>
|
|
7
|
-
<option value="N801" />
|
|
8
|
-
<option value="N806" />
|
|
9
|
-
</list>
|
|
10
|
-
</option>
|
|
11
|
-
</inspection_tool>
|
|
12
|
-
</profile>
|
|
13
|
-
</component>
|
package/.idea/modules.xml
DELETED
package/.idea/vcs.xml
DELETED
package/.idea/x86-js.iml
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="WEB_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$">
|
|
5
|
-
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
6
|
-
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
7
|
-
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
-
</content>
|
|
9
|
-
<orderEntry type="inheritedJdk" />
|
|
10
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
11
|
-
</component>
|
|
12
|
-
</module>
|
package/src/index.ts
DELETED
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
//@ts-ignore
|
|
2
|
-
import {uc} from './x86/deps/unicorn-x86.min'
|
|
3
|
-
//@ts-ignore
|
|
4
|
-
import {ks} from './x86/deps/keystone-x86.min'
|
|
5
|
-
//@ts-ignore
|
|
6
|
-
import {cs} from './x86/deps/capstone-x86.min'
|
|
7
|
-
import {X86InstructionCode, X86InstructionName, X86InstructionNames} from "./instructions";
|
|
8
|
-
import {enumKeys} from "./utils";
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
X86InstructionNames,
|
|
12
|
-
X86InstructionCode,
|
|
13
|
-
X86InstructionName,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Enumeration of x86 condition flags
|
|
18
|
-
*/
|
|
19
|
-
export enum X86ConditionFlags {
|
|
20
|
-
C = 0, // Carry flag
|
|
21
|
-
Z = 1, // Zero flag
|
|
22
|
-
S = 2, // Sign flag
|
|
23
|
-
O = 3, // Overflow flag
|
|
24
|
-
P = 4, // Parity flag
|
|
25
|
-
A = 5, // Auxiliary carry flag
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Enumeration of x86 registers and their corresponding values
|
|
30
|
-
*/
|
|
31
|
-
export enum X86Register {
|
|
32
|
-
EAX = 19,
|
|
33
|
-
EBX = 21,
|
|
34
|
-
ECX = 22,
|
|
35
|
-
ESP = 30,
|
|
36
|
-
EBP = 20,
|
|
37
|
-
EDI = 23,
|
|
38
|
-
ESI = 29,
|
|
39
|
-
EDX = 24,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
/** Array of all x86 register names */
|
|
45
|
-
export const X86_REGISTERS = enumKeys(X86Register)
|
|
46
|
-
|
|
47
|
-
/** Stack pointer register identifier */
|
|
48
|
-
export const X86_STACK_POINTER_REGISTER = X86Register.ESP;
|
|
49
|
-
|
|
50
|
-
/** Program counter register identifier (EIP) */
|
|
51
|
-
export const X86_PROGRAM_COUNTER_REGISTER = 26 //X86_REG_EIP
|
|
52
|
-
|
|
53
|
-
/** Flags register identifier (EFLAGS) */
|
|
54
|
-
export const X86_FLAGS_REGISTER = 25; //X86_REG_EFLAGS
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Converts a signed 32-bit integer to an unsigned 32-bit integer
|
|
58
|
-
* @param {number} value - Signed 32-bit integer
|
|
59
|
-
* @returns {number} Unsigned 32-bit integer
|
|
60
|
-
*/
|
|
61
|
-
function signed32ToUnsigned32(value: number): number {
|
|
62
|
-
if (value < 0) {
|
|
63
|
-
return value + Math.pow(2, 32);
|
|
64
|
-
}
|
|
65
|
-
return value;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Represents a single x86 instruction
|
|
70
|
-
*/
|
|
71
|
-
export type X86Instruction = {
|
|
72
|
-
/** Memory address of the instruction */
|
|
73
|
-
address: number;
|
|
74
|
-
/** Human-readable text representation of the instruction */
|
|
75
|
-
text: string;
|
|
76
|
-
/** Mnemonic name of the instruction */
|
|
77
|
-
name: string;
|
|
78
|
-
/** Instruction identifier */
|
|
79
|
-
id: X86InstructionCode;
|
|
80
|
-
/** Raw bytes of the instruction */
|
|
81
|
-
bytes: number[];
|
|
82
|
-
/** Line number in the source code */
|
|
83
|
-
line: number;
|
|
84
|
-
/** Size of the instruction in bytes */
|
|
85
|
-
size: number;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
//TODO
|
|
89
|
-
type HistoryEntry = {}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Calculates the required page size for memory allocation
|
|
93
|
-
* @param {number} size - Required memory size
|
|
94
|
-
* @returns {number} Padded size aligned to page boundaries (4KB)
|
|
95
|
-
*/
|
|
96
|
-
function calculatePageSize(size: number): number {
|
|
97
|
-
const pageSize = 4096; // 4KB
|
|
98
|
-
return Math.ceil(size / pageSize) * pageSize;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Simplifies error objects to string messages
|
|
103
|
-
* @param {any} e - Error object or string
|
|
104
|
-
* @returns {string} Simplified error message
|
|
105
|
-
*/
|
|
106
|
-
function simplifyError(e: any) {
|
|
107
|
-
return typeof e === 'string' ? e : e.message
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* X86 assembly interpreter using Unicorn, Keystone, and Capstone engines
|
|
112
|
-
*/
|
|
113
|
-
export class X86Interpreter {
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Creates a new X86Interpreter instance
|
|
117
|
-
* @param {string} code - Assembly code to interpret
|
|
118
|
-
* @param {any} assembler - Keystone assembler instance
|
|
119
|
-
* @param {any} interpreter - Unicorn interpreter instance
|
|
120
|
-
* @param {any} disambler - Capstone disassembler instance
|
|
121
|
-
*/
|
|
122
|
-
constructor(
|
|
123
|
-
private readonly code: string,
|
|
124
|
-
private readonly assembler: any,
|
|
125
|
-
private readonly interpreter: any,
|
|
126
|
-
private readonly disambler: any,
|
|
127
|
-
) {
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Starting address for the code segment */
|
|
131
|
-
static START_ADDRESS = 0x1000;
|
|
132
|
-
/** Starting address for the stack segment */
|
|
133
|
-
static STACK_START_ADDRESS = 0xf0000;
|
|
134
|
-
/** Size of the stack segment */
|
|
135
|
-
static STACK_SIZE = 0xf0000;
|
|
136
|
-
/** Buffer containing assembled code bytes */
|
|
137
|
-
private codeBuffer: number[] | null = null;
|
|
138
|
-
/** Array of disassembled instructions */
|
|
139
|
-
private instructions: X86Instruction[] = []
|
|
140
|
-
/** Address following the last instruction */
|
|
141
|
-
private endAddress: number = 0;
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Hook callback for the interpreter
|
|
145
|
-
* @param {any} handle - Hook handle
|
|
146
|
-
* @param {number} addr_lo - Lower address bound
|
|
147
|
-
* @param {number} addr_hi - Higher address bound
|
|
148
|
-
* @param {number} size - Size of the region
|
|
149
|
-
* @param {any} user_data - User data
|
|
150
|
-
*/
|
|
151
|
-
//@ts-ignore
|
|
152
|
-
private hook = (handle, addr_lo, addr_hi, size, user_data) => {
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Gets the disassembled instructions from the code buffer
|
|
158
|
-
* @returns {any[]} Array of disassembled instructions
|
|
159
|
-
* @throws {Error} If disassembly fails
|
|
160
|
-
*/
|
|
161
|
-
private getInstructions() {
|
|
162
|
-
try {
|
|
163
|
-
return this.disambler.disasm(this.codeBuffer, X86Interpreter.START_ADDRESS) as any[]
|
|
164
|
-
} catch (e) {
|
|
165
|
-
throw new Error(simplifyError(e));
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Assembles the code and prepares for interpretation
|
|
171
|
-
* @throws {Error} If assembly fails
|
|
172
|
-
*/
|
|
173
|
-
assemble() {
|
|
174
|
-
try {
|
|
175
|
-
this.codeBuffer = this.assembler.asm(this.code) as number[]
|
|
176
|
-
const instructions = this.getInstructions();
|
|
177
|
-
this.instructions = instructions.map((ins, i) => {
|
|
178
|
-
return {
|
|
179
|
-
address: ins.address,
|
|
180
|
-
bytes: ins.bytes,
|
|
181
|
-
size: ins.size,
|
|
182
|
-
id: ins.id,
|
|
183
|
-
text: `${ins.mnemonic} ${ins.op_str}`,
|
|
184
|
-
name: ins.mnemonic,
|
|
185
|
-
line: i,
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
this.endAddress = X86Interpreter.START_ADDRESS + this.codeBuffer.length;
|
|
189
|
-
} catch (e) {
|
|
190
|
-
throw new Error(simplifyError(e));
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Initializes memory and registers for the interpreter
|
|
196
|
-
* @throws {Error} If code buffer is not initialized
|
|
197
|
-
*/
|
|
198
|
-
initialize() {
|
|
199
|
-
if (!this.codeBuffer) {
|
|
200
|
-
throw new Error('Code buffer is not initialized. Please call assemble() first.');
|
|
201
|
-
}
|
|
202
|
-
this.interpreter.mem_map(X86Interpreter.START_ADDRESS, calculatePageSize(this.codeBuffer.length), uc.PROT_ALL);
|
|
203
|
-
this.interpreter.mem_write(X86Interpreter.START_ADDRESS, this.codeBuffer);
|
|
204
|
-
this.interpreter.mem_map(X86Interpreter.STACK_START_ADDRESS, calculatePageSize(X86Interpreter.STACK_SIZE), uc.PROT_ALL);
|
|
205
|
-
this.interpreter.reg_write_i32(X86Register.ESP, X86Interpreter.STACK_START_ADDRESS + X86Interpreter.STACK_SIZE);
|
|
206
|
-
this.interpreter.reg_write_i32(X86Register.EBP, X86Interpreter.STACK_START_ADDRESS + X86Interpreter.STACK_SIZE);
|
|
207
|
-
this.interpreter.reg_write_i32(X86_PROGRAM_COUNTER_REGISTER, X86Interpreter.START_ADDRESS);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Simulates the execution of the code
|
|
212
|
-
* @param {number} [limit] - Optional instruction execution limit
|
|
213
|
-
* @throws {Error} If simulation fails
|
|
214
|
-
*/
|
|
215
|
-
simulate(limit?: number) {
|
|
216
|
-
try {
|
|
217
|
-
//TODO add tracing
|
|
218
|
-
const address = this.getProgramCounter();
|
|
219
|
-
this.interpreter.emu_start(address, this.endAddress, 0, limit);
|
|
220
|
-
} catch (e) {
|
|
221
|
-
throw new Error(simplifyError(e));
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Simulates execution with specified breakpoints
|
|
227
|
-
* @param {number[]} breakpoints - Array of addresses to break at
|
|
228
|
-
* @param {number} [limit] - Optional instruction execution limit
|
|
229
|
-
* @throws {Error} If simulation fails
|
|
230
|
-
*/
|
|
231
|
-
simulateWithBreakpoints(breakpoints: number[], limit?: number) {
|
|
232
|
-
const breakpointsMap = new Map(breakpoints.map((bp) => [bp, true]));
|
|
233
|
-
const handle = this.interpreter.hook_add(uc.HOOK_CODE, (_: unknown, address: number) => {
|
|
234
|
-
if (breakpointsMap.has(address)) {
|
|
235
|
-
this.interpreter.emu_stop();
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
try {
|
|
239
|
-
this.simulate(limit);
|
|
240
|
-
} catch (e) {
|
|
241
|
-
throw new Error(simplifyError(e));
|
|
242
|
-
} finally {
|
|
243
|
-
this.interpreter.hook_del(handle);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Executes a single instruction
|
|
249
|
-
*/
|
|
250
|
-
step() {
|
|
251
|
-
this.simulate(1);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Gets the value of a register
|
|
256
|
-
* @param {X86Register} register - Register to read
|
|
257
|
-
* @returns {number} Unsigned 32-bit register value
|
|
258
|
-
* @throws {Error} If reading the register fails
|
|
259
|
-
*/
|
|
260
|
-
getRegisterValue(register: X86Register) {
|
|
261
|
-
try {
|
|
262
|
-
const signed = this.interpreter.reg_read_i32(register);
|
|
263
|
-
return signed32ToUnsigned32(signed);
|
|
264
|
-
} catch (e) {
|
|
265
|
-
throw new Error(simplifyError(e));
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Sets the value of a register
|
|
271
|
-
* @param {X86Register} register - Register to write
|
|
272
|
-
* @param {number} value - Value to set
|
|
273
|
-
* @throws {Error} If writing to the register fails
|
|
274
|
-
*/
|
|
275
|
-
setRegisterValue(register: X86Register, value: number) {
|
|
276
|
-
try {
|
|
277
|
-
this.interpreter.reg_write_i32(register, value);
|
|
278
|
-
} catch (e) {
|
|
279
|
-
throw new Error(simplifyError(e));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Gets the current stack pointer value
|
|
285
|
-
* @returns {number} Stack pointer value
|
|
286
|
-
*/
|
|
287
|
-
getStackPointer() {
|
|
288
|
-
return this.getRegisterValue(X86_STACK_POINTER_REGISTER);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Gets the current program counter value
|
|
293
|
-
* @returns {number} Program counter value
|
|
294
|
-
*/
|
|
295
|
-
getProgramCounter() {
|
|
296
|
-
return this.getRegisterValue(X86_PROGRAM_COUNTER_REGISTER as X86Register);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Gets the values of all registers
|
|
301
|
-
* @returns {number[]} Array of register values
|
|
302
|
-
*/
|
|
303
|
-
getRegistersValues(): number[] {
|
|
304
|
-
return X86_REGISTERS.map((reg) => {
|
|
305
|
-
return this.getRegisterValue(X86Register[reg as keyof typeof X86Register]);
|
|
306
|
-
})
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Reads bytes from memory
|
|
311
|
-
* @param {number} address - Memory address to read from
|
|
312
|
-
* @param {number} length - Number of bytes to read
|
|
313
|
-
* @returns {number[]} Array of byte values
|
|
314
|
-
* @throws {Error} If reading memory fails
|
|
315
|
-
*/
|
|
316
|
-
readMemoryBytes(address: number, length: number) {
|
|
317
|
-
try {
|
|
318
|
-
return [...this.interpreter.mem_read(address, length)];
|
|
319
|
-
} catch (e) {
|
|
320
|
-
throw new Error(simplifyError(e));
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Writes bytes to memory
|
|
326
|
-
* @param {number} address - Memory address to write to
|
|
327
|
-
* @param {number[]} bytes - Array of byte values to write
|
|
328
|
-
*/
|
|
329
|
-
setMemoryBytes(address: number, bytes: number[]) {
|
|
330
|
-
try {
|
|
331
|
-
this.interpreter.mem_write(address, bytes);
|
|
332
|
-
} catch (e) {
|
|
333
|
-
// Error handling is missing here
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Gets the next instruction to be executed
|
|
339
|
-
* @returns {X86Instruction | null} Next instruction or null if no more instructions
|
|
340
|
-
*/
|
|
341
|
-
getNextStatement(): X86Instruction | null {
|
|
342
|
-
const pc = this.getProgramCounter();
|
|
343
|
-
const instruction = this.instructions.find((ins) => ins.address === pc);
|
|
344
|
-
return instruction ?? null;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Checks if the execution has terminated (no more instructions)
|
|
349
|
-
* @returns {boolean} True if execution is terminated
|
|
350
|
-
*/
|
|
351
|
-
isTerminated() {
|
|
352
|
-
return this.getNextStatement() === null;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Gets the current state of all condition flags
|
|
357
|
-
* @returns {Record<X86ConditionFlags, number>} Object mapping flags to their values (0 or 1)
|
|
358
|
-
*/
|
|
359
|
-
getConditionFlags(): Record<X86ConditionFlags, number> {
|
|
360
|
-
const flags = this.interpreter.reg_read_i32(X86_FLAGS_REGISTER) as number;
|
|
361
|
-
return {
|
|
362
|
-
[X86ConditionFlags.C]: (flags >> 0) & 1,
|
|
363
|
-
[X86ConditionFlags.P]: (flags >> 2) & 1,
|
|
364
|
-
[X86ConditionFlags.A]: (flags >> 4) & 1,
|
|
365
|
-
[X86ConditionFlags.Z]: (flags >> 6) & 1,
|
|
366
|
-
[X86ConditionFlags.S]: (flags >> 7) & 1,
|
|
367
|
-
[X86ConditionFlags.O]: (flags >> 11) & 1,
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* Gets the instruction at a specific address
|
|
373
|
-
* @param {number} address - Memory address
|
|
374
|
-
* @returns {X86Instruction | null} Instruction at the address or null if not found
|
|
375
|
-
*/
|
|
376
|
-
getStatementAtAddress(address: number): X86Instruction | null {
|
|
377
|
-
const instruction = this.instructions.find((ins) => ins.address === address);
|
|
378
|
-
return instruction ?? null;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Gets the source code as a string
|
|
383
|
-
* @returns {string} Source code
|
|
384
|
-
*/
|
|
385
|
-
getSource() {
|
|
386
|
-
this.instructions.map((ins) => ins.text).join('/n')
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Gets the instruction at a specific source line
|
|
391
|
-
* @param {number} lineIndex - Line index in the source code
|
|
392
|
-
* @returns {X86Instruction | null} Instruction at the line or null if not found
|
|
393
|
-
*/
|
|
394
|
-
getStatementAtSourceLine(lineIndex: number): X86Instruction | null {
|
|
395
|
-
return this.instructions[lineIndex] ?? null;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/*
|
|
399
|
-
getCallStack() {
|
|
400
|
-
throw new Error('Method not implemented.');
|
|
401
|
-
}
|
|
402
|
-
*/
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Gets all disassembled instructions
|
|
406
|
-
* @returns {X86Instruction[]} Array of all instructions
|
|
407
|
-
*/
|
|
408
|
-
getAssembledStatements() {
|
|
409
|
-
return this.instructions
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Creates a new X86Interpreter instance with the provided assembly code
|
|
414
|
-
* @param {string} code - Assembly code to interpret
|
|
415
|
-
* @returns {X86Interpreter} New interpreter instance
|
|
416
|
-
* @throws {Error} If creating the interpreter fails
|
|
417
|
-
*/
|
|
418
|
-
static create(code: string) {
|
|
419
|
-
try {
|
|
420
|
-
const keystone = new ks.Keystone(ks.ARCH_X86, ks.MODE_32);
|
|
421
|
-
keystone.option(ks.OPT_SYNTAX, ks.OPT_SYNTAX_INTEL);
|
|
422
|
-
const interpreter = new X86Interpreter(
|
|
423
|
-
code,
|
|
424
|
-
keystone,
|
|
425
|
-
new uc.Unicorn(uc.ARCH_X86, uc.MODE_32),
|
|
426
|
-
//@ts-ignore
|
|
427
|
-
new cs.Capstone(cs.ARCH_X86, cs.MODE_32)
|
|
428
|
-
)
|
|
429
|
-
return interpreter;
|
|
430
|
-
} catch (e) {
|
|
431
|
-
throw new Error(simplifyError(e));
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Releases resources used by the interpreter
|
|
437
|
-
*/
|
|
438
|
-
dispose() {
|
|
439
|
-
this.interpreter.close();
|
|
440
|
-
this.assembler.close()
|
|
441
|
-
this.disambler.close();
|
|
442
|
-
}
|
|
443
|
-
}
|
package/src/test.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import {X86Interpreter, X86Register} from '../dist/index.js'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const interpreter = X86Interpreter.create(`
|
|
5
|
-
mov eax, 0
|
|
6
|
-
mov edx, 1
|
|
7
|
-
mov ecx, 30
|
|
8
|
-
xadd ecx, edx
|
|
9
|
-
`)
|
|
10
|
-
|
|
11
|
-
interpreter.assemble()
|
|
12
|
-
interpreter.initialize()
|
|
13
|
-
interpreter.simulate()
|
|
14
|
-
|
|
15
|
-
console.log(interpreter.getRegisterValue(X86Register.ECX))
|
|
16
|
-
|
|
17
|
-
|
package/src/utils.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns the keys of an enum
|
|
6
|
-
* @template T - Enum type
|
|
7
|
-
* @param {T} e - Enum object
|
|
8
|
-
* @returns {string[]} Array of enum keys
|
|
9
|
-
*/
|
|
10
|
-
export function enumKeys<T extends object>(e: T) {
|
|
11
|
-
const keys = Object.keys(e)
|
|
12
|
-
const isStringEnum = isNaN(Number(keys[0]))
|
|
13
|
-
return isStringEnum ? keys : keys.slice(keys.length / 2)
|
|
14
|
-
}
|