mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-24 08:24:11 +02:00
Refactor E and S
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Non-Credit Practice Exercise 1 Solution: Explore in Ghidra
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Question 1: What does the function return?
|
||||
`stdio_init_all()` returns `_bool` (displayed as `void` in some Ghidra versions). The function signature shows `_bool stdio_init_all(void)`.
|
||||
|
||||
##### Question 2: What parameters does it take?
|
||||
**None** - the function signature shows `(void)` in parentheses, meaning zero parameters.
|
||||
|
||||
##### Question 3: What functions does it call?
|
||||
`stdio_init_all()` calls initialization functions for:
|
||||
- **USB CDC** initialization (USB serial communication)
|
||||
- **UART** initialization (serial pin communication)
|
||||
|
||||
These set up the standard I/O subsystem so that `printf()` can output data.
|
||||
|
||||
##### Question 4: What's the purpose?
|
||||
`stdio_init_all()` initializes **Standard Input/Output** for the Pico 2:
|
||||
- **std** = Standard
|
||||
- **io** = Input/Output
|
||||
|
||||
It sets up both USB CDC and UART communication channels, which allows `printf()` to send output through the serial connection.
|
||||
|
||||
##### Expected Output
|
||||
|
||||
```
|
||||
stdio_init_all() returns: void (_bool)
|
||||
It takes 0 parameters
|
||||
It calls the following functions: USB CDC init, UART init
|
||||
Based on these calls, I believe it initializes: Standard I/O for USB and UART serial communication
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why would we need to initialize standard I/O before using `printf()`?**
|
||||
Without initialization, there is no communication channel configured. `printf()` needs a destination (USB or UART) to send its output. Without `stdio_init_all()`, output has nowhere to go.
|
||||
|
||||
2. **Can you find other functions in the Symbol Tree that might be related to I/O?**
|
||||
Yes - functions like `stdio_usb_init`, `stdio_uart_init`, `__wrap_puts`, and other I/O-related functions appear in the Symbol Tree.
|
||||
|
||||
3. **How does this function support the `printf("hello, world\r\n")` call in main?**
|
||||
It configures the USB and UART hardware so that when `printf()` (optimized to `__wrap_puts`) executes, the characters are transmitted over the serial connection to the host computer.
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Exercise 1: Explore in Ghidra
|
||||
### Non-Credit Practice Exercise 1: Explore in Ghidra
|
||||
|
||||
#### Objective
|
||||
Learn how to navigate Ghidra's Symbol Tree to find and analyze functions, specifically examining the `stdio_init_all` function.
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Non-Credit Practice Exercise 2 Solution: Find Strings in Ghidra
|
||||
|
||||
#### Answers
|
||||
|
||||
##### String Location
|
||||
|
||||
| Item | Answer |
|
||||
|------|--------|
|
||||
| **String Address** | `0x100019CC` |
|
||||
| **Actual String Content** | `"hello, world\r\n"` |
|
||||
| **String Length** | 14 bytes |
|
||||
| **Located In** | Flash memory (XIP region, starts with `0x10000...`) |
|
||||
|
||||
##### Question 1: What is the address and is it Flash or RAM?
|
||||
The string `"hello, world\r\n"` is located at address **`0x100019CC`** in **Flash memory**. We know it is Flash because the address begins with `0x10000...` (the XIP/Execute-In-Place region starts at `0x10000000`). RAM addresses start at `0x20000000`.
|
||||
|
||||
##### Question 2: How many bytes does the string take?
|
||||
**14 bytes** total:
|
||||
- 12 printable characters: `h`, `e`, `l`, `l`, `o`, `,`, ` `, `w`, `o`, `r`, `l`, `d`
|
||||
- 2 special characters: `\r` (carriage return, 0x0D) and `\n` (newline, 0x0A)
|
||||
|
||||
##### Question 3: How many times and which functions reference it?
|
||||
The string is referenced **1 time**, only in the **`main()`** function. The `ldr` instruction at `0x1000023a` loads the string address into register `r0`, which is then passed to `__wrap_puts`.
|
||||
|
||||
##### Question 4: How is the string encoded?
|
||||
The string is encoded in **ASCII**. Each character occupies exactly one byte:
|
||||
- `\r` = `0x0D` (carriage return)
|
||||
- `\n` = `0x0A` (newline/line feed)
|
||||
|
||||
##### Expected Output
|
||||
|
||||
```
|
||||
String Found: "hello, world\r\n"
|
||||
Address: 0x100019CC
|
||||
Located in: Flash (XIP region)
|
||||
Total Size: 14 bytes
|
||||
Referenced by: main()
|
||||
Used in: printf() argument (optimized to __wrap_puts) to print the string in an infinite loop
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why is the string stored in Flash instead of RAM?**
|
||||
String literals are constants that never change. Storing them in Flash (read-only) saves precious RAM for variables and the stack. The XIP feature allows the processor to read directly from Flash.
|
||||
|
||||
2. **What would happen if you tried to modify this string at runtime?**
|
||||
Flash memory is read-only at runtime. Attempting to write to a Flash address would cause a fault. To modify printed output, you must write your new string to SRAM (`0x20000000+`) and redirect the pointer.
|
||||
|
||||
3. **How does the Listing view help you understand string storage?**
|
||||
The Listing view shows the raw hex bytes alongside their ASCII interpretation, letting you see exactly how each character maps to memory and where the string boundaries are.
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Exercise 2: Find Strings in Ghidra
|
||||
### Non-Credit Practice Exercise 2: Find Strings in Ghidra
|
||||
|
||||
#### Objective
|
||||
Learn how to locate and analyze strings in a binary, understanding where they are stored in memory and how they're used.
|
||||
@@ -74,7 +74,7 @@ Now that you're at the string's location:
|
||||
To see where this string is **used**:
|
||||
|
||||
1. In the Listing view where the string is displayed, **right-click** on the string
|
||||
2. Select **References** → **Show References to**
|
||||
2. Select **References** ? **Show References to**
|
||||
3. A dialog should appear showing which functions/instructions reference this string
|
||||
4. This tells you which parts of the code use this string
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Non-Credit Practice Exercise 3 Solution: Find Cross-References in Ghidra
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Fill-in-the-Blank Items
|
||||
|
||||
| Item | Answer |
|
||||
|------|--------|
|
||||
| **Data reference address** | `0x10000244` (DAT_10000244) |
|
||||
| **Number of references** | 1 |
|
||||
| **Reference type** | Read (`ldr` instruction) |
|
||||
| **Function using it** | `main()` |
|
||||
| **Next instruction after ldr** | `bl __wrap_puts` at `0x100015fc` |
|
||||
|
||||
##### Question 1: What is the address of the data reference?
|
||||
The data reference is at **`0x10000244`** (labeled `DAT_10000244` in Ghidra). This location stores the pointer value `0x100019CC`, which is the address of the `"hello, world"` string.
|
||||
|
||||
##### Question 2: How many places reference this data?
|
||||
**1 place** - it is only referenced in the `main()` function via the `ldr` instruction.
|
||||
|
||||
##### Question 3: Is it a read or write operation? Why?
|
||||
It is a **READ** operation. The `ldr` (Load Register) instruction reads the pointer value from `DAT_10000244` into register `r0`. The program needs to read this pointer to pass the string address as an argument to `__wrap_puts`.
|
||||
|
||||
##### Question 4: What happens next?
|
||||
After the `ldr r0, [DAT_10000244]` instruction loads the string address into `r0`, the next instruction is **`bl 0x100015fc <__wrap_puts>`** which calls the `puts` function with `r0` as its argument (the string pointer).
|
||||
|
||||
##### Question 5: Complete Data Flow Chain
|
||||
|
||||
```
|
||||
String "hello, world\r\n" stored at 0x100019CC (Flash)
|
||||
|
|
||||
v
|
||||
Pointer to string stored at DAT_10000244 (0x10000244)
|
||||
|
|
||||
v
|
||||
main() executes: ldr r0, [DAT_10000244] -> r0 = 0x100019CC
|
||||
|
|
||||
v
|
||||
main() executes: bl __wrap_puts -> prints the string
|
||||
|
|
||||
v
|
||||
main() executes: b.n main+6 -> loops back (infinite loop)
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why does the compiler use a pointer (indirect reference) instead of embedding the string address directly in the instruction?**
|
||||
ARM Thumb instructions have limited immediate value sizes. The `ldr` instruction uses a PC-relative offset to reach a nearby literal pool entry (`DAT_10000244`) that holds the full 32-bit address. This pattern allows addressing any location in the 4 GB address space.
|
||||
|
||||
2. **What is a literal pool?**
|
||||
A literal pool is a region of constant data placed near the code that uses it. The compiler stores full 32-bit values here that cannot fit as immediates in Thumb instructions. The `ldr` instruction loads from the literal pool using a small PC-relative offset.
|
||||
|
||||
3. **How does cross-referencing help in reverse engineering?**
|
||||
Cross-references let you trace data flow through a program. Starting from a known string, you can find which functions use it, how data moves between functions, and understand the program's control flow without having source code.
|
||||
+12
-12
@@ -1,10 +1,10 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Exercise 3: Find Cross-References in Ghidra
|
||||
### Non-Credit Practice Exercise 3: Find Cross-References in Ghidra
|
||||
|
||||
#### Objective
|
||||
Learn how to use Ghidra's cross-reference feature to trace how data flows through code, understanding where specific data is read, written, or referenced.
|
||||
@@ -25,17 +25,17 @@ In this exercise, you'll:
|
||||
#### Background: What are Cross-References?
|
||||
|
||||
A **cross-reference** is a link between different parts of the code:
|
||||
- **Code → Data**: An instruction reads or writes data
|
||||
- **Code → Code**: A function calls another function
|
||||
- **Data → Data**: One data item references another
|
||||
- **Code ? Data**: An instruction reads or writes data
|
||||
- **Code ? Code**: A function calls another function
|
||||
- **Data ? Data**: One data item references another
|
||||
|
||||
In this exercise, we're tracking **code → data** references to understand where and how the program uses the "hello, world" string.
|
||||
In this exercise, we're tracking **code ? data** references to understand where and how the program uses the "hello, world" string.
|
||||
|
||||
#### Step-by-Step Instructions
|
||||
|
||||
##### Step 1: Navigate to the main Function
|
||||
|
||||
1. In Ghidra's CodeBrowser, use **Search → For Address or Label** (or press **Ctrl+G**)
|
||||
1. In Ghidra's CodeBrowser, use **Search ? For Address or Label** (or press **Ctrl+G**)
|
||||
2. Type `main` and press Enter
|
||||
3. Ghidra will navigate to the `main` function
|
||||
4. You should see the disassembly in the Listing view (center panel)
|
||||
@@ -167,7 +167,7 @@ Read backwards: `10 00 19 CC` = `0x100019CC`
|
||||
#### Tips and Hints
|
||||
|
||||
- If you right-click and don't see "References", try right-clicking directly on the instruction address instead
|
||||
- You can also use **Search → For Cross References** from the menu for a more advanced search
|
||||
- You can also use **Search ? For Cross References** from the menu for a more advanced search
|
||||
- In the Decompile view (right side), cross-references may be shown in a different format or with different colors
|
||||
- Multi-level references: You can right-click on a data item and then follow the chain to another data item
|
||||
|
||||
@@ -193,12 +193,12 @@ By completing this exercise, you've learned:
|
||||
You should now understand this flow:
|
||||
```
|
||||
String "hello, world" is stored at address 0x100019CC in Flash
|
||||
↓
|
||||
?
|
||||
A pointer to this address is stored at DAT_10000244 in Flash
|
||||
↓
|
||||
?
|
||||
The main() function loads this pointer: ldr r0, [DAT_10000244]
|
||||
↓
|
||||
?
|
||||
main() calls printf with r0 (the string address) as the argument
|
||||
↓
|
||||
?
|
||||
printf() reads the bytes at that address and prints them
|
||||
```
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Non-Credit Practice Exercise 4 Solution: Connect GDB & Basic Exploration
|
||||
|
||||
#### Answers
|
||||
|
||||
##### Question 1: GDB Connection
|
||||
- **Was GDB able to connect to OpenOCD?** Yes, via `target extended-remote localhost:3333`
|
||||
- **Did program stop at breakpoint?** Yes, at `Breakpoint 1, main () at ../0x0001_hello-world.c:4`
|
||||
|
||||
##### Question 2: Memory Address of main
|
||||
- **Address of main's first instruction:** `0x10000234`
|
||||
- **Flash or RAM?** **Flash memory** - the address starts with `0x10000...` (XIP region starting at `0x10000000`)
|
||||
|
||||
##### Question 3: Stack Pointer Value
|
||||
- **SP value at main:** `0x20082000`
|
||||
- **Flash or RAM?** **RAM** - the address starts with `0x20000...` (SRAM starts at `0x20000000`)
|
||||
|
||||
##### Question 4: First Instruction
|
||||
- **First instruction in main:** `push {r3, lr}`
|
||||
- **What does it do?** Saves register `r3` and the Link Register (`lr`) onto the stack. This preserves the return address so `main()` can call other functions (like `stdio_init_all()` and `__wrap_puts`) and they can properly use `lr` themselves.
|
||||
|
||||
##### Question 5: Comparison to Ghidra
|
||||
**Yes, they match.** The GDB disassembly output is identical to what Ghidra shows in the Listing View. Both static analysis (Ghidra) and dynamic analysis (GDB) reveal the same instructions.
|
||||
|
||||
##### Register Values at Breakpoint
|
||||
|
||||
| Register | Value | Description |
|
||||
|----------|-------|-------------|
|
||||
| **pc** | `0x10000234` | Program Counter - at start of main (Flash) |
|
||||
| **sp** | `0x20082000` | Stack Pointer - top of stack (RAM) |
|
||||
| **lr** | `0x1000018f` | Link Register - return address after main |
|
||||
| **r0** | `0x0` | General Purpose - will hold function arguments |
|
||||
| **r1** | `0x10000235` | General Purpose |
|
||||
| **r2** | `0x80808080` | General Purpose |
|
||||
| **r3** | `0xe000ed08` | General Purpose |
|
||||
|
||||
##### Full Disassembly of main
|
||||
|
||||
```
|
||||
0x10000234 <+0>: push {r3, lr} # Save registers to stack
|
||||
0x10000236 <+2>: bl 0x1000156c <stdio_init_all> # Initialize I/O
|
||||
0x1000023a <+6>: ldr r0, [pc, #8] # Load string pointer
|
||||
0x1000023c <+8>: bl 0x100015fc <__wrap_puts> # Print string
|
||||
0x10000240 <+12>: b.n 0x1000023a <main+6> # Infinite loop
|
||||
0x10000242 <+14>: nop
|
||||
0x10000244 <+16>: (data: pointer to string)
|
||||
```
|
||||
|
||||
##### GDB Connection Sequence
|
||||
|
||||
```
|
||||
Terminal 1: openocd -s "..." -f interface/cmsis-dap.cfg -f target/rp2350.cfg
|
||||
Terminal 2: arm-none-eabi-gdb build/0x0001_hello-world.elf
|
||||
(gdb) target extended-remote localhost:3333
|
||||
(gdb) monitor reset halt
|
||||
(gdb) b main
|
||||
(gdb) c
|
||||
(gdb) disassemble main
|
||||
(gdb) i r
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why does the stack pointer start at `0x20082000`?**
|
||||
The initial stack pointer value comes from the first entry in the vector table at `0x10000000`. The linker script sets `__StackTop` to `0x20082000`, which is the top of the SCRATCH_Y region in SRAM. The stack grows downward from this address.
|
||||
|
||||
2. **Why does `push {r3, lr}` save `r3` even though it doesn't seem to be used?**
|
||||
ARM requires 8-byte stack alignment. Pushing `lr` alone would only move SP by 4 bytes. Including `r3` ensures the stack remains 8-byte aligned, which is required by the ARM Architecture Procedure Call Standard (AAPCS).
|
||||
|
||||
3. **How does the infinite loop work?**
|
||||
The instruction at `0x10000240` is `b.n 0x1000023a` - an unconditional branch back to `main+6`, which reloads the string pointer and calls `__wrap_puts` again. The function never returns.
|
||||
+8
-8
@@ -1,10 +1,10 @@
|
||||
# Embedded Systems Reverse Engineering
|
||||
# Embedded Systems Reverse Engineering
|
||||
[Repository](https://github.com/mytechnotalent/Embedded-Hacking)
|
||||
|
||||
## Week 1
|
||||
Introduction and Overview of Embedded Reverse Engineering: Ethics, Scoping, and Basic Concepts
|
||||
|
||||
### Exercise 4: Connect GDB & Basic Exploration
|
||||
### Non-Credit Practice Exercise 4: Connect GDB & Basic Exploration
|
||||
|
||||
#### Objective
|
||||
Set up GDB (GNU Debugger) to dynamically analyze the "hello, world" program running on your Pico 2, verifying that your debugging setup works correctly.
|
||||
@@ -350,12 +350,12 @@ This is useful when you want to break on a condition rather than every time.
|
||||
#### Summary
|
||||
|
||||
By completing this exercise, you've:
|
||||
1. ✅ Set up OpenOCD as a debug server
|
||||
2. ✅ Connected GDB to a Pico 2 board
|
||||
3. ✅ Set a breakpoint and halted execution
|
||||
4. ✅ Examined assembly language in a live debugger
|
||||
5. ✅ Viewed CPU registers and their values
|
||||
6. ✅ Verified your dynamic debugging setup works
|
||||
1. ? Set up OpenOCD as a debug server
|
||||
2. ? Connected GDB to a Pico 2 board
|
||||
3. ? Set a breakpoint and halted execution
|
||||
4. ? Examined assembly language in a live debugger
|
||||
5. ? Viewed CPU registers and their values
|
||||
6. ? Verified your dynamic debugging setup works
|
||||
|
||||
You're now ready for Week 2, where you'll:
|
||||
- Step through code line by line
|
||||
|
||||
Reference in New Issue
Block a user