Updated DS refs
@@ -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.
|
||||
@@ -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.)
|
||||
@@ -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.
|
||||
@@ -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.)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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 0–255 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 0–65535), (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.
|
||||
@@ -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!
|
||||
@@ -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. 101–104). 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. 31–33). 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! 🔧
|
||||
@@ -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 |
@@ -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 |
@@ -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 & 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 |
@@ -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 & strings</text>
|
||||
</svg>
|
||||
@@ -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 |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |