From f36217539eddf8ec63d1ab4b8e4fbaefd81ceefe Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Sun, 1 Mar 2026 12:45:26 -0500 Subject: [PATCH] Added WEEK06 and WEEK07 --- README.md | 20 + WEEK06/WEEK06-01.md | 156 +++++++ WEEK06/WEEK06-02.md | 185 ++++++++ WEEK06/WEEK06-03.md | 181 ++++++++ WEEK06/WEEK06-04.md | 188 ++++++++ WEEK06/WEEK06.md | 957 ++++++++++++++++++++++++++++++++++++++++ WEEK07/WEEK07-01.md | 204 +++++++++ WEEK07/WEEK07-02.md | 157 +++++++ WEEK07/WEEK07-03.md | 206 +++++++++ WEEK07/WEEK07-04.md | 147 +++++++ WEEK07/WEEK07.md | 1022 +++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 3423 insertions(+) create mode 100644 WEEK06/WEEK06-01.md create mode 100644 WEEK06/WEEK06-02.md create mode 100644 WEEK06/WEEK06-03.md create mode 100644 WEEK06/WEEK06-04.md create mode 100644 WEEK06/WEEK06.md create mode 100644 WEEK07/WEEK07-01.md create mode 100644 WEEK07/WEEK07-02.md create mode 100644 WEEK07/WEEK07-03.md create mode 100644 WEEK07/WEEK07-04.md create mode 100644 WEEK07/WEEK07.md diff --git a/README.md b/README.md index 4ac6b9d..456fc00 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,16 @@ This chapter covers hacking the double floating-point data type as it relates to ## Week 6 Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics +### Week 6 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06.md) + +#### Exercise 1: Change the Static Variable Initial Value from 42 to 100 [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06-01.md) + +#### Exercise 2: Reverse Engineer gpio_set_pulls with GDB [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06-02.md) + +#### Exercise 3: Make the Overflow Happen Faster [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06-03.md) + +#### Exercise 4: Invert the Button Logic with XOR [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK06/WEEK06-04.md) + ### Chapter 20: Static Variables This chapter covers static variables as well as an intro to GPIO inputs as we work with push buttons as it relates to embedded development on the Pico 2. @@ -225,6 +235,16 @@ This chapter covers hacking static variables as well as an intro to GPIO inputs ## Week 7 Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics +### Week 7 Notebook [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07.md) + +#### Exercise 1: Change Both LCD Lines [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07-01.md) + +#### Exercise 2: Find All String Literals in the Binary [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07-02.md) + +#### Exercise 3: Trace the I²C Struct Pointer Chain [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07-03.md) + +#### Exercise 4: Display Your Own Custom Message on the LCD [HERE](https://github.com/mytechnotalent/Embedded-Hacking/blob/main/WEEK07/WEEK07-04.md) + ### Chapter 23: Constants This chapter covers constants as well as an intro to I2C as we work a 1602 LCD as it relates to embedded development on the Pico 2. diff --git a/WEEK06/WEEK06-01.md b/WEEK06/WEEK06-01.md new file mode 100644 index 0000000..40dbc27 --- /dev/null +++ b/WEEK06/WEEK06-01.md @@ -0,0 +1,156 @@ +# 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 + +### Exercise 1: Change the Static Variable Initial Value from 42 to 100 + +#### Objective +Use GDB to locate the static variable `static_fav_num` in the `.data` section of the `0x0014_static-variables` binary, calculate the corresponding file offset, patch the initial value from `42` (`0x2A`) to `100` (`0x64`) using a hex editor, convert the patched binary to UF2 format, and flash it to the Pico 2 to verify the change. + +#### Prerequisites +- Completed Week 6 tutorial (GDB section) +- `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 +You will use GDB to examine the static variable at its known RAM address (`0x200005a8`), trace back to where the initial value is stored in the `.data` section of flash, calculate the file offset in the `.bin` file, patch the byte from `0x2A` to `0x64` with a hex editor, and verify on hardware that the serial output now starts counting from `100` instead of `42`. + +#### Step-by-Step Instructions + +##### Step 1: Start the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0014_static-variables.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +##### Step 2: Examine the Static Variable in RAM + +We know from the tutorial that the static variable lives at `0x200005a8`. Examine its current value: + +```gdb +(gdb) x/1db 0x200005a8 +``` + +You should see the current value (probably `42` or a count if the program has been running). + +##### Step 3: Find Where the Initial Value Lives in Flash + +Static variables that are initialized get their starting values from the `.data` section in flash. The startup code copies these values from flash to RAM before `main()` runs. + +Examine the disassembly to find data copy references. The initial value `42` (`0x2A`) is stored somewhere in flash. Use GDB to search: + +```gdb +(gdb) find /b 0x10000000, 0x10010000, 0x2a +``` + +This searches the flash region for the byte `0x2A`. You may get multiple hits — look for one that is in the data initialization area (typically near the end of the code section). + +##### Step 4: Confirm the Address + +Once you identify a candidate address, verify it by examining the surrounding bytes: + +```gdb +(gdb) x/8xb +``` + +The `0x2A` byte at this address should be the initialization value for our static variable. + +##### Step 5: Calculate the File Offset + +The binary is loaded at base address `0x10000000`. To find the file offset: + +``` +file_offset = address - 0x10000000 +``` + +For example, if the initial value is at `0x10004xxx`: +- File offset = `0x10004xxx` - `0x10000000` = `0x4xxx` + +##### Step 6: Patch the Value with a Hex Editor + +1. Open `0x0014_static-variables.bin` in HxD +2. Press **Ctrl+G** (Go to offset) +3. Enter the calculated offset +4. You should see the byte `2A` at this position +5. Change `2A` to `64` (100 in decimal) +6. Click **File** → **Save As** → `0x0014_static-variables-h.bin` + +##### Step 7: Convert to UF2 and Flash + +```bash +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: 100 ← Starts at 100 now! +regular_fav_num: 42 +static_fav_num: 101 +regular_fav_num: 42 +static_fav_num: 102 +... +``` + +The static variable now starts counting from 100 instead of 42! + +#### Expected Output + +After completing this exercise, you should be able to: +- Use GDB `find` command to search for byte patterns in flash +- Distinguish between RAM addresses (where the variable lives at runtime) and flash addresses (where the initial value is stored) +- Calculate file offsets from memory addresses +- Patch initialization values with a hex editor +- Understand the `.data` section copy mechanism at startup + +#### Questions for Reflection + +###### Question 1: Why does the initial value live in flash AND get copied to RAM? Why not just use flash directly? + +###### Question 2: The static variable wraps around at 255 (since it's `uint8_t`). After patching the initial value to 100, after how many iterations will it overflow back to 0? + +###### Question 3: If you also wanted to change the `regular_fav_num` constant from 42, would you patch the same area of the binary? Why or why not? + +###### Question 4: What would happen if the `.data` section had TWO static variables — would their initial values be adjacent in flash? + +#### Tips and Hints +- The `find` command in GDB can search for bytes, halfwords, or words in any memory range +- Static variables with initial values are in `.data`; without initial values they're in `.bss` +- The startup code (`crt0`) copies the entire `.data` section from flash to RAM before calling `main()` +- HxD shows both hex and ASCII — the value `0x2A` is the ASCII character `*` + +#### Next Steps +- Proceed to Exercise 2 to try a more complex hack (reversing GPIO logic) +- Try patching the static variable to `0xFF` (255) and observe immediate overflow behavior +- Look at the startup code in GDB to find the memcpy that copies `.data` from flash to RAM diff --git a/WEEK06/WEEK06-02.md b/WEEK06/WEEK06-02.md new file mode 100644 index 0000000..ce93a32 --- /dev/null +++ b/WEEK06/WEEK06-02.md @@ -0,0 +1,185 @@ +# 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 + +### 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:** + +```bash +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:** + +```bash +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 `0x10000250` with arguments for GPIO 15: + +```gdb +(gdb) x/5i 0x1000024a +``` + +You should see: +``` +0x1000024a: movs r0, #15 ; GPIO pin 15 +0x1000024c: movs r1, #1 ; up = true +0x1000024e: movs r2, #0 ; down = false +0x10000250: bl 0x100002dc ; gpio_set_pulls +``` + +##### Step 3: Disassemble gpio_set_pulls + +Now examine the function itself: + +```gdb +(gdb) x/30i 0x100002dc +``` + +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 *0x100002dc +(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 diff --git a/WEEK06/WEEK06-03.md b/WEEK06/WEEK06-03.md new file mode 100644 index 0000000..a787823 --- /dev/null +++ b/WEEK06/WEEK06-03.md @@ -0,0 +1,181 @@ +# 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 + +### 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:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0014_static-variables.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +##### Step 2: Locate the Increment Instruction + +From the tutorial, we know the static variable operations are around address `0x10000260`. Disassemble the loop body: + +```gdb +(gdb) x/20i 0x10000260 +``` + +Look for this sequence: + +``` +ldrb r3, [r4, #0x0] ; Load static_fav_num from RAM +adds r3, #0x1 ; Increment by 1 ← THIS IS OUR TARGET +strb r3, [r4, #0x0] ; Store back to RAM +``` + +Note the exact address of the `adds r3, #0x1` instruction. + +##### Step 3: Examine the Instruction Encoding + +Look at the raw bytes of the instruction: + +```gdb +(gdb) x/2bx +``` + +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 +(gdb) b * +(gdb) c +``` + +When the breakpoint hits: + +```gdb +(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. Open `0x0014_static-variables.bin` in HxD +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` + +> 🔍 **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 + +```bash +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)? diff --git a/WEEK06/WEEK06-04.md b/WEEK06/WEEK06-04.md new file mode 100644 index 0000000..56ce77b --- /dev/null +++ b/WEEK06/WEEK06-04.md @@ -0,0 +1,188 @@ +# 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 + +### Exercise 4: Invert the Button Logic with XOR + +#### Objective +Find the `eor r3, r3, #0x1` instruction that implements the ternary operator's button inversion, patch it to `eor r3, r3, #0x0` using a hex editor to reverse the LED behavior, and verify that the LED is now ON when the button is pressed and OFF when released — the opposite of the original behavior. + +#### Prerequisites +- Completed Week 6 tutorial (all 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 with button on GP15 and LED on GP16 + +#### Task Description +The original program uses `gpio_put(LED_GPIO, !gpio_get(BUTTON_GPIO))` which the compiler implements as an XOR (`eor r3, r3, #0x1`) to invert the button state. With the pull-up resistor, button released = HIGH, so `HIGH XOR 1 = 0` (LED off). You will patch the XOR operand from `#0x1` to `#0x0`, which effectively removes the inversion: `HIGH XOR 0 = 1` (LED on when released). This exercise demonstrates how a single-byte binary patch can completely reverse hardware behavior. + +#### Step-by-Step Instructions + +##### Step 1: Start the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0014_static-variables.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +##### Step 2: Locate the GPIO Logic + +From the tutorial, the GPIO input/output logic is near address `0x10000274`. Disassemble: + +```gdb +(gdb) x/10i 0x10000274 +``` + +Look for this sequence: + +``` +mov.w r1, #0xd0000000 ; SIO base address +ldr r3, [r1, #offset] ; Read GPIO input register +ubfx r3, r3, #0xf, #0x1 ; Extract bit 15 (button state) +eor r3, r3, #0x1 ; XOR with 1 — INVERT ← OUR TARGET +mcrr p0, 0x4, r2, r3, cr0 ; Write to GPIO output +``` + +Note the exact address of the `eor r3, r3, #0x1` instruction. + +##### Step 3: Understand the Current Logic + +Trace the logic with the pull-up resistor: + +| Button State | GPIO 15 Input | After UBFX | After EOR #1 | LED (GPIO 16) | +| ------------ | ------------- | ---------- | ------------ | -------------- | +| Released | 1 (HIGH) | 1 | 0 | OFF | +| Pressed | 0 (LOW) | 0 | 1 | ON | + +The `eor #0x1` flips the bit, implementing the `!` (NOT) from the C code. + +##### Step 4: Verify with GDB + +Set a breakpoint at the `eor` instruction: + +```gdb +(gdb) b * +(gdb) c +``` + +When it hits, check what value is about to be XORed: + +```gdb +(gdb) info registers r3 +``` + +- If button is **released**: `r3 = 1` → after EOR: `r3 = 0` +- If button is **pressed**: `r3 = 0` → after EOR: `r3 = 1` + +##### Step 5: Test the Patch in GDB + +Modify the EOR operand in RAM to see the effect live: + +```gdb +(gdb) set $r3 = 0 +(gdb) si +(gdb) info registers r3 +``` + +Or skip the EOR entirely by advancing the PC past it, then observe the LED behavior. + +##### Step 6: Examine the Instruction Encoding + +Look at the raw bytes: + +```gdb +(gdb) x/4bx +``` + +The `eor` instruction in Thumb-2 (32-bit encoding) will contain the immediate value `0x01`. Study the bytes carefully — the immediate operand is encoded within the instruction word. + +##### Step 7: Patch with the Hex Editor + +1. Open `0x0014_static-variables.bin` in HxD +2. Calculate the file offset: `address - 0x10000000` +3. Press **Ctrl+G** and enter the offset +4. Locate the byte that encodes the `#0x1` immediate value +5. Change `01` to `00` +6. Verify the surrounding bytes are unchanged +7. Click **File** → **Save As** → `0x0014_static-variables-h.bin` + +> 🔍 **Thumb-2 encoding note:** The `eor` instruction is 4 bytes (32-bit Thumb-2). The immediate value may not be in the most obvious position — it is typically encoded in bit fields spread across the instruction. Look for the `01` byte within the 4-byte sequence. + +##### Step 8: Predict the New Behavior + +After patching, the logic changes: + +| Button State | GPIO 15 Input | After UBFX | After EOR #0 | LED (GPIO 16) | +| ------------ | ------------- | ---------- | ------------ | -------------- | +| Released | 1 (HIGH) | 1 | 1 | **ON** | +| Pressed | 0 (LOW) | 0 | 0 | **OFF** | + +The LED behavior is now **inverted** from the original! + +##### Step 9: Convert to UF2 and Flash + +```bash +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 + +##### Step 10: Verify the Hack + +Test the button: +- **Button NOT pressed**: LED should now be **ON** (was OFF before patching) +- **Button PRESSED**: LED should now be **OFF** (was ON before patching) + +The LED behavior is completely reversed by changing a single byte! + +#### Expected Output + +After completing this exercise, you should be able to: +- Locate XOR / EOR instructions in disassembled GPIO logic +- Understand how XOR implements logical NOT for single-bit values +- Patch a Thumb-2 encoded immediate operand +- Predict hardware behavior changes from binary patches + +#### Questions for Reflection + +###### Question 1: Why does XOR with 1 act as a NOT for single-bit values? Write out the truth table for `x XOR 1` and `x XOR 0` where x is 0 or 1. + +###### Question 2: Instead of changing `eor r3, r3, #0x1` to `eor r3, r3, #0x0`, could you achieve the same result by NOPing (removing) the instruction entirely? What bytes encode a NOP in Thumb? + +###### Question 3: The pull-up resistor means "pressed = LOW." If you removed the pull-up (changed `gpio_pull_up` to no pull), would the button still work? Why or why not? + +###### Question 4: The `ubfx r3, r3, #0xf, #0x1` instruction extracts bit 15. If you changed `#0xf` to `#0x10` (bit 16), what GPIO pin would you be reading? What value would you get if nothing is connected to that pin? + +#### Tips and Hints +- `eor r3, r3, #0x1` is a 32-bit Thumb-2 instruction (4 bytes), not a 16-bit Thumb instruction +- A Thumb NOP is `00 bf` (2 bytes) — you would need two NOPs to replace a 4-byte instruction +- Use GDB `x/1tw` to view a word in binary format, making bit manipulation easier to see +- The SIO base address `0xd0000000` provides single-cycle access to GPIO — it's separate from the IO_BANK0 registers at `0x40028000` + +#### Next Steps +- Review all four exercises and verify you can patch any part of the binary: data values, arithmetic operations, and logic operations +- Try combining multiple hacks in a single binary: change the initial value, speed up the overflow, AND invert the button logic +- Compare your patched binary with the original using `fc /b original.bin patched.bin` in the command prompt to see all changed bytes diff --git a/WEEK06/WEEK06.md b/WEEK06/WEEK06.md new file mode 100644 index 0000000..23e598d --- /dev/null +++ b/WEEK06/WEEK06.md @@ -0,0 +1,957 @@ +# Week 6: Static Variables in Embedded Systems: Debugging and Hacking Static Variables w/ GPIO Input Basics + +## 🎯 What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand the difference between regular (automatic) variables and static variables +- Know where different types of variables are stored (stack vs static storage) +- Configure GPIO pins as inputs and use internal pull-up resistors +- Read button states using `gpio_get()` and control LEDs based on input +- Use GDB to examine how the compiler handles static vs automatic variables +- Identify compiler optimizations by stepping through assembly +- Hack variable values and invert GPIO input/output logic using a hex editor +- Convert patched binaries to UF2 format for flashing + +--- + +## 📚 Part 1: Understanding Static Variables + +### What is a Static Variable? + +A **static variable** is a special kind of variable that "remembers" its value between function calls or loop iterations. Unlike regular variables that get created and destroyed each time, static variables **persist** for the entire lifetime of your program. + +Think of it like this: +- **Regular variable:** Like writing on a whiteboard that gets erased after each class +- **Static variable:** Like writing in a notebook that you keep forever + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Regular vs Static Variables │ +│ │ +│ REGULAR (automatic): │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ Loop 1: Create → Set to 42 → Increment to 43 → Destroy │ │ +│ │ Loop 2: Create → Set to 42 → Increment to 43 → Destroy │ │ +│ │ Loop 3: Create → Set to 42 → Increment to 43 → Destroy │ │ +│ │ Result: Always appears as 42! │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ STATIC: │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ Loop 1: Already exists → Read 42 → Increment → Store 43 │ │ +│ │ Loop 2: Already exists → Read 43 → Increment → Store 44 │ │ +│ │ Loop 3: Already exists → Read 44 → Increment → Store 45 │ │ +│ │ Result: Keeps incrementing! │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### The `static` Keyword + +In C, you declare a static variable by adding the `static` keyword: + +```c +uint8_t regular_fav_num = 42; // Regular - recreated each time +static uint8_t static_fav_num = 42; // Static - persists forever +``` + +### Where Do Variables Live in Memory? + +Different types of variables are stored in different memory locations: + +| Variable Type | Storage Location | Lifetime | Example | +| ----------------- | ---------------- | ------------------------- | ------------------------------------ | +| Automatic (local) | Stack | Until function/block ends | `uint8_t x = 5;` | +| Static | Static Storage | Entire program lifetime | `static uint8_t x = 5;` | +| Global | Static Storage | Entire program lifetime | `uint8_t x = 5;` (outside functions) | +| Dynamic (heap) | Heap | Until `free()` is called | `malloc(sizeof(int))` | + +### Stack vs Static Storage vs Heap + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Memory Layout │ +│ │ +│ ┌───────────────────┐ High Address (0x20082000) │ +│ │ STACK │ ← Automatic/local variables │ +│ │ (grows down) │ Created/destroyed per function │ +│ ├───────────────────┤ │ +│ │ │ │ +│ │ (free space) │ │ +│ │ │ │ +│ ├───────────────────┤ │ +│ │ HEAP │ ← Dynamic allocation (malloc/free) │ +│ │ (grows up) │ │ +│ ├───────────────────┤ │ +│ │ .bss section │ ← Uninitialized static/global vars │ +│ ├───────────────────┤ │ +│ │ .data section │ ← Initialized static/global vars │ +│ └───────────────────┘ Low Address (0x20000000) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**Key Point:** Static variables are NOT on the heap! They live in a fixed location in the `.data` section (if initialized) or `.bss` section (if uninitialized). This is different from heap memory which is dynamically allocated at runtime. + +### What Happens with Overflow? + +Since `static_fav_num` is a `uint8_t` (unsigned 8-bit), it can only hold values 0-255. What happens when it reaches 255 and we add 1? + +``` +255 + 1 = 256... but that doesn't fit in 8 bits! +Binary: 11111111 + 1 = 100000000 (9 bits) +The 9th bit is lost, so we get: 00000000 = 0 +``` + +This is called **overflow** or **wrap-around**. The value "wraps" back to 0 and starts counting again! + +--- + +## 📚 Part 2: Understanding GPIO Inputs + +### Input vs Output + +So far, we've used GPIO pins as **outputs** to control LEDs. Now we'll learn to use them as **inputs** to read button states! + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ GPIO Direction │ +│ │ +│ OUTPUT (what we've done before): │ +│ ┌─────────┐ │ +│ │ Pico │ ───────► LED │ +│ │ GPIO 16 │ (We control the LED) │ +│ └─────────┘ │ +│ │ +│ INPUT (new this week): │ +│ ┌─────────┐ │ +│ │ Pico │ ◄─────── Button │ +│ │ GPIO 15 │ (We read the button state) │ +│ └─────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### The Floating Input Problem + +When a GPIO pin is set as an input but nothing is connected, it's called a **floating input**. The voltage on the pin is undefined and can randomly read as HIGH (1) or LOW (0) due to electrical noise. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Floating Input = Random Values! │ +│ │ +│ GPIO Pin (no connection): │ +│ Reading 1: HIGH │ +│ Reading 2: LOW │ +│ Reading 3: HIGH │ +│ Reading 4: HIGH │ +│ Reading 5: LOW │ +│ (Completely unpredictable!) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Pull-Up and Pull-Down Resistors + +To solve the floating input problem, we use **pull resistors**: + +| Resistor Type | Default State | When Button Pressed | +| ------------- | ------------- | ------------------- | +| **Pull-Up** | HIGH (1) | LOW (0) | +| **Pull-Down** | LOW (0) | HIGH (1) | + +The Pico 2 has **internal** pull resistors that you can enable with software - no external components needed! + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Pull-Up Resistor (what we're using) │ +│ │ +│ 3.3V │ +│ │ │ +│ ┴ (internal pull-up resistor) │ +│ │ │ +│ ├──────► GPIO 15 (reads HIGH normally) │ +│ │ │ +│ ┌─┴─┐ │ +│ │BTN│ ← Button connects GPIO to GND when pressed │ +│ └─┬─┘ │ +│ │ │ +│ GND │ +│ │ +│ Button NOT pressed: GPIO reads 1 (HIGH) │ +│ Button PRESSED: GPIO reads 0 (LOW) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### GPIO Input Functions + +| Function | Purpose | +| ---------------------------- | --------------------------------------- | +| `gpio_init(pin)` | Initialize a GPIO pin for use | +| `gpio_set_dir(pin, GPIO_IN)` | Set pin as INPUT | +| `gpio_pull_up(pin)` | Enable internal pull-up resistor | +| `gpio_pull_down(pin)` | Enable internal pull-down resistor | +| `gpio_get(pin)` | Read the current state (returns 0 or 1) | + +### The Ternary Operator + +The code uses a **ternary operator** to control the LED based on button state: + +```c +gpio_put(LED_GPIO, pressed ? 0 : 1); +``` + +This is a compact if-else statement: +- If `pressed` is **true (1)**: output `0` (LED OFF... wait, that seems backwards!) +- If `pressed` is **false (0)**: output `1` (LED ON) + +**Why is it inverted?** Because of the pull-up resistor! +- Button **released** → GPIO reads `1` → `pressed = 1` → output `0` → LED OFF +- Button **pressed** → GPIO reads `0` → `pressed = 0` → output `1` → LED ON + +A clearer way to write this: +```c +gpio_put(LED_GPIO, !gpio_get(BUTTON_GPIO)); +``` + +--- + +## 📚 Part 3: Understanding Compiler Optimizations + +### Why Does Code Disappear? + +When you compile code, the compiler tries to make it faster and smaller. This is called **optimization**. Sometimes the compiler removes code that it thinks has no effect! + +**Example from our code:** +```c +while (true) { + uint8_t regular_fav_num = 42; // Created + regular_fav_num++; // Incremented to 43 + // But then it's destroyed and recreated as 42 next loop! +} +``` + +The compiler sees that incrementing `regular_fav_num` has no lasting effect (because it's recreated as 42 each loop), so it may **optimize away** the increment operation entirely! + +### Function Inlining + +Sometimes the compiler **inlines** functions, meaning it replaces a function call with the function's code directly. + +**Original code:** +```c +gpio_pull_up(BUTTON_GPIO); +``` + +**What the compiler might do:** +```c +// Instead of calling gpio_pull_up, it calls the underlying function: +gpio_set_pulls(BUTTON_GPIO, true, false); +``` + +This is why when you look for `gpio_pull_up` in the binary, you might find `gpio_set_pulls` instead! + +--- + +## 📚 Part 4: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. OpenOCD installed and configured +4. GDB (`arm-none-eabi-gdb`) installed +5. Python installed (for UF2 conversion) +6. A serial monitor (PuTTY, minicom, or screen) +7. A push button connected to GPIO 15 +8. An LED connected to GPIO 16 (or use the breadboard LED) +9. A hex editor (HxD, ImHex, or similar) +10. The sample project: `0x0014_static-variables` + +### Hardware Setup + +Connect your button like this: +- One side of button → GPIO 15 +- Other side of button → GND + +The internal pull-up resistor provides the 3.3V connection, so you only need to connect to GND! + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Breadboard Wiring │ +│ │ +│ Pico 2 │ +│ ┌──────────┐ │ +│ │ │ │ +│ │ GPIO 15 │────────┐ │ +│ │ │ │ │ +│ │ GPIO 16 │────────┼───► LED (with resistor to GND) │ +│ │ │ │ │ +│ │ GND │────────┼───┐ │ +│ │ │ │ │ │ +│ └──────────┘ ┌─┴─┐ │ │ +│ │BTN│─┘ │ +│ └───┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Project Structure + +``` +Embedded-Hacking/ +├── 0x0014_static-variables/ +│ ├── build/ +│ │ ├── 0x0014_static-variables.uf2 +│ │ └── 0x0014_static-variables.bin +│ └── 0x0014_static-variables.c +└── uf2conv.py +``` + +--- + +## 🔬 Part 5: Hands-On Tutorial - Static Variables and GPIO Input + +### Step 1: Review the Source Code + +Let's examine the static variables code: + +**File: `0x0014_static-variables.c`** + +```c +#include +#include "pico/stdlib.h" + +int main(void) { + stdio_init_all(); + + const uint BUTTON_GPIO = 15; + const uint LED_GPIO = 16; + bool pressed = 0; + + gpio_init(BUTTON_GPIO); + gpio_set_dir(BUTTON_GPIO, GPIO_IN); + gpio_pull_up(BUTTON_GPIO); + + gpio_init(LED_GPIO); + gpio_set_dir(LED_GPIO, GPIO_OUT); + + while (true) { + uint8_t regular_fav_num = 42; + static uint8_t static_fav_num = 42; + + printf("regular_fav_num: %d\r\n", regular_fav_num); + printf("static_fav_num: %d\r\n", static_fav_num); + + regular_fav_num++; + static_fav_num++; + + pressed = gpio_get(BUTTON_GPIO); + gpio_put(LED_GPIO, pressed ? 0 : 1); + } +} +``` + +**What this code does:** + +1. **Line 6-8:** Defines constants for button (GPIO 15) and LED (GPIO 16) pins +2. **Line 10-12:** Sets up GPIO 15 as input with internal pull-up resistor +3. **Line 14-15:** Sets up GPIO 16 as output for the LED +4. **Line 18-19:** Creates two variables: + - `regular_fav_num` - a normal local variable (recreated each loop) + - `static_fav_num` - a static variable (persists across loops) +5. **Line 21-22:** Prints both values to the serial terminal +6. **Line 24-25:** Increments both values +7. **Line 27-28:** Reads button and controls LED accordingly + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0014_static-variables.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Open Your Serial Monitor + +Open PuTTY, minicom, or screen and connect to your Pico's serial port. + +**You should see output like this:** + +``` +regular_fav_num: 42 +static_fav_num: 42 +regular_fav_num: 42 +static_fav_num: 43 +regular_fav_num: 42 +static_fav_num: 44 +regular_fav_num: 42 +static_fav_num: 45 +... +``` + +**Notice the difference:** +- `regular_fav_num` stays at 42 every time (it's recreated each loop) +- `static_fav_num` increases each time (it persists and remembers its value) + +### Step 4: Test the Button + +Now test the button behavior: +- **Button NOT pressed:** LED should be ON (because of the inverted logic) +- **Button PRESSED:** LED should turn OFF + +Wait... that seems backwards from what you'd expect! That's because of the pull-up resistor and the ternary operator. We'll hack this later to make it more intuitive! + +### Step 5: Watch for Overflow + +Keep the program running and watch `static_fav_num`. After 255, you'll see: + +``` +static_fav_num: 254 +static_fav_num: 255 +static_fav_num: 0 ← Wrapped around! +static_fav_num: 1 +static_fav_num: 2 +... +``` + +This demonstrates unsigned integer overflow! + +--- + +## 🔬 Part 6: Debugging with GDB (Dynamic Analysis) + +> 🔄 **REVIEW:** This setup is identical to previous weeks. If you need a refresher on OpenOCD and GDB connection, refer back to Week 3 Part 6. + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0014_static-variables.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +### Step 6: Examine Main Function + +Let's examine the main function at its entry point. First, disassemble from the start: + +``` +x/50i 0x10000234 +``` + +You should see output like: + +``` +0x10000234: push {r4, r5, r6, r7, lr} +0x10000236: sub sp, #12 +0x10000238: bl 0x10003014 ; stdio_init_all +0x1000023c: movs r0, #15 ; BUTTON_GPIO = 15 +0x1000023e: bl 0x100002b4 ; gpio_init +0x10000242: movs r0, #15 +0x10000244: movs r1, #0 ; GPIO_IN +0x10000246: bl 0x100002c8 ; gpio_set_dir +0x1000024a: movs r0, #15 +0x1000024c: movs r1, #1 ; up = true +0x1000024e: movs r2, #0 ; down = false +0x10000250: bl 0x100002dc ; gpio_set_pulls (inlined gpio_pull_up) +0x10000254: movs r0, #16 ; LED_GPIO = 16 +0x10000256: bl 0x100002b4 ; gpio_init +0x1000025a: movs r0, #16 +0x1000025c: movs r1, #1 ; GPIO_OUT +0x1000025e: bl 0x100002c8 ; gpio_set_dir +... +``` + +### Step 7: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +GDB responds: +``` +Breakpoint 1 at 0x10000234 +Continuing. + +Breakpoint 1, 0x10000234 in ?? () +``` + +### Step 8: Examine the Static Variable Location + +Static variables live at fixed RAM addresses. Let's find where `static_fav_num` is stored: + +``` +x/20i 0x10000260 +``` + +Look for instructions that load/store to addresses in the `0x20000xxx` range - that's where static variables live in RAM. + +### Step 9: Step Through the Loop + +``` +si +``` + +Use `si` (step instruction) to execute one instruction at a time and watch how the static variable gets loaded, incremented, and stored back. + +### Step 10: Examine Register Values + +``` +i r +``` + +This shows all register values. Pay attention to: +- `r0-r3` - Used for function arguments and return values +- `sp` - Stack pointer +- `pc` - Program counter (current instruction) + +### Step 11: Watch the Static Variable Change + +Once you identify the static variable address (e.g., `0x200005a8`), you can watch it: + +``` +x/1db 0x200005a8 +``` + +This displays 1 byte in decimal format. Step through a few loop iterations and re-examine to see it increment. + +### Step 12: Examine GPIO State + +Read the GPIO input register to see button state: + +``` +x/1wx 0xd0000004 +``` + +The SIO GPIO input register at `0xd0000004` shows the current state of all GPIO pins. Bit 15 corresponds to our button on GPIO 15. + +--- + +## 🔬 Part 7: Understanding the Assembly + +Now that we've explored the binary in GDB, let's make sense of the key patterns. + +### Step 13: Analyze the Regular Variable + +In GDB, examine the code around the first `printf` call: + +```gdb +(gdb) x/5i 0x1000028c +``` + +Look for this instruction: + +``` +0x1000028e: movs r1, #0x2a +``` + +This loads the value `0x2a` (42 in decimal) directly into register `r1` for the first `printf` call. + +**Key insight:** The compiler **optimized away** the `regular_fav_num` variable entirely! Since it's always 42 when printed, the compiler just uses the constant `42` directly. The `regular_fav_num++` after the print is also removed because it has no observable effect. + +### Step 14: Analyze the Static Variable + +Examine the static variable operations: + +```gdb +(gdb) x/10i 0x10000260 +``` + +Look for references to addresses in the `0x200005xx` range: + +``` +ldrb r1,[r4,#0x0] ; Load static_fav_num from RAM +... +ldrb r3,[r4,#0x0] ; Load it again +adds r3,#0x1 ; Increment by 1 +strb r3,[r4,#0x0] ; Store it back to RAM +``` + +**Key insight:** The static variable lives at a **fixed RAM address** (`0x200005a8`). It's loaded, incremented, and stored back — unlike the regular variable which was optimized away! + +Verify the static variable value: + +```gdb +(gdb) x/1db 0x200005a8 +``` + +### Step 15: Analyze the GPIO Logic + +Examine the GPIO input/output code: + +```gdb +(gdb) x/10i 0x10000274 +``` + +Look for this sequence: + +``` +mov.w r1,#0xd0000000 ; SIO base address +ldr r3,[r1,#offset] ; Read GPIO input register +ubfx r3,r3,#0xf,#0x1 ; Extract bit 15 (button state) +eor r3,r3,#0x1 ; Invert the value (the ternary operator!) +mcrr p0,0x4,r2,r3,cr0 ; Write to GPIO output +``` + +**Breaking this down:** + +| Instruction | Purpose | +| ----------------------- | ------------------------------------------- | +| `mov.w r1,#0xd0000000` | Load SIO (Single-cycle I/O) base address | +| `ldr r3,[r1,#offset]` | Read GPIO input state | +| `ubfx r3,r3,#0xf,#0x1` | Extract bit 15 (GPIO 15 = button) | +| `eor r3,r3,#0x1` | XOR with 1 to invert (implements `? 0 : 1`) | +| `mcrr p0,0x4,...` | Write result to GPIO output (LED) | + +### Step 16: Find the Infinite Loop + +At the end of the function, look for: + +``` +b 0x10000264 +``` + +This is an **unconditional branch** back to the start of the loop — this is the `while (true)` in our code! + +--- + +## 🔬 Part 8: Hacking the Binary with a Hex Editor + +Now for the fun part — we'll patch the `.bin` file directly using a hex editor! + +> 💡 **Why a hex editor?** GDB can modify values in RAM at runtime, but those changes are lost when the device reboots. To make **permanent** changes, we edit the `.bin` file on disk and re-flash it. + +### Step 17: Open the Binary in a Hex Editor + +1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.) +2. Click **File** → **Open** +3. Navigate to `0x0014_static-variables/build/` +4. Open `0x0014_static-variables.bin` + +### Step 18: Calculate the File Offset + +The binary is loaded at base address `0x10000000`. To find the file offset of any address: + +``` +file_offset = address - 0x10000000 +``` + +For example: +- Address `0x1000028e` → file offset `0x28e` (654 in decimal) +- Address `0x10000274` → file offset `0x274` (628 in decimal) + +### Step 19: Hack #1 — Change regular_fav_num from 42 to 43 + +From GDB, we know the instruction at `0x1000028e` is: + +``` +movs r1, #0x2a → bytes: 2a 21 +``` + +To change the value from 42 (`0x2a`) to 43 (`0x2b`): + +1. In HxD, press **Ctrl+G** (Go to offset) +2. Enter offset: `28E` +3. You should see the byte `2A` at this position +4. Change `2A` to `2B` +5. The instruction is now `movs r1, #0x2b` (43 in decimal) + +> 🔍 **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a`. + +### Step 20: Hack #2 — Invert the Button Logic + +From GDB, we found the `eor r3, r3, #0x1` instruction that inverts the button value. We need to find where this instruction is in the binary. + +In GDB, examine the exact address and bytes: + +```gdb +(gdb) x/2bx +``` + +The `eor` instruction with `#0x1` needs to become `eor` with `#0x0`: + +1. Calculate the file offset of the EOR instruction +2. In HxD, go to that offset +3. Find the byte encoding the immediate value `0x01` +4. Change it to `0x00` + +Now the logic is: +- Button released (input = 1): `1 XOR 0 = 1` → LED **ON** +- Button pressed (input = 0): `0 XOR 0 = 0` → LED **OFF** + +This is the **opposite** of the original behavior! + +### Step 21: Save the Patched Binary + +1. Click **File** → **Save As** +2. Save as `0x0014_static-variables-h.bin` in the build directory +3. Close the hex editor + +--- + +## 🔬 Part 9: Converting and Flashing the Hacked Binary + +### Step 22: Convert to UF2 Format + +Open a terminal and navigate to your project directory: + +```bash +cd Embedded-Hacking/0x0014_static-variables +``` + +Run the conversion command: + +```bash +python ../uf2conv.py build/0x0014_static-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build/hacked.uf2 +``` + +**What this command means:** +- `uf2conv.py` = the conversion script +- `--base 0x10000000` = the XIP base address where code runs from +- `--family 0xe48bff59` = the RP2350 family ID +- `--output build/hacked.uf2` = the output filename + +### Step 23: Flash the Hacked Binary + +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 24: Verify the Hacks + +**Check the serial output:** +``` +regular_fav_num: 43 ← Changed from 42! +static_fav_num: 42 +regular_fav_num: 43 +static_fav_num: 43 +... +``` + +**Check the LED behavior:** +- LED should now be **ON by default** (when button is NOT pressed) +- LED should turn **OFF** when you press the button + +🎉 **BOOM! We successfully:** +1. Changed the printed value from 42 to 43 +2. Inverted the LED/button logic + +--- + +## 📊 Part 10: Summary and Review + +### What We Accomplished + +1. **Learned about static variables** - How they persist across function calls and loop iterations +2. **Understood memory layout** - Stack vs static storage vs heap +3. **Configured GPIO inputs** - Using pull-up resistors and reading button states +4. **Analyzed compiled code in GDB** - Saw how the compiler optimizes code +5. **Discovered function inlining** - `gpio_pull_up` became `gpio_set_pulls` +6. **Hacked variable values** - Changed 42 to 43 using a hex editor +7. **Inverted GPIO logic** - Made LED behavior opposite + +### Static vs Automatic Variables + +| Aspect | Automatic (Regular) | Static | +| ------------------ | ------------------------ | --------------------------- | +| **Storage** | Stack | Static storage (.data/.bss) | +| **Lifetime** | Block/function scope | Entire program | +| **Initialization** | Every time block entered | Once at program start | +| **Persistence** | Lost when scope exits | Retained between calls | +| **Compiler view** | May be optimized away | Always has memory location | + +### GPIO Input Configuration + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ GPIO Input Setup Steps │ +│ │ +│ 1. gpio_init(pin) - Initialize the pin │ +│ 2. gpio_set_dir(pin, GPIO_IN) - Set as input │ +│ 3. gpio_pull_up(pin) - Enable pull-up │ +│ OR gpio_pull_down(pin) - OR enable pull-down │ +│ 4. gpio_get(pin) - Read the state │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### The Binary Hacking Workflow + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 1. Analyze the binary with GDB │ +│ - Disassemble functions with x/Ni │ +│ - Identify key instructions and addresses │ +├─────────────────────────────────────────────────────────────────┤ +│ 2. Understand compiler optimizations │ +│ - Some functions get inlined (gpio_pull_up → gpio_set_pulls)│ +│ - Some variables are optimized away │ +├─────────────────────────────────────────────────────────────────┤ +│ 3. Calculate file offsets │ +│ - file_offset = address - 0x10000000 │ +├─────────────────────────────────────────────────────────────────┤ +│ 4. Patch the .bin file with a hex editor │ +│ - Open the .bin file in HxD / ImHex │ +│ - Go to the calculated offset │ +│ - Change the target byte(s) │ +├─────────────────────────────────────────────────────────────────┤ +│ 5. Convert to UF2 │ +│ python uf2conv.py file.bin --base 0x10000000 │ +│ --family 0xe48bff59 --output hacked.uf2 │ +├─────────────────────────────────────────────────────────────────┤ +│ 6. Flash and verify │ +│ - Hold BOOTSEL, plug in, drag UF2 │ +│ - Check serial output and button/LED behavior │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Key Memory Addresses + +| Address | Description | +| ------------ | ----------------------------------- | +| `0x10000234` | Typical main() entry point | +| `0x10003014` | stdio_init_all() function | +| `0x200005a8` | Static variable storage (example) | +| `0xd0000000` | SIO (Single-cycle I/O) base address | + +--- + +## ✅ Practice Exercises + +### Exercise 1: Change Static Variable Initial Value +The static variable starts at 42. Hack the binary to make it start at 100 instead. + +**Hint:** Find where `DAT_200005a8` is initialized in the .data section. + +### Exercise 2: Make the LED Blink +Instead of responding to button presses, hack the binary to make the LED blink continuously. + +**Hint:** You'll need to change the GPIO output logic to toggle instead of following button state. + +### Exercise 3: Reverse Engineer gpio_set_pulls +Using GDB, disassemble the `gpio_set_pulls` function and figure out what registers it writes to. + +**Hint:** Look for writes to addresses around `0x40038000` (PADS_BANK0). + +### Exercise 4: Add a Second Static Variable +If you had two static variables, where would they be stored in memory? Would they be next to each other? + +**Hint:** Static variables in the same compilation unit are typically placed consecutively in the .data section. + +### Exercise 5: Overflow Faster +The static variable overflows after 255 iterations. Can you hack it to overflow sooner? + +**Hint:** Change the increment from `+1` to `+10` by modifying the `adds r3,#0x1` instruction. + +--- + +## 🎓 Key Takeaways + +1. **Static variables persist** - They keep their value between function calls and loop iterations. + +2. **Static storage ≠ heap** - Static variables are in a fixed location, not dynamically allocated. + +3. **Compilers optimize aggressively** - Regular variables may be optimized away if the compiler sees no effect. + +4. **Function inlining is common** - `gpio_pull_up` becomes `gpio_set_pulls` in the binary. + +5. **Pull-up resistors invert logic** - Button pressed = LOW, button released = HIGH. + +6. **XOR is useful for inverting** - `eor r3,r3,#0x1` flips a bit between 0 and 1. + +7. **Static variables have fixed addresses** - You can find them in the .data section at known RAM addresses. + +8. **Overflow wraps around** - A `uint8_t` at 255 becomes 0 when incremented. + +9. **UBFX extracts bits** - Used to read a single GPIO pin from a register. + +10. **Binary patching is powerful** - Change values and logic without source code! + +--- + +## 📖 Glossary + +| Term | Definition | +| --------------------- | ---------------------------------------------------------------- | +| **Automatic** | Variable that's created and destroyed automatically (local vars) | +| **eor/XOR** | Exclusive OR - flips bits where operands differ | +| **Floating Input** | GPIO input with undefined voltage (reads random values) | +| **Function Inlining** | Compiler replaces function call with the function's code | +| **gpio_get** | Function to read the current state of a GPIO pin | +| **Heap** | Memory area for dynamic allocation (malloc/free) | +| **Overflow** | When a value exceeds its type's maximum and wraps around | +| **Pull-Down** | Resistor that holds a pin LOW when nothing drives it | +| **Pull-Up** | Resistor that holds a pin HIGH when nothing drives it | +| **SIO** | Single-cycle I/O - fast GPIO access on RP2350 | +| **Stack** | Memory area for local variables and function call frames | +| **Static Storage** | Fixed memory area for static and global variables | +| **Static Variable** | Variable declared with `static` that persists across calls | +| **Ternary Operator** | `condition ? value_if_true : value_if_false` | +| **UBFX** | Unsigned Bit Field Extract - extracts bits from a register | +| **Varargs** | Variable arguments - functions that take unlimited parameters | + +--- + +## 🔗 Additional Resources + +### GPIO Input Reference + +| Function | Purpose | +| ----------------------------- | -------------------------- | +| `gpio_init(pin)` | Initialize GPIO pin | +| `gpio_set_dir(pin, GPIO_IN)` | Set pin as input | +| `gpio_set_dir(pin, GPIO_OUT)` | Set pin as output | +| `gpio_pull_up(pin)` | Enable internal pull-up | +| `gpio_pull_down(pin)` | Enable internal pull-down | +| `gpio_disable_pulls(pin)` | Disable all pull resistors | +| `gpio_get(pin)` | Read pin state (0 or 1) | +| `gpio_put(pin, value)` | Set pin output (0 or 1) | + +### Key Assembly Instructions + +| Instruction | Description | +| ----------------------- | -------------------------------------------- | +| `movs rN, #imm` | Move immediate value to register | +| `ldrb rN, [rM, #off]` | Load byte from memory | +| `strb rN, [rM, #off]` | Store byte to memory | +| `adds rN, #imm` | Add immediate value to register | +| `eor rN, rM, #imm` | Exclusive OR (XOR) with immediate | +| `ubfx rN, rM, #lsb, #w` | Extract unsigned bit field | +| `mcrr p0, ...` | Move to coprocessor (GPIO control on RP2350) | +| `b LABEL` | Unconditional branch (jump) | + +### Memory Map Quick Reference + +| Address Range | Description | +| --------------------- | ------------------------------ | +| `0x10000000` | XIP Flash (code execution) | +| `0x20000000-200005xx` | SRAM (.data section) | +| `0x20082000` | Stack top (initial SP) | +| `0x40038000` | PADS_BANK0 (pad configuration) | +| `0xd0000000` | SIO (single-cycle I/O) | + +--- + +**Remember:** Static variables are your friends when you need to remember values across function calls. But they also make your program's behavior more complex to analyze - which is exactly why we practice reverse engineering! + +Happy hacking! 🔧 diff --git a/WEEK07/WEEK07-01.md b/WEEK07/WEEK07-01.md new file mode 100644 index 0000000..c2cb7b9 --- /dev/null +++ b/WEEK07/WEEK07-01.md @@ -0,0 +1,204 @@ +# Embedded Systems Reverse Engineering +[Repository](https://github.com/mytechnotalent/Embedded-Hacking) + +## Week 7 +Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +### Exercise 1: Change Both LCD Lines + +#### Objective +Patch the LCD display strings in the `.bin` file to change "Reverse" to "Exploit" on line 1 and "Engineering" to "Hacking!!!!" on line 2, using GDB to locate the string addresses, a hex editor to perform the patches, and the Pico 2 hardware to verify the changes on the physical LCD. + +#### Prerequisites +- Completed Week 7 tutorial (GDB and hex editor sections) +- `0x0017_constants.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 with 1602 LCD connected via I²C + +#### Task Description +The LCD currently displays "Reverse" on line 1 and "Engineering" on line 2. You will find both string literals in flash memory using GDB, calculate their file offsets, and patch them to display custom text. The critical constraint is that replacement strings must be the **same length** as the originals (or shorter, padded with null bytes) — otherwise you'll corrupt adjacent data. + +#### Step-by-Step Instructions + +##### Step 1: Start the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0017_constants.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +##### Step 2: Locate the String Literals in Memory + +From the tutorial, we know the strings are in the `.rodata` section: + +```gdb +(gdb) x/s 0x10003ee8 +``` + +Output: +``` +0x10003ee8: "Reverse" +``` + +```gdb +(gdb) x/s 0x10003ef0 +``` + +Output: +``` +0x10003ef0: "Engineering" +``` + +##### Step 3: Examine the Raw Bytes + +Look at the hex encoding of both strings: + +```gdb +(gdb) x/8xb 0x10003ee8 +``` + +Output: +``` +0x10003ee8: 0x52 0x65 0x76 0x65 0x72 0x73 0x65 0x00 + R e v e r s e \0 +``` + +```gdb +(gdb) x/12xb 0x10003ef0 +``` + +Output: +``` +0x10003ef0: 0x45 0x6e 0x67 0x69 0x6e 0x65 0x65 0x72 + E n g i n e e r +0x10003ef8: 0x69 0x6e 0x67 0x00 + i n g \0 +``` + +##### Step 4: Plan Your Replacement Strings + +**Original strings and their lengths:** +- "Reverse" = 7 characters + null terminator = 8 bytes +- "Engineering" = 11 characters + null terminator = 12 bytes + +**Replacement strings (MUST be same length or shorter):** +- "Exploit" = 7 characters ✅ (same as "Reverse") +- "Hacking!!!!" = 11 characters ✅ (same as "Engineering") + +Build the ASCII hex for "Exploit": + +| Character | Hex | +| --------- | ------ | +| E | `0x45` | +| x | `0x78` | +| p | `0x70` | +| l | `0x6c` | +| o | `0x6f` | +| i | `0x69` | +| t | `0x74` | +| \0 | `0x00` | + +Build the ASCII hex for "Hacking!!!!": + +| Character | Hex | +| --------- | ------ | +| H | `0x48` | +| a | `0x61` | +| c | `0x63` | +| k | `0x6b` | +| i | `0x69` | +| n | `0x6e` | +| g | `0x67` | +| ! | `0x21` | +| ! | `0x21` | +| ! | `0x21` | +| ! | `0x21` | +| \0 | `0x00` | + +##### Step 5: Calculate File Offsets + +``` +file_offset = address - 0x10000000 +``` + +- "Reverse" at `0x10003ee8` → file offset `0x3EE8` +- "Engineering" at `0x10003ef0` → file offset `0x3EF0` + +##### Step 6: Patch String 1 — "Reverse" → "Exploit" + +1. Open `0x0017_constants.bin` in HxD +2. Press **Ctrl+G** and enter offset: `3EE8` +3. You should see: `52 65 76 65 72 73 65 00` ("Reverse\0") +4. Replace with: `45 78 70 6C 6F 69 74 00` ("Exploit\0") + +##### Step 7: Patch String 2 — "Engineering" → "Hacking!!!!" + +1. Press **Ctrl+G** and enter offset: `3EF0` +2. You should see: `45 6E 67 69 6E 65 65 72 69 6E 67 00` ("Engineering\0") +3. Replace with: `48 61 63 6B 69 6E 67 21 21 21 21 00` ("Hacking!!!!\0") + +##### Step 8: Save and Convert + +1. Click **File** → **Save As** → `0x0017_constants-h.bin` + +```bash +python ../uf2conv.py build/0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build/hacked.uf2 +``` + +##### Step 9: Flash and Verify + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive + +**Check the LCD:** +- Line 1 should now show: `Exploit` +- Line 2 should now show: `Hacking!!!!` + +#### Expected Output + +After completing this exercise, you should be able to: +- Locate string literals in flash memory using GDB +- Convert ASCII characters to hex bytes for patching +- Patch multiple strings in a single binary +- Understand the same-length constraint for string patching + +#### Questions for Reflection + +###### Question 1: Why must the replacement string be the same length (or shorter) as the original? What specific data would you corrupt if you used a longer string? + +###### Question 2: The two strings "Reverse" and "Engineering" are stored only 8 bytes apart (`0x3EE8` to `0x3EF0`). "Reverse" is 7 characters + null = 8 bytes — it perfectly fills the gap. What would happen if you patched "Reverse" with "Reversal" (8 characters + null = 9 bytes)? + +###### Question 3: If you wanted the LCD to display "Hello" on line 1 (5 characters instead of 7), what would you put in the remaining 2 bytes plus null? Write out the full 8-byte hex sequence. + +###### Question 4: Could you change the LCD to display nothing on line 1 by patching just one byte? Which byte and what value? + +#### Tips and Hints +- Use an ASCII table to convert characters: uppercase A-Z = `0x41`-`0x5A`, lowercase a-z = `0x61`-`0x7A` +- The null terminator `0x00` marks the end of the string — anything after it is ignored by `lcd_puts` +- If your replacement is shorter, pad with `0x00` bytes to fill the original length +- The 1602 LCD has 16 characters per line — you cannot display more than 16 characters per line regardless of string length + +#### Next Steps +- Proceed to Exercise 2 to explore finding all string literals in the binary +- Try displaying reversed text: can you make it show "gninnignE" and "esreveR"? +- Use GDB to verify your patches before flashing: `set {char[8]} 0x10003ee8 = "Exploit"` to test in RAM first diff --git a/WEEK07/WEEK07-02.md b/WEEK07/WEEK07-02.md new file mode 100644 index 0000000..83c0290 --- /dev/null +++ b/WEEK07/WEEK07-02.md @@ -0,0 +1,157 @@ +# Embedded Systems Reverse Engineering +[Repository](https://github.com/mytechnotalent/Embedded-Hacking) + +## Week 7 +Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +### Exercise 2: Find All String Literals in the Binary + +#### Objective +Systematically search through the `0x0017_constants` binary using GDB and a hex editor to locate every human-readable string literal, catalog their addresses, contents, and purposes, and gain experience identifying data structures in compiled binaries. + +#### Prerequisites +- Completed Week 7 tutorial (GDB section) +- `0x0017_constants.elf` and `0x0017_constants.bin` available in your build directory +- GDB (`arm-none-eabi-gdb`) and OpenOCD installed +- A hex editor (HxD, ImHex, or similar) + +#### Task Description +Compiled binaries contain string literals in the `.rodata` section — format strings for `printf`, LCD messages, library strings, and more. You will use two techniques to find them: (1) searching with GDB's `x/s` command to examine suspected string regions, and (2) visually scanning the binary in a hex editor for ASCII sequences. You will document every string you find, its address, and its likely purpose. + +#### Step-by-Step Instructions + +##### Step 1: Start the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0017_constants.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +##### Step 2: Locate the Known Strings + +We already know about these strings from the tutorial: + +```gdb +(gdb) x/s 0x10003ee8 +0x10003ee8: "Reverse" + +(gdb) x/s 0x10003ef0 +0x10003ef0: "Engineering" +``` + +These are the LCD display strings. Now let's find more. + +##### Step 3: Find printf Format Strings + +The program uses `printf("FAV_NUM: %d\r\n", ...)` and `printf("OTHER_FAV_NUM: %d\r\n", ...)`. These format strings must be somewhere in flash. Look in the `.rodata` section near the LCD strings: + +```gdb +(gdb) x/10s 0x10003ec0 +``` + +This displays 10 consecutive strings starting from that address. Examine the output — you should find the `printf` format strings. Try different starting addresses if needed: + +```gdb +(gdb) x/20s 0x10003e00 +``` + +##### Step 4: Search the Binary Systematically + +Use GDB to scan through flash memory in large chunks, looking for readable strings: + +```gdb +(gdb) x/50s 0x10003d00 +``` + +Many results will be garbage (non-ASCII data interpreted as text), but real strings will stand out. Look for: +- Format strings containing `%d`, `%s`, `%f`, `\r\n` +- Error messages from the Pico SDK +- Function names or debug info + +##### Step 5: Open the Binary in a Hex Editor + +1. Open `0x0017_constants.bin` in HxD +2. Switch to the "Text" pane (right side) to see ASCII representation +3. Scroll through the binary and look for readable text sequences + +In HxD, printable ASCII characters (0x20–0x7E) are displayed as text; non-printable bytes appear as dots. + +##### Step 6: Use Hex Editor Search + +Most hex editors can search for ASCII text: + +1. Press **Ctrl+F** (Find) +2. Switch to "Text" or "String" search mode +3. Search for known strings like `Reverse`, `FAV_NUM`, `Engineering` +4. For each hit, note the file offset + +##### Step 7: Catalog Your Findings + +Create a table of all strings you find: + +| Address | File Offset | String Content | Purpose | +| -------------- | ----------- | ----------------------- | --------------------------- | +| `0x10003ee8` | `0x3EE8` | "Reverse" | LCD line 1 text | +| `0x10003ef0` | `0x3EF0` | "Engineering" | LCD line 2 text | +| `0x10003eXX` | `0x3EXX` | "FAV_NUM: %d\r\n" | printf format string | +| `0x10003eXX` | `0x3EXX` | "OTHER_FAV_NUM: %d\r\n" | printf format string | +| ... | ... | ... | ... | + +Fill in the actual addresses you discover. + +##### Step 8: Identify SDK and Library Strings + +The Pico SDK may include additional strings (error messages, assert messages, etc.). Look for text patterns like: +- "panic" or "assert" +- File paths (e.g., paths from the SDK source) +- Function names + +These strings reveal internal SDK behavior that isn't visible in the source code. + +#### Expected Output + +After completing this exercise, you should be able to: +- Systematically enumerate string literals in a compiled binary +- Use both GDB (`x/s`) and hex editor text view to find strings +- Distinguish between application strings, format strings, and SDK/library strings +- Understand that string literals reveal program functionality to a reverse engineer + +#### Questions for Reflection + +###### Question 1: How many distinct strings did you find? Were any of them surprising or unexpected? + +###### Question 2: Why are strings so valuable to a reverse engineer? What can an attacker learn about a program just from its strings? + +###### Question 3: What technique could a developer use to make strings harder to find in a binary? (Think about what the strings look like in memory.) + +###### Question 4: The `printf` format strings contain `\r\n`. In the binary, these appear as two bytes: `0x0D 0x0A`. Why two bytes instead of the four characters `\`, `r`, `\`, `n`? + +#### Tips and Hints +- In GDB, `x/s` treats any address as the start of a null-terminated string — it will print garbage if the address isn't really a string +- Use `x/Ns address` where N is a number to print N consecutive strings (useful for scanning regions) +- In HxD, use **Edit** → **Select Block** to highlight a region and examine the text pane +- Real strings are typically 4+ printable ASCII characters followed by a null byte (`0x00`) +- The `.rodata` section is usually located after the `.text` (code) section in the binary + +#### Next Steps +- Proceed to Exercise 3 to trace the I²C struct pointer chain +- Try the `strings` command if available: `strings 0x0017_constants.bin` will extract all printable character sequences +- Consider: if you found a password string in an embedded device binary, what security implications would that have? diff --git a/WEEK07/WEEK07-03.md b/WEEK07/WEEK07-03.md new file mode 100644 index 0000000..2146b3b --- /dev/null +++ b/WEEK07/WEEK07-03.md @@ -0,0 +1,206 @@ +# Embedded Systems Reverse Engineering +[Repository](https://github.com/mytechnotalent/Embedded-Hacking) + +## Week 7 +Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +### Exercise 3: Trace the I²C Struct Pointer Chain + +#### Objective +Use GDB to follow the `i2c1_inst` struct pointer chain from the code instruction that loads it, through the struct in SRAM, to the hardware registers at `0x40098000`. Document every step of the chain: `I2C_PORT` → `i2c1` → `&i2c1_inst` → `hw` → `0x40098000`, and verify each pointer and value in memory. + +#### Prerequisites +- Completed Week 7 tutorial (Parts 3-4 on structs and the macro chain) +- `0x0017_constants.elf` binary available in your build directory +- GDB (`arm-none-eabi-gdb`) and OpenOCD installed +- Understanding of C pointers and structs + +#### Task Description +The Pico SDK uses a chain of macros and structs to abstract hardware access. When you write `I2C_PORT` in C, it expands through multiple macro definitions to ultimately become a pointer to an `i2c_inst_t` struct in SRAM, which in turn contains a pointer to the I²C hardware registers. You will trace this entire chain in GDB, examining each link to understand how the SDK connects your code to silicon. + +#### Step-by-Step Instructions + +##### Step 1: Start the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0017_constants.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +##### Step 2: Find the i2c_init Call + +Run the program to allow initialization to complete, then halt: + +```gdb +(gdb) b *0x10000234 +(gdb) c +``` + +Now step through to just before the `i2c_init` call: + +```gdb +(gdb) x/10i 0x1000023c +``` + +Look for the instruction that loads the `i2c1_inst` pointer into `r0`: + +``` +0x1000023c: ldr r0, [pc, #offset] ; Load &i2c1_inst into r0 +0x1000023e: ldr r1, =0x186A0 ; 100000 (baud rate) +0x10000240: bl i2c_init ; Call i2c_init(i2c1, 100000) +``` + +##### Step 3: Follow the PC-Relative Load + +The `ldr r0, [pc, #offset]` instruction loads a value from a **literal pool** — a data area near the code. Examine what's at the literal pool: + +```gdb +(gdb) x/4wx 0x100002a8 +``` + +Look for a value in the `0x2000xxxx` range — this is the SRAM address of `i2c1_inst`. It should be `0x2000062c`. + +##### Step 4: Examine the i2c1_inst Struct in SRAM + +Now examine the struct at that SRAM address: + +```gdb +(gdb) x/2wx 0x2000062c +``` + +Expected output: +``` +0x2000062c: 0x40098000 0x00000000 +``` + +This maps to the `i2c_inst_t` struct: + +| Offset | Field | Value | Meaning | +| ------ | ----------------- | ------------ | -------------------------------- | +| `+0x00` | `hw` | `0x40098000` | Pointer to I²C1 hardware regs | +| `+0x04` | `restart_on_next` | `0x00000000` | `false` (no pending restart) | + +##### Step 5: Follow the hw Pointer to Hardware Registers + +The first member of the struct (`hw`) points to `0x40098000` — the I²C1 hardware register block. Examine it: + +```gdb +(gdb) x/8wx 0x40098000 +``` + +You should see the I²C1 control and status registers: + +| Offset | Register | Description | +| ------ | -------------- | ------------------------------ | +| `+0x00` | IC_CON | I²C control register | +| `+0x04` | IC_TAR | Target address register | +| `+0x08` | IC_SAR | Slave address register | +| `+0x0C` | (reserved) | — | +| `+0x10` | IC_DATA_CMD | Data command register | + +##### Step 6: Verify the I²C Target Address + +After `i2c_init` and `lcd_i2c_init` have run, check the target address register: + +Let the program run past initialization: + +```gdb +(gdb) delete +(gdb) b * +(gdb) c +``` + +Then examine IC_TAR: + +```gdb +(gdb) x/1wx 0x40098004 +``` + +You should see `0x27` (or a value containing 0x27) — this is the LCD's I²C address! + +##### Step 7: Document the Complete Chain + +Create a diagram of the complete pointer chain: + +``` +Your Code: I2C_PORT + │ + ▼ (preprocessor macro) +i2c1 + │ + ▼ (macro: #define i2c1 (&i2c1_inst)) +&i2c1_inst = 0x2000062c (SRAM address) + │ + ▼ (struct member access) +i2c1_inst.hw = 0x40098000 (hardware register base) + │ + ▼ (memory-mapped I/O) +I²C1 Hardware Registers (silicon) + │ + ├── IC_CON at 0x40098000 + ├── IC_TAR at 0x40098004 + ├── IC_DATA_CMD at 0x40098010 + └── ... +``` + +##### Step 8: Compare with I²C0 + +The Pico 2 has two I²C controllers. Find the `i2c0_inst` struct and compare: + +```gdb +(gdb) x/2wx 0x20000628 +``` + +If I²C0's struct is at a nearby address, you should see: +- `hw` pointing to `0x40090000` (I²C0 base, different from I²C1's `0x40098000`) +- `restart_on_next` = 0 + +This demonstrates how the SDK uses the same struct layout for both I²C controllers, with only the hardware pointer changing. + +#### Expected Output + +After completing this exercise, you should be able to: +- Trace pointer chains from high-level code to hardware registers +- Understand how the Pico SDK uses structs to abstract hardware +- Read struct members from raw memory using GDB +- Navigate from SRAM data structures to memory-mapped I/O registers + +#### Questions for Reflection + +###### Question 1: Why does the SDK use a struct with a pointer to hardware registers instead of accessing `0x40098000` directly? What advantage does this abstraction provide? + +###### Question 2: The `hw` pointer stores `0x40098000`. In the binary, this appears as bytes `00 80 09 40`. Why is the byte order reversed from how we write the address? + +###### Question 3: If you changed the `hw` pointer at `0x2000062c` from `0x40098000` to `0x40090000` using GDB (`set {int}0x2000062c = 0x40090000`), what I²C controller would the program use? What would happen to the LCD? + +###### Question 4: The macro chain has 4 levels of indirection (I2C_PORT → i2c1 → &i2c1_inst → hw → registers). Is this typical for embedded SDKs? What are the trade-offs of this approach? + +#### Tips and Hints +- Use `x/wx` to examine 32-bit words (pointers are 32 bits on ARM Cortex-M33) +- SRAM addresses start with `0x20xxxxxx`; hardware register addresses start with `0x40xxxxxx` +- The literal pool (where PC-relative loads get their data) is usually right after the function's code +- `i2c_inst_t` is only 8 bytes: 4-byte pointer + 4-byte bool (padded to 4 bytes for alignment) +- I²C0 base = `0x40090000`, I²C1 base = `0x40098000` — they are `0x8000` bytes apart + +#### Next Steps +- Proceed to Exercise 4 to patch the LCD to display your own custom message +- Try modifying the `restart_on_next` field in GDB and observe if it changes I²C behavior +- Explore the I²C hardware registers at `0x40098000` — can you read the IC_STATUS register to see if the bus is active? diff --git a/WEEK07/WEEK07-04.md b/WEEK07/WEEK07-04.md new file mode 100644 index 0000000..1f9cfcf --- /dev/null +++ b/WEEK07/WEEK07-04.md @@ -0,0 +1,147 @@ +# Embedded Systems Reverse Engineering +[Repository](https://github.com/mytechnotalent/Embedded-Hacking) + +## Week 7 +Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +### Exercise 4: Display Your Own Custom Message on the LCD + +#### Objective +Patch both LCD string literals in the binary to display your name (or any custom message) on the 1602 LCD, respecting the character length constraints, converting your text to hex bytes, and verifying the result on hardware. + +#### Prerequisites +- Completed Week 7 tutorial (hex editor section) and Exercise 1 +- `0x0017_constants.bin` binary available in your build directory +- A hex editor (HxD, ImHex, or similar) +- Python installed (for UF2 conversion) +- Raspberry Pi Pico 2 with 1602 LCD connected via I²C + +#### Task Description +You will choose two custom messages to display on the LCD — one for each line. Line 1 replaces "Reverse" (7 characters max) and line 2 replaces "Engineering" (11 characters max). You must convert your chosen text to ASCII hex, handle the case where your text is shorter than the original (pad with null bytes), patch the binary, and flash it to see your custom message on the physical LCD. + +#### Step-by-Step Instructions + +##### Step 1: Choose Your Messages + +Plan two messages that fit the constraints: + +| Line | Original | Max Length | Your Message | Length | Valid? | +| ---- | ------------- | ---------- | ------------ | ------ | ------ | +| 1 | "Reverse" | 7 chars | | | | +| 2 | "Engineering" | 11 chars | | | | + +**Examples that work:** +- Line 1: "Hello!!" (7 chars) ✅ +- Line 2: "World!!" (7 chars, pad with 4 null bytes) ✅ +- Line 1: "Hi" (2 chars, pad with 5 null bytes) ✅ +- Line 2: "My Name Here" — ❌ (12 chars, too long!) + +> ⚠️ **Remember:** The 1602 LCD can display up to 16 characters per line, but the binary only allocates 8 bytes for "Reverse" and 12 bytes for "Engineering". You cannot exceed these byte allocations. + +##### Step 2: Convert Your Messages to Hex + +Use an ASCII table to convert each character: + +**Common ASCII values:** + +| Character | Hex | Character | Hex | Character | Hex | +| --------- | ------ | --------- | ------ | --------- | ------ | +| Space | `0x20` | 0-9 | `0x30`-`0x39` | A-Z | `0x41`-`0x5A` | +| ! | `0x21` | : | `0x3A` | a-z | `0x61`-`0x7A` | +| " | `0x22` | ? | `0x3F` | \0 (null) | `0x00` | + +Write out the hex bytes for each message, including the null terminator and any padding: + +**Line 1 (8 bytes total):** +``` +[char1] [char2] [char3] [char4] [char5] [char6] [char7] [0x00] +``` + +If your message is shorter than 7 characters, fill the remaining bytes with `0x00`. + +**Line 2 (12 bytes total):** +``` +[char1] [char2] [char3] [char4] [char5] [char6] [char7] [char8] [char9] [char10] [char11] [0x00] +``` + +If your message is shorter than 11 characters, fill the remaining bytes with `0x00`. + +##### Step 3: Open the Binary and Navigate + +1. Open `0x0017_constants.bin` in HxD +2. Press **Ctrl+G** and enter offset: `3EE8` (Line 1: "Reverse") +3. Verify you see: `52 65 76 65 72 73 65 00` ("Reverse\0") + +##### Step 4: Patch Line 1 + +Replace the 8 bytes starting at offset `0x3EE8` with your prepared hex sequence. + +For example, to write "Hello!!" (7 chars + null): +``` +Before: 52 65 76 65 72 73 65 00 (Reverse) +After: 48 65 6C 6C 6F 21 21 00 (Hello!!) +``` + +For a shorter message like "Hi" (2 chars + null + padding): +``` +Before: 52 65 76 65 72 73 65 00 (Reverse) +After: 48 69 00 00 00 00 00 00 (Hi\0\0\0\0\0\0) +``` + +##### Step 5: Patch Line 2 + +1. Press **Ctrl+G** and enter offset: `3EF0` (Line 2: "Engineering") +2. Verify you see: `45 6E 67 69 6E 65 65 72 69 6E 67 00` +3. Replace the 12 bytes with your prepared hex sequence + +##### Step 6: Save the Patched Binary + +1. Click **File** → **Save As** → `0x0017_constants-h.bin` + +##### Step 7: Convert to UF2 and Flash + +```bash +python ../uf2conv.py build/0x0017_constants-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 + +##### Step 8: Verify on the LCD + +Check the physical LCD display. Your custom messages should appear on lines 1 and 2. + +If the LCD shows garbled text or nothing at all: +- Verify your hex conversion was correct +- Ensure you included the null terminator (`0x00`) +- Confirm you didn't accidentally modify bytes outside the string regions +- Re-open the binary and double-check offsets `0x3EE8` and `0x3EF0` + +#### Expected Output + +After completing this exercise, you should be able to: +- Convert any ASCII text to hex bytes for binary patching +- Handle strings shorter than the allocated space using null padding +- Patch string literals in any compiled binary +- Verify patches work on real hardware + +#### Questions for Reflection + +###### Question 1: You padded short strings with `0x00` null bytes. Would it also work to pad with `0x20` (space characters)? What would be the difference on the LCD display? + +###### Question 2: The LCD is a 1602 (16 columns × 2 rows). What would happen if you could somehow put a 20-character string in memory? Would the LCD display all 20, or only the first 16? + +###### Question 3: If you wanted to combine both hacks from Exercise 1 (changing strings) AND the Week 7 tutorial hacks (changing FAV_NUM and OTHER_FAV_NUM), could you do all patches in a single `.bin` file? What offsets would you need to modify? + +###### Question 4: Besides LCD text, what other strings could you patch in a real-world embedded device to change its behavior? Think about Wi-Fi SSIDs, Bluetooth device names, HTTP headers, etc. + +#### Tips and Hints +- HxD shows the ASCII representation of bytes in the right panel — use this to verify your patches look correct +- A quick way to compute ASCII: lowercase letter hex = uppercase letter hex + `0x20` +- If you make a mistake, close the file WITHOUT saving and start over with the original `.bin` +- Take a photo of your custom LCD display for your portfolio! + +#### Next Steps +- Review all four WEEK07 exercises and verify you understand string patching, data analysis, struct tracing, and custom message creation +- Try patching the `printf` format strings to display different labels in the serial output +- Challenge: can you make the LCD display emoji-like characters using the LCD's custom character feature (if supported by the backpack)? diff --git a/WEEK07/WEEK07.md b/WEEK07/WEEK07.md new file mode 100644 index 0000000..379337d --- /dev/null +++ b/WEEK07/WEEK07.md @@ -0,0 +1,1022 @@ +# Week 7: Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics + +## 🎯 What You'll Learn This Week + +By the end of this tutorial, you will be able to: +- Understand the difference between `#define` macros and `const` variables +- Know how constants are stored differently in memory (compile-time vs runtime) +- Understand the I²C (Inter-Integrated Circuit) communication protocol +- Configure I²C peripherals and communicate with LCD displays +- Understand C structs and how the Pico SDK uses them for hardware abstraction +- Use GDB to examine constants, structs, and string literals in memory +- Hack constant values and string literals using a hex editor +- Patch LCD display text without access to source code + +--- + +## 📚 Part 1: Understanding Constants in C + +### Two Types of Constants + +In C, there are two ways to create values that shouldn't change: + +| Type | Syntax | Where It Lives | When Resolved | +| ----------- | ----------------------- | ------------------ | ------------- | +| **#define** | `#define FAV_NUM 42` | Nowhere (replaced) | Compile time | +| **const** | `const int NUM = 1337;` | Flash (.rodata) | Runtime | + +### Preprocessor Macros (#define) + +A **preprocessor macro** is a text replacement that happens BEFORE your code is compiled: + +```c +#define FAV_NUM 42 + +printf("Value: %d", FAV_NUM); +// Becomes: printf("Value: %d", 42); +``` + +Think of it like a "find and replace" in a text editor. The compiler never sees `FAV_NUM` - it only sees `42`! + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Preprocessor Macro Flow │ +│ │ +│ Source Code Preprocessor Compiler │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ #define │ │ Replace │ │ Compile │ │ +│ │ FAV_NUM │ ─────► │ FAV_NUM │ ─────► │ binary │ │ +│ │ 42 │ │ with 42 │ │ code │ │ +│ └──────────┘ └──────────┘ └──────────┘ │ +│ │ +│ FAV_NUM doesn't exist in the final binary! │ +│ The value 42 is embedded directly in instructions. │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Const Variables + +A **const variable** is an actual variable stored in memory, but marked as read-only: + +```c +const int OTHER_FAV_NUM = 1337; +``` + +Unlike `#define`, this creates a real memory location in the `.rodata` (read-only data) section of flash: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Const Variable in Memory │ +│ │ +│ Flash Memory (.rodata section) │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ Address: 0x10001234 │ │ +│ │ Value: 0x00000539 (1337 in hex) │ │ +│ │ Name: OTHER_FAV_NUM (in debug symbols only) │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ The variable EXISTS in memory and can be read at runtime. │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Comparison: #define vs const + +| Feature | #define | const | +| -------------------- | ------------------------ | ---------------------------- | +| **Type checking** | None (just text) | Yes (compiler enforced) | +| **Memory usage** | None (inlined) | Uses flash space | +| **Debugger visible** | No | Yes (with symbols) | +| **Can take address** | No (`&FAV_NUM` fails) | Yes (`&OTHER_FAV_NUM` works) | +| **Scope** | Global (from definition) | Normal C scoping rules | + +--- + +## 📚 Part 2: Understanding I²C Communication + +### What is I²C? + +**I²C** (pronounced "I-squared-C" or "I-two-C") stands for **Inter-Integrated Circuit**. It's a way for chips to talk to each other using just TWO wires! + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ I²C Bus - Two Wires, Many Devices │ +│ │ +│ 3.3V │ +│ │ │ +│ ┴ Pull-up ┴ Pull-up │ +│ │ │ │ +│ SDA ─┼────────────┼─────────────────────────────────────── │ +│ │ │ │ +│ SCL ─┼────────────┼─────────────────────────────────────── │ +│ │ │ │ │ │ +│ ┌───┴────┐ ┌──┴───┐ ┌────┴──┐ ┌─────┴───┐ │ +│ │ Pico │ │ LCD │ │Sensor │ │ EEPROM │ │ +│ │(Master)│ │ 0x27 │ │ 0x48 │ │ 0x50 │ │ +│ └────────┘ └──────┘ └───────┘ └─────────┘ │ +│ │ +│ Each device has a unique address (0x27, 0x48, 0x50...) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### The Two I²C Wires + +| Wire | Name | Purpose | +| ------- | ------------ | ------------------------------------ | +| **SDA** | Serial Data | Carries the actual data bits | +| **SCL** | Serial Clock | Timing signal that synchronizes data | + +### Why Pull-Up Resistors? + +I²C uses **open-drain** signals, meaning devices can only pull the line LOW. They can't drive it HIGH! Pull-up resistors are needed to bring the lines back to HIGH when no device is pulling them down. + +The Pico 2 has internal pull-ups that we can enable with `gpio_pull_up()`. + +### I²C Addresses + +Every I²C device has a unique **7-bit address**. Common addresses: + +| Device Type | Typical Address | +| --------------------- | ---------------- | +| 1602 LCD with PCF8574 | `0x27` or `0x3F` | +| Temperature sensor | `0x48` | +| EEPROM | `0x50` | +| Real-time clock | `0x68` | + +### I²C Communication Flow + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ I²C Transaction │ +│ │ +│ 1. Master sends START condition │ +│ 2. Master sends device address (7 bits) + R/W bit │ +│ 3. Addressed device sends ACK (acknowledge) │ +│ 4. Data is transferred (8 bits at a time) │ +│ 5. Receiver sends ACK after each byte │ +│ 6. Master sends STOP condition │ +│ │ +│ START ──► Address ──► ACK ──► Data ──► ACK ──► STOP │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📚 Part 3: Understanding C Structs + +### What is a Struct? + +A **struct** (short for "structure") is a way to group related variables together under one name. Think of it like a form with multiple fields: + +```c +// A struct definition - like a template +struct student { + char name[50]; + int age; + float gpa; +}; + +// Creating a variable of this struct type +struct student alice = {"Alice", 16, 3.8}; +``` + +### Why Use Structs? + +Instead of passing many separate variables: +```c +void print_student(char *name, int age, float gpa); // Messy! +``` + +You pass one struct: +```c +void print_student(struct student s); // Clean! +``` + +### The typedef Keyword + +Writing `struct student` everywhere is tedious. The `typedef` keyword creates an alias: + +```c +typedef struct student student_t; + +// Now you can write: +student_t alice; // Instead of: struct student alice; +``` + +### Forward Declaration + +Sometimes you need to tell the compiler "this struct exists" before defining it: + +```c +typedef struct i2c_inst i2c_inst_t; // Forward declaration + alias + +// Later, the full definition: +struct i2c_inst { + i2c_hw_t *hw; + bool restart_on_next; +}; +``` + +--- + +## 📚 Part 4: Understanding the Pico SDK's I²C Structs + +### The i2c_inst_t Struct + +The Pico SDK uses a struct to represent each I²C controller: + +```c +struct i2c_inst { + i2c_hw_t *hw; // Pointer to hardware registers + bool restart_on_next; // SDK internal flag +}; +``` + +**What each member means:** + +| Member | Type | Purpose | +| ----------------- | ------------ | ---------------------------------------- | +| `hw` | `i2c_hw_t *` | Pointer to the actual hardware registers | +| `restart_on_next` | `bool` | Tracks if next transfer needs a restart | + +### The Macro Chain + +When you write `I2C_PORT` in your code, here's what happens: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Macro Expansion Chain │ +│ │ +│ In your code: #define I2C_PORT i2c1 │ +│ │ │ +│ ▼ │ +│ In i2c.h: #define i2c1 (&i2c1_inst) │ +│ │ │ +│ ▼ │ +│ In i2c.c: i2c_inst_t i2c1_inst = {i2c1_hw, false}; │ +│ │ │ +│ ▼ │ +│ In i2c.h: #define i2c1_hw ((i2c_hw_t *)I2C1_BASE) │ +│ │ │ +│ ▼ │ +│ In addressmap.h: #define I2C1_BASE 0x40098000 │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +So `I2C_PORT` eventually becomes a pointer to a struct that contains a pointer to hardware registers at address `0x40098000`! + +### The Hardware Register Pointer + +The `i2c_hw_t *hw` member points to the actual silicon: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Memory Map │ +│ │ +│ Address 0x40098000: I²C1 Hardware Registers │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ Offset 0x00: IC_CON (Control register) │ │ +│ │ Offset 0x04: IC_TAR (Target address register) │ │ +│ │ Offset 0x10: IC_DATA_CMD (Data command register) │ │ +│ │ ... │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ The i2c_hw_t struct maps directly to these registers! │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📚 Part 5: The ARM Calling Convention (AAPCS) + +### How Arguments Are Passed + +On ARM Cortex-M, the **ARM Architecture Procedure Call Standard (AAPCS)** defines how functions receive arguments: + +| Register | Purpose | +| -------- | ---------------- | +| `r0` | First argument | +| `r1` | Second argument | +| `r2` | Third argument | +| `r3` | Fourth argument | +| Stack | Fifth+ arguments | +| `r0` | Return value | + +### Example: i2c_init(i2c1, 100000) + +```c +i2c_init(I2C_PORT, 100000); +``` + +In assembly: +```assembly +ldr r0, [address of i2c1_inst] ; r0 = pointer to struct (first arg) +ldr r1, =0x186A0 ; r1 = 100000 (second arg) +bl i2c_init ; Call the function +``` + +--- + +## 📚 Part 6: Setting Up Your Environment + +### Prerequisites + +Before we start, make sure you have: +1. A Raspberry Pi Pico 2 board +2. A Raspberry Pi Pico Debug Probe +3. OpenOCD installed and configured +4. GDB (`arm-none-eabi-gdb`) installed +5. Python installed (for UF2 conversion) +6. A serial monitor (PuTTY, minicom, or screen) +7. A 1602 LCD display with I²C backpack (PCF8574) +8. A hex editor (HxD, ImHex, or similar) +9. The sample project: `0x0017_constants` + +### Hardware Setup + +Connect your LCD like this: + +| LCD Pin | Pico 2 Pin | +| ------- | ---------- | +| VCC | 3.3V or 5V | +| GND | GND | +| SDA | GPIO 2 | +| SCL | GPIO 3 | + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ I²C LCD Wiring │ +│ │ +│ Pico 2 1602 LCD + I²C Backpack │ +│ ┌──────────┐ ┌──────────────────────┐ │ +│ │ │ │ │ │ +│ │ GPIO 2 │─────── SDA ─────►│ SDA │ │ +│ │ (SDA) │ │ │ │ +│ │ │ │ ┌────────────┐ │ │ +│ │ GPIO 3 │─────── SCL ─────►│ SCL│ Reverse │ │ │ +│ │ (SCL) │ │ │Engineering │ │ │ +│ │ │ │ └────────────┘ │ │ +│ │ 3.3V │─────── VCC ─────►│ VCC │ │ +│ │ │ │ │ │ +│ │ GND │─────── GND ─────►│ GND │ │ +│ │ │ │ │ │ +│ └──────────┘ └──────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Project Structure + +``` +Embedded-Hacking/ +├── 0x0017_constants/ +│ ├── build/ +│ │ ├── 0x0017_constants.uf2 +│ │ └── 0x0017_constants.bin +│ ├── 0x0017_constants.c +│ └── lcd_1602.h +└── uf2conv.py +``` + +--- + +## 🔬 Part 7: Hands-On Tutorial - Constants and I²C LCD + +### Step 1: Review the Source Code + +Let's examine the constants code: + +**File: `0x0017_constants.c`** + +```c +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "lcd_1602.h" + +#define FAV_NUM 42 +#define I2C_PORT i2c1 +#define I2C_SDA_PIN 2 +#define I2C_SCL_PIN 3 + +const int OTHER_FAV_NUM = 1337; + +int main(void) { + stdio_init_all(); + + i2c_init(I2C_PORT, 100000); + gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SDA_PIN); + gpio_pull_up(I2C_SCL_PIN); + + lcd_i2c_init(I2C_PORT, 0x27, 4, 0x08); + lcd_set_cursor(0, 0); + lcd_puts("Reverse"); + lcd_set_cursor(1, 0); + lcd_puts("Engineering"); + + while (true) { + printf("FAV_NUM: %d\r\n", FAV_NUM); + printf("OTHER_FAV_NUM: %d\r\n", OTHER_FAV_NUM); + } +} +``` + +**What this code does:** + +1. **Lines 7-10:** Define preprocessor macros for constants and I²C configuration +2. **Line 12:** Define a `const` variable stored in flash +3. **Line 15:** Initialize UART for serial output +4. **Lines 17-21:** Initialize I²C1 at 100kHz, configure GPIO pins, enable pull-ups +5. **Lines 23-27:** Initialize LCD and display "Reverse" on line 0, "Engineering" on line 1 +6. **Lines 29-32:** Infinite loop printing both constant values to serial terminal + +### Step 2: Flash the Binary to Your Pico 2 + +1. Hold the BOOTSEL button on your Pico 2 +2. Plug in the USB cable (while holding BOOTSEL) +3. Release BOOTSEL - a drive called "RPI-RP2" appears +4. Drag and drop `0x0017_constants.uf2` onto the drive +5. The Pico will reboot and start running! + +### Step 3: Verify It's Working + +**Check the LCD:** +- Line 1 should show: `Reverse` +- Line 2 should show: `Engineering` + +**Check the serial monitor (PuTTY/screen):** +``` +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +FAV_NUM: 42 +OTHER_FAV_NUM: 1337 +... +``` + +--- + +## 🔬 Part 8: Debugging with GDB (Dynamic Analysis) + +> 🔄 **REVIEW:** This setup is identical to previous weeks. If you need a refresher on OpenOCD and GDB connection, refer back to Week 3 Part 6. + +### Starting the Debug Session + +**Terminal 1 - Start OpenOCD:** + +```bash +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:** + +```bash +arm-none-eabi-gdb build/0x0017_constants.elf +``` + +**Connect to target:** + +```gdb +(gdb) target remote :3333 +(gdb) monitor reset halt +``` + +### Step 4: Examine Main Function + +Let's examine the main function. Disassemble from the entry point: + +``` +x/60i 0x10000234 +``` + +You should see output like: + +``` +0x10000234: push {r4, r5, r6, r7, lr} +0x10000236: sub sp, #12 +0x10000238: bl 0x10003014 ; stdio_init_all +0x1000023c: ldr r0, [pc, #108] ; Load i2c1_inst pointer +0x1000023e: ldr r1, =0x186A0 ; 100000 (baud rate) +0x10000240: bl 0x100002b4 ; i2c_init +0x10000244: movs r0, #2 ; GPIO 2 (SDA) +0x10000246: movs r1, #3 ; GPIO_FUNC_I2C +0x10000248: bl 0x100002c8 ; gpio_set_function +0x1000024c: movs r0, #3 ; GPIO 3 (SCL) +0x1000024e: movs r1, #3 ; GPIO_FUNC_I2C +0x10000250: bl 0x100002c8 ; gpio_set_function +... +``` + +### Step 5: Set a Breakpoint at Main + +``` +b *0x10000234 +c +``` + +GDB responds: +``` +Breakpoint 1 at 0x10000234 +Continuing. + +Breakpoint 1, 0x10000234 in ?? () +``` + +### Step 6: Find the #define Constant (FAV_NUM) + +Step through to the printf call and examine the registers: + +``` +x/20i 0x1000028e +``` + +Look for: +``` +0x1000028e: movs r1,#0x2a ; 0x2a = 42 decimal (FAV_NUM) +``` + +The `#define` constant is embedded directly as an immediate value in the instruction! + +### Step 7: Find the const Variable (OTHER_FAV_NUM) + +Continue examining the code: + +``` +x/10i 0x10000296 +``` + +Look for a load instruction: +``` +0x10000296: ldr r1, [pc, #offset] ; Load OTHER_FAV_NUM from memory +``` + +The `const` variable is loaded from a memory address because it actually exists in flash! + +### Step 8: Examine the const Value in Memory + +Find where OTHER_FAV_NUM is stored: + +``` +x/1wx 0x100002ac +``` + +This should show the address pointing to the value. Then examine that address: + +``` +x/1wd 0x10003xxx +``` + +You should see `1337` (or `0x539` in hex). + +### Step 9: Examine the I²C Struct + +Find the i2c1_inst struct address loaded into r0 before i2c_init: + +``` +x/2wx 0x2000062c +``` + +You should see: +``` +0x2000062c: 0x40098000 ; hw pointer (I2C1 hardware base) +0x20000630: 0x00000000 ; restart_on_next = false +``` + +### Step 10: Examine the LCD String Literals + +Find the strings passed to lcd_puts: + +``` +x/s 0x10003ee8 +``` + +Output: +``` +0x10003ee8: "Reverse" +``` + +``` +x/s 0x10003ef0 +``` + +Output: +``` +0x10003ef0: "Engineering" +``` + +### Step 11: Step Through I²C Initialization + +Use `si` to step through instructions and watch the I²C setup: + +``` +si +i r r0 r1 +``` + +Observe how: +- `r0` gets the i2c_inst pointer +- `r1` gets the baud rate (100000) +- The function call happens via `bl` + +--- + +## 🔬 Part 9: Understanding the Assembly + +Now that we've explored the binary in GDB, let's make sense of the key patterns we found. + +### Step 12: Analyze #define vs const in Assembly + +From GDB, we discovered a critical difference: + +**For FAV_NUM (42) — a `#define` macro:** +``` +0x1000028e: movs r1, #0x2a ; 0x2a = 42 decimal +``` + +The value is embedded **directly as an immediate** in the instruction. There is no memory location — the preprocessor replaced `FAV_NUM` with `42` before compilation. + +**For OTHER_FAV_NUM (1337) — a `const` variable:** +``` +0x10000296: ldr r1, [pc, #offset] ; Load value from flash +``` + +The value is **loaded from a memory address** because `const` creates a real variable stored in the `.rodata` section of flash. + +### Step 13: Analyze the I²C Struct Layout + +In GDB, we examined the `i2c1_inst` struct at `0x2000062c`: + +```gdb +(gdb) x/2wx 0x2000062c +0x2000062c: 0x40098000 0x00000000 +``` + +This maps to the `i2c_inst_t` struct: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ i2c_inst_t at 0x2000062c │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ Offset Type Name Value │ │ +│ │ 0x00 i2c_hw_t * hw 0x40098000 │ │ +│ │ 0x04 bool restart_on_next 0x00 (false) │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +The first member (`hw`) points to `0x40098000` — the I²C1 hardware register base. This is the end of the macro chain: `I2C_PORT` → `i2c1` → `&i2c1_inst` → `hw` → `0x40098000`. + +### Step 14: Locate the String Literals + +We found the LCD strings in flash memory: + +```gdb +(gdb) x/s 0x10003ee8 +0x10003ee8: "Reverse" + +(gdb) x/s 0x10003ef0 +0x10003ef0: "Engineering" +``` + +These are stored consecutively in the `.rodata` section. Note the addresses — we'll need them for patching. + +--- + +## 🔬 Part 10: Hacking the Binary with a Hex Editor + +Now for the fun part — we'll patch the `.bin` file directly using a hex editor! + +> 💡 **Why a hex editor?** GDB can modify values in RAM at runtime, but those changes are lost when the device reboots. To make **permanent** changes, we edit the `.bin` file on disk and re-flash it. + +### Step 15: Open the Binary in a Hex Editor + +1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.) +2. Click **File** → **Open** +3. Navigate to `0x0017_constants/build/` +4. Open `0x0017_constants.bin` + +### Step 16: Calculate the File Offset + +The binary is loaded at base address `0x10000000`. To find the file offset of any address: + +``` +file_offset = address - 0x10000000 +``` + +For example: +- Address `0x1000028e` → file offset `0x28E` (654 in decimal) +- Address `0x10003ee8` → file offset `0x3EE8` (16104 in decimal) + +### Step 17: Hack #1 — Change FAV_NUM from 42 to 43 + +From GDB, we know the instruction at `0x1000028e` is: + +``` +movs r1, #0x2a → bytes: 2a 21 +``` + +To change the value from 42 (`0x2a`) to 43 (`0x2b`): + +1. In HxD, press **Ctrl+G** (Go to offset) +2. Enter offset: `28E` +3. You should see the byte `2A` at this position +4. Change `2A` to `2B` +5. The instruction is now `movs r1, #0x2b` (43 in decimal) + +> 🔍 **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a`. + +### Step 18: Hack #2 — Change OTHER_FAV_NUM from 1337 to 1344 + +The `const` value 1337 (`0x539`) is stored in the data section, not as an instruction immediate. From GDB, we found its memory address. + +Examine the literal pool to find where `0x539` is stored: + +```gdb +(gdb) x/4wx 0x100002a8 +``` + +Look for the word `0x00000539`. Calculate its file offset and patch it: + +1. In HxD, go to the offset where `0x539` is stored +2. The bytes will be `39 05 00 00` (little-endian) +3. Change to `40 05 00 00` (`0x540` = 1344 in decimal) + +> 🔍 **Little-endian byte order:** The RP2350 stores multi-byte values with the least significant byte first. So `0x00000539` appears as `39 05 00 00` in memory. + +### Step 19: Hack #3 — Change LCD Text from "Reverse" to "Exploit" + +**IMPORTANT:** The new string must be the **same length** as the original! "Reverse" and "Exploit" are both 7 characters — perfect! + +From GDB, we know "Reverse" is at address `0x10003ee8`: + +1. In HxD, press **Ctrl+G** and enter offset: `3EE8` +2. You should see the bytes for "Reverse": `52 65 76 65 72 73 65 00` +3. Change the bytes to spell "Exploit": `45 78 70 6c 6f 69 74 00` + +**ASCII Reference:** + +| Character | Hex | +| --------- | ------ | +| E | `0x45` | +| x | `0x78` | +| p | `0x70` | +| l | `0x6c` | +| o | `0x6f` | +| i | `0x69` | +| t | `0x74` | + +### Step 20: Save the Patched Binary + +1. Click **File** → **Save As** +2. Save as `0x0017_constants-h.bin` in the build directory +3. Close the hex editor + +--- + +## 🔬 Part 11: Converting and Flashing the Hacked Binary + +### Step 21: Convert to UF2 Format + +Open a terminal and navigate to your project directory: + +```bash +cd Embedded-Hacking/0x0017_constants +``` + +Run the conversion command: + +```bash +python ../uf2conv.py build/0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build/hacked.uf2 +``` + +### Step 22: Flash the Hacked Binary + +1. Hold BOOTSEL and plug in your Pico 2 +2. Drag and drop `hacked.uf2` onto the RPI-RP2 drive +3. Check your LCD and serial monitor + +### Step 23: Verify the Hacks + +**Check the LCD:** +- Line 1 should now show: `Exploit` (instead of "Reverse") +- Line 2 should still show: `Engineering` + +**Check the serial monitor:** +``` +FAV_NUM: 43 +OTHER_FAV_NUM: 1344 +FAV_NUM: 43 +OTHER_FAV_NUM: 1344 +... +``` + +🎉 **BOOM! We successfully:** +1. Changed FAV_NUM from 42 to 43 +2. Changed OTHER_FAV_NUM from 1337 to 1344 +3. Changed the LCD text from "Reverse" to "Exploit" + +--- + +## 📊 Part 12: Summary and Review + +### What We Accomplished + +1. **Learned about constants** - `#define` macros vs `const` variables +2. **Understood I²C communication** - Two-wire protocol for peripheral communication +3. **Explored C structs** - How the Pico SDK abstracts hardware +4. **Mastered the macro chain** - From `I2C_PORT` to `0x40098000` +5. **Examined structs in GDB** - Inspected memory layout of `i2c_inst_t` +6. **Hacked constant values** - Both immediate and memory-stored using a hex editor +7. **Patched string literals** - Changed LCD display text + +### #define vs const Summary + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ #define FAV_NUM 42 │ +│ ─────────────────── │ +│ • Text replacement at compile time │ +│ • No memory allocated │ +│ • Cannot take address (&FAV_NUM is invalid) │ +│ • In binary: value appears as immediate (movs r1, #0x2a) │ +│ • To hack: patch the instruction operand │ +├─────────────────────────────────────────────────────────────────┤ +│ const int OTHER_FAV_NUM = 1337 │ +│ ────────────────────────────── │ +│ • Real variable in .rodata section │ +│ • Takes memory (4 bytes for int) │ +│ • Can take address (&OTHER_FAV_NUM is valid) │ +│ • In binary: value loaded from memory (ldr r1, [address]) │ +│ • To hack: patch the value in the data section │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### I²C Configuration Summary + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ I²C Setup Steps │ +│ │ +│ 1. i2c_init(i2c1, 100000) - Initialize at 100kHz │ +│ 2. gpio_set_function(pin, I2C) - Assign pins to I²C │ +│ 3. gpio_pull_up(sda_pin) - Enable SDA pull-up │ +│ 4. gpio_pull_up(scl_pin) - Enable SCL pull-up │ +│ 5. lcd_i2c_init(...) - Initialize the device │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### The Struct Chain + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ I2C_PORT → i2c1 → &i2c1_inst → i2c_inst_t │ +│ │ │ +│ ├── hw → i2c_hw_t * │ +│ │ └── 0x40098000 │ +│ │ │ +│ └── restart_on_next (bool) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Key Memory Addresses + +| Address | Description | +| ------------ | ---------------------------------- | +| `0x10000234` | main() entry point | +| `0x1000028e` | FAV_NUM value in instruction | +| `0x10000296` | OTHER_FAV_NUM load instruction | +| `0x10003ee8` | "Reverse" string literal (example) | +| `0x40098000` | I²C1 hardware registers base | +| `0x2000062C` | i2c1_inst struct in SRAM | + +--- + +## ✅ Practice Exercises + +### Exercise 1: Change Both LCD Lines +Change "Engineering" to "Hacking!!!" (same number of characters). + +**Hint:** Find the second string after "Reverse" in memory. + +### Exercise 2: Change the I²C Address +The LCD is at address `0x27`. Find where this is passed to `lcd_i2c_init` and change it. + +**Warning:** If you change to an invalid address, the LCD won't work! + +### Exercise 3: Find All String Literals +Search the binary for all readable strings. How many can you find? What do they reveal about the program? + +**Hint:** In GDB, use `x/s` to search for strings in the binary, or scan through the `.bin` file in your hex editor. + +### Exercise 4: Trace the Struct Pointer +Follow the `i2c1_inst` pointer from the code to SRAM. What values are stored in the struct? + +**Hint:** The first member should point to `0x40098000`. + +### Exercise 5: Add Your Own Message +Can you make the LCD display your name? Remember the character limit! + +**Hint:** Line 1 and Line 2 each have 16 characters maximum on a 1602 LCD. + +--- + +## 🎓 Key Takeaways + +1. **#define is text replacement** - It happens before compilation, no memory used. + +2. **const creates real variables** - Stored in .rodata, takes memory, has an address. + +3. **I²C uses two wires** - SDA for data, SCL for clock, pull-ups required. + +4. **Structs group related data** - The SDK uses them to abstract hardware. + +5. **Macros can chain** - `I2C_PORT` → `i2c1` → `&i2c1_inst` → hardware pointer. + +6. **ARM passes args in registers** - r0-r3 for first four arguments. + +7. **GDB reveals struct layouts** - Examine memory to understand data organization. + +8. **String hacking requires same length** - Or you'll corrupt adjacent data! + +9. **Constants aren't constant** - With binary patching, everything can change! + +10. **Compiler optimization changes code** - `gpio_pull_up` becomes `gpio_set_pulls`. + +--- + +## 📖 Glossary + +| Term | Definition | +| ----------------------- | --------------------------------------------------- | +| **#define** | Preprocessor directive for text replacement | +| **AAPCS** | ARM Architecture Procedure Call Standard | +| **const** | Keyword marking a variable as read-only | +| **Forward Declaration** | Telling compiler a type exists before defining it | +| **I²C** | Inter-Integrated Circuit - two-wire serial protocol | +| **Immediate Value** | A constant embedded directly in an instruction | +| **Open-Drain** | Output that can only pull low, not drive high | +| **PCF8574** | Common I²C I/O expander chip used in LCD backpacks | +| **Preprocessor** | Tool that processes code before compilation | +| **Pull-Up Resistor** | Resistor that holds a line HIGH by default | +| **SCL** | Serial Clock - I²C timing signal | +| **SDA** | Serial Data - I²C data line | +| **Struct** | User-defined type grouping related variables | +| **typedef** | Creates an alias for a type | + +--- + +## 🔗 Additional Resources + +### I²C Timing Reference + +| Speed Mode | Maximum Frequency | +| ---------- | ----------------- | +| Standard | 100 kHz | +| Fast | 400 kHz | +| Fast Plus | 1 MHz | + +### Common I²C Addresses + +| Device | Address | +| --------------------- | ------------- | +| PCF8574 LCD (default) | `0x27` | +| PCF8574A LCD | `0x3F` | +| DS3231 RTC | `0x68` | +| BMP280 Sensor | `0x76`/`0x77` | +| SSD1306 OLED | `0x3C`/`0x3D` | + +### Key ARM Instructions for Constants + +| Instruction | Description | +| -------------------- | ------------------------------------------- | +| `movs rN, #imm` | Load small immediate (0-255) directly | +| `ldr rN, [pc, #off]` | Load larger value from literal pool | +| `ldr rN, =value` | Pseudo-instruction for loading any constant | + +### RP2350 I²C Memory Map + +| Address | Description | +| ------------ | ----------------------- | +| `0x40090000` | I²C0 hardware registers | +| `0x40098000` | I²C1 hardware registers | + +--- + +**Remember:** When you see complex nested structures in a binary, take your time to understand the hierarchy. Use GDB to examine struct layouts in memory and trace pointer chains. And always remember — even "constants" can be hacked! + +Happy hacking! 🔧