Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Look inside a program without running it

After this lesson: I can learn about a program without running it.

50 min lesson6 commandsReverse engineering lab: reading programs with gdb, radare2, strace and ltrace on one VM

Reverse engineering starts with looking

Reverse engineering is working out how a program does its job from the program itself, with no source code.

Defenders do it to understand a strange tool on their own server, and to see how a flaw looks before an attacker finds it. Before anything else, one rule: use these tools only on programs made for this lab, on your own copy of it.

By the end of this lesson

Note

I can learn about a program without running it.

Key words

WordWhat it meansExample
Binarya program as the computer runs it, a file of machine instructions
ELFthe file format of programs on Linux
Stringa run of readable characters inside a file
Symbola name inside a program for one of its functionsmain
Disassemblera tool that turns machine instructions back into assembly you can read
Strippeda program with its symbol names removed

The cells students fill in are left blank in this preview.

The one rule of this module

Important

Read only programs made for the lab, and only your own copy. Never take apart anybody else's software, a licence check, a game or a protection. The reason is defence: you learn how a flaw looks so that it can be found and fixed. Nothing in this module is aimed at a real system.

What a compiled program file is

Source code is the text a person writes. A binary is what the compiler makes from it, and Linux keeps it in the ELF format.

The file command reads the start of a file and says what it is. For a program it names the processor it was built for, and whether it is stripped, with its function names removed, or not stripped.

A practice program called vault

Code: c
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
    if (argc != 2) { puts("usage: vault WORD"); return 2; }
    if (strcmp(argv[1], "blue-harbour") == 0) { puts("Door open."); return 0; }
    puts("Door shut.");
    return 1;
}

Write the practice program

Command
printf '#include <stdio.h>\n#include <string.h>\nint main(int argc, char **argv) {\n    if (argc != 2) { puts("usage: vault WORD"); return 2; }\n    if (strcmp(argv[1], "blue-harbour") == 0) { puts("Door open."); return 0; }\n    puts("Door shut.");\n    return 1;\n}\n' > vault.c

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Compile it into a binary

Command
gcc -O0 -no-pie -fno-stack-protector -o vault vault.c

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Ask what kind of file it is

Command
file vault

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

The strings command prints readable text

A program carries its text inside it. strings scans a file and prints every run of readable characters that is long enough, four by default.

The first Run feeds it a few bytes of our own making. Short runs such as ab and xyz are too short at four, so only two words come out. The second Run keeps the lines of vault that matter.

Worked example: runs of at least four

Run of readable bytesLengthPrinted with a minimum of 4?
ab2No, too short
hello5Yes
xyz3No, too short
world!6Yes

The cells students fill in are left blank in this preview.

Strings on bytes you made

Command
printf 'ab\x01hello\x02xyz\x00world!' | strings -n 4

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

The readable text inside vault

Command
strings vault | grep -E "usage|Door|harbour"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A common mistake

Warning

Many people think compiling hides what a program says. It only changes the form. Text the program needs, such as a password it compares with, is still inside the file, so a reader can find it. Never keep a secret in a program.

A disassembler turns bytes back into steps

objdump -d is a disassembler: it prints the instructions of a binary as assembly, one step per line.

--disassemble=main asks for one function only. Each call line is a request to another function, with its name in angle brackets. Reading only the names of the calls already tells you a lot, and it does not run anything.

The functions main calls

Command
objdump -d vault --disassemble=main | grep -o "<[a-z]*@plt>"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

About this lab and its source: Reverse engineering lab: reading programs with gdb, radare2, strace and ltrace on one VM

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.