# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)

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

### Exercise 3: Analyze and Understand GPIO Control

#### Objective
Import the `0x0008_uninitialized-variables.bin` binary, analyze the GPIO initialization and control sequences, understand how `gpio_init()`, `gpio_set_dir()`, and `gpio_put()` work at the assembly level.

#### Prerequisites
- Completed Exercises 1 and 2
- Understanding of GPIO basics from Week 4 Part 3
- Raspberry Pi Pico 2 with an LED connected to GPIO 16
- Basic knowledge of ARM Thumb-2 instruction set

#### Task Description
You will import a new binary that controls GPIO pins, identify the GPIO-related function calls, trace the initialization sequence, and understand how the Pico SDK controls hardware at the low level.

#### Step-by-Step Instructions

##### Step 1: Flash the Original Binary

Before analysis, let's see what the program does:

1. Hold **BOOTSEL** and plug in your Pico 2
2. Flash `0x0008_uninitialized-variables.uf2` to the RPI-RP2 drive
3. Open your serial monitor

**Expected output:**
```
age: 0
age: 0
age: 0
...
```

**Expected behavior:**
- The red LED on GPIO 16 blinks on/off every 500ms
- The value `0` is printed (uninitialized variable)

##### Step 2: Create a New Ghidra Project

1. Launch Ghidra: `ghidraRun`
2. Click **File** → **New Project**
3. Select **Non-Shared Project**
4. Project Name: `week04-ex03-gpio-analysis`
5. Click **Finish**

##### Step 3: Import the GPIO Binary

1. Drag and drop `0x0008_uninitialized-variables.bin` into Ghidra
2. Set Language: **ARM Cortex 32 little endian default**
3. Click **Options…**
   - Block Name: `.text`
   - Base Address: `10000000`
4. Click **OK** on all dialogs
5. Double-click the file and click **Yes** to analyze

##### Step 4: Identify the Main Function

Look for the main function (likely `FUN_10000234` or similar):

**In the Symbol Tree:**
1. Expand **Functions**
2. Look for a function that appears to be an entry point
3. Click on potential `main` candidates

**Look for these patterns in the decompile:**
- Call to `stdio_init_all()`
- Call to `gpio_init()`
- Infinite while loop with `gpio_put()` and `sleep_ms()`

##### Step 5: Rename the Main Function

Once you identify `main`:

1. Right-click on the function name
2. Select **Edit Function Signature**
3. Change to: `int main(void)`
4. Click **OK**

**Expected decompiled code structure:**
```c
int main(void)
{
    // Some initial value
    stdio_init_all();
    gpio_init(0x10);  // GPIO 16
    // ... more GPIO setup
    
    while (true) {
        printf(...);
        gpio_put(0x10, 1);
        sleep_ms(0x1f4);
        gpio_put(0x10, 0);
        sleep_ms(0x1f4);
    }
}
```

##### Step 6: Identify GPIO Function Calls

Look in the decompiled main for function calls. You should see several undefined functions.

**Find and rename these GPIO functions:**

| Auto-Generated Name | Actual Function   | How to Identify                            |
| ------------------- | ----------------- | ------------------------------------------ |
| `FUN_xxxxx`         | `gpio_init`       | Takes one parameter (pin number)           |
| `FUN_xxxxx`         | `gpio_set_dir`    | Takes two parameters (pin, direction)      |
| `FUN_xxxxx`         | `gpio_put`        | Takes two parameters (pin, value)          |
| `FUN_xxxxx`         | `sleep_ms`        | Takes one parameter (milliseconds)         |
| `FUN_xxxxx`         | `stdio_init_all`  | Takes no parameters, called first          |
| `FUN_xxxxx`         | `printf`          | Takes variable args, has format string     |

**Example renaming gpio_init:**
1. Click on the function call in the decompile window
2. Right-click → **Edit Function Signature**
3. Change name to: `gpio_init`
4. Set signature to: `void gpio_init(uint gpio)`
5. Click **OK**

##### Step 7: Analyze GPIO Initialization Sequence

After renaming, your decompiled main should look clearer:

```c
int main(void)
{
    stdio_init_all();
    
    gpio_init(0x10);              // Initialize GPIO 16
    gpio_set_dir(0x10, 1);        // Set as output (1 = GPIO_OUT)
    
    while (true) {
        printf("age: %d\r\n", 0);
        gpio_put(0x10, 1);        // LED ON
        sleep_ms(0x1f4);          // Wait 500ms (0x1f4 = 500)
        gpio_put(0x10, 0);        // LED OFF
        sleep_ms(0x1f4);          // Wait 500ms
    }
}
```

