Port debug scripts to new API
This commit is contained in:
parent
68e428ecc7
commit
86bcc094fa
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,3 +4,5 @@
|
|||||||
/tools/ackermann.o
|
/tools/ackermann.o
|
||||||
/tools/venv
|
/tools/venv
|
||||||
/tools/*.pdf
|
/tools/*.pdf
|
||||||
|
|
||||||
|
__pycache__
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# synacor.py
|
# 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
|
## 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:
|
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:
|
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:
|
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:
|
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:
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# synacor.py - An implementation of the Synacor Challenge
|
# 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
|
# 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
|
# 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]))
|
print('Usage: .{} <file>'.format(dbg_args[0]))
|
||||||
else:
|
else:
|
||||||
model = {
|
model = {
|
||||||
'SYN_PTR': SYN_PTR,
|
'SYN_PTR': cpu.SYN_PTR,
|
||||||
'SYN_MEM': SYN_MEM,
|
'SYN_MEM': cpu.SYN_MEM,
|
||||||
'SYN_REG': SYN_REG,
|
'SYN_REG': cpu.SYN_REG,
|
||||||
'SYN_STK': SYN_STK
|
'SYN_STK': cpu.SYN_STK
|
||||||
}
|
}
|
||||||
|
|
||||||
with open(dbg_args[1], 'wb') as f:
|
with open(dbg_args[1], 'wb') as f:
|
@ -14,4 +14,12 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# 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
|
@ -1,5 +1,5 @@
|
|||||||
# synacor.py - An implementation of the Synacor Challenge
|
# 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
|
# 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
|
# 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:
|
with open(dbg_args[1], 'rb') as f:
|
||||||
model = pickle.load(f)
|
model = pickle.load(f)
|
||||||
|
|
||||||
SYN_PTR = model['SYN_PTR']
|
cpu.SYN_PTR = model['SYN_PTR']
|
||||||
SYN_MEM = model['SYN_MEM']
|
cpu.SYN_MEM = model['SYN_MEM']
|
||||||
SYN_REG = model['SYN_REG']
|
cpu.SYN_REG = model['SYN_REG']
|
||||||
SYN_STK = model['SYN_STK']
|
cpu.SYN_STK = model['SYN_STK']
|
@ -97,16 +97,5 @@ take mirror
|
|||||||
use 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
|
# We cannot directly set SYN_STDIN_BUF since debug commands are processed only at stdin read
|
||||||
sys.stdin = io.StringIO(transcript)
|
sys.stdin = io.StringIO(transcript)
|
@ -1,5 +1,5 @@
|
|||||||
# synacor.py - An implementation of the Synacor Challenge
|
# 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
|
# 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
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# Set R7 to 6486
|
# Set R7 to 6486
|
||||||
SYN_REG[7] = 0x6486
|
cpu.SYN_REG[7] = 0x6486
|
||||||
|
|
||||||
# Patch instructions 1571 to 1579 inclusive with nop's
|
# 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".')
|
print('Patched. Ready to run "use teleporter".')
|
@ -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
|
|
@ -196,6 +196,12 @@ class InstructionIn(Instruction):
|
|||||||
|
|
||||||
while len(cpu.SYN_STDIN_BUF) == 0:
|
while len(cpu.SYN_STDIN_BUF) == 0:
|
||||||
line = sys.stdin.readline()
|
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)
|
cpu.SYN_STDIN_BUF = list(line)
|
||||||
|
|
||||||
self.args[0].set(cpu, ord(cpu.SYN_STDIN_BUF.pop(0)))
|
self.args[0].set(cpu, ord(cpu.SYN_STDIN_BUF.pop(0)))
|
||||||
|
6
notes.md
6
notes.md
@ -88,7 +88,7 @@ Proceed to the `north` door and `use` the `teleporter` to obtain the code:
|
|||||||
## The true-believers-only codes
|
## 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:
|
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/dump_to_raw.py dumps/init dumps/init.raw
|
||||||
./tools/disasm.py dumps/init.raw > dumps/init.asm
|
./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:
|
I've implemented this as a debug function to prepare the teleporter:
|
||||||
|
|
||||||
> .dbg_teleporter
|
> .dbg/teleporter
|
||||||
Patched. Ready to run "use teleporter".
|
Patched. Ready to run "use teleporter".
|
||||||
> 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.
|
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
|
||||||
|
@ -23,5 +23,10 @@ cpu = CPU()
|
|||||||
with open(sys.argv[1], 'rb') as data:
|
with open(sys.argv[1], 'rb') as data:
|
||||||
cpu.SYN_MEM = memory_from_file(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:
|
while True:
|
||||||
cpu.step()
|
cpu.step()
|
||||||
|
Reference in New Issue
Block a user