After completing the tutorial tutorial level, explained in detail here, we are tossed into the wild. New Orleans is the first level that no longer guides the process of debugging and reversing nor holds our hand or gives us fancy hints. We are now facing the assembly code all by ourselves. Let’s break it!
As we will always do, the first thing to inspect is our
main function because that’s where our program starts. (This may change in the future)
New Orleans’ main has the following code:
438 <main> 4438: 3150 9cff add #0xff9c, sp 443c: b012 7e44 call #0x447e <create_password> 4440: 3f40 e444 mov #0x44e4 "Enter the password to continue", r15 4444: b012 9445 call #0x4594 <puts> 4448: 0f41 mov sp, r15 444a: b012 b244 call #0x44b2 <get_password> 444e: 0f41 mov sp, r15 4450: b012 bc44 call #0x44bc <check_password> 4454: 0f93 tst r15 4456: 0520 jnz #0x4462 <main+0x2a> 4458: 3f40 0345 mov #0x4503 "Invalid password; try again.", r15 445c: b012 9445 call #0x4594 <puts> 4460: 063c jmp #0x446e <main+0x36> 4462: 3f40 2045 mov #0x4520 "Access Granted!", r15 4466: b012 9445 call #0x4594 <puts> 446a: b012 d644 call #0x44d6 <unlock_door> 446e: 0f43 clr r15 4470: 3150 6400 add #0x64, sp
The structure is almost identical to previous level main. A string gets printed, user inserts his/her password, the password is checked and depending on that check result, which is stored in
r15, it either unlocks the door or jumps over it and executes until
__stop_progExec__ is reached and program ends with no success.
Nevertheless, there is a little but very important difference. At address
0x4450 there is a call to
create_password function. Let’s find out what
create_password is up to.
447e <create_password> 447e: 3f40 0024 mov #0x2400, r15 4482: ff40 6600 0000 mov.b #0x66, 0x0(r15) 4488: ff40 6900 0100 mov.b #0x69, 0x1(r15) 448e: ff40 7300 0200 mov.b #0x73, 0x2(r15) 4494: ff40 3500 0300 mov.b #0x35, 0x3(r15) 449a: ff40 6100 0400 mov.b #0x61, 0x4(r15) 44a0: ff40 6b00 0500 mov.b #0x6b, 0x5(r15) 44a6: ff40 2900 0600 mov.b #0x29, 0x6(r15) 44ac: cf43 0700 mov.b #0x0, 0x7(r15) 44b0: 3041 ret
First of all, at instruction
0x447e, the program is placing the hex number 0x2400 into
r15. At this point you may feel a little confused about when a value is a simply number, an integer, or a memoy address. It does not matter. It is just data being placed into a register. Data is represented in bits that form bytes and those bytes are placed either in the stack, heap, registers, etc… They are simply bytes. The difference comes at the time of reading, using, interpreting and manipulating those bytes. For example, if you would now do a mov r15, sp, sp content would automatically be 0x2400 and that would be treated as a memory address since sp is a pointer that points (wow) to some memory location.
The next 8 instructions, from
0x44ac, are moving bytes intro addresses relatives to
r15. This is called relative offset addressing. You can read about it here and about general relative addressing here. Relative addressing is coded in the way of
r15 is 0x2400, instruction at
0x4482 will place #0x66 into address 0x2400, instruction at
0x4488 will place #0x69 into address 0x2401 and so forth.
You will notice, however, the instruction being used is
mov.b rather than
mov. In MSP430 family,
We do also have a very useful excerpt from MSP430’s manual which provides us a very handy instruction’s set overview.
create_password does is placing a sequence of 8 bytes starting from
0x2400 all the way to
0x2407. (Plase note that the bytes placed at these addresses may differ. I guess they are randomly generated based on every single user’s name.)
In order to check that, we can place a breakpoint at
0x44b0, right before
create_password finishes, execute te program and inspect memory contents at that very moment.
You will notice that when the program reaches the breakpoint, at address
0x2400 there will be indeed the aforementioned bytes.
You will notice in the
create_password moves 8 bytes starting from address
0x2400. That’s because the last byte is the null trailing byte that marks the end of a char sequence, also called a string, and it is not ASCII printable. You can see the full ASCII printable characters here.
If we look back at
main for a moment, we can see
unlock_door gets called at address
0x446a and it’s executed based upon the result of instruction at address
0x4544 which is set by
check_password. Let’s find out what does
44bc <check_password> 44bc: 0e43 clr r14 44be: 0d4f mov r15, r13 44c0: 0d5e add r14, r13 44c2: ee9d 0024 cmp.b @r13, 0x2400(r14) 44c6: 0520 jne #0x44d2 <check_password+0x16> 44c8: 1e53 inc r14 44ca: 3e92 cmp #0x8, r14 44cc: f823 jne #0x44be <check_password+0x2> 44ce: 1f43 mov #0x1, r15 44d0: 3041 ret 44d2: 0f43 clr r15 44d4: 3041 ret
The first thing we see is some registers work. It clears out
r14 (that is, it performs
r14 = 0), it moves content of
r13 and adds
r13 += r14). In order to know what
r15 value is at this point, we can either set a breakpoint at instruction
0x44be for example, or simply look at the code itself. The
main function, before calling
mov sp, r15 at
0x444e. This means that
r15 points at the address where our input is living in memory (stack).
0x44c2 we see there is a byte comparison.
cmp.b @r13, 0x2400(r14) is simply performing, in human language, (compare the byte stored at address saved in r13 with contents of the address stored at r14 with 0x2400 offset). Remember the
cmp instruction what really does inside is
0x2400(r14) - @r13 and sets the flags accordingly without saving the result.
If result of the comparison is other than zero, that is, the bytes are not equal,
0x44c6 will get executed jumping all the way down to
0x44d2 finishing the function. If the result is zero, if the bytes are equal,
r14 will be incremented by one.
0x44ca there is
cmp #0x8, r14 which compares the value of r14 with hexadecimal number 0x8. If the two values are not equal, it jumps back to
0x44be starting a new iteration of the loop. IF they indeed are equal, hexadecimal 0x1 is placed into
r15 and exits the function. (Remember that
r15 must be different from zero after
check_password so main can call
In other words,
check_password is checking byte per byte the user input against the password created by
create_password that was previously stored at address
0x2400. Please note how
r14 gets incremented by one with every character from user’s input that matches the bytes at ‘0x2400’. That’s the way the function iterates over the stored bytes, with relative addressing. Think it that way:
Suppose r15 is 0x4000.
Repeat until r14 ix 0x8
r14 = 0x0. r13 = r15 + r14 (0x4000 + 0x0). Byte at 0x4000 equals to byte at 0x2400(0)? If so, r14 += 0x1 and go to Iteration 2.
r14 = 0x1. r13 = 0x4000 + 0x1. Byte ate 0x4001 equals to byte at 0x2400(1)? If so, r14 += 0x1 and go to Iteration 3.
0x2400(1) is exactly the same as
1(0x2400). That’s what relative addressing is.
create_password is placing into memory.
Input can be either inserted as text or hexadecimal encoded.
In this level we’ve seen how important is to read the code seeking for hardcoded values, what ASCII is and what .b suffix means in the context of MSP430 assembly language.