Updated DS refs

This commit is contained in:
Kevin Thomas
2026-04-15 17:23:21 -04:00
parent 6d01ea5f24
commit 38b5b1bcb5
220 changed files with 33267 additions and 0 deletions
+186
View File
@@ -0,0 +1,186 @@
# Week 4 Quiz: Variables, Memory Sections, and GPIO
## Instructions
Choose the best answer for each question. There is only one correct answer per question.
---
## Questions
### Question 1
Which memory section contains initialized global variables (e.g., `int counter = 42;`)?
A) `.bss`
B) `.text`
C) `.data`
D) `.rodata`
> 📖 **Reference:** Week 4, Part 2 "The Three Main Sections"
**Correct Answer: C**
---
### Question 2
What is the data type `uint8_t`, and what is its valid value range?
A) Signed 8-bit integer; -128 to 127
B) Unsigned 8-bit integer; 0 to 255
C) Unsigned 16-bit integer; 0 to 65,535
D) Signed 16-bit integer; -32,768 to 32,767
> 📖 **Reference:** Week 4, Part 1 "Understanding Data Types" (data types table)
**Correct Answer: B**
---
### Question 3
In the intro-to-variables program, the original code sets `age = 42` then immediately sets `age = 43`. In the compiled binary, what value appeared and why?
A) `42`, because the first assignment always takes precedence
B) Both `42` and `43`, because the compiler keeps all assignments
C) `43` only, because the compiler optimized out the unused value `42`
D) `0`, because the variable was placed in the BSS section
> 📖 **Reference:** Week 4, Part 8, Step 15 "Understand the Optimization"
**Correct Answer: C**
---
### Question 4
When analyzing a raw `.bin` file in Ghidra (without debug symbols), what base address must you set when importing RP2350 firmware?
A) `0x00000000`
B) `0x20000000`
C) `0x10000000`
D) `0x00100000`
> 📖 **Reference:** Week 4, Part 6, Step 7 "Configure the Binary Format"
**Correct Answer: C**
---
### Question 5
Why do uninitialized global variables (e.g., `uint8_t age;`) always print `0` in the uninitialized-variables example?
A) The C compiler sets all uninitialized variables to 0 before generating machine code
B) The startup code (reset handler) zeros out the entire BSS section before `main()` runs
C) The Pico SDK automatically initializes all variables through `stdio_init_all()`
D) Uninitialized variables are stored in flash memory which always reads as 0
> 📖 **Reference:** Week 4, Part 2 "What Happens to Uninitialized Variables?"
**Correct Answer: B**
---
### Question 6
What is the purpose of the `uf2conv.py` script used in Week 4?
A) It decompiles a UF2 binary back into C source code
B) It converts a raw `.bin` file into the UF2 format required to flash the Pico 2
C) It compresses the firmware binary to fit within flash memory limits
D) It strips debug symbols from an ELF file to create a raw binary
> 📖 **Reference:** Week 4, Part 10, Step 19 "Convert to UF2 Format"
**Correct Answer: B**
---
### Question 7
Which Pico SDK function sets a GPIO pin's direction to output?
A) `gpio_init(pin)`
B) `gpio_put(pin, value)`
C) `gpio_set_dir(pin, GPIO_OUT)`
D) `gpio_enable(pin, OUTPUT)`
> 📖 **Reference:** Week 4, Part 3 "GPIO Functions in the Pico SDK" (table)
**Correct Answer: C**
---
### Question 8
What does `gpio_put(LED_PIN, 1)` do to an LED connected to that GPIO pin?
A) It reads and returns the current voltage level of the pin
B) It initializes the GPIO pin and registers it for output use
C) It drives the pin HIGH, turning the LED on
D) It drives the pin LOW, turning the LED off
> 📖 **Reference:** Week 4, Part 3 "GPIO Functions in the Pico SDK" and Part 11, Step 22
**Correct Answer: C**
---
### Question 9
What hexadecimal value does `0x2b` represent, and why did it appear in the compiled binary for the intro-to-variables program?
A) `0x2b` = 42; it is the first value assigned to `age`
B) `0x2b` = 43; it is the optimized constant that replaced the `age` variable
C) `0x2b` = 44; it is the address of the `age` variable in SRAM
D) `0x2b` = 27; it is a stack offset calculated by the compiler
> 📖 **Reference:** Week 4, Part 8, Step 15 "Understand the Optimization" (0x2b = 43 decimal)
**Correct Answer: B**
---
### Question 10
What does the `--family 0xe48bff59` argument specify when running `uf2conv.py`?
A) The base address of the XIP flash region
B) The CRC checksum of the compiled binary
C) The RP2350 chip family identifier required by the UF2 format
D) The baud rate used for serial communication
> 📖 **Reference:** Week 4, Part 10, Step 19 "Convert to UF2 Format"
**Correct Answer: C**
---
## Answer Key
1. C - Initialized global variables belong to the `.data` section
2. B - `uint8_t` is an unsigned 8-bit integer with range 0 to 255
3. C - The compiler optimized out the unused `42` assignment, keeping only `43` (0x2b)
4. C - RP2350 firmware loads at the XIP base address `0x10000000`
5. B - The reset handler's BSS clear phase zeros all uninitialized global variables before `main()` runs
6. B - `uf2conv.py` converts a raw `.bin` file to the UF2 format the Pico 2 uses for flashing
7. C - `gpio_set_dir(pin, GPIO_OUT)` configures the pin direction as output
8. C - `gpio_put(pin, 1)` drives the pin HIGH, turning the LED connected to that pin on
9. B - `0x2b` = 43; the compiler replaced the `age` variable entirely with this constant
10. C - `0xe48bff59` is the RP2350 family ID embedded in the UF2 file header
---
## Scoring Guide
- **10 correct**: Excellent! You have a strong grasp of Week 4 concepts
- **8-9 correct**: Very good! Review the topics you missed
- **6-7 correct**: Good start. Go back and review the key concepts
- **5 or fewer**: Review the Week 4 material again and try the practice exercises
---
## Topics Covered
This quiz tests your understanding of:
- Memory sections: `.data`, `.bss`, `.rodata`, and `.text`
- Data types: `uint8_t` size and range
- Compiler optimization of unused variable assignments
- Importing raw `.bin` files into Ghidra with the correct base address
- BSS zero initialization at startup
- UF2 format and the `uf2conv.py` conversion script
- GPIO functions: `gpio_init()`, `gpio_set_dir()`, `gpio_put()`
- GPIO control: `gpio_put()` behavior (HIGH/LOW)
- Hexadecimal to decimal conversion in context
- UF2 family IDs for chip identification
+63
View File
@@ -0,0 +1,63 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 1 Solution: Analyze Variable Storage in Ghidra
#### Answers
##### Main Function Analysis
| Item | Value/Location | Notes |
|-----------------------|---------------|---------------------------------|
| Main function address | 0x10000234 | Entry point of program |
| Age value (hex) | 0x2b | Optimized constant |
| Age value (decimal) | 43 | Original variable value |
| Variable in memory? | No | Compiler optimized it away |
| printf address | 0x10003100 | Standard library function |
| stdio_init_all addr | 0x100030cc | I/O initialization |
| Format string | "age: %d\r\n" | Located in .rodata section |
##### Decompiled main() After Renaming
```c
int main(void)
{
stdio_init_all();
do {
printf("age: %d\r\n", 0x2b);
} while (true);
}
```
##### Hex-to-Decimal Conversion
```
0x2b = (2 × 16) + 11 = 32 + 11 = 43
```
The compiler replaced both `age = 42` and `age = 43` with the final constant value `0x2b` (43) as an immediate operand in `movs r1, #0x2b`.
##### Assembly Listing
```assembly
movs r1, #0x2b ; Load 43 directly into r1 (printf argument)
ldr r0, [pc, #...] ; Load format string address
bl printf ; Call printf
```
#### Reflection Answers
1. **Why did the compiler optimize away the `age` variable?**
The compiler performs **dead store elimination** and **constant propagation**. The initial assignment `age = 42` is immediately overwritten by `age = 43` with no intervening reads of the value 42. Since the only value ever observed is 43, the compiler replaces all references to `age` with the constant `0x2b` (43) as an immediate operand. No memory allocation is needed—the value lives entirely in the instruction encoding (`movs r1, #0x2b`).
2. **In what memory section would `age` have been stored if it wasn't optimized away?**
As a local variable declared inside `main()`, `age` would have been stored on the **stack** (in RAM at `0x2000xxxx`). The compiler would allocate space by subtracting from SP, store the value with a `str` instruction, and load it back with `ldr` before passing it to `printf`. If `age` were declared as a global initialized variable, it would be placed in the **`.data`** section (RAM, initialized from flash at boot). If declared as `static` or global but uninitialized, it would go in the **`.bss`** section (RAM, zeroed at boot).
3. **Where is the string "age: %d\r\n" stored, and why can't it be in RAM?**
The format string is stored in the **`.rodata`** (read-only data) section in flash memory at `0x1000xxxx`. It cannot be in RAM because: (a) string literals are constants that never change, so storing them in limited RAM would waste space; (b) flash is non-volatile—the string persists across power cycles without needing to be copied from anywhere; (c) the XIP (Execute In Place) mechanism allows the CPU to read directly from flash, so `.rodata` access is efficient.
4. **What would happen if we had used `age` in a calculation before reassigning it to 43?**
The compiler would be forced to preserve the value 42 because it's actually read before being overwritten. For example, `printf("before: %d\n", age); age = 43;` would require the compiler to generate both `movs r1, #0x2a` (42) for the first print and `movs r1, #0x2b` (43) for subsequent uses. Alternatively, a calculation like `age = age + 1` would allow the compiler to constant-fold `42 + 1 = 43` at compile time and still emit just `#0x2b`. Only if the value depends on runtime input would the variable require actual memory allocation.
+241
View File
@@ -0,0 +1,241 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 1: Analyze Variable Storage in Ghidra
#### Objective
Import and analyze the `0x0005_intro-to-variables.bin` binary in Ghidra to understand where variables are stored, identify memory sections, and trace how the compiler optimizes variable usage.
#### Prerequisites
- Ghidra installed and configured
- `0x0005_intro-to-variables.bin` binary available in your build directory
- Understanding of memory sections (`.data`, `.bss`, `.rodata`) from Week 4 Part 2
- Basic Ghidra navigation skills from Week 3
#### Task Description
You will import the binary into Ghidra, configure it for ARM Cortex-M33, analyze the code structure, resolve function names, and locate where the `age` variable is used in the compiled binary.
#### Step-by-Step Instructions
##### Step 1: Start Ghidra and Create New Project
```powershell
ghidraRun
```
1. Click **File** ? **New Project**
2. Select **Non-Shared Project**
3. Click **Next**
4. Enter Project Name: `week04-ex01-intro-to-variables`
5. Choose a project directory
6. Click **Finish**
##### Step 2: Import the Binary
1. Navigate to your file explorer
2. Find `Embedded-Hacking\0x0005_intro-to-variables\build\0x0005_intro-to-variables.bin`
3. **Drag and drop** the `.bin` file into Ghidra's project window
##### Step 3: Configure Import Settings
When the import dialog appears:
1. Click the three dots (**…**) next to **Language**
2. Search for: `Cortex`
3. Select: **ARM Cortex 32 little endian default**
4. Click **OK**
Now click **Options…** button:
1. Change **Block Name** to: `.text`
2. Change **Base Address** to: `10000000` (XIP flash base)
3. Click **OK**
Then click **OK** on the main import dialog.
##### Step 4: Analyze the Binary
1. Double-click the imported file in the project window
2. When prompted "Analyze now?" click **Yes**
3. Leave all default analysis options selected
4. Click **Analyze**
5. Wait for analysis to complete (watch bottom-right progress bar)
##### Step 5: Navigate to the Symbol Tree
Look at the left panel for the **Symbol Tree**. Expand **Functions** to see the auto-detected functions:
You should see function names like:
- `FUN_1000019a`
- `FUN_10000210`
- `FUN_10000234`
- Many more...
These are auto-generated names because we're analyzing a raw binary without debug symbols.
##### Step 6: Identify the Main Function
From Week 3, we know the typical boot sequence:
1. Reset handler copies data
2. `frame_dummy` runs
3. `main()` is called
Click on `FUN_10000234` - this should be our `main()` function.
**Look at the Decompile window:**
```c
void FUN_10000234(void)
{
FUN_100030cc();
do {
FUN_10003100("age: %d\r\n", 0x2b);
} while (true);
}
```
**Observations:**
- `FUN_100030cc()` is likely `stdio_init_all()`
- `FUN_10003100()` is likely `printf()`
- The magic value `0x2b` appears (what is this?)
##### Step 7: Convert 0x2b to Decimal
Let's figure out what `0x2b` means:
**Manual calculation:**
- `0x2b` in hexadecimal
- `2 × 16 + 11 = 32 + 11 = 43` in decimal
**In GDB (alternative method):**
```gdb
(gdb) p/d 0x2b
$1 = 43
```
So `0x2b = 43`! This matches our `age = 43` from the source code!
##### Step 8: Rename Functions for Clarity
Let's rename the functions to their actual names:
**Rename FUN_10000234 to main:**
1. Right-click on `FUN_10000234` in the Symbol Tree
2. Select **Rename Function**
3. Enter: `main`
4. Press **Enter**
**Update main's signature:**
1. In the Decompile window, right-click on `main`
2. Select **Edit Function Signature**
3. Change to: `int main(void)`
4. Click **OK**
**Rename FUN_100030cc to stdio_init_all:**
1. Click on `FUN_100030cc` in the decompile window
2. Right-click ? **Edit Function Signature**
3. Change name to: `stdio_init_all`
4. Change signature to: `bool stdio_init_all(void)`
5. Click **OK**
**Rename FUN_10003100 to printf:**
1. Click on `FUN_10003100`
2. Right-click ? **Edit Function Signature**
3. Change name to: `printf`
4. Check the **Varargs** checkbox (printf accepts variable arguments)
5. Click **OK**
##### Step 9: Examine the Optimized Code
After renaming, the decompiled main should now look like:
```c
int main(void)
{
stdio_init_all();
do {
printf("age: %d\r\n", 0x2b);
} while (true);
}
```
**Critical observation:** Where did our `age` variable go?
Original source code:
```c
uint8_t age = 42;
age = 43;
```
The compiler **optimized it completely away**!
**Why?**
1. `age = 42` is immediately overwritten
2. The value `42` is never used
3. The compiler replaces `age` with the constant `43` (`0x2b`)
4. No variable allocation in memory is needed!
##### Step 10: Examine the Assembly Listing
Click on the **Listing** window (shows assembly code):
Find the instruction that loads `0x2b`:
```assembly
10000xxx movs r1, #0x2b
10000xxx ...
10000xxx bl printf
```
**What this does:**
- `movs r1, #0x2b` - Moves the immediate value 0x2b (43) into register r1
- `bl printf` - Branches to printf, which expects format args in r1+
##### Step 11: Document Your Findings
Create a table of your observations:
| Item | Value/Location | Notes |
| --------------------- | -------------- | ------------------------------- |
| Main function address | `0x10000234` | Entry point of program |
| Age value (hex) | `0x2b` | Optimized constant |
| Age value (decimal) | `43` | Original variable value |
| Variable in memory? | No | Compiler optimized it away |
| Printf address | `0x10003100` | Standard library function |
| Format string | "age: %d\r\n" | Located in .rodata section |
#### Expected Output
After completing this exercise, you should be able to:
- Successfully import and configure ARM binaries in Ghidra
- Navigate the Symbol Tree and identify functions
- Understand how compiler optimization removes unnecessary variables
- Convert hexadecimal values to decimal
- Rename functions for better code readability
#### Questions for Reflection
###### Question 1: Why did the compiler optimize away the `age` variable?
###### Question 2: In what memory section would `age` have been stored if it wasn't optimized away?
###### Question 3: Where is the string "age: %d\r\n" stored, and why can't it be in RAM?
###### Question 4: What would happen if we had used `age` in a calculation before reassigning it to 43?
#### Tips and Hints
- Use **CTRL+F** in Ghidra to search for specific values or strings
- The **Data Type Manager** window shows all recognized data types
- If Ghidra's decompiler output looks wrong, try re-analyzing with different options
- Remember: optimized code often looks very different from source code
- The **Display** ? **Function Graph** shows control flow visually
#### Next Steps
- Proceed to Exercise 2 to learn binary patching
- Try analyzing `0x0008_uninitialized-variables.bin` to see how uninitialized variables behave
- Explore the `.rodata` section to find string literals
#### Additional Challenge
Find the format string "age: %d\r\n" in Ghidra. What address is it stored at? How does the program reference this string in the assembly code? (Hint: Look for an `ldr` instruction that loads the string address into a register.)
+53
View File
@@ -0,0 +1,53 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 2 Solution: Patch Binary to Change Variable Value
#### Answers
##### Patch Details
| Item | Original | Patched |
|--------------------|-------------- |---------------|
| Instruction | movs r1, #0x2b | movs r1, #0x46 |
| Hex bytes | 21 2b | 21 46 |
| Decimal value | 43 | 70 |
| Output | age: 43 | age: 70 |
##### Patching Steps
1. Located `movs r1, #0x2b` in Ghidra Listing view
2. Right-click → **Patch Instruction** → changed `#0x2b` to `#0x46`
3. Verified in Decompile window: `printf("age: %d\r\n", 0x46)`
4. Exported: **File → Export Program → Binary**`0x0005_intro-to-variables-h.bin`
5. Converted to UF2:
```powershell
python ..\uf2conv.py build\0x0005_intro-to-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
```
6. Flashed via BOOTSEL → RPI-RP2 drive
##### Serial Output
```
age: 70
age: 70
age: 70
...
```
#### Reflection Answers
1. **Why do we need to convert to UF2 format instead of flashing the raw .bin file?**
The RP2350 bootloader (accessed via BOOTSEL) expects **UF2 (USB Flashing Format)** files. UF2 is a container format that includes metadata: the target flash address for each 256-byte block, a family ID (`0xe48bff59` for RP2350), and checksums. A raw `.bin` file contains only code bytes with no addressing information—the bootloader wouldn't know where in flash to write the data. UF2 also supports partial updates and is self-describing, making it safer for USB mass storage flashing.
2. **What is the significance of the base address 0x10000000 in the conversion command?**
`0x10000000` is the **XIP (Execute In Place) flash base address** on the RP2350. This tells the UF2 converter that byte 0 of the binary should be written to flash address `0x10000000`. The CPU fetches instructions directly from this address range via the XIP controller. If the base address were wrong (e.g., `0x20000000` for RAM), the code would be written to the wrong location and the processor would fail to boot because the vector table wouldn't be found at the expected address.
3. **What would happen if you patched the wrong instruction by mistake?**
The consequences depend on what was changed: (a) Patching a different `movs` might corrupt an unrelated function parameter, causing incorrect behavior or a crash. (b) Patching opcode bytes (not just the immediate) could create an invalid instruction, triggering a HardFault or UsageFault. (c) Patching inside a multi-byte instruction could split it into two unintended instructions, corrupting the entire subsequent instruction stream. The program would likely crash, output garbage, or hang—requiring reflashing with the original UF2 to recover.
4. **How can you verify a patch was applied correctly before exporting?**
Multiple verification methods: (a) Check the **Decompile window**—it should reflect the new value (e.g., `printf("age: %d\r\n", 0x46)`). (b) Inspect the **Listing window** bytes—confirm the instruction bytes changed from `21 2b` to `21 46`. (c) Use **right-click → Highlight → Highlight Difference** to see patched bytes highlighted. (d) Compare the patched instruction against the ARM Thumb encoding reference to verify the encoding is valid. (e) Check surrounding instructions are unchanged—patches should not accidentally modify adjacent code.
+220
View File
@@ -0,0 +1,220 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 2: Patch Binary to Change Variable Value
#### Objective
Use Ghidra's patching capabilities to modify the compiled binary, changing the value printed by the program from 43 to a different value of your choice, then convert and flash the modified binary to the Pico 2.
#### Prerequisites
- Completed Exercise 1 (binary imported and analyzed in Ghidra)
- Python installed for UF2 conversion
- `uf2conv.py` script available in your project directory
- Raspberry Pi Pico 2 connected via USB
- Serial monitor software (PuTTY, minicom, or screen)
#### Task Description
You will locate the instruction that loads the value 43 into a register, patch it to use a different value (70 in this example), export the modified binary, convert it to UF2 format, and flash it to verify the change.
#### Step-by-Step Instructions
##### Step 1: Open Your Ghidra Project
If you closed Ghidra from Exercise 1:
1. Launch `ghidraRun`
2. Open the project: `week04-ex01-intro-to-variables`
3. Double-click the binary file to open it
##### Step 2: Navigate to the Value Load Instruction
In Exercise 1, we found that `main()` calls `printf("age: %d\r\n", 0x2b)`.
**Find the assembly instruction:**
1. Click on the `main` function in the Symbol Tree
2. Look at the **Listing** window (assembly view)
3. Find the line with `movs r1, #0x2b`
The instruction should look like:
```assembly
10000xxx 21 2b movs r1, #0x2b
```
**What this instruction means:**
- Opcode: `21 2b` (encoded instruction bytes)
- Mnemonic: `movs r1, #0x2b`
- Operation: Move the immediate value 0x2b into register r1
- Register r1 will be used as the argument to printf
##### Step 3: Choose Your New Value
Let's change the value from `43` (0x2b) to `70` (0x46).
**Convert 70 to hexadecimal:**
- 70 ÷ 16 = 4 remainder 6
- Therefore: 70 decimal = 0x46 hexadecimal
You can verify this in Python:
```python
>>> hex(70)
'0x46'
>>> 0x46
70
```
##### Step 4: Patch the Instruction
Now we'll modify the binary:
1. **Right-click** on the instruction `movs r1, #0x2b`
2. Select **Patch Instruction**
3. A dialog appears showing the current instruction
4. Change `#0x2b` to `#0x46`
5. Press **Enter** or click **OK**
The instruction now reads:
```assembly
10000xxx 21 46 movs r1, #0x46
```
**Visual confirmation:**
- The patched instruction should be highlighted (usually in red or orange)
- The bytes should change from `21 2b` to `21 46`
- The decompiled view should update to show `printf("age: %d\r\n", 0x46);`
##### Step 5: Verify the Patch in Decompile Window
Click on the `main` function again and check the **Decompile** window:
```c
int main(void)
{
stdio_init_all();
do {
printf("age: %d\r\n", 0x46); // Changed from 0x2b!
} while (true);
}
```
Perfect! The decompiler recognized our patch.
##### Step 6: Export the Patched Binary
Now we need to save the modified binary:
1. Click **File****Export Program**
2. Set **Format** to: **Binary**
3. Navigate to your build directory:
- `Embedded-Hacking\0x0005_intro-to-variables\build\`
4. Set **Filename** to: `0x0005_intro-to-variables-h.bin`
- The `-h` suffix means "hacked"
5. Click **OK**
**Important:** Make sure you're exporting to the correct location!
##### Step 7: Convert to UF2 Format
The Pico 2 requires UF2 format. Open a terminal and run:
**Navigate to the project directory:**
```powershell
cd C:\path\to\Embedded-Hacking\0x0005_intro-to-variables
```
**Run the conversion:**
```powershell
python ..\uf2conv.py build\0x0005_intro-to-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
```
**Command breakdown:**
- `uf2conv.py` - The conversion script
- `--base 0x10000000` - XIP flash base address (where code runs from)
- `--family 0xe48bff59` - RP2350 family ID
- `--output build\hacked.uf2` - Output filename
**Expected output:**
```
Converting to uf2, output size: 57856, start address: 0x10000000
Wrote 57856 bytes to build\hacked.uf2
```
##### Step 8: Flash the Hacked Binary
**Enter bootloader mode:**
1. Disconnect your Pico 2 from USB
2. Hold down the **BOOTSEL** button
3. While holding BOOTSEL, plug in the USB cable
4. Release BOOTSEL
5. A drive called **RPI-RP2** appears
**Flash the binary:**
1. Open the RPI-RP2 drive
2. Drag and drop `build\hacked.uf2` onto the drive
3. The Pico will automatically reboot
##### Step 9: Verify the Changes
**Open your serial monitor:**
For PuTTY:
1. Select **Serial** connection type
2. Set the COM port (check Device Manager)
3. Set speed to **115200**
4. Click **Open**
For PowerShell:
```powershell
# Find the COM port
Get-PnpDevice -Class Ports | Where-Object {$_.FriendlyName -like "*USB Serial*"}
# Connect (replace COM3 with your port)
$port = new-Object System.IO.Ports.SerialPort COM3,115200,None,8,one
$port.Open()
while($true) { $port.ReadLine() }
```
**Expected output:**
```
age: 70
age: 70
age: 70
age: 70
...
```
🎉 **Success!** You've successfully patched a binary and changed its behavior!
#### Expected Output
After completing this exercise, you should:
- See `age: 70` printing instead of `age: 43`
- Have a patched binary file (`0x0005_intro-to-variables-h.bin`)
- Have a UF2 file (`hacked.uf2`)
- Understand the complete patching workflow
#### Questions for Reflection
###### Question 1: Why do we need to convert to UF2 format instead of flashing the raw .bin file?
###### Question 2: What is the significance of the base address 0x10000000 in the conversion command?
###### Question 3: What would happen if you patched the wrong instruction by mistake?
###### Question 4: How can you verify a patch was applied correctly before exporting?
#### Tips and Hints
- Always make a backup of the original binary before patching
- Use descriptive names like `-h` (hacked) or `-patch` for modified binaries
- Test your patches on hardware to ensure they work as expected
- If something goes wrong, you can always flash the original UF2 file
- Use `File → Export Program → Original File` to revert all patches
#### Next Steps
- Try patching to different values (100, 255, etc.)
- Proceed to Exercise 3 to learn about GPIO control
- Experiment with patching multiple values in the same binary
#### Additional Challenge
Instead of changing the value to 70, change it to 255 (0xFF) - the maximum value for a `uint8_t`. What do you observe? Now try changing it to 256 (0x100). What happens and why? (Hint: Think about the size limits of the instruction encoding.)
+97
View File
@@ -0,0 +1,97 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 3 Solution: Analyze and Understand GPIO Control
#### Answers
##### Main Function Decompiled (After Renaming)
```c
int main(void)
{
stdio_init_all();
gpio_init(0x10); // Initialize GPIO 16
gpio_set_dir(0x10, 1); // Set as output (GPIO_OUT = 1)
while (true) {
printf("age: %d\r\n", 0); // Uninitialized variable = 0
gpio_put(0x10, 1); // LED ON
sleep_ms(0x1f4); // Wait 500ms
gpio_put(0x10, 0); // LED OFF
sleep_ms(0x1f4); // Wait 500ms
}
}
```
##### GPIO Function Identification Table
| Auto-Generated Name | Actual Function | Parameters | Identification Method |
|---------------------|-----------------|--------------------------------|--------------------------------------|
| FUN_100030cc | stdio_init_all | (void) | Called first, no parameters |
| FUN_xxxxxxxx | gpio_init | (uint gpio) | Single parameter = pin number (0x10) |
| FUN_xxxxxxxx | gpio_set_dir | (uint gpio, bool out) | Two params: pin + direction (1=out) |
| FUN_xxxxxxxx | gpio_put | (uint gpio, bool value) | Two params: pin + value (0 or 1) |
| FUN_xxxxxxxx | sleep_ms | (uint32_t ms) | Single param = delay (0x1f4 = 500) |
| FUN_10003100 | printf | (const char *fmt, ...) | Format string + varargs |
##### Key Value Conversions
| Hex | Decimal | Binary | Purpose |
|--------|---------|-------------|------------------------|
| 0x10 | 16 | 0000 1000 | GPIO pin number (red LED) |
| 0x1f4 | 500 | — | Sleep duration (ms) |
| 0x01 | 1 | 0000 0001 | GPIO_OUT / LED ON |
| 0x00 | 0 | 0000 0000 | GPIO_IN / LED OFF |
```
0x10 = (1 × 16) + 0 = 16
0x1f4 = (1 × 256) + (15 × 16) + 4 = 256 + 240 + 4 = 500
```
##### GPIO Memory Map
| Address | Register | Purpose |
|-------------|-------------|------------------------------|
| 0x40028000 | IO_BANK0 | GPIO function selection |
| 0x40038000 | PADS_BANK0 | GPIO pad configuration |
| 0xd0000000 | SIO | Single-cycle I/O |
GPIO 16 specific:
- Pad control: `0x40038000 + (16 × 4) = 0x40038040`
- Function select: `0x40028000 + (16 × 4) = 0x40028040`
##### gpio_put Coprocessor Instruction
```assembly
mcrr p0, #4, r4, r5, c0
```
- `mcrr` = Move to Coprocessor from two ARM Registers
- `p0` = Coprocessor 0 (GPIO coprocessor on RP2350)
- `r4` = GPIO pin number, `r5` = value (0 or 1)
- Single-cycle GPIO operation
##### Blink Timing Analysis
- ON duration: `sleep_ms(0x1f4)` = 500ms
- OFF duration: `sleep_ms(0x1f4)` = 500ms
- Total cycle: 1000ms = 1 second
- Blink rate: **1 Hz**
#### Reflection Answers
1. **Why does gpio_init() need to configure both PADS_BANK0 and IO_BANK0 registers?**
These registers control different aspects of GPIO operation. **PADS_BANK0** (`0x40038000`) configures the physical pad properties: input enable (IE), output disable (OD), pull-up/pull-down resistors, drive strength, and slew rate. **IO_BANK0** (`0x40028000`) configures the function multiplexer (FUNCSEL), selecting which internal peripheral drives the pin—SIO (function 5) for software control, UART, SPI, I2C, PWM, etc. Both must be configured: PADS sets the electrical characteristics of the physical pin, while IO_BANK0 routes the correct internal signal to it.
2. **What is the advantage of using the GPIO coprocessor instruction (`mcrr`) instead of writing to memory-mapped registers?**
The `mcrr` coprocessor instruction completes in a **single CPU cycle**, whereas writing to memory-mapped GPIO registers requires multiple cycles: an address load, a data load, and a store instruction (plus potential bus wait states). On the RP2350, the SIO coprocessor provides deterministic, single-cycle access to GPIO outputs, which is critical for bit-banging protocols (like SPI or custom serial) where precise timing is required. The coprocessor path bypasses the AHB/APB bus entirely.
3. **If you wanted to blink the LED at 10 Hz instead of 1 Hz, what value should `sleep_ms()` use?**
At 10 Hz, each full on/off cycle is 100ms. Since the loop has two `sleep_ms()` calls (one for ON, one for OFF), each should be `100 / 2 = 50ms`. In hex: `50 = 0x32`. So both `sleep_ms()` calls should use `sleep_ms(0x32)`.
4. **What would happen if you called `gpio_put()` on a pin that hasn't been initialized with `gpio_init()` first?**
The GPIO pin's function would still be set to its reset default (typically NULL/function 0), not SIO (function 5). The `gpio_put()` coprocessor instruction would update the SIO output register internally, but since the pin's function multiplexer isn't routing SIO to the physical pad, the electrical state of the pin wouldn't change. The LED would remain off. Additionally, without pad configuration, input enable and output disable bits may not be set correctly, further preventing any observable output.
+270
View File
@@ -0,0 +1,270 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 3: Analyze and Understand GPIO Control
#### Objective
Import the `0x0008_uninitialized-variables.bin` binary, analyze the GPIO initialization and control sequences, understand how `gpio_init()`, `gpio_set_dir()`, and `gpio_put()` work at the assembly level.
#### Prerequisites
- Completed Exercises 1 and 2
- Understanding of GPIO basics from Week 4 Part 3
- Raspberry Pi Pico 2 with an LED connected to GPIO 16
- Basic knowledge of ARM Thumb-2 instruction set
#### Task Description
You will import a new binary that controls GPIO pins, identify the GPIO-related function calls, trace the initialization sequence, and understand how the Pico SDK controls hardware at the low level.
#### Step-by-Step Instructions
##### Step 1: Flash the Original Binary
Before analysis, let's see what the program does:
1. Hold **BOOTSEL** and plug in your Pico 2
2. Flash `0x0008_uninitialized-variables.uf2` to the RPI-RP2 drive
3. Open your serial monitor
**Expected output:**
```
age: 0
age: 0
age: 0
...
```
**Expected behavior:**
- The red LED on GPIO 16 blinks on/off every 500ms
- The value `0` is printed (uninitialized variable)
##### Step 2: Create a New Ghidra Project
1. Launch Ghidra: `ghidraRun`
2. Click **File****New Project**
3. Select **Non-Shared Project**
4. Project Name: `week04-ex03-gpio-analysis`
5. Click **Finish**
##### Step 3: Import the GPIO Binary
1. Drag and drop `0x0008_uninitialized-variables.bin` into Ghidra
2. Set Language: **ARM Cortex 32 little endian default**
3. Click **Options…**
- Block Name: `.text`
- Base Address: `10000000`
4. Click **OK** on all dialogs
5. Double-click the file and click **Yes** to analyze
##### Step 4: Identify the Main Function
Look for the main function (likely `FUN_10000234` or similar):
**In the Symbol Tree:**
1. Expand **Functions**
2. Look for a function that appears to be an entry point
3. Click on potential `main` candidates
**Look for these patterns in the decompile:**
- Call to `stdio_init_all()`
- Call to `gpio_init()`
- Infinite while loop with `gpio_put()` and `sleep_ms()`
##### Step 5: Rename the Main Function
Once you identify `main`:
1. Right-click on the function name
2. Select **Edit Function Signature**
3. Change to: `int main(void)`
4. Click **OK**
**Expected decompiled code structure:**
```c
int main(void)
{
// Some initial value
stdio_init_all();
gpio_init(0x10); // GPIO 16
// ... more GPIO setup
while (true) {
printf(...);
gpio_put(0x10, 1);
sleep_ms(0x1f4);
gpio_put(0x10, 0);
sleep_ms(0x1f4);
}
}
```
##### Step 6: Identify GPIO Function Calls
Look in the decompiled main for function calls. You should see several undefined functions.
**Find and rename these GPIO functions:**
| Auto-Generated Name | Actual Function | How to Identify |
| ------------------- | ----------------- | ------------------------------------------ |
| `FUN_xxxxx` | `gpio_init` | Takes one parameter (pin number) |
| `FUN_xxxxx` | `gpio_set_dir` | Takes two parameters (pin, direction) |
| `FUN_xxxxx` | `gpio_put` | Takes two parameters (pin, value) |
| `FUN_xxxxx` | `sleep_ms` | Takes one parameter (milliseconds) |
| `FUN_xxxxx` | `stdio_init_all` | Takes no parameters, called first |
| `FUN_xxxxx` | `printf` | Takes variable args, has format string |
**Example renaming gpio_init:**
1. Click on the function call in the decompile window
2. Right-click → **Edit Function Signature**
3. Change name to: `gpio_init`
4. Set signature to: `void gpio_init(uint gpio)`
5. Click **OK**
##### Step 7: Analyze GPIO Initialization Sequence
After renaming, your decompiled main should look clearer:
```c
int main(void)
{
stdio_init_all();
gpio_init(0x10); // Initialize GPIO 16
gpio_set_dir(0x10, 1); // Set as output (1 = GPIO_OUT)
while (true) {
printf("age: %d\r\n", 0);
gpio_put(0x10, 1); // LED ON
sleep_ms(0x1f4); // Wait 500ms (0x1f4 = 500)
gpio_put(0x10, 0); // LED OFF
sleep_ms(0x1f4); // Wait 500ms
}
}
```
**Key observations:**
- `0x10` is hexadecimal for 16 (GPIO 16 - red LED)
- `0x1f4` is hexadecimal for 500 (milliseconds)
- `1` means GPIO_OUT (output direction)
- The LED is controlled by toggling between 1 (on) and 0 (off)
##### Step 8: Examine gpio_init Assembly
Double-click on `gpio_init` to jump to its implementation.
**Look for these key operations in the assembly:**
```assembly
; Load GPIO pin number into register
movs r4, r0 ; Save pin number
; Calculate pad register address
; Base address: 0x40038000 (PADS_BANK0)
; Offset: pin * 4
ldr r3, =0x40038000
lsls r5, r4, #2 ; pin * 4
add r3, r5 ; Calculate address
; Configure pad (clear OD bit, set IE bit)
ldr r2, [r3] ; Read current config
bic r2, #0x80 ; Clear output disable
orr r2, #0x40 ; Set input enable
str r2, [r3] ; Write back
; Set GPIO function to SIO (0x05)
ldr r3, =0x40028000 ; IO_BANK0 base
add r3, r5 ; Add offset
movs r2, #5 ; FUNCSEL = SIO
str r2, [r3] ; Set function
```
**What this does:**
1. Configures the GPIO pad registers (physical pin properties)
2. Sets the GPIO function to SIO (Software I/O)
3. Prepares the pin for software control
##### Step 9: Examine gpio_put Assembly
Find the `gpio_put` function and examine its implementation.
**Look for the GPIO coprocessor instruction:**
```assembly
gpio_put:
movs r4, r0 ; GPIO pin number
movs r5, r1 ; Value (0 or 1)
; Use ARM coprocessor to control GPIO
mcrr p0, #4, r4, r5, c0
bx lr ; Return
```
**Critical instruction: `mcrr p0, #4, r4, r5, c0`**
- `mcrr` = Move to Coprocessor from two ARM Registers
- `p0` = Coprocessor 0 (GPIO coprocessor in RP2350)
- `#4` = Operation code
- `r4, r5` = Source registers (pin number, value)
- `c0` = Coprocessor register (GPIO output control)
This is a **single-cycle GPIO operation** - extremely fast!
##### Step 10: Document the GPIO Memory Map
Create a reference table of the addresses you found:
| Address | Register | Purpose |
| ------------ | ------------- | ------------------------------- |
| `0x40028000` | IO_BANK0 | GPIO function selection |
| `0x40038000` | PADS_BANK0 | GPIO pad configuration |
| `0xd0000000` | SIO | Single-cycle I/O (coprocessor) |
**GPIO 16 specific addresses:**
- Pad control: `0x40038000 + (16 * 4) = 0x40038040`
- Function select: `0x40028000 + (16 * 4) = 0x40028040`
##### Step 11: Trace the Blink Timing
Calculate the actual timing:
**sleep_ms(0x1f4):**
- Convert: 0x1f4 = (1 × 256) + (15 × 16) + 4 = 256 + 240 + 4 = 500 decimal
- So the LED is on for 500ms, off for 500ms
- Total cycle time: 1000ms = 1 second
- Blink rate: 1 Hz
#### Expected Output
After completing this exercise, you should understand:
- How GPIO initialization configures hardware registers
- The role of the GPIO coprocessor in the RP2350
- How `gpio_put()` uses a single ARM instruction for fast I/O
- The memory-mapped addresses for GPIO control
- How timing delays are implemented with `sleep_ms()`
#### Questions for Reflection
###### Question 1: Why does gpio_init() need to configure both PADS_BANK0 and IO_BANK0 registers?
###### Question 2: What is the advantage of using the GPIO coprocessor instruction (`mcrr`) instead of writing to memory-mapped registers?
###### Question 3: If you wanted to blink the LED at 10 Hz instead of 1 Hz, what value should `sleep_ms()` use?
###### Question 4: What would happen if you called `gpio_put()` on a pin that hasn't been initialized with `gpio_init()` first?
#### Tips and Hints
- Use Ghidra's **References** feature (right-click → Find References) to see where functions are called
- The **Display****Memory Map** shows all memory regions
- Look for bit manipulation instructions (`bic`, `orr`) to understand register configuration
- The ARM Architecture Reference Manual has complete documentation on coprocessor instructions
- Use hex-to-decimal converters online if you're unsure about conversions
#### Next Steps
- Proceed to Exercise 4 to patch the GPIO binary
- Try to identify other SDK functions like `gpio_get()` if they appear
- Explore the full GPIO initialization in the SDK source code
#### Additional Challenge
Find the `gpio_set_dir()` function in Ghidra. Does it also use a GPIO coprocessor instruction? What coprocessor register does it use (c0, c4, or something else)? Compare its implementation to `gpio_put()` and document the differences.
+102
View File
@@ -0,0 +1,102 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 4 Solution: Patch GPIO Binary to Change LED Pin
#### Answers
##### Patch Summary
| Item | Original | Patched | Hex Change |
|---------------|-----------------|-----------------|---------------|
| LED pin | GPIO 16 | GPIO 17 | 0x10 → 0x11 |
| Printed value | 0 (uninitialized)| 66 | 0x00 → 0x42 |
| Blink timing | 500ms | 100ms | 0x1f4 → 0x64 |
##### Detailed Patch Locations
**1. gpio_init parameter:**
```assembly
; Before: movs r0, #0x10 (bytes: 10 20)
; After: movs r0, #0x11 (bytes: 11 20)
```
**2. gpio_set_dir parameter:**
```assembly
; Before: movs r3, #0x10 (bytes: 10 23)
; After: movs r3, #0x11 (bytes: 11 23)
```
**3. gpio_put (LED ON) parameter:**
```assembly
; Before: movs r4, #0x10 (bytes: 10 24)
; After: movs r4, #0x11 (bytes: 11 24)
```
**4. gpio_put (LED OFF) parameter:**
```assembly
; Before: movs r4, #0x10 (bytes: 10 24)
; After: movs r4, #0x11 (bytes: 11 24)
```
**5. printf value:**
```assembly
; Before: movs r1, #0x00 (bytes: 00 21)
; After: movs r1, #0x42 (bytes: 42 21)
```
**6. sleep_ms (both calls):**
```assembly
; Before: loads 0x1f4 (500ms)
; After: movs r0, #0x64 (100ms)
```
##### Hex Conversions
```
GPIO 17: 17 = 0x11 = 0001 0001 binary
Value 66: 66 = 0x42 = 0100 0010 binary
100ms: 100 = 0x64 = 0110 0100 binary
```
##### Decompiled Result After All Patches
```c
int main(void)
{
stdio_init_all();
gpio_init(0x11); // GPIO 17 (green LED)
gpio_set_dir(0x11, 1); // Output
while (true) {
printf("age: %d\r\n", 0x42); // Prints 66
gpio_put(0x11, 1); // Green LED ON
sleep_ms(0x64); // 100ms
gpio_put(0x11, 0); // Green LED OFF
sleep_ms(0x64); // 100ms
}
}
```
##### Hardware Verification
- GREEN LED (GPIO 17) blinks at 10 Hz (100ms on, 100ms off)
- RED LED (GPIO 16) remains off
- Serial output: `age: 66` repeating
#### Reflection Answers
1. **Why did we need to patch GPIO 16 in multiple places (gpio_init, gpio_set_dir, gpio_put)?**
Each function takes the GPIO pin number as a separate parameter. `gpio_init(16)` configures the pad and function mux for pin 16. `gpio_set_dir(16, 1)` sets pin 16's direction to output. `gpio_put(16, value)` toggles pin 16's output state. These are independent function calls with independent immediate values in the instruction stream—the compiler doesn't share or reuse a single "pin number variable." Each `movs rN, #0x10` loads the pin number fresh for its respective function call. Missing any one patch would result in a mismatch: e.g., initializing pin 17 but toggling pin 16.
2. **What would happen if you forgot to patch one of the gpio_put calls?**
You would get asymmetric behavior. For example, if you patched the "LED ON" `gpio_put` to pin 17 but left the "LED OFF" at pin 16: GPIO 17 (green) would turn on but never turn off (staying permanently lit), while GPIO 16 (red) would receive the "off" command for a pin that was never initialized—which would have no visible effect. The result: green LED stuck on, no blinking.
3. **How does the instruction encoding differ for immediate values less than 256 vs. greater than 255?**
In 16-bit Thumb encoding, `movs Rd, #imm8` can only encode immediate values 0255 in a single 2-byte instruction. For values > 255 (like 500 = 0x1f4), the compiler must use either: (a) a 32-bit Thumb-2 `movw Rd, #imm16` instruction (4 bytes, can encode 065535), (b) a multi-instruction sequence that constructs the value (e.g., `movs` + `lsls` + `add`), or (c) an `ldr Rd, [pc, #offset]` that loads the constant from a literal pool in flash. This is why patching `sleep_ms(500)` may be more complex than patching `gpio_put(16, 1)`.
4. **If you wanted to make the LED blink at exactly 5 Hz, what sleep_ms value would you use?**
At 5 Hz, each complete cycle is `1000 / 5 = 200ms`. With two `sleep_ms()` calls per cycle (ON and OFF), each call should be `200 / 2 = 100ms`. In hex: `100 = 0x64`. So `sleep_ms(0x64)` for each call—which is exactly the value used in this exercise's patch. For a different duty cycle (e.g., 150ms on, 50ms off), you'd use different values for each call while keeping the total at 200ms.
+345
View File
@@ -0,0 +1,345 @@
# Embedded Systems Reverse Engineering
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
## Week 4
Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
### Non-Credit Practice Exercise 4: Patch GPIO Binary to Change LED Pin
#### Objective
Patch the `0x0008_uninitialized-variables.bin` binary to change which LED blinks, modify the printed value, and change the blink timing, then verify all changes work correctly on hardware.
#### Prerequisites
- Completed Exercise 3 (GPIO binary analyzed in Ghidra)
- Understanding of how GPIO pin numbers are encoded
- Knowledge of hexadecimal-to-decimal conversion
- Pico 2 with ability to test multiple GPIO pins
#### Task Description
You will locate all instances where GPIO 16 is used, patch them to GPIO 17 (changing from red LED to green LED), modify the printed value from 0 to 66, and adjust the blink timing from 500ms to 100ms for faster blinking.
#### Step-by-Step Instructions
##### Step 1: Plan Your Patches
Before we start patching, let's identify what needs to change:
| Current Value | New Value | Description | Hex Conversion |
| ------------- | --------- | --------------------- | ------------------- |
| GPIO 16 | GPIO 17 | Change LED pin | 0x10 → 0x11 |
| age = 0 | age = 66 | Change printed value | 0x00 → 0x42 |
| 500ms | 100ms | Change blink timing | 0x1f4 → 0x64 |
**Verify the hex conversions:**
- 17 decimal = 0x11 hex ✓
- 66 decimal = 0x42 hex ✓
- 100 decimal = 0x64 hex ✓
##### Step 2: Open the GPIO Project in Ghidra
1. Launch Ghidra and open `week04-ex03-gpio-analysis`
2. Double-click the binary to open the CodeBrowser
3. Navigate to the `main` function
**Review the decompiled code:**
```c
int main(void)
{
stdio_init_all();
gpio_init(0x10);
gpio_set_dir(0x10, 1);
while (true) {
printf("age: %d\r\n", 0);
gpio_put(0x10, 1);
sleep_ms(0x1f4);
gpio_put(0x10, 0);
sleep_ms(0x1f4);
}
}
```
##### Step 3: Find and Patch gpio_init Parameter
Look at the **Listing** window (assembly view) for the main function.
**Find the gpio_init call:**
```assembly
1000023a 10 20 movs r0, #0x10
1000023c xx xx bl gpio_init
```
**Patch instruction:**
1. Right-click on `movs r0, #0x10`
2. Select **Patch Instruction**
3. Change `#0x10` to `#0x11`
4. Press **Enter**
**Result:**
```assembly
1000023a 11 20 movs r0, #0x11
```
The instruction bytes change from `10 20` to `11 20`.
##### Step 4: Find and Patch gpio_set_dir Parameter
Continue down the assembly listing:
```assembly
10000240 10 23 movs r3, #0x10
10000242 01 22 movs r2, #1
10000244 xx xx bl gpio_set_dir
```
**Patch the r3 load:**
1. Right-click on `movs r3, #0x10`
2. Select **Patch Instruction**
3. Change to `#0x11`
4. Press **Enter**
**Why r3 instead of r0?** The SDK might pass GPIO pin as the first parameter differently, or this could be due to register allocation. Trust the analysis!
##### Step 5: Find All gpio_put Calls
Inside the while loop, there are two `gpio_put` calls:
**First gpio_put (LED ON):**
```assembly
10000252 10 24 movs r4, #0x10
10000254 01 25 movs r5, #1
10000256 xx xx bl gpio_put
```
**Patch:**
1. Right-click on `movs r4, #0x10`
2. Change to `#0x11`
**Second gpio_put (LED OFF):**
```assembly
1000025e 10 24 movs r4, #0x10
10000260 00 25 movs r5, #0
10000262 xx xx bl gpio_put
```
**Patch:**
1. Right-click on `movs r4, #0x10`
2. Change to `#0x11`
**Note:** The exact addresses will vary, but the pattern is consistent.
##### Step 6: Patch the Printed Value
Find the printf call in the loop:
```assembly
1000024a 00 21 movs r1, #0x0
1000024c xx xx ldr r0, [pc, #offset]
1000024e xx xx bl printf
```
**Patch the value:**
1. Right-click on `movs r1, #0x0`
2. Select **Patch Instruction**
3. Change to `#0x42` (66 in decimal)
4. Press **Enter**
**Result:**
```assembly
1000024a 42 21 movs r1, #0x42
```
##### Step 7: Patch the Sleep Timing (First)
Find the first `sleep_ms(0x1f4)` call:
```assembly
10000258 f4 21 movs r1, #0xf4
1000025a 01 20 movs r0, #1
1000025c 00 04 lsls r0, r0, #16
1000025e 08 44 add r0, r1
10000260 xx xx bl sleep_ms
```
**Wait, this looks complex!** The value 0x1f4 (500) is being constructed:
- Load 1 into r0
- Shift left 16 bits: 1 << 16 = 0x10000
- Load 0xf4 into r1
- Add them: 0x10000 + 0xf4 = 0x1f4
**Alternative pattern (simpler):**
```assembly
10000xxx f4 20 movs r0, #0xf4
10000xxx 01 20 movs r1, #0x01
10000xxx ...
```
**For 100ms (0x64):**
Simply find where 0x1f4 is loaded and change it to 0x64.
**If the instruction is:**
```assembly
movs r0, #0x1f4
```
**Change to:**
```assembly
movs r0, #0x64
```
**Note:** The exact encoding depends on the instruction. For immediate values > 255, Thumb-2 uses different encodings.
##### Step 8: Handle Large Immediate Values
If `sleep_ms(500)` uses a multi-instruction sequence to load 0x1f4, you have two options:
**Option A: Patch both instructions**
If it's loading 0x1f4 as (0x100 + 0xf4):
1. Find where 0xf4 is loaded
2. Change it to 0x64
3. Find where 0x1 is loaded for the high byte
4. Change it to 0x0
**Option B: Simplify to single instruction**
Since 0x64 (100) fits in an 8-bit immediate, you can replace the multi-instruction sequence with:
```assembly
movs r0, #0x64
nop
nop
```
##### Step 9: Verify All Patches
Check the **Decompile** window to confirm changes:
```c
int main(void)
{
stdio_init_all();
gpio_init(0x11); // Changed from 0x10!
gpio_set_dir(0x11, 1);
while (true) {
printf("age: %d\r\n", 0x42); // Changed from 0!
gpio_put(0x11, 1); // Changed from 0x10!
sleep_ms(0x64); // Changed from 0x1f4!
gpio_put(0x11, 0); // Changed from 0x10!
sleep_ms(0x64); // Changed from 0x1f4!
}
}
```
Perfect! All changes are reflected.
##### Step 10: Export the Patched Binary
1. Click **File****Export Program**
2. Set Format: **Binary**
3. Navigate to: `Embedded-Hacking\0x0008_uninitialized-variables\build\`
4. Filename: `0x0008_uninitialized-variables-h.bin`
5. Click **OK**
##### Step 11: Convert to UF2
Open PowerShell and navigate to the project:
```powershell
cd C:\path\to\Embedded-Hacking\0x0008_uninitialized-variables
```
**Run conversion:**
```powershell
python ..\uf2conv.py build\0x0008_uninitialized-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
```
**Expected output:**
```
Converting to uf2, output size: 61952, start address: 0x10000000
Wrote 61952 bytes to build\hacked.uf2
```
##### Step 12: Flash and Test
**Enter bootloader:**
1. Hold **BOOTSEL** button
2. Plug in USB
3. Release BOOTSEL
**Flash:**
1. Drag `build\hacked.uf2` to RPI-RP2 drive
2. Pico reboots automatically
**Test with serial monitor:**
```
age: 66
age: 66
age: 66
...
```
**Hardware verification:**
- ✅ GREEN LED (GPIO 17) should be blinking
- ✅ RED LED (GPIO 16) should be off
- ✅ Blink rate should be much faster (10 Hz instead of 1 Hz)
- ✅ Serial output shows 66 instead of 0
🎉 **Triple success!** You've patched three different aspects of the program!
#### Expected Output
After completing this exercise, you should:
- See `age: 66` printing continuously
- Observe the green LED (GPIO 17) blinking rapidly
- Understand how to find and patch all instances of a value
- Know how to handle different immediate value encoding schemes
#### Questions for Reflection
###### Question 1: Why did we need to patch GPIO 16 in multiple places (gpio_init, gpio_set_dir, gpio_put)?
###### Question 2: What would happen if you forgot to patch one of the gpio_put calls?
###### Question 3: How does the instruction encoding differ for immediate values less than 256 vs. greater than 255?
###### Question 4: If you wanted to make the LED blink at exactly 5 Hz, what sleep_ms value would you use?
#### Tips and Hints
- Use Ghidra's **Search****For Scalars** to find all instances of a hex value
- Right-click in Listing → **Highlight****Highlight Instruction** helps track your patches
- Make notes of addresses you've patched to avoid confusion
- Test incrementally - patch one thing at a time if you're unsure
- Keep the original UF2 to revert if needed
#### Next Steps
- Try patching to use GPIO 18 (blue LED) instead
- Change the printf format string to display in hexadecimal
- Experiment with different timing patterns (e.g., 200ms on, 800ms off)
#### Additional Challenge
**Advanced Multi-LED Pattern:**
Patch the binary to create an alternating pattern:
- GPIO 16 (red) blinks for 100ms
- GPIO 17 (green) blinks for 100ms
- GPIO 18 (blue) blinks for 100ms
- Repeat
This requires:
1. Adding new gpio_init and gpio_set_dir calls for GPIO 18
2. Restructuring the while loop to have three gpio_put sequences
3. Finding space in the binary or replacing existing code
**Hint:** You might need to NOP out some instructions and carefully insert new ones. This is advanced patching!
#### Verification Checklist
Before moving on, confirm:
- [ ] GPIO 17 LED blinks (not GPIO 16)
- [ ] Blink rate is approximately 10 Hz (100ms on/off)
- [ ] Serial output shows "age: 66"
- [ ] You can explain each patch you made
- [ ] You understand why each patch was necessary
- [ ] You successfully converted and flashed the UF2
**Congratulations!** You've completed all Week 4 exercises and mastered variable analysis, binary patching, and GPIO manipulation!
Binary file not shown.
+964
View File
@@ -0,0 +1,964 @@
# Week 4: Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics
## 🎯 What You'll Learn This Week
By the end of this tutorial, you will be able to:
- Understand what variables are and how they're stored in memory
- Know the difference between initialized, uninitialized, and constant variables
- Use Ghidra to analyze binaries without debug symbols
- Patch binary files to change program behavior permanently
- Control GPIO pins to blink LEDs on the Pico 2
- Convert patched binaries to UF2 format for flashing
- Understand the `.data`, `.bss`, and `.rodata` memory sections
---
## 📚 Part 1: Understanding Variables
### What is a Variable?
A **variable** is like a labeled box where you can store information. Imagine you have a row of boxes numbered 0 to 9. Each box can hold one item. In programming:
- The **boxes** are memory locations (addresses in SRAM)
- The **items** are the values you store
- The **labels** are the variable names you choose
```
┌─────────────────────────────────────────────────────────────────┐
│ Memory (SRAM) - Like a row of numbered boxes │
│ │
│ Box 0 Box 1 Box 2 Box 3 Box 4 ... │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ 42 │ │ 17 │ │ 0 │ │255 │ │ 99 │ │
│ └────┘ └────┘ └────┘ └────┘ └────┘ │
│ age score count max temp │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### Declaration vs Definition
When working with variables, there are two important concepts:
| Concept | What It Does | Example |
| ------------------ | ------------------------------------ | -------------------------- |
| **Declaration** | Tells the compiler the name and type | `uint8_t age;` |
| **Definition** | Allocates memory for the variable | (happens with declaration) |
| **Initialization** | Assigns an initial value | `uint8_t age = 42;` |
**Important Rule:** You must declare a variable BEFORE you use it!
### Understanding Data Types
The **data type** tells the compiler how much memory to allocate:
| Type | Size | Range | Description |
| ---------- | ------- | ------------------------------- | ----------------------- |
| `uint8_t` | 1 byte | 0 to 255 | Unsigned 8-bit integer |
| `int8_t` | 1 byte | -128 to 127 | Signed 8-bit integer |
| `uint16_t` | 2 bytes | 0 to 65,535 | Unsigned 16-bit integer |
| `int16_t` | 2 bytes | -32,768 to 32,767 | Signed 16-bit integer |
| `uint32_t` | 4 bytes | 0 to 4,294,967,295 | Unsigned 32-bit integer |
| `int32_t` | 4 bytes | -2,147,483,648 to 2,147,483,647 | Signed 32-bit integer |
### Anatomy of a Variable Declaration
Let's break down this line of code:
```c
uint8_t age = 42;
```
| Part | Meaning |
| --------- | ----------------------------------------------------- |
| `uint8_t` | Data type - unsigned 8-bit integer (1 byte) |
| `age` | Variable name - how we refer to this storage location |
| `=` | Assignment operator - puts a value into the variable |
| `42` | The initial value |
| `;` | Semicolon - tells compiler the statement is complete |
---
## 📚 Part 2: Memory Sections - Where Variables Live
### The Three Main Sections
When your program is compiled, variables go to different places depending on how they're declared:
> 📖 **Datasheet Reference:** SRAM starts at `0x20000000` and XIP flash at `0x10000000` (Section 2.2, p. 31). The `.data` and `.bss` sections live in SRAM; `.text` and `.rodata` live in flash and are accessed via Execute-In-Place (Section 4.4, p. 340+).
```
┌─────────────────────────────────────────────────────────────────┐
│ .data Section (Flash → copied to RAM at startup) │
│ Contains: Initialized global/static variables │
│ Example: int counter = 42; │
├─────────────────────────────────────────────────────────────────┤
│ .bss Section (RAM - zeroed at startup) │
│ Contains: Uninitialized global/static variables │
│ Example: int counter; (will be 0) │
├─────────────────────────────────────────────────────────────────┤
│ .rodata Section (Flash - read only) │
│ Contains: Constants, string literals │
│ Example: const int MAX = 100; │
│ Example: "hello, world" │
└─────────────────────────────────────────────────────────────────┘
```
### What Happens to Uninitialized Variables?
In older C compilers, uninitialized variables could contain "garbage" - random leftover data. But modern compilers (including the Pico SDK) are smarter:
1. Uninitialized global variables go into the `.bss` section
2. The `.bss` section is **NOT stored in the binary** (saves space!)
3. At boot, the startup code uses `memset` to **zero out** all of `.bss`
4. So uninitialized variables are always `0`!
This is why in our code:
```c
uint8_t age; // This will be 0, not garbage!
```
---
## 📚 Part 3: Understanding GPIO (General Purpose Input/Output)
### What is GPIO?
**GPIO** stands for **General Purpose Input/Output**. These are pins on the microcontroller that you can control with software. Think of them as tiny switches you can turn on and off.
```
┌─────────────────────────────────────────────────────────────────┐
│ Raspberry Pi Pico 2 │
│ │
│ GPIO 16 ───────► Red LED │
│ GPIO 17 ───────► Green LED │
│ GPIO 18 ───────► Blue LED │
│ ... │
│ GPIO 25 ───────► Onboard LED │
└─────────────────────────────────────────────────────────────────┘
```
### GPIO Functions in the Pico SDK
The Pico SDK provides simple functions to control GPIO pins:
| Function | Purpose |
| ------------------------------ | ------------------------------- |
| `gpio_init(pin)` | Initialize a GPIO pin for use |
| `gpio_set_dir(pin, direction)` | Set pin as INPUT or OUTPUT |
| `gpio_put(pin, value)` | Set pin HIGH (1) or LOW (0) |
| `sleep_ms(ms)` | Wait for specified milliseconds |
### Basic LED Blink Code
```c
#include <stdio.h>
#include "pico/stdlib.h"
#define LED_PIN 16
int main(void) {
gpio_init(LED_PIN); // Initialize GPIO 16
gpio_set_dir(LED_PIN, GPIO_OUT); // Set as output
while (true) {
gpio_put(LED_PIN, 1); // LED ON
sleep_ms(500); // Wait 500ms
gpio_put(LED_PIN, 0); // LED OFF
sleep_ms(500); // Wait 500ms
}
}
```
### What Happens Behind the Scenes?
Each high-level function calls lower-level code. Let's trace `gpio_init()`:
```
gpio_init(LED_PIN)
gpio_set_dir(LED_PIN, GPIO_IN) // Initially set as input
gpio_put(LED_PIN, 0) // Set output value to 0
gpio_set_function(LED_PIN, GPIO_FUNC_SIO) // Connect to SIO block
```
The SIO (Single-cycle I/O) block is a special hardware unit in the RP2350 that provides fast GPIO control!
> 📖 **Datasheet Reference:** IO_BANK0 is at `0x40028000` and PADS_BANK0 is at `0x40038000` (Section 2.2, p. 33). FUNCSEL value 5 selects the SIO function for a GPIO pin (Section 9.6.1, p. 612). The SIO block base address is `0xd0000000` (Section 2.2, p. 31).
---
## 📚 Part 4: Setting Up Your Environment
### Prerequisites
Before we start, make sure you have:
1. A Raspberry Pi Pico 2 board
2. Ghidra installed (for static analysis)
3. Python installed (for UF2 conversion)
4. The sample projects:
- `0x0005_intro-to-variables`
- `0x0008_uninitialized-variables`
5. A serial monitor (PuTTY, minicom, or screen)
### Project Structure
```
Embedded-Hacking/
├── 0x0005_intro-to-variables/
│ ├── build/
│ │ ├── 0x0005_intro-to-variables.uf2
│ │ └── 0x0005_intro-to-variables.bin
│ └── 0x0005_intro-to-variables.c
├── 0x0008_uninitialized-variables/
│ ├── build/
│ │ ├── 0x0008_uninitialized-variables.uf2
│ │ └── 0x0008_uninitialized-variables.bin
│ └── 0x0008_uninitialized-variables.c
└── uf2conv.py
```
---
## 🔬 Part 5: Hands-On Tutorial - Analyzing Variables in Ghidra
### Step 1: Review the Source Code
First, let's look at the code we'll be analyzing:
**File: `0x0005_intro-to-variables.c`**
```c
#include <stdio.h>
#include "pico/stdlib.h"
int main(void) {
uint8_t age = 42;
age = 43;
stdio_init_all();
while (true)
printf("age: %d\r\n", age);
}
```
**What this code does:**
1. Declares a variable `age` and initializes it to `42`
2. Changes `age` to `43`
3. Initializes the serial output
4. Prints `age` forever in a loop
### 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 `0x0005_intro-to-variables.uf2` onto the drive
5. The Pico will reboot and start running!
### Step 3: Verify It's Working
Open your serial monitor (PuTTY, minicom, or screen) and you should see:
```
age: 43
age: 43
age: 43
...
```
The program is printing `43` because that's what we assigned after the initial `42`.
---
## 🔬 Part 6: Setting Up Ghidra for Binary Analysis
### Step 4: Start Ghidra
**Open a terminal and type:**
```powershell
ghidraRun
```
Ghidra will open. Now we need to create a new project.
### Step 5: Create a New Project
1. Click **File****New Project**
2. Select **Non-Shared Project**
3. Click **Next**
4. Enter Project Name: `0x0005_intro-to-variables`
5. Click **Finish**
### Step 6: Import the Binary
1. Open your file explorer
2. Navigate to the `Embedded-Hacking` folder
3. Find `0x0005_intro-to-variables.bin`
4. Select Cortex M Little Endian 32
5. Select Options and set up the .text and offset 10000000
6. **Drag and drop** the `.bin` file into Ghidra's project window
### Step 7: Configure the Binary Format
A dialog appears. The file is identified as a "BIN" (raw binary without debug symbols).
**Click the three dots (…) next to "Language" and:**
1. Search for "Cortex"
2. Select **ARM Cortex 32 little endian default**
3. Click **OK**
**Click the "Options…" button and:**
1. Change **Block Name** to `.text`
2. Change **Base Address** to `10000000` (the XIP address!)
3. Click **OK**
### Step 8: Open and Analyze
1. Double-click on the file in the project window
2. A dialog asks "Analyze now?" - Click **Yes**
3. Use default analysis options and click **Analyze**
Wait for analysis to complete (watch the progress bar in the bottom right).
---
## 🔬 Part 7: Navigating and Resolving Functions
### Step 9: Find the Functions
Look at the **Symbol Tree** panel on the left. Expand **Functions**.
You'll see function names like:
- `FUN_1000019a`
- `FUN_10000210`
- `FUN_10000234`
These are auto-generated names because we imported a raw binary without symbols!
### Step 10: Resolve Known Functions
From our previous chapters, we know what some of these functions are:
| Ghidra Name | Actual Name | How We Know |
| -------------- | ------------- | -------------------------- |
| `FUN_1000019a` | `data_cpy` | From Week 3 boot analysis |
| `FUN_10000210` | `frame_dummy` | From Week 3 boot analysis |
| `FUN_10000234` | `main` | This is where our code is! |
**To rename `FUN_1000019a` to `data_cpy`:**
1. Click on `FUN_1000019a` in the Symbol Tree
2. In the Decompile window, right-click on the function name
3. Select **Edit Function Signature**
4. Change the name to `data_cpy`
5. Click **OK**
**Repeat for the other functions:**
- Rename `FUN_10000210` to `frame_dummy`
- Rename `FUN_10000234` to `main`
### Step 11: Update Main's Signature
For `main`, let's also fix the return type:
1. Right-click on `main` in the Decompile window
2. Select **Edit Function Signature**
3. Change to: `int main(void)`
4. Click **OK**
---
## 🔬 Part 8: Analyzing the Main Function
### Step 12: Examine Main in Ghidra
Click on `main` (or `FUN_10000234`). Look at the **Decompile** window:
You'll see something like:
```c
void FUN_10000234(void)
{
FUN_10002f54();
do {
FUN_100030e4(DAT_10000244,0x2b);
} while( true );
}
```
### Step 13: Resolve stdio_init_all
1. Click on `FUN_10002f54`
2. Right-click → **Edit Function Signature**
3. Change to: `bool stdio_init_all(void)`
4. Click **OK**
### Step 14: Resolve printf
1. Click on `FUN_100030e4`
2. Right-click → **Edit Function Signature**
3. Change the name to `printf`
4. Check the **Varargs** checkbox (printf takes variable arguments!)
5. Click **OK**
### Step 15: Understand the Optimization
Look at the decompiled code. This will look different if you resolved your functions however do you notice something interesting?
```c
void FUN_10000234(void)
{
FUN_10002f54();
do {
FUN_100030e4(DAT_10000244,0x2b);
} while( true );
}
```
**Where's `uint8_t age = 42`?** It's gone!
The compiler **optimized it out**! Here's what happened:
1. Original code: `age = 42`, then `age = 43`
2. Compiler sees: "The `42` is never used, only `43` matters"
3. Compiler removes the unused `42` and just uses `43` directly
**What is `0x2b`?** Let's check:
- `0x2b` in hexadecimal = `43` in decimal ✓
The compiler replaced our variable with the constant value!
---
## 🔬 Part 9: Patching the Binary - Changing the Value
### Step 16: Find the Value to Patch
Look at the **Listing** window (assembly view). Find the instruction that loads `0x2b`:
```assembly
1000023a 2b 21 movs r1,#0x2b
```
This instruction loads the value `0x2b` (43) into register `r1` before calling `printf`.
### Step 17: Patch the Instruction
We're going to change `0x2b` (43) to `0x46` (70)!
1. Click on the instruction `movs r1,#0x2b`
2. Right-click and select **Patch Instruction**
3. Change `0x2b` to `0x46`
4. Press Enter
The instruction now reads:
```assembly
1000023a 46 21 movs r1,#0x46
```
### Step 18: Export the Patched Binary
1. Click **File****Export Program**
2. Set **Format** to **Raw Bytes**
3. Navigate to your build directory
4. Name the file `0x0005_intro-to-variables-h.bin`
5. Click **OK**
---
## 🔬 Part 10: Converting and Flashing the Hacked Binary
### Step 19: Convert to UF2 Format
The Pico 2 expects UF2 files, not raw BIN files. We need to convert it!
**Open a terminal and navigate to your project directory:**
```powershell
cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0005_intro-to-variables
```
**Run the conversion command:**
```powershell
python ..\uf2conv.py build\0x0005_intro-to-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
- `--family 0xe48bff59` = the RP2350 family ID
- `--output build\hacked.uf2` = the output filename
### Step 20: 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
**You should see:**
```
age: 70
age: 70
age: 70
...
```
🎉 **BOOM! We hacked it!** The value changed from 43 to 70!
---
## 🔬 Part 11: Uninitialized Variables and GPIO
Now let's work with a more complex example that includes GPIO control.
### Step 21: Review the Uninitialized Variables Code
**File: `0x0008_uninitialized-variables.c`**
```c
#include <stdio.h>
#include "pico/stdlib.h"
#define LED_PIN 16
int main(void) {
uint8_t age; // Uninitialized!
stdio_init_all();
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
while (true) {
printf("age: %d\r\n", age);
gpio_put(LED_PIN, 1);
sleep_ms(500);
gpio_put(LED_PIN, 0);
sleep_ms(500);
}
}
```
**What this code does:**
1. Declares `age` without initializing it (will be 0 due to BSS zeroing)
2. Initializes GPIO 16 as an output
3. In a loop: prints age, blinks the LED
### Step 22: Flash and Verify
1. Flash `0x0008_uninitialized-variables.uf2` to your Pico 2
2. Open your serial monitor
**You should see:**
```
age: 0
age: 0
age: 0
...
```
And the **red LED on GPIO 16 should be blinking**!
The value is `0` because uninitialized variables in the `.bss` section are zeroed at startup.
---
## 🔬 Part 12: Analyzing GPIO Code in Ghidra
### Step 23: Set Up Ghidra for the New Binary
1. Create a new project: `0x0008_uninitialized-variables`
2. Import `0x0008_uninitialized-variables.bin`
3. Set Language to **ARM Cortex 32 little endian**
4. Set Base Address to `10000000`
5. Auto-analyze
### Step 24: Resolve the Functions
Find and rename these functions:
| Ghidra Name | Actual Name |
| -------------- | ---------------- |
| `FUN_10000234` | `main` |
| `FUN_100030cc` | `stdio_init_all` |
| `FUN_100002b4` | `gpio_init` |
| `FUN_1000325c` | `printf` |
For `gpio_init`, set the signature to:
```c
void gpio_init(uint gpio)
```
### Step 25: Examine the Main Function
The decompiled main should look something like:
```c
void FUN_10000234(void)
{
undefined4 extraout_r1;
undefined4 extraout_r2;
undefined4 in_cr0;
undefined4 in_cr4;
FUN_100030cc();
FUN_100002b4(0x10);
coprocessor_moveto2(0,4,0x10,1,in_cr4);
do {
FUN_1000325c(DAT_10000274,0);
coprocessor_moveto2(0,4,0x10,1,in_cr0);
FUN_10000d10(500);
coprocessor_moveto2(0,4,0x10,0,in_cr0);
FUN_10000d10(500,extraout_r1,extraout_r2,0);
} while( true );
}
```
---
## 🔬 Part 13: Hacking GPIO - Changing the LED Pin
### Step 26: Find the GPIO Pin Value
Look in the assembly for instructions that use `0x10` (which is 16 in decimal - our LED pin):
```assembly
1000023a 10 20 movs r0,#0x10
```
This is where `gpio_init(LED_PIN)` is called with GPIO 16.
### Step 27: Patch GPIO 16 to GPIO 17
We'll change the red LED (GPIO 16) to the green LED (GPIO 17)!
1. Find the instruction `movs r0,#0x10`
2. Right-click → **Patch Instruction**
3. Change `0x10` to `0x11` (17 in hex)
4. Click **OK**
### Step 28: Find All GPIO 16 References
There are more places that use GPIO 16. Look for:
```assembly
10000244 10 23 movs r3,#0x10
```
This is used in `gpio_set_dir`. Patch this to `0x11` as well.
```assembly
10000252 10 24 movs r4,#0x10
```
This is inside the loop for `gpio_put`. Patch this to `0x11` as well.
### Step 29: Bonus - Change the Printed Value
Let's also change the printed value from `0` to `0x42` (66 in decimal):
```assembly
1000024a 00 21 movs r1,#0x0
```
1. Right-click → **Patch Instruction**
2. Change `0x0` to `0x42`
3. Click **OK**
---
## 🔬 Part 14: Export and Test the Hacked GPIO
### Step 30: Export the Patched Binary
1. Click **File****Export Program**
2. Format: **Raw Bytes**
3. Filename: `0x0008_uninitialized-variables-h.bin`
4. Click **OK**
### Step 31: Convert to UF2
```powershell
cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0008_uninitialized-variables
python ..\uf2conv.py build\0x0008_uninitialized-variables-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
```
### Step 32: Flash and Verify
1. Flash `hacked.uf2` to your Pico 2
2. Check your serial monitor
**You should see:**
```
age: 66
age: 66
age: 66
...
```
And now the **GREEN LED on GPIO 17** should be blinking instead of the red one!
🎉 **We successfully:**
1. Changed the printed value from 0 to 66
2. Changed which LED blinks from red (GPIO 16) to green (GPIO 17)
---
## 📚 Part 15: Deep Dive - GPIO at the Assembly Level
### Understanding the GPIO Coprocessor
The RP2350 has a special **GPIO coprocessor** that provides fast, single-cycle GPIO control. This is different from the RP2040!
> 📖 **Datasheet Reference:** The GPIO coprocessor (GPIOC) is documented in Section 3.7.5 (p. 101104). It provides low-overhead Cortex-M33 coprocessor access to SIO GPIO registers via `mcrr`/`mrrc` instructions, allowing single-cycle GPIO operations.
The coprocessor is accessed using special ARM instructions:
```assembly
mcrr p0, #4, r4, r5, c0 ; GPIO output control
mcrr p0, #4, r4, r5, c4 ; GPIO direction control
```
**What this means:**
- `mcrr` = Move to Coprocessor from two ARM Registers
- `p0` = Coprocessor 0 (the GPIO coprocessor)
- `r4` = Contains the GPIO pin number
- `r5` = Contains the value (0 or 1)
- `c0` = Output value register
- `c4` = Output enable register
### The Full GPIO Initialization Sequence
When you call `gpio_init(16)`, here's what actually happens:
```
Step 1: Configure pad (address 0x40038044)
┌─────────────────────────────────────────────────────────────────┐
│ - Clear OD bit (output disable) │
│ - Set IE bit (input enable) │
│ - Clear ISO bit (isolation) │
└─────────────────────────────────────────────────────────────────┘
Step 2: Set function (address 0x40028084)
┌─────────────────────────────────────────────────────────────────┐
│ - Set FUNCSEL to 5 (SIO - Software I/O) │
└─────────────────────────────────────────────────────────────────┘
Step 3: Enable output (via coprocessor)
┌─────────────────────────────────────────────────────────────────┐
│ - mcrr p0, #4, r4, r5, c4 (where r4=16, r5=1) │
└─────────────────────────────────────────────────────────────────┘
```
### Raw Assembly LED Blink
Here's what a completely hand-written assembly LED blink looks like:
```assembly
; Initialize GPIO 16 as output
movs r4, #0x10 ; GPIO 16
movs r5, #0x01 ; Enable
mcrr p0, #4, r4, r5, c4 ; Set as output
; Configure pad registers
ldr r3, =0x40038044 ; Pad control for GPIO 16
ldr r2, [r3] ; Load current config
bic r2, r2, #0x80 ; Clear OD (output disable)
orr r2, r2, #0x40 ; Set IE (input enable)
str r2, [r3] ; Store config
; Set GPIO function to SIO
ldr r3, =0x40028084 ; IO bank control for GPIO 16
movs r2, #5 ; FUNCSEL = SIO
str r2, [r3] ; Set function
; Main loop
loop:
; LED ON
movs r4, #0x10 ; GPIO 16
movs r5, #0x01 ; High
mcrr p0, #4, r4, r5, c0
; Delay
ldr r2, =0x17D7840 ; ~25 million iterations
delay1:
subs r2, r2, #1
bne delay1
; LED OFF
movs r4, #0x10 ; GPIO 16
movs r5, #0x00 ; Low
mcrr p0, #4, r4, r5, c0
; Delay
ldr r2, =0x17D7840
delay2:
subs r2, r2, #1
bne delay2
b loop ; Repeat forever
```
---
## 📊 Part 16: Summary and Review
### What We Accomplished
1. **Learned about variables** - How they're declared, initialized, and stored
2. **Understood memory sections** - `.data`, `.bss`, and `.rodata`
3. **Analyzed binaries in Ghidra** - Without debug symbols!
4. **Patched binaries** - Changed values directly in the binary
5. **Controlled GPIO** - Made LEDs blink
6. **Changed program behavior** - Different LED, different value
### The Binary Patching Workflow
```
┌─────────────────────────────────────────────────────────────────┐
│ 1. Import .bin file into Ghidra │
│ - Set language to ARM Cortex │
│ - Set base address to 0x10000000 │
├─────────────────────────────────────────────────────────────────┤
│ 2. Analyze and resolve functions │
│ - Rename functions to meaningful names │
│ - Fix function signatures │
├─────────────────────────────────────────────────────────────────┤
│ 3. Find the values/instructions to patch │
│ - Look in the assembly listing │
│ - Right-click → Patch Instruction │
├─────────────────────────────────────────────────────────────────┤
│ 4. Export the patched binary │
│ - File → Export Program │
│ - Format: Raw Bytes │
├─────────────────────────────────────────────────────────────────┤
│ 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 LED behavior │
└─────────────────────────────────────────────────────────────────┘
```
### Key Memory Sections
| Section | Location | Contains | Writable? |
| --------- | -------- | ------------------------------ | --------- |
| `.text` | Flash | Code | No |
| `.rodata` | Flash | Constants, strings | No |
| `.data` | RAM | Initialized globals | Yes |
| `.bss` | RAM | Uninitialized globals (zeroed) | Yes |
### Important Ghidra Commands
| Action | How To Do It |
| ----------------- | ------------------------------------- |
| Rename function | Right-click → Edit Function Signature |
| Patch instruction | Right-click → Patch Instruction |
| Export binary | File → Export Program → Raw Bytes |
| Go to address | Press 'G' and enter address |
---
## ✅ Practice Exercises
### Exercise 1: Change the Delay
The LED blinks every 500ms. Find the `sleep_ms(500)` calls in the binary and change them to `sleep_ms(100)` for faster blinking.
**Hint:** Look for the value `0x1F4` (500 in hex) being loaded into a register.
### Exercise 2: Reverse the LED
Instead of GPIO 16 → ON → OFF, make it GPIO 16 → OFF → ON (start with LED on).
**Hint:** Find and swap the two `gpio_put` calls (the ones with values 0 and 1).
### Exercise 3: Add a Second LED
Patch the binary so that BOTH GPIO 16 and GPIO 17 blink together.
**Hint:** You'll need to find space for additional instructions or modify existing ones cleverly.
### Exercise 4: Change the Format String
The program prints "age: %d\r\n". Can you find this string in Ghidra and figure out where it's stored?
**Hint:** Look in the `.rodata` section around address `0x10001xxx`.
---
## 🎓 Key Takeaways
1. **Variables are just memory locations** - The compiler assigns them addresses in SRAM.
2. **Compilers optimize aggressively** - Unused code and values may be removed entirely.
3. **Uninitialized doesn't mean random** - Modern compilers zero out the `.bss` section.
4. **Ghidra works without symbols** - You can analyze any binary, even stripped ones.
5. **Binary patching is powerful** - You can change behavior without source code.
6. **UF2 conversion is required** - The Pico 2 needs UF2 format, not raw binaries.
7. **GPIO is just memory-mapped I/O** - Writing to specific addresses controls hardware.
---
## 📖 Glossary
| Term | Definition |
| ------------------ | --------------------------------------------------------------------- |
| **BSS** | Block Started by Symbol - section for uninitialized global variables |
| **Declaration** | Telling the compiler a variable's name and type |
| **Definition** | Allocating memory for a variable |
| **GPIO** | General Purpose Input/Output - controllable pins on a microcontroller |
| **Initialization** | Assigning an initial value to a variable |
| **Linker** | Tool that combines compiled code and assigns memory addresses |
| **Optimization** | Compiler removing or simplifying code for efficiency |
| **Patching** | Modifying bytes directly in a binary file |
| **rodata** | Read-only data section for constants and string literals |
| **SIO** | Single-cycle I/O - fast GPIO control block in RP2350 |
| **UF2** | USB Flashing Format - file format for Pico 2 firmware |
| **Variable** | A named storage location in memory |
---
## 🔗 Additional Resources
### GPIO Coprocessor Reference
The RP2350 GPIO coprocessor instructions:
| Instruction | Description |
| -------------------------- | ---------------------------- |
| `mcrr p0, #4, Rt, Rt2, c0` | Set/clear GPIO output |
| `mcrr p0, #4, Rt, Rt2, c4` | Set/clear GPIO output enable |
### RP2350 Memory Map Quick Reference
| Address | Description |
| ------------ | ------------------------ |
| `0x10000000` | XIP Flash (code) |
| `0x20000000` | SRAM (data) |
| `0x40028000` | IO_BANK0 (GPIO control) |
| `0x40038000` | PADS_BANK0 (pad control) |
| `0xd0000000` | SIO (single-cycle I/O) |
> 📖 **Datasheet Reference:** Full address map in Section 2.2 (p. 3133). IO_BANK0 at `0x40028000` (p. 605), PADS_BANK0 at `0x40038000` (p. 786), SIO at `0xd0000000`.
---
**Remember:** Every binary you encounter in the real world can be analyzed and understood using these same techniques. Practice makes perfect!
Happy hacking! 🔧
+79
View File
@@ -0,0 +1,79 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Background grid decoration -->
<g opacity="0.06">
<line x1="0" y1="100" x2="1200" y2="100" stroke="#00ff41" stroke-width="1"/>
<line x1="0" y1="200" x2="1200" y2="200" stroke="#00ff41" stroke-width="1"/>
<line x1="0" y1="300" x2="1200" y2="300" stroke="#00ff41" stroke-width="1"/>
<line x1="0" y1="400" x2="1200" y2="400" stroke="#00ff41" stroke-width="1"/>
<line x1="0" y1="500" x2="1200" y2="500" stroke="#00ff41" stroke-width="1"/>
<line x1="0" y1="600" x2="1200" y2="600" stroke="#00ff41" stroke-width="1"/>
<line x1="0" y1="700" x2="1200" y2="700" stroke="#00ff41" stroke-width="1"/>
<line x1="200" y1="0" x2="200" y2="800" stroke="#00ff41" stroke-width="1"/>
<line x1="400" y1="0" x2="400" y2="800" stroke="#00ff41" stroke-width="1"/>
<line x1="600" y1="0" x2="600" y2="800" stroke="#00ff41" stroke-width="1"/>
<line x1="800" y1="0" x2="800" y2="800" stroke="#00ff41" stroke-width="1"/>
<line x1="1000" y1="0" x2="1000" y2="800" stroke="#00ff41" stroke-width="1"/>
</g>
<!-- Hex rain decoration -->
<g opacity="0.04" font-family="'Courier New',monospace" font-size="14" fill="#00ff41">
<text x="50" y="80">4F 70 65 6E 4F 43 44</text>
<text x="900" y="120">10 00 02 34 08 B5 01</text>
<text x="150" y="180">47 44 42 20 52 45 56</text>
<text x="800" y="240">20 08 20 00 FF AA 00</text>
<text x="80" y="350">52 50 32 33 35 30 00</text>
<text x="950" y="380">0A 0A 0F 12 12 1A 1A</text>
<text x="100" y="520">41 52 4D 76 38 2D 4D</text>
<text x="870" y="560">00 FF 41 00 D4 FF 88</text>
<text x="60" y="680">47 48 49 44 52 41 00</text>
<text x="920" y="720">FF 00 40 C0 C0 C0 00</text>
</g>
<!-- Corner accents -->
<polyline points="30,30 30,80 80,80" fill="none" stroke="#00ff41" stroke-width="2" opacity="0.3"/>
<polyline points="1170,30 1170,80 1120,80" fill="none" stroke="#00ff41" stroke-width="2" opacity="0.3"/>
<polyline points="30,770 30,720 80,720" fill="none" stroke="#00ff41" stroke-width="2" opacity="0.3"/>
<polyline points="1170,770 1170,720 1120,720" fill="none" stroke="#00ff41" stroke-width="2" opacity="0.3"/>
<!-- Top accent line -->
<rect x="100" y="140" width="1000" height="2" fill="#00ff41" opacity="0.4"/>
<!-- Course Title -->
<text x="600" y="210" text-anchor="middle" font-family="'Courier New',monospace" font-size="56" font-weight="bold" fill="#00ff41">Embedded Systems</text>
<text x="600" y="278" text-anchor="middle" font-family="'Courier New',monospace" font-size="56" font-weight="bold" fill="#00ff41">Reverse Engineering</text>
<!-- Divider -->
<rect x="300" y="310" width="600" height="2" fill="#00d4ff" opacity="0.6"/>
<!-- Week Number -->
<text x="600" y="380" text-anchor="middle" font-family="'Courier New',monospace" font-size="42" font-weight="bold" fill="#00d4ff">// WEEK 04</text>
<!-- Week Topic -->
<text x="600" y="440" text-anchor="middle" font-family="'Courier New',monospace" font-size="28" fill="#c0c0c0">Variables in Embedded Systems:</text>
<text x="600" y="478" text-anchor="middle" font-family="'Courier New',monospace" font-size="28" fill="#c0c0c0">Debugging and Hacking Variables</text>
<text x="600" y="516" text-anchor="middle" font-family="'Courier New',monospace" font-size="28" fill="#c0c0c0">w/ GPIO Output Basics</text>
<!-- Bottom accent line -->
<rect x="100" y="570" width="1000" height="2" fill="#00ff41" opacity="0.4"/>
<!-- University -->
<text x="600" y="635" text-anchor="middle" font-family="'Courier New',monospace" font-size="36" font-weight="bold" fill="#ffaa00">George Mason University</text>
<!-- Bottom badge -->
<rect x="400" y="670" width="400" height="40" rx="20" fill="none" stroke="#00ff41" stroke-width="1.5" opacity="0.5"/>
<text x="600" y="697" text-anchor="middle" font-family="'Courier New',monospace" font-size="20" fill="#00ff41" opacity="0.7">RP2350 // ARM Cortex-M33</text>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+96
View File
@@ -0,0 +1,96 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">What is a Variable?</text>
<text x="600" y="88" text-anchor="middle" class="dim">Labeled Boxes in Memory (SRAM)</text>
<!-- Memory boxes visualization -->
<rect x="40" y="110" width="1120" height="200" rx="8" class="pnl"/>
<text x="60" y="148" class="sub">Memory — A Row of Numbered Boxes</text>
<!-- Box 0 -->
<rect x="70" y="170" width="170" height="80" rx="6" fill="#0a0a0f" stroke="#00ff41" stroke-width="2"/>
<text x="155" y="207" text-anchor="middle" class="grn">42</text>
<text x="155" y="240" text-anchor="middle" class="dim">age</text>
<text x="155" y="280" text-anchor="middle" class="dim">Box 0</text>
<!-- Box 1 -->
<rect x="260" y="170" width="170" height="80" rx="6" fill="#0a0a0f" stroke="#00d4ff" stroke-width="2"/>
<text x="345" y="207" text-anchor="middle" class="cyn">17</text>
<text x="345" y="240" text-anchor="middle" class="dim">score</text>
<text x="345" y="280" text-anchor="middle" class="dim">Box 1</text>
<!-- Box 2 -->
<rect x="450" y="170" width="170" height="80" rx="6" fill="#0a0a0f" stroke="#ffaa00" stroke-width="2"/>
<text x="535" y="207" text-anchor="middle" class="amb">0</text>
<text x="535" y="240" text-anchor="middle" class="dim">count</text>
<text x="535" y="280" text-anchor="middle" class="dim">Box 2</text>
<!-- Box 3 -->
<rect x="640" y="170" width="170" height="80" rx="6" fill="#0a0a0f" stroke="#ff0040" stroke-width="2"/>
<text x="725" y="207" text-anchor="middle" class="red">255</text>
<text x="725" y="240" text-anchor="middle" class="dim">max</text>
<text x="725" y="280" text-anchor="middle" class="dim">Box 3</text>
<!-- Box 4 -->
<rect x="830" y="170" width="170" height="80" rx="6" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="915" y="207" text-anchor="middle" class="txt">99</text>
<text x="915" y="240" text-anchor="middle" class="dim">temp</text>
<text x="915" y="280" text-anchor="middle" class="dim">Box 4</text>
<!-- Anatomy Panel -->
<rect x="40" y="330" width="1120" height="200" rx="8" class="pnl"/>
<text x="60" y="368" class="sub">Anatomy of a Declaration</text>
<rect x="60" y="390" width="1080" height="50" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="2"/>
<text x="80" y="422" class="grn">uint8_t age = 42;</text>
<text x="80" y="472" class="cyn">uint8_t</text>
<text x="280" y="472" class="dim">Data type (1 byte)</text>
<text x="80" y="502" class="amb">age</text>
<text x="280" y="502" class="dim">Variable name (label)</text>
<text x="600" y="472" class="red">= 42</text>
<text x="760" y="472" class="dim">Initial value</text>
<text x="600" y="502" class="txt">;</text>
<text x="760" y="502" class="dim">End of statement</text>
<!-- Key concepts -->
<rect x="40" y="555" width="540" height="215" rx="8" class="pnl"/>
<text x="60" y="593" class="sub">Key Concepts</text>
<rect x="60" y="610" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="75" y="638" class="grn">Declaration</text>
<text x="290" y="638" class="dim">name + type</text>
<rect x="60" y="660" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="75" y="688" class="cyn">Definition</text>
<text x="290" y="688" class="dim">allocates memory</text>
<rect x="60" y="710" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="75" y="738" class="amb">Initialization</text>
<text x="290" y="738" class="dim">assigns value</text>
<!-- Rule -->
<rect x="620" y="555" width="540" height="215" rx="8" class="pnl"/>
<text x="640" y="593" class="sub">Important Rule</text>
<text x="640" y="640" class="txt">You MUST declare a</text>
<text x="640" y="672" class="txt">variable BEFORE you</text>
<text x="640" y="704" class="txt">use it!</text>
<text x="640" y="748" class="dim">Compiler needs to know the type</text>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+86
View File
@@ -0,0 +1,86 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">Data Types &amp; Sizes</text>
<text x="600" y="88" text-anchor="middle" class="dim">How Much Memory Each Type Uses</text>
<!-- Table -->
<rect x="40" y="110" width="1120" height="660" rx="8" class="pnl"/>
<!-- Header -->
<rect x="60" y="130" width="1080" height="45" rx="4" fill="#1a1a2e"/>
<text x="80" y="160" class="cyn">Type</text>
<text x="300" y="160" class="cyn">Size</text>
<text x="460" y="160" class="cyn">Range</text>
<text x="880" y="160" class="cyn">Description</text>
<!-- uint8_t -->
<rect x="60" y="185" width="1080" height="55" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="80" y="220" class="grn">uint8_t</text>
<text x="300" y="220" class="txt">1 byte</text>
<text x="460" y="220" class="txt">0 — 255</text>
<text x="880" y="220" class="dim">Unsigned 8-bit</text>
<!-- int8_t -->
<rect x="60" y="250" width="1080" height="55" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="80" y="285" class="amb">int8_t</text>
<text x="300" y="285" class="txt">1 byte</text>
<text x="460" y="285" class="txt">-128 — 127</text>
<text x="880" y="285" class="dim">Signed 8-bit</text>
<!-- uint16_t -->
<rect x="60" y="315" width="1080" height="55" rx="4" fill="#0a0a0f" stroke="#00d4ff" stroke-width="1"/>
<text x="80" y="350" class="cyn">uint16_t</text>
<text x="300" y="350" class="txt">2 bytes</text>
<text x="460" y="350" class="txt">0 — 65,535</text>
<text x="880" y="350" class="dim">Unsigned 16-bit</text>
<!-- int16_t -->
<rect x="60" y="380" width="1080" height="55" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="80" y="415" class="amb">int16_t</text>
<text x="300" y="415" class="txt">2 bytes</text>
<text x="460" y="415" class="txt">-32,768 — 32,767</text>
<text x="880" y="415" class="dim">Signed 16-bit</text>
<!-- uint32_t -->
<rect x="60" y="445" width="1080" height="55" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="80" y="480" class="grn">uint32_t</text>
<text x="300" y="480" class="txt">4 bytes</text>
<text x="460" y="480" class="txt">0 — 4,294,967,295</text>
<text x="880" y="480" class="dim">Unsigned 32-bit</text>
<!-- int32_t -->
<rect x="60" y="510" width="1080" height="55" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="80" y="545" class="amb">int32_t</text>
<text x="300" y="545" class="txt">4 bytes</text>
<text x="460" y="545" class="txt">-2.1B — 2.1B</text>
<text x="880" y="545" class="dim">Signed 32-bit</text>
<!-- Visual size comparison -->
<text x="60" y="600" class="sub">Size Comparison</text>
<rect x="60" y="620" width="80" height="40" rx="4" fill="#0f1a0f" stroke="#00ff41" stroke-width="2"/>
<text x="100" y="646" text-anchor="middle" class="dim">1B</text>
<text x="160" y="646" class="grn">uint8_t</text>
<rect x="60" y="670" width="160" height="40" rx="4" fill="#0a0a1f" stroke="#00d4ff" stroke-width="2"/>
<text x="140" y="696" text-anchor="middle" class="dim">2B</text>
<text x="240" y="696" class="cyn">uint16_t</text>
<rect x="60" y="720" width="320" height="40" rx="4" fill="#0a0a0f" stroke="#ffaa00" stroke-width="2"/>
<text x="220" y="746" text-anchor="middle" class="dim">4 Bytes</text>
<text x="400" y="746" class="amb">uint32_t</text>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+63
View File
@@ -0,0 +1,63 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">Memory Sections</text>
<text x="600" y="88" text-anchor="middle" class="dim">Where Variables Live After Compilation</text>
<!-- .data Section -->
<rect x="40" y="110" width="1120" height="170" rx="8" class="pnl"/>
<rect x="60" y="130" width="180" height="40" rx="6" fill="#0f1a0f" stroke="#00ff41" stroke-width="2"/>
<text x="150" y="157" text-anchor="middle" class="grn">.data</text>
<text x="260" y="157" class="txt">Flash -> copied to RAM at startup</text>
<text x="60" y="200" class="dim">Contains: Initialized global/static variables</text>
<text x="60" y="230" class="cyn">int counter = 42;</text>
<text x="60" y="260" class="dim">Initial value stored in flash, copied to SRAM by data_cpy</text>
<!-- .bss Section -->
<rect x="40" y="295" width="1120" height="170" rx="8" class="pnl"/>
<rect x="60" y="315" width="180" height="40" rx="6" fill="#1a0a0a" stroke="#ff0040" stroke-width="2"/>
<text x="150" y="342" text-anchor="middle" class="red">.bss</text>
<text x="260" y="342" class="txt">RAM — zeroed at startup</text>
<text x="60" y="385" class="dim">Contains: Uninitialized global/static variables</text>
<text x="60" y="415" class="cyn">int counter;</text>
<text x="60" y="445" class="dim">NOT stored in binary (saves space!) — memset to 0 at boot</text>
<!-- .rodata Section -->
<rect x="40" y="480" width="1120" height="170" rx="8" class="pnl"/>
<rect x="60" y="500" width="180" height="40" rx="6" fill="#0a0a1f" stroke="#00d4ff" stroke-width="2"/>
<text x="150" y="527" text-anchor="middle" class="cyn">.rodata</text>
<text x="260" y="527" class="txt">Flash — read only</text>
<text x="60" y="570" class="dim">Contains: Constants and string literals</text>
<text x="60" y="600" class="amb">const int MAX = 100;</text>
<text x="60" y="630" class="dim">Lives in flash permanently — cannot be modified at runtime</text>
<!-- Summary Table -->
<rect x="40" y="665" width="1120" height="115" rx="8" class="pnl"/>
<text x="80" y="700" class="grn">.data</text>
<text x="260" y="700" class="txt">RAM</text>
<text x="420" y="700" class="dim">Writable</text>
<text x="600" y="700" class="dim">Initialized globals</text>
<text x="80" y="730" class="red">.bss</text>
<text x="260" y="730" class="txt">RAM</text>
<text x="420" y="730" class="dim">Writable</text>
<text x="600" y="730" class="dim">Uninitialized globals (zeroed)</text>
<text x="80" y="760" class="cyn">.rodata</text>
<text x="260" y="760" class="txt">Flash</text>
<text x="420" y="760" class="dim">Read-only</text>
<text x="600" y="760" class="dim">Constants &amp; strings</text>
</svg>
+79
View File
@@ -0,0 +1,79 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">GPIO Basics</text>
<text x="600" y="88" text-anchor="middle" class="dim">General Purpose Input/Output on RP2350</text>
<!-- Left Panel: Pico 2 Pin Diagram -->
<rect x="40" y="110" width="520" height="300" rx="8" class="pnl"/>
<text x="300" y="148" text-anchor="middle" class="sub">Pico 2 GPIO Pins</text>
<text x="60" y="195" class="grn">GPIO 16</text>
<line x1="230" y1="189" x2="350" y2="189" stroke="#00ff41" stroke-width="2"/>
<text x="370" y="195" class="red">Red LED</text>
<text x="60" y="240" class="grn">GPIO 17</text>
<line x1="230" y1="234" x2="350" y2="234" stroke="#00ff41" stroke-width="2"/>
<text x="370" y="240" class="cyn">Green LED</text>
<text x="60" y="285" class="grn">GPIO 18</text>
<line x1="230" y1="279" x2="350" y2="279" stroke="#00ff41" stroke-width="2"/>
<text x="370" y="285" class="amb">Blue LED</text>
<text x="60" y="330" class="grn">GPIO 25</text>
<line x1="230" y1="324" x2="350" y2="324" stroke="#00ff41" stroke-width="2"/>
<text x="370" y="330" class="txt">Onboard LED</text>
<text x="60" y="385" class="dim">Software-controlled switches</text>
<!-- Right Panel: SDK Functions -->
<rect x="580" y="110" width="580" height="300" rx="8" class="pnl"/>
<text x="870" y="148" text-anchor="middle" class="sub">Pico SDK Functions</text>
<rect x="600" y="170" width="540" height="45" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="615" y="200" class="cyn">gpio_init(pin)</text>
<text x="900" y="200" class="dim">Init pin</text>
<rect x="600" y="225" width="540" height="45" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="615" y="255" class="cyn">gpio_set_dir(pin,d)</text>
<text x="900" y="255" class="dim">I/O dir</text>
<rect x="600" y="280" width="540" height="45" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="615" y="310" class="cyn">gpio_put(pin,val)</text>
<text x="900" y="310" class="dim">Set H/L</text>
<rect x="600" y="335" width="540" height="45" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="615" y="365" class="cyn">sleep_ms(ms)</text>
<text x="900" y="365" class="dim">Delay</text>
<!-- Bottom: LED Blink Code -->
<rect x="40" y="430" width="1120" height="350" rx="8" class="pnl"/>
<text x="60" y="468" class="sub">Basic LED Blink Code</text>
<rect x="60" y="485" width="1080" height="280" rx="6" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="80" y="515" class="cyn">#define LED_PIN 16</text>
<text x="80" y="540" class="txt">int main(void) {</text>
<text x="100" y="565" class="txt">gpio_init(LED_PIN);</text>
<text x="100" y="590" class="txt">gpio_set_dir(LED_PIN, GPIO_OUT);</text>
<text x="100" y="615" class="txt">while (true) {</text>
<text x="120" y="640" class="grn">gpio_put(LED_PIN, 1);</text>
<text x="500" y="640" class="dim">// ON</text>
<text x="120" y="665" class="txt">sleep_ms(500);</text>
<text x="120" y="690" class="red">gpio_put(LED_PIN, 0);</text>
<text x="500" y="690" class="dim">// OFF</text>
<text x="120" y="715" class="txt">sleep_ms(500);</text>
<text x="100" y="740" class="txt">}}</text>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

