SmashTheStack IO Level 1
The first level is pretty straightforward. Connect to the smashthestack.org server using an SSH Client. For level01, connect to level01@io.smashthestack.org and port 2224. Get into the "levels" folder using the "cd levels" command. Now, let's gather some information about the program first.Source Code available: No
For the sake of understanding, I have mentioned the program logic below. We will see how this can be derived later.Program Logic:
Argument passed at command line is compared with an internal string. At the assembly level, each character of the input and the internal pwd is compared one by one.GDB Commands we will be using:
display/i $pc: Turns on automatic display of the program counter. After every step instruction, the next intruction to be executed will be displayed.ni: Next instruction (Jumps functions/calls)
si: Step instruction (Does not Jump functions/calls)
x/c <addr>: Prints the character at the specidied address (Shows the hex value as well)
break: Breaks the program execution at the specified function.
break *<addr>: Breaks the program execution at the specified address (after the asterisk).
strings file: Displays all the hardcorded strings in a file
Approach:
Start by disassembling the main function.Dump of assembler code for function main:
0x08048596 <main+0>: push %ebp 0x08048597 <main+1>: mov %esp,%ebp 0x08048599 <main+3>: sub $0x18,%esp 0x0804859c <main+6>: and $0xfffffff0,%esp 0x0804859f <main+9>: mov $0x0,%eax 0x080485a4 <main+14>: sub %eax,%esp 0x080485a6 <main+16>: cmpl $0x2,0x8(%ebp) 0x080485aa <main+20>: je 0x80485ca <main+52> 0x080485ac <main+22>: mov 0xc(%ebp),%eax 0x080485af <main+25>: mov (%eax),%eax 0x080485b1 <main+27>: mov %eax,0x4(%esp) 0x080485b5 <main+31>: movl $0x8048760,(%esp) 0x080485bc <main+38>: call 0x80483b8 <printf@plt> 0x080485c1 <main+43>: movl $0x0,-0x4(%ebp) 0x080485c8 <main+50>: jmp 0x8048618 <main+130> 0x080485ca <main+52>: call 0x804852d <pass> 0x080485cf <main+57>: movl $0x64,0x8(%esp) 0x080485d7 <main+65>: mov 0xc(%ebp),%eax 0x080485da <main+68>: add $0x4,%eax 0x080485dd <main+71>: mov (%eax),%eax 0x080485df <main+73>: mov %eax,0x4(%esp) 0x080485e3 <main+77>: movl $0x80491a0,(%esp) 0x080485ea <main+84>: call 0x80483a8 <mbstowcs@plt> 0x080485ef <main+89>: movl $0x8049140,0x4(%esp) 0x080485f7 <main+97>: movl $0x80491a0,(%esp) 0x080485fe <main+104>: call 0x80483d8 <wcscmp@plt> 0x08048603 <main+109>: test %eax,%eax 0x08048605 <main+111>: jne 0x804860c <main+118> 0x08048607 <main+113>: call 0x80484b4 <win> 0x0804860c <main+118>: movl $0x8048795,(%esp) 0x08048613 <main+125>: call 0x80483e8 <puts@plt> 0x08048618 <main+130>: mov -0x4(%ebp),%eax 0x0804861b <main+133>: leave 0x0804861c <main+134>: ret End of assembler dump.Looking through the assembly quickly. The last function called before <win> is "wcscmp". It is likely that this function is where the comparison takes place. Also, we need %eax to be 0 to move on to the call <win> function.
test %eax, %eax will set the ZF = 1 if %eax =0. (test ANDs the 2 operands and sets ZF=1 if the AND is 0)
Disassemble the function:
Dump of assembler code for function wcscmp:
0x00b8b4d0 <wcscmp+0>: push %ebp 0x00b8b4d1 <wcscmp+1>: xor %edx,%edx 0x00b8b4d3 <wcscmp+3>: mov %esp,%ebp 0x00b8b4d5 <wcscmp+5>: push %edi 0x00b8b4d6 <wcscmp+6>: push %esi 0x00b8b4d7 <wcscmp+7>: mov 0x8(%ebp),%edi 0x00b8b4da <wcscmp+10>: mov 0xc(%ebp),%esi 0x00b8b4dd <wcscmp+13>: lea 0x0(%esi),%esi 0x00b8b4e0 <wcscmp+16>: mov (%edi,%edx,1),%eax 0x00b8b4e3 <wcscmp+19>: mov (%esi,%edx,1),%ecx 0x00b8b4e6 <wcscmp+22>: test %eax,%eax 0x00b8b4e8 <wcscmp+24>: je 0xb8b4f8 <wcscmp+40> 0x00b8b4ea <wcscmp+26>: add $0x4,%edx 0x00b8b4ed <wcscmp+29>: cmp %ecx,%eax 0x00b8b4ef <wcscmp+31>: je 0xb8b4e0 <wcscmp+16> 0x00b8b4f1 <wcscmp+33>: sub %ecx,%eax 0x00b8b4f3 <wcscmp+35>: pop %esi 0x00b8b4f4 <wcscmp+36>: pop %edi 0x00b8b4f5 <wcscmp+37>: pop %ebp 0x00b8b4f6 <wcscmp+38>: ret 0x00b8b4f7 <wcscmp+39>: nop 0x00b8b4f8 <wcscmp+40>: mov %ecx,%eax 0x00b8b4fa <wcscmp+42>: neg %eax 0x00b8b4fc <wcscmp+44>: pop %esi 0x00b8b4fd <wcscmp+45>: pop %edi 0x00b8b4fe <wcscmp+46>: pop %ebp 0x00b8b4ff <wcscmp+47>: ret End of assembler dump.Assembly interpretation:
0x00b8b4d0 <wcscmp+0>: push %ebp 0x00b8b4d1 <wcscmp+1>: xor %edx,%edx 0x00b8b4d3 <wcscmp+3>: mov %esp,%ebp 0x00b8b4d5 <wcscmp+5>: push %edi 0x00b8b4d6 <wcscmp+6>: push %esixor %edx,%edx makes the %edx register = 0. Later, the register is used as a counter.
Main's %edi and %esi are pushed because the current function i.e. "wcscmp" will be using them later on for the comparison.
0x00b8b4d7 <wcscmp+7>: mov 0x8(%ebp),%edi 0x00b8b4da <wcscmp+10>: mov 0xc(%ebp),%esi 0x00b8b4dd <wcscmp+13>: lea 0x0(%esi),%esiThe arguments passed by the main function are loaded into %edi and %esi. The argument-passing instructions in the main function are the two instructions above the call to wcscmp:
0x080485ef <main+89>: movl $0x8049140,0x4(%esp) 0x080485f7 <main+97>: movl $0x80491a0,(%esp) 0x080485fe <main+104>: call 0x80483d8 <wcscmp@plt>Next sequence of instructions:
0x00b8b4e0 <wcscmp+16>: mov (%edi,%edx,1),%eax 0x00b8b4e3 <wcscmp+19>: mov (%esi,%edx,1),%ecx 0x00b8b4e6 <wcscmp+22>: test %eax,%eax 0x00b8b4e8 <wcscmp+24>: je 0xb8b4f8 <wcscmp+40>The first characters of the input and the internal pass string are loaded into %eax and %ecx. Note that %edx=0 since it was XORed with itself at the start of the function. If %eax=0 i.e. it contains a null character, the program jumps to:
0x00b8b4f8 <wcscmp+40>: mov %ecx,%eax 0x00b8b4fa <wcscmp+42>: neg %eax 0x00b8b4fc <wcscmp+44>: pop %esi 0x00b8b4fd <wcscmp+45>: pop %edi 0x00b8b4fe <wcscmp+46>: pop %ebp 0x00b8b4ff <wcscmp+47>: ret%eax will be set to a non-zero integer. Then the function returns after the cleanup. If this happens, the test %eax,%eax in the main function will fail and the "win" function will not be called.
The remaining piece of instructions is the most interesting:
0x00b8b4ea <wcscmp+26>: add $0x4,%edx 0x00b8b4ed <wcscmp+29>: cmp %ecx,%eax 0x00b8b4ef <wcscmp+31>: je 0xb8b4e0 <wcscmp+16> 0x00b8b4f1 <wcscmp+33>: sub %ecx,%eax 0x00b8b4f3 <wcscmp+35>: pop %esi 0x00b8b4f4 <wcscmp+36>: pop %edi 0x00b8b4f5 <wcscmp+37>: pop %ebp 0x00b8b4f6 <wcscmp+38>: ret 0x00b8b4f7 <wcscmp+39>: nopCounter is increased (%edx+4) so that the next character will be fetched in the next round. Both the characters are compared by the instruction cmp %ecx,%eax and if they are equal the program jumps back to fetch the next character and compare them and so on. Once the strings end, the jump will not be taken and %eax will be set to 0 by subtracting it with %ecx. After restoring main's registers, the program returns with %eax=0. The jump is not taken in the main function and the "win" function is called.