QEMU - Playable-Quotes/QuoteKit GitHub Wiki

Launch the emulator

$ qemu-system-i386 -m 64M -hda software.qcow2 -monitor stdio #where software.qcow2 is a qcow2 file of the software

Attach Frida to the emulator

$ frida -l script.js qemu-system-i386

Hook the main loop

let main_loop_addr = DebugSymbol.fromName("main_loop_wait").address;
Interceptor.attach(main_loop_addr, () => {
      ...
    }
})

Compute call rates

let count = 0
let start = null;

let main_loop_addr = DebugSymbol.fromName("main_loop_wait").address;
Interceptor.attach(main_loop_addr, () => {
    if (start === null) {
        start = Date.now()
    }
    const now = Date.now()
    let avg = (now - start) / count++;
    if (count % 100 == 1) {
        console.log('avg: ' + avg + 'ms')
        console.log('fps: ' + 1/avg * 1000)
    }
    
})

Get the main memory range

let mainMemoryRange = mainModule.base; // Get the base address of the main module
let mainMemorySize = mainModule.size; // Get the size of the main module

console.log(`Main module base address: ${mainMemoryRange}`);
console.log(`Main module size: ${mainMemorySize}`);

Get a save state

let main_loop_wait = DebugSymbol.fromName("main_loop_wait").address;
let save_state = DebugSymbol.fromName("save_snapshot").address;

const save_state_native = new NativeFunction(save_state, "bool", 
["pointer", "bool", "pointer", "bool", "pointer", "pointer"]);

let snapshot_count = 0;

Interceptor.attach(main_loop_wait, () => {
  let res = save_state_native(Memory.allocUtf8String(`snapshot_${snapshot_count++}`), 1, NULL, 0, NULL, NULL);
  console.log(`save_state: ${res}`);
});