# Embedded Systems Reverse Engineering [Repository](https://github.com/mytechnotalent/Embedded-Hacking) ## Week 6 Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics ### Non-Credit Practice Exercise 2: Reverse Engineer gpio_set_pulls with GDB #### Objective Use GDB to disassemble the `gpio_set_pulls` function, trace its register writes to identify the PADS_BANK0 hardware register it modifies, and document how the Pico 2 configures internal pull-up and pull-down resistors at the hardware level. #### Prerequisites - Completed Week 6 tutorial (GDB section) - `0x0014_static-variables.elf` binary available in your build directory - GDB (`arm-none-eabi-gdb`) and OpenOCD installed - Understanding of GPIO pull-up resistors from Week 6 Part 2 - Understanding of function inlining from Week 6 Part 3 #### Task Description You will use GDB to locate the `gpio_set_pulls` function (remember: `gpio_pull_up` is inlined and becomes `gpio_set_pulls`), disassemble it, step through it instruction by instruction, and identify the PADS_BANK0 register address it writes to. You will document the bit fields being set and explain how the hardware implements pull-up and pull-down resistors. #### Step-by-Step Instructions ##### Step 1: Start the Debug Session **Terminal 1 - Start OpenOCD:** ```powershell 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:** ```powershell arm-none-eabi-gdb build\0x0014_static-variables.elf ``` **Connect to target:** ```gdb (gdb) target remote :3333 (gdb) monitor reset halt ``` ##### Step 2: Find Where gpio_set_pulls is Called From the tutorial, we know `gpio_set_pulls` is called at `0x1000024e` with arguments for GPIO 15: ```gdb (gdb) x/5i 0x10000240 ``` You should see: ``` 0x10000240: movs r0, #15 ; GPIO pin 15 0x10000242: mov.w r3, #0 ; GPIO_IN (direction) 0x10000246: mcrr 0, 4, r0, r3, cr4 ; gpio_set_dir via coprocessor 0x1000024a: movs r2, #0 ; down = false 0x1000024c: movs r1, #1 ; up = true 0x1000024e: bl 0x100002d8 ; gpio_set_pulls ``` ##### Step 3: Disassemble gpio_set_pulls Now examine the function itself: ```gdb (gdb) x/30i 0x100002d8 ``` Study the disassembly carefully. Look for: - Address calculations (base address of PADS_BANK0) - Register loads and stores - Bit manipulation instructions (AND, OR, BIC) - The write to the hardware register ##### Step 4: Set a Breakpoint at gpio_set_pulls ```gdb (gdb) b *0x100002d8 (gdb) monitor reset halt (gdb) c ``` When the breakpoint hits, examine the arguments: ```gdb (gdb) info registers r0 r1 r2 ``` You should see: - `r0 = 15` (GPIO pin) - `r1 = 1` (pull-up enable) - `r2 = 0` (pull-down disable) ##### Step 5: Step Through the Function Use `si` (step instruction) to execute one instruction at a time: ```gdb (gdb) si (gdb) info registers ``` Repeat, watching the registers change. Pay attention to: 1. **Address calculation**: The function computes the PADS_BANK0 register address for GPIO 15. The base address is `0x40038000`, and each GPIO pad has a 4-byte register. GPIO 0 starts at offset `0x04`, so GPIO 15 is at: $$0x40038000 + 0x04 + (15 \times 4) = 0x40038000 + 0x04 + 0x3C = 0x40038040$$ 2. **Read the current register value**: The function reads the existing pad configuration 3. **Modify the pull bits**: It sets or clears the pull-up (bit 3) and pull-down (bit 2) bits 4. **Write back**: It stores the modified value ##### Step 6: Examine the PADS_BANK0 Register After the function completes, examine the result: ```gdb (gdb) x/1wx 0x40038040 ``` Document the value you see. The relevant bits are: | Bit | Name | Value | Meaning | | --- | -------- | ----- | ----------------------- | | 3 | PUE | 1 | Pull-up enable | | 2 | PDE | 0 | Pull-down enable | | 1 | SCHMITT | 1 | Schmitt trigger enabled | | 0 | SLEWFAST | 0 | Slow slew rate | ##### Step 7: Compare with gpio_set_pulls for the LED Pin Continue execution until gpio_set_pulls is called again (if it is). Or, examine the pad register for GPIO 16 (the LED pin): ```gdb (gdb) x/1wx 0x40038044 ``` Compare the values. The LED pin (output) should NOT have pull-up enabled. ##### Step 8: Document Your Findings Create a table documenting the function's behavior: | Step | Instruction(s) | Register Changes | | ------------------------ | ------------------ | --------------------------- | | Load PADS base address | `ldr rX, =...` | rX = `0x40038000` | | Calculate pad offset | `adds`, `lsls` | offset = `0x04 + pin * 4` | | Read current pad config | `ldr rX, [addr]` | rX = current register value | | Clear pull bits | `bic rX, rX, #0xC` | Clear bits 2 and 3 | | Set pull-up bit | `orr rX, rX, #0x8` | Set bit 3 (PUE) | | Write back | `str rX, [addr]` | Updated pad register | #### Expected Output After completing this exercise, you should be able to: - Disassemble and trace through a hardware configuration function - Identify PADS_BANK0 register addresses for any GPIO pin - Understand how pull-up and pull-down resistors are controlled at the register level - Explain why `gpio_pull_up(pin)` becomes `gpio_set_pulls(pin, true, false)` in the binary #### Questions for Reflection ###### Question 1: Why does the function read-modify-write the register instead of just writing a new value? What would happen if it just wrote without reading first? ###### Question 2: The pad register for GPIO 15 is at offset `0x40` from the PADS base. How is this offset calculated? What would the offset be for GPIO 0? ###### Question 3: What would happen if you enabled BOTH pull-up and pull-down at the same time (bits 2 and 3 both set)? ###### Question 4: The compiler inlines `gpio_pull_up` into `gpio_set_pulls`. What does this tell you about the compiler's optimization level? How does inlining affect binary analysis? #### Tips and Hints - PADS_BANK0 base address on RP2350 is `0x40038000` - Each GPIO has a 4-byte pad control register starting at offset `0x04` - The `bic` instruction clears bits (Bit Clear); `orr` sets bits (OR) - Use `x/1wx` to examine a 32-bit word; use `x/1tb` to see individual bits - The RP2350 datasheet section on PADS_BANK0 has the full register bit map #### Next Steps - Proceed to Exercise 3 to reverse engineer the `eor` (XOR) instruction in the GPIO logic - Try enabling pull-down instead of pull-up by modifying the `gpio_set_pulls` arguments in GDB: `set $r1 = 0` and `set $r2 = 1` before the function call - Examine the PADS registers for all GPIOs to see which pins have pull-ups enabled after boot