frida-mem-scan 1.0.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.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # frida-mem-scan
2
+
3
+ Fast SIMD-accelerated memory scanner for Frida. Targets either the
4
+ current process or any other process by PID, with the same API.
5
+
6
+ The scanner core is a self-contained, relocation-free assembly blob
7
+ (SSE4.1 on x86_64, NEON on arm64) that's copied into a JIT page at
8
+ load time. On modern hardware it saturates memory bandwidth on the
9
+ parallel path; see `src/README.md` for measurements on a specific
10
+ machine. The cross-process path uses the platform's native VM API
11
+ (mach\_vm\_\* on macOS, with Windows and Linux backends stubbed for
12
+ future contributions).
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ frida-pm install frida-mem-scan
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```ts
23
+ import { Target } from "frida-mem-scan";
24
+
25
+ // In-process scan: find every Hv::Vm vtable pointer.
26
+ const me = Target.self();
27
+ const hits = me.find(["Hypervisor!_ZTVN2Hv2VmE+16"]);
28
+ for (const h of hits) {
29
+ console.log(h.addr);
30
+ }
31
+
32
+ // Cross-process scan, restricted to readable regions <= 100 MB:
33
+ const t = Target.pid(9549);
34
+ const hits = t.find(["Hypervisor!_ZTVN2Hv2VmE+16"], {
35
+ filter: { readable: true, maxBytes: 100 * 1024 * 1024 },
36
+ });
37
+
38
+ // Raw memory read (works for both self and cross-process):
39
+ const bytes = t.read(ptr("0x104dbd560"), 128);
40
+ ```
41
+
42
+ Targets are either raw addresses (`NativePointer`, `UInt64`, or
43
+ `number`) or `<module>!<symbol>[+<offset>]` strings that resolve
44
+ against the loaded image set. The `+16` convention matches the
45
+ Itanium C++ ABI vtable address-point so you can scan for object
46
+ instances by their class name.
47
+
48
+ By default a mask of `0x00007ffffffffff8` is applied to each candidate
49
+ slot before equality comparison; that strips arm64e PAC and the
50
+ non-pointer-isa flag bits in one go. Pass your own `mask` to opt out
51
+ or scan for something else (e.g. `~0` for raw equality).
52
+
53
+ ## API
54
+
55
+ ```ts
56
+ class Target {
57
+ static self(): Target
58
+ static pid(pid: number): Target
59
+
60
+ find(targets: TargetSpec[], opts?: FindOpts): ScanHit[]
61
+ read(addr: NativePointer, size: number): ArrayBuffer
62
+ regions(filter?: RegionFilter): Iterable<Region>
63
+ }
64
+
65
+ interface FindOpts {
66
+ mask?: UInt64
67
+ filter?: RegionFilter
68
+ capPerThread?: number
69
+ }
70
+
71
+ interface RegionFilter {
72
+ readable?: boolean
73
+ writable?: boolean
74
+ executable?: boolean
75
+ tags?: number[] | Set<number>
76
+ minBytes?: number
77
+ maxBytes?: number
78
+ }
79
+ ```
80
+
81
+ The lower-level `Scanner` class is also exported for callers that
82
+ want to feed in already-prepared buffers without going through the
83
+ `Target` abstraction.
84
+
85
+ ## Building
86
+
87
+ The published package ships pre-built JavaScript and TypeScript
88
+ typings; you only need this section if you're modifying the assembly
89
+ core.
90
+
91
+ ```sh
92
+ cd src
93
+ make # builds scan_<arch>.dylib, regenerates ../lib/scanner/bytes-*.ts
94
+ make test # native + Rosetta tests with perf numbers
95
+ ```
96
+
97
+ The assembly sources, build scripts, and test harness live in `src/`
98
+ and are excluded from the npm package (only `dist/` ships).
99
+
100
+ ## Platform support
101
+
102
+ | platform | status |
103
+ | ---------------------------- | ------------------------------------------------------- |
104
+ | Windows | stub (planned: `ReadProcessMemory` + `VirtualQueryEx`) |
105
+ | macOS x86_64 (incl. Rosetta) | full — Mach backend |
106
+ | macOS arm64 (incl. arm64e) | full — Mach backend |
107
+ | Linux | stub (planned: `process_vm_readv` + `/proc/<pid>/maps`) |
108
+
109
+ The scanner SIMD blob itself is platform-neutral; only the
110
+ cross-process VM access needs per-OS implementation.
@@ -0,0 +1,4 @@
1
+ export { DEFAULT_ISA_MASK, Hit, Scanner } from "./scanner/index.js";
2
+ export { ProcessMemory, Region, RegionFilter } from "./process/types.js";
3
+ export { resolveTarget } from "./symbols.js";
4
+ export { FindOpts, ScanHit, Target, TargetSpec } from "./target.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { DEFAULT_ISA_MASK, Scanner } from "./scanner/index.js";
2
+ export { resolveTarget } from "./symbols.js";
3
+ export { Target } from "./target.js";
@@ -0,0 +1,10 @@
1
+ import { ProcessMemory, Region, RegionFilter } from "./types.js";
2
+ export declare class DarwinProcessMemory implements ProcessMemory {
3
+ #private;
4
+ readonly pid: number;
5
+ readonly isSelf: boolean;
6
+ constructor(pid: number);
7
+ regions(filter?: RegionFilter): Iterable<Region>;
8
+ read(addr: NativePointer, size: number): ArrayBuffer | null;
9
+ readInto(addr: NativePointer, dst: NativePointer, size: number): boolean;
10
+ }
@@ -0,0 +1,126 @@
1
+ const KERN_SUCCESS = 0;
2
+ const VM_PROT_EXEC = 0x04;
3
+ const VM_PROT_READ = 0x01;
4
+ const VM_PROT_WRITE = 0x02;
5
+ const VM_REGION_INFO_COUNT = 19;
6
+ export class DarwinProcessMemory {
7
+ pid;
8
+ isSelf;
9
+ #task;
10
+ constructor(pid) {
11
+ resolveNatives();
12
+ this.pid = pid;
13
+ this.isSelf = pid === Process.id;
14
+ this.#task = openTask(pid);
15
+ }
16
+ *regions(filter) {
17
+ const wantReadable = filter?.readable === true;
18
+ const wantWritable = filter?.writable === true;
19
+ const wantExecutable = filter?.executable === true;
20
+ const tagSet = makeTagSet(filter?.tags);
21
+ const minBytes = filter?.minBytes;
22
+ const maxBytes = filter?.maxBytes;
23
+ let cursor = NULL;
24
+ while (true) {
25
+ const r = nextRegion(this.#task, cursor);
26
+ if (r === null) {
27
+ break;
28
+ }
29
+ cursor = r.base.add(r.size);
30
+ if (wantReadable && !r.readable) {
31
+ continue;
32
+ }
33
+ if (wantWritable && !r.writable) {
34
+ continue;
35
+ }
36
+ if (wantExecutable && !r.executable) {
37
+ continue;
38
+ }
39
+ if (tagSet !== null && !tagSet.has(r.tag)) {
40
+ continue;
41
+ }
42
+ const sz = r.size.toNumber();
43
+ if (minBytes !== undefined && sz < minBytes) {
44
+ continue;
45
+ }
46
+ if (maxBytes !== undefined && sz > maxBytes) {
47
+ continue;
48
+ }
49
+ yield r;
50
+ }
51
+ }
52
+ read(addr, size) {
53
+ const buf = Memory.alloc(size);
54
+ if (!this.readInto(addr, buf, size)) {
55
+ return null;
56
+ }
57
+ const view = new Uint8Array(buf.readByteArray(size));
58
+ return view.slice().buffer;
59
+ }
60
+ readInto(addr, dst, size) {
61
+ const outsize = Memory.alloc(8);
62
+ const kr = machVmReadOverwrite(this.#task, addr, uint64(size), dst, outsize);
63
+ return kr === KERN_SUCCESS;
64
+ }
65
+ }
66
+ function makeTagSet(tags) {
67
+ if (tags === undefined) {
68
+ return null;
69
+ }
70
+ return tags instanceof Set ? tags : new Set(tags);
71
+ }
72
+ function openTask(pid) {
73
+ if (pid === Process.id) {
74
+ return machTaskSelf;
75
+ }
76
+ const out = Memory.alloc(4);
77
+ const kr = taskForPid(machTaskSelf, pid, out);
78
+ if (kr !== KERN_SUCCESS) {
79
+ throw new Error(`task_for_pid(${pid}) failed: kr=${kr}`);
80
+ }
81
+ return out.readU32();
82
+ }
83
+ function nextRegion(task, startAddr) {
84
+ const ADDR_OFF = 0;
85
+ const SIZE_OFF = 8;
86
+ const DEPTH_OFF = 16;
87
+ const INFO_OFF = 24;
88
+ const COUNT_OFF = 152;
89
+ const TOTAL = 156;
90
+ const scratch = Memory.alloc(TOTAL);
91
+ const addrPtr = scratch.add(ADDR_OFF);
92
+ const sizePtr = scratch.add(SIZE_OFF);
93
+ const depthPtr = scratch.add(DEPTH_OFF);
94
+ const info = scratch.add(INFO_OFF);
95
+ const countPtr = scratch.add(COUNT_OFF);
96
+ addrPtr.writePointer(startAddr);
97
+ depthPtr.writeU32(2048);
98
+ countPtr.writeU32(VM_REGION_INFO_COUNT);
99
+ const kr = machVmRegionRecurse(task, addrPtr, sizePtr, depthPtr, info, countPtr);
100
+ if (kr !== KERN_SUCCESS) {
101
+ return null;
102
+ }
103
+ const prot = info.readS32();
104
+ return {
105
+ base: addrPtr.readPointer(),
106
+ size: sizePtr.readU64(),
107
+ readable: (prot & VM_PROT_READ) !== 0,
108
+ writable: (prot & VM_PROT_WRITE) !== 0,
109
+ executable: (prot & VM_PROT_EXEC) !== 0,
110
+ tag: info.add(24).readU32(),
111
+ };
112
+ }
113
+ let machTaskSelf = 0;
114
+ let machVmReadOverwrite = null;
115
+ let machVmRegionRecurse = null;
116
+ let taskForPid = null;
117
+ function resolveNatives() {
118
+ if (taskForPid !== null) {
119
+ return;
120
+ }
121
+ const libSystem = Process.getModuleByName("libSystem.B.dylib");
122
+ machTaskSelf = libSystem.getExportByName("mach_task_self_").readU32();
123
+ taskForPid = new NativeFunction(libSystem.getExportByName("task_for_pid"), "int", ["uint", "int", "pointer"]);
124
+ machVmRegionRecurse = new NativeFunction(libSystem.getExportByName("mach_vm_region_recurse"), "int", ["uint", "pointer", "pointer", "pointer", "pointer", "pointer"]);
125
+ machVmReadOverwrite = new NativeFunction(libSystem.getExportByName("mach_vm_read_overwrite"), "int", ["uint", "pointer", "uint64", "pointer", "pointer"]);
126
+ }
@@ -0,0 +1,2 @@
1
+ import { ProcessMemory } from "./types.js";
2
+ export declare function openProcessMemory(pid: number): ProcessMemory;
@@ -0,0 +1,12 @@
1
+ import { DarwinProcessMemory } from "./darwin.js";
2
+ import { LinuxProcessMemory } from "./linux.js";
3
+ import { WindowsProcessMemory } from "./windows.js";
4
+ export function openProcessMemory(pid) {
5
+ switch (Process.platform) {
6
+ case "windows": return new WindowsProcessMemory(pid);
7
+ case "darwin": return new DarwinProcessMemory(pid);
8
+ case "linux": return new LinuxProcessMemory(pid);
9
+ default:
10
+ throw new Error(`frida-mem-scan: no backend for platform ${Process.platform}`);
11
+ }
12
+ }
@@ -0,0 +1,9 @@
1
+ import { ProcessMemory, Region, RegionFilter } from "./types.js";
2
+ export declare class LinuxProcessMemory implements ProcessMemory {
3
+ readonly pid: number;
4
+ readonly isSelf: boolean;
5
+ constructor(pid: number);
6
+ regions(_filter?: RegionFilter): Iterable<Region>;
7
+ read(_addr: NativePointer, _size: number): ArrayBuffer | null;
8
+ readInto(_addr: NativePointer, _dst: NativePointer, _size: number): boolean;
9
+ }
@@ -0,0 +1,19 @@
1
+ const UNIMPLEMENTED = "frida-mem-scan: Linux backend not yet implemented";
2
+ export class LinuxProcessMemory {
3
+ pid;
4
+ isSelf;
5
+ constructor(pid) {
6
+ this.pid = pid;
7
+ this.isSelf = pid === Process.id;
8
+ throw new Error(UNIMPLEMENTED);
9
+ }
10
+ *regions(_filter) {
11
+ throw new Error(UNIMPLEMENTED);
12
+ }
13
+ read(_addr, _size) {
14
+ throw new Error(UNIMPLEMENTED);
15
+ }
16
+ readInto(_addr, _dst, _size) {
17
+ throw new Error(UNIMPLEMENTED);
18
+ }
19
+ }
@@ -0,0 +1,23 @@
1
+ export interface Region {
2
+ base: NativePointer;
3
+ size: UInt64;
4
+ readable: boolean;
5
+ writable: boolean;
6
+ executable: boolean;
7
+ tag: number;
8
+ }
9
+ export interface RegionFilter {
10
+ readable?: boolean;
11
+ writable?: boolean;
12
+ executable?: boolean;
13
+ tags?: number[] | Set<number>;
14
+ minBytes?: number;
15
+ maxBytes?: number;
16
+ }
17
+ export interface ProcessMemory {
18
+ readonly pid: number;
19
+ readonly isSelf: boolean;
20
+ regions(filter?: RegionFilter): Iterable<Region>;
21
+ read(addr: NativePointer, size: number): ArrayBuffer | null;
22
+ readInto(addr: NativePointer, dst: NativePointer, size: number): boolean;
23
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { ProcessMemory, Region, RegionFilter } from "./types.js";
2
+ export declare class WindowsProcessMemory implements ProcessMemory {
3
+ readonly pid: number;
4
+ readonly isSelf: boolean;
5
+ constructor(pid: number);
6
+ regions(_filter?: RegionFilter): Iterable<Region>;
7
+ read(_addr: NativePointer, _size: number): ArrayBuffer | null;
8
+ readInto(_addr: NativePointer, _dst: NativePointer, _size: number): boolean;
9
+ }
@@ -0,0 +1,19 @@
1
+ const UNIMPLEMENTED = "frida-mem-scan: Windows backend not yet implemented";
2
+ export class WindowsProcessMemory {
3
+ pid;
4
+ isSelf;
5
+ constructor(pid) {
6
+ this.pid = pid;
7
+ this.isSelf = pid === Process.id;
8
+ throw new Error(UNIMPLEMENTED);
9
+ }
10
+ *regions(_filter) {
11
+ throw new Error(UNIMPLEMENTED);
12
+ }
13
+ read(_addr, _size) {
14
+ throw new Error(UNIMPLEMENTED);
15
+ }
16
+ readInto(_addr, _dst, _size) {
17
+ throw new Error(UNIMPLEMENTED);
18
+ }
19
+ }
@@ -0,0 +1,2 @@
1
+ export declare const SCAN_ARM64_BYTES: Uint8Array<ArrayBuffer>;
2
+ export declare const SYMBOLS: Record<string, number>;
@@ -0,0 +1,106 @@
1
+ // Generated by extract_text.sh + emit_ts.sh from build/scan_arm64.bin
2
+ // Do not edit by hand; rerun `make` after editing the .S source.
3
+ export const SCAN_ARM64_BYTES = new Uint8Array([
4
+ 0xff, 0x83, 0x01, 0xd1, 0xfd, 0x7b, 0x05, 0xa9, 0xfd, 0x43, 0x01, 0x91,
5
+ 0xe9, 0x33, 0x40, 0xf9, 0xea, 0x37, 0x40, 0xf9, 0xeb, 0x3b, 0x40, 0xf9,
6
+ 0xec, 0x3f, 0x40, 0xf9, 0x8d, 0x05, 0x00, 0xd1, 0x6d, 0x00, 0x0d, 0x8b,
7
+ 0xad, 0x09, 0xcc, 0x9a, 0xe2, 0x37, 0x00, 0xa9, 0xe3, 0x13, 0x01, 0xa9,
8
+ 0xe5, 0x1b, 0x02, 0xa9, 0xe7, 0x27, 0x03, 0xa9, 0xea, 0x2f, 0x04, 0xa9,
9
+ 0xf0, 0x03, 0x00, 0xaa, 0xe0, 0x03, 0x0c, 0xaa, 0xe2, 0x03, 0x00, 0x91,
10
+ 0xc3, 0x00, 0x00, 0x10, 0x00, 0x02, 0x3f, 0xd6, 0xfd, 0x7b, 0x45, 0xa9,
11
+ 0xff, 0x83, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6, 0x1f, 0x20, 0x03, 0xd5,
12
+ 0xfd, 0x7b, 0xbe, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0xe0, 0x07, 0x01, 0xa9,
13
+ 0x0a, 0x2c, 0x40, 0xa9, 0x0c, 0x34, 0x41, 0xa9, 0x0e, 0x3c, 0x42, 0xa9,
14
+ 0x10, 0x44, 0x43, 0xa9, 0x09, 0x24, 0x40, 0xf9, 0x22, 0x7c, 0x0b, 0x9b,
15
+ 0x22, 0x79, 0x21, 0xf8, 0x5f, 0x00, 0x0c, 0xeb, 0x83, 0x00, 0x00, 0x54,
16
+ 0x09, 0x20, 0x40, 0xf9, 0x3f, 0x79, 0x21, 0xf8, 0x12, 0x00, 0x00, 0x14,
17
+ 0x83, 0x01, 0x02, 0xcb, 0x7f, 0x00, 0x0b, 0xeb, 0x63, 0x30, 0x8b, 0x9a,
18
+ 0x4a, 0x0d, 0x02, 0x8b, 0x24, 0x7c, 0x11, 0x9b, 0x10, 0x0a, 0x04, 0x8b,
19
+ 0xe0, 0x03, 0x0a, 0xaa, 0xe1, 0x03, 0x03, 0xaa, 0xe2, 0x03, 0x0d, 0xaa,
20
+ 0xe3, 0x03, 0x0e, 0xaa, 0xe4, 0x03, 0x0f, 0xaa, 0xe5, 0x03, 0x10, 0xaa,
21
+ 0xe6, 0x03, 0x11, 0xaa, 0x44, 0x00, 0x00, 0x94, 0xe9, 0x2b, 0x41, 0xa9,
22
+ 0x2b, 0x21, 0x40, 0xf9, 0x60, 0x79, 0x2a, 0xf8, 0xfd, 0x7b, 0xc2, 0xa8,
23
+ 0xc0, 0x03, 0x5f, 0xd6, 0x1f, 0x20, 0x03, 0xd5, 0x1f, 0x20, 0x03, 0xd5,
24
+ 0xff, 0x83, 0x01, 0xd1, 0xfd, 0x7b, 0x05, 0xa9, 0xfd, 0x43, 0x01, 0x91,
25
+ 0xe9, 0x33, 0x40, 0xf9, 0xea, 0x37, 0x40, 0xf9, 0xeb, 0x3b, 0x40, 0xf9,
26
+ 0x6c, 0x05, 0x00, 0xd1, 0x6c, 0x00, 0x0c, 0x8b, 0x8c, 0x09, 0xcb, 0x9a,
27
+ 0xe2, 0x33, 0x00, 0xa9, 0xe3, 0x13, 0x01, 0xa9, 0xe5, 0x13, 0x00, 0xf9,
28
+ 0xe6, 0x1f, 0x03, 0xa9, 0xe9, 0x2b, 0x04, 0xa9, 0xf0, 0x03, 0x00, 0xaa,
29
+ 0xe0, 0x03, 0x0b, 0xaa, 0xe2, 0x03, 0x00, 0x91, 0xe3, 0x00, 0x00, 0x10,
30
+ 0x00, 0x02, 0x3f, 0xd6, 0xfd, 0x7b, 0x45, 0xa9, 0xff, 0x83, 0x01, 0x91,
31
+ 0xc0, 0x03, 0x5f, 0xd6, 0x1f, 0x20, 0x03, 0xd5, 0x1f, 0x20, 0x03, 0xd5,
32
+ 0xfd, 0x7b, 0xbe, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0xe0, 0x07, 0x01, 0xa9,
33
+ 0x0a, 0x2c, 0x40, 0xa9, 0x0c, 0x34, 0x41, 0xa9, 0x0e, 0x10, 0x40, 0xf9,
34
+ 0x0f, 0x40, 0x43, 0xa9, 0x09, 0x24, 0x40, 0xf9, 0x22, 0x7c, 0x0b, 0x9b,
35
+ 0x22, 0x79, 0x21, 0xf8, 0x5f, 0x00, 0x0c, 0xeb, 0x83, 0x00, 0x00, 0x54,
36
+ 0x09, 0x20, 0x40, 0xf9, 0x3f, 0x79, 0x21, 0xf8, 0x11, 0x00, 0x00, 0x14,
37
+ 0x83, 0x01, 0x02, 0xcb, 0x7f, 0x00, 0x0b, 0xeb, 0x63, 0x30, 0x8b, 0x9a,
38
+ 0x4a, 0x0d, 0x02, 0x8b, 0x24, 0x7c, 0x10, 0x9b, 0xef, 0x09, 0x04, 0x8b,
39
+ 0xe0, 0x03, 0x0a, 0xaa, 0xe1, 0x03, 0x03, 0xaa, 0xe2, 0x03, 0x0d, 0xaa,
40
+ 0xe3, 0x03, 0x0e, 0xaa, 0xe4, 0x03, 0x0f, 0xaa, 0xe5, 0x03, 0x10, 0xaa,
41
+ 0x75, 0x00, 0x00, 0x94, 0xe9, 0x2b, 0x41, 0xa9, 0x2b, 0x21, 0x40, 0xf9,
42
+ 0x60, 0x79, 0x2a, 0xf8, 0xfd, 0x7b, 0xc2, 0xa8, 0xc0, 0x03, 0x5f, 0xd6,
43
+ 0x1f, 0x20, 0x03, 0xd5, 0x1f, 0x20, 0x03, 0xd5, 0x1f, 0x20, 0x03, 0xd5,
44
+ 0xfd, 0x7b, 0xbf, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0xe7, 0x03, 0x1f, 0xaa,
45
+ 0xa1, 0x0c, 0x00, 0xb4, 0x40, 0x0c, 0x08, 0x4e, 0x9f, 0x10, 0x00, 0xf1,
46
+ 0x4c, 0x09, 0x00, 0x54, 0x62, 0x00, 0x40, 0xfd, 0x9f, 0x04, 0x00, 0xf1,
47
+ 0x20, 0x01, 0x00, 0x54, 0x63, 0x04, 0x40, 0xfd, 0x9f, 0x08, 0x00, 0xf1,
48
+ 0x60, 0x01, 0x00, 0x54, 0x64, 0x08, 0x40, 0xfd, 0x9f, 0x0c, 0x00, 0xf1,
49
+ 0xa0, 0x01, 0x00, 0x54, 0x65, 0x0c, 0x40, 0xfd, 0x10, 0x00, 0x00, 0x14,
50
+ 0x42, 0x04, 0x08, 0x4e, 0x43, 0x1c, 0xa2, 0x4e, 0x44, 0x1c, 0xa2, 0x4e,
51
+ 0x45, 0x1c, 0xa2, 0x4e, 0x0f, 0x00, 0x00, 0x14, 0x42, 0x04, 0x08, 0x4e,
52
+ 0x63, 0x04, 0x08, 0x4e, 0x44, 0x1c, 0xa2, 0x4e, 0x45, 0x1c, 0xa2, 0x4e,
53
+ 0x0a, 0x00, 0x00, 0x14, 0x42, 0x04, 0x08, 0x4e, 0x63, 0x04, 0x08, 0x4e,
54
+ 0x84, 0x04, 0x08, 0x4e, 0x45, 0x1c, 0xa2, 0x4e, 0x05, 0x00, 0x00, 0x14,
55
+ 0x42, 0x04, 0x08, 0x4e, 0x63, 0x04, 0x08, 0x4e, 0x84, 0x04, 0x08, 0x4e,
56
+ 0xa5, 0x04, 0x08, 0x4e, 0xe8, 0x03, 0x1f, 0xaa, 0x29, 0xf8, 0x7f, 0x92,
57
+ 0x1f, 0x01, 0x09, 0xeb, 0x2a, 0x03, 0x00, 0x54, 0x0a, 0x0c, 0x08, 0x8b,
58
+ 0x41, 0x01, 0xc0, 0x3d, 0x21, 0x1c, 0x20, 0x4e, 0x30, 0x8c, 0xe2, 0x6e,
59
+ 0x31, 0x8c, 0xe3, 0x6e, 0x32, 0x8c, 0xe4, 0x6e, 0x33, 0x8c, 0xe5, 0x6e,
60
+ 0x10, 0x1e, 0xb1, 0x4e, 0x52, 0x1e, 0xb3, 0x4e, 0x10, 0x1e, 0xb2, 0x4e,
61
+ 0x0b, 0x3e, 0x08, 0x4e, 0x0c, 0x3e, 0x18, 0x4e, 0x6d, 0x01, 0x0c, 0xaa,
62
+ 0x2d, 0x01, 0x00, 0xb4, 0x4b, 0x00, 0x00, 0xb4, 0x2a, 0x00, 0x00, 0x94,
63
+ 0xcc, 0x00, 0x00, 0xb4, 0x0e, 0x05, 0x00, 0x91, 0xef, 0x03, 0x08, 0xaa,
64
+ 0xe8, 0x03, 0x0e, 0xaa, 0x25, 0x00, 0x00, 0x94, 0xe8, 0x03, 0x0f, 0xaa,
65
+ 0x08, 0x09, 0x00, 0x91, 0xe7, 0xff, 0xff, 0x17, 0x1f, 0x01, 0x01, 0xeb,
66
+ 0xca, 0x04, 0x00, 0x54, 0x0a, 0x0c, 0x08, 0x8b, 0x4b, 0x01, 0x40, 0xf9,
67
+ 0x6b, 0x01, 0x02, 0x8a, 0xec, 0x03, 0x1f, 0xaa, 0x9f, 0x01, 0x04, 0xeb,
68
+ 0x0a, 0x04, 0x00, 0x54, 0x6d, 0x78, 0x6c, 0xf8, 0x7f, 0x01, 0x0d, 0xeb,
69
+ 0x61, 0x00, 0x00, 0x54, 0x16, 0x00, 0x00, 0x94, 0x1b, 0x00, 0x00, 0x14,
70
+ 0x8c, 0x05, 0x00, 0x91, 0xf8, 0xff, 0xff, 0x17, 0xe8, 0x03, 0x1f, 0xaa,
71
+ 0x1f, 0x01, 0x01, 0xeb, 0xca, 0x02, 0x00, 0x54, 0x0a, 0x0c, 0x08, 0x8b,
72
+ 0x4b, 0x01, 0x40, 0xf9, 0x6b, 0x01, 0x02, 0x8a, 0xec, 0x03, 0x1f, 0xaa,
73
+ 0x9f, 0x01, 0x04, 0xeb, 0x0a, 0x01, 0x00, 0x54, 0x6d, 0x78, 0x6c, 0xf8,
74
+ 0x7f, 0x01, 0x0d, 0xeb, 0x61, 0x00, 0x00, 0x54, 0x06, 0x00, 0x00, 0x94,
75
+ 0x03, 0x00, 0x00, 0x14, 0x8c, 0x05, 0x00, 0x91, 0xf8, 0xff, 0xff, 0x17,
76
+ 0x08, 0x05, 0x00, 0x91, 0xf0, 0xff, 0xff, 0x17, 0xe7, 0x04, 0x00, 0x91,
77
+ 0xff, 0x00, 0x06, 0xeb, 0x68, 0x00, 0x00, 0x54, 0xf0, 0x04, 0x00, 0xd1,
78
+ 0xa8, 0x78, 0x30, 0xb8, 0xc0, 0x03, 0x5f, 0xd6, 0xe0, 0x03, 0x07, 0xaa,
79
+ 0xfd, 0x7b, 0xc1, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x1f, 0x20, 0x03, 0xd5,
80
+ 0xfd, 0x7b, 0xbf, 0xa9, 0xfd, 0x03, 0x00, 0x91, 0xe6, 0x03, 0x1f, 0xaa,
81
+ 0xc1, 0x06, 0x00, 0xb4, 0x40, 0x0c, 0x08, 0x4e, 0x62, 0x0c, 0x08, 0x4e,
82
+ 0xe8, 0x03, 0x1f, 0xaa, 0x29, 0xf4, 0x7e, 0x92, 0x1f, 0x01, 0x09, 0xeb,
83
+ 0x0a, 0x04, 0x00, 0x54, 0x0a, 0x0c, 0x08, 0x8b, 0x41, 0x0d, 0x40, 0xad,
84
+ 0x21, 0x1c, 0x20, 0x4e, 0x63, 0x1c, 0x20, 0x4e, 0x21, 0x8c, 0xe2, 0x6e,
85
+ 0x63, 0x8c, 0xe2, 0x6e, 0x24, 0x1c, 0xa3, 0x4e, 0x8b, 0x3c, 0x08, 0x4e,
86
+ 0x8c, 0x3c, 0x18, 0x4e, 0x6d, 0x01, 0x0c, 0xaa, 0x6d, 0x02, 0x00, 0xb4,
87
+ 0x2b, 0x3c, 0x08, 0x4e, 0x4b, 0x00, 0x00, 0xb4, 0x1c, 0x00, 0x00, 0x94,
88
+ 0x2b, 0x3c, 0x18, 0x4e, 0x8b, 0x00, 0x00, 0xb4, 0x08, 0x05, 0x00, 0x91,
89
+ 0x18, 0x00, 0x00, 0x94, 0x08, 0x05, 0x00, 0xd1, 0x6b, 0x3c, 0x08, 0x4e,
90
+ 0x8b, 0x00, 0x00, 0xb4, 0x08, 0x09, 0x00, 0x91, 0x13, 0x00, 0x00, 0x94,
91
+ 0x08, 0x09, 0x00, 0xd1, 0x6b, 0x3c, 0x18, 0x4e, 0x8b, 0x00, 0x00, 0xb4,
92
+ 0x08, 0x0d, 0x00, 0x91, 0x0e, 0x00, 0x00, 0x94, 0x08, 0x0d, 0x00, 0xd1,
93
+ 0x08, 0x11, 0x00, 0x91, 0xe0, 0xff, 0xff, 0x17, 0x1f, 0x01, 0x01, 0xeb,
94
+ 0xea, 0x01, 0x00, 0x54, 0x0a, 0x0c, 0x08, 0x8b, 0x4b, 0x01, 0x40, 0xf9,
95
+ 0x6b, 0x01, 0x02, 0x8a, 0x7f, 0x01, 0x03, 0xeb, 0x41, 0x00, 0x00, 0x54,
96
+ 0x03, 0x00, 0x00, 0x94, 0x08, 0x05, 0x00, 0x91, 0xf7, 0xff, 0xff, 0x17,
97
+ 0xc6, 0x04, 0x00, 0x91, 0xdf, 0x00, 0x05, 0xeb, 0x68, 0x00, 0x00, 0x54,
98
+ 0xce, 0x04, 0x00, 0xd1, 0x88, 0x78, 0x2e, 0xb8, 0xc0, 0x03, 0x5f, 0xd6,
99
+ 0xe0, 0x03, 0x06, 0xaa, 0xfd, 0x7b, 0xc1, 0xa8, 0xc0, 0x03, 0x5f, 0xd6
100
+ ]);
101
+ export const SYMBOLS = {
102
+ scan: 0x1e0,
103
+ scan1: 0x390,
104
+ scan1_parallel: 0xf0,
105
+ scan_parallel: 0x0,
106
+ };
@@ -0,0 +1,2 @@
1
+ export declare const SCAN_X86_64_BYTES: Uint8Array<ArrayBuffer>;
2
+ export declare const SYMBOLS: Record<string, number>;
@@ -0,0 +1,104 @@
1
+ // Generated by extract_text.sh + emit_ts.sh from build/scan_x86_64.bin
2
+ // Do not edit by hand; rerun `make` after editing the .S source.
3
+ export const SCAN_X86_64_BYTES = new Uint8Array([
4
+ 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x14, 0x24,
5
+ 0x48, 0x89, 0x4c, 0x24, 0x10, 0x4c, 0x89, 0x44, 0x24, 0x18, 0x4c, 0x89,
6
+ 0x4c, 0x24, 0x20, 0x48, 0x8b, 0x45, 0x10, 0x48, 0x89, 0x44, 0x24, 0x28,
7
+ 0x48, 0x8b, 0x45, 0x18, 0x48, 0x89, 0x44, 0x24, 0x30, 0x48, 0x8b, 0x45,
8
+ 0x20, 0x48, 0x89, 0x44, 0x24, 0x38, 0x48, 0x8b, 0x45, 0x28, 0x48, 0x89,
9
+ 0x44, 0x24, 0x40, 0x48, 0x8b, 0x45, 0x30, 0x48, 0x89, 0x44, 0x24, 0x48,
10
+ 0x48, 0x89, 0xc8, 0x4c, 0x8b, 0x55, 0x38, 0x4c, 0x01, 0xd0, 0x48, 0xff,
11
+ 0xc8, 0x31, 0xd2, 0x49, 0xf7, 0xf2, 0x48, 0x89, 0x44, 0x24, 0x08, 0x48,
12
+ 0x89, 0x7c, 0x24, 0x58, 0x4c, 0x89, 0xd7, 0x48, 0x89, 0xe2, 0x48, 0x8d,
13
+ 0x0d, 0x0f, 0x00, 0x00, 0x00, 0xff, 0x54, 0x24, 0x58, 0x48, 0x83, 0xc4,
14
+ 0x60, 0x5d, 0xc3, 0x0f, 0x1f, 0x44, 0x00, 0x00, 0x55, 0x48, 0x89, 0xe5,
15
+ 0x48, 0x83, 0xec, 0x30, 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0,
16
+ 0x4c, 0x8b, 0x57, 0x08, 0x4c, 0x8b, 0x5f, 0x10, 0x48, 0x89, 0xf0, 0x49,
17
+ 0x0f, 0xaf, 0xc2, 0x48, 0x8b, 0x4f, 0x48, 0x48, 0x89, 0x04, 0xf1, 0x4c,
18
+ 0x39, 0xd8, 0x72, 0x0e, 0x48, 0x8b, 0x4f, 0x40, 0x48, 0xc7, 0x04, 0xf1,
19
+ 0x00, 0x00, 0x00, 0x00, 0xeb, 0x4f, 0x49, 0x29, 0xc3, 0x4d, 0x39, 0xd3,
20
+ 0x4d, 0x0f, 0x43, 0xda, 0x48, 0x8b, 0x57, 0x38, 0x48, 0x89, 0xf1, 0x48,
21
+ 0x0f, 0xaf, 0xca, 0x48, 0xc1, 0xe1, 0x02, 0x48, 0x03, 0x4f, 0x30, 0x48,
22
+ 0x89, 0x14, 0x24, 0x49, 0x89, 0xc9, 0x4c, 0x8b, 0x47, 0x28, 0x48, 0x8b,
23
+ 0x57, 0x18, 0x48, 0x8b, 0x4f, 0x20, 0x4c, 0x89, 0xde, 0x4c, 0x8b, 0x17,
24
+ 0x49, 0x8d, 0x3c, 0xc2, 0xe8, 0x27, 0x01, 0x00, 0x00, 0x48, 0x8b, 0x4d,
25
+ 0xf8, 0x48, 0x8b, 0x75, 0xf0, 0x4c, 0x8b, 0x59, 0x40, 0x49, 0x89, 0x04,
26
+ 0xf3, 0x48, 0x83, 0xc4, 0x30, 0x5d, 0xc3, 0x90, 0x55, 0x48, 0x89, 0xe5,
27
+ 0x48, 0x83, 0xec, 0x60, 0x48, 0x89, 0x14, 0x24, 0x48, 0x89, 0x4c, 0x24,
28
+ 0x10, 0x4c, 0x89, 0x44, 0x24, 0x18, 0x4c, 0x89, 0x4c, 0x24, 0x20, 0x48,
29
+ 0x8b, 0x45, 0x10, 0x48, 0x89, 0x44, 0x24, 0x30, 0x48, 0x8b, 0x45, 0x18,
30
+ 0x48, 0x89, 0x44, 0x24, 0x38, 0x48, 0x8b, 0x45, 0x20, 0x48, 0x89, 0x44,
31
+ 0x24, 0x40, 0x48, 0x8b, 0x45, 0x28, 0x48, 0x89, 0x44, 0x24, 0x48, 0x48,
32
+ 0x89, 0xc8, 0x4c, 0x8b, 0x55, 0x30, 0x4c, 0x01, 0xd0, 0x48, 0xff, 0xc8,
33
+ 0x31, 0xd2, 0x49, 0xf7, 0xf2, 0x48, 0x89, 0x44, 0x24, 0x08, 0x48, 0x89,
34
+ 0x7c, 0x24, 0x58, 0x4c, 0x89, 0xd7, 0x48, 0x89, 0xe2, 0x48, 0x8d, 0x0d,
35
+ 0x18, 0x00, 0x00, 0x00, 0xff, 0x54, 0x24, 0x58, 0x48, 0x83, 0xc4, 0x60,
36
+ 0x5d, 0xc3, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
37
+ 0x0f, 0x1f, 0x40, 0x00, 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x30,
38
+ 0x48, 0x89, 0x7d, 0xf8, 0x48, 0x89, 0x75, 0xf0, 0x4c, 0x8b, 0x57, 0x08,
39
+ 0x4c, 0x8b, 0x5f, 0x10, 0x48, 0x89, 0xf0, 0x49, 0x0f, 0xaf, 0xc2, 0x48,
40
+ 0x8b, 0x4f, 0x48, 0x48, 0x89, 0x04, 0xf1, 0x4c, 0x39, 0xd8, 0x72, 0x0e,
41
+ 0x48, 0x8b, 0x4f, 0x40, 0x48, 0xc7, 0x04, 0xf1, 0x00, 0x00, 0x00, 0x00,
42
+ 0xeb, 0x4a, 0x49, 0x29, 0xc3, 0x4d, 0x39, 0xd3, 0x4d, 0x0f, 0x43, 0xda,
43
+ 0x48, 0x8b, 0x57, 0x38, 0x48, 0x89, 0xf1, 0x48, 0x0f, 0xaf, 0xca, 0x48,
44
+ 0xc1, 0xe1, 0x02, 0x48, 0x03, 0x4f, 0x30, 0x49, 0x89, 0xc8, 0x49, 0x89,
45
+ 0xd1, 0x48, 0x8b, 0x4f, 0x20, 0x48, 0x8b, 0x57, 0x18, 0x4c, 0x89, 0xde,
46
+ 0x4c, 0x8b, 0x17, 0x49, 0x8d, 0x3c, 0xc2, 0xe8, 0x7c, 0x01, 0x00, 0x00,
47
+ 0x48, 0x8b, 0x4d, 0xf8, 0x48, 0x8b, 0x75, 0xf0, 0x4c, 0x8b, 0x59, 0x40,
48
+ 0x49, 0x89, 0x04, 0xf3, 0x48, 0x83, 0xc4, 0x30, 0x5d, 0xc3, 0x66, 0x0f,
49
+ 0x1f, 0x44, 0x00, 0x00, 0x55, 0x48, 0x89, 0xe5, 0x53, 0x41, 0x54, 0x31,
50
+ 0xdb, 0x48, 0x85, 0xf6, 0x0f, 0x84, 0x3d, 0x01, 0x00, 0x00, 0x4c, 0x8b,
51
+ 0x5d, 0x10, 0x49, 0x89, 0xcc, 0x66, 0x48, 0x0f, 0x6e, 0xc2, 0x66, 0x0f,
52
+ 0x6c, 0xc0, 0x49, 0x83, 0xf8, 0x04, 0x0f, 0x87, 0xe9, 0x00, 0x00, 0x00,
53
+ 0xf3, 0x0f, 0x7e, 0x11, 0x66, 0x0f, 0x6c, 0xd2, 0x49, 0x83, 0xf8, 0x01,
54
+ 0x74, 0x29, 0xf3, 0x0f, 0x7e, 0x59, 0x08, 0x66, 0x0f, 0x6c, 0xdb, 0x49,
55
+ 0x83, 0xf8, 0x02, 0x74, 0x28, 0xf3, 0x0f, 0x7e, 0x61, 0x10, 0x66, 0x0f,
56
+ 0x6c, 0xe4, 0x49, 0x83, 0xf8, 0x03, 0x74, 0x23, 0xf3, 0x0f, 0x7e, 0x69,
57
+ 0x18, 0x66, 0x0f, 0x6c, 0xed, 0xeb, 0x1c, 0x66, 0x0f, 0x6f, 0xda, 0x66,
58
+ 0x0f, 0x6f, 0xe2, 0x66, 0x0f, 0x6f, 0xea, 0xeb, 0x0e, 0x66, 0x0f, 0x6f,
59
+ 0xe2, 0x66, 0x0f, 0x6f, 0xea, 0xeb, 0x04, 0x66, 0x0f, 0x6f, 0xea, 0x4d,
60
+ 0x31, 0xd2, 0x48, 0x89, 0xf0, 0x48, 0x83, 0xe0, 0xfe, 0x49, 0x39, 0xc2,
61
+ 0x73, 0x62, 0xf3, 0x42, 0x0f, 0x6f, 0x0c, 0xd7, 0x66, 0x0f, 0xdb, 0xc8,
62
+ 0x66, 0x0f, 0x6f, 0xf1, 0x66, 0x0f, 0x38, 0x29, 0xf2, 0x66, 0x0f, 0x6f,
63
+ 0xf9, 0x66, 0x0f, 0x38, 0x29, 0xfb, 0x66, 0x0f, 0xeb, 0xf7, 0x66, 0x0f,
64
+ 0x6f, 0xf9, 0x66, 0x0f, 0x38, 0x29, 0xfc, 0x66, 0x0f, 0xeb, 0xf7, 0x66,
65
+ 0x0f, 0x6f, 0xf9, 0x66, 0x0f, 0x38, 0x29, 0xfd, 0x66, 0x0f, 0xeb, 0xf7,
66
+ 0x66, 0x0f, 0x50, 0xce, 0x84, 0xc9, 0x74, 0x1a, 0xf6, 0xc1, 0x01, 0x74,
67
+ 0x05, 0xe8, 0x68, 0x00, 0x00, 0x00, 0xf6, 0xc1, 0x02, 0x74, 0x0b, 0x49,
68
+ 0xff, 0xc2, 0xe8, 0x5b, 0x00, 0x00, 0x00, 0x49, 0xff, 0xca, 0x49, 0x83,
69
+ 0xc2, 0x02, 0xeb, 0x99, 0x49, 0x39, 0xf2, 0x73, 0x5a, 0x4a, 0x8b, 0x04,
70
+ 0xd7, 0x48, 0x21, 0xd0, 0x31, 0xc9, 0x4c, 0x39, 0xc1, 0x73, 0x4c, 0x49,
71
+ 0x3b, 0x04, 0xcc, 0x75, 0x07, 0xe8, 0x34, 0x00, 0x00, 0x00, 0xeb, 0x3f,
72
+ 0x48, 0xff, 0xc1, 0xeb, 0xe9, 0x4d, 0x31, 0xd2, 0x49, 0x39, 0xf2, 0x73,
73
+ 0x32, 0x4a, 0x8b, 0x04, 0xd7, 0x48, 0x21, 0xd0, 0x31, 0xc9, 0x4c, 0x39,
74
+ 0xc1, 0x73, 0x12, 0x49, 0x3b, 0x04, 0xcc, 0x75, 0x07, 0xe8, 0x0c, 0x00,
75
+ 0x00, 0x00, 0xeb, 0x05, 0x48, 0xff, 0xc1, 0xeb, 0xe9, 0x49, 0xff, 0xc2,
76
+ 0xeb, 0xd6, 0x4c, 0x39, 0xdb, 0x73, 0x04, 0x45, 0x89, 0x14, 0x99, 0x48,
77
+ 0xff, 0xc3, 0xc3, 0x48, 0x89, 0xd8, 0x41, 0x5c, 0x5b, 0x5d, 0xc3, 0x66,
78
+ 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x48, 0x89, 0xe5,
79
+ 0x53, 0x41, 0x54, 0x31, 0xdb, 0x48, 0x85, 0xf6, 0x0f, 0x84, 0xc3, 0x00,
80
+ 0x00, 0x00, 0x49, 0x89, 0xcc, 0x66, 0x48, 0x0f, 0x6e, 0xc2, 0x66, 0x0f,
81
+ 0x6c, 0xc0, 0x66, 0x48, 0x0f, 0x6e, 0xd1, 0x66, 0x0f, 0x6c, 0xd2, 0x4d,
82
+ 0x31, 0xd2, 0x49, 0x89, 0xf3, 0x49, 0x83, 0xe3, 0xfc, 0x4d, 0x39, 0xda,
83
+ 0x73, 0x77, 0xf3, 0x42, 0x0f, 0x6f, 0x0c, 0xd7, 0xf3, 0x42, 0x0f, 0x6f,
84
+ 0x5c, 0xd7, 0x10, 0x66, 0x0f, 0xdb, 0xc8, 0x66, 0x0f, 0xdb, 0xd8, 0x66,
85
+ 0x0f, 0x38, 0x29, 0xca, 0x66, 0x0f, 0x38, 0x29, 0xda, 0x66, 0x0f, 0x6f,
86
+ 0xe1, 0x66, 0x0f, 0xeb, 0xe3, 0x66, 0x0f, 0x50, 0xc4, 0x85, 0xc0, 0x74,
87
+ 0x42, 0x66, 0x0f, 0x50, 0xc1, 0xa8, 0x01, 0x74, 0x05, 0xe8, 0x56, 0x00,
88
+ 0x00, 0x00, 0xa8, 0x02, 0x74, 0x0b, 0x49, 0xff, 0xc2, 0xe8, 0x4a, 0x00,
89
+ 0x00, 0x00, 0x49, 0xff, 0xca, 0x66, 0x0f, 0x50, 0xc3, 0xa8, 0x01, 0x74,
90
+ 0x0d, 0x49, 0x83, 0xc2, 0x02, 0xe8, 0x36, 0x00, 0x00, 0x00, 0x49, 0x83,
91
+ 0xea, 0x02, 0xa8, 0x02, 0x74, 0x0d, 0x49, 0x83, 0xc2, 0x03, 0xe8, 0x25,
92
+ 0x00, 0x00, 0x00, 0x49, 0x83, 0xea, 0x03, 0x49, 0x83, 0xc2, 0x04, 0xeb,
93
+ 0x84, 0x49, 0x39, 0xf2, 0x73, 0x23, 0x4a, 0x8b, 0x04, 0xd7, 0x48, 0x21,
94
+ 0xd0, 0x4c, 0x39, 0xe0, 0x75, 0x05, 0xe8, 0x05, 0x00, 0x00, 0x00, 0x49,
95
+ 0xff, 0xc2, 0xeb, 0xe5, 0x4c, 0x39, 0xcb, 0x73, 0x04, 0x45, 0x89, 0x14,
96
+ 0x98, 0x48, 0xff, 0xc3, 0xc3, 0x48, 0x89, 0xd8, 0x41, 0x5c, 0x5b, 0x5d,
97
+ 0xc3
98
+ ]);
99
+ export const SYMBOLS = {
100
+ scan: 0x220,
101
+ scan1: 0x380,
102
+ scan1_parallel: 0x110,
103
+ scan_parallel: 0x0,
104
+ };
@@ -0,0 +1,11 @@
1
+ export interface Hit {
2
+ value: NativePointer;
3
+ index: number;
4
+ }
5
+ export declare const DEFAULT_ISA_MASK: UInt64;
6
+ export declare class Scanner {
7
+ #private;
8
+ constructor(nthreads?: number);
9
+ scanRemoteRegion(remoteBase: NativePointer, localBuf: NativePointer, byteLen: number, mask: UInt64, targets: (UInt64 | number)[], capPerThread?: number): Hit[];
10
+ scanLocal(buf: NativePointer, qcount: number | UInt64, mask: UInt64, targets: (UInt64 | number)[], capPerThread?: number): Hit[];
11
+ }
@@ -0,0 +1,140 @@
1
+ import { SCAN_ARM64_BYTES, SYMBOLS as SYMBOLS_ARM64 } from "./bytes-arm64.js";
2
+ import { SCAN_X86_64_BYTES, SYMBOLS as SYMBOLS_X86_64 } from "./bytes-x86_64.js";
3
+ // arm64e PAC + non-pointer-isa bits masked off; leaves bits 3..47.
4
+ export const DEFAULT_ISA_MASK = uint64("0x00007ffffffffff8");
5
+ const PARALLEL_THRESHOLD_BYTES = 4 * 1024 * 1024;
6
+ const QOS_CLASS_USER_INITIATED = 0x21;
7
+ export class Scanner {
8
+ #scan;
9
+ #scan1;
10
+ #scanParallel;
11
+ #scan1Parallel;
12
+ #dispatchApplyF;
13
+ #queue;
14
+ #nthreads;
15
+ // Pin the JIT page so the runtime doesn't reclaim it while the
16
+ // NativeFunction descriptors below still hold raw pointers into it.
17
+ #code;
18
+ constructor(nthreads) {
19
+ const blob = selectBlob();
20
+ this.#code = Memory.alloc(Math.max(blob.bytes.byteLength, Process.pageSize));
21
+ Memory.patchCode(this.#code, blob.bytes.byteLength, (dst) => {
22
+ dst.writeByteArray(blob.bytes.buffer);
23
+ });
24
+ this.#scan = new NativeFunction(this.#code.add(blob.symbols.scan), "uint64", ["pointer", "uint64", "uint64", "pointer", "uint64", "pointer", "uint64"]);
25
+ this.#scan1 = new NativeFunction(this.#code.add(blob.symbols.scan1), "uint64", ["pointer", "uint64", "uint64", "uint64", "pointer", "uint64"]);
26
+ this.#scanParallel = new NativeFunction(this.#code.add(blob.symbols.scan_parallel), "void", ["pointer", "pointer", "pointer", "uint64", "uint64", "pointer", "uint64", "pointer", "uint64",
27
+ "pointer", "pointer", "uint64"]);
28
+ this.#scan1Parallel = new NativeFunction(this.#code.add(blob.symbols.scan1_parallel), "void", ["pointer", "pointer", "pointer", "uint64", "uint64", "uint64", "pointer", "uint64",
29
+ "pointer", "pointer", "uint64"]);
30
+ const libSystem = Process.getModuleByName("libSystem.B.dylib");
31
+ this.#dispatchApplyF = libSystem.getExportByName("dispatch_apply_f");
32
+ const getQueue = new NativeFunction(libSystem.getExportByName("dispatch_get_global_queue"), "pointer", ["long", "ulong"]);
33
+ this.#queue = getQueue(QOS_CLASS_USER_INITIATED, 0);
34
+ this.#nthreads = hwThreadCount(libSystem, nthreads);
35
+ }
36
+ scanRemoteRegion(remoteBase, localBuf, byteLen, mask, targets, capPerThread = 1024) {
37
+ const qcount = Math.floor(byteLen / 8);
38
+ const localHits = this.scanLocal(localBuf, qcount, mask, targets, capPerThread);
39
+ return localHits.map(h => ({
40
+ value: remoteBase.add(h.index * 8),
41
+ index: h.index,
42
+ }));
43
+ }
44
+ scanLocal(buf, qcount, mask, targets, capPerThread = 1024) {
45
+ const qc = uint64(qcount);
46
+ const small = qc.toNumber() * 8 < PARALLEL_THRESHOLD_BYTES;
47
+ if (targets.length === 1) {
48
+ const target = uint64(targets[0]).and(mask);
49
+ if (small) {
50
+ return this.#runScan1Single(buf, qc, mask, target, capPerThread);
51
+ }
52
+ return this.#runScan1Parallel(buf, qc, mask, target, capPerThread);
53
+ }
54
+ const targetsMem = packTargets(targets, mask);
55
+ if (small) {
56
+ return this.#runScanSingle(buf, qc, mask, targetsMem, targets.length, capPerThread);
57
+ }
58
+ return this.#runScanParallel(buf, qc, mask, targetsMem, targets.length, capPerThread);
59
+ }
60
+ #runScan1Single(buf, qc, mask, target, cap) {
61
+ const out = Memory.alloc(cap * 4);
62
+ const hits = this.#scan1(buf, qc, mask, target, out, uint64(cap)).toNumber();
63
+ return readHits(buf, out, Math.min(hits, cap), 0);
64
+ }
65
+ #runScan1Parallel(buf, qc, mask, target, capPerThread) {
66
+ const nthreads = this.#nthreads;
67
+ const scratch = allocScratch(nthreads, capPerThread);
68
+ this.#scan1Parallel(this.#dispatchApplyF, this.#queue, buf, qc, mask, target, scratch.outFlat, uint64(capPerThread), scratch.counts, scratch.offs, uint64(nthreads));
69
+ return collectHits(buf, scratch.outFlat, scratch.counts, scratch.offs, nthreads, capPerThread);
70
+ }
71
+ #runScanSingle(buf, qc, mask, targetsMem, ntargets, cap) {
72
+ const out = Memory.alloc(cap * 4);
73
+ const hits = this.#scan(buf, qc, mask, targetsMem, uint64(ntargets), out, uint64(cap)).toNumber();
74
+ return readHits(buf, out, Math.min(hits, cap), 0);
75
+ }
76
+ #runScanParallel(buf, qc, mask, targetsMem, ntargets, capPerThread) {
77
+ const nthreads = this.#nthreads;
78
+ const scratch = allocScratch(nthreads, capPerThread);
79
+ this.#scanParallel(this.#dispatchApplyF, this.#queue, buf, qc, mask, targetsMem, uint64(ntargets), scratch.outFlat, uint64(capPerThread), scratch.counts, scratch.offs, uint64(nthreads));
80
+ return collectHits(buf, scratch.outFlat, scratch.counts, scratch.offs, nthreads, capPerThread);
81
+ }
82
+ }
83
+ function allocScratch(nthreads, capPerThread) {
84
+ const outFlatBytes = nthreads * capPerThread * 4;
85
+ const countsBytes = nthreads * 8;
86
+ const offsBytes = nthreads * 8;
87
+ const scratch = Memory.alloc(outFlatBytes + countsBytes + offsBytes);
88
+ return {
89
+ outFlat: scratch,
90
+ counts: scratch.add(outFlatBytes),
91
+ offs: scratch.add(outFlatBytes + countsBytes),
92
+ };
93
+ }
94
+ function packTargets(targets, mask) {
95
+ const mem = Memory.alloc(Math.max(1, targets.length) * 8);
96
+ for (let i = 0; i !== targets.length; i++) {
97
+ const masked = uint64(targets[i]).and(mask);
98
+ mem.add(i * 8).writeU64(masked);
99
+ }
100
+ return mem;
101
+ }
102
+ function collectHits(buf, outFlat, counts, offs, nthreads, capPerThread) {
103
+ const hits = [];
104
+ for (let i = 0; i !== nthreads; i++) {
105
+ const count = counts.add(i * 8).readU64().toNumber();
106
+ const chunkOff = offs.add(i * 8).readU64().toNumber();
107
+ const base = outFlat.add(i * capPerThread * 4);
108
+ hits.push(...readHits(buf, base, Math.min(count, capPerThread), chunkOff));
109
+ }
110
+ return hits;
111
+ }
112
+ function readHits(buf, out, n, chunkOff) {
113
+ const hits = [];
114
+ for (let j = 0; j !== n; j++) {
115
+ const index = chunkOff + out.add(j * 4).readU32();
116
+ hits.push({ value: buf.add(index * 8), index });
117
+ }
118
+ return hits;
119
+ }
120
+ function selectBlob() {
121
+ if (Process.arch === "x64") {
122
+ return { bytes: SCAN_X86_64_BYTES, symbols: SYMBOLS_X86_64 };
123
+ }
124
+ if (Process.arch === "arm64") {
125
+ return { bytes: SCAN_ARM64_BYTES, symbols: SYMBOLS_ARM64 };
126
+ }
127
+ throw new Error(`frida-mem-scan: unsupported arch ${Process.arch}`);
128
+ }
129
+ function hwThreadCount(libSystem, override) {
130
+ if (override !== undefined) {
131
+ return override;
132
+ }
133
+ const sysctlbyname = new NativeFunction(libSystem.getExportByName("sysctlbyname"), "int", ["pointer", "pointer", "pointer", "pointer", "size_t"]);
134
+ const name = Memory.allocUtf8String("hw.logicalcpu");
135
+ const out = Memory.alloc(4);
136
+ const len = Memory.alloc(8);
137
+ len.writeU64(4);
138
+ sysctlbyname(name, out, len, NULL, 0);
139
+ return out.readU32();
140
+ }
@@ -0,0 +1 @@
1
+ export declare function resolveTarget(t: string): UInt64;
@@ -0,0 +1,16 @@
1
+ export function resolveTarget(t) {
2
+ if (!t.includes("!")) {
3
+ return uint64(t);
4
+ }
5
+ const m = t.match(/^([^!]+)!([^+]+)(?:\+(0x[0-9a-fA-F]+|\d+))?$/);
6
+ if (m === null) {
7
+ throw new Error(`bad target spec: ${t}`);
8
+ }
9
+ const [, modName, symName, offStr] = m;
10
+ let addr = Process.getModuleByName(modName).getSymbolByName(symName);
11
+ if (offStr !== undefined) {
12
+ const off = offStr.startsWith("0x") ? parseInt(offStr, 16) : parseInt(offStr, 10);
13
+ addr = addr.add(off);
14
+ }
15
+ return uint64(addr.toString());
16
+ }
@@ -0,0 +1,26 @@
1
+ import { ProcessMemory, Region, RegionFilter } from "./process/types.js";
2
+ export type TargetSpec = string | NativePointer | UInt64 | number;
3
+ export interface FindOpts {
4
+ mask?: UInt64;
5
+ filter?: RegionFilter;
6
+ capPerThread?: number;
7
+ }
8
+ export interface ScanHit {
9
+ addr: NativePointer;
10
+ target: UInt64;
11
+ region: {
12
+ base: NativePointer;
13
+ size: number;
14
+ tag: number;
15
+ };
16
+ }
17
+ export declare class Target {
18
+ #private;
19
+ readonly memory: ProcessMemory;
20
+ static self(): Target;
21
+ static pid(pid: number): Target;
22
+ constructor(memory: ProcessMemory);
23
+ find(targets: TargetSpec[], opts?: FindOpts): ScanHit[];
24
+ read(addr: NativePointer, size: number): ArrayBuffer;
25
+ regions(filter?: RegionFilter): Iterable<Region>;
26
+ }
package/dist/target.js ADDED
@@ -0,0 +1,78 @@
1
+ import { openProcessMemory } from "./process/index.js";
2
+ import { DEFAULT_ISA_MASK, Scanner } from "./scanner/index.js";
3
+ import { resolveTarget } from "./symbols.js";
4
+ export class Target {
5
+ memory;
6
+ #scanner = null;
7
+ static self() {
8
+ return new Target(openProcessMemory(Process.id));
9
+ }
10
+ static pid(pid) {
11
+ return new Target(openProcessMemory(pid));
12
+ }
13
+ constructor(memory) {
14
+ this.memory = memory;
15
+ }
16
+ find(targets, opts = {}) {
17
+ const mask = opts.mask ?? DEFAULT_ISA_MASK;
18
+ const cap = opts.capPerThread ?? 1024;
19
+ const filter = opts.filter ?? { readable: true };
20
+ const targetVals = targets.map(normalizeTarget);
21
+ const maskedTargets = targetVals.map(v => v.and(mask));
22
+ const sc = this.#getScanner();
23
+ const hits = [];
24
+ for (const region of this.memory.regions(filter)) {
25
+ const sz = region.size.toNumber();
26
+ const buf = Memory.alloc(sz);
27
+ if (!this.memory.readInto(region.base, buf, sz)) {
28
+ continue;
29
+ }
30
+ const regionHits = sc.scanRemoteRegion(region.base, buf, sz, mask, targetVals, cap);
31
+ const regionInfo = { base: region.base, size: sz, tag: region.tag };
32
+ for (const h of regionHits) {
33
+ hits.push({
34
+ addr: h.value,
35
+ target: pickMatchingTarget(maskedTargets, h.value.readU64().and(mask)),
36
+ region: regionInfo,
37
+ });
38
+ }
39
+ }
40
+ return hits;
41
+ }
42
+ read(addr, size) {
43
+ const buf = this.memory.read(addr, size);
44
+ if (buf === null) {
45
+ throw new Error(`read(${addr}, ${size}) failed`);
46
+ }
47
+ return buf;
48
+ }
49
+ regions(filter) {
50
+ return this.memory.regions(filter);
51
+ }
52
+ #getScanner() {
53
+ if (this.#scanner === null) {
54
+ this.#scanner = new Scanner();
55
+ }
56
+ return this.#scanner;
57
+ }
58
+ }
59
+ function normalizeTarget(t) {
60
+ if (typeof t === "string") {
61
+ return resolveTarget(t);
62
+ }
63
+ if (typeof t === "number") {
64
+ return uint64(t);
65
+ }
66
+ if (t instanceof NativePointer) {
67
+ return uint64(t.toString());
68
+ }
69
+ return t;
70
+ }
71
+ function pickMatchingTarget(maskedTargets, slot) {
72
+ for (const t of maskedTargets) {
73
+ if (t.compare(slot) === 0) {
74
+ return t;
75
+ }
76
+ }
77
+ return slot;
78
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "frida-mem-scan",
3
+ "version": "1.0.0",
4
+ "description": "Fast SIMD-accelerated memory scanner for Frida — in-process and cross-process (PID).",
5
+ "author": "Ole André Vadla Ravnås <oleavr@frida.re>",
6
+ "license": "MIT",
7
+ "repository": "github:oleavr/frida-mem-scan",
8
+ "keywords": [
9
+ "cross-process",
10
+ "frida",
11
+ "frida-gum",
12
+ "mach",
13
+ "memory",
14
+ "neon",
15
+ "scanner",
16
+ "simd",
17
+ "sse"
18
+ ],
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "files": [
22
+ "/dist/**/*.js",
23
+ "/dist/**/*.d.ts"
24
+ ],
25
+ "type": "module",
26
+ "scripts": {
27
+ "prepare": "npm run build",
28
+ "build": "tsc",
29
+ "watch": "tsc -w"
30
+ },
31
+ "devDependencies": {
32
+ "@types/frida-gum": "^19.1.0",
33
+ "@types/node": "^18.19.130",
34
+ "typescript": "^6.0.3"
35
+ }
36
+ }