Skip to content

jguida941/binary_to_assembly

Repository files navigation

Binary to Assembly

Taking a compiled program apart: working out what functions are inside it, turning the machine code back into assembly, and explaining what it does.

Written because the course that assigned this never explained the mechanics. Which command, typed where, and why.

Start here

GUIDE.md: the walkthrough, start to finish

Every command, what it does, why it was run, what the output meant. Written while doing the work, not reconstructed afterward. This is the main thing in this repo.

Setup

You need Docker, and nothing else. The analysis tools are GNU binutils, which exist on Linux and not on macOS, so everything runs in a pinned container that matches the course environment: Ubuntu 18.04, gcc 7.5.0, binutils 2.30.

git clone https://github.com/jguida941/binary_to_assembly.git
cd binary_to_assembly

make build       # builds the analysis image, prints its digest. Takes a few minutes once.
make examples    # compiles the five example programs, three ways each

Then take one apart:

make functions   FILE=examples/05_functions/build/05_functions
make disassemble FILE=examples/05_functions/build/05_functions FUNCTION=report
make strings     FILE=examples/05_functions/build/05_functions

make help lists everything, and every target prints the raw command it runs before running it, so you can copy that instead and stop using make whenever you like.

If you are in Codio doing the assignment

You need none of the above. Codio already has file, readelf, nm, objdump and Bless installed, and GUIDE.md is written against exactly that. The container is for taking apart programs you compiled yourself, on your own machine.

Running the CLI without make

docker run --rm --platform linux/amd64 -v "$PWD:/work" -w /work \
  -e PYTHONPATH=/work/src binary-assembly-guide:local \
  python3.8 -m binary_assembly_guide.cli functions BINARY

python3.8, not python3: Ubuntu 18.04's default is 3.6.9, too old to parse the CLI. environment/README.md explains why the image is pinned to an old toolchain anyway, and what was measured to prove it matches Codio.

Running the tests

On the host, not in the container, because they need git and the container has none.

python3 -m pytest -q                 # everything
python3 -m pytest tests/privacy -q   # the guardrail that matters most

The tools, and what each one is for

Four programs do the work. Each reads a different part of the same file, which is why one is not enough.

Tool What it is What it tells you
file identifies file types by content is this a program, what CPU, are the names still in it
readelf reads ELF structure the header fields file was summarising: type, entry point
nm lists the symbol table every name, with a letter saying whether it is code or data
objdump disassembler machine code decoded back into assembly, and raw section contents
Bless a hex editor the bytes themselves, and the names sitting in them as plain text

A hex editor shows you that function names are literally in the file as text. It cannot tell you which of those names is a function, which is a variable, and which is a source filename. nm reads the table next door that records exactly that. objdump reads the executable code itself.

The CLI

Four commands, one per step of the workflow the guide describes.

binary-assembly-guide inspect      BINARY
binary-assembly-guide functions    BINARY [--compare OTHER ...]
binary-assembly-guide disassemble  BINARY FUNCTION [--resolve]
binary-assembly-guide strings      BINARY [--section .rodata]

inspect shows what the file is, and whether it can be analyzed at all. Refuses anything that is not x86-64 ELF and says why, rather than producing confident nonsense.

functions lists every function, with who wrote each one. Given other unrelated binaries via --compare, it classifies by evidence rather than by a list of remembered names: anything present in all of them was not written for any of them.

disassemble --resolve is the one worth having. It prints a function's assembly and looks up what every address the code references actually contains:

  400556:  lea    0xf7(%rip),%rdi    # 400654
  400562:  callq  4003f0 <printf@plt>

Addresses this function references:
  0x400654  "%s: %d"

By hand that is objdump -s -j .rodata, finding the right line, counting bytes in, decoding hex, and stopping at the first 00. Per address. Skipping it is what turns an explanation into a description of a mechanism that never says what the program does.

strings shows the string literals in the data sections, split correctly. objdump's own ASCII column renders every unprintable byte as ., so strings run together and the boundaries are lost.

Shell wrappers in scripts/ call the same commands, so the underlying tool stays visible rather than hidden behind a program.

Full CLI guide: every command and every flag, with real output for each, including what happens on a stripped binary, a Mach-O file, and a name that does not exist.

Practice

Five small C programs, each isolating one construct, each with a written exercise beside it. You own the source, so you can check your reading against something whose answer you already know.

Example Construct
01_simple_return prologue, epilogue, and a return value
02_arithmetic where operands live, and which register carries the result
03_condition cmp plus a conditional jump, and why the condition is inverted
04_loop the backward jump, and where the counter is kept
05_functions four functions, argument registers, and a call graph

Each builds three ways: make for the debug build that resembles the course files, make stripped to see what happens when the symbol table is gone, and make optimized to see what -O2 does to code you just learned to read.

Everything else

docs/ five background pages, and troubleshooting where every error is one that actually happened
images/ screenshots used in the guide. Read its README before adding one
environment/ measured toolchain parity between Codio and the pinned container
dev/PLAN.md the architecture, and what building it proved wrong

Status

Working and finished for what it covers. The guide walks the full workflow, the CLI is tested against every example binary in three build variants, and the five examples compile clean and are documented from captured output.

Scope is deliberately narrow: x86-64 ELF only, non-stripped or it will tell you it cannot name anything. No JSON output and no GUI. Everything in here is something I ran and checked.

What is not in here

No completed coursework and no supplied binaries. inputs/ and outputs/ are gitignored and stay local, and a test suite enforces it rather than trusting .gitignore. This repo is the method, not the answers.

If you are taking this course: reading someone's finished worksheet teaches you nothing and your instructor can tell. The examples exist so you can practise on programs whose source you own, then do your own files yourself.

About

Take a compiled binary apart and understand it: a beginner's walkthrough of file, nm, objdump and readelf on real x86-64 ELF programs, plus a tested Python CLI that runs the same workflow and resolves the string data a disassembly points at. Written for SNHU CS-410 Module 3, useful for anyone learning to read assembly.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages