hexcore-unicorn 1.2.0

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.
@@ -0,0 +1,379 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ #ifndef UNICORN_WRAPPER_H
6
+ #define UNICORN_WRAPPER_H
7
+
8
+ #include <napi.h>
9
+ #include <unicorn/unicorn.h>
10
+ #include <unordered_map>
11
+ #include <unordered_set>
12
+ #include <map>
13
+ #include <memory>
14
+ #include <vector>
15
+ #include <mutex>
16
+ #include <atomic>
17
+
18
+ // Forward declarations
19
+ struct HookData;
20
+ class UnicornContext;
21
+
22
+ /**
23
+ * UnicornWrapper - N-API wrapper for Unicorn Engine
24
+ *
25
+ * HikariSystem HexCore - Unicorn Emulator Bindings
26
+ * Provides CPU emulation capabilities with hook support
27
+ */
28
+ class UnicornWrapper : public Napi::ObjectWrap<UnicornWrapper> {
29
+ public:
30
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
31
+ static Napi::FunctionReference constructor;
32
+
33
+ UnicornWrapper(const Napi::CallbackInfo& info);
34
+ ~UnicornWrapper();
35
+
36
+ // Get the engine handle (for internal use)
37
+ uc_engine* GetEngine() const { return engine_; }
38
+ bool IsClosed() const { return closed_; }
39
+
40
+ private:
41
+ uc_engine* engine_;
42
+ uc_arch arch_;
43
+ uc_mode mode_;
44
+ bool closed_;
45
+ std::atomic<bool> emulating_;
46
+ std::mutex hookMutex_;
47
+
48
+ // Map of active hooks: hook handle -> HookData
49
+ std::unordered_map<uc_hook, std::unique_ptr<HookData>> hooks_;
50
+ uc_hook nextHookId_;
51
+
52
+ // Native Breakpoints
53
+ std::unordered_set<uint64_t> breakpoints_;
54
+ uc_hook breakpointHookHandle_ = 0;
55
+ bool hasBreakpointHook_ = false;
56
+
57
+ // Shared Memory References (Keep buffers alive)
58
+ std::map<uint64_t, Napi::ObjectReference> mappedBuffers_;
59
+
60
+ // ============== Emulation Control ==============
61
+
62
+ /**
63
+ * Start emulation
64
+ * @param begin - Start address
65
+ * @param until - End address (0 to run until error/hook stop)
66
+ * @param timeout - Timeout in microseconds (0 for no timeout)
67
+ * @param count - Number of instructions to execute (0 for unlimited)
68
+ */
69
+ Napi::Value EmuStart(const Napi::CallbackInfo& info);
70
+
71
+ /**
72
+ * Start emulation asynchronously
73
+ * Returns a Promise that resolves when emulation completes
74
+ */
75
+ Napi::Value EmuStartAsync(const Napi::CallbackInfo& info);
76
+
77
+ /**
78
+ * Stop emulation (can be called from hooks)
79
+ */
80
+ Napi::Value EmuStop(const Napi::CallbackInfo& info);
81
+
82
+ // ============== Memory Operations ==============
83
+
84
+ /**
85
+ * Map a memory region
86
+ * @param address - Start address (must be aligned to 4KB)
87
+ * @param size - Size in bytes (must be multiple of 4KB)
88
+ * @param perms - Memory permissions (PROT.READ | PROT.WRITE | PROT.EXEC)
89
+ */
90
+ Napi::Value MemMap(const Napi::CallbackInfo& info);
91
+
92
+ /**
93
+ * Map a memory region with existing data
94
+ * @param address - Start address
95
+ * @param data - Buffer containing initial data
96
+ * @param perms - Memory permissions
97
+ */
98
+ Napi::Value MemMapPtr(const Napi::CallbackInfo& info);
99
+
100
+ /**
101
+ * Unmap a memory region
102
+ */
103
+ Napi::Value MemUnmap(const Napi::CallbackInfo& info);
104
+
105
+ /**
106
+ * Change memory permissions
107
+ */
108
+ Napi::Value MemProtect(const Napi::CallbackInfo& info);
109
+
110
+ /**
111
+ * Read memory
112
+ * @param address - Address to read from
113
+ * @param size - Number of bytes to read
114
+ * @returns Buffer containing the data
115
+ */
116
+ Napi::Value MemRead(const Napi::CallbackInfo& info);
117
+
118
+ /**
119
+ * Write memory
120
+ * @param address - Address to write to
121
+ * @param data - Buffer containing data to write
122
+ */
123
+ Napi::Value MemWrite(const Napi::CallbackInfo& info);
124
+
125
+ /**
126
+ * Get list of mapped memory regions
127
+ * @returns Array of {begin, end, perms} objects
128
+ */
129
+ Napi::Value MemRegions(const Napi::CallbackInfo& info);
130
+
131
+ // ============== Register Operations ==============
132
+
133
+ /**
134
+ * Read a register value
135
+ * @param regId - Register ID (architecture-specific)
136
+ * @returns BigInt for 64-bit values, Number for smaller
137
+ */
138
+ Napi::Value RegRead(const Napi::CallbackInfo& info);
139
+
140
+ /**
141
+ * Write a register value
142
+ * @param regId - Register ID
143
+ * @param value - Value to write (BigInt or Number)
144
+ */
145
+ Napi::Value RegWrite(const Napi::CallbackInfo& info);
146
+
147
+ /**
148
+ * Read multiple registers at once
149
+ * @param regIds - Array of register IDs
150
+ * @returns Array of values
151
+ */
152
+ Napi::Value RegReadBatch(const Napi::CallbackInfo& info);
153
+
154
+ /**
155
+ * Write multiple registers at once
156
+ * @param regIds - Array of register IDs
157
+ * @param values - Array of values
158
+ */
159
+ Napi::Value RegWriteBatch(const Napi::CallbackInfo& info);
160
+
161
+ // ============== Hook Operations ==============
162
+
163
+ /**
164
+ * Add a hook
165
+ * @param type - Hook type (HOOK.CODE, HOOK.MEM_READ, etc.)
166
+ * @param callback - JavaScript function to call
167
+ * @param begin - Start address (optional, default 1)
168
+ * @param end - End address (optional, default 0 = all addresses)
169
+ * @param extra - Extra argument for instruction hooks (optional)
170
+ * @returns Hook handle (number)
171
+ */
172
+ Napi::Value HookAdd(const Napi::CallbackInfo& info);
173
+
174
+ /**
175
+ * Remove a hook
176
+ * @param hookHandle - Handle returned by hookAdd
177
+ */
178
+ Napi::Value HookDel(const Napi::CallbackInfo& info);
179
+
180
+ // ============== Native Breakpoints ==============
181
+
182
+ /**
183
+ * Add a native breakpoint
184
+ * @param address - Address to break at
185
+ */
186
+ Napi::Value BreakpointAdd(const Napi::CallbackInfo& info);
187
+
188
+ /**
189
+ * Remove a native breakpoint
190
+ * @param address - Address to remove
191
+ */
192
+ Napi::Value BreakpointDel(const Napi::CallbackInfo& info);
193
+
194
+ // ============== Context Operations ==============
195
+
196
+ /**
197
+ * Save the current CPU context
198
+ * @returns UnicornContext object
199
+ */
200
+ Napi::Value ContextSave(const Napi::CallbackInfo& info);
201
+
202
+ /**
203
+ * Restore a previously saved context
204
+ * @param context - UnicornContext object
205
+ */
206
+ Napi::Value ContextRestore(const Napi::CallbackInfo& info);
207
+
208
+ // ============== Snapshot Operations ==============
209
+
210
+ /**
211
+ * Save full emulation state (Context + Memory)
212
+ * @return { context: Buffer, memory: [ { address, size, perms, data } ] }
213
+ */
214
+ Napi::Value StateSave(const Napi::CallbackInfo& info);
215
+
216
+ /**
217
+ * Restore full emulation state
218
+ * @param state - The object returned by StateSave
219
+ */
220
+ Napi::Value StateRestore(const Napi::CallbackInfo& info);
221
+
222
+ // ============== Query & Control ==============
223
+
224
+ /**
225
+ * Query engine information
226
+ * @param queryType - QUERY.MODE, QUERY.PAGE_SIZE, QUERY.ARCH
227
+ * @returns Query result
228
+ */
229
+ Napi::Value Query(const Napi::CallbackInfo& info);
230
+
231
+ /**
232
+ * Set engine option
233
+ * @param optType - Option type
234
+ * @param value - Option value
235
+ */
236
+ Napi::Value CtlWrite(const Napi::CallbackInfo& info);
237
+
238
+ /**
239
+ * Get engine option
240
+ * @param optType - Option type
241
+ * @returns Option value
242
+ */
243
+ Napi::Value CtlRead(const Napi::CallbackInfo& info);
244
+
245
+ /**
246
+ * Close the engine and free resources
247
+ */
248
+ Napi::Value Close(const Napi::CallbackInfo& info);
249
+
250
+ // ============== Property Getters ==============
251
+
252
+ Napi::Value GetArch(const Napi::CallbackInfo& info);
253
+ Napi::Value GetMode(const Napi::CallbackInfo& info);
254
+ Napi::Value GetHandle(const Napi::CallbackInfo& info);
255
+ Napi::Value GetPageSize(const Napi::CallbackInfo& info);
256
+
257
+ // ============== Internal Helpers ==============
258
+
259
+ void ThrowUnicornError(Napi::Env env, uc_err err, const char* context = nullptr);
260
+ void CleanupHooks();
261
+
262
+ // Determine register size based on architecture and register ID
263
+ size_t GetRegisterSize(int regId);
264
+
265
+ // Check if register is 64-bit
266
+ bool Is64BitRegister(int regId);
267
+
268
+ public:
269
+ // Helper for checking breakpoints from static callback
270
+ bool IsBreakpointHit(uint64_t address) {
271
+ // No lock needed here as we only read, and it's called from the same thread as emulation
272
+ // strict consistency isn't critical for a breakpoint check
273
+ // (if we miss one cycle due to race during add, it's fine)
274
+ // But for correctness with the defined mutex:
275
+ // std::lock_guard<std::mutex> lock(hookMutex_);
276
+ // Locking every instruction IS expensive.
277
+ // Since emulation is single-threaded usually, and JS calls add/del from main thread,
278
+ // there IS a race if we add/del while running async.
279
+ // However, standard use case is add/del while paused.
280
+ // If async, we might need a lockless atomic check or read-copy-update.
281
+ // For now, avoiding lock for perf.
282
+ return breakpoints_.count(address) > 0;
283
+ }
284
+ };
285
+
286
+ /**
287
+ * UnicornContext - Wrapper for saved CPU context
288
+ */
289
+ class UnicornContext : public Napi::ObjectWrap<UnicornContext> {
290
+ public:
291
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
292
+ static Napi::FunctionReference constructor;
293
+
294
+ UnicornContext(const Napi::CallbackInfo& info);
295
+ ~UnicornContext();
296
+
297
+ uc_context* GetContext() const { return context_; }
298
+ void SetContext(uc_context* ctx) { context_ = ctx; }
299
+
300
+ private:
301
+ uc_context* context_;
302
+ uc_engine* engine_; // Keep reference for proper cleanup
303
+
304
+ Napi::Value Free(const Napi::CallbackInfo& info);
305
+ Napi::Value GetSize(const Napi::CallbackInfo& info);
306
+ };
307
+
308
+ // ============== Hook Data Structures ==============
309
+
310
+ /**
311
+ * Data passed to hook callbacks
312
+ * Uses ThreadSafeFunction for safe JS callback invocation
313
+ */
314
+ struct HookData {
315
+ Napi::ThreadSafeFunction tsfn;
316
+ uc_hook handle;
317
+ int type;
318
+ UnicornWrapper* wrapper;
319
+ bool active;
320
+
321
+ HookData() : handle(0), type(0), wrapper(nullptr), active(true) {}
322
+ ~HookData() {
323
+ if (tsfn) {
324
+ tsfn.Release();
325
+ }
326
+ }
327
+ };
328
+
329
+ // Data structures for passing to JavaScript callbacks
330
+ struct CodeHookCallData {
331
+ uint64_t address;
332
+ uint32_t size;
333
+ };
334
+
335
+ struct BlockHookCallData {
336
+ uint64_t address;
337
+ uint32_t size;
338
+ };
339
+
340
+ struct MemHookCallData {
341
+ int type;
342
+ uint64_t address;
343
+ int size;
344
+ int64_t value;
345
+ };
346
+
347
+ struct InterruptHookCallData {
348
+ uint32_t intno;
349
+ };
350
+
351
+ struct InsnHookCallData {
352
+ uint64_t address;
353
+ uint32_t size;
354
+ };
355
+
356
+ struct InvalidMemHookCallData {
357
+ int type;
358
+ uint64_t address;
359
+ int size;
360
+ int64_t value;
361
+ };
362
+
363
+ // ============== Hook Callback Functions ==============
364
+
365
+ void CodeHookCB(uc_engine* uc, uint64_t address, uint32_t size, void* user_data);
366
+ void BlockHookCB(uc_engine* uc, uint64_t address, uint32_t size, void* user_data);
367
+ void MemHookCB(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data);
368
+ void InterruptHookCB(uc_engine* uc, uint32_t intno, void* user_data);
369
+ void InsnHookCB(uc_engine* uc, void* user_data);
370
+ bool InvalidMemHookCB(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data);
371
+ void BreakpointHookCB(uc_engine* uc, uint64_t address, uint32_t size, void* user_data);
372
+
373
+ // ============== Utility Functions ==============
374
+
375
+ Napi::Object CreateErrorObject(Napi::Env env, uc_err err);
376
+ const char* GetErrorMessage(uc_err err);
377
+
378
+ #endif // UNICORN_WRAPPER_H
379
+