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

5.9 KiB

Embedded Systems Reverse Engineering

Repository

Week 10

Conditionals in Embedded Systems: Debugging and Hacking Static & Dynamic Conditionals w/ SG90 Servo Motor PWM Basics

Non-Credit Practice Exercise 3: Reverse the Servo Direction

Objective

Find the branch targets for case '1' and case '2' in the 0x0020_dynamic-conditionals binary using GDB, identify where each case loads its angle values from the literal pool, and swap the angle pairs so that pressing '1' now does what '2' originally did (180°→0°) and pressing '2' does what '1' originally did (0°→180°).

Prerequisites

  • Completed Week 10 tutorial (GDB and hex editor sections)
  • 0x0020_dynamic-conditionals.elf and 0x0020_dynamic-conditionals.bin available in your build directory
  • GDB (arm-none-eabi-gdb) and OpenOCD installed
  • A hex editor (HxD, ImHex, or similar)
  • Python installed (for UF2 conversion)
  • Raspberry Pi Pico 2 with SG90 servo motor connected on GPIO 6

Task Description

Currently, case '1' calls servo_set_angle(0.0f) then servo_set_angle(180.0f), while case '2' calls servo_set_angle(180.0f) then servo_set_angle(0.0f). To reverse the servo direction, you will swap the literal pool angle values between the two cases. This means patching case '1' to load 180.0f first and 0.0f second, and case '2' to load 0.0f first and 180.0f second.

Step-by-Step Instructions

Step 1: Start the Debug Session

Terminal 1 - Start OpenOCD:

openocd ^
  -s "C:\Users\flare-vm\.pico-sdk\openocd\0.12.0+dev\scripts" ^
  -f interface/cmsis-dap.cfg ^
  -f target/rp2350.cfg ^
  -c "adapter speed 5000"

Terminal 2 - Start GDB:

arm-none-eabi-gdb build\0x0020_dynamic-conditionals.elf

Connect to target:

(gdb) target remote :3333
(gdb) monitor reset halt
Step 2: Map Both Case Branch Targets

Disassemble main and trace both paths:

(gdb) disassemble 0x10000234,+250

Identify:

  • Case '1' branch target → first ldr r0 (loads 0.0f), bl servo_set_angle, second ldr r0 (loads 180.0f), bl servo_set_angle
  • Case '2' branch target → first ldr r0 (loads 180.0f), bl servo_set_angle, second ldr r0 (loads 0.0f), bl servo_set_angle
Step 3: Find All Four Literal Pool Entries

Examine the literal pool entries for each angle load:

(gdb) x/wx <case1_angle1_literal>
(gdb) x/wx <case1_angle2_literal>
(gdb) x/wx <case2_angle1_literal>
(gdb) x/wx <case2_angle2_literal>

Record which addresses contain 0x00000000 (0.0f) and which contain 0x43340000 (180.0f).

Step 4: Calculate All File Offsets
file_offset = literal_pool_address - 0x10000000

You need four offsets — two for case '1' angles and two for case '2' angles.

Step 5: Plan the Swap
Location Original New
Case 1 Angle 1 00 00 00 00 (0.0f) 00 00 34 43 (180.0f)
Case 1 Angle 2 00 00 34 43 (180.0f) 00 00 00 00 (0.0f)
Case 2 Angle 1 00 00 34 43 (180.0f) 00 00 00 00 (0.0f)
Case 2 Angle 2 00 00 00 00 (0.0f) 00 00 34 43 (180.0f)
Question 1: Could the compiler share a single literal pool entry for all references to 0x43340000? How would that affect your patching plan?
Step 6: Patch with HxD
  1. In HxD, open C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0020_dynamic-conditionals\build\0x0020_dynamic-conditionals.bin
  2. For each of the four literal pool entries, navigate to its file offset and swap the values as planned
  3. Be methodical — patch one at a time and verify each before moving to the next
Step 7: Save and Convert
  1. Click FileSave As0x0020_dynamic-conditionals-h.bin
cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0020_dynamic-conditionals
python ..\uf2conv.py build\0x0020_dynamic-conditionals-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
Step 8: Flash and Verify
  1. Hold BOOTSEL and plug in your Pico 2
  2. Drag and drop hacked.uf2 onto the RPI-RP2 drive

Check the behavior:

  • Press '1' → servo sweeps from 180° to 0° (was 0° to 180°)
  • Press '2' → servo sweeps from 0° to 180° (was 180° to 0°)
  • The text output still says "one" and "two" — only the physical behavior changed

Expected Output

After completing this exercise, you should be able to:

  • Trace multiple branch paths to their literal pool references
  • Understand how data (angles) and code (branches) are separate concerns
  • Swap data values to reverse physical behavior while keeping code structure intact
  • Recognize shared vs. separate literal pool entries

Questions for Reflection

Question 1: The terminal still prints "one" and "two" with the original meanings, but the servo does the opposite. Why is this a security concern in real embedded systems?
Question 2: Instead of swapping literal pool values, could you swap the branch targets themselves? What are the pros and cons of each approach?
Question 3: If the literal pool entries are shared between cases (one 0x43340000 word referenced by both), how would your patch strategy change?
Question 4: What tool could you use to confirm the swapped behavior without physical hardware — just by reading the patched disassembly?

Tips and Hints

  • Use x/4wx <literal_pool_start> to dump the entire literal pool at once and see all angle values together
  • If the compiler shares a single literal pool entry for 180.0f across both cases, swapping it would affect both — you may need to create a duplicate entry
  • The simplest approach: if each case has its own literal pool entries, just swap the 4-byte values at each offset
  • Verify by disassembling the patched binary in GDB to confirm the ldr instructions now reference the swapped values