Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions frida/autodetect/win32.js
Original file line number Diff line number Diff line change
Expand Up @@ -546,15 +546,31 @@ const recoverRemoteGuard = (instructions, name) => {

const candidates = [];
for (const scene of comparisons.filter((item) => item.immediate === 1101)) {
for (const mode of comparisons.filter(
const sameSource = comparisons.filter(
(item) => item.immediate === 1 && item.baseKey === scene.baseKey,
)) {
);
for (const mode of sameSource) {
candidates.push({
rootOffsets: scene.rootOffsets,
sceneOffset: scene.fieldOffset,
modeOffset: mode.fieldOffset,
});
}

// Some legacy builds read the remote-debug flag straight off the first
// argument (e.g. `cmp byte ptr [rsi + 0x29], 1` after `mov rsi, rcx`)
// instead of storing it next to the scene value. There the mode field
// has no shared base with the scene field, so `baseKey` never matches.
// Legacy detection only needs the scene side (SceneOffsets is built
// from rootOffsets + sceneOffset), so fall back to an unpaired scene
// candidate rather than rejecting the build outright.
if (sameSource.length === 0) {
candidates.push({
rootOffsets: scene.rootOffsets,
sceneOffset: scene.fieldOffset,
modeOffset: null,
});
}
}

const unique = new Map(
Expand Down Expand Up @@ -759,6 +775,12 @@ const detectModern = (module, functions, xrefs, loadStart) => {
) {
throw new Error("OnLoadStart and Start remote-debug fields disagree");
}
// Modern builds always expose a paired mode field; a null here means the
// unpaired-scene fallback was taken, which is only valid for legacy
// detection. Refuse rather than emit a config with a null offset.
if (startGuard.modeOffset === null) {
throw new Error("modern WMPF layout is missing the remote-debug mode field");
}

const launchExpression = loadExpression(argumentExpression(0), startGuard.rootOffsets[0]);
const websocketUrlOffset = recoverWebSocketUrlOffset(startInstructions, launchExpression);
Expand Down
19 changes: 12 additions & 7 deletions frida/hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,23 @@ const handleOnLoadStart = (a1, config) => {

// legacy scene config
if (config.SceneOffsets) {
// Legacy configs describe a single 6-hop scene chain. Keep it in one
// pass: re-splitting it like the modern path inserts an extra
// dereference after offset[2] and lands on the wrong struct.
miniappLaunchConfigPtr = a1
.add(structOffsets[0])
.readPointer()
.add(structOffsets[1])
.readPointer()
.add(structOffsets[2])
.readPointer();
remoteDebugConfigPtr = miniappLaunchConfigPtr
miniappScenePtr = miniappLaunchConfigPtr
.add(structOffsets[2])
.readPointer()
.add(structOffsets[3])
.readPointer()
.add(structOffsets[4])
.readPointer();

miniappScenePtr = remoteDebugParametersPtr.add(structOffsets[5]);
.readPointer()
.add(structOffsets[5]);
remoteDebugConfigPtr = miniappLaunchConfigPtr;
} else {
// later wmpf builds (win32)
const launchConfigOffsets = structOffsets.LaunchConfigOffsets;
Expand Down Expand Up @@ -134,7 +137,9 @@ const handleOnLoadStart = (a1, config) => {
miniappScenePtr.writeInt(1101);

if (config.SceneOffsets) {
// legacy path, we are done here
// Legacy path: debug mode is enabled by the args[1] |= 0x1 write in
// patchOnLoadStart. There is no websocket-URL / remote-debug-mode field
// to patch on these builds, so we are done here.
return;
}

Expand Down