Wun Code
New area

Reverse Engineering

Learn to understand a program from its bytes: read hex and byte order, undo simple encryption, read assembly and build an emulator for a tiny machine. Then solve challenges where your code extracts the key. All in Python, right in your browser, on small programs we built for you.

Lessons
17
Challenges
6
Exam stages
3
Duration
6 hours
Python session
>>> prog = bytes.fromhex("01480502210500")>>> prog.hex(" ")'01 48 05 02 21 05 00'>>> print(*disasm(prog), sep="\n")LOAD 0x48
OUT
ADD 0x21
OUT
HALT>>> run(prog)b'Hi'>>> 
You build this yourself in the program analysis track: a disassembler that reads bytes as instructions, and an emulator that runs them.

Three tracks

Start at the byte and finish with challenges where you extract the key yourself. Each track builds on the one before it, so take them in order.

Exam and certificate

The exam unlocks once you finish every lesson and challenge in this area. It has three stages, easy, medium and hard, each with two problems and no hints or model solutions. Passing a stage unlocks the next one.

  1. Easy stage

    • Reading an ELF header
    • Breaking repeating-key XOR
  2. Medium stage

    • Extracting strings with their offsets
    • A disassembler with jumps
  3. Hard stage

    • An emulator with jumps
    • Reversing the keygen
Reverse Engineering exam 0/3
Preview

Certificate of Mastery

Reverse Engineering

Your name here

has passed all three stages of the Reverse Engineering exam — easy, medium and hard — without hints or model solutions, passing every automated check.

Stages
3 of 3
Problems
6
Checks passed
32
This is the Reverse Engineering mastery certificate. It carries your name once you pass all three stages. Signed in, you can issue a verified copy with its own number, QR code and verification page, after Wun Code re-grades your answers on the server, outside your browser.

Quick reference

Short tables you'll reach for in every lesson and challenge: number bases, characters, struct format codes, registers and instructions, byte order, and our tiny machine's instruction set.

Number bases

DecimalHexBinary
00x000000 0000
10x010000 0001
100x0a0000 1010
150x0f0000 1111
160x100001 0000
420x2a0010 1010
650x410100 0001
1270x7f0111 1111
2550xff1111 1111

A byte is a number from 0 to 255, written as two hex digits or eight bits. In Python, hex and bin display it, and int parses it back from text.

hex(42)         '0x2a'
bin(42)         '0b101010'
int("2a", 16)   42
f"{42:08b}"     '00101010'

Byte order (endianness)

The number 0x12345678 as four bytes in memory:

Big-endian

  1. 12+0
  2. 34+1
  3. 56+2
  4. 78+3

Little-endian

  1. 78+0
  2. 56+1
  3. 34+2
  4. 12+3

Little-endian puts the lowest byte first, and it's what most processors use. The highlighted byte is the highest one, and the numbers under each byte give its offset from the start.

n = 0x12345678
n.to_bytes(4, "big").hex(" ")
'12 34 56 78'
n.to_bytes(4, "little").hex(" ")
'78 56 34 12'

x86-64 registers

RegisterWhat it holds
raxThe function's return value
rdi rsi rdx rcx r8 r9Arguments one through six
rspTop of the stack
rbpBase of the function's frame
ripAddress of the next instruction

This is the System V convention used on Linux and macOS. Windows passes the first four arguments in rcx, rdx, r8 and r9. Each register also has a name for its lower half: eax is the lower half of rax.

Common x86-64 instructions

InstructionWhat it does
mov a, bCopies the second into the first
lea a, [m]Puts the address of m, not its value, in the first
add a, bAdds the second to the first
sub a, bSubtracts the second from the first
xor a, bXORs the first with the second
inc a / dec aAdds one or subtracts one
cmp a, bCompares and sets the flags
test a, bANDs and sets the flags
jmp LAlways jumps to L
je / jz LJumps if equal, or if the result is zero
jne / jnz LJumps if not equal
jg / jle LJumps if the first is greater, or less than or equal
push aPushes a value onto the stack
pop aPops a value off the stack
call f / retCalls a function, and returns from it

These are in Intel syntax, as tools like Ghidra show them, with the destination before the source. Linux tools like objdump default to AT&T syntax, which reverses the order and prefixes registers with %, so add -M intel to match this table. cmp and test don't store a result; they set the flags that the following jumps read. xor eax, eax is a short way to zero a register.

Printable ASCII table

