CMU Bomb Lab Phase 5
Table of Contents
CMU Bomb Lab reverse engineering
Phase 5
We've reached phase 5. Let's analyze the phase_5
function:
# Section 1
0x08048d2c <+0>: push ebp
0x08048d2d <+1>: mov ebp,esp
0x08048d2f <+3>: sub esp,0x10
0x08048d32 <+6>: push esi
0x08048d33 <+7>: push ebx
0x08048d34 <+8>: mov ebx,DWORD PTR [ebp+0x8]
0x08048d37 <+11>: add esp,0xfffffff4
0x08048d3a <+14>: push ebx
0x08048d3b <+15>: call 0x8049018 <string_length>
0x08048d40 <+20>: add esp,0x10
0x08048d43 <+23>: cmp eax,0x6
0x08048d46 <+26>: je 0x8048d4d <phase_5+33>
0x08048d48 <+28>: call 0x80494fc <explode_bomb>
# Section 2
0x08048d4d <+33>: xor edx,edx # set edx to 0
0x08048d4f <+35>: lea ecx,[ebp-0x8] # Load address ebp-0x8 to ecx
0x08048d52 <+38>: mov esi,0x804b220 # load string pointer to esi
# Section 3
0x08048d57 <+43>: mov al,BYTE PTR [edx+ebx*1] # `ebx` stores the pointer to our string. This acceses character at position edx of the string and loads it to al
0x08048d5a <+46>: and al,0xf # Make & operation with the character and 0xf which is 15 in decimal
0x08048d5c <+48>: movsx eax,al # Move al register to eax register, extending from
0x08048d5f <+51>: mov al,BYTE PTR [eax+esi*1] # move character at position eax of string at esi to register al
0x08048d62 <+54>: mov BYTE PTR [edx+ecx*1],al # move al register to
0x08048d65 <+57>: inc edx # increase edx counter
0x08048d66 <+58>: cmp edx,0x5 # If we reached edx counter = 5
0x08048d69 <+61>: jle 0x8048d57 <phase_5+43> jump to instruction 43
# Section 4
0x08048d6b <+63>: mov BYTE PTR [ebp-0x2],0x0
0x08048d6f <+67>: add esp,0xfffffff8
0x08048d72 <+70>: push 0x804980b
0x08048d77 <+75>: lea eax,[ebp-0x8]
0x08048d7a <+78>: push eax
0x08048d7b <+79>: call 0x8049030 <strings_not_equal>
0x08048d80 <+84>: add esp,0x10
0x08048d83 <+87>: test eax,eax
0x08048d85 <+89>: je 0x8048d8c <phase_5+96>
0x08048d87 <+91>: call 0x80494fc <explode_bomb>
0x08048d8c <+96>: lea esp,[ebp-0x18]
0x08048d8f <+99>: pop ebx
0x08048d90 <+100>: pop esi
0x08048d91 <+101>: mov esp,ebp
0x08048d93 <+103>: pop ebp
0x08048d94 <+104>: ret
We can split the function in 4 main sections.
In the first section (instruction 0
to instruction 28
) we have the functino prologue, we allocate some space in the stack and we check the length of the input string we gave to the phase_5
function using the string_length
function. If the length is equal to 0x6
(6
in decimal) we continue, if not, the bomb explotes.
0x08048d2c <+0>: push ebp
0x08048d2d <+1>: mov ebp,esp
0x08048d2f <+3>: sub esp,0x10
0x08048d32 <+6>: push esi
0x08048d33 <+7>: push ebx
0x08048d34 <+8>: mov ebx,DWORD PTR [ebp+0x8]
0x08048d37 <+11>: add esp,0xfffffff4
0x08048d3a <+14>: push ebx
0x08048d3b <+15>: call 0x8049018 <string_length>
0x08048d40 <+20>: add esp,0x10
0x08048d43 <+23>: cmp eax,0x6
0x08048d46 <+26>: je 0x8048d4d <phase_5+33>
0x08048d48 <+28>: call 0x80494fc <explode_bomb>
We also see that the ebx
register has the input we passed on the phase 5. We can verify this in gdb doing:
(gdb) x/s $ebx
0x804b7c0 <input_strings+320>: "hello"
Then we have the second section:
# Section 2
0x08048d4d <+33>: xor edx,edx # set edx to 0
0x08048d4f <+35>: lea ecx,[ebp-0x8] # Load address ebp-0x8 to ecx
0x08048d52 <+38>: mov esi,0x804b220 # load string pointer to esi
On the first instruction we set the edx
register to 0
, then we load the address ebp-0x8
to the ecx
register and finally we load the number 0x804b220
to the register esi
, this address points to a string (char *
) which we canc check by doing:
(gdb) x/s 0x804b220
0x804b220: "isrveawhobpnutfg"
Now, in the third section, thing get interesting.
0x08048d57 <+43>: mov al,BYTE PTR [edx+ebx*1] # `ebx` stores the pointer to our string. This acceses character at position edx of the string and loads it to al
0x08048d5a <+46>: and al,0xf # Make & operation with the character and 0xf which is 15 in decimal
0x08048d5c <+48>: movsx eax,al # Move al register to eax register, extending from
0x08048d5f <+51>: mov al,BYTE PTR [eax+esi*1] # move character at position eax of string at esi to register al
0x08048d62 <+54>: mov BYTE PTR [edx+ecx*1],al # move al register to
0x08048d65 <+57>: inc edx # increase edx counter
0x08048d66 <+58>: cmp edx,0x5 # If we reached edx counter = 5
0x08048d69 <+61>: jle 0x8048d57 <phase_5+43> jump to instruction 43
If we look at the last third instructions of this section, we can asume that this section is a loop, since we increase a counter (edx
register), check if the counter is bigger than some value and if it's not, we jump back to the beginning of the section.
Ok, so now we know that we are dealing with a function that has a loop inside, but what does the loop do? Let's figure it out.
We know the counter that increases for each loop is the edx
register. We also use this register to access a specific char (BYTE
) on the string we input on the phase 5 and move that char to the register al
:
0x08048d57 <+43>: mov al,BYTE PTR [edx+ebx*1] # `ebx` stores the pointer to our string. This acceses character at position edx of the string and loads it to al
0x08048d5a <+46>: and al,0xf # Make & operation with the character and 0xf which is 15 in decimal
We then make an and
operation with that char and the number 0xf
(15
in decimal) and store it back to the al
register.
After this, we move the al
value to the eax
register.
Then on instruction 51
and 54
we have:
0x08048d5f <+51>: mov al,BYTE PTR [eax+esi*1] # move character at position eax of string at esi to register al
0x08048d62 <+54>: mov BYTE PTR [edx+ecx*1],al # move al register to
Remember, the esi
register has a pointer to the string literal "isrveawhobpnutfg"
, so we are now accesing a character on that string by the index number that's on the eax
register and moving that character to the al
register.
Finally we move the value at the al
register to the memory address that's on ecx
with an offset given by the current edx
value (which increases for each iteration on the loop)
After this, we increase the edx
value and if it's greater than 5, we break the loop, if not, we continue.
After breaking the loop (edx
register is now greater than 5) we go to the section 4 of the function:
0x08048d6b <+63>: mov BYTE PTR [ebp-0x2],0x0
0x08048d6f <+67>: add esp,0xfffffff8
0x08048d72 <+70>: push 0x804980b
0x08048d77 <+75>: lea eax,[ebp-0x8]
0x08048d7a <+78>: push eax
0x08048d7b <+79>: call 0x8049030 <strings_not_equal>
0x08048d80 <+84>: add esp,0x10
0x08048d83 <+87>: test eax,eax
0x08048d85 <+89>: je 0x8048d8c <phase_5+96>
0x08048d87 <+91>: call 0x80494fc <explode_bomb>
0x08048d8c <+96>: lea esp,[ebp-0x18]
0x08048d8f <+99>: pop ebx
0x08048d90 <+100>: pop esi
0x08048d91 <+101>: mov esp,ebp
0x08048d93 <+103>: pop ebp
0x08048d94 <+104>: ret
In this section we are calling a function strings_not_equal
with a pointer to a string literal on address 0x804980b
and the string we build which memory address is on the register eax
After calling this function, we test if the strings are equal, if they are, we continue execution on instruction 96
, if they are not equal, the bomb explotes.
We can check the value of the string literal we are comparing our new string:
(gdb) x/s 0x804980b
0x804980b: "giants"
Hmm, so our new string has to be equal to the string "giant"
.
Now we know that when we enter phase 6 and give an input, it has to be of length 6, the input will be used to iterate over each character and do an binary and
operation with the character and 0xf
and the result of that will be the index we will use to access a char in the string "isrveawhobpnutfg"
, then we will take that char and append it to the new string. Once our string has length 6, we break the loop and check if it's equal to "giants"
, if it is, we pass phase 5, if it's not, the bomb explotes.
We can build the algorithm used to create the new string in python:
= ;
=
= & 0xf
+=
return
To build our string, we have to access index [15, 0, 5, 11, 13, 1]
in that order so for each of those values we have to find an x
wich when doing x & 0xf
returns the value on that array.
for each n in [15, 0, 5, 11, 13, 1] we need x such that x % 0xf == n
We also want x
to be between 32
and 122
to be a nice printable ASCII value.
To get the list of valid number we can do a simple python script:
=
=
return
Running the script
$ python solve.py
['/', '?', 'O', '_', 'o']
[' ', '0', '@', 'P', '`', 'p']
['%', '5', 'E', 'U', 'e', 'u']
['+', ';', 'K', '[', 'k']
['-', '=', 'M', ']', 'm']
['!', '1', 'A', 'Q', 'a', 'q']
So for each list on that output we choose one character, an arbitrary solution would be "?pukmq"