+79
View File
@@ -0,0 +1,79 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">Ghidra Binary Analysis</text>
<text x="600" y="88" text-anchor="middle" class="dim">Analyzing a Raw .bin Without Symbols</text>
<!-- Step 1: Import -->
<rect x="40" y="110" width="360" height="280" rx="8" class="pnl"/>
<text x="220" y="148" text-anchor="middle" class="sub">1. Import</text>
<rect x="60" y="165" width="320" height="40" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="80" y="192" class="grn">File -> Import</text>
<text x="60" y="232" class="txt">Language:</text>
<text x="60" y="262" class="cyn">ARM Cortex 32 LE</text>
<text x="60" y="302" class="txt">Block:</text>
<text x="200" y="302" class="grn">.text</text>
<text x="60" y="340" class="txt">Base:</text>
<text x="200" y="340" class="amb">10000000</text>
<text x="60" y="370" class="dim">XIP address for RP2350</text>
<!-- Step 2: Analyze -->
<rect x="420" y="110" width="360" height="280" rx="8" class="pnl"/>
<text x="600" y="148" text-anchor="middle" class="sub">2. Analyze</text>
<rect x="440" y="165" width="320" height="40" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="460" y="192" class="grn">Auto-Analyze: Yes</text>
<text x="440" y="235" class="txt">Ghidra finds:</text>
<text x="440" y="270" class="dim">FUN_1000019a</text>
<text x="440" y="300" class="dim">FUN_10000210</text>
<text x="440" y="330" class="dim">FUN_10000234</text>
<text x="440" y="365" class="dim">Auto-generated names</text>
<!-- Step 3: Resolve -->
<rect x="800" y="110" width="360" height="280" rx="8" class="pnl"/>
<text x="980" y="148" text-anchor="middle" class="sub">3. Resolve</text>
<rect x="820" y="165" width="320" height="40" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="840" y="192" class="grn">Edit Function Sig</text>
<text x="820" y="235" class="txt">Rename to:</text>
<text x="820" y="270" class="cyn">data_cpy</text>
<text x="820" y="300" class="cyn">frame_dummy</text>
<text x="820" y="330" class="grn">main</text>
<text x="820" y="365" class="dim">Fix signatures</text>
<!-- Bottom: Decompiled Output -->
<rect x="40" y="410" width="1120" height="370" rx="8" class="pnl"/>
<text x="60" y="448" class="sub">Decompiled main() in Ghidra</text>
<rect x="60" y="465" width="520" height="295" rx="6" fill="#0a0a0f" stroke="#ff0040" stroke-width="1"/>
<text x="80" y="500" class="red">Before Resolving:</text>
<text x="80" y="535" class="txt">void FUN_10000234(void){</text>
<text x="100" y="570" class="dim">FUN_10002f54();</text>
<text x="100" y="605" class="txt">do {</text>
<text x="120" y="640" class="dim">FUN_100030e4(</text>
<text x="140" y="675" class="dim">DAT_10000244,0x2b);</text>
<text x="100" y="710" class="txt">} while(true);</text>
<text x="80" y="740" class="txt">}</text>
<rect x="600" y="465" width="540" height="295" rx="6" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="620" y="500" class="grn">After Resolving:</text>
<text x="620" y="535" class="txt">int main(void) {</text>
<text x="640" y="570" class="cyn">stdio_init_all();</text>
<text x="640" y="605" class="txt">do {</text>
<text x="660" y="640" class="cyn">printf(</text>
<text x="680" y="675" class="amb">"age: %d\r\n"</text>
<text x="680" y="700" class="grn">, 0x2b);</text>
<text x="640" y="725" class="txt">} while(true);</text>
<text x="620" y="750" class="txt">}</text>
</svg>
+77
View File
@@ -0,0 +1,77 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">Compiler Optimization</text>
<text x="600" y="88" text-anchor="middle" class="dim">Why Your Variable Disappeared</text>
<!-- Source Code Panel -->
<rect x="40" y="110" width="540" height="280" rx="8" class="pnl"/>
<text x="310" y="148" text-anchor="middle" class="sub">Source Code</text>
<rect x="60" y="165" width="500" height="205" rx="6" fill="#0a0a0f" stroke="#00d4ff" stroke-width="1"/>
<text x="80" y="195" class="txt">int main(void) {</text>
<text x="100" y="225" class="amb">uint8_t age = 42;</text>
<text x="100" y="255" class="red">age = 43;</text>
<text x="100" y="285" class="txt">stdio_init_all();</text>
<text x="100" y="315" class="txt">while (true)</text>
<text x="120" y="345" class="txt">printf("age: %d", age);</text>
<!-- Compiler Thought Bubble -->
<rect x="620" y="110" width="540" height="280" rx="8" class="pnl"/>
<text x="890" y="148" text-anchor="middle" class="sub">Compiler Thinks...</text>
<rect x="640" y="170" width="500" height="50" rx="4" fill="#0a0a0f" stroke="#ffaa00" stroke-width="2"/>
<text x="655" y="202" class="amb">age = 42 is NEVER read</text>
<rect x="640" y="232" width="500" height="50" rx="4" fill="#0a0a0f" stroke="#ff0040" stroke-width="2"/>
<text x="655" y="264" class="red">Dead store -> REMOVED</text>
<rect x="640" y="294" width="500" height="50" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="2"/>
<text x="655" y="326" class="grn">age = 43 -> constant fold</text>
<text x="640" y="370" class="dim">Replaces variable with literal</text>
<!-- Assembly Result -->
<rect x="40" y="410" width="1120" height="170" rx="8" class="pnl"/>
<text x="600" y="448" text-anchor="middle" class="sub">Resulting Assembly</text>
<rect x="60" y="465" width="1080" height="95" rx="6" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="80" y="498" class="grn">1000023a</text>
<text x="280" y="498" class="amb">2b 21</text>
<text x="420" y="498" class="txt">movs r1, #0x2b</text>
<text x="730" y="498" class="dim">; 0x2b = 43</text>
<text x="80" y="535" class="dim">No age=42 instruction — compiler removed it</text>
<!-- Key Takeaway -->
<rect x="40" y="600" width="1120" height="180" rx="8" class="pnl"/>
<text x="600" y="640" text-anchor="middle" class="sub">Key Takeaway</text>
<rect x="60" y="658" width="330" height="105" rx="6" fill="#0a0a0f" stroke="#ff0040" stroke-width="1"/>
<text x="225" y="693" text-anchor="middle" class="red">Source Code</text>
<text x="225" y="723" text-anchor="middle" class="txt">age = 42</text>
<text x="225" y="748" text-anchor="middle" class="txt">age = 43</text>
<text x="425" y="718" class="grn">-></text>
<rect x="460" y="658" width="310" height="105" rx="6" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="615" y="693" text-anchor="middle" class="grn">Binary</text>
<text x="615" y="733" text-anchor="middle" class="amb">movs r1, #0x2b</text>
<rect x="800" y="658" width="340" height="105" rx="6" fill="#0a0a0f" stroke="#1a1a2e" stroke-width="1"/>
<text x="970" y="698" text-anchor="middle" class="cyn">Compiler</text>
<text x="970" y="728" text-anchor="middle" class="dim">Optimizes dead</text>
<text x="970" y="748" text-anchor="middle" class="dim">stores away!</text>
</svg>
+86
View File
@@ -0,0 +1,86 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">Binary Patching</text>
<text x="600" y="88" text-anchor="middle" class="dim">Changing Values in the Binary</text>
<!-- Before Patch -->
<rect x="40" y="110" width="540" height="240" rx="8" class="pnl"/>
<text x="310" y="148" text-anchor="middle" class="sub">Before Patch</text>
<rect x="60" y="168" width="500" height="50" rx="4" fill="#0a0a0f" stroke="#ff0040" stroke-width="2"/>
<text x="80" y="200" class="grn">1000023a</text>
<text x="230" y="200" class="red">2b 21</text>
<text x="330" y="200" class="txt">movs r1,#0x2b</text>
<text x="60" y="248" class="dim">0x2b = 43 decimal</text>
<text x="60" y="278" class="txt">Output:</text>
<text x="200" y="278" class="amb">age: 43</text>
<text x="60" y="308" class="dim">Compiler-optimized constant</text>
<!-- After Patch -->
<rect x="620" y="110" width="540" height="240" rx="8" class="pnl"/>
<text x="890" y="148" text-anchor="middle" class="sub">After Patch</text>
<rect x="640" y="168" width="500" height="50" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="2"/>
<text x="660" y="200" class="grn">1000023a</text>
<text x="810" y="200" class="grn">46 21</text>
<text x="910" y="200" class="txt">movs r1,#0x46</text>
<text x="640" y="248" class="dim">0x46 = 70 decimal</text>
<text x="640" y="278" class="txt">Output:</text>
<text x="780" y="278" class="grn">age: 70</text>
<text x="640" y="308" class="dim">Changed program behavior!</text>
<!-- How To Patch -->
<rect x="40" y="370" width="1120" height="200" rx="8" class="pnl"/>
<text x="600" y="410" text-anchor="middle" class="sub">How to Patch in Ghidra</text>
<rect x="60" y="430" width="240" height="55" rx="6" fill="#0a0a0f" stroke="#00d4ff" stroke-width="1"/>
<text x="180" y="465" text-anchor="middle" class="cyn">1. Find Instr</text>
<text x="320" y="460" class="grn">-></text>
<rect x="370" y="430" width="220" height="55" rx="6" fill="#0a0a0f" stroke="#ffaa00" stroke-width="1"/>
<text x="480" y="465" text-anchor="middle" class="amb">2. Rt-Click</text>
<text x="610" y="460" class="grn">-></text>
<rect x="650" y="430" width="240" height="55" rx="6" fill="#0a0a0f" stroke="#ff0040" stroke-width="1"/>
<text x="770" y="465" text-anchor="middle" class="red">3. Patch Val</text>
<text x="910" y="460" class="grn">-></text>
<rect x="950" y="430" width="180" height="55" rx="6" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="1040" y="465" text-anchor="middle" class="grn">Done!</text>
<text x="60" y="540" class="dim">Patch Instruction: change operand</text>
<!-- Export Steps -->
<rect x="40" y="590" width="1120" height="190" rx="8" class="pnl"/>
<text x="600" y="630" text-anchor="middle" class="sub">Export Patched Binary</text>
<rect x="60" y="650" width="330" height="50" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="225" y="682" text-anchor="middle" class="cyn">File: Export</text>
<rect x="420" y="650" width="300" height="50" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="570" y="682" text-anchor="middle" class="amb">Format: Raw Bytes</text>
<rect x="750" y="650" width="380" height="50" rx="4" fill="#0a0a0f" stroke="#1a1a2e"/>
<text x="940" y="682" text-anchor="middle" class="grn">Save as *-h.bin</text>
<text x="60" y="745" class="dim">Exported binary has your patches</text>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

+99
View File
@@ -0,0 +1,99 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">GPIO Hacking</text>
<text x="600" y="88" text-anchor="middle" class="dim">Patching GPIO 16 to GPIO 17</text>
<!-- Original Code -->
<rect x="40" y="110" width="540" height="280" rx="8" class="pnl"/>
<text x="310" y="148" text-anchor="middle" class="sub">Original: GPIO 16</text>
<text x="60" y="185" class="red">Red LED on pin 16</text>
<rect x="60" y="205" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#ff0040" stroke-width="1"/>
<text x="80" y="233" class="grn">1000023a</text>
<text x="230" y="233" class="red">10 20</text>
<text x="330" y="233" class="txt">movs r0,#0x10</text>
<rect x="60" y="255" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#ff0040" stroke-width="1"/>
<text x="80" y="283" class="grn">10000244</text>
<text x="230" y="283" class="red">10 23</text>
<text x="330" y="283" class="txt">movs r3,#0x10</text>
<rect x="60" y="305" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#ff0040" stroke-width="1"/>
<text x="80" y="333" class="grn">10000252</text>
<text x="230" y="333" class="red">10 24</text>
<text x="330" y="333" class="txt">movs r4,#0x10</text>
<text x="60" y="368" class="dim">0x10 = 16, three locations</text>
<!-- Patched Code -->
<rect x="620" y="110" width="540" height="280" rx="8" class="pnl"/>
<text x="890" y="148" text-anchor="middle" class="sub">Patched: GPIO 17</text>
<text x="640" y="185" class="grn">Green LED on pin 17</text>
<rect x="640" y="205" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="660" y="233" class="grn">1000023a</text>
<text x="810" y="233" class="grn">11 20</text>
<text x="910" y="233" class="txt">movs r0,#0x11</text>
<rect x="640" y="255" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="660" y="283" class="grn">10000244</text>
<text x="810" y="283" class="grn">11 23</text>
<text x="910" y="283" class="txt">movs r3,#0x11</text>
<rect x="640" y="305" width="500" height="42" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="660" y="333" class="grn">10000252</text>
<text x="810" y="333" class="grn">11 24</text>
<text x="910" y="333" class="txt">movs r4,#0x11</text>
<text x="640" y="368" class="dim">0x11 = 17, all patched!</text>
<!-- What Each Patch Does -->
<rect x="40" y="410" width="1120" height="170" rx="8" class="pnl"/>
<text x="600" y="448" text-anchor="middle" class="sub">What Each Patch Controls</text>
<rect x="60" y="468" width="340" height="50" rx="4" fill="#0a0a0f" stroke="#00d4ff" stroke-width="1"/>
<text x="80" y="500" class="cyn">gpio_init</text>
<text x="240" y="500" class="dim">r0</text>
<rect x="420" y="468" width="340" height="50" rx="4" fill="#0a0a0f" stroke="#ffaa00" stroke-width="1"/>
<text x="440" y="500" class="amb">gpio_set_dir</text>
<text x="640" y="500" class="dim">r3</text>
<rect x="780" y="468" width="360" height="50" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="800" y="500" class="grn">gpio_put</text>
<text x="960" y="500" class="dim">r4</text>
<text x="60" y="550" class="dim">ALL pin refs must be patched</text>
<!-- Bonus Patch -->
<rect x="40" y="600" width="1120" height="180" rx="8" class="pnl"/>
<text x="600" y="640" text-anchor="middle" class="sub">Bonus: Change Print Value</text>
<rect x="60" y="660" width="480" height="50" rx="4" fill="#0a0a0f" stroke="#ff0040" stroke-width="1"/>
<text x="80" y="692" class="red">00 21</text>
<text x="170" y="692" class="txt">movs r1,#0x0</text>
<text x="400" y="692" class="dim">age: 0</text>
<text x="560" y="692" class="grn">-></text>
<rect x="600" y="660" width="530" height="50" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="620" y="692" class="grn">42 21</text>
<text x="720" y="692" class="txt">movs r1,#0x42</text>
<text x="960" y="692" class="grn">age: 66</text>
<text x="60" y="748" class="dim">Changed value: 0 to 66 (0x42)</text>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

+78
View File
@@ -0,0 +1,78 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">GPIO Coprocessor</text>
<text x="600" y="88" text-anchor="middle" class="dim">RP2350 Single-Cycle I/O via mcrr</text>
<!-- MCRR Instruction Anatomy -->
<rect x="40" y="110" width="1120" height="195" rx="8" class="pnl"/>
<text x="600" y="148" text-anchor="middle" class="sub">mcrr Instruction Breakdown</text>
<rect x="60" y="168" width="1080" height="50" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="2"/>
<text x="600" y="200" text-anchor="middle" class="grn">mcrr p0, #4, r4, r5, c0</text>
<text x="80" y="250" class="cyn">mcrr</text>
<text x="200" y="250" class="dim">Move to Coprocessor (2 regs)</text>
<text x="80" y="278" class="amb">p0</text>
<text x="200" y="278" class="dim">Coprocessor 0 (GPIO)</text>
<text x="620" y="250" class="grn">r4</text>
<text x="700" y="250" class="dim">GPIO pin number</text>
<text x="620" y="278" class="red">r5</text>
<text x="700" y="278" class="dim">Value (0=LOW, 1=HIGH)</text>
<!-- Two Coprocessor Registers -->
<rect x="40" y="320" width="540" height="210" rx="8" class="pnl"/>
<text x="310" y="358" text-anchor="middle" class="sub">Output Value (c0)</text>
<rect x="60" y="378" width="500" height="50" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="80" y="410" class="grn">mcrr p0,#4,r4,r5,c0</text>
<text x="60" y="455" class="txt">r4 = pin number</text>
<text x="60" y="485" class="txt">r5 = 0 or 1</text>
<text x="60" y="515" class="dim">Controls GPIO output state</text>
<rect x="620" y="320" width="540" height="210" rx="8" class="pnl"/>
<text x="890" y="358" text-anchor="middle" class="sub">Output Enable (c4)</text>
<rect x="640" y="378" width="500" height="50" rx="4" fill="#0a0a0f" stroke="#ffaa00" stroke-width="1"/>
<text x="660" y="410" class="amb">mcrr p0,#4,r4,r5,c4</text>
<text x="640" y="455" class="txt">r4 = pin number</text>
<text x="640" y="485" class="txt">r5 = 1 (enable output)</text>
<text x="640" y="515" class="dim">Sets pin direction to OUTPUT</text>
<!-- GPIO Init Full Sequence -->
<rect x="40" y="545" width="1120" height="235" rx="8" class="pnl"/>
<text x="600" y="583" text-anchor="middle" class="sub">gpio_init(16) Sequence</text>
<rect x="60" y="603" width="340" height="80" rx="6" fill="#0a0a0f" stroke="#00d4ff" stroke-width="1"/>
<text x="230" y="633" text-anchor="middle" class="cyn">Step 1: Config Pad</text>
<text x="230" y="660" text-anchor="middle" class="dim">addr 0x40038044</text>
<rect x="420" y="603" width="340" height="80" rx="6" fill="#0a0a0f" stroke="#ffaa00" stroke-width="1"/>
<text x="590" y="633" text-anchor="middle" class="amb">Step 2: Set Func</text>
<text x="590" y="660" text-anchor="middle" class="dim">FUNCSEL = 5 (SIO)</text>
<rect x="780" y="603" width="340" height="80" rx="6" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="950" y="633" text-anchor="middle" class="grn">Step 3: Enable Out</text>
<text x="950" y="660" text-anchor="middle" class="dim">mcrr p0,#4,r4,r5,c4</text>
<line x1="400" y1="643" x2="420" y2="643" stroke="#00ff41" stroke-width="2"/>
<line x1="760" y1="643" x2="780" y2="643" stroke="#00ff41" stroke-width="2"/>
<text x="60" y="720" class="dim">Pad: clear OD, set IE, clear ISO</text>
<text x="60" y="750" class="dim">SIO = fast single-cycle GPIO access</text>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

+119
View File
@@ -0,0 +1,119 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 800">
<style>
.bg{fill:#0a0a0f}.pnl{fill:#12121a;stroke:#1a1a2e}.hdr{fill:#12121a}
.title{font:bold 42px 'Courier New',monospace;fill:#00ff41}
.sub{font:bold 28px 'Courier New',monospace;fill:#00d4ff}
.txt{font:24px 'Courier New',monospace;fill:#c0c0c0}
.dim{font:20px 'Courier New',monospace;fill:#888}
.grn{font:bold 24px 'Courier New',monospace;fill:#00ff41}
.red{font:bold 24px 'Courier New',monospace;fill:#ff0040}
.cyn{font:bold 24px 'Courier New',monospace;fill:#00d4ff}
.amb{font:bold 24px 'Courier New',monospace;fill:#ffaa00}
.badge{stroke:#00ff41;rx:14}
</style>
<rect class="bg" width="1200" height="800"/>
<!-- Title -->
<text x="600" y="52" text-anchor="middle" class="title">Full Patching Pipeline</text>
<text x="600" y="88" text-anchor="middle" class="dim">End-to-End Binary Hacking Workflow</text>
<!-- Step 1 -->
<rect x="40" y="115" width="365" height="160" rx="8" class="pnl"/>
<rect x="55" y="130" width="40" height="40" rx="20" fill="#0a0a0f" stroke="#00ff41" stroke-width="2"/>
<text x="75" y="158" text-anchor="middle" class="grn">1</text>
<text x="110" y="158" class="sub">Import .bin</text>
<text x="55" y="195" class="txt">Ghidra: Import</text>
<text x="55" y="225" class="cyn">ARM Cortex 32 LE</text>
<text x="55" y="252" class="dim">Base: 0x10000000</text>
<!-- Arrow 1-2 -->
<line x1="405" y1="195" x2="425" y2="195" stroke="#00ff41" stroke-width="2"/>
<!-- Step 2 -->
<rect x="425" y="115" width="345" height="160" rx="8" class="pnl"/>
<rect x="440" y="130" width="40" height="40" rx="20" fill="#0a0a0f" stroke="#00d4ff" stroke-width="2"/>
<text x="460" y="158" text-anchor="middle" class="cyn">2</text>
<text x="495" y="158" class="sub">Analyze</text>
<text x="440" y="195" class="txt">Auto-analyze</text>
<text x="440" y="225" class="txt">Rename functions</text>
<text x="440" y="252" class="dim">Fix signatures</text>
<!-- Arrow 2-3 -->
<line x1="770" y1="195" x2="790" y2="195" stroke="#00ff41" stroke-width="2"/>
<!-- Step 3 -->
<rect x="790" y="115" width="370" height="160" rx="8" class="pnl"/>
<rect x="805" y="130" width="40" height="40" rx="20" fill="#0a0a0f" stroke="#ffaa00" stroke-width="2"/>
<text x="825" y="158" text-anchor="middle" class="amb">3</text>
<text x="860" y="158" class="sub">Find Target</text>
<text x="805" y="195" class="txt">Listing window</text>
<text x="805" y="225" class="txt">Find movs rN,#val</text>
<text x="805" y="252" class="dim">Identify bytes to change</text>
<!-- Step 4 -->
<rect x="40" y="295" width="365" height="160" rx="8" class="pnl"/>
<rect x="55" y="310" width="40" height="40" rx="20" fill="#0a0a0f" stroke="#ff0040" stroke-width="2"/>
<text x="75" y="338" text-anchor="middle" class="red">4</text>
<text x="110" y="338" class="sub">Patch</text>
<text x="55" y="375" class="txt">Right-click:</text>
<text x="55" y="405" class="txt">Patch Instruction</text>
<text x="55" y="432" class="dim">Change operand value</text>
<!-- Arrow 4-5 -->
<line x1="405" y1="375" x2="425" y2="375" stroke="#00ff41" stroke-width="2"/>
<!-- Step 5 -->
<rect x="425" y="295" width="345" height="160" rx="8" class="pnl"/>
<rect x="440" y="310" width="40" height="40" rx="20" fill="#0a0a0f" stroke="#00ff41" stroke-width="2"/>
<text x="460" y="338" text-anchor="middle" class="grn">5</text>
<text x="495" y="338" class="sub">Export</text>
<text x="440" y="375" class="txt">File: Export</text>
<text x="440" y="405" class="amb">Format: Raw Bytes</text>
<text x="440" y="432" class="dim">Save as *-h.bin</text>
<!-- Arrow 5-6 -->
<line x1="770" y1="375" x2="790" y2="375" stroke="#00ff41" stroke-width="2"/>
<!-- Step 6 -->
<rect x="790" y="295" width="370" height="160" rx="8" class="pnl"/>
<rect x="805" y="310" width="40" height="40" rx="20" fill="#0a0a0f" stroke="#00d4ff" stroke-width="2"/>
<text x="825" y="338" text-anchor="middle" class="cyn">6</text>
<text x="860" y="338" class="sub">Convert UF2</text>
<text x="805" y="375" class="txt">uf2conv.py</text>
<text x="805" y="405" class="amb">--family 0xe48bff59</text>
<text x="805" y="432" class="dim">RP2350 family ID</text>
<!-- UF2 Command -->
<rect x="40" y="475" width="1120" height="90" rx="8" class="pnl"/>
<text x="60" y="505" class="sub">UF2 Command</text>
<rect x="60" y="518" width="1080" height="35" rx="4" fill="#0a0a0f" stroke="#00ff41" stroke-width="1"/>
<text x="80" y="542" class="grn">python uf2conv.py file.bin --base 0x10000000 -o hacked.uf2</text>
<!-- Flash & Verify -->
<rect x="40" y="585" width="540" height="195" rx="8" class="pnl"/>
<text x="310" y="623" text-anchor="middle" class="sub">Flash to Pico 2</text>
<text x="60" y="658" class="txt">1. Hold BOOTSEL + USB</text>
<text x="60" y="688" class="txt">2. Drop hacked.uf2</text>
<text x="60" y="718" class="txt">3. Pico reboots hacked</text>
<text x="60" y="748" class="dim">RPI-RP2 drive in BOOTSEL</text>
<!-- Key Memory Sections -->
<rect x="620" y="585" width="540" height="195" rx="8" class="pnl"/>
<text x="890" y="623" text-anchor="middle" class="sub">Key Sections</text>
<text x="640" y="658" class="grn">.text</text>
<text x="800" y="658" class="txt">Flash</text>
<text x="940" y="658" class="dim">Code</text>
<text x="640" y="688" class="cyn">.rodata</text>
<text x="800" y="688" class="txt">Flash</text>
<text x="940" y="688" class="dim">Constants</text>
<text x="640" y="718" class="amb">.data</text>
<text x="800" y="718" class="txt">RAM</text>
<text x="940" y="718" class="dim">Init globals</text>
<text x="640" y="748" class="red">.bss</text>
<text x="800" y="748" class="txt">RAM</text>
<text x="940" y="748" class="dim">Zeroed globals</text>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB