mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-08-28 05:40:36 +02:00
Refactor E and S
This commit is contained in:
+37
-37
@@ -1,13 +1,13 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 7
|
||||
Constants in Embedded Systems: Debugging and Hacking Constants w/ 1602 LCD I2C Basics
|
||||
|
||||
### Exercise 3: Trace the I²C Struct Pointer Chain
|
||||
### Non-Credit Practice Exercise 3: Trace the I²C Struct Pointer Chain
|
||||
|
||||
#### Objective
|
||||
Use GDB to follow the `i2c1_inst` struct pointer chain from the code instruction that loads it, through the struct in SRAM, to the hardware registers at `0x40098000`. Document every step of the chain: `I2C_PORT` → `i2c1` → `&i2c1_inst` → `hw` → `0x40098000`, and verify each pointer and value in memory.
|
||||
Use GDB to follow the `i2c1_inst` struct pointer chain from the code instruction that loads it, through the struct in SRAM, to the hardware registers at `0x40098000`. Document every step of the chain: `I2C_PORT` ? `i2c1` ? `&i2c1_inst` ? `hw` ? `0x40098000`, and verify each pointer and value in memory.
|
||||
|
||||
#### Prerequisites
|
||||
- Completed Week 7 tutorial (Parts 3-4 on structs and the macro chain)
|
||||
@@ -16,7 +16,7 @@ Use GDB to follow the `i2c1_inst` struct pointer chain from the code instruction
|
||||
- Understanding of C pointers and structs
|
||||
|
||||
#### Task Description
|
||||
The Pico SDK uses a chain of macros and structs to abstract hardware access. When you write `I2C_PORT` in C, it expands through multiple macro definitions to ultimately become a pointer to an `i2c_inst_t` struct in SRAM, which in turn contains a pointer to the I²C hardware registers. You will trace this entire chain in GDB, examining each link to understand how the SDK connects your code to silicon.
|
||||
The Pico SDK uses a chain of macros and structs to abstract hardware access. When you write `I2C_PORT` in C, it expands through multiple macro definitions to ultimately become a pointer to an `i2c_inst_t` struct in SRAM, which in turn contains a pointer to the I²C hardware registers. You will trace this entire chain in GDB, examining each link to understand how the SDK connects your code to silicon.
|
||||
|
||||
#### Step-by-Step Instructions
|
||||
|
||||
@@ -70,13 +70,13 @@ Look for the instruction that loads the `i2c1_inst` pointer into `r0`:
|
||||
|
||||
##### Step 3: Follow the PC-Relative Load
|
||||
|
||||
The `ldr r0, [pc, #offset]` instruction loads a value from a **literal pool** — a data area near the code. Examine what's at the literal pool:
|
||||
The `ldr r0, [pc, #offset]` instruction loads a value from a **literal pool** — a data area near the code. Examine what's at the literal pool:
|
||||
|
||||
```gdb
|
||||
(gdb) x/4wx 0x100002a8
|
||||
```
|
||||
|
||||
Look for a value in the `0x2000xxxx` range — this is the SRAM address of `i2c1_inst`. It should be `0x2000062c`.
|
||||
Look for a value in the `0x2000xxxx` range — this is the SRAM address of `i2c1_inst`. It should be `0x2000062c`.
|
||||
|
||||
##### Step 4: Examine the i2c1_inst Struct in SRAM
|
||||
|
||||
@@ -95,28 +95,28 @@ This maps to the `i2c_inst_t` struct:
|
||||
|
||||
| Offset | Field | Value | Meaning |
|
||||
| ------ | ----------------- | ------------ | -------------------------------- |
|
||||
| `+0x00` | `hw` | `0x40098000` | Pointer to I²C1 hardware regs |
|
||||
| `+0x00` | `hw` | `0x40098000` | Pointer to I²C1 hardware regs |
|
||||
| `+0x04` | `restart_on_next` | `0x00000000` | `false` (no pending restart) |
|
||||
|
||||
##### Step 5: Follow the hw Pointer to Hardware Registers
|
||||
|
||||
The first member of the struct (`hw`) points to `0x40098000` — the I²C1 hardware register block. Examine it:
|
||||
The first member of the struct (`hw`) points to `0x40098000` — the I²C1 hardware register block. Examine it:
|
||||
|
||||
```gdb
|
||||
(gdb) x/8wx 0x40098000
|
||||
```
|
||||
|
||||
You should see the I²C1 control and status registers:
|
||||
You should see the I²C1 control and status registers:
|
||||
|
||||
| Offset | Register | Description |
|
||||
| ------ | -------------- | ------------------------------ |
|
||||
| `+0x00` | IC_CON | I²C control register |
|
||||
| `+0x00` | IC_CON | I²C control register |
|
||||
| `+0x04` | IC_TAR | Target address register |
|
||||
| `+0x08` | IC_SAR | Slave address register |
|
||||
| `+0x0C` | (reserved) | — |
|
||||
| `+0x0C` | (reserved) | — |
|
||||
| `+0x10` | IC_DATA_CMD | Data command register |
|
||||
|
||||
##### Step 6: Verify the I²C Target Address
|
||||
##### Step 6: Verify the I²C Target Address
|
||||
|
||||
After `i2c_init` and `lcd_i2c_init` have run, check the target address register:
|
||||
|
||||
@@ -134,7 +134,7 @@ Then examine IC_TAR:
|
||||
(gdb) x/1wx 0x40098004
|
||||
```
|
||||
|
||||
You should see `0x27` (or a value containing 0x27) — this is the LCD's I²C address!
|
||||
You should see `0x27` (or a value containing 0x27) — this is the LCD's I²C address!
|
||||
|
||||
##### Step 7: Document the Complete Chain
|
||||
|
||||
@@ -142,38 +142,38 @@ Create a diagram of the complete pointer chain:
|
||||
|
||||
```
|
||||
Your Code: I2C_PORT
|
||||
│
|
||||
▼ (preprocessor macro)
|
||||
¦
|
||||
? (preprocessor macro)
|
||||
i2c1
|
||||
│
|
||||
▼ (macro: #define i2c1 (&i2c1_inst))
|
||||
¦
|
||||
? (macro: #define i2c1 (&i2c1_inst))
|
||||
&i2c1_inst = 0x2000062c (SRAM address)
|
||||
│
|
||||
▼ (struct member access)
|
||||
¦
|
||||
? (struct member access)
|
||||
i2c1_inst.hw = 0x40098000 (hardware register base)
|
||||
│
|
||||
▼ (memory-mapped I/O)
|
||||
I²C1 Hardware Registers (silicon)
|
||||
│
|
||||
├── IC_CON at 0x40098000
|
||||
├── IC_TAR at 0x40098004
|
||||
├── IC_DATA_CMD at 0x40098010
|
||||
└── ...
|
||||
¦
|
||||
? (memory-mapped I/O)
|
||||
I²C1 Hardware Registers (silicon)
|
||||
¦
|
||||
+-- IC_CON at 0x40098000
|
||||
+-- IC_TAR at 0x40098004
|
||||
+-- IC_DATA_CMD at 0x40098010
|
||||
+-- ...
|
||||
```
|
||||
|
||||
##### Step 8: Compare with I²C0
|
||||
##### Step 8: Compare with I²C0
|
||||
|
||||
The Pico 2 has two I²C controllers. Find the `i2c0_inst` struct and compare:
|
||||
The Pico 2 has two I²C controllers. Find the `i2c0_inst` struct and compare:
|
||||
|
||||
```gdb
|
||||
(gdb) x/2wx 0x20000628
|
||||
```
|
||||
|
||||
If I²C0's struct is at a nearby address, you should see:
|
||||
- `hw` pointing to `0x40090000` (I²C0 base, different from I²C1's `0x40098000`)
|
||||
If I²C0's struct is at a nearby address, you should see:
|
||||
- `hw` pointing to `0x40090000` (I²C0 base, different from I²C1's `0x40098000`)
|
||||
- `restart_on_next` = 0
|
||||
|
||||
This demonstrates how the SDK uses the same struct layout for both I²C controllers, with only the hardware pointer changing.
|
||||
This demonstrates how the SDK uses the same struct layout for both I²C controllers, with only the hardware pointer changing.
|
||||
|
||||
#### Expected Output
|
||||
|
||||
@@ -189,18 +189,18 @@ After completing this exercise, you should be able to:
|
||||
|
||||
###### Question 2: The `hw` pointer stores `0x40098000`. In the binary, this appears as bytes `00 80 09 40`. Why is the byte order reversed from how we write the address?
|
||||
|
||||
###### Question 3: If you changed the `hw` pointer at `0x2000062c` from `0x40098000` to `0x40090000` using GDB (`set {int}0x2000062c = 0x40090000`), what I²C controller would the program use? What would happen to the LCD?
|
||||
###### Question 3: If you changed the `hw` pointer at `0x2000062c` from `0x40098000` to `0x40090000` using GDB (`set {int}0x2000062c = 0x40090000`), what I²C controller would the program use? What would happen to the LCD?
|
||||
|
||||
###### Question 4: The macro chain has 4 levels of indirection (I2C_PORT → i2c1 → &i2c1_inst → hw → registers). Is this typical for embedded SDKs? What are the trade-offs of this approach?
|
||||
###### Question 4: The macro chain has 4 levels of indirection (I2C_PORT ? i2c1 ? &i2c1_inst ? hw ? registers). Is this typical for embedded SDKs? What are the trade-offs of this approach?
|
||||
|
||||
#### Tips and Hints
|
||||
- Use `x/wx` to examine 32-bit words (pointers are 32 bits on ARM Cortex-M33)
|
||||
- SRAM addresses start with `0x20xxxxxx`; hardware register addresses start with `0x40xxxxxx`
|
||||
- The literal pool (where PC-relative loads get their data) is usually right after the function's code
|
||||
- `i2c_inst_t` is only 8 bytes: 4-byte pointer + 4-byte bool (padded to 4 bytes for alignment)
|
||||
- I²C0 base = `0x40090000`, I²C1 base = `0x40098000` — they are `0x8000` bytes apart
|
||||
- I²C0 base = `0x40090000`, I²C1 base = `0x40098000` — they are `0x8000` bytes apart
|
||||
|
||||
#### Next Steps
|
||||
- Proceed to Exercise 4 to patch the LCD to display your own custom message
|
||||
- Try modifying the `restart_on_next` field in GDB and observe if it changes I²C behavior
|
||||
- Explore the I²C hardware registers at `0x40098000` — can you read the IC_STATUS register to see if the bus is active?
|
||||
- Try modifying the `restart_on_next` field in GDB and observe if it changes I²C behavior
|
||||
- Explore the I²C hardware registers at `0x40098000` — can you read the IC_STATUS register to see if the bus is active?
|
||||
|
||||
Reference in New Issue
Block a user