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

6.7 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 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:

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: 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) 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) 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) b *0x100002d8
(gdb) monitor reset halt
(gdb) c

When the breakpoint hits, examine the arguments:

(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) 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
  1. Read the current register value: The function reads the existing pad configuration
  2. Modify the pull bits: It sets or clears the pull-up (bit 3) and pull-down (bit 2) bits
  3. Write back: It stores the modified value
Step 6: Examine the PADS_BANK0 Register

After the function completes, examine the result:

(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) 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