Thursday, August 11, 2016

Learning with DVRF - Step 8 - What the MIPS?

Step 8: What the MIPS?

Without a good foundation of knowledge for MIPS (e.g. like knowing what it is) and how it works, looking at MIPS assembly can be weird. Like why are there way more registers than x86 if it’s based on a reduced instruction set? This post analyzes a simple MIPS binary to show foundational parts of a disassembled binary. This is blog post 8 of x on learning with the DVRF project blog post series.

The best place to start learning MIPS assembly may be with first learning x86 assembly (if you don’t know x86 assembly already). Learning x86 assembly is the first assembly language I learned which helped me to adapt and learn MIPS. The best place I have learned x86 assembly is at with their Introductory Intel x86: Architecture, Assembly, Applications, & Alliteration and Introduction To Software Exploits courses.

To dive into MIPS assembly, check out these resources:

The above resources go into pretty good detail about MIPS. This blog post assumes that you have gone through the above links (or get the gist of MIPS). In the steps below, we will look at one binary and discuss parts of the disassembled binary. To start with a simple “basic as it gets” binary would be best found by making it yourself! Fortunately for us, we already have a MIPS ready system ready for us to make one.

Let’s make a MIPS binary!

  1. Log into the VM
  2. Open up a Terminal window
  3. Change your working directory to the DVRF squashfs folder. In Terminal, type in:
    cd Downloads/DVRF-master/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/
  4. We should now be in the Intro folder for the pwnables on the DVRF firmware. Now we are going to make a simple C program that literally does nothing. We will also create a basic “Hello World” program. This really is just to help show the prologue and epilogue of the main function within the MIPS disassembly through a visual “diff”. In the folder, type in:
    touch blank.c  (press Enter)

    touch hello.c  (press Enter)

  5. Now we are going to open blank.c in a graphical text editor (gedit) and add in the shell of a C program. In Terminal, type in:

    gedit blank.c
  6. In gedit, type in:
    #include <stdio.h>

    return 0;

  7. Click the “Save” button in the top right corner and exit gedit (click the red x button in the top left). We will now add in the C code for the “Hello World” C program. In Terminal, type in:
    gedit hello.c
  8. In gedit, type in:
    #include <stdio.h>

    printf("Hello world");

    return 0;

  9. Click the “Save” button in the top right corner and exit gedit (click the red x button in the top left). Now we will use the mipsel (MIPS little endian) version of gcc from buildroot to compile our program. In Terminal, type in:
    cd    (press Enter)

    cd buildroot/buildroot-2016.05/output/host/usr/bin   (press Enter)

  10. There are a lot of different programs in here that could be useful to us in the future (type in ls -l to see all of the files). But, we need to compile the two C programs in our Intro folder. In Terminal, type in:
    ./mipsel-linux-gcc /home/andy/Downloads/DVRF-master/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/blank.c -o /home/andy/Downloads/DVRF-master/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/blank

  11. Let’s do the same as above, but for hello.c. In Terminal, type in:
    ./mipsel-linux-gcc /home/andy/Downloads/DVRF-master/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/hello.c -o /home/andy/Downloads/DVRF-master/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/hello

  12. We could have compiled the programs from the pwnable/Intro folder, but the programs in the buildroot..bin folder are useful for this step and future steps. Let’s go to the squash folder. In Terminal, type in:
    cd /home/andy/Downloads/DVRF-master/Firmware/_DVRF_v03.bin.extracted/squashfs-root/

  13. In the squash file system root folder, we can use qemu to run our new programs. In Terminal, type in:
    sudo chroot . ./qemu-mipsel-static pwnable/Intro/blank

  14. Unfortunately, we get this error saying a library is missing.

  15. Luckily, this is an easy solution (at least nothing in the future appears broken). In Terminal, type in:
    cd lib     (press Enter)

    ls -l      (press Enter)

  16. In this directory, we can see a “”, but no “”. Let’s try to copy the .0 and make it a .1. In Terminal, type in:

  17. Go back to the prior directory (cd ..) and let’s try qemu again. In Terminal, type in:
    sudo chroot . ./qemu-mipsel-static pwnable/Intro/blank

  18. Yay! No error!

  19. But the blank program literally does nothing. Let’s try the hello program. In Terminal, type in:
    sudo chroot . ./qemu-mipsel-static pwnable/Intro/hello

  20. Yay! It worked!

  21. We can use a utility program called “objdump” to produce disassembly of programs. Let’s try that to see what these programs look like. In Terminal, type in:
    objdump -d pwnable/Intro/blank

  22. Unfortunately, this program has no idea what to do with this.

  23. Fortunately, we have a version of objdump that can handle mipsel programs! In Terminal, type in:
    cd /home/andy/buildroot/buildroot-2016.05/output/host/usr/bin

  24. Let’s list all of the mipsel-linux files in this directory. In Terminal, type in:
    ls -l mipsel-linux-*
  25. It appears there’s a suitable objdump for mipsel programs!

  26. In Terminal, type in:
    ./mipsel-linux-objdump -d /home/andy/Downloads/DVRF-master/Firmware/_DVRF_v03.bin.extracted/squashfs-root/pwnable/Intro/blank

  27. A lot of text will fly by. The beginning looks like this:

  28. We want to look for the “main” function. If you scroll through the text, you should find:

  29. We know from reading the MIPS resources at the beginning of this blog post that there should be a prologue and an epilogue. We know that the program is going to grow space in the stack (toward zero) in the prologue. Let’s do a line by line discussion of the above disassembled C code with MIPS opcodes and mnemonics.
  30. The 400790 line is an “addiu” instruction:
    27bdfff8 addiu sp,sp,-8

    This line grows the stack by an unsigned immediate (8) from the current stack pointer memory location (sp) and stores the new memory address in the stack pointer register. In this case, the stack pointer is going to decrease by 8 toward zero.
  31. The 400794 line is an “sw” instruction:
    afbe0004 sw s8,4(sp)

    This line gets the address from an offset of 4 from the stack pointer and stores the address on the stack. The s8 register is also known as the frame pointer.
  32. The 400798 line is a “move” instruction:
    03a0f021 move s8,sp

    This line performs the move of the stack pointer to the frame pointer (s8).
  33. The 40079c line is another “move” instruction:
    00001021 move v0,zero

    This line moves zero into the v0 register. From different MIPS resources, it would appear that v0 is used to hold a return value. Setting a return value of zero would be just what we would expect for our C line of code of “return 0;”. This is the last (and only) line of code in the main function. It is also something we would expect to see toward the end of a disassembled function as well. We can surmise for now (but also verify later with the “Hello” program) that this move instruction is probably something we would find in the epilogue. With this information, we can tell that the previous three instructions are the prologue that set up and change the frame and stack pointers based on approximate size requirements.
  34. Since we have dealt with the single line of instruction in our code and have nothing else to do, the program is going to clean itself up. The first order of business is line 4007a0 which is a “move” instruction:
    03c0e821 move sp,s8

    This line moves the frame pointer to the stack pointer.
  35. The 4007a4 line is a “lw” instruction:
    8fbe0004 lw s8,4(sp)

    This line is similar to the “sw” instruction from above. In this case, rather than store whatever into the stack, we are going to load (load word) something from something into something. Specifically, we are going to load the previously saved frame pointer on the stack from the beginning of the function to the frame pointer register.
  36. The 4007a8 line is a “addiu” instruction:
    27bd0008 addiu sp,sp,8

    This line is the opposite of the similar “addiu” instruction at the beginning of the function. Rather than carve out space, this is going to reclaim space from the stack and reset it back to the original memory location.
  37. The 4007ac line is a “jr” instruction:
    03e00008 jr ra

    This line is a “jump register” instruction that will take the program control flow to wherever to the address in the register. In this case, the register is “ra” which is the returning address memory location of the previously called function or wherever. In our case, the system launched this program so the control flow will return back to the system. This register was set for us to the originating location before we entered the “main” function. Since we did not go into any sub-functions from “main”, we did not have to load this register to the original “ra” register location.
  38. Last, but not least, the 4007b0 line is a “nop” instruction:
    00000000 nop

    This line is a “nop” instruction that does nothing and acts as a “delay slot”. This is in case there were some other instructions after the jump instruction. In this case, a “nop” is after the “jr ra” line and if that instruction is executed, nothing will happen.
  39. This concludes the analysis of the “blank” MIPS binary. In the next post, we will go over the “hello” MIPS binary and discuss the new things we see there as opposed to the “blank” binary. We will continue to learn more things about MIPS!

No comments:

Post a Comment