mirror of
https://github.com/mytechnotalent/Embedded-Hacking.git
synced 2026-05-27 01:32:25 +02:00
Updated WEEK04
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
# 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`. The function signature is `_Bool stdio_init_all(void)`.
|
||||
|
||||
##### Question 2: What parameters does it take?
|
||||
None. The signature uses `(void)`, which means zero parameters.
|
||||
|
||||
##### Question 3: What functions does it call?
|
||||
In this build, it calls `stdio_uart_init()` to initialize serial output.
|
||||
|
||||
##### Question 4: What's the purpose?
|
||||
Its purpose is to initialize standard I/O so `printf()`/`puts()` output can be transmitted over UART.
|
||||
|
||||
##### Expected Output
|
||||
|
||||
```
|
||||
stdio_init_all() returns: _Bool
|
||||
It takes 0 parameters
|
||||
It calls the following functions: UART init
|
||||
Based on these calls, I believe it initializes: Standard I/O for UART serial communication
|
||||
```
|
||||
|
||||
#### Reflection Answers
|
||||
|
||||
1. **Why would we need to initialize standard I/O before using `printf()`?**
|
||||
Without initialization, there is no configured output path. `printf()` needs a destination (UART in this exercise) to transmit characters.
|
||||
|
||||
2. **Can you find other functions in the Symbol Tree that might be related to I/O?**
|
||||
Yes - `stdio_uart_init`, `__wrap_puts`, and related low-level serial/output helpers are I/O-related.
|
||||
|
||||
3. **How does this function support the `printf("hello, world\r\n")` call in main?**
|
||||
It configures the UART output path so when `printf()` (optimized to `__wrap_puts`) runs, the string is sent over serial.
|
||||
@@ -1,122 +0,0 @@
|
||||
# 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: Explore in Ghidra
|
||||
|
||||
#### Objective
|
||||
Learn how to navigate Ghidra's Symbol Tree to find and analyze functions, specifically examining the `stdio_init_all` function.
|
||||
|
||||
#### Prerequisites
|
||||
- Ghidra installed and running
|
||||
- `0x0001_hello-world` project already created and imported in Ghidra
|
||||
- The `0x0001_hello-world.elf` file already imported and analyzed
|
||||
|
||||
#### Task Description
|
||||
|
||||
Your goal is to explore the `stdio_init_all` function in Ghidra and understand what it does based on:
|
||||
1. Its decompiled code
|
||||
2. The functions it calls
|
||||
3. The variables it accesses
|
||||
|
||||
#### Step-by-Step Instructions
|
||||
|
||||
##### Step 1: Open Your Ghidra Project
|
||||
|
||||
1. Launch **Ghidra** on your computer
|
||||
2. In the Ghidra Project Manager window, you should see your `0x0001_hello-world` project
|
||||
3. If you don't see it, create a new project or open an existing one
|
||||
4. **Double-click** on the project to open it
|
||||
|
||||
##### Step 2: Access the Symbol Tree
|
||||
|
||||
In the CodeBrowser window that opens:
|
||||
- Look at the left side panel - you should see several tabs
|
||||
- Find and click on the **Symbol Tree** tab (it might be labeled "Symbol Tree" or showing a tree icon)
|
||||
- If you don't see it, go to **Window → Symbol Tree** in the menu
|
||||
|
||||
##### Step 3: Expand the Functions List
|
||||
|
||||
1. In the Symbol Tree, look for a folder or section labeled **Functions**
|
||||
2. **Click the arrow/triangle** next to "Functions" to expand it
|
||||
3. This will show you a list of all the functions that Ghidra identified in the binary
|
||||
|
||||
##### Step 4: Find the stdio_init_all Function
|
||||
|
||||
1. In the expanded Functions list, scroll through to find `stdio_init_all`
|
||||
2. **Alternative method**: If the list is long, you can use **Search → For Address or Label** from the menu and type `stdio_init_all` to jump directly to it
|
||||
3. Once you find it, **click on it** to navigate to that function in the CodeBrowser
|
||||
|
||||
##### Step 5: Examine the Decompiled Code
|
||||
|
||||
Once you've navigated to `stdio_init_all`:
|
||||
- On the **right side** of the window, you should see the **Decompile** view
|
||||
- This shows the C-like code that Ghidra has reconstructed from the assembly
|
||||
- Read through the decompiled code carefully
|
||||
|
||||
##### Step 6: Answer These Questions
|
||||
|
||||
Based on what you see in the decompiled code, answer the following:
|
||||
|
||||
###### Question 1: What does the function return?
|
||||
Look at the return type at the top of the function. Is it `void`, `int`, `_Bool`, or something else?
|
||||
|
||||
###### Question 2: What parameters does it take?
|
||||
Look at the function signature. Does it take any parameters? (Hint: Look for anything inside the parentheses)
|
||||
|
||||
###### Question 3: What functions does it call?
|
||||
Look for function calls within `stdio_init_all`. What other functions does it call? List them:
|
||||
- Function 1: ________________
|
||||
- Function 2: ________________
|
||||
- Function 3: ________________
|
||||
(There may be more or fewer)
|
||||
|
||||
###### Question 4: What's the purpose?
|
||||
Based on the functions it calls and the overall structure, what do you think `stdio_init_all()` is setting up? Think about what "stdio" stands for:
|
||||
- **std** = Standard
|
||||
- **io** = Input/Output
|
||||
|
||||
What types of I/O might be getting initialized?
|
||||
|
||||
##### Step 7: Explore Called Functions (Optional Challenge)
|
||||
|
||||
If you want to go deeper:
|
||||
|
||||
1. In the Decompile view, **click on one of the functions** that `stdio_init_all` calls
|
||||
2. Ghidra will navigate to that function
|
||||
3. Look at what **that** function does
|
||||
4. Can you build a picture of what's being initialized?
|
||||
|
||||
#### Expected Output
|
||||
|
||||
You should be able to write a brief summary like:
|
||||
|
||||
```
|
||||
stdio_init_all() returns: [your answer]
|
||||
It takes [number] parameters
|
||||
It calls the following functions: [list them]
|
||||
Based on these calls, I believe it initializes: [your analysis]
|
||||
```
|
||||
|
||||
#### Questions for Reflection
|
||||
|
||||
1. Why would we need to initialize standard I/O before using `printf()`?
|
||||
2. Can you find other functions in the Symbol Tree that might be related to I/O?
|
||||
3. How does this function support the `printf("hello, world\r\n")` call in main?
|
||||
|
||||
#### Tips and Hints
|
||||
|
||||
- If you see a function name you don't recognize, you can right-click on it to see more options
|
||||
- The Decompile view is your best friend - it shows you what code is doing in an almost-C format
|
||||
- Don't worry if some variable names are automatic (like `local_4` or `param_1`) - that's normal when symbols aren't available
|
||||
- You can collapse/expand sections in the Decompile view by clicking the arrows next to braces `{}`
|
||||
|
||||
#### Next Steps
|
||||
|
||||
After completing this exercise, you'll have a better understanding of:
|
||||
- How to navigate Ghidra's interface
|
||||
- How to find functions using the Symbol Tree
|
||||
- How to read decompiled code
|
||||
- How initialization functions work in embedded systems
|
||||
@@ -1,56 +0,0 @@
|
||||
# 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 is it referenced, and by which function(s)?
|
||||
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.
|
||||
@@ -1,162 +0,0 @@
|
||||
# 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: 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.
|
||||
|
||||
#### Prerequisites
|
||||
- Ghidra installed with `0x0001_hello-world` project open
|
||||
- Basic familiarity with Ghidra's interface (from Exercise 1)
|
||||
- CodeBrowser window open with the binary loaded
|
||||
|
||||
#### Task Description
|
||||
|
||||
In this exercise, you'll find the "hello, world" string in the binary and determine:
|
||||
1. **Where** it's located in memory (its address)
|
||||
2. **How** it's used by the program
|
||||
3. **What** format it's stored in
|
||||
|
||||
#### Step-by-Step Instructions
|
||||
|
||||
##### Step 1: Open the Defined Strings Window
|
||||
|
||||
1. In the CodeBrowser menu, go to **Window** (top menu bar)
|
||||
2. Look for and click on **Defined Strings**
|
||||
3. A new window should appear showing all strings Ghidra found in the binary
|
||||
|
||||
##### Step 2: Understand the Strings Window
|
||||
|
||||
The Defined Strings window shows:
|
||||
- **Address**: The memory location where the string starts
|
||||
- **String**: The actual text content
|
||||
- **Length**: How many bytes the string uses
|
||||
- **Defined**: Whether Ghidra has marked it as data
|
||||
|
||||
##### Step 3: Search for "hello, world"
|
||||
|
||||
1. In the Defined Strings window, look through the list to find `"hello, world"`
|
||||
2. **Search method**: If the window has a search box at the top, you can type to filter. Otherwise, use **Ctrl+F** to open the search function
|
||||
3. Once you find it, **click on it** to highlight the entry
|
||||
|
||||
##### Step 4: Record the Address
|
||||
|
||||
When you find `"hello, world"`, note down:
|
||||
|
||||
**String Address**: ________________
|
||||
|
||||
**Actual String Content**: ________________
|
||||
|
||||
**String Length**: ________________ bytes
|
||||
|
||||
##### Step 5: Double-Click to Navigate
|
||||
|
||||
1. **Double-click** on the `"hello, world"` entry in the Defined Strings window
|
||||
2. Ghidra will automatically navigate you to that address in the CodeBrowser
|
||||
3. You should see the string displayed in the **Listing** view (center panel)
|
||||
|
||||
##### Step 6: Examine the Listing View
|
||||
|
||||
Now that you're at the string's location:
|
||||
|
||||
1. Look at the **Listing view** (center panel) where the string is shown
|
||||
2. You'll see the string in **hex/ASCII** format
|
||||
3. Notice how it appears in memory - each character takes one byte
|
||||
4. Look for the string content: `hello, world\r\n`
|
||||
5. What comes after the string? (Ghidra may show other data nearby)
|
||||
|
||||
##### Step 7: Look at the Cross-References
|
||||
|
||||
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**
|
||||
3. A dialog should appear showing which functions/instructions reference this string
|
||||
4. This tells you which parts of the code use this string
|
||||
|
||||
##### Step 8: Answer These Questions
|
||||
|
||||
Based on what you found:
|
||||
|
||||
###### Question 1: What is the address, and is it Flash or RAM?
|
||||
- What is the address of the "hello, world" string? __________
|
||||
- Is it in Flash memory (starts with `0x100...`) or RAM (starts with `0x200...`)? __________
|
||||
|
||||
###### Question 2: How many bytes does the string take?
|
||||
- How many bytes does the string take in memory? __________
|
||||
- Can you count the characters? (h-e-l-l-o-,-space-w-o-r-l-d-\r-\n)
|
||||
|
||||
###### Question 3: How many times is it referenced, and by which function(s)?
|
||||
- How many times is this string referenced in the code? __________
|
||||
- Which function(s) reference it? (Hint: Look at the cross-references)
|
||||
|
||||
###### Question 4: How is the string encoded?
|
||||
- How is the string encoded in memory?
|
||||
- Is each character one byte or more? __________
|
||||
- What does `\r` and `\n` represent? (Hint: `\r` = carriage return, `\n` = newline)
|
||||
|
||||
#### Expected Output
|
||||
|
||||
You should be able to fill in a summary like:
|
||||
|
||||
```
|
||||
String Found: "hello, world\r\n"
|
||||
Address: 0x________
|
||||
Located in: [Flash / RAM]
|
||||
Total Size: ________ bytes
|
||||
Referenced by: [Function names]
|
||||
Used in: [How the program uses it]
|
||||
```
|
||||
|
||||
#### Deeper Exploration (Optional Challenge)
|
||||
|
||||
##### Challenge 1: Follow the String Usage
|
||||
1. From the cross-references you found, click on the instruction that uses the string
|
||||
2. You should navigate to the `ldr` (load) instruction that loads the string's address into register `r0`
|
||||
3. This is how the `printf` function gets the pointer to the string!
|
||||
|
||||
##### Challenge 2: Find Other Strings
|
||||
1. Go back to the Defined Strings window
|
||||
2. Look for other strings in the binary
|
||||
3. Are there any other text strings besides "hello, world"?
|
||||
4. If yes, where are they and what are they used for?
|
||||
|
||||
##### Challenge 3: Understand Little-Endian
|
||||
1. When Ghidra shows the string address in the `ldr` instruction, it's showing a number
|
||||
2. Look at the raw bytes of that address value
|
||||
3. Notice how the bytes are stored in "backwards" order? That's little-endian!
|
||||
4. Can you convert the hex bytes to the actual address?
|
||||
|
||||
#### Questions for Reflection
|
||||
|
||||
1. **Why is the string stored in Flash instead of RAM?**
|
||||
2. **What would happen if you tried to modify this string at runtime?**
|
||||
3. **How does the Listing view help you understand string storage?**
|
||||
|
||||
#### Tips and Hints
|
||||
|
||||
- Strings in compiled binaries are often stored in read-only memory (Flash) to save RAM
|
||||
- The `\r` and `\n` characters are special: they're single bytes (0x0D and 0x0A in hex)
|
||||
- When you see a string in Ghidra's listing, the ASCII representation is shown on the right side
|
||||
- You can scroll left/right in the Listing view to see different representations (hex, ASCII, disassembly)
|
||||
|
||||
#### Real-World Application
|
||||
|
||||
Understanding where strings are stored is crucial for:
|
||||
- **Firmware modification**: Finding text messages to modify
|
||||
- **Reverse engineering**: Understanding what a program does by finding its strings
|
||||
- **Vulnerability analysis**: Finding format string bugs or hardcoded credentials
|
||||
- **Localization**: Finding where text needs to be translated
|
||||
|
||||
#### Summary
|
||||
|
||||
By completing this exercise, you've learned:
|
||||
1. How to find strings in a binary using Ghidra's Defined Strings window
|
||||
2. How to determine the memory address of a string
|
||||
3. How to follow cross-references to see where strings are used
|
||||
4. How strings are stored in memory and referenced in code
|
||||
5. The relationship between C code (`printf()`) and assembly (`ldr`)
|
||||
@@ -1,60 +0,0 @@
|
||||
# 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`?
|
||||
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).
|
||||
|
||||
##### Complete the 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.
|
||||
@@ -1,190 +0,0 @@
|
||||
# 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: 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.
|
||||
|
||||
#### Prerequisites
|
||||
- Ghidra installed with `0x0001_hello-world` project open
|
||||
- Completed Exercise 2 (Find Strings) - you should know where the "hello, world" string is located
|
||||
- CodeBrowser window open with the binary loaded
|
||||
|
||||
#### Task Description
|
||||
|
||||
In this exercise, you'll:
|
||||
1. Navigate to a specific data reference in the `main` function
|
||||
2. Find where a particular data item (`DAT_...`) is used
|
||||
3. Trace back to see which functions access this data
|
||||
4. Understand how data flows from memory to the CPU and then to functions
|
||||
|
||||
#### 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
|
||||
|
||||
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**)
|
||||
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)
|
||||
|
||||
##### Step 2: Locate the `ldr` Instruction
|
||||
|
||||
In the main function's disassembly, look for an `ldr` (load register) instruction. It should look something like:
|
||||
|
||||
```
|
||||
ldr r0, [DAT_10000244]
|
||||
```
|
||||
|
||||
or similar. This instruction:
|
||||
- **`ldr`** = load register (read data from memory)
|
||||
- **`r0`** = put the data into register `r0`
|
||||
- **`[DAT_10000244]`** = read from the address stored at location `DAT_10000244`
|
||||
|
||||
##### Step 3: Understand the Notation
|
||||
|
||||
In Ghidra's decompiler notation:
|
||||
- **`DAT_10000244`** = a data item (not code) at address `0x10000244`
|
||||
- **`[...]`** = the address of; accessing memory at that location
|
||||
- The actual value is the address of the "hello, world" string in Flash memory
|
||||
|
||||
##### Step 4: Right-Click on the Data Reference
|
||||
|
||||
1. In the Listing view, find the `ldr` instruction that loads the string address
|
||||
2. **Right-click** on the `DAT_...` part (the data reference)
|
||||
3. A context menu should appear
|
||||
|
||||
##### Step 5: Select "References" Option
|
||||
|
||||
In the context menu:
|
||||
1. Look for an option that says **References**
|
||||
2. Click on it to see a submenu
|
||||
3. Select **Show References to** (this shows "where is this data used?")
|
||||
|
||||
##### Step 6: Review the References Window
|
||||
|
||||
A new window should appear showing all the locations where `DAT_10000244` (or whatever the address is) is referenced:
|
||||
|
||||
**Expected output might look like:**
|
||||
```
|
||||
DAT_10000244 (1 xref):
|
||||
main:10000236 (read)
|
||||
```
|
||||
|
||||
This means:
|
||||
- The data at `DAT_10000244` is used in 1 place
|
||||
- That place is in the `main` function at instruction `10000236`
|
||||
- It's a **read** operation (the code is reading this data)
|
||||
|
||||
##### Step 7: Answer These Questions
|
||||
|
||||
###### Question 1: What is the address of the data reference?
|
||||
- What is the address of the data reference you found? (e.g., `DAT_10000244`)
|
||||
- __________
|
||||
|
||||
###### Question 2: How many places reference this data?
|
||||
- How many places reference this data?
|
||||
- __________
|
||||
- Which function(s) use it?
|
||||
- __________
|
||||
|
||||
###### Question 3: Is it a read or write operation? Why?
|
||||
- Is it a read or write operation?
|
||||
- __________
|
||||
- Why? (What's the program doing with this data?)
|
||||
- __________
|
||||
|
||||
###### Question 4: What happens next after the `ldr`?
|
||||
- The `ldr` instruction loads an address into `r0`
|
||||
- What happens next? (Hint: Look at the next instruction after the `ldr`)
|
||||
- __________
|
||||
- Is there a function call? If so, which one?
|
||||
- __________
|
||||
|
||||
###### Complete the Data Flow Chain
|
||||
- **`DAT_10000244`** contains the address of the "hello, world" string
|
||||
- The `ldr` loads that address into `r0`
|
||||
- Then a function (probably `printf` or `puts`) is called with `r0` as the argument
|
||||
- Can you trace this complete flow?
|
||||
|
||||
#### Deeper Analysis (Optional Challenge)
|
||||
|
||||
##### Challenge 1: Find the Actual String Address
|
||||
1. Navigate to the `DAT_10000244` location
|
||||
2. Look at the value stored there
|
||||
3. Can you decode the hex bytes and find the actual address of "hello, world"?
|
||||
4. Hint: The RP2350 uses little-endian encoding, so the bytes are "backwards"
|
||||
|
||||
**Example:**
|
||||
If you see bytes: `CC 19 00 10`
|
||||
Read backwards: `10 00 19 CC` = `0x100019CC`
|
||||
|
||||
##### Challenge 2: Understand the Indirection
|
||||
1. In C, if we want to load an address, we do: `char *ptr = &some_string;`
|
||||
2. Then to use it: `printf(ptr);`
|
||||
3. In assembly, this becomes:
|
||||
- Load the pointer: `ldr r0, [DAT_...]`
|
||||
- Call the function: `bl printf`
|
||||
4. Can you see this pattern in the assembly?
|
||||
|
||||
##### Challenge 3: Follow Multiple References
|
||||
1. Try this with different data items in the binary
|
||||
2. Find a data reference that has **multiple** cross-references
|
||||
3. What data is used in more than one place?
|
||||
|
||||
#### Questions for Reflection
|
||||
|
||||
1. **Why does the compiler use an indirect pointer reference here?**
|
||||
2. **What is a literal pool?**
|
||||
3. **How does cross-referencing help in reverse engineering?**
|
||||
|
||||
#### 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
|
||||
- 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
|
||||
|
||||
#### Real-World Applications
|
||||
|
||||
Understanding cross-references is crucial for:
|
||||
- **Vulnerability hunting**: Finding where user input flows through the code
|
||||
- **Firmware patching**: Changing constants, strings, or data values
|
||||
- **Malware analysis**: Tracking command-and-control server addresses or encryption keys
|
||||
- **Reverse engineering**: Understanding program logic by following data dependencies
|
||||
|
||||
#### Summary
|
||||
|
||||
By completing this exercise, you've learned:
|
||||
1. How to find and interpret cross-references in Ghidra
|
||||
2. How to trace data from its definition to where it's used
|
||||
3. How the `ldr` (load) instruction works to pass data to functions
|
||||
4. The relationship between high-level C code and assembly-level data flow
|
||||
5. How addresses are indirectly referenced in position-independent code
|
||||
|
||||
#### Expected Final Understanding
|
||||
|
||||
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
|
||||
```
|
||||
@@ -1,72 +0,0 @@
|
||||
# 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
|
||||
|
||||
##### Step 3: Answer Exactly
|
||||
|
||||
- **Was GDB able to connect to OpenOCD?**
|
||||
- Yes, via `target extended-remote localhost:3333`.
|
||||
- **Did the program stop at the `main` breakpoint?**
|
||||
- Yes, at `Breakpoint 1, main () at ../0x0001_hello-world.c:4`.
|
||||
- **What is the address of `main`'s first instruction, and is it Flash or RAM?**
|
||||
- `0x10000234`, and it is in **Flash** (`0x100...` XIP region).
|
||||
- **What is the `sp` value at `main`, and is it Flash or RAM?**
|
||||
- `0x20082000`, and it is in **RAM** (`0x200...` SRAM region).
|
||||
- **What is the first instruction in `main`, and what does it do?**
|
||||
- `push {r3, lr}`; it saves `r3` and `lr` on the stack and keeps 8-byte stack alignment for ABI-compliant calls.
|
||||
- **Does GDB match what Ghidra shows?**
|
||||
- Yes. The disassembly and flow match the Ghidra listing.
|
||||
|
||||
##### Step 4: Capture Register Values (`pc`, `sp`, `lr`, `r0-r3`)
|
||||
|
||||
| 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 |
|
||||
|
||||
##### Reference Disassembly (for verification)
|
||||
|
||||
```
|
||||
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) disas main
|
||||
(gdb) i r
|
||||
```
|
||||
|
||||
#### Step 5: 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.
|
||||
@@ -1,360 +0,0 @@
|
||||
# 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: 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.
|
||||
|
||||
#### Prerequisites
|
||||
- Raspberry Pi Pico 2 with "hello-world" binary already flashed
|
||||
- OpenOCD installed and working
|
||||
- GDB (arm-none-eabi-gdb) installed
|
||||
- Your Pico 2 connected to your computer via USB CMSIS-DAP interface
|
||||
- CMake build artifacts available (`.elf` file from compilation)
|
||||
|
||||
#### Task Description
|
||||
|
||||
In this exercise, you'll:
|
||||
1. Start OpenOCD to provide a debug server
|
||||
2. Connect GDB to the Pico 2 via OpenOCD
|
||||
3. Set a breakpoint at the main function
|
||||
4. Examine registers and memory while the program is running
|
||||
5. Verify that your dynamic debugging setup works
|
||||
|
||||
#### Important Setup Notes
|
||||
|
||||
Before you start, make sure:
|
||||
- Your Pico 2 is **powered on** and connected to your computer
|
||||
- You have **OpenOCD** installed for ARM debugging
|
||||
- You have **GDB** (specifically `arm-none-eabi-gdb`) installed
|
||||
- Your binary file (`0x0001_hello-world.elf`) is available in the `build/` directory
|
||||
|
||||
#### Step-by-Step Instructions
|
||||
|
||||
##### Step 1: Start OpenOCD in Terminal 1
|
||||
|
||||
Open a **new terminal window** (PowerShell, Command Prompt, or WSL):
|
||||
|
||||
**On Windows (PowerShell/Command Prompt):**
|
||||
```
|
||||
openocd ^
|
||||
-s "C:\Users\flare-vm\.pico-sdk\openocd\0.12.0+dev\scripts" ^
|
||||
-f interface/cmsis-dap.cfg ^
|
||||
-f target/rp2350.cfg ^
|
||||
-c "adapter speed 5000"
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Open On-Chip Debugger 0.12.0+dev
|
||||
...
|
||||
Info : CMSIS-DAP: SWD detected
|
||||
Info : RP2350 (dual core) detected
|
||||
Info : Using JTAG interface
|
||||
...
|
||||
Info : accepting 'gdb' connection on tcp/3333
|
||||
```
|
||||
|
||||
##### Step 2: Start GDB in Terminal 2
|
||||
|
||||
Open a **second terminal window** and navigate to your project directory:
|
||||
|
||||
```
|
||||
arm-none-eabi-gdb build\0x0001_hello-world.elf
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Reading symbols from build\0x0001_hello-world.elf...
|
||||
(gdb)
|
||||
```
|
||||
|
||||
##### Step 3: Connect GDB to OpenOCD
|
||||
|
||||
At the GDB prompt `(gdb)`, type:
|
||||
|
||||
```gdb
|
||||
target extended-remote localhost:3333
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Remote debugging using localhost:3333
|
||||
(gdb)
|
||||
```
|
||||
|
||||
(The warning is normal - you already loaded the .elf file, so it doesn't matter)
|
||||
|
||||
##### Step 4: Reset and Halt the Target
|
||||
|
||||
To reset the Pico 2 and prepare for debugging, type:
|
||||
|
||||
```gdb
|
||||
monitor reset halt
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
(gdb)
|
||||
```
|
||||
|
||||
(This resets the processor and halts it, preventing execution until you tell it to run)
|
||||
|
||||
##### Step 5: Set a Breakpoint at main
|
||||
|
||||
To stop execution at the beginning of the `main` function:
|
||||
|
||||
```gdb
|
||||
b main
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Breakpoint 1 at 0x10000234: file ../0x0001_hello-world.c, line 4.
|
||||
(gdb)
|
||||
```
|
||||
|
||||
**What this means:**
|
||||
- Breakpoint 1 is set at address `0x10000234`
|
||||
- That's in the file `../0x0001_hello-world.c` at line 4
|
||||
- The breakpoint is at the `main` function
|
||||
|
||||
##### Step 6: Continue Execution to the Breakpoint
|
||||
|
||||
Now let the program run until it hits your breakpoint:
|
||||
|
||||
```gdb
|
||||
c
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, main () at ../0x0001_hello-world.c:4
|
||||
4 stdio_init_all();
|
||||
(gdb)
|
||||
```
|
||||
|
||||
**Great!** Your program is now halted at the beginning of `main()`.
|
||||
|
||||
##### Step 7: Examine the Assembly with `disas`
|
||||
|
||||
To see the assembly language of the current function:
|
||||
|
||||
```gdb
|
||||
disas
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
Dump of assembler code for function main:
|
||||
=> 0x10000234 <+0>: push {r3, lr}
|
||||
0x10000236 <+2>: bl 0x1000156c <stdio_init_all>
|
||||
0x1000023a <+6>: ldr r0, [pc, #8] @ (0x10000244 <main+16>)
|
||||
0x1000023c <+8>: bl 0x100015fc <__wrap_puts>
|
||||
0x10000240 <+12>: b.n 0x1000023a <main+6>
|
||||
0x10000242 <+14>: nop
|
||||
0x10000244 <+16>: adds r4, r1, r7
|
||||
0x10000246 <+18>: asrs r0, r0, #32
|
||||
End of assembler dump.
|
||||
(gdb)
|
||||
```
|
||||
|
||||
**Interpretation:**
|
||||
- The `=>` arrow shows where we're currently stopped (at `0x10000234`)
|
||||
- We can see the `push`, `bl` (branch and link), `ldr`, and `b.n` (branch) instructions
|
||||
- This is the exact code you analyzed in the Ghidra exercises!
|
||||
|
||||
##### Step 8: View All Registers with `i r`
|
||||
|
||||
To see the current state of all CPU registers:
|
||||
|
||||
```gdb
|
||||
i r
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
r0 0x0 0
|
||||
r1 0x10000235 268436021
|
||||
r2 0x80808080 -2139062144
|
||||
r3 0xe000ed08 -536810232
|
||||
r4 0x100001d0 268435920
|
||||
r5 0x88526891 -2007865199
|
||||
r6 0x4f54710 83183376
|
||||
r7 0x400e0014 1074659348
|
||||
r8 0x43280035 1126694965
|
||||
r9 0x0 0
|
||||
r10 0x10000000 268435456
|
||||
r11 0x62707361 1651536737
|
||||
r12 0xed07f600 -318245376
|
||||
sp 0x20082000 0x20082000
|
||||
lr 0x1000018f 268435855
|
||||
pc 0x10000234 0x10000234 <main>
|
||||
xpsr 0x69000000 1761607680
|
||||
```
|
||||
|
||||
**Key Registers to Understand:**
|
||||
| Register | Value | Meaning |
|
||||
| -------- | ------------ | ------------------------------------------------- |
|
||||
| `pc` | `0x10000234` | Program Counter - we're at the start of `main` |
|
||||
| `sp` | `0x20082000` | Stack Pointer - top of our stack in RAM |
|
||||
| `lr` | `0x1000018f` | Link Register - where we return from `main` |
|
||||
| `r0-r3` | Various | Will hold function arguments and return values |
|
||||
|
||||
##### Step 9: Step Into the First Instruction
|
||||
|
||||
To execute one assembly instruction:
|
||||
|
||||
```gdb
|
||||
si
|
||||
```
|
||||
|
||||
**Expected Output:**
|
||||
```
|
||||
0x10000236 in main () at ../0x0001_hello-world.c:5
|
||||
5 stdio_init_all();
|
||||
(gdb)
|
||||
```
|
||||
|
||||
The `pc` should now be at `0x10000236`, which is the next instruction.
|
||||
|
||||
##### Step 10: Answer These Questions
|
||||
|
||||
Based on what you've observed:
|
||||
|
||||
###### Question 1: Was GDB able to connect to OpenOCD?
|
||||
- Was GDB able to connect to OpenOCD? (Yes/No)
|
||||
- Did the program stop at your breakpoint? (Yes/No)
|
||||
- __________
|
||||
|
||||
###### Question 2: What is the address of `main`'s first instruction, and is it Flash or RAM?
|
||||
- What is the memory address of the `main` function's first instruction?
|
||||
- __________
|
||||
- Is this in Flash memory (0x100...) or RAM (0x200...)?
|
||||
- __________
|
||||
|
||||
###### Question 3: What is the `sp` value at `main`, and is it Flash or RAM?
|
||||
- What is the value of the Stack Pointer (sp) when you're at `main`?
|
||||
- __________
|
||||
- Is this in Flash or RAM?
|
||||
- __________
|
||||
|
||||
###### Question 4: What is the first instruction in `main`, and what does it do?
|
||||
- What is the first instruction in `main`?
|
||||
- __________
|
||||
- What does it do? (Hint: `push` = save to stack)
|
||||
- __________
|
||||
|
||||
###### Question 5: Does GDB match what Ghidra shows?
|
||||
- Look at the disassembly from GDB (Step 7)
|
||||
- Compare it to the disassembly from Ghidra (Exercise 1)
|
||||
- Are they the same?
|
||||
- __________
|
||||
|
||||
#### Deeper Exploration (Optional Challenge)
|
||||
|
||||
##### Challenge 1: Step Through stdio_init_all
|
||||
1. Continue stepping: `si` (step into) or `ni` (next instruction)
|
||||
2. Eventually, you'll reach `bl 0x1000156c <stdio_init_all>`
|
||||
3. Use `si` to step **into** that function
|
||||
4. What instructions do you see?
|
||||
5. What registers are being modified?
|
||||
|
||||
##### Challenge 2: View Specific Registers
|
||||
Instead of viewing all registers, you can view just a few:
|
||||
```gdb
|
||||
i r pc sp lr r0 r1 r2
|
||||
```
|
||||
This shows only the registers you care about.
|
||||
|
||||
##### Challenge 3: Examine Memory
|
||||
To examine memory at a specific address (e.g., where the string is):
|
||||
```gdb
|
||||
x/16b 0x100019cc
|
||||
```
|
||||
This displays 16 bytes (`b` = byte) starting at address `0x100019cc`. Can you see the "hello, world" string?
|
||||
|
||||
##### Challenge 4: Set a Conditional Breakpoint
|
||||
Set a breakpoint that only triggers after a certain condition:
|
||||
```gdb
|
||||
b *0x1000023a if $r0 != 0
|
||||
```
|
||||
This is useful when you want to break on a condition rather than every time.
|
||||
|
||||
#### Questions for Reflection
|
||||
|
||||
1. **Why does the stack pointer start where it does?**
|
||||
2. **Why does `push {r3, lr}` include `r3`?**
|
||||
3. **How does the infinite loop work in assembly?**
|
||||
|
||||
#### Important GDB Commands Reference
|
||||
|
||||
| Command | Short Form | What It Does |
|
||||
| ---------------------- | ---------- | ------------------------------------ |
|
||||
| `target extended-remote localhost:3333` | | Connect to OpenOCD |
|
||||
| `monitor reset halt` | | Reset and halt the processor |
|
||||
| `break main` | `b main` | Set a breakpoint at main function |
|
||||
| `continue` | `c` | Continue until breakpoint |
|
||||
| `step instruction` | `si` | Step one instruction (into calls) |
|
||||
| `next instruction` | `ni` | Step one instruction (over calls) |
|
||||
| `disassemble` | `disas` | Show assembly for current function |
|
||||
| `info registers` | `i r` | Show all register values |
|
||||
| `x/Nxy ADDRESS` | `x` | Examine memory (N=count, x=format, y=size) |
|
||||
| `quit` | `q` | Exit GDB |
|
||||
|
||||
**Examples for `x` command:**
|
||||
- `x/10i $pc` - examine 10 instructions at program counter
|
||||
- `x/16b 0x20000000` - examine 16 bytes starting at RAM address
|
||||
- `x/4w 0x10000000` - examine 4 words (4-byte values) starting at Flash address
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
##### Problem: "OpenOCD not found"
|
||||
**Solution:** Make sure OpenOCD is in your PATH or use the full path to the executable
|
||||
|
||||
##### Problem: "Target not responding"
|
||||
**Solution:**
|
||||
- Check that your Pico 2 is properly connected
|
||||
- Make sure OpenOCD is running and shows "accepting 'gdb' connection"
|
||||
- Restart both OpenOCD and GDB
|
||||
|
||||
##### Problem: "Cannot find breakpoint at main"
|
||||
**Solution:**
|
||||
- Make sure you compiled with debug symbols
|
||||
- The .elf file must include symbol information
|
||||
- Try breaking at an address instead: `b *0x10000234`
|
||||
|
||||
##### Problem: GDB shows "No source available"
|
||||
**Solution:**
|
||||
- This happens with stripped binaries
|
||||
- You can still see assembly with `disas`
|
||||
- You can still examine memory and registers
|
||||
|
||||
#### 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
|
||||
|
||||
You're now ready for Week 2, where you'll:
|
||||
- Step through code line by line
|
||||
- Watch variables and memory change
|
||||
- Understand program flow in detail
|
||||
- Use this knowledge to modify running code
|
||||
|
||||
#### Next Steps
|
||||
|
||||
1. **Close GDB**: Type `quit` or `q` to exit
|
||||
2. **Close OpenOCD**: Type `Ctrl+C` in the OpenOCD terminal
|
||||
3. **Review**: Go back to the Ghidra exercises and compare static vs. dynamic analysis
|
||||
4. **Prepare**: Read through Week 2 materials to understand what's coming next
|
||||
+53
-132
@@ -13,7 +13,7 @@ By the end of this week, you will be able to:
|
||||
|
||||
---
|
||||
|
||||
## 📚 Part 1: Understanding the Basics
|
||||
## Part 1: Understanding the Basics
|
||||
|
||||
### What is a Microcontroller?
|
||||
|
||||
@@ -32,7 +32,7 @@ Reverse engineering is like being a detective for code. Instead of writing code
|
||||
|
||||
---
|
||||
|
||||
## 📚 Part 2: Understanding Processor Registers
|
||||
## Part 2: Understanding Processor Registers
|
||||
|
||||
### What is a Register?
|
||||
|
||||
@@ -70,13 +70,13 @@ The two Arm ABI documents we verified give the formal proof for these rules. In
|
||||
|
||||
```
|
||||
Higher Memory Address (0x20082000)
|
||||
┌──────────────────┐
|
||||
│ │ ← Stack starts here (empty)
|
||||
├──────────────────┤
|
||||
│ Pushed Item 1 │ ← SP points here after 1 push
|
||||
├──────────────────┤
|
||||
│ Pushed Item 2 │ ← SP points here after 2 pushes
|
||||
└──────────────────┘
|
||||
+------------------+
|
||||
| | ← Stack starts here (empty)
|
||||
+------------------+
|
||||
| Pushed Item 1 | ← SP points here after 1 push
|
||||
+------------------+
|
||||
| Pushed Item 2 | ← SP points here after 2 pushes
|
||||
+------------------+
|
||||
Lower Memory Address (0x20081FF8)
|
||||
```
|
||||
|
||||
@@ -103,7 +103,7 @@ The Program Counter always points to the **next instruction** the processor will
|
||||
|
||||
---
|
||||
|
||||
## 📚 Part 3: Understanding Memory Layout
|
||||
## Part 3: Understanding Memory Layout
|
||||
|
||||
### XIP - Execute In Place
|
||||
|
||||
@@ -116,15 +116,15 @@ This is where your program code starts in flash memory. Remember this address -
|
||||
### Memory Map Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Flash Memory (XIP) │
|
||||
│ Starts at: 0x10000000 │
|
||||
│ Contains: Your program code │
|
||||
├─────────────────────────────────────┤
|
||||
│ RAM │
|
||||
│ Starts at: 0x20000000 │
|
||||
│ Contains: Stack, Heap, Variables │
|
||||
└─────────────────────────────────────┘
|
||||
+-------------------------------------+
|
||||
| Flash Memory (XIP) |
|
||||
| Starts at: 0x10000000 |
|
||||
| Contains: Your program code |
|
||||
+-------------------------------------+
|
||||
| RAM |
|
||||
| Starts at: 0x20000000 |
|
||||
| Contains: Stack, Heap, Variables |
|
||||
+-------------------------------------+
|
||||
```
|
||||
|
||||
### Stack vs Heap
|
||||
@@ -139,7 +139,7 @@ This is where your program code starts in flash memory. Remember this address -
|
||||
|
||||
---
|
||||
|
||||
## 📚 Part 3.5: Reviewing Our Hello World Code
|
||||
## Part 3.5: Reviewing Our Hello World Code
|
||||
|
||||
Before we start debugging, let's understand the code we'll be working with. Here's our `0x0001_hello-world.c` program:
|
||||
|
||||
@@ -197,7 +197,7 @@ while (true)
|
||||
- **`while (true)`** - This creates an infinite loop. The program will keep running forever (or until you reset/power off the Pico).
|
||||
- **`printf("hello, world\r\n")`** - This prints the text "hello, world" followed by a carriage return (`\r`) and newline (`\n`).
|
||||
|
||||
> 💡 **Why `\r\n` instead of just `\n`?**
|
||||
> Tip: **Why `\r\n` instead of just `\n`?**
|
||||
>
|
||||
> In embedded systems, we often use both carriage return (`\r`) and newline (`\n`) together. The `\r` moves the cursor back to the beginning of the line, and `\n` moves to the next line. This ensures proper display across different terminal programs.
|
||||
|
||||
@@ -239,7 +239,7 @@ To flash new code to your Pico 2, you need to put it into **BOOTSEL mode**:
|
||||
|
||||
When done correctly, your Pico 2 will appear as a USB mass storage device (like a flash drive) on your computer. This means it's ready to receive new firmware!
|
||||
|
||||
> 💡 **Tip:** You'll see a drive called "RP2350" appear in your file explorer when the Pico 2 is in flash loading mode.
|
||||
> Tip: **Tip:** You'll see a drive called "RP2350" appear in your file explorer when the Pico 2 is in flash loading mode.
|
||||
|
||||
##### Step 3: Flash and Run
|
||||
|
||||
@@ -251,7 +251,7 @@ Once flashed, your Pico 2 will immediately start executing the hello-world progr
|
||||
|
||||
---
|
||||
|
||||
## 📚 Part 4: Dynamic Analysis with GDB
|
||||
## Part 4: Dynamic Analysis with GDB
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -267,7 +267,7 @@ Open a terminal and start OpenOCD:
|
||||
|
||||
```powershell
|
||||
openocd ^
|
||||
-s "C:\Users\flare-vm\.pico-sdk\openocd\0.12.0+dev\scripts" ^
|
||||
-s "C:\Users\assem.KEVINTHOMAS\.pico-sdk\openocd\0.12.0+dev\scripts" ^
|
||||
-f interface/cmsis-dap.cfg ^
|
||||
-f target/rp2350.cfg ^
|
||||
-c "adapter speed 5000"
|
||||
@@ -318,10 +318,6 @@ Breakpoint 1, main () at ../0x0001_hello-world.c:5
|
||||
|
||||
The program has stopped right at the beginning of `main`!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
##### Disassembling with `disas`
|
||||
|
||||
The `disas` (disassemble) command shows us the assembly instructions for the current function:
|
||||
@@ -352,9 +348,9 @@ To see how the ELF is laid out in memory, use:
|
||||
|
||||
```gdb
|
||||
(gdb) info files
|
||||
Symbols from "C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf".
|
||||
Symbols from "C:\Users\assem.KEVINTHOMAS\OneDrive\Documents\Embedded-Hacking\0x0001_hello-world\build\0x0001_hello-world.elf".
|
||||
Extended remote target using gdb-specific protocol:
|
||||
`C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm.
|
||||
`C:\Users\assem.KEVINTHOMAS\OneDrive\Documents\Embedded-Hacking\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm.
|
||||
Entry point: 0x1000014c
|
||||
0x10000000 - 0x100019cc is .text
|
||||
0x100019cc - 0x10001b18 is .rodata
|
||||
@@ -370,7 +366,7 @@ Extended remote target using gdb-specific protocol:
|
||||
0x10001ce8 - 0x10001cfc is .flash_end
|
||||
While running this, GDB does not access memory from...
|
||||
Local exec file:
|
||||
`C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm.
|
||||
`C:\Users\assem.KEVINTHOMAS\OneDrive\Documents\Embedded-Hacking\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm.
|
||||
Entry point: 0x1000014c
|
||||
0x10000000 - 0x100019cc is .text
|
||||
0x100019cc - 0x10001b18 is .rodata
|
||||
@@ -385,7 +381,7 @@ Local exec file:
|
||||
0x20081000 - 0x20081800 is .stack_dummy
|
||||
0x10001ce8 - 0x10001cfc is .flash_end
|
||||
(gdb) maintenance info sections
|
||||
Exec file: `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm.
|
||||
Exec file: `C:\Users\assem.KEVINTHOMAS\OneDrive\Documents\Embedded-Hacking\0x0001_hello-world\build\0x0001_hello-world.elf', file type elf32-littlearm.
|
||||
[0] 0x10000000->0x100019cc at 0x00001000: .text ALLOC LOAD READONLY CODE HAS_CONTENTS
|
||||
[1] 0x100019cc->0x10001b18 at 0x000029cc: .rodata ALLOC LOAD READONLY DATA HAS_CONTENTS
|
||||
[2] 0x10001b18->0x10001b20 at 0x00002b18: .ARM.exidx ALLOC LOAD READONLY DATA HAS_CONTENTS
|
||||
@@ -445,7 +441,7 @@ Exec file: `C:\Users\flare-vm\Desktop\Embedded-Hacking-main\0x0001_hello-world\b
|
||||
| `.debug_loclists` | Variable location lists (where variables live over PC ranges). |
|
||||
| `.debug_line_str` | Extra string pool used by `.debug_line` data. |
|
||||
|
||||
> 💡 **Practical rule:** For reverse engineering runtime behavior, focus first on `.text`, `.rodata`, `.data`, `.bss`, heap/stack regions, and the vector table. Debug sections are for source-level mapping and symbol intelligence.
|
||||
> Tip: **Practical rule:** For reverse engineering runtime behavior, focus first on `.text`, `.rodata`, `.data`, `.bss`, heap/stack regions, and the vector table. Debug sections are for source-level mapping and symbol intelligence.
|
||||
|
||||
**Fast interpretation checklist (use this every time):**
|
||||
|
||||
@@ -493,7 +489,7 @@ xpsr 0x69000000 1761607680
|
||||
| `lr` | `0x100002d5` | Link Register - where we return after `main` |
|
||||
| `r0-r3` | Various | Will hold function arguments and return values |
|
||||
|
||||
> 💡 **Tip:** You can also use `i r pc sp lr` to show only specific registers you care about.
|
||||
> Tip: **Tip:** You can also use `i r pc sp lr` to show only specific registers you care about.
|
||||
|
||||
### Quick Reference: Essential GDB Commands
|
||||
|
||||
@@ -528,7 +524,7 @@ Notice the difference between inspecting memory at `$sp` and inspecting `$lr`.
|
||||
0x1000018f <platform_entry+8>: 0x00478849
|
||||
```
|
||||
|
||||
> 💡 **What's Next?** In Week 2, we'll put these GDB commands to work with hands-on debugging exercises! We'll step through code, examine the stack, watch registers change, and ultimately use these skills to modify a running program. The commands you learned here are the foundation for everything that follows.
|
||||
> Tip: **What's Next?** In Week 2, we'll put these GDB commands to work with hands-on debugging exercises! We'll step through code, examine the stack, watch registers change, and ultimately use these skills to modify a running program. The commands you learned here are the foundation for everything that follows.
|
||||
|
||||
---
|
||||
|
||||
@@ -541,7 +537,7 @@ Before we dive into GDB debugging, let's set up Ghidra to analyze our hello-worl
|
||||
##### Step 1: Create a New Project
|
||||
|
||||
1. Launch Ghidra
|
||||
2. A window will appear - select **File → New Project**
|
||||
2. A window will appear - select **File -> New Project**
|
||||
3. Choose **Non-Shared Project** and click **Next**
|
||||
4. Enter the Project Name: `0x0001_hello-world`
|
||||
5. Click **Finish**
|
||||
@@ -555,7 +551,7 @@ Before we dive into GDB debugging, let's set up Ghidra to analyze our hello-worl
|
||||
|
||||
In the small window that appears, you will see the file identified as an **ELF** (Executable and Linkable Format).
|
||||
|
||||
> 💡 **What is an ELF file?**
|
||||
> Tip: **What is an ELF file?**
|
||||
>
|
||||
> ELF stands for **Executable and Linkable Format**. This format includes **symbols** - human-readable names for functions and variables. These symbols make reverse engineering much easier because you can see function names like `main` and `printf` instead of just memory addresses.
|
||||
>
|
||||
@@ -575,7 +571,7 @@ Ghidra will now process the binary, identifying functions, strings, and cross-re
|
||||
Once analysis is complete, let's find our `main` function:
|
||||
|
||||
1. In the **Symbol Tree** panel on the left, expand **Functions**
|
||||
2. Look for `main` in the list (you can also use **Search → For Address or Label** and type "main")
|
||||
2. Look for `main` in the list (you can also use **Search -> For Address or Label** and type "main")
|
||||
3. Click on `main` to navigate to it
|
||||
|
||||
##### What You'll See
|
||||
@@ -677,104 +673,27 @@ In future weeks, we'll work with `.bin` files that have been stripped of symbols
|
||||
### The Program Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ 1. push {r3, lr} │
|
||||
│ Save registers to stack │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 2. bl stdio_init_all │
|
||||
│ Initialize standard I/O │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 3. ldr r0, [pc, #8] ────────────────┐ │
|
||||
│ Load address of "hello, world" into r0│ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 4. bl __wrap_puts │ │
|
||||
│ Print the string │ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ 5. b.n (back to step 3) ────────────────┘ │
|
||||
│ Infinite loop! │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
+-----------------------------------------------------+
|
||||
| 1. push {r3, lr} |
|
||||
| Save registers to stack |
|
||||
+-----------------------------------------------------+
|
||||
| 2. bl stdio_init_all |
|
||||
| Initialize standard I/O |
|
||||
+-----------------------------------------------------+
|
||||
| 3. ldr r0, [pc, #8] ----------------+ |
|
||||
| Load address of "hello, world" into r0| |
|
||||
+-----------------------------------------------------+
|
||||
| 4. bl __wrap_puts | |
|
||||
| Print the string | |
|
||||
+-----------------------------------------------------+
|
||||
| 5. b.n (back to step 3) ----------------+ |
|
||||
| Infinite loop! |
|
||||
+-----------------------------------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Practice Exercises
|
||||
|
||||
Try these on your own to reinforce what you learned:
|
||||
|
||||
These prompts are intentionally aligned 1:1 with the four Week 1 solution files:
|
||||
- `WEEK01-01-S.md`
|
||||
- `WEEK01-02-S.md`
|
||||
- `WEEK01-03-S.md`
|
||||
- `WEEK01-04-S.md`
|
||||
|
||||
### Exercise 1: Analyze `stdio_init_all` in Ghidra
|
||||
1. Open your `0x0001_hello-world` project in Ghidra.
|
||||
2. Find `stdio_init_all` in the Symbol Tree.
|
||||
3. Answer exactly:
|
||||
- What does the function return?
|
||||
- What parameters does it take?
|
||||
- What functions does it call?
|
||||
- What is its purpose?
|
||||
4. Reflection:
|
||||
- Why would we need to initialize standard I/O before using `printf()`?
|
||||
- Can you find other functions in the Symbol Tree that might be related to I/O?
|
||||
- How does this function support the `printf("hello, world\r\n")` call in `main`?
|
||||
|
||||
### Exercise 2: Locate and Characterize the String
|
||||
1. In Ghidra, go to **Window → Defined Strings**.
|
||||
2. Find `"hello, world\r\n"` and record its address.
|
||||
3. Answer exactly:
|
||||
- What is the address, and is it Flash or RAM?
|
||||
- How many bytes does the string take?
|
||||
- How many times is it referenced, and by which function(s)?
|
||||
- How is the string encoded?
|
||||
4. Reflection:
|
||||
- Why is the string stored in Flash instead of RAM?
|
||||
- What would happen if you tried to modify this string at runtime?
|
||||
- How does the Listing view help you understand string storage?
|
||||
|
||||
### Exercise 3: Trace Cross-References and Data Flow
|
||||
1. In `main`, locate `DAT_10000244` and open its references.
|
||||
2. Fill in:
|
||||
- Data reference address
|
||||
- Number of references
|
||||
- Reference type (read or write)
|
||||
- Function using it
|
||||
- Next instruction after `ldr`
|
||||
3. Answer exactly:
|
||||
- What is the address of the data reference?
|
||||
- How many places reference this data?
|
||||
- Is it a read or write operation? Why?
|
||||
- What happens next after the `ldr`?
|
||||
4. Complete the data flow chain from string storage to print call.
|
||||
5. Reflection:
|
||||
- Why does the compiler use an indirect pointer reference here?
|
||||
- What is a literal pool?
|
||||
- How does cross-referencing help in reverse engineering?
|
||||
|
||||
### Exercise 4: Verify Runtime View in GDB
|
||||
1. Start OpenOCD and connect GDB as shown in Part 4.
|
||||
2. Break at `main` and continue to the breakpoint.
|
||||
3. Answer exactly:
|
||||
- Was GDB able to connect to OpenOCD?
|
||||
- Did the program stop at the `main` breakpoint?
|
||||
- What is the address of `main`'s first instruction, and is it Flash or RAM?
|
||||
- What is the `sp` value at `main`, and is it Flash or RAM?
|
||||
- What is the first instruction in `main`, and what does it do?
|
||||
- Does GDB match what Ghidra shows?
|
||||
4. Capture register values for `pc`, `sp`, `lr`, and `r0-r3`.
|
||||
5. Reflection:
|
||||
- Why does the stack pointer start where it does?
|
||||
- Why does `push {r3, lr}` include `r3`?
|
||||
- How does the infinite loop work in assembly?
|
||||
|
||||
Use these solution keys after attempting the exercises:
|
||||
- `WEEK01-01-S.md`
|
||||
- `WEEK01-02-S.md`
|
||||
- `WEEK01-03-S.md`
|
||||
- `WEEK01-04-S.md`
|
||||
|
||||
> 💡 **Note:** The detailed hands-on GDB debugging (stepping through code, watching the stack, examining memory) will be covered in Week 2!
|
||||
> **Note:** The detailed hands-on GDB debugging (stepping through code, watching the stack, examining memory) will be covered in Week 2!
|
||||
|
||||
---
|
||||
|
||||
@@ -807,3 +726,5 @@ Use these solution keys after attempting the exercises:
|
||||
| **Stack** | Memory region for temporary storage during function calls |
|
||||
| **Stack Pointer** | Register that points to the top of the stack |
|
||||
| **XIP** | Execute In Place - running code directly from flash |
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user