Small 6 7 fixes

This commit is contained in:
Kevin Thomas
2026-03-01 22:07:59 -05:00
parent d2685b03f2
commit 786b37ecf3
5 changed files with 183 additions and 124 deletions
+3 -39
View File
@@ -724,7 +724,7 @@ This is an **unconditional branch** back to `0x10000264` (the `movs r1, #42` at
Now for the fun part — we'll patch the `.bin` file directly using a hex editor! Now for the fun part — we'll patch the `.bin` file directly using a hex editor!
> 💡 **Why a hex editor?** GDB lets you modify values **in RAM** at runtime (e.g., `set *(char *)0x200005a8 = 100`), but those changes are **lost when the device reboots** — GDB has no way to write changes back to the `.bin` file on disk. To make **permanent** patches that survive a power cycle, we edit the `.bin` file directly with a hex editor and re-flash it. > 💡 **Why a hex editor?** GDB **cannot write to flash memory** — the `0x10000000+` address range where program instructions live. Trying `set *(char *)0x10000264 = 0x2b` in GDB gives `Writing to flash memory forbidden in this context`. To make **permanent** patches that survive a power cycle, we edit the `.bin` file directly with a hex editor and re-flash it.
### Step 17: Open the Binary in a Hex Editor ### Step 17: Open the Binary in a Hex Editor
@@ -747,30 +747,7 @@ For example:
### Step 19: Hack #1 — Change regular_fav_num from 42 to 43 ### Step 19: Hack #1 — Change regular_fav_num from 42 to 43
#### Test in GDB First (Temporary) From our GDB analysis, we know the instruction at `0x10000264` is:
Before touching the binary file, test the change in GDB to make sure it works. From your GDB session (with the breakpoint at `0x10000264`):
```gdb
(gdb) set *(char *)0x10000264 = 0x2b
(gdb) x/1i 0x10000264
0x10000264 <main+48>: movs r1, #43 @ 0x2b
(gdb) c
```
Check your serial monitor — you should see `regular_fav_num: 43` instead of `42`. It works!
> ⚠️ **This change is temporary.** GDB modified the value in RAM, but the `.bin` file on disk is unchanged. When you reboot the Pico, it will reload the original binary and go back to `42`. To make the change **permanent**, we need to patch the `.bin` file with a hex editor.
#### Make It Permanent in HxD
Now quit GDB and open the hex editor:
```gdb
(gdb) q
```
From GDB, we know the instruction at `0x10000264` is:
``` ```
movs r1, #0x2a → bytes: 2a 21 movs r1, #0x2a → bytes: 2a 21
@@ -812,20 +789,7 @@ This is the 32-bit Thumb-2 encoding of `eor.w r3, r3, #1`. The bytes break down
└─────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────┘
``` ```
#### Test in GDB First (Temporary) To change `eor.w r3, r3, #1` to `eor.w r3, r3, #0` (making XOR do nothing):
```gdb
(gdb) set *(char *)(0x10000286 + 2) = 0x00
(gdb) x/4bx 0x10000286
0x10000286 <main+82>: 0x83 0xf0 0x00 0x03
(gdb) c
```
Now test the button — the LED behavior should be **inverted** (ON when not pressed, OFF when pressed). It works!
> ⚠️ Again, this is **temporary** — reboot and it's gone. Let's make it permanent.
#### Make It Permanent in HxD
The file offset is `0x10000286 - 0x10000000 = 0x286`. The immediate byte is the 3rd byte of the instruction, so: `0x286 + 2 = 0x288`. The file offset is `0x10000286 - 0x10000000 = 0x286`. The immediate byte is the 3rd byte of the instruction, so: `0x286 + 2 = 0x288`.
+4 -3
View File
@@ -146,7 +146,7 @@ file_offset = address - 0x10000000
##### Step 6: Patch String 1 — "Reverse" → "Exploit" ##### Step 6: Patch String 1 — "Reverse" → "Exploit"
1. Open `0x0017_constants.bin` in HxD 1. In HxD, open `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants\build\0x0017_constants.bin`
2. Press **Ctrl+G** and enter offset: `3EE8` 2. Press **Ctrl+G** and enter offset: `3EE8`
3. You should see: `52 65 76 65 72 73 65 00` ("Reverse\0") 3. You should see: `52 65 76 65 72 73 65 00` ("Reverse\0")
4. Replace with: `45 78 70 6C 6F 69 74 00` ("Exploit\0") 4. Replace with: `45 78 70 6C 6F 69 74 00` ("Exploit\0")
@@ -161,8 +161,9 @@ file_offset = address - 0x10000000
1. Click **File****Save As**`0x0017_constants-h.bin` 1. Click **File****Save As**`0x0017_constants-h.bin`
```bash ```powershell
python ../uf2conv.py build/0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build/hacked.uf2 cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants
python ..\uf2conv.py build\0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
``` ```
##### Step 9: Flash and Verify ##### Step 9: Flash and Verify
+1 -1
View File
@@ -88,7 +88,7 @@ Many results will be garbage (non-ASCII data interpreted as text), but real stri
##### Step 5: Open the Binary in a Hex Editor ##### Step 5: Open the Binary in a Hex Editor
1. Open `0x0017_constants.bin` in HxD 1. Open `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants\build\0x0017_constants.bin` in HxD
2. Switch to the "Text" pane (right side) to see ASCII representation 2. Switch to the "Text" pane (right side) to see ASCII representation
3. Scroll through the binary and look for readable text sequences 3. Scroll through the binary and look for readable text sequences
+4 -3
View File
@@ -68,7 +68,7 @@ If your message is shorter than 11 characters, fill the remaining bytes with `0x
##### Step 3: Open the Binary and Navigate ##### Step 3: Open the Binary and Navigate
1. Open `0x0017_constants.bin` in HxD 1. In HxD, open `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants\build\0x0017_constants.bin`
2. Press **Ctrl+G** and enter offset: `3EE8` (Line 1: "Reverse") 2. Press **Ctrl+G** and enter offset: `3EE8` (Line 1: "Reverse")
3. Verify you see: `52 65 76 65 72 73 65 00` ("Reverse\0") 3. Verify you see: `52 65 76 65 72 73 65 00` ("Reverse\0")
@@ -100,8 +100,9 @@ After: 48 69 00 00 00 00 00 00 (Hi\0\0\0\0\0\0)
##### Step 7: Convert to UF2 and Flash ##### Step 7: Convert to UF2 and Flash
```bash ```powershell
python ../uf2conv.py build/0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build/hacked.uf2 cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants
python ..\uf2conv.py build\0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
``` ```
1. Hold BOOTSEL and plug in your Pico 2 1. Hold BOOTSEL and plug in your Pico 2
+171 -78
View File
@@ -203,7 +203,7 @@ Writing `struct student` everywhere is tedious. The `typedef` keyword creates an
typedef struct student student_t; typedef struct student student_t;
// Now you can write: // Now you can write:
student_t alice; // Instead of: struct student alice; student_t alice; // Instead of: struct student alice;
``` ```
### Forward Declaration ### Forward Declaration
@@ -211,7 +211,7 @@ student_t alice; // Instead of: struct student alice;
Sometimes you need to tell the compiler "this struct exists" before defining it: Sometimes you need to tell the compiler "this struct exists" before defining it:
```c ```c
typedef struct i2c_inst i2c_inst_t; // Forward declaration + alias typedef struct i2c_inst i2c_inst_t; // Forward declaration + alias
// Later, the full definition: // Later, the full definition:
struct i2c_inst { struct i2c_inst {
@@ -497,25 +497,71 @@ arm-none-eabi-gdb build/0x0017_constants.elf
Let's examine the main function. Disassemble from the entry point: Let's examine the main function. Disassemble from the entry point:
``` ```
x/60i 0x10000234 x/54i 0x10000234
``` ```
You should see output like: You should see output like:
``` ```
0x10000234: push {r4, r5, r6, r7, lr} 0x10000234 <main>: push {r3, lr}
0x10000236: sub sp, #12 0x10000236 <main+2>: bl 0x100037fc <stdio_init_all>
0x10000238: bl 0x10003014 ; stdio_init_all 0x1000023a <main+6>: ldr r1, [pc, #104] @ (0x100002a4 <main+112>)
0x1000023c: ldr r0, [pc, #108] ; Load i2c1_inst pointer 0x1000023c <main+8>: ldr r0, [pc, #104] @ (0x100002a8 <main+116>)
0x1000023e: ldr r1, =0x186A0 ; 100000 (baud rate) 0x1000023e <main+10>: bl 0x10003cdc <i2c_init>
0x10000240: bl 0x100002b4 ; i2c_init 0x10000242 <main+14>: movs r1, #3
0x10000244: movs r0, #2 ; GPIO 2 (SDA) 0x10000244 <main+16>: movs r0, #2
0x10000246: movs r1, #3 ; GPIO_FUNC_I2C 0x10000246 <main+18>: bl 0x100008f0 <gpio_set_function>
0x10000248: bl 0x100002c8 ; gpio_set_function 0x1000024a <main+22>: movs r1, #3
0x1000024c: movs r0, #3 ; GPIO 3 (SCL) 0x1000024c <main+24>: mov r0, r1
0x1000024e: movs r1, #3 ; GPIO_FUNC_I2C 0x1000024e <main+26>: bl 0x100008f0 <gpio_set_function>
0x10000250: bl 0x100002c8 ; gpio_set_function 0x10000252 <main+30>: movs r2, #0
... 0x10000254 <main+32>: movs r1, #1
0x10000256 <main+34>: movs r0, #2
0x10000258 <main+36>: bl 0x1000092c <gpio_set_pulls>
0x1000025c <main+40>: movs r2, #0
0x1000025e <main+42>: movs r1, #1
0x10000260 <main+44>: movs r0, #3
0x10000262 <main+46>: bl 0x1000092c <gpio_set_pulls>
0x10000266 <main+50>: movs r3, #8
0x10000268 <main+52>: movs r2, #4
0x1000026a <main+54>: movs r1, #39 @ 0x27
0x1000026c <main+56>:
ldr r0, [pc, #56] @ (0x100002a8 <main+116>)
0x1000026e <main+58>: bl 0x100002bc <lcd_i2c_init>
0x10000272 <main+62>: movs r1, #0
0x10000274 <main+64>: mov r0, r1
0x10000276 <main+66>: bl 0x100006f4 <lcd_set_cursor>
0x1000027a <main+70>:
ldr r0, [pc, #48] @ (0x100002ac <main+120>)
0x1000027c <main+72>: bl 0x100007f0 <lcd_puts>
0x10000280 <main+76>: movs r0, #1
0x10000282 <main+78>: movs r1, #0
0x10000284 <main+80>: bl 0x100006f4 <lcd_set_cursor>
0x10000288 <main+84>:
ldr r0, [pc, #36] @ (0x100002b0 <main+124>)
0x1000028a <main+86>: bl 0x100007f0 <lcd_puts>
0x1000028e <main+90>: movs r1, #42 @ 0x2a
0x10000290 <main+92>:
ldr r0, [pc, #32] @ (0x100002b4 <main+128>)
0x10000292 <main+94>: bl 0x1000398c <__wrap_printf>
0x10000296 <main+98>: movw r1, #1337 @ 0x539
0x1000029a <main+102>:
ldr r0, [pc, #28] @ (0x100002b8 <main+132>)
0x1000029c <main+104>: bl 0x1000398c <__wrap_printf>
0x100002a0 <main+108>: b.n 0x1000028e <main+90>
0x100002a2 <main+110>: nop
0x100002a4 <main+112>: strh r0, [r4, #52] @ 0x34
0x100002a6 <main+114>: movs r1, r0
0x100002a8 <main+116>: lsls r4, r5, #24
0x100002aa <main+118>: movs r0, #0
0x100002ac <main+120>: subs r6, #232 @ 0xe8
0x100002ae <main+122>: asrs r0, r0, #32
0x100002b0 <main+124>: subs r6, #240 @ 0xf0
0x100002b2 <main+126>: asrs r0, r0, #32
0x100002b4 <main+128>: subs r6, #252 @ 0xfc
0x100002b6 <main+130>: asrs r0, r0, #32
0x100002b8 <main+132>: subs r7, #12
0x100002ba <main+134>: asrs r0, r0, #32
``` ```
### Step 5: Set a Breakpoint at Main ### Step 5: Set a Breakpoint at Main
@@ -527,12 +573,18 @@ c
GDB responds: GDB responds:
``` ```
Breakpoint 1 at 0x10000234 Breakpoint 1 at 0x10000234: file C:/Users/flare-vm/Desktop/Embedded-Hacking-main/0x0017_constants/0x0017_constants.c, line 16.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) c
Continuing. Continuing.
Breakpoint 1, 0x10000234 in ?? () Thread 1 "rp2350.cm0" hit Breakpoint 1, main ()
at C:/Users/flare-vm/Desktop/Embedded-Hacking-main/0x0017_constants/0x0017_constants.c:16
16 stdio_init_all();
``` ```
> ⚠️ **Note:** If GDB says `The program is not being run.` when you type `c`, the target hasn't been started yet. Use `monitor reset halt` first, then `c` to continue to your breakpoint.
### Step 6: Find the #define Constant (FAV_NUM) ### Step 6: Find the #define Constant (FAV_NUM)
Step through to the printf call and examine the registers: Step through to the printf call and examine the registers:
@@ -543,41 +595,59 @@ x/20i 0x1000028e
Look for: Look for:
``` ```
0x1000028e: movs r1,#0x2a ; 0x2a = 42 decimal (FAV_NUM) ...
0x1000028e <main+90>: movs r1, #42 @ 0x2a
...
``` ```
The `#define` constant is embedded directly as an immediate value in the instruction! The `#define` constant is embedded directly as an immediate value in the instruction!
### Step 7: Find the const Variable (OTHER_FAV_NUM) ### Step 7: Find the const Variable (OTHER_FAV_NUM)
Continue examining the code: Continue examining the loop body:
``` ```gdb
x/10i 0x10000296 (gdb) x/5i 0x10000296
``` ```
Look for a load instruction: Look for this instruction:
```
0x10000296: ldr r1, [pc, #offset] ; Load OTHER_FAV_NUM from memory
```
The `const` variable is loaded from a memory address because it actually exists in flash!
### Step 8: Examine the const Value in Memory
Find where OTHER_FAV_NUM is stored:
``` ```
x/1wx 0x100002ac ...
0x10000296 <main+98>: movw r1, #1337 @ 0x539
...
``` ```
This should show the address pointing to the value. Then examine that address: **Surprise!** The `const` variable is ALSO embedded as an immediate value — not loaded from memory! The compiler saw that `OTHER_FAV_NUM` is never address-taken (`&OTHER_FAV_NUM` is never used), so it optimized the `const` the same way as `#define` — as a constant embedded directly in the instruction.
``` The difference is the instruction encoding:
x/1wd 0x10003xxx - `FAV_NUM` (42): `movs r1, #0x2a` — 16-bit Thumb instruction (values 0-255)
- `OTHER_FAV_NUM` (1337): `movw r1, #0x539` — 32-bit Thumb-2 instruction (values 0-65535)
> 💡 **Why `movw` instead of `movs`?** The value 1337 doesn't fit in 8 bits (max 255), so the compiler uses `movw` (Move Wide) which can encode any 16-bit immediate (0-65535) in a 32-bit instruction.
### Step 8: Examine the Literal Pool
The literal pool after the loop contains addresses and constants that are too large for regular instruction immediates. Let's examine it:
```gdb
(gdb) x/6wx 0x100002a4
0x100002a4 <main+112>: 0x000186a0 0x2000062c 0x10003ee8 0x10003ef0
0x100002b4 <main+128>: 0x10003efc 0x10003f0c
``` ```
You should see `1337` (or `0x539` in hex). These are the values that `ldr rN, [pc, #offset]` instructions load:
| Literal Pool Addr | Value | Used By |
| ----------------- | -------------- | ------------------------------ |
| `0x100002a4` | `0x000186A0` | I2C baudrate (100000) |
| `0x100002a8` | `0x2000062C` | &i2c1_inst (I2C struct in RAM) |
| `0x100002ac` | `0x10003EE8` | "Reverse" string address |
| `0x100002b0` | `0x10003EF0` | "Engineering" string address |
| `0x100002b4` | `0x10003EFC` | "FAV_NUM: %d\r\n" format str |
| `0x100002b8` | `0x10003F0C` | "OTHER_FAV_NUM: %d\r\n" fmt |
> 💡 **Why does the disassembly at `0x100002a4` show `strh r0, [r4, #52]` instead of data?** Same reason as Week 6 — GDB's `x/i` tries to decode raw data as instructions. Use `x/wx` to see the actual word values.
### Step 9: Examine the I²C Struct ### Step 9: Examine the I²C Struct
@@ -589,8 +659,7 @@ x/2wx 0x2000062c
You should see: You should see:
``` ```
0x2000062c: 0x40098000 ; hw pointer (I2C1 hardware base) 0x2000062c <i2c1_inst>: 0x40098000 0x00000000
0x20000630: 0x00000000 ; restart_on_next = false
``` ```
### Step 10: Examine the LCD String Literals ### Step 10: Examine the LCD String Literals
@@ -624,11 +693,6 @@ si
i r r0 r1 i r r0 r1
``` ```
Observe how:
- `r0` gets the i2c_inst pointer
- `r1` gets the baud rate (100000)
- The function call happens via `bl`
--- ---
## 🔬 Part 9: Understanding the Assembly ## 🔬 Part 9: Understanding the Assembly
@@ -637,21 +701,28 @@ Now that we've explored the binary in GDB, let's make sense of the key patterns
### Step 12: Analyze #define vs const in Assembly ### Step 12: Analyze #define vs const in Assembly
From GDB, we discovered a critical difference: From GDB, we discovered something interesting — **both constants ended up as instruction immediates!**
**For FAV_NUM (42) — a `#define` macro:** **For FAV_NUM (42) — a `#define` macro:**
``` ```
0x1000028e: movs r1, #0x2a ; 0x2a = 42 decimal 0x1000028e: movs r1, #42 @ 0x2a
``` ```
The value is embedded **directly as an immediate** in the instruction. There is no memory location — the preprocessor replaced `FAV_NUM` with `42` before compilation. The value 42 is embedded directly in a 16-bit Thumb instruction. This is expected — `#define` is text replacement, so the compiler never sees `FAV_NUM`, only `42`.
**For OTHER_FAV_NUM (1337) — a `const` variable:** **For OTHER_FAV_NUM (1337) — a `const` variable:**
``` ```
0x10000296: ldr r1, [pc, #offset] ; Load value from flash 0x10000296: movw r1, #1337 @ 0x539
``` ```
The value is **loaded from a memory address** because `const` creates a real variable stored in the `.rodata` section of flash. The value 1337 is ALSO embedded directly in an instruction — but this time a 32-bit Thumb-2 `movw` because the value doesn't fit in 8 bits.
**Why wasn't `const` stored in memory?** In theory, `const int OTHER_FAV_NUM = 1337` creates a variable in the `.rodata` section. But the compiler optimized it away because:
1. We never take the address of `OTHER_FAV_NUM` (no `&OTHER_FAV_NUM`)
2. The value fits in a 16-bit `movw` immediate
3. Loading from an immediate is faster than loading from memory
> 💡 **Key takeaway for reverse engineering:** Don't assume `const` variables will appear as memory loads. Modern compilers aggressively inline constant values. The C keyword `const` is a **source-level** concept — the compiler may or may not honor it in the final binary.
### Step 13: Analyze the I²C Struct Layout ### Step 13: Analyze the I²C Struct Layout
@@ -659,7 +730,7 @@ In GDB, we examined the `i2c1_inst` struct at `0x2000062c`:
```gdb ```gdb
(gdb) x/2wx 0x2000062c (gdb) x/2wx 0x2000062c
0x2000062c: 0x40098000 0x00000000 0x2000062c <i2c1_inst>: 0x40098000 0x00000000
``` ```
This maps to the `i2c_inst_t` struct: This maps to the `i2c_inst_t` struct:
@@ -699,13 +770,13 @@ These are stored consecutively in the `.rodata` section. Note the addresses —
Now for the fun part — we'll patch the `.bin` file directly using a hex editor! Now for the fun part — we'll patch the `.bin` file directly using a hex editor!
> 💡 **Why a hex editor?** GDB can modify values in RAM at runtime, but those changes are lost when the device reboots. To make **permanent** changes, we edit the `.bin` file on disk and re-flash it. > 💡 **Why a hex editor?** GDB **cannot write to flash memory** — the `0x10000000+` address range where program instructions and read-only data live. Trying `set *(char *)0x1000028e = 0x2b` in GDB gives `Writing to flash memory forbidden in this context`. To make **permanent** patches that survive a power cycle, we edit the `.bin` file directly with a hex editor and re-flash it.
### Step 15: Open the Binary in a Hex Editor ### Step 15: Open the Binary in a Hex Editor
1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.) 1. Open **HxD** (or your preferred hex editor: ImHex, 010 Editor, etc.)
2. Click **File****Open** 2. Click **File****Open**
3. Navigate to `0x0017_constants/build/` 3. Navigate to `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants\build\`
4. Open `0x0017_constants.bin` 4. Open `0x0017_constants.bin`
### Step 16: Calculate the File Offset ### Step 16: Calculate the File Offset
@@ -722,7 +793,7 @@ For example:
### Step 17: Hack #1 — Change FAV_NUM from 42 to 43 ### Step 17: Hack #1 — Change FAV_NUM from 42 to 43
From GDB, we know the instruction at `0x1000028e` is: From our GDB analysis, we know the instruction at `0x1000028e` is:
``` ```
movs r1, #0x2a → bytes: 2a 21 movs r1, #0x2a → bytes: 2a 21
@@ -730,37 +801,59 @@ movs r1, #0x2a → bytes: 2a 21
To change the value from 42 (`0x2a`) to 43 (`0x2b`): To change the value from 42 (`0x2a`) to 43 (`0x2b`):
1. In HxD, press **Ctrl+G** (Go to offset) 1. In HxD, open `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants\build\0x0017_constants.bin`
2. Enter offset: `28E` 2. Press **Ctrl+G** (Go to offset)
3. You should see the byte `2A` at this position 3. Enter offset: `28E`
4. Change `2A` to `2B` 4. You should see the byte `2A` at this position
5. The instruction is now `movs r1, #0x2b` (43 in decimal) 5. Change `2A` to `2B`
6. The instruction is now `movs r1, #0x2b` (43 in decimal)
> 🔍 **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a`. > 🔍 **How Thumb encoding works:** In `movs r1, #imm8`, the immediate value is the first byte, and the opcode `21` is the second byte. So the bytes `2a 21` encode `movs r1, #0x2a`.
### Step 18: Hack #2 — Change OTHER_FAV_NUM from 1337 to 1344 ### Step 18: Hack #2 — Change OTHER_FAV_NUM from 1337 to 1344
The `const` value 1337 (`0x539`) is stored in the data section, not as an instruction immediate. From GDB, we found its memory address. #### Understand the Encoding
Examine the literal pool to find where `0x539` is stored: From GDB, we found the `movw r1, #1337` instruction at `0x10000296`. Examine the exact bytes:
```gdb ```gdb
(gdb) x/4wx 0x100002a8 (gdb) x/4bx 0x10000296
0x10000296 <main+98>: 0x40 0xf2 0x39 0x51
``` ```
Look for the word `0x00000539`. Calculate its file offset and patch it: This is the 32-bit Thumb-2 encoding of `movw r1, #0x539` (1337). The bytes break down as:
1. In HxD, go to the offset where `0x539` is stored ```
2. The bytes will be `39 05 00 00` (little-endian) ┌─────────────────────────────────────────────────────────────────┐
3. Change to `40 05 00 00` (`0x540` = 1344 in decimal) │ movw r1, #0x539 → bytes: 40 F2 39 51 │
│ │
│ Byte 0: 0x40 ─┐ │
│ Byte 1: 0xF2 ─┘ First halfword (opcode + upper imm bits) │
│ Byte 2: 0x39 ──── Lower 8 bits of immediate (imm8) ← CHANGE │
│ Byte 3: 0x51 ──── Destination register (r1) + upper imm bits │
│ │
│ imm16 = 0x0539 = 1337 decimal │
│ imm8 field = 0x39 (lower 8 bits of the value) │
│ │
└─────────────────────────────────────────────────────────────────┘
```
> 🔍 **Little-endian byte order:** The RP2350 stores multi-byte values with the least significant byte first. So `0x00000539` appears as `39 05 00 00` in memory. The file offset is `0x10000296 - 0x10000000 = 0x296`. The imm8 byte is the 3rd byte of the instruction: `0x296 + 2 = 0x298`.
To change `movw r1, #1337` to `movw r1, #1344`:
1. In HxD, press **Ctrl+G** (Go to offset)
2. Enter offset: `298` (the third byte of the 4-byte instruction)
3. You should see the byte `39` at this position
4. Change `39` to `40`
> 🔍 **Why offset `0x298` and not `0x296`?** The lower 8 bits of the immediate (`imm8`) are in the **third byte** of the 4-byte `movw` instruction. The instruction starts at file offset `0x296`, so imm8 is at `0x296 + 2 = 0x298`. Changing `0x39` to `0x40` changes the value from `0x539` (1337) to `0x540` (1344).
### Step 19: Hack #3 — Change LCD Text from "Reverse" to "Exploit" ### Step 19: Hack #3 — Change LCD Text from "Reverse" to "Exploit"
**IMPORTANT:** The new string must be the **same length** as the original! "Reverse" and "Exploit" are both 7 characters — perfect! **IMPORTANT:** The new string must be the **same length** as the original! "Reverse" and "Exploit" are both 7 characters — perfect!
From GDB, we know "Reverse" is at address `0x10003ee8`: From our GDB analysis in Step 10, we found the string at `0x10003ee8`. File offset = `0x10003ee8 - 0x10000000 = 0x3EE8`.
1. In HxD, press **Ctrl+G** and enter offset: `3EE8` 1. In HxD, press **Ctrl+G** and enter offset: `3EE8`
2. You should see the bytes for "Reverse": `52 65 76 65 72 73 65 00` 2. You should see the bytes for "Reverse": `52 65 76 65 72 73 65 00`
@@ -792,14 +885,14 @@ From GDB, we know "Reverse" is at address `0x10003ee8`:
Open a terminal and navigate to your project directory: Open a terminal and navigate to your project directory:
```bash ```powershell
cd Embedded-Hacking/0x0017_constants cd C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0017_constants
``` ```
Run the conversion command: Run the conversion command:
```bash ```powershell
python ../uf2conv.py build/0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build/hacked.uf2 python ..\uf2conv.py build\0x0017_constants-h.bin --base 0x10000000 --family 0xe48bff59 --output build\hacked.uf2
``` ```
### Step 22: Flash the Hacked Binary ### Step 22: Flash the Hacked Binary
@@ -839,7 +932,7 @@ OTHER_FAV_NUM: 1344
3. **Explored C structs** - How the Pico SDK abstracts hardware 3. **Explored C structs** - How the Pico SDK abstracts hardware
4. **Mastered the macro chain** - From `I2C_PORT` to `0x40098000` 4. **Mastered the macro chain** - From `I2C_PORT` to `0x40098000`
5. **Examined structs in GDB** - Inspected memory layout of `i2c_inst_t` 5. **Examined structs in GDB** - Inspected memory layout of `i2c_inst_t`
6. **Hacked constant values** - Both immediate and memory-stored using a hex editor 6. **Hacked constant values** - Both `movs` (8-bit) and `movw` (16-bit) immediates using a hex editor
7. **Patched string literals** - Changed LCD display text 7. **Patched string literals** - Changed LCD display text
### #define vs const Summary ### #define vs const Summary
@@ -856,11 +949,11 @@ OTHER_FAV_NUM: 1344
├─────────────────────────────────────────────────────────────────┤ ├─────────────────────────────────────────────────────────────────┤
│ const int OTHER_FAV_NUM = 1337 │ │ const int OTHER_FAV_NUM = 1337 │
│ ────────────────────────────── │ │ ────────────────────────────── │
│ • Real variable in .rodata section │ • Theoretically in .rodata, but compiler optimized it away
│ • Takes memory (4 bytes for int) │ • Value embedded as immediate: movw r1, #0x539 (32-bit instr)
│ • Can take address (&OTHER_FAV_NUM is valid) │ • Optimization: compiler saw &OTHER_FAV_NUM is never used
│ • In binary: value loaded from memory (ldr r1, [address]) │ • In binary: immediate in instruction, same as #define!
│ • To hack: patch the value in the data section │ • To hack: patch instruction operand (imm8 byte at offset +2)
└─────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────┘
``` ```
@@ -899,7 +992,7 @@ OTHER_FAV_NUM: 1344
| ------------ | ---------------------------------- | | ------------ | ---------------------------------- |
| `0x10000234` | main() entry point | | `0x10000234` | main() entry point |
| `0x1000028e` | FAV_NUM value in instruction | | `0x1000028e` | FAV_NUM value in instruction |
| `0x10000296` | OTHER_FAV_NUM load instruction | | `0x10000296` | OTHER_FAV_NUM value in instruction |
| `0x10003ee8` | "Reverse" string literal (example) | | `0x10003ee8` | "Reverse" string literal (example) |
| `0x40098000` | I²C1 hardware registers base | | `0x40098000` | I²C1 hardware registers base |
| `0x2000062C` | i2c1_inst struct in SRAM | | `0x2000062C` | i2c1_inst struct in SRAM |