5.9 KiB
Embedded Systems Reverse Engineering
Week 11
Structures and Functions in Embedded Systems: Debugging and Hacking w/ IR Remote Control and NEC Protocol Basics
Non-Credit Practice Exercise 3: Swap All Three LEDs
Objective
Find the struct initialization instructions where led1_pin = 0x10 (GPIO 16, Red), led2_pin = 0x11 (GPIO 17, Green), and led3_pin = 0x12 (GPIO 18, Yellow) are written in the 0x0026_functions binary using GDB, calculate the file offsets, and rotate the GPIO values so that button 1→Green (0x11), button 2→Yellow (0x12), and button 3→Red (0x10), then verify on hardware that the LED mapping has shifted.
Prerequisites
- Completed Week 11 tutorial (GDB and hex editor sections)
0x0026_functions.elfand0x0026_functions.binavailable 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 IR remote and LEDs on GPIO 16, 17, 18
Task Description
The struct initialization sets led1_pin = 16 (0x10), led2_pin = 17 (0x11), led3_pin = 18 (0x12) using movs instructions that store each value into the struct. By patching the immediate values in these three movs instructions, you can rotate the LED assignment: led1_pin = 17 (Green), led2_pin = 18 (Yellow), led3_pin = 16 (Red). This means button 1 will light the Green LED, button 2 the Yellow LED, and button 3 the Red LED.
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\0x0026_functions.elf
Connect to target:
(gdb) target remote :3333
(gdb) monitor reset halt
Step 2: Find the Three movs Instructions
Disassemble main and find the struct pin initialization:
(gdb) disassemble 0x10000234,+300
Look for three consecutive movs/strb pairs:
movs r0, #0x10 ; led1_pin = 16 (Red)
strb r0, [r4, #0]
movs r0, #0x11 ; led2_pin = 17 (Green)
strb r0, [r4, #1]
movs r0, #0x12 ; led3_pin = 18 (Yellow)
strb r0, [r4, #2]
Note the address of each movs instruction.
Step 3: Examine the Instruction Bytes
Check the raw encoding of each movs:
(gdb) x/2bx <address_of_movs_0x10>
(gdb) x/2bx <address_of_movs_0x11>
(gdb) x/2bx <address_of_movs_0x12>
Each will have the GPIO pin number as the immediate byte.
Step 4: Calculate the File Offsets
file_offset = address - 0x10000000
Note the offset of the immediate byte in each of the three movs instructions.
Step 5: Plan the Rotation
| Struct Member | Original | New | Effect |
|---|---|---|---|
led1_pin |
10 (GPIO 16 Red) |
11 (GPIO 17 Green) |
Button 1 → Green |
led2_pin |
11 (GPIO 17 Green) |
12 (GPIO 18 Yellow) |
Button 2 → Yellow |
led3_pin |
12 (GPIO 18 Yellow) |
10 (GPIO 16 Red) |
Button 3 → Red |
Step 6: Patch with HxD
- In HxD, open
C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0026_functions\build\0x0026_functions.bin - Go to the first
movsimmediate offset and change10to11 - Go to the second
movsimmediate offset and change11to12 - Go to the third
movsimmediate offset and change12to10
Question 1: All three patches are single-byte changes in movs immediates. Why is this simpler than patching literal pool entries?
Step 7: Save and Convert
- Click File → Save As →
0x0026_functions-h.bin
cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0026_functions
python ..\uf2conv.py build\0x0026_functions-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
Step 8: Flash and Verify
- Hold BOOTSEL and plug in your Pico 2
- Drag and drop
hacked.uf2onto the RPI-RP2 drive
Check the behavior:
- Press button 1 → Green LED blinks (was Red)
- Press button 2 → Yellow LED blinks (was Green)
- Press button 3 → Red LED blinks (was Yellow)
- Terminal still says "LED 1 activated on GPIO 16" — but the actual LED is Green (GPIO 17)
Expected Output
After completing this exercise, you should be able to:
- Locate struct initialization patterns in ARM Thumb assembly
- Patch multiple
movsimmediates to rotate data values - Understand the disconnect between logged values and actual hardware behavior
- Recognize log desynchronization as a security concern
Questions for Reflection
Question 1: The terminal log still says "LED 1 activated on GPIO 16" even though GPIO 17 (Green) is actually blinking. Why don't the logs update automatically?
Question 2: If the struct initialization used ldr from a literal pool instead of movs immediates, how would the patching approach differ?
Question 3: Could you achieve the same LED rotation by patching the gpio_init and gpio_put calls instead of the struct? Which approach is cleaner and why?
Question 4: In a real attack scenario, why is log desynchronization (logs say one thing, hardware does another) particularly dangerous for forensic analysis?
Tips and Hints
- The three
movsinstructions are likely within 10-20 bytes of each other — usex/20ito see them all at once - The
movs rN, #imm8immediate is in the lower byte of the 2-byte Thumb instruction - Make sure you patch the
movsfor the struct initialization, not any othermovs #0x10/0x11/0x12that may exist elsewhere - Verify by examining the struct in memory after initialization:
x/6bx <struct_address>should show11 12 10for the pin bytes