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

6.2 KiB

Embedded Systems Reverse Engineering

Repository

Week 6

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

Non-Credit Practice Exercise 3: Make the Overflow Happen Faster

Objective

Patch the adds r3, #0x1 instruction — which increments static_fav_num by 1 each loop iteration — to adds r3, #0xa so the variable increments by 10 instead. Use GDB to locate the instruction, calculate the hex editor file offset, patch the binary, and verify on hardware that the uint8_t overflow occurs roughly 10 times sooner.

Prerequisites

  • Completed Week 6 tutorial (GDB and hex editor sections)
  • 0x0014_static-variables.bin binary 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 connected via USB
  • Serial monitor software (PuTTY, minicom, or screen)

Task Description

The static variable static_fav_num is a uint8_t that counts from 42 to 255 before wrapping to 0. Currently it increments by 1 each iteration, so it takes 214 steps to overflow. You will change the increment value from 1 to 10 so that it overflows after only ~22 steps. This exercise teaches you how Thumb immediate encoding works for small arithmetic instructions and demonstrates the real-world impact of patching arithmetic operations.

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 Increment Instruction

From the tutorial, we know the static variable operations are in the loop body starting at 0x10000274. Disassemble the loop body:

(gdb) x/20i 0x10000274

Look for this sequence:

0x10000278:    ldrb r3, [r4, #0]   ; Load static_fav_num from RAM
0x1000027a:    movs r2, #16        ; LED GPIO pin number
0x1000027c:    adds r3, #1         ; Increment by 1 ? THIS IS OUR TARGET
0x1000027e:    strb r3, [r4, #0]   ; Store back to RAM

The adds r3, #1 instruction is at address 0x1000027c.

Step 3: Examine the Instruction Encoding

Look at the raw bytes of the instruction:

(gdb) x/2bx 0x1000027c

You should see:

01 33

Thumb encoding breakdown:

  • 01 = the immediate value 0x01 (decimal 1)
  • 33 = the opcode for adds r3, #imm8
Step 4: Test the Change in GDB First

Before making a permanent patch, test the change in RAM:

(gdb) b *0x1000027c
(gdb) c

When the breakpoint hits:

(gdb) x/1db 0x200005a8

Note the current value of static_fav_num. Now continue a few iterations and check how it increments.

Step 5: Calculate the File Offset
file_offset = address - 0x10000000

For the adds r3, #0x1 instruction at its address, calculate the offset.

Step 6: Patch with the Hex Editor
  1. In HxD, open C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0014_static-variables\build\0x0014_static-variables.bin
  2. Press Ctrl+G (Go to offset) and enter the calculated offset
  3. You should see the byte 01 followed by 33
  4. Change 01 to 0A (10 in decimal)
  5. Verify: the bytes should now read 0A 33 — encoding adds r3, #0xa
  6. Click File ? Save As ? 0x0014_static-variables-h.bin (in the same build directory)

?? Why this works: In Thumb adds rD, #imm8 encoding, the immediate value is stored in the first byte. The #imm8 field accepts values 0-255, so changing 1 to 10 is safe.

Step 7: 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
  1. Hold BOOTSEL and plug in your Pico 2
  2. Drag and drop hacked.uf2 onto the RPI-RP2 drive
  3. Open your serial monitor
Step 8: Verify the Hack

Expected serial output:

regular_fav_num: 42
static_fav_num: 42
regular_fav_num: 42
static_fav_num: 52     ? Jumped by 10!
regular_fav_num: 42
static_fav_num: 62
...
regular_fav_num: 42
static_fav_num: 242
regular_fav_num: 42
static_fav_num: 252
regular_fav_num: 42
static_fav_num: 6      ? Overflow! 252 + 10 = 262, but uint8_t wraps: 262 - 256 = 6

Notice the overflow now happens much sooner, and the wrap value is no longer 0 — it's 6 because 252 + 10 = 262 which wraps to 262 mod 256 = 6.

Expected Output

After completing this exercise, you should be able to:

  • Locate arithmetic instructions in disassembled code
  • Understand Thumb adds rD, #imm8 encoding
  • Patch an immediate operand in a hex editor
  • Predict the effects of changing an increment value on overflow behavior

Questions for Reflection

Question 1: The overflow now wraps to 6 instead of 0. Explain why, using the modular arithmetic of a uint8_t (range 0-255).
Question 2: What is the maximum value you could change the increment to while still using adds r3, #imm8? What would happen if you needed an increment larger than 255?
Question 3: If you changed the increment to 128 (0x80), how many iterations would it take to wrap, and what value would it wrap to?
Question 4: Could you achieve the same speedup by changing the strb (store byte) to strh (store halfword)? Why or why not?

Tips and Hints

  • In Thumb encoding, adds rD, #imm8 has the immediate in the first byte and opcode in the second
  • The imm8 field is 8 bits, so valid ranges are 0x00 to 0xFF (0-255)
  • Overflow for uint8_t is (value) \bmod 256
  • Use GDB to verify: set *(unsigned char*)0x200005a8 = 250 then continue to watch the wrap quickly

Next Steps

  • Proceed to Exercise 4 to learn about inverting button logic with XOR
  • Try changing the increment to other values (2, 5, 50, 128) and predict the wrap behavior before flashing
  • Consider: what would happen if you changed adds to subs (subtract)?