Add summary, fixups, and brain meme
This commit is contained in:
parent
b7a8a80350
commit
e0173f270b
|
@ -1,5 +1,16 @@
|
|||
# Google CTF 2020 Write-up: Sprint
|
||||
|
||||
In this challenge, we're given a binary to reverse engineer. We find
|
||||
that it's an interpreter for a Turing-complete language made entirely
|
||||
of `sprintf` format strings, along with a program in this language.
|
||||
We write a disassembler for this language, and use it to
|
||||
reverse engineer the embedded program. We find that the embedded
|
||||
program is a game, and map out a series of moves to win the game.
|
||||
|
||||
![The galaxy brain meme. Small brain: Writing a game in a high-level
|
||||
language. Medium brain: Writing a game in x86 assembly. Large brain:
|
||||
Writing a game in sprintf format strings](brain_meme.jpg)
|
||||
|
||||
## Picking apart the binary
|
||||
|
||||
We're given a single binary file. Running it through `objdump`, we
|
||||
|
@ -149,7 +160,7 @@ In order:
|
|||
to by vararg 3. That is, it writes it to the lower two bytes of
|
||||
`regs[0]`.
|
||||
3. `%1$65498s` writes 65498 spaces, so the total number of spaces so
|
||||
far is 65536, or `0x10000`.
|
||||
far is 65536, or `0x10000`.
|
||||
4. `%1$28672s` writes 28672 spaces, or `0x7000` spaces.
|
||||
5. `%9$hn` counts the number of characters written so far, and writes
|
||||
it to the `short int` pointed to by vararg 9. That is, the lower
|
||||
|
@ -346,7 +357,7 @@ Now we can see the output:
|
|||
### Using `gdb`
|
||||
|
||||
Now we have yet another chunk of assembly to reverse. It will be
|
||||
useful to be able to use `gdb` to step through this code, so lets
|
||||
useful to be able to use `gdb` to step through this code, so let's
|
||||
understand how to do that. The call to `sprintf` is at `main+555`, so
|
||||
by putting a breakpoint there, we can step through the embedded code
|
||||
one instruction at a time. To view the 10 registers, we can examine
|
||||
|
@ -404,7 +415,9 @@ but I'll leave that out for brevity.
|
|||
|
||||
We'll jump straight to the first place where we read the user input.
|
||||
Instead of picking apart the code before that, we can just use `gdb`
|
||||
to examine the state of the program at this point.
|
||||
to examine the state of the program at this point. Since the earlier
|
||||
code doesn't reference the user input, the state here will always be
|
||||
the same.
|
||||
|
||||
0374: mov 0xe000, %r2 # Address of user input
|
||||
039a: mov 0x0, %r3 # Counter, initialized to 0
|
||||
|
@ -444,7 +457,7 @@ Next we load some data into registers
|
|||
0536: mov 0x0, %r2
|
||||
0558: mov 0x0, %r3
|
||||
057b: mov 0xf100, %r1
|
||||
05a1: mov (%r1), %r4 # Read from 0xf100, value = 0x11
|
||||
05a1: mov (%r1), %r4 # Read from 0xf100, value = 0x11
|
||||
05c6: mov 0x1, %r5
|
||||
05e9: mov 0x0, %r9
|
||||
|
||||
|
@ -588,7 +601,7 @@ can't ever enter an invalid direction, and we can't ever step on
|
|||
nonzero terrain. We also see that we need to step on exactly nine
|
||||
checkpoints.
|
||||
|
||||
The register `%r9` appears to hold a "reason code" for why we lose.
|
||||
The register `%r9` appears to hold a reason code for why we lose.
|
||||
This could be useful for debugging later.
|
||||
|
||||
There's still more code after, but for now our task is clear: we need
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 253 KiB |
Loading…
Reference in New Issue