Skip to content

Instantly share code, notes, and snippets.

@oleavr
Last active July 6, 2021 19:04
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oleavr/51066491b6961b608fb38fb1fb971dd3 to your computer and use it in GitHub Desktop.
Save oleavr/51066491b6961b608fb38fb1fb971dd3 to your computer and use it in GitHub Desktop.
Quake REST API demo
import * as Koa from "koa";
import * as Router from "koa-router";
const app = new Koa();
const router = new Router();
enum Stat {
Health = 0,
Shells = 6,
}
interface Stats {
health: number;
shells: number;
}
router
.get("/stats", async (ctx, next) => {
ctx.body = await readStats();
})
.post("/attack", async (ctx, next) => {
await attack();
ctx.body = {};
});
app
.use(router.routes())
.use(router.allowedMethods())
.listen(1337);
const clientState = importSymbol("cl");
const attackDown: any = new NativeFunction(importSymbol("IN_AttackDown"), "void", []);
const attackUp: any = new NativeFunction(importSymbol("IN_AttackUp"), "void", []);
function readStats(): Promise<Stats> {
return perform(() => {
return {
health: readStat(Stat.Health),
shells: readStat(Stat.Shells),
};
});
}
function readStat(stat: Stat): number {
return Memory.readInt(clientState.add(28 + stat * 4));
}
async function attack(): Promise<void> {
await perform(() => {
attackDown();
});
await sleep(50);
await perform(() => {
attackUp();
});
}
function sleep(duration: number): Promise<void> {
return new Promise(resolve => {
setTimeout(() => { resolve(); }, duration);
});
}
type PendingWork = () => any;
const pending: PendingWork[] = [];
function perform<T>(f: () => T): Promise<T> {
return new Promise((resolve, reject) => {
pending.push(() => {
try {
const result = f();
resolve(result);
} catch (e) {
reject(e);
}
});
});
}
Interceptor.attach(importSymbol("IN_SendKeyEvents"), {
onEnter() {
while (pending.length > 0) {
const f = pending.shift();
f();
}
}
});
function importSymbol(name: string): NativePointer {
return Module.findExportByName("QuakeSpasm", name);
}
{
"name": "quake-rest-api",
"version": "1.0.0",
"description": "Quake REST API",
"private": true,
"main": "agent/index.ts",
"scripts": {
"prepare": "npm run build",
"build": "frida-compile agent/index.ts -o _agent.js -x",
"watch": "frida-compile agent/index.ts -o _agent.js -x -w"
},
"devDependencies": {
"@types/koa": "^2.0.46",
"@types/koa-router": "^7.0.31",
"frida-compile": "^6.0.0",
"frida-gum-types": "^2.0.0",
"koa": "^2.5.2",
"koa-router": "^7.4.0"
}
}

Build

npm install

Run

$ frida QuakeSpasm --enable-jit -l _agent.js
$ curl -s http://localhost:1337/stats | jq
$ curl -s -X POST http://localhost:1337/attack | jq

Rebuild

npm run build

Iterate

npm run watch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment