7.5 KiB
Embedded Systems Reverse Engineering
Week 6
Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics
Non-Credit Practice Exercise 4: Invert the Button Logic with XOR
Objective
Find the eor.w r3, r3, #1 instruction that implements the ternary operator's button inversion, patch it to eor.w r3, r3, #0 using a hex editor to reverse the LED behavior, and verify that the LED is now ON when the button is pressed and OFF when released — the opposite of the original behavior.
Prerequisites
- Completed Week 6 tutorial (all GDB and hex editor sections)
0x0014_static-variables.binbinary 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 button on GP15 and LED on GP16
Task Description
The original program uses gpio_put(LED_GPIO, !gpio_get(BUTTON_GPIO)) which the compiler implements as an XOR (eor.w r3, r3, #1) to invert the button state. With the pull-up resistor, button released = HIGH, so HIGH XOR 1 = 0 (LED off). You will patch the XOR operand from #1 to #0, which effectively removes the inversion: HIGH XOR 0 = 1 (LED on when released). This exercise demonstrates how a single-byte binary patch can completely reverse hardware behavior.
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\0x0014_static-variables.elf
Connect to target:
(gdb) target remote :3333
(gdb) monitor reset halt
Step 2: Locate the GPIO Logic
From the tutorial, the GPIO input/output logic is near address 0x10000274. Disassemble:
(gdb) x/10i 0x10000274
Look for this sequence:
0x10000274: mov.w r1, #0xd0000000 ; SIO base address
0x10000280: ldr r3, [r1, #4] ; Read GPIO input register
0x10000282: ubfx r3, r3, #15, #1 ; Extract bit 15 (button state)
0x10000286: eor.w r3, r3, #1 ; XOR with 1 — INVERT ? OUR TARGET
0x1000028a: mcrr 0, 4, r2, r3, cr0 ; Write to GPIO output
The eor.w r3, r3, #1 instruction is at address 0x10000286.
Step 3: Understand the Current Logic
Trace the logic with the pull-up resistor:
| Button State | GPIO 15 Input | After UBFX | After EOR #1 | LED (GPIO 16) |
|---|---|---|---|---|
| Released | 1 (HIGH) | 1 | 0 | OFF |
| Pressed | 0 (LOW) | 0 | 1 | ON |
The eor.w #1 flips the bit, implementing the ! (NOT) from the C code.
Step 4: Verify with GDB
Set a breakpoint at the eor.w instruction:
(gdb) b *0x10000286
(gdb) c
When it hits, check what value is about to be XORed:
(gdb) info registers r3
- If button is released:
r3 = 1? after EOR:r3 = 0 - If button is pressed:
r3 = 0? after EOR:r3 = 1
Step 5: Test the Patch in GDB
Modify the EOR operand in RAM to see the effect live:
(gdb) set $r3 = 0
(gdb) si
(gdb) info registers r3
Or skip the EOR entirely by advancing the PC past it, then observe the LED behavior.
Step 6: Examine the Instruction Encoding
Look at the raw bytes:
(gdb) x/4bx 0x10000286
0x10000286 <main+82>: 0x83 0xf0 0x01 0x03
The eor.w instruction is a 32-bit Thumb-2 encoding. The 4 bytes break down as:
0x83 0xF0— opcode + source register (r3)0x01— the immediate value (#1) ? this is what we change0x03— destination register (r3)
Step 7: Patch with the Hex Editor
- In HxD, open
C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables\build\0x0014_static-variables.bin - The instruction starts at file offset:
0x10000286 - 0x10000000 = 0x286 - The immediate byte is the 3rd byte: offset
0x286 + 2 = 0x288 - Press Ctrl+G and enter offset:
288 - You should see
01— change it to00 - Verify the surrounding bytes (
83 F0before and03after) are unchanged - Click File ? Save As ?
0x0014_static-variables-h.bin(in the samebuilddirectory)
?? Why offset
0x288? The 4-byte instruction starts at0x286, but the immediate value#1is in the third byte (index 2), so it's at0x286 + 2 = 0x288.
Step 8: Predict the New Behavior
After patching, the logic changes:
| Button State | GPIO 15 Input | After UBFX | After EOR #0 | LED (GPIO 16) |
|---|---|---|---|---|
| Released | 1 (HIGH) | 1 | 1 | ON |
| Pressed | 0 (LOW) | 0 | 0 | OFF |
The LED behavior is now inverted from the original!
Step 9: Convert to UF2 and Flash
cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables
python ..\uf2conv.py build\0x0014_static-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
- Hold BOOTSEL and plug in your Pico 2
- Drag and drop
hacked.uf2onto the RPI-RP2 drive
Step 10: Verify the Hack
Test the button:
- Button NOT pressed: LED should now be ON (was OFF before patching)
- Button PRESSED: LED should now be OFF (was ON before patching)
The LED behavior is completely reversed by changing a single byte!
Expected Output
After completing this exercise, you should be able to:
- Locate XOR / EOR instructions in disassembled GPIO logic
- Understand how XOR implements logical NOT for single-bit values
- Patch a Thumb-2 encoded immediate operand
- Predict hardware behavior changes from binary patches
Questions for Reflection
Question 1: Why does XOR with 1 act as a NOT for single-bit values? Write out the truth table for x XOR 1 and x XOR 0 where x is 0 or 1.
Question 2: Instead of changing eor.w r3, r3, #1 to eor.w r3, r3, #0, could you achieve the same result by NOPing (removing) the instruction entirely? What bytes encode a NOP in Thumb?
Question 3: The pull-up resistor means "pressed = LOW." If you removed the pull-up (changed gpio_pull_up to no pull), would the button still work? Why or why not?
Question 4: The ubfx r3, r3, #0xf, #0x1 instruction extracts bit 15. If you changed #0xf to #0x10 (bit 16), what GPIO pin would you be reading? What value would you get if nothing is connected to that pin?
Tips and Hints
eor.w r3, r3, #1is a 32-bit Thumb-2 instruction (4 bytes), not a 16-bit Thumb instruction- A Thumb NOP is
00 bf(2 bytes) — you would need two NOPs to replace a 4-byte instruction - Use GDB
x/1twto view a word in binary format, making bit manipulation easier to see - The SIO base address
0xd0000000provides single-cycle access to GPIO — it's separate from the IO_BANK0 registers at0x40028000
Next Steps
- Review all four exercises and verify you can patch any part of the binary: data values, arithmetic operations, and logic operations
- Try combining multiple hacks in a single binary: change the initial value, speed up the overflow, AND invert the button logic
- Compare your patched binary with the original using
fc /b original.bin patched.binin the command prompt to see all changed bytes