Another day, another revision of the Lockitall Bluetooth lock. This writeup is for the Reykjavik level, which I hopefully will be able to spell without hesitation by the end of this blog post.
Software Updates
The Lockitall release notes are getting a bit cheeky:
This is Software Revision 02. This release contains military-grade
encryption so users can be confident that the passwords they enter
can not be read from memory. We apologize for making it too easy
for the password to be recovered on prior versions. The engineers
responsible have been sacked.
Poor engineers. Well, time to ruin their day again.
Reykjavik Code
If we take a peek at main
, it’s a bit different from other levels:
That’s it. Moving some memory around, calling enc
, and then we’re done. Huh.
If we look at enc
, we see:
4486 <enc>
4486: 0b12 push r11
4488: 0a12 push r10
448a: 0912 push r9
448c: 0812 push r8
448e: 0d43 clr r13
4490: cd4d 7c24 mov.b r13, 0x247c(r13)
4494: 1d53 inc r13
4496: 3d90 0001 cmp #0x100, r13
449a: fa23 jne #0x4490 <enc+0xa>
449c: 3c40 7c24 mov #0x247c, r12
44a0: 0d43 clr r13
44a2: 0b4d mov r13, r11
44a4: 684c mov.b @r12, r8
44a6: 4a48 mov.b r8, r10
44a8: 0d5a add r10, r13
44aa: 0a4b mov r11, r10
44ac: 3af0 0f00 and #0xf, r10
44b0: 5a4a 7244 mov.b 0x4472(r10), r10
44b4: 8a11 sxt r10
44b6: 0d5a add r10, r13
44b8: 3df0 ff00 and #0xff, r13
44bc: 0a4d mov r13, r10
44be: 3a50 7c24 add #0x247c, r10
44c2: 694a mov.b @r10, r9
44c4: ca48 0000 mov.b r8, 0x0(r10)
44c8: cc49 0000 mov.b r9, 0x0(r12)
44cc: 1b53 inc r11
44ce: 1c53 inc r12
44d0: 3b90 0001 cmp #0x100, r11
44d4: e723 jne #0x44a4 <enc+0x1e>
44d6: 0b43 clr r11
44d8: 0c4b mov r11, r12
44da: 183c jmp #0x450c <enc+0x86>
44dc: 1c53 inc r12
44de: 3cf0 ff00 and #0xff, r12
44e2: 0a4c mov r12, r10
44e4: 3a50 7c24 add #0x247c, r10
44e8: 684a mov.b @r10, r8
44ea: 4b58 add.b r8, r11
44ec: 4b4b mov.b r11, r11
44ee: 0d4b mov r11, r13
44f0: 3d50 7c24 add #0x247c, r13
44f4: 694d mov.b @r13, r9
44f6: cd48 0000 mov.b r8, 0x0(r13)
44fa: ca49 0000 mov.b r9, 0x0(r10)
44fe: 695d add.b @r13, r9
4500: 4d49 mov.b r9, r13
4502: dfed 7c24 0000 xor.b 0x247c(r13), 0x0(r15)
4508: 1f53 inc r15
450a: 3e53 add #-0x1, r14
450c: 0e93 tst r14
450e: e623 jnz #0x44dc <enc+0x56>
4510: 3841 pop r8
4512: 3941 pop r9
4514: 3a41 pop r10
4516: 3b41 pop r11
4518: 3041 ret
Phew, that’s a lot of assembly.
Let’s just ignore that for a minute.
Back to main, this was confusing to me the first time I did it, and I’m still a little confused:
4438 <main>
4438: 3e40 2045 mov #0x4520, r14
443c: 0f4e mov r14, r15
443e: 3e40 f800 mov #0xf8, r14
4442: 3f40 0024 mov #0x2400, r15
4446: b012 8644 call #0x4486 <enc>
444a: b012 0024 call #0x2400
444e: 0f43 clr r15
We’re moving 0x4520 into r14. Then, we’re moving that into r15 (so now r15 contains “4520”). After that, we move 0xf8 into r14, overwriting the value of 4520. Okay.
But then we move 0x2400 into r15, overwriting 0x4520. What was the point of that earlier instruction then? 🤷♀️
Disassembly
Anyway… we’re gonna call enc
, then we’ll call whatever is going on at 0x2400. What is going on at 0x2400?
None of that is human readable, unfortunately. However, if we can call it, is it just assembly instructions stored in a different place?
All of… that, but with the extra text stripped out, is:
4c85 1bc5 80df e9bf 3864 2bc6 4277 62b8
c3ca d965 a40a c1a3 bbd1 a6ea b3eb 180f
78af ea7e 5c8e c695 cb6f b8e9 333c 5aa1
5cee 906b d1aa a1c3 a986 8d14 08a5 a22c
baa5 1957 192d abe1 66b9 70ff 4a08 e95c
d919 8069 07a5 ef01 caa2 a30d f344 815e
3e10 e765 2bc8 2837 abad ab3f 8cfa 754d
8ff0 b083 6b3e b3c7 aefe b409
If you head over to the Assembler page on Microcorruption, you can copy/paste that and hit “Disassemble”.
Here’s what we get:
Oooooh boy. My plan to avoid lengthy assembly blobs didn’t really pan out.
Empirical Learning
Let’s avoid the assembly for just a minute and step through the code.
I set a breakpoint before enc
. Here’s what the memory block starting at 0x2400 looked like at that point:
After enc
(using another breakpoint), that same memory block looks like this:
So clearly, something happened here, and maybe the assembly I showed earlier is no longer relevant.
Right now, we’re stopped at this breakpoint.
From here, type s
to start stepping through the assembly instructions stored at 0x2400. If you watch the “Current Instruction” window, you can get a general sense of what’s going on, one instruction at a time.
For example, at this point, we’re getting ready to prompt the user for a password:
How do I know? Let’s look at 0x4520.
However, as you’re stepping through this, you’re probably noticing that it’s taking an ETERNITY and ain’t nobody got time for that.
Let’s try to get a bigger-picture view of the situation.
Back to the Assembler, Batman
If you go back to this page, you can assemble (or disassemble) instructions.
This time, let’s take the post-enc values from 0x2400.
0b12 0412 0441 2452 3150 e0ff 3b40 2045
073c 1b53 8f11 0f12 0312 b012 6424 2152
6f4b 4f93 f623 3012 0a00 0312 b012 6424
2152 3012 1f00 3f40 dcff 0f54 0f12 2312
b012 6424 3150 0600 b490 1711 dcff 0520
3012 7f00 b012 6424 2153 3150 2000 3441
3b41 3041 1e41 0200 0212 0f4e 8f10 024f
32d0 0080 b012 1000 3241 3041 d21a 189a
22dc 45b9 4279 2d55 858e a4a2 67d7 14ae
a119 76f6 42cb 1c04 0efa a61b 74a7 416b
d237 a253 22e4 66af c1a5 938b 8971 9b88
fa9b 6674 4e21 2a6b b143 9151 3dcc a6f5
daa7 db3f 8d3c 4d18 4736 dfa6 459a 2461
921d 3291 14e6 8157 b0fe 2ddd 400b 8688
6310 3ab3 612b 0bd9 483f 4e04 5870 4c38
c93c ff36 0e01 7f3e fa55 aeef 051c 242c
3c56 13af e57b 8abf 3040 c537 656e 8278
9af9 9d02 be83 b38c e181 3ad8 395a fce3
4f03 8ec9 9395 4a15 ce3b fd1e 7779 c9c3
5ff2 3dc7 5953 8826 d0b5 d9f8 639e e970
01cd 2119 ca6a d12c 97e2 7538 96c5 8f28
d682 1be5 ab20 7389 48aa 1fa3 472f a564
de2d b710 9081 5205 8d44 cff4 bc2e 577a
d5f4 a851 c243 277d a4ca 1e6b 0000 0000
Hit “Disassemble” and see the results:
Again, this is cut for length. I grabbed the assembly up to the second “ret”:
0b12 push r11
0412 push r4
0441 mov sp, r4
2452 add #0x4, r4
3150 e0ff add #0xffe0, sp
3b40 2045 mov #0x4520, r11
073c jmp $+0x10
1b53 inc r11
8f11 sxt r15
0f12 push r15
0312 push #0x0
b012 6424 call #0x2464
2152 add #0x4, sp
6f4b mov.b @r11, r15
4f93 tst.b r15
f623 jnz $-0x12
3012 0a00 push #0xa
0312 push #0x0
b012 6424 call #0x2464
2152 add #0x4, sp
3012 1f00 push #0x1f
3f40 dcff mov #0xffdc, r15
0f54 add r4, r15
0f12 push r15
2312 push #0x2
b012 6424 call #0x2464
3150 0600 add #0x6, sp
b490 1711 dcff cmp #0x1117, -0x24(r4)
0520 jnz $+0xc
3012 7f00 push #0x7f
b012 6424 call #0x2464
2153 incd sp
3150 2000 add #0x20, sp
3441 pop r4
3b41 pop r11
3041 ret
1e41 0200 mov 0x2(sp), r14
0212 push sr
0f4e mov r14, r15
8f10 swpb r15
024f mov r15, sr
32d0 0080 bis #0x8000, sr
b012 1000 call #0x10
3241 pop sr
3041 ret
Why did I stop there? Because the instructions after that looked weird and were making calls to places in memory like:
d21a 189a call &0x9a18
Is 0x9a18 even a place in memory in this example? Further down, we see addresses that end in an odd digit. Wat.
Anyway… that second block (after the first ret) reminds me of the interrupt that we’ve seen in previous examples:
1e41 0200 mov 0x2(sp), r14
0212 push sr
0f4e mov r14, r15
8f10 swpb r15
024f mov r15, sr
32d0 0080 bis #0x8000, sr
b012 1000 call #0x10
3241 pop sr
3041 ret
In the first block, there are multiple lines that call 0x2464.
b012 6424 call #0x2464
Let’s figure out what that’s about. Set a breakpoint by typing break 2464
.
That wasn’t very exciting. If we keep stepping (or continuing) to our breakpoint, we see that we’re slowly typing out “what’s the password?". We also see that we’re going through the interrupt-looking code to print each char.
Let’s assume that we can ignore the interrupt-ish code, and also ignore the code printing up “what’s the password” (or rather, I removed all the code up to the last “call 0x2464” line). That leaves us with:
3150 0600 add #0x6, sp
b490 1711 dcff cmp #0x1117, -0x24(r4)
0520 jnz $+0xc
3012 7f00 push #0x7f
b012 6424 call #0x2464
2153 incd sp
3150 2000 add #0x20, sp
3441 pop r4
3b41 pop r11
3041 ret
Which is much more manageable.
Reykjavik Solution
So, if my assumptions hold up, the answer is somewhere in those 9 lines of assembly. The compare (cmp
) looks interesting… presumably if we gave a password, it needs to compare it against a known good value somewhere.
It’s comparing some value to “0x1117”. That doesn’t look like it’s in the ASCII readable range, but let’s give it a shot. If we type “AAAA” as our password, we see the sr
change on the next step
, then program execution ends shortly after.
If we reset, and enter in “1711” as our password (hex-encoded), let’s see what happens. “1117” becomes “1711” because of endianness.
And it worked!
To solve this level, type “solve” and then enter the password (hex-encoded) of “1711”.