Files
Embedded-Hacking/WEEK04/WEEK04-02.md
2026-03-19 15:01:07 -04:00

6.5 KiB

Embedded Systems Reverse Engineering

Repository

Week 4

Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics

Non-Credit Practice Exercise 2: Patch Binary to Change Variable Value

Objective

Use Ghidra's patching capabilities to modify the compiled binary, changing the value printed by the program from 43 to a different value of your choice, then convert and flash the modified binary to the Pico 2.

Prerequisites

  • Completed Exercise 1 (binary imported and analyzed in Ghidra)
  • Python installed for UF2 conversion
  • uf2conv.py script available in your project directory
  • Raspberry Pi Pico 2 connected via USB
  • Serial monitor software (PuTTY, minicom, or screen)

Task Description

You will locate the instruction that loads the value 43 into a register, patch it to use a different value (70 in this example), export the modified binary, convert it to UF2 format, and flash it to verify the change.

Step-by-Step Instructions

Step 1: Open Your Ghidra Project

If you closed Ghidra from Exercise 1:

  1. Launch ghidraRun
  2. Open the project: week04-ex01-intro-to-variables
  3. Double-click the binary file to open it
Step 2: Navigate to the Value Load Instruction

In Exercise 1, we found that main() calls printf("age: %d\r\n", 0x2b).

Find the assembly instruction:

  1. Click on the main function in the Symbol Tree
  2. Look at the Listing window (assembly view)
  3. Find the line with movs r1, #0x2b

The instruction should look like:

10000xxx    21 2b    movs    r1, #0x2b

What this instruction means:

  • Opcode: 21 2b (encoded instruction bytes)
  • Mnemonic: movs r1, #0x2b
  • Operation: Move the immediate value 0x2b into register r1
  • Register r1 will be used as the argument to printf
Step 3: Choose Your New Value

Let's change the value from 43 (0x2b) to 70 (0x46).

Convert 70 to hexadecimal:

  • 70 ÷ 16 = 4 remainder 6
  • Therefore: 70 decimal = 0x46 hexadecimal

You can verify this in Python:

>>> hex(70)
'0x46'
>>> 0x46
70
Step 4: Patch the Instruction

Now we'll modify the binary:

  1. Right-click on the instruction movs r1, #0x2b
  2. Select Patch Instruction
  3. A dialog appears showing the current instruction
  4. Change #0x2b to #0x46
  5. Press Enter or click OK

The instruction now reads:

10000xxx    21 46    movs    r1, #0x46

Visual confirmation:

  • The patched instruction should be highlighted (usually in red or orange)
  • The bytes should change from 21 2b to 21 46
  • The decompiled view should update to show printf("age: %d\r\n", 0x46);
Step 5: Verify the Patch in Decompile Window

Click on the main function again and check the Decompile window:

int main(void)
{
    stdio_init_all();
    do {
        printf("age: %d\r\n", 0x46);  // Changed from 0x2b!
    } while (true);
}

Perfect! The decompiler recognized our patch.

Step 6: Export the Patched Binary

Now we need to save the modified binary:

  1. Click FileExport Program
  2. Set Format to: Binary
  3. Navigate to your build directory:
    • Embedded-Hacking\0x0005_intro-to-variables\build\
  4. Set Filename to: 0x0005_intro-to-variables-h.bin
    • The -h suffix means "hacked"
  5. Click OK

Important: Make sure you're exporting to the correct location!

Step 7: Convert to UF2 Format

The Pico 2 requires UF2 format. Open a terminal and run:

Navigate to the project directory:

cd C:\path\to\Embedded-Hacking\0x0005_intro-to-variables

Run the conversion:

python ..\uf2conv.py build\0x0005_intro-to-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2

Command breakdown:

  • uf2conv.py - The conversion script
  • --base 0x10000000 - XIP flash base address (where code runs from)
  • --family 0xe48bff59 - RP2350 family ID
  • --output build\hacked.uf2 - Output filename

Expected output:

Converting to uf2, output size: 57856, start address: 0x10000000
Wrote 57856 bytes to build\hacked.uf2
Step 8: Flash the Hacked Binary

Enter bootloader mode:

  1. Disconnect your Pico 2 from USB
  2. Hold down the BOOTSEL button
  3. While holding BOOTSEL, plug in the USB cable
  4. Release BOOTSEL
  5. A drive called RPI-RP2 appears

Flash the binary:

  1. Open the RPI-RP2 drive
  2. Drag and drop build\hacked.uf2 onto the drive
  3. The Pico will automatically reboot
Step 9: Verify the Changes

Open your serial monitor:

For PuTTY:

  1. Select Serial connection type
  2. Set the COM port (check Device Manager)
  3. Set speed to 115200
  4. Click Open

For PowerShell:

# Find the COM port
Get-PnpDevice -Class Ports | Where-Object {$_.FriendlyName -like "*USB Serial*"}

# Connect (replace COM3 with your port)
$port = new-Object System.IO.Ports.SerialPort COM3,115200,None,8,one
$port.Open()
while($true) { $port.ReadLine() }

Expected output:

age: 70
age: 70
age: 70
age: 70
...

🎉 Success! You've successfully patched a binary and changed its behavior!

Expected Output

After completing this exercise, you should:

  • See age: 70 printing instead of age: 43
  • Have a patched binary file (0x0005_intro-to-variables-h.bin)
  • Have a UF2 file (hacked.uf2)
  • Understand the complete patching workflow

Questions for Reflection

Question 1: Why do we need to convert to UF2 format instead of flashing the raw .bin file?
Question 2: What is the significance of the base address 0x10000000 in the conversion command?
Question 3: What would happen if you patched the wrong instruction by mistake?
Question 4: How can you verify a patch was applied correctly before exporting?

Tips and Hints

  • Always make a backup of the original binary before patching
  • Use descriptive names like -h (hacked) or -patch for modified binaries
  • Test your patches on hardware to ensure they work as expected
  • If something goes wrong, you can always flash the original UF2 file
  • Use File → Export Program → Original File to revert all patches

Next Steps

  • Try patching to different values (100, 255, etc.)
  • Proceed to Exercise 3 to learn about GPIO control
  • Experiment with patching multiple values in the same binary

Additional Challenge

Instead of changing the value to 70, change it to 255 (0xFF) - the maximum value for a uint8_t. What do you observe? Now try changing it to 256 (0x100). What happens and why? (Hint: Think about the size limits of the instruction encoding.)