Port debug scripts to new API

This commit is contained in:
RunasSudo 2017-02-12 22:43:28 +10:30
parent 68e428ecc7
commit 86bcc094fa
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
11 changed files with 43 additions and 72 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@
/tools/ackermann.o
/tools/venv
/tools/*.pdf
__pycache__

View File

@ -1,6 +1,6 @@
# synacor.py
My sort-of-OOP, ~~poorly-documented~~concise response to the Synacor challenge
My OOP, ~~poorly-documented~~ ~~concise~~ working response to the Synacor challenge
## Debug commands
@ -12,15 +12,15 @@ This will execute the file `<cmd>.py` with `dbg_args[0]` set to `<cmd>` and `<ar
For example, the self-test and decryption at the beginning of the program takes a comparatively long time. To save the state to the `dumps/init` file, enter:
.dbg_dump dumps/init
.dbg/dump dumps/init
Similarly, debug commands may be passed as command-line arguments to `synacor.py` in the form:
./synacor.py <cmd> <args>
./synacor.py <file> <cmd> <args>
For example, to load the `dumps/init` state to skip the self-test and decryption, run:
./synacor.py dbg_load dumps/init
./synacor.py challenge.bin dbg/load dumps/init
Dump files are stored in Python [pickle](https://docs.python.org/3/library/pickle.html) format, so if you want to inspect the memory in a hex editor, for example, it will be necessary to extract a raw memory dump:

View File

@ -1,5 +1,5 @@
# synacor.py - An implementation of the Synacor Challenge
# Copyright © 2016 RunasSudo
# Copyright © 2016–2017 RunasSudo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -20,10 +20,10 @@ if len(dbg_args) < 2:
print('Usage: .{} <file>'.format(dbg_args[0]))
else:
model = {
'SYN_PTR': SYN_PTR,
'SYN_MEM': SYN_MEM,
'SYN_REG': SYN_REG,
'SYN_STK': SYN_STK
'SYN_PTR': cpu.SYN_PTR,
'SYN_MEM': cpu.SYN_MEM,
'SYN_REG': cpu.SYN_REG,
'SYN_STK': cpu.SYN_STK
}
with open(dbg_args[1], 'wb') as f:

View File

@ -14,4 +14,12 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
DBG_FLAG = True
# Emulate 06bb
for R1 in range(0x17b4, 0x7562):
R0 = cpu.SYN_MEM[R1]
R0 ^= pow(R1, 2, 32768)
R0 ^= 0x4154
cpu.SYN_MEM[R1] = R0
# Jump past self-test
cpu.SYN_PTR = 0x0377

View File

@ -1,5 +1,5 @@
# synacor.py - An implementation of the Synacor Challenge
# Copyright © 2016 RunasSudo
# Copyright © 2016–2017 RunasSudo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -22,7 +22,7 @@ else:
with open(dbg_args[1], 'rb') as f:
model = pickle.load(f)
SYN_PTR = model['SYN_PTR']
SYN_MEM = model['SYN_MEM']
SYN_REG = model['SYN_REG']
SYN_STK = model['SYN_STK']
cpu.SYN_PTR = model['SYN_PTR']
cpu.SYN_MEM = model['SYN_MEM']
cpu.SYN_REG = model['SYN_REG']
cpu.SYN_STK = model['SYN_STK']

View File

@ -97,16 +97,5 @@ take mirror
use mirror
"""
# Read code into memory
SYN_PTR = 0
with open('challenge.bin', 'rb') as data:
while True:
byteData = data.read(2)
if len(byteData) < 2:
break
SYN_MEM[SYN_PTR] = struct.unpack('<H', byteData)[0]
SYN_PTR += 1
SYN_PTR = 0
# We cannot directly set SYN_STDIN_BUF since debug commands are processed only at stdin read
sys.stdin = io.StringIO(transcript)

View File

@ -1,5 +1,5 @@
# synacor.py - An implementation of the Synacor Challenge
# Copyright © 2016 RunasSudo
# Copyright © 2016–2017 RunasSudo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -15,9 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Set R7 to 6486
SYN_REG[7] = 0x6486
cpu.SYN_REG[7] = 0x6486
# Patch instructions 1571 to 1579 inclusive with nop's
SYN_MEM[0x1571:0x157a] = [21] * 9
cpu.SYN_MEM[0x1571:0x157a] = [21] * 9
print('Patched. Ready to run "use teleporter".')

View File

@ -1,39 +0,0 @@
# synacor.py - An implementation of the Synacor Challenge
# Copyright © 2017 RunasSudo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pickle
# Read code into memory
SYN_MEM = [0] * 32768
with open('challenge.bin', 'rb') as data:
i = 0
while True:
byteData = data.read(2)
if len(byteData) < 2:
break
SYN_MEM[i] = struct.unpack('<H', byteData)[0]
i += 1
# Emulate 06bb
for R1 in range(0x17b4, 0x7562):
R0 = SYN_MEM[R1]
R0 ^= pow(R1, 2, 32768)
R0 ^= 0x4154
SYN_MEM[R1] = R0
# Jump past self-test
SYN_PTR = 0x0377

View File

@ -196,6 +196,12 @@ class InstructionIn(Instruction):
while len(cpu.SYN_STDIN_BUF) == 0:
line = sys.stdin.readline()
if line.startswith('.'):
# Debug command
dbg_args = line[1:].split()
with open(dbg_args[0] + '.py', 'r') as f:
exec(f.read(), globals(), locals())
else:
cpu.SYN_STDIN_BUF = list(line)
self.args[0].set(cpu, ord(cpu.SYN_STDIN_BUF.pop(0)))

View File

@ -88,7 +88,7 @@ Proceed to the `north` door and `use` the `teleporter` to obtain the code:
## The true-believers-only codes
At this point, you will almost certainly need to delve into the code of the challenge, if you haven't already. The code in `challenge.bin` past the self-test is encrypted, so disassembling and analysing the code is most easily done based off a memory dump from a running copy:
.dbg_dump dumps/init (From inside the game)
.dbg/dump dumps/init (From inside the game)
./tools/dump_to_raw.py dumps/init dumps/init.raw
./tools/disasm.py dumps/init.raw > dumps/init.asm
@ -232,7 +232,7 @@ Running the algorithm, the correct value is revealed to be `0x6486`. Now we simp
I've implemented this as a debug function to prepare the teleporter:
> .dbg_teleporter
> .dbg/teleporter
Patched. Ready to run "use teleporter".
> use teleporter
@ -284,4 +284,4 @@ Given that you've made it this far (You didn't cheat, did you? I did warn you at
Now that we've solved the puzzle, the only thing left is to write a [tool-assisted speed-run](https://github.com/RunasSudo/synacor.py/blob/master/dbg_speedrun.py) to completely break any given instance of the challenge in 5 seconds.
time python -u synacor.py dbg_speedrun | head -n 849
time python -u synacor.py challenge.bin dbg/speedrun | head -n 849

View File

@ -23,5 +23,10 @@ cpu = CPU()
with open(sys.argv[1], 'rb') as data:
cpu.SYN_MEM = memory_from_file(data)
if len(sys.argv) > 2:
dbg_args = sys.argv[2:]
with open(dbg_args[0] + '.py', 'r') as f:
exec(f.read(), globals(), locals())
while True:
cpu.step()