Tuesday, September 6, 2016

Learning with DVRF - Step 11 - Part 4 - Let's get that GDB running

Step 11 - “Hello” GDB demonstrated in 5 parts In the previous post, we were able to get the appropriate “pre-checks” done for GDB/pwndbg. We trudged through the steps necessary to get GDB/pwndbg correctly aligned with steps to get a better debugging experience. This post will briefly examine the “hello” binary in GDB/pwndbg and then go through each line step-by-step in GDB/pwndbg. This is blog post 11, part 4 of 5, of post x in the learning with DVRF project series.

Part 4: The heart of the problem!


  1. Let’s get a refresher of where we are. In pwndbg, type in:

    context



  2. We can see in the previous screenshot that the next instruction to perform will be a “lui / load upper immediate” instruction. We’re going to take the upper part of a number (in our case the 0x0042 portion of the full 0x00420000) and put that into the $gp register. We also see that the $s8 register (which holds the frame pointer) has a “*” indicating it was altered in the previous instruction (where we saved the stack pointer).
  3. If we look in the registers section, we can see we do not have a holder for $gp. Let’s use the GDB method to see what's in the $gp register. In pwndbg, type in:

    i r


  4. Let’s step through the next instruction to place 0x0042 into the $gp register. In pwndbg, type in:

    n



  5. There does not appear to be any visible changes in the registers. Let’s check out the registers. In pwndbg, type in:

    i r


  6. We can see that 42 (well 0x0042) is in the upper half (left four digits) of the $gp register
  7. In the next instruction, we will subtract -76e0 from the value in the $gp register (0x00420000). The result will be stored in the $gp register.


  8. Doing the math 0x042000 - 0x0076e0 equals 418920. We know from blog article #9 that this calculates the canonical gp value for the global offset table. We will need to know this value (418920) so we can calculate the offset for functions like printf. Let’s see this math affect the value to the $gp register in action. In pwndbg, type in:

    n



  9. Let’s check out the $gp register. In pwndbg, type in:

    i r


  10. Let’s get a fresh screen of what’s going on. In pwndbg, type in:

    context



  11. The next instruction we see highlighted as well in the $PC register is to store the contents of the $gp register onto the stack at an offset of 0x10 from the stack pointer. As we can already see in the stack section, 0x418920 is already on the stack at 0x10. Perhaps this was placed there already from GDB or some other mechanism? It should be interesting to see if the 0x10 stack offset changes when we step through the next instruction. In pwndbg, type in:

    n



  12. As we can see in the stack section, nothing appeared to have changed. The correct value is in there, but it was already in there.
  13. The next opcode is another “lui / load upper immediate” instruction. This time, we’re going to load 0x40 into the $v0 register. Currently in $v0, there’s nothing of value to us in there now.


  14. The $v0 register is typically used to store values from expression results or functions returning values. In this particular line, we’re going to do some number manipulation to set up future instructions. In pwndbg, type in:

    n



  15. We can see the $v0 register has a “*” next to it meaning it was changed in the last instruction. In the $v0 register we see that it has 0x400000 from the last instruction. The program shifted 0040 into the top half of the register.
  16. The next instruction is an “addiu” instruction where the program will add the middle and right values and store the value into the left register. In this case, the program will add the contents of the $v0 register (0x40000) with the immediate value of 0x8e0. The result of this addition will be stored in the $a0 register. Currently, the $a0 register has 0x1. This is should be the addition of 0x00400000 + 0x000008e0 which equals 0x004008e0. We can see this in action! In pwndbg, type in:

    n



  17. As we can see in the $a0 register, the value 0x4008e0 is in this register. Also, we can see that this is a memory address to the location in the .rodata section (discussed previously here) of the start of the “Hello world” string. In the register, we see that it automatically tries to resolve memory addresses to the representation of their value. In this case, this works out well for us as we can see this memory address holds “Hello world”. Additionally, we can see that the $a0 register has a “*” indicating it was altered from the last instruction.


  18. The next instruction to be executed is a “lw / load word” instruction.


  19. Previously we had seen values from the stack at an offset of the $sp register put into registers. In this case, we’re going have a “similar” concept where we’re going to pull an address from an offset of the $gp register. This process will resolve the location of the printf function that we identified with readelf. There are two ways (maybe more?) that we can resolve what this address (-0x7fb8($gp)) will be. The first way is to pull the address of $gp from the previous math operations that was eventually stored at 0x10($sp). Then we can do the math of subtracting 0x7fb8 to get the final address. That sounds exhausting.
  20. The second way is to use the examine command to help us out. This allows us to get creative with how we can figure out the memory address and what’s there. First things first, in pwndbg, type in:

    x/xw $gp-0x7fb8


  21. The examine command (the first “x”) tells GDB to use the examine command. The “x” after the “/” tells GDB/pwndbg to give us the hexadecimal value. The “w” asks to give us the full word length of the address. We don’t necessarily need to specify “w”, but if you change the size of the values to be returned, this won’t change back to the default of “w” in some cases. It may help to specifically specify “w” to make sure we get all of the values back we care about… specifically.
  22. The address of 0x00400860 is the same address of printf that we identified in the previous blog article. But, we only know it’s printf from prior research. If we hadn’t done that and/or forgot, we wouldn’t have any idea what 0x00400860 meant. While we used the above command to get us the memory address, we can dereference the address to see what’s in there. To get that information, type in pwndbg:

    x/xw *($gp-0x7fb8)


  23. In the above screenshot are two changes from the previous command. We added parenthesis around the $gp and number. This just helped us to group the instructions together to make sure this subtraction expression was evaluated first. The more important change was to add “*” to the front of the parenthesis. If we did not add the “*”, we would get the same value of 0x400860 returned back. Adding in the “*” tells GDB to return back what’s in there rather than the address. In this case, we see it’s printf.
  24. From what we just learned, we can see that the program will put in the address of printf into the $v0 register. For functions to execute, first the arguments to functions are loaded into registers (or additionally onto the stack if there are not enough registers). Once the arguments are placed correctly (right to left), the function is “loaded” up. Once the function is correctly placed, the call to the function can be performed. In our case right now, the string of “Hello world” was placed into the $a0 register. This upcoming lw instruction, and the next instruction after that, setup printf to be placed correctly in the appropriate register to be executed with a jump. Once we step through the lw instruction, we should see 0x400860 placed into the $v0 register. In pwndbg, type in:

    n



  25. We can see the $v0 register has the 0x400860 value as well as tells us that the printf function is there.


  26. We didn’t necessarily need to do the examine work in a previous step, but it does help knowing what to expect and what the program is setting up for us. We may have figured out some kind of screen printing operation was going to happen with a string loaded into a register, but it helps to know how to figure it out. We also didn’t have automatic symbol resolution to tell us that printf was being resolved with those math operations.
  27. The next instruction moves the contents from the $v0 register into the $t9 register. We will talk more about that after this instruction is executed. In pwndbg, type in:

    n



  28. We see the “*” by the $t9 register indicating that this register was altered. We see the same value in the $v0 register.
  29. Let’s talk about the $t9 register and why we had to move the value from $v0 to $t9. GDB needs to call another function with work to do and MIPS likes to have programs follow a specific way of performing function calls. The upcoming instruction is a jalr opcode. This instruction will jump to a specific address that is stored in a register and store the return address in another register ($ra). In this case, for a jalr instruction, the $t9 register needs to have the memory address that the jump function needs of the destination location to make the jump. The program will shift execution from within our main function and go to the printf function (located outside of main). We have previously set the arguments to printf and printf should operate with the parameters we set. In this case, we have provided printf with a string to print of “Hello world”.
  30. Let’s check out the $ra register to see what value is in there now. In pwndbg, type in:

    i r


  31. From what we know now, we should see the other Terminal window print out our string and see the $ra register change. In pwndbg, type in:

    n



  32. We did not see the “Hello world” string print out in the other Terminal window (but we will see it once execution finishes with our GDB/pwndbg session). This may be a result of emulation with Qemu and running the “hello” binary with a GDB debugger stub. Hard to say. However, we see a lot of registers with the “*” next to them after the printf function completed. Program execution also skipped over the “nop” opcode and went to the function epilogue. Let’s check out the other registers. In pwndbg, type in:

    i r


  33. We see the $ra register pointing to the memory address of the “lw” instruction set from the jalr instruction.
  34. This wraps up this post with the meat of the “main” completed. The rest of the instructions in the “main” function is cleanup and will be covered in the next post.

