Skip to content

Instantly share code, notes, and snippets.

@widberg
Created June 15, 2025 04:37
Show Gist options
  • Select an option

  • Save widberg/781bc7fa00c777a881e572f4b14ebf48 to your computer and use it in GitHub Desktop.

Select an option

Save widberg/781bc7fa00c777a881e572f4b14ebf48 to your computer and use it in GitHub Desktop.
// The Witness Console Command Runner Frida Agent
// Usage:
// launch the game with "frida -l "the_witness_console.js" -f witness64_d3d11.exe"
// * Run print_commands() in the frida repl to print registered command names. (the help command
// doesn't list them all)
// * Run something like cmd("command argument0 argument1") to run commands.
// Some commands I tried:
// * The "go" command accepts marker names, run print_gotos() to print a list of marker names the
// game knows about, they don't all have names.
// * If you are clicked into a panel and drawing a line "solve_panel" will print out a shit tonne of
// info. You can also add 1 to any of the values here
// https://github.com/sigma144/witness-randomizer/blob/master/Source/Panels.h, converted to decimal
// since the command parser doesn't like hex and use that as an argument. It doesn't actually change
// the panel state.
// * The "ls" command lists variable "directories", "cd" can be used to navigate them, "pwd" prints
// the current working directory.
// * "set" and "print" can poke and peak the variable names in the current working directory.
// * These variables correspond to the ones in the All.variables.raw and Local.variables files. Note
// that the game checks certain variables only on startup so changing them like this might not do
// anything.
// * The agent will print out text from the console.
// Sorry, hardcoded values from steam latest 64-bit (sha256: 8d672d444df6a6df7130f25a517bdab1af92731fe2138dc5b324d519561d55a5)
const PASSED_TO_COMMAND_STUFF_PTR = ptr(0x000000014062d4b8);
const RUN_COMMAND_PTR = ptr(0x00000001402f69d0);
const REGISTER_COMMAND_PTR = ptr(0x00000001402f58b0);
const CONSOLE_LOG_PTR = ptr(0x0000000140072d40);
const GLOBALS_PTR = ptr(0x0000000140652ee8);
const REGISTER_COMMANDS_PTR = ptr(0x0000000140071b20);
const CALLBACK_WALK_MONSTER_PTR = ptr(0x00000001402d7b50); // https://www.youtube.com/watch?v=YE8MVNMzpbo
const WALK_MONSTER_COMMAND_NAME = "walk_monster";
const GOTO_STUFF_0_PTR = ptr(0x000000014062e700);
const GOTO_STUFF_1_PTR = ptr(0x000000014062d0a0);
const GET_GOTOS_VECTOR_PTR = ptr(0x00000001401875d0);
function get_command_manager() {
return GLOBALS_PTR.readPointer().add(0x10).readPointer();
}
var run_command = new NativeFunction(
RUN_COMMAND_PTR,
"void",
["pointer", "pointer", "pointer", "pointer"],
"win64"
);
// Add a "cmd" function to the repl to run command strings
globalThis.cmd = (s) => {
var command_manager = get_command_manager();
var command_thing = PASSED_TO_COMMAND_STUFF_PTR.readPointer()
.add(0x28)
.readPointer();
var result_ptr = Memory.alloc(4);
run_command(
command_manager,
command_thing,
Memory.allocAnsiString(s),
result_ptr
);
// console.log("Command result:", result_ptr.readUInt());
};
// Log stuff that would have been printed out
Interceptor.attach(CONSOLE_LOG_PTR, {
onEnter(args) {
console.log(args[0].readAnsiString());
},
});
// Log registered command
Interceptor.attach(REGISTER_COMMAND_PTR, {
onEnter(args) {
globalThis.registered_commands = globalThis.registered_commands ?? [];
globalThis.registered_commands.push(args[2].readAnsiString());
},
});
// Add a "print_commands" function to the repl to print registered commands
globalThis.print_commands = () => {
globalThis.registered_commands = globalThis.registered_commands ?? [];
console.log(globalThis.registered_commands);
};
var register_command = new NativeFunction(
REGISTER_COMMAND_PTR,
"void",
["pointer", "pointer", "pointer", "uint", "uint"],
"win64"
);
// Restore removed command
Interceptor.attach(REGISTER_COMMANDS_PTR, {
onLeave(retval) {
var command_manager = get_command_manager();
register_command(
command_manager,
CALLBACK_WALK_MONSTER_PTR,
Memory.allocAnsiString(WALK_MONSTER_COMMAND_NAME),
0,
-1
);
globalThis.registered_commands = globalThis.registered_commands ?? [];
globalThis.registered_commands.push(WALK_MONSTER_COMMAND_NAME);
},
});
var get_gotos_vector = new NativeFunction(
GET_GOTOS_VECTOR_PTR,
"pointer",
["pointer", "pointer"],
"win64"
);
// Add a "print_gotos" function to the repl to print registered markers
globalThis.print_gotos = () => {
var gotos = get_gotos_vector(
GOTO_STUFF_1_PTR.readPointer(),
GOTO_STUFF_0_PTR
);
var length = gotos.add(0x0).readUInt();
var entries = gotos.add(0x8).readPointer();
for (var i = 0; i < length; ++i) {
var entry = entries.add(i * Process.pointerSize).readPointer();
var name = entry.add(0x58).readPointer().readAnsiString();
var gotoable = (entry.add(0x10).readU64() & 0x100000000) == 0;
if (gotoable) {
console.log(name);
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment