1 - Introduction to RE&PE
How’s it going everyone. So this tutorial series is for those who want to learn the basics of a few topics:
- Reverse Engineering
- Penetration Testing
- Network Security
I may decide to add more to the list, but we are going to start with these three. I don’t want to label this as a “how to hack” tutorial set. I won’t be teaching modern hacking techniques, but rather fundamental lessons in order to understand higher levels of security engineering.
I will be starting with reverse engineering, and this introductory post is for environment set up and to refresh your memory of C programming.
Vagrant
Vagrant is a development environment software. Similar to docker, we can make VM’s for all sorts of operating systems. Download Vagrant for your machine:
It is also important to download VirtualBox, which will act as the “VM Provider” for our development machines:
Once these are both downloaded, open up a terminal and create a folder “Vagrant”, this is where your going to keep your vagrant machines.
Enter this directory and let’s create our first machine. We are going to start with Ubuntu (Xenial64 Edition):
$ vagrant init ubuntu/xenial64
That command will create a Vagrantfile for you. Vagrant files have configuration details for your “box”. We are going to mess around with these configurations later on, but for now let’s boot up our box:
$ vagrant up
This will begin the creation process of our new VM. You will see some items for the SSH address and username. Host names can be changed in the Vagrantfile.
To make sure our vagrant machine is up to date:
$ vagrant box update
Now that we have created and updated our vagrant machine, let’s enter the machine:
$ vagrant ssh
You should now be in the vagrant machine, which is a mini ubuntu box. To exit out:
ubuntu@ubuntu-xenial:~$ exit
Changing some configurations
Let’s change some configurations in the Vagrant file. You can edit the file using any text editor.
There are a couple lines that are commented out via “#” in the vagrant file. We want to un-comment or change some of these lines. Be sure to un-comment and change the following:
Line 40: config.vm.synced_folder "../data", "/vagrant_data"
Line 46 - 52: un-comment loop and change line 51: vb.memory = "2048"
Add the following line (pick a hostname): config.vm.hostname = <whatever>
Save and reload the vagrant machine:
$ vagrant reload
Notice: If you are having issues with the following error during vagrant up:
The guest additions on this VM do not match the install version of
VirtualBox! This may cause things such as forwarded ports, shared
folders, and more to not work properly. If any of those things fail on
this machine, please update the guest additions and repackage the
box.
We can fix that by installing a plugin with vagrant plugin:
$ vagrant plugin install vagrant-vbguest
This will allow vagrant up to check and install the correct guest additions module on boot.
Tool Installation
We will be using some tools for our beginning RE exercises.
Enter your vagrant machine and download the following packages:
$ vagrant ssh
# now in vm
$ sudo apt-get update
$ sudo apt-get upgrae
$ sudo apt-get install lsb-core libc6-i386 libc6-dev-i386 build-essential
Lots of dependencies will be downloaded. Let’s move on to installing radare:
$ git clone https://github.com/radare/radare2
$ cd radare2
$ sys/install.sh
Radare is a portable reversing framework that can do a number of cool functions, such as disassembling different architectures and aid in software exploitation. Now that we have radare installed, we should be able to use the r2 command.
C Refresher
So before we get into some more complicated reverse engineering topics, we are going to refresh ourselves with C. The following are a few simple programs with their assembly code output. I’ll format them as tasks in case you want to have a crack at it before scrolling down to see the answer.
Task 1
Write a program that takes two command line arugments and prints the sum of these two integers. For example:
$ ./lab1-3a 4 8
12
Answer:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if(argc<2){
printf("usage: %s [number] [number]\n", argv[0]);
}
else {
printf("%d\n",atoi(argv[1]) + atoi(argv[2]));
}
return 0;
}
You can compile this program via the following command:
# 32 bit version
$ gcc -m32 -o <output-filename> <input-file>
# here's how to compile while also spitting out the assembly
$ gcc -m32 -S <input-file>
# So in order to compile our file task1.c:
$ gcc -m32 -S -o task1-32 task1.c
# Now we can call our outputted binary
$ ./task1-32
usage: ./task1-32 [number] [number]
$ ./task1-32 3 5
8
Also we have the task.s file
If we look at some assembly without any knowledge it looks scary as hell:
.file "task1.c"
.section .rodata
.LC0:
.string "usage: %s [number] [number]\n"
.LC1:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
leal 4(%esp), %ecx
.cfi_def_cfa 1, 0
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, %ebp
pushl %esi
pushl %ebx
pushl %ecx
.cfi_escape 0xf,0x3,0x75,0x74,0x6
.cfi_escape 0x10,0x6,0x2,0x75,0x7c
.cfi_escape 0x10,0x3,0x2,0x75,0x78
subl $12, %esp
movl %ecx, %ebx
cmpl $1, (%ebx)
jg .L2
movl 4(%ebx), %eax
movl (%eax), %eax
subl $8, %esp
pushl %eax
pushl $.LC0
call printf
addl $16, %esp
jmp .L3
.L2:
movl 4(%ebx), %eax
addl $4, %eax
movl (%eax), %eax
subl $12, %esp
pushl %eax
call atoi
addl $16, %esp
movl %eax, %esi
movl 4(%ebx), %eax
addl $8, %eax
movl (%eax), %eax
subl $12, %esp
pushl %eax
call atoi
addl $16, %esp
addl %esi, %eax
subl $8, %esp
pushl %eax
pushl $.LC1
call printf
addl $16, %esp
.L3:
movl $0, %eax
leal -12(%ebp), %esp
popl %ecx
.cfi_restore 1
.cfi_def_cfa 1, 0
popl %ebx
.cfi_restore 3
popl %esi
.cfi_restore 6
popl %ebp
.cfi_restore 5
leal -4(%ecx), %esp
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
Don’t worry, we will go into what this gibberish means and why it is significant.
Here’s the command to output the assembly in intel syntax:
gcc -m32 -S -masm=intel task1.c
Intel is less overwhelming than AT&T so we will mainly be looking at Intel format.
Task 2: Pointer Refresher Write a program that does the following:
- Assign a value to variable x
- Assign a pointer y to variable x
- Print the value of x, y, and the address of x and y
- Print out the value that y is pointing at
Answer:
#include <stdio.h>
int main(int argc, char* argv[])
{
int x = 8;
int *y = &x;
printf("The Value of x is %d\n", x);
printf("The Value of y is %x\n", y);
printf("The Address of x is %x\n", &x);
printf("The Address of y is %x\n", &y);
printf("The Value pointed at by y is %d\n", *y);
return 0;
}
If we compile this we get the following output:
The Value of x is 8
The Value of y is ff874534
The Address of x is ff874534
The Address of y is ff874538
The Value pointed at by y is 8
Things might be coming back to ya now ;). If not thats ok. Pointers can be tricky, but try to think of it as simply as they literally point to another spot of memory. An asterisk specifies a pointer (*) and an ampersand represents “the address of”.
- We create a variable x that has a value of 8. The actual spot of memory where this 8 variable exists is at &x.
- We create a pointer y to reference the address of x. This means that the value of y will be the same as the address of x.
- The address of y will be unique in itself. It may sound silly, but pointers need their spot in memory too, but in this can it is not far, only a byte off.
- Since y is a pointer, we can get the value that it references to by adding an asterisk in front *y
That’s it for today. We have set up our vagrant machine along with our RE dependencies. Next time we mess with Vagrant we will try some manual de-compilation. Until next time my friends.