timkurvers/synacor-challenge

JavaScript / ES6+ solution for Synacor Challenge

★ 2Forks 0JavaScriptGitHub ↗Compare

Project website ↗

README

Synacor Challenge

Node Version MIT License CI

JavaScript / ES6+ virtual machine for https://challenge.synacor.com.

The architecture spec is bundled in this repository.

See synacor-binja-plugin for a Binary Ninja plugin capable of analyzing Synacor programs.

Setup

Install Node.js 12 or higher for your platform.

Install dependencies through npm:

npm install

Running VM

To run a program (from the programs-folder) in the VM:

npm start programs/alphabet.synbin

Scenarios

Rather than typing in user input or commands manually, a scenario can be fed directly via stdin:

cat scenarios/challenge.txt - | npm run start programs/challenge.synbin

Note: - is required to ensure the VM does not terminate after the scenario is exhausted, but rather stays alive and accepts further input.

Debug commands

Whenever the program in the VM is awaiting input or the debugger is interrupted, commands prefixed with $ are executed as JavaScript code using eval.

Use this to easily inspect and mutate program state:

$ registers
[
  25975, 25974, 26006,
      0,   101,     0,
      0,     0
]

$ address = 0xa0a
2570

For convenience the hex and hexoffset helpers are also available:

$ hexoffset(address)
0xa0a

To dump raw memory for the current program for analysis:

$ dump()
raw memory dumped to: programs/challenge-20200618T223617810Z.synbin

Analysis

To analyze the official challenge binary and generate a visualization of the Synacor world to visualizations/synacore-map.dot:

npm run analyze programs/challenge.synbin

Locations annotated with '❗️' have special behaviour on entry.

External Debugging

Debuggers like gdb do not support arbitrary architectures like Synacor out of the box.

However, by piggybacking on the GDB remote protocol and replicating packets sent back and forth we can trick gdb into happily controlling a Synacor program whilst running in a JavaScript VM within a Node.js process. 🤯

Features supported:

  • Breakpoints (break)
  • Control flow (stepping using stepi and continue)

To debug a program and listen for "remote gdb" connections on localhost:31337:

npm run debug programs/alphabet.synbin

Fire up gdb in a separate shell and attach to the remote target:

(gdb) target remote :31337
Remote debugging using :31337
0x00000000 in ?? ()
(gdb) b *0x2a
Breakpoint 1 at 0x2a

(gdb) stepi
0x00000004 in ?? ()

(gdb) c
Continuing.

Breakpoint 1, 0x0000002a in ?? ()
(gdb) c
Continuing.

Program terminated with signal SIGTERM, Terminated.

The debugger allows interrupting a running program by sending Ctrl+C from gdb:

(gdb) c
Continuing.
^C
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00002f44 in ?? ()

Binary Ninja

See synacor-binja-plugin for debugging Synacor programs using Binary Ninja.

Options

To listen on a different host/port use GDB_HOST and GDB_PORT:

GDB_HOST=customhost GDB_PORT=1337 npm run debug programs/alphabet.synbin

To log all GDB remote protocol traffic use GDB_LOG:

GDB_LOG= npm run debug programs/alphabet.synbin

Development

To monitor code changes and re-run programs in the VM during development:

npm run start:dev programs/alphabet.synbin

Similary, for the debugger during development:

npm run debug:dev programs/alphabet.synbin

Contributors

timkurversdependabot[bot]

Issues