Up next, Sydney! Our prompt says…
Wait, what was that last bit?
We have received reports that the prior version of the lock was bypassable without knowing the password. We have fixed this and removed the password from memory.
Booooooooo ☹️
Sydney Program Structure
So, if we check out the main section of the code again, we can get an idea of how this program works, and what might be different (compared to the New Orleans level).
So it isn’t too different from last time, aside from the fact that this level has no create_password
function. Generally speaking, this program:
- Prompts the user for their password.
- Reads in the user response.
- Checks it against a known good value (presumably… somewhere…)
- Grants access (or not)
Nothing too different from the previous level. Let’s see if we can figure out how it determines if a password is valid or not.
That check_password
function looks interesting…
Checking Passwords
I set a breakpoint in the check_password
function. You can either click on line 448a, or type break check_password
in the debugger console. Reset
the program if you need to, and then type c
to continue until you get to your breakpoint. I gave the program a password attempt of “AAAAAAAA”
The first line says to compare the value 0x7d71 with the value at the address of r15 (starting with the 0th byte).
What is that address? Take a peek at the Register State view.
R15 is (hex) “439c”. If we look at our Live Memory Dump:
See where all the “41"s start? That’s the hex value for ASCII character “A”, which is what I entered as the password. If you count over from 4390, you see that the “A"s start at… 0x439c. Yay.
Okay, so we’re comparing 0x7d71 with the first two bytes of memory at 0x439c… which are “AA” or “0x4141”. We can type s
to step to the next instruction. Okay, so that comparison is obviously not going to work. Because our comparison failed, we’ll get kicked down to 0x44ac where r14 is cleared, moved into r15, and we return.
Before we go, however, let’s see what happens when we know the answer is wrong. Another way of asking this is: how does the microcontroller know what the result of the cmp
instruction was? It looks at the status register or sr
. You may have noticed that before the cmp
instruction, the value of sr
was 0000. Now, it’s 0004.
If we reset
and try again, and enter in “}q”, which is the ASCII equivalent of “7d 71″… we see… oh no. The sr
is equal to 0004 again. What’s going on?
The issue is the order in which we’re providing the bytes.
Even though it looks right (see the 7d 71 in the image above), it’s out of order to the computer. This is called endianness.
It’s a bit of a “reading right-to-left vs left-to-right” problem, but for computers. What it means for us is that we have to swap our order. If we reset
and enter in “q}” instead of “}q”, and continue to our breakpoint:
Once we’re at our breakpoint, type s
to step to the next instruction. Our sr
register is set to 0003 this time. Interesting!
You can read more about the status register and conditional flags here. The difference in sr
values means that we’re on the right path.
Continuing On…
If we look at the check_password function again, we can get an idea of what we need to provide as a password.
448a <check_password>
448a: bf90 717d 0000 cmp #0x7d71, 0x0(r15)
4490: 0d20 jnz $+0x1c
4492: bf90 6672 0200 cmp #0x7266, 0x2(r15)
4498: 0920 jnz $+0x14
449a: bf90 5c5f 0400 cmp #0x5f5c, 0x4(r15)
44a0: 0520 jne #0x44ac <check_password+0x22>
44a2: 1e43 mov #0x1, r14
44a4: bf90 4939 0600 cmp #0x3949, 0x6(r15)
44aa: 0124 jeq #0x44ae <check_password+0x24>
44ac: 0e43 clr r14
44ae: 0f4e mov r14, r15
44b0: 3041 ret
We need 0x7d71, adjusted for endianness, and then 0x7266 (also adjusted), and then 0x5f5c (yep, also adjusted).
That is… “q}fr_” Wow. This password’s got entropy. After resetting, and entering that password, we can get all the way to this line:
We’ve moved a value of “1” into r14, which could give us a return value of “true” or “1”, meaning that we gave the correct password.
But… we’ve still got one hurdle left to clear. The last two bytes of the password are compared to 0x3949. Let’s add that to our password and try again: “q}fr_I9”
Sydney Solution
TL;DR The password, as we’ve found by looking at the comparison of two bytes of input at a time, is “q}fr_I9”. We had to consider endianness, and also use the status register comparison flags to help us find our way.
To solve the level, type “solve” and then enter in “q}fr_I9” as the password. Yay!