A file with no source
A company finds a small tool on its own server and nobody remembers who wrote it. There is no source code, only the program. Before anyone runs it, somebody has to read what it does. That is reverse engineering: working out how a program works from the program itself.
By the end of this lesson
Note
I can use a tool to find out what a program does without its source code.
The one rule
Note
This lab is for reading how a program works, as a defender or a student of compilers does. Use it on the programs in your own lab and nothing else. It never teaches, and you must never use it for: malware, breaking the licence of real software, key generators, hiding a program from a debugger, or attacking a program that is not yours. In the UK that is a crime under the Computer Misuse Act 1990.
Key words
- Binary: a program as the computer runs it, a file of machine instructions.
- Disassembler: a tool that turns machine instructions back into assembly you can read.
- Debugger: a tool that runs a program, stops it where you say and shows its memory.
- Register: a tiny store inside the processor. On this machine
rdiholds the first argument of a call. - Library call: a call to ready-made code such as
puts, which prints text. - System call: a request from a program to the operating system, such as writing.
Starter: what you already know
Note
Answer in your head. 1. What is source code? 2. What does a compiler do with it? 3. What is a function argument? You need the third answer in a moment.
Before you start
Note
Press Start the lab on the bar at the top of this lesson. This lab is a whole Ubuntu machine of your own, so it takes a little longer to come up than a container. Each Run button types its command into the machine's terminal, one after another in the same session. If one says the machine is still starting, wait a few seconds and press it again. To watch them, or to type your own, press Open terminal on the bar.
A binary is a file you can describe
The lab keeps its programs in the revlab folder. hello holds no secret: it prints one line. The file command says what kind of file it is without running it. Notice ELF, the format of programs on Linux, the processor it was built for, and not stripped, which means the names of its functions are still inside.
file revlab/helloShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Strings finds the text inside a program
A program carries its text inside it, plain and readable, whether or not anybody meant to show it. strings prints every run of readable characters, which is a long list, so the command keeps the line with Hello in it. You find out what the program says without running it.
strings revlab/hello | grep HelloShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Check
Tip
Where did the words come from, when you never ran the program? Answer: from inside the file: a program carries its text with it.
A common mistake
Warning
Many people think that compiling a program hides what it says. Compiling changes the form, not the facts: the text, the names of the calls and the order of the steps are all still there. Anything a program must know in order to work, a reader can find, so never keep a secret in one.
A disassembler shows the instructions
objdump -d turns the instructions into assembly, one per line. The command asks for the function main only and keeps the lines that mention puts: there is one, a call to the library function that prints. radare2 does the same job and adds names. aa asks it to analyse the file, pdf @ main prints the function. Look for the name puts and for the text it is given.
objdump -d revlab/hello --disassemble=main | grep putsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
r2 -q -e scr.color=0 -c 'aa' -c 'pdf @ main' revlab/hello 2>/dev/nullShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A debugger stops the program and shows its memory
Run the program under gdb and stop it on the first line of puts. By the way a call works on this machine, puts finds the address of its text in the register rdi. x/s $rdi means: show the string at that address. So you read the argument of a function while the program is paused, the way you will read a password.
gdb -batch -ex 'break puts' -ex 'run' -ex 'x/s $rdi' revlab/helloShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Tracing shows the calls a program makes
ltrace prints each library call with its arguments, and strace prints each system call. The first shows puts being given the text. The second shows the program asking the operating system to write the same bytes to the screen, using write. The greeting is sent to nowhere here so that only the trace is shown. Neither one needs to read any assembly.
ltrace revlab/helloShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
strace -e trace=write revlab/hello >/dev/nullShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Now do the lab
Tip
You have looked at one harmless program with each tool. Your lab has five programs, each hiding a key that is different on your copy, and you will read each with the tool that suits it. Open the README.md in your home folder with cat README.md in the terminal, and write the keys you find in answers.txt.
Exit check
Tip
Answer without looking back. 1. Which tool lists the readable text in a program? 2. Which tool lets you stop a running program and read its memory? 3. What is the difference between a library call and a system call? 4. Why is a secret in a program never really secret? Answers: strings; a debugger, gdb; a library call goes to ready-made code, a system call goes to the operating system; because anyone can read what is in the file.
For teachers
Tip
This lesson is a small example of a lesson worked in a lab: the steps and the Run buttons are the lesson, and the lab is each student's own copy. This lesson is worked in a VM lab: each student has an Ubuntu machine of their own, and a Run types into its terminal and shows what it printed. Key learning points: 1. A program can be read without its source. 2. Text and calls stay inside a compiled program. 3. A debugger shows a running program's memory. 4. Tracing shows the calls a program makes. Common mistake: believing compiling hides what a program does. Equipment: the lab and a browser. Tip: ask the class to say what hello does before any tool, then check each guess. The lab itself has a check that compares the keys in answers.txt with the ones made for each copy. It is defensive reading of the lab's own programs only. Copy it into your own module and change it, or build one from scratch with the Lab menu in the lesson builder.