**Key observations:**
- `0x10` is hexadecimal for 16 (GPIO 16 - red LED)
- `0x1f4` is hexadecimal for 500 (milliseconds)
- `1` means GPIO_OUT (output direction)
- The LED is controlled by toggling between 1 (on) and 0 (off)

##### Step 8: Examine gpio_init Assembly

Double-click on `gpio_init` to jump to its implementation.

**Look for these key operations in the assembly:**

```assembly
; Load GPIO pin number into register
movs    r4, r0          ; Save pin number

; Calculate pad register address
; Base address: 0x40038000 (PADS_BANK0)
; Offset: pin * 4
ldr     r3, =0x40038000
lsls    r5, r4, #2      ; pin * 4
add     r3, r5          ; Calculate address

; Configure pad (clear OD bit, set IE bit)
ldr     r2, [r3]        ; Read current config
bic     r2, #0x80       ; Clear output disable
orr     r2, #0x40       ; Set input enable
str     r2, [r3]        ; Write back

; Set GPIO function to SIO (0x05)
ldr     r3, =0x40028000 ; IO_BANK0 base
add     r3, r5          ; Add offset
movs    r2, #5          ; FUNCSEL = SIO
str     r2, [r3]        ; Set function
```

**What this does:**
1. Configures the GPIO pad registers (physical pin properties)
2. Sets the GPIO function to SIO (Software I/O)
3. Prepares the pin for software control

##### Step 9: Examine gpio_put Assembly

Find the `gpio_put` function and examine its implementation.

**Look for the GPIO coprocessor instruction:**

```assembly
gpio_put:
    movs    r4, r0          ; GPIO pin number
    movs    r5, r1          ; Value (0 or 1)
    
    ; Use ARM coprocessor to control GPIO
    mcrr    p0, #4, r4, r5, c0
    
    bx      lr              ; Return
```

**Critical instruction: `mcrr p0, #4, r4, r5, c0`**
- `mcrr` = Move to Coprocessor from two ARM Registers
- `p0` = Coprocessor 0 (GPIO coprocessor in RP2350)
- `#4` = Operation code
- `r4, r5` = Source registers (pin number, value)
- `c0` = Coprocessor register (GPIO output control)

This is a **single-cycle GPIO operation** - extremely fast!

##### Step 10: Document the GPIO Memory Map

Create a reference table of the addresses you found:

| Address      | Register      | Purpose                         |
| ------------ | ------------- | ------------------------------- |
| `0x40028000` | IO_BANK0      | GPIO function selection         |
| `0x40038000` | PADS_BANK0    | GPIO pad configuration          |
| `0xd0000000` | SIO           | Single-cycle I/O (coprocessor)  |

**GPIO 16 specific addresses:**
- Pad control: `0x40038000 + (16 * 4) = 0x40038040`
- Function select: `0x40028000 + (16 * 4) = 0x40028040`

##### Step 11: Trace the Blink Timing

Calculate the actual timing:

**sleep_ms(0x1f4):**
- Convert: 0x1f4 = (1 × 256) + (15 × 16) + 4 = 256 + 240 + 4 = 500 decimal
- So the LED is on for 500ms, off for 500ms
- Total cycle time: 1000ms = 1 second
- Blink rate: 1 Hz

#### Expected Output

After completing this exercise, you should understand:
- How GPIO initialization configures hardware registers
- The role of the GPIO coprocessor in the RP2350
- How `gpio_put()` uses a single ARM instruction for fast I/O
- The memory-mapped addresses for GPIO control
- How timing delays are implemented with `sleep_ms()`

#### Questions for Reflection

###### Question 1: Why does gpio_init() need to configure both PADS_BANK0 and IO_BANK0 registers?

###### Question 2: What is the advantage of using the GPIO coprocessor instruction (`mcrr`) instead of writing to memory-mapped registers?

###### Question 3: If you wanted to blink the LED at 10 Hz instead of 1 Hz, what value should `sleep_ms()` use?

###### Question 4: What would happen if you called `gpio_put()` on a pin that hasn't been initialized with `gpio_init()` first?

#### Tips and Hints
- Use Ghidra's **References** feature (right-click → Find References) to see where functions are called
- The **Display** → **Memory Map** shows all memory regions
- Look for bit manipulation instructions (`bic`, `orr`) to understand register configuration
- The ARM Architecture Reference Manual has complete documentation on coprocessor instructions
- Use hex-to-decimal converters online if you're unsure about conversions

#### Next Steps
- Proceed to Exercise 4 to patch the GPIO binary
- Try to identify other SDK functions like `gpio_get()` if they appear
- Explore the full GPIO initialization in the SDK source code

#### Additional Challenge
Find the `gpio_set_dir()` function in Ghidra. Does it also use a GPIO coprocessor instruction? What coprocessor register does it use (c0, c4, or something else)? Compare its implementation to `gpio_put()` and document the differences.