Thursday, September 1, 2016

Learning with DVRF - Step 11 - Part 3 - Let's get that GDB running

Step 11 - “Hello” GDB demonstrated in 5 parts In the previous post, we were able to get the appropriate “pre-checks” done for GDB/pwndbg. We trudged through the steps necessary to get GDB/pwndbg correctly aligned with steps to get a better debugging experience. This post will briefly examine the “hello” binary in GDB/pwndbg and then go through each line step-by-step in GDB/pwndbg. This is blog post 11, part 3 of 5, of post x in the learning with DVRF project series.

Part 3: Step-by-Step, line-by-line, here we go with the intro!


  1. In the first line of “main”, we see the highlighted line of “addiu” which will carve out space on the stack. Interestingly, in objdump, we saw the numerical value represented in decimal form as -32. Here in GDB/pwndbg, we see this represented as a hexadecimal value of -0x20.


  2. If we look in the registers section, we see that $PC is also on the same line with the same instructions:


  3. Another interesting observation is that both $T9 and $S7 registers are set to the first memory address of main. The $T9 register is set from a previous function which would appear to be set from a jalr instruction as described in this article. The $S7 register would appear to be set from the previous function that called the main function and was changed as described in this article.


  4. Let’s step to the next line and see what happens next. Once we enter in “n”, we will see the stack grow as well as see each pane update. In pwndbg, type in:

    n



  5. Let’s do a before and after comparison of the stack.

    4007b0 <main> view of the stack:



    4007b4 <main+4> view of the stack:


  6. We know the instruction in the <main> was to decrement the value of the memory address held in the $SP register by 0x20. The memory address before the line of instruction was 0x76fff708. The memory address after the line of instruction was 0x76fff6e8.


  7. If we do some hex math of 0x76fff708 - 0x76fff6e8 we get 20:


  8. This just proves that the instruction worked! We also see zero as the value being inserted at the 0x76fff6e8 address:


  9. Up in the registers section, we also see the same value reflected in the $SP register:


  10. The highlighted instruction (sw $ra,0x1c($sp)) in the code window tells us that we are going to store the value in the $ra register at an offset of 0x1c from the stack pointer. We do not however, see the $ra register in the “Registers” section. However, we can use the stock GDB option of viewing the registers.
  11. In pwndbg, type in:

    i r
  12. We can see the ra register has the address of the function to return to once the main function finishes. Here we can see the value in $ra is “0x76765c28”.


  13. Above the output of the “info registers” command, we should see the output from the context command. Right above, we should see the stack content as in the screenshot below. It would appear that at 0x1c (in our main function) from the stack pointer offset is a load word opcode that is loading whatever is located 0x10 from the stack pointer in the caller function into the $gp (global pointer) register.


  14. Let’s continue on with the execution of the program. In pwndbg, type in:

    n



  15. We can see now that the value in the $ra register (0x76765c28) is now in the offset of 0x1c from the stack pointer (which that offset is the memory address of 0x76fff704). Looking at the backtrace section, we see the same value that was in the $ra register that is saved on the stack is also the same address at “f 1”. This is like going back where we came from once main finishes and where the program execution flow would continue on from. If we were going to another function (e.g. a sub-function to calculate something), we would see an additional line in backtrace. We would see “f 2” with the address to return to in uClibc_main (at the offset of 704 bytes from the start of main). Then we would see “f 1” with the address of main and the appropriate offset from where in main the sub-function was called. Then in “f 0” we would see the memory address of the start of the sub-function and whatever offset we were at in the sub-function.
  16. In the next opcode, we see a similar instruction where the program will save the frame pointer onto the stack. However, this value will be stored at a memory address closer to the base memory address of the stack pointer register (0x18($sp)).


  17. From what we can see from the screenshot above, the value in memory address 0x76fff708 (which is labeled as the frame pointer register address) currently holds the value of 0x767fe020. The instruction highlighted in green suggests that it will store this value at the memory address located at an offset of 0x18 from the stack pointer register once this instruction is performed.
  18. We see in the stack the current values at an offset of 0x18. We know that once the next instruction is performed, this value at 0x18/0x76fff700 should change.


  19. In pwndbg, type in:

    n



  20. Interestingly, if we look at the “Stack” section, we do not see a memory address listed at 0x18 (which should be between 0014 and 001c). In fact, we see “...” instead of anything useful like the value of within the $fp register at this address.
  21. We can manually examine any memory address within GDB/pwndbg. We want to look at the memory address of an offset of 0x18 from the stack pointer. The memory address values on the stack are incrementing by 4 (in hex) from the stack pointer. If we subtract 4 (in hex) from 0x1c, we get 0x18. To calculate the memory address of the offset at 0x18, we can subtract 4 from the 0x1c offset which is memory address 0x76fff704. Subtracting 4 from 0x76fff704 leaves us with 0x76fff700. The memory address 0x76fff700 is the memory address at an offset of 0x18 from the stack pointer memory address. In pwndbg, type in:

    x/w 0x76fff700


  22. The preceding command we performed was the “examine” command that lets us look at the “word” value of what we want to look at. In our case, we wanted to examine the full memory address of the memory location we specified (0x76fff700). But, this only returned zeroes or a NOP in MIPS. Well, that’s confusing. This may be an unexpected bug with GDB or pwndbg. Let’s look at the registers again.


  23. Depending on the program interpreting a MIPS binary, the frame pointer can be referred to as $fp or $S8. In this case, it would appear that the frame pointer is actually being manipulated/changed/etc in S8.


  24. Based on this information, it would make sense that we saw “...” in the visual representation of the stack and 0x0 when we examined the memory address.
  25. While we are kinda talking about the stack, we can also see more of the stack than what we see in the “context” view. On the command line, we can specify, for example, 12 lines of the stack rather than the usual 4 or 5 values. In pwndbg, type in:

    stack 12


  26. Since we carved out 0x20 worth of space on the stack (executed in line 0x4007b0), we can see where the frame pointer starts. Fun to play with and check out. Neat.
  27. The next instruction to be executed is “move $fp, $sp”. This command will move the value in the $sp (stack pointer) register into the $fp (frame pointer) register. This saves the memory location of the stack pointer into the frame pointer register. We can see the values in each register as below:


  28. Based on the instructions above, we should see 0x76fff6e8 in $SP also appear in $S8. Both of these values should hold a value of 0x0 once this instruction completes.
  29. Let’s step through the next instruction. In pwndbg, type in:

    n



  30. As expected, we can see the registers updated with the appropriate values:


  31. Another interesting observation: We can see a “*” next to the S8 register after the change. This looks to be an indicator to show what register was changed from the previous instruction.
  32. This post covered the prologue of the program to get us a better idea of the inner workings of how this process works. The next post will cover the meat of the program and step through getting “Hello world” printed to the screen.