< Home

While I was busy with the second pwnable I made a stupid mistake costing me some time. I kinda started thinking high level language again and forgot how arrays and strings work in low level.

That is why I created this C code to help me see it. Warning this is what they call shitcode.

#include <stdio.h>
int main(int argc, char* argv[]){
    for (int i = 0; i < 5; i++) {
        printf("%c\n", argv[1][i]);
    }
}

As you can see this code just expects one string argument and youk make sure it is bigger then 5. I’ve omitted some checks as they can make our code less readable because in-line compilation.

When build as release and opened up by x32dbg it will initially fail as this code is shit and expects an argument. The standard breakpoints will stop the crash and gives us a chance to pass an argument. File -> Change command line –> “path to release exe” example

Now we restart it (CTRL+F2) and our program won’t crash as we have passed a valid string now.

Now let us breakpoint our main function. Go to the symbols window (CTRL+ALT+S), select our exe in the left window and filter in the right window for main, our code will probably reside in the _main, so I’ll put a breakpoint on that function and run the program until we hit the breakpoint.

A quick diagonal look shows that this is indeed my code. Now we can step through it and see what happens!

Assembly code of previous C code

Assembly code of previous C code

The first four lines are part of the function epilogue (ESI, EDI are callee-saved registers). Now the first line of code of this function is moving the pointer stored inside EBP+C into EDI. But what does EBP+C point to? Right click -> Follow in dump -> Value [ebp+C], x32dbg makes it quite easy to follow a pointer here and even a pointer to a pointer. But if the option Value was not available we had to do the following

  1. Calculate the address of EBP+C (0x004FF848 + 0xC = 0x004FF854)
  2. As this program is running in little endian the address of step 1 is has the value 0x00A86250 Little endian of pointer address
  3. When we follow that address we see… a pointer to another address Little endian of second pointer address
  4. And when we follow that address (0x00A8625C), we see the string of where our program resides. Which is the first element of argv in C.
Memory of argv

Memory of argv

Now if you look at this as a human you will maybe notice something. The addresses are quite close to each other. At the start we notice that after our first pointer another pointer is stored. Also after our program path string we notice one 0x00 which indicates EOL and then the argument we passed. So this is how an array resides in memory! We’ve got our pointers to the array elements and the elements just reside next to each other. Only the pointers really distinguish the size of the elements. This isn’t always so but here we can see that our compiler did it as such.

So back to our main code. We store the pointer to the array in EDI then we XOR ESI with itself rendering it to zero. (ESI is used frequently as a counter)

Now comes the part where we will access the first parameter. Currently in EDI we have the pointer to the first element of the array. This is a 4 byte address so if we want to access the pointer to the data of the second element we will add 0x4 to the address of the first element.

Which is exactly what line 0x008F1050 does! Now we have the pointer saved in EAX (0x00A862A4) and we can manipulate it. If you follow the next lines you’ll maybe notice that we will now iterate over the bytes with the help of ESI and print them out. Just as the source code did.)

< Home