wowdump 0.2.1 → 0.3.2
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/LICENSE +1 -1
- package/README.md +17 -111
- package/dist/adapters/reader.js +33 -0
- package/dist/analysis/disassemble.js +77 -0
- package/dist/{frida-runtime.js → analysis/frida-runtime.js} +3 -25
- package/dist/analysis/runtime-script.js +36 -0
- package/dist/cli.js +563 -0
- package/dist/core/profile-engine.js +238 -0
- package/dist/frida-worker.js +99 -0
- package/dist/reader/broker.js +475 -0
- package/dist/reader/client.js +1 -0
- package/dist/reader/launcher.js +219 -0
- package/dist/reader/main.js +100 -0
- package/dist/reader/protocol.js +1 -0
- package/dist/reader/windows.js +242 -0
- package/dist/reader-main.js +2 -0
- package/dist/toolchain.js +123 -0
- package/package.json +19 -37
- package/skills/wowdump/SKILL.md +22 -0
- package/skills/wowdump/references/commands.md +63 -0
- package/skills/wowdump/references/disassemble.md +18 -0
- package/skills/wowdump/references/dynamic.md +54 -0
- package/skills/wowdump/references/evidence-workflow.md +41 -0
- package/skills/wowdump/references/profiles.md +34 -0
- package/skills/wowdump/references/request-schema.md +28 -0
- package/skills/wowdump/references/workflow.md +44 -0
- package/skills/wowdump/scripts/dynamic-session.js +133 -0
- package/dist/agent.js +0 -1335
- package/dist/analysis-path.js +0 -38
- package/dist/analysis-process-log.js +0 -146
- package/dist/broker-client.js +0 -411
- package/dist/broker-codec.js +0 -148
- package/dist/broker-core.js +0 -1045
- package/dist/broker-gateway.js +0 -447
- package/dist/broker-ledger.js +0 -196
- package/dist/broker-main.js +0 -291
- package/dist/broker-protocol.js +0 -119
- package/dist/broker-runtime.js +0 -1283
- package/dist/broker-server.js +0 -466
- package/dist/build-bundle-loader.js +0 -183
- package/dist/build-bundle.js +0 -11
- package/dist/discovery.js +0 -59
- package/dist/dry-run.js +0 -38
- package/dist/error-log.js +0 -71
- package/dist/focus-errors.js +0 -63
- package/dist/focus-service.js +0 -1855
- package/dist/focused-session.js +0 -1357
- package/dist/mcp-main.js +0 -51
- package/dist/mcp.js +0 -924
- package/dist/observability.js +0 -41
- package/dist/process-log-lock.js +0 -195
- package/dist/processes.js +0 -47
- package/dist/runtime-config.js +0 -399
- package/dist/session.js +0 -145
- package/dist/storage.js +0 -12
- package/dist/wow-analysis.js +0 -1430
- package/resources/builds/retail/12.0.7.68974/build-profile.json +0 -290
- package/resources/builds/retail/12.0.7.68974/data-sources.json +0 -1633
- package/resources/builds/retail/12.0.7.68974/lua-targets.jsonl +0 -5130
- package/resources/builds/retail/12.0.7.68974/manifest.json +0 -63
- package/resources/builds/retail/12.0.7.68974/signatures.json +0 -260
- /package/dist/{adapters.js → core/build-adapters.js} +0 -0
- /package/dist/{types.js → core/types.js} +0 -0
package/dist/discovery.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
-
import { basename, join, resolve } from "node:path";
|
|
3
|
-
function parseBuildInfo(file) {
|
|
4
|
-
if (!existsSync(file))
|
|
5
|
-
return [];
|
|
6
|
-
const lines = readFileSync(file, "utf8").split(/\r?\n/).filter(Boolean);
|
|
7
|
-
if (lines.length < 2)
|
|
8
|
-
return [];
|
|
9
|
-
const headers = lines[0].split("|").map(header => header.split("!")[0]);
|
|
10
|
-
return lines.slice(1).map(line => {
|
|
11
|
-
const values = line.split("|");
|
|
12
|
-
return Object.fromEntries(headers.map((header, i) => [header, values[i] ?? ""]));
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
function flavorFromDirectory(name) {
|
|
16
|
-
return name.startsWith("_") && name.endsWith("_") ? name.slice(1, -1) : name;
|
|
17
|
-
}
|
|
18
|
-
export function discoverInstalls(gameRoot) {
|
|
19
|
-
const rootBuildRows = parseBuildInfo(join(gameRoot, ".build.info"));
|
|
20
|
-
let entries = [];
|
|
21
|
-
try {
|
|
22
|
-
entries = readdirSync(gameRoot, { withFileTypes: true })
|
|
23
|
-
.filter(entry => entry.isDirectory())
|
|
24
|
-
.map(entry => join(gameRoot, entry.name));
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
29
|
-
return entries.flatMap(root => {
|
|
30
|
-
const executable = join(root, "Wow.exe");
|
|
31
|
-
if (!existsSync(executable))
|
|
32
|
-
return [];
|
|
33
|
-
const flavor = flavorFromDirectory(basename(root));
|
|
34
|
-
const wantedProduct = flavor.includes("classic") || flavor.includes("anniversary")
|
|
35
|
-
? "wow_classic" : "wow";
|
|
36
|
-
const rootBuild = rootBuildRows.find(row => row.Product === wantedProduct) ?? rootBuildRows[0] ?? {};
|
|
37
|
-
const version = rootBuild.Version ?? rootBuild.ProductVersion ?? rootBuild.Build;
|
|
38
|
-
const fileVersion = rootBuild.ProductVersion;
|
|
39
|
-
const build = {
|
|
40
|
-
product: rootBuild.Product ?? rootBuild.ProductName,
|
|
41
|
-
version,
|
|
42
|
-
branch: rootBuild.Branch,
|
|
43
|
-
fileVersion,
|
|
44
|
-
buildKey: [flavor, version || fileVersion || "unknown"].filter(Boolean).join("@")
|
|
45
|
-
};
|
|
46
|
-
return [{ flavor, root, executable, build }];
|
|
47
|
-
}).sort((a, b) => a.flavor.localeCompare(b.flavor));
|
|
48
|
-
}
|
|
49
|
-
export function discoverInstallsAcrossRoots(gameRoots, discover = discoverInstalls) {
|
|
50
|
-
const installs = new Map();
|
|
51
|
-
for (const gameRoot of gameRoots) {
|
|
52
|
-
for (const install of discover(gameRoot)) {
|
|
53
|
-
const key = resolve(install.executable).toLowerCase();
|
|
54
|
-
if (!installs.has(key))
|
|
55
|
-
installs.set(key, install);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return [...installs.values()].sort((a, b) => a.flavor.localeCompare(b.flavor) || a.executable.localeCompare(b.executable));
|
|
59
|
-
}
|
package/dist/dry-run.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { getObservationTiers } from "./observability.js";
|
|
2
|
-
function toAdapterFields(lookup) {
|
|
3
|
-
if (lookup.status === "verified" && lookup.adapter) {
|
|
4
|
-
return {
|
|
5
|
-
adapterStatus: lookup.status,
|
|
6
|
-
observationTiers: getObservationTiers(lookup.adapter),
|
|
7
|
-
adapterName: lookup.adapter.name
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
return {
|
|
11
|
-
adapterStatus: lookup.status,
|
|
12
|
-
observationTiers: [],
|
|
13
|
-
adapterReason: lookup.reason
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
function installStatus(install, registry) {
|
|
17
|
-
return {
|
|
18
|
-
flavor: install.flavor,
|
|
19
|
-
buildKey: install.build.buildKey,
|
|
20
|
-
...toAdapterFields(registry.lookup(install.build.buildKey))
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export function createDryRunReport(installs, processes, registry) {
|
|
24
|
-
return {
|
|
25
|
-
mode: "dry-run",
|
|
26
|
-
installs: installs
|
|
27
|
-
.map(install => installStatus(install, registry))
|
|
28
|
-
.sort((a, b) => a.flavor.localeCompare(b.flavor)),
|
|
29
|
-
processes: processes
|
|
30
|
-
.map(process => ({
|
|
31
|
-
pid: process.pid,
|
|
32
|
-
executable: process.executable,
|
|
33
|
-
...(process.startTime ? { startTime: process.startTime } : {}),
|
|
34
|
-
...installStatus(process.install, registry)
|
|
35
|
-
}))
|
|
36
|
-
.sort((a, b) => a.pid - b.pid)
|
|
37
|
-
};
|
|
38
|
-
}
|
package/dist/error-log.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { ErrorEventSchema } from "./types.js";
|
|
3
|
-
const DEFAULT_LIMIT = 100;
|
|
4
|
-
const MAX_LIMIT = 1000;
|
|
5
|
-
function validateLimit(limit) {
|
|
6
|
-
const value = limit ?? DEFAULT_LIMIT;
|
|
7
|
-
if (!Number.isInteger(value) || value < 1 || value > MAX_LIMIT) {
|
|
8
|
-
throw new RangeError(`limit must be an integer between 1 and ${MAX_LIMIT}`);
|
|
9
|
-
}
|
|
10
|
-
return value;
|
|
11
|
-
}
|
|
12
|
-
function matches(event, query) {
|
|
13
|
-
return (query.pid === undefined || event.pid === query.pid)
|
|
14
|
-
&& (query.buildKey === undefined || event.buildKey === query.buildKey)
|
|
15
|
-
&& (query.flavor === undefined || event.flavor === query.flavor)
|
|
16
|
-
&& (query.addon === undefined || event.addon === query.addon);
|
|
17
|
-
}
|
|
18
|
-
function schemaMessage(error) {
|
|
19
|
-
return error.issues
|
|
20
|
-
.map(issue => `${issue.path.length > 0 ? issue.path.join(".") : "event"}: ${issue.message}`)
|
|
21
|
-
.join("; ");
|
|
22
|
-
}
|
|
23
|
-
export async function readErrorLog(file, query = {}) {
|
|
24
|
-
const limit = validateLimit(query.limit);
|
|
25
|
-
let contents;
|
|
26
|
-
try {
|
|
27
|
-
contents = await readFile(file, "utf8");
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
if (error.code === "ENOENT") {
|
|
31
|
-
return { events: [], diagnostics: [] };
|
|
32
|
-
}
|
|
33
|
-
throw error;
|
|
34
|
-
}
|
|
35
|
-
const diagnostics = [];
|
|
36
|
-
const matchesByTime = [];
|
|
37
|
-
for (const [index, rawLine] of contents.split(/\r?\n/u).entries()) {
|
|
38
|
-
if (rawLine.trim().length === 0)
|
|
39
|
-
continue;
|
|
40
|
-
const line = index + 1;
|
|
41
|
-
let value;
|
|
42
|
-
try {
|
|
43
|
-
value = JSON.parse(rawLine);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
diagnostics.push({
|
|
47
|
-
line,
|
|
48
|
-
code: "invalid_json",
|
|
49
|
-
message: error instanceof Error ? error.message : "Invalid JSON"
|
|
50
|
-
});
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
const parsed = ErrorEventSchema.safeParse(value);
|
|
54
|
-
if (!parsed.success) {
|
|
55
|
-
diagnostics.push({
|
|
56
|
-
line,
|
|
57
|
-
code: "invalid_event",
|
|
58
|
-
message: schemaMessage(parsed.error)
|
|
59
|
-
});
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (matches(parsed.data, query)) {
|
|
63
|
-
matchesByTime.push({ event: parsed.data, line });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
matchesByTime.sort((left, right) => left.event.timestamp - right.event.timestamp || left.line - right.line);
|
|
67
|
-
return {
|
|
68
|
-
events: matchesByTime.slice(-limit).map(item => item.event),
|
|
69
|
-
diagnostics
|
|
70
|
-
};
|
|
71
|
-
}
|
package/dist/focus-errors.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
export class FocusRequestError extends Error {
|
|
2
|
-
code;
|
|
3
|
-
details;
|
|
4
|
-
constructor(code, message, details) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.name = "FocusRequestError";
|
|
7
|
-
this.code = code;
|
|
8
|
-
this.details = details;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
export function asSelectorRequestError(error) {
|
|
12
|
-
if (error instanceof FocusRequestError)
|
|
13
|
-
return error;
|
|
14
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
-
if (/exceeding maxTargets|TARGETS_OVER_LIMIT/i.test(message)) {
|
|
16
|
-
return new FocusRequestError("TARGETS_OVER_LIMIT", message);
|
|
17
|
-
}
|
|
18
|
-
if (/must identify a target|requires (?:rvas|dataSourceIds|objectIds|ranges)|SELECTOR_EMPTY/i.test(message)) {
|
|
19
|
-
return new FocusRequestError("SELECTOR_EMPTY", message);
|
|
20
|
-
}
|
|
21
|
-
return new FocusRequestError("SELECTOR_INVALID", message);
|
|
22
|
-
}
|
|
23
|
-
const ATTACHMENT_FATAL_CODES = new Set([
|
|
24
|
-
"ATTACHMENT_FATAL",
|
|
25
|
-
"PROCESS_NOT_FOUND",
|
|
26
|
-
"PROCESS_TERMINATED",
|
|
27
|
-
"SESSION_DETACHED",
|
|
28
|
-
"SCRIPT_DESTROYED",
|
|
29
|
-
"TRANSPORT_CLOSED",
|
|
30
|
-
]);
|
|
31
|
-
const ATTACHMENT_FATAL_NAMES = new Set([
|
|
32
|
-
"ProcessNotFoundError",
|
|
33
|
-
"ProcessTerminatedError",
|
|
34
|
-
"SessionDetachedError",
|
|
35
|
-
"ScriptDestroyedError",
|
|
36
|
-
]);
|
|
37
|
-
/** Classifies only failures that unambiguously mean the Frida target attachment is gone. */
|
|
38
|
-
export function isAttachmentFatalError(error) {
|
|
39
|
-
const visited = new Set();
|
|
40
|
-
let current = error;
|
|
41
|
-
while (current !== null && current !== undefined && !visited.has(current)) {
|
|
42
|
-
visited.add(current);
|
|
43
|
-
if (typeof current === "object") {
|
|
44
|
-
const value = current;
|
|
45
|
-
if (typeof value.code === "string" && ATTACHMENT_FATAL_CODES.has(value.code.toUpperCase()))
|
|
46
|
-
return true;
|
|
47
|
-
if (typeof value.name === "string" && ATTACHMENT_FATAL_NAMES.has(value.name))
|
|
48
|
-
return true;
|
|
49
|
-
if (typeof value.message === "string" && isAttachmentFatalMessage(value.message))
|
|
50
|
-
return true;
|
|
51
|
-
current = value.cause;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
return typeof current === "string" && isAttachmentFatalMessage(current);
|
|
55
|
-
}
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
function isAttachmentFatalMessage(message) {
|
|
59
|
-
return /\bscript is destroyed\b/i.test(message)
|
|
60
|
-
|| /\bprocess (?:was |has been )?terminated\b/i.test(message)
|
|
61
|
-
|| /\btarget process (?:exited|terminated)\b/i.test(message)
|
|
62
|
-
|| /\bsession is detached\b/i.test(message);
|
|
63
|
-
}
|