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

4.5 KiB
Raw Blame History

Embedded Systems Reverse Engineering

Repository

Week 4

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

Non-Credit Practice Exercise 4 Solution: Patch GPIO Binary to Change LED Pin

Answers

Patch Summary
Item Original Patched Hex Change
LED pin GPIO 16 GPIO 17 0x10 → 0x11
Printed value 0 (uninitialized) 66 0x00 → 0x42
Blink timing 500ms 100ms 0x1f4 → 0x64
Detailed Patch Locations

1. gpio_init parameter:

; Before: movs r0, #0x10    (bytes: 10 20)
; After:  movs r0, #0x11    (bytes: 11 20)

2. gpio_set_dir parameter:

; Before: movs r3, #0x10    (bytes: 10 23)
; After:  movs r3, #0x11    (bytes: 11 23)

3. gpio_put (LED ON) parameter:

; Before: movs r4, #0x10    (bytes: 10 24)
; After:  movs r4, #0x11    (bytes: 11 24)

4. gpio_put (LED OFF) parameter:

; Before: movs r4, #0x10    (bytes: 10 24)
; After:  movs r4, #0x11    (bytes: 11 24)

5. printf value:

; Before: movs r1, #0x00    (bytes: 00 21)
; After:  movs r1, #0x42    (bytes: 42 21)

6. sleep_ms (both calls):

; Before: loads 0x1f4 (500ms)
; After:  movs r0, #0x64 (100ms)
Hex Conversions
GPIO 17:  17 = 0x11 = 0001 0001 binary
Value 66: 66 = 0x42 = 0100 0010 binary
100ms:   100 = 0x64 = 0110 0100 binary
Decompiled Result After All Patches
int main(void)
{
    stdio_init_all();
    gpio_init(0x11);              // GPIO 17 (green LED)
    gpio_set_dir(0x11, 1);        // Output
    
    while (true) {
        printf("age: %d\r\n", 0x42);  // Prints 66
        gpio_put(0x11, 1);            // Green LED ON
        sleep_ms(0x64);               // 100ms
        gpio_put(0x11, 0);            // Green LED OFF
        sleep_ms(0x64);               // 100ms
    }
}
Hardware Verification
  • GREEN LED (GPIO 17) blinks at 10 Hz (100ms on, 100ms off)
  • RED LED (GPIO 16) remains off
  • Serial output: age: 66 repeating

Reflection Answers

  1. Why did we need to patch GPIO 16 in multiple places (gpio_init, gpio_set_dir, gpio_put)? Each function takes the GPIO pin number as a separate parameter. gpio_init(16) configures the pad and function mux for pin 16. gpio_set_dir(16, 1) sets pin 16's direction to output. gpio_put(16, value) toggles pin 16's output state. These are independent function calls with independent immediate values in the instruction stream—the compiler doesn't share or reuse a single "pin number variable." Each movs rN, #0x10 loads the pin number fresh for its respective function call. Missing any one patch would result in a mismatch: e.g., initializing pin 17 but toggling pin 16.

  2. What would happen if you forgot to patch one of the gpio_put calls? You would get asymmetric behavior. For example, if you patched the "LED ON" gpio_put to pin 17 but left the "LED OFF" at pin 16: GPIO 17 (green) would turn on but never turn off (staying permanently lit), while GPIO 16 (red) would receive the "off" command for a pin that was never initialized—which would have no visible effect. The result: green LED stuck on, no blinking.

  3. How does the instruction encoding differ for immediate values less than 256 vs. greater than 255? In 16-bit Thumb encoding, movs Rd, #imm8 can only encode immediate values 0255 in a single 2-byte instruction. For values > 255 (like 500 = 0x1f4), the compiler must use either: (a) a 32-bit Thumb-2 movw Rd, #imm16 instruction (4 bytes, can encode 065535), (b) a multi-instruction sequence that constructs the value (e.g., movs + lsls + add), or (c) an ldr Rd, [pc, #offset] that loads the constant from a literal pool in flash. This is why patching sleep_ms(500) may be more complex than patching gpio_put(16, 1).

  4. If you wanted to make the LED blink at exactly 5 Hz, what sleep_ms value would you use? At 5 Hz, each complete cycle is 1000 / 5 = 200ms. With two sleep_ms() calls per cycle (ON and OFF), each call should be 200 / 2 = 100ms. In hex: 100 = 0x64. So sleep_ms(0x64) for each call—which is exactly the value used in this exercise's patch. For a different duty cycle (e.g., 150ms on, 50ms off), you'd use different values for each call while keeping the total at 200ms.