Characters 0x20 to 0x7e: the row is the high hex digit, the column the low one
0123456789abcdef
2xsp!"#$%&'()*+,-./
3x0123456789:;<=>?
4x@ABCDEFGHIJKLMNO
5xPQRSTUVWXYZ[\]^_
6x`abcdefghijklmno
7xpqrstuvwxyz{|}~

Read a character's code from its row and column: A sits in row 4x, column 1, so it's 0x41. Everything printable falls between 0x20 and 0x7e, and anything outside that range is usually noise. In Python, ord turns a character into a number and chr turns a number back into a character.

struct format characters

CharBytesMeaning
<Little-endian
>Big-endian
!Network order, which is big-endian
x1Pad byte, skipped
B b1Unsigned and signed byte
H h2Unsigned and signed short
I i4Unsigned and signed int
Q q8Unsigned and signed long long
4s4Four raw bytes
struct.unpack("<IHB", data)
struct.calcsize("<IHB")   # 7

The format string in the example reads four bytes, then two, then one, little-endian. Always start a format string with a byte-order character: without one, Python adds padding to align the fields.

Tiny machine instructions

ByteInstructionOperandWhat it does
0x00HALTNoStops execution
0x01LOADYesPuts the operand in the accumulator
0x02ADDYesAdds the operand to the accumulator
0x03XORYesXORs the accumulator with the operand
0x04SUBYesSubtracts the operand from the accumulator
0x05OUTNoAppends the accumulator to the output

A machine we invented for learning, used by the program analysis lessons, the challenges and the exam. The accumulator starts at zero, all arithmetic is mod 256, and an instruction with an operand is two bytes long.

Glossary

The terms you'll meet in the lessons, each with a short definition.

byte
A unit of eight bits, a number from 0 to 255
hexadecimal
Base-16 notation, written with a 0x prefix, where a byte is two digits
endianness
The order of a number's bytes in memory, big-endian or little-endian
bitmask
A number used with bitwise operations to isolate specific bits
struct
Fixed-size fields laid out in a block of bytes, read with struct.unpack
header
Fields at the start of a file that describe its contents, like image size or file type
magic number
Bytes at the start of a file that identify its format, each format with its own
strings
Pulling runs of printable characters out of a block of bytes
hexdump
Bytes shown in rows, each with its offset, hex values and text
xor cipher
Encryption undone with the same key, since applying XOR twice gives back the original value
flag
A secret string you extract when you solve a challenge, proof that you beat it
hash
A one-way number computed from data that can't be turned back into the original
brute force
Trying every possibility until one matches
assembly
A language close to the processor's instructions, one instruction per line
register
A small, fast slot in the processor that holds a value while code runs
calling convention
The agreed rules for passing arguments and returning a value
stack
Memory that grows toward lower addresses, where a function keeps its arguments and return address
stack frame
The space a function reserves on the stack for its variables and return address
control flow
The path execution takes through conditions, jumps and loops
accumulator
The tiny machine's slot where arithmetic happens, starting at zero
virtual machine
A program that runs bytecode as if it were a small processor
bytecode
Instructions encoded as bytes, run by a virtual machine rather than the processor itself
disassembler
A tool that turns machine code or bytecode into readable instruction names
emulator
A program that runs another machine's instructions step by step
patching
Changing a program's bytes to alter its behavior
checksum
A number computed from bytes to check they're intact, like CRC32
crackme
A small program made for practice, where you reverse the check function to extract the key

Questions before you start

Quick answers to what people ask most before diving in.

Do I need special tools?

No. Everything runs in Python in your browser. Nothing to install, no system tools, and every program you analyze is a few bytes embedded in the lesson.

Is this legal?

Learning here is, because you only analyze programs we made for this purpose. Outside the site, the law varies by country, so stick to your own software, software whose license allows it, or practice challenges. Don't break into other people's software or get around its protection.

Do I need to know assembly first?

No. We start from the byte and work up to reading assembly step by step, and every instruction we use is in the quick reference above.

What does Python have to do with reverse engineering?

Python is a great tool for reading and processing bytes, and its standard library, with struct, zlib and hashlib, is enough to parse formats, undo simple encryption and build an emulator.

How are challenges different from lessons?

Challenges have fewer hints, and the grader checks the key your code computes against several hidden cases, so copying a ready-made answer won't work.

Do I get a certificate?

Yes. Pass all three stages of the Reverse Engineering exam and you get a mastery certificate in your name, just like the language exams.

Start from the first byte

The first lesson is short: you read the first bytes of a file and learn when reverse engineering is legitimate.