Classroom Public page

RE-011 Lab 8: Patch to Bypass

875 words

Find the key check function, identify the conditional branch that produces "Wrong" output, and patch it with the smallest possible change so the binary produces "Correct" for any input.


Overview

Binary patching is a fundamental RE skill: modifying compiled code without source, to change observable behavior. In this lab you apply the minimum viable patch to bypass a CrackMe's key check. The grading rewards precision: a single-byte patch earns full credit; a larger patch earns partial credit; a patch that works but requires you to know the correct key earns no credit.

Tools: Ghidra, objdump, hexedit or Python or dd, xxd

Time: ~90 minutes.

The binary: lab8_target -- a stripped x86-64 ELF provided by the instructor. It accepts one argument, prints "Correct" or "Wrong". It may include a simple anti-debug check (from Week 10) that you will need to recognize and optionally bypass. Self-paced fallback: see labs/_artifacts/README.md ("Self-paced fallback: Lab 8") for a C source + compile command that produces an equivalent binary with a license check and a ptrace anti-debug routine.


Part A: Static analysis -- find the branch

First: perform your full static analysis on lab8_target. Do not patch before you understand.

file lab8_target
nm -D lab8_target
strings -n 6 lab8_target
readelf -S lab8_target

Import into Ghidra, analyse, and navigate to the check function.

What you are looking for:

The key check function compares the user's input against an expected value and then takes one of two paths:

  • The "Correct" path: returns 1 or jumps to the success handler
  • The "Wrong" path: returns 0 or jumps to the failure handler

Your job is to identify the conditional branch instruction that separates these two paths. Record:

  1. The virtual address of the conditional jump instruction.
  2. The jump mnemonic (je, jne, jl, jge, etc.).
  3. What condition causes the "Wrong" path to execute (flags state when the jump is taken or not taken).
  4. What a one-byte or two-byte change would cause the "Correct" path to always execute.

Part B: Identify the file offset

To patch the binary file, you need the file offset of the byte(s) to change.

Option 1: Calculate from section headers. From readelf -S lab8_target, find the .text section's Off (file offset) and Addr (virtual address):

file_offset = virtual_address - section_load_address + section_file_offset

Option 2: Use objdump with file offsets.

objdump -d --file-offsets lab8_target | grep -A 2 <address_of_jump>

Some versions of objdump print the file offset alongside the virtual address.

Option 3: Use a hex search. Read the bytes at the jump instruction's virtual address from the Ghidra listing (e.g., 74 XX for je). Search for that byte sequence near the expected file offset:

xxd lab8_target | grep '74 XX'

Be careful: there may be multiple occurrences. Cross-check that the context bytes match what Ghidra shows.

Record:

  1. The file offset of the byte to patch (in hexadecimal).
  2. The original byte at that offset.
  3. The replacement byte.
  4. The expected behavioral change (what the patched instruction does, and why that causes "Correct" to be printed).

Part C: Apply the patch

Copy the binary first. Never patch your only copy:

cp lab8_target lab8_patched

Method 1: Python (precise, scriptable)

with open('lab8_patched', 'r+b') as f:
    f.seek(0x<file_offset>)
    original = f.read(1)
    print(f"Original byte: {original.hex()}")
    f.seek(0x<file_offset>)
    f.write(b'\x<replacement_byte>')
print("Patch applied.")

Method 2: dd (one-liner)

printf '\x<replacement_byte>' | dd of=lab8_patched bs=1 seek=$((0x<file_offset>)) count=1 conv=notrunc

Method 3: hexedit (interactive)

hexedit lab8_patched
# Ctrl-G to go to offset; type new hex value; Ctrl-X to save

After patching, verify the patch:

xxd -s $((0x<file_offset> - 2)) -l 8 lab8_patched

Confirm the patched byte is what you intended.


Part D: Verify the patch

Test the patched binary with multiple inputs:

./lab8_patched "anything"
./lab8_patched "wrong_key"
./lab8_patched ""
./lab8_patched "12345"

All four should produce "Correct" (or equivalent success output). If any input still produces "Wrong," the patch did not work as intended. Diagnose: did you patch the right instruction? Did the binary have a secondary check?

Record the test results and confirm that the binary accepts any input.


Part E: Anti-debug note (if applicable)

If lab8_target contains an anti-debug check (a ptrace call, a /proc/self/status read, or a timing check), document it:

  1. Where is the anti-debug code? (Function, address.)
  2. What does it check?
  3. Did you need to patch it to complete this lab? If so, document the additional patch.

If the binary has no anti-debug check, write a one-sentence confirmation: "No anti-debug check found; the binary's import table does not include ptrace and there is no timing check visible in the check function."


Lab Report

Submit one document with Parts A through E:

  • Part A: the branch instruction address, mnemonic, condition, and your proposed patch
  • Part B: file offset calculation (show your work)
  • Part C: the patching command you used and the verification xxd output
  • Part D: the four test outputs showing "Correct" for all inputs
  • Part E: anti-debug note

Submit both the original and patched binary (so the instructor can verify your patch is minimal).


Grading

Criterion Points
Part A: Branch correctly identified with address and mnemonic 20
Part B: File offset calculation is correct and shown 20
Part C: Patch correctly applied; verification shown 20
Part D: Binary accepts any input after patch; test results shown 25
Part E: Anti-debug documented or absence confirmed 15
Total 100

Patch size bonus: A single-byte patch (changing one byte, e.g., 74 to 75) that achieves the goal earns +5 bonus points. A two-byte patch (e.g., je to jmp) earns +2. A NOP sled or larger patch earns no bonus.

Penalty: A patch that requires you to know the correct key first (e.g., hardcoding the correct serial into the comparison) earns zero in Part D. The goal is to bypass the check entirely, not to satisfy it.


Lab 8 of 9. Due: end of Week 11. The patch technique here is the same technique used in vulnerability research to verify that a specific code path triggers a bug.