# Embedded Systems Reverse Engineering [Repository](https://github.com/mytechnotalent/Embedded-Hacking) ## Week 4 Variables in Embedded Systems: Debugging and Hacking Variables w/ GPIO Output Basics ### Exercise 1: Analyze Variable Storage in Ghidra #### Objective Import and analyze the `0x0005_intro-to-variables.bin` binary in Ghidra to understand where variables are stored, identify memory sections, and trace how the compiler optimizes variable usage. #### Prerequisites - Ghidra installed and configured - `0x0005_intro-to-variables.bin` binary available in your build directory - Understanding of memory sections (`.data`, `.bss`, `.rodata`) from Week 4 Part 2 - Basic Ghidra navigation skills from Week 3 #### Task Description You will import the binary into Ghidra, configure it for ARM Cortex-M33, analyze the code structure, resolve function names, and locate where the `age` variable is used in the compiled binary. #### Step-by-Step Instructions ##### Step 1: Start Ghidra and Create New Project ```bash ghidraRun ``` 1. Click **File** → **New Project** 2. Select **Non-Shared Project** 3. Click **Next** 4. Enter Project Name: `week04-ex01-intro-to-variables` 5. Choose a project directory 6. Click **Finish** ##### Step 2: Import the Binary 1. Navigate to your file explorer 2. Find `Embedded-Hacking/0x0005_intro-to-variables/build/0x0005_intro-to-variables.bin` 3. **Drag and drop** the `.bin` file into Ghidra's project window ##### Step 3: Configure Import Settings When the import dialog appears: 1. Click the three dots (**…**) next to **Language** 2. Search for: `Cortex` 3. Select: **ARM Cortex 32 little endian default** 4. Click **OK** Now click **Options…** button: 1. Change **Block Name** to: `.text` 2. Change **Base Address** to: `10000000` (XIP flash base) 3. Click **OK** Then click **OK** on the main import dialog. ##### Step 4: Analyze the Binary 1. Double-click the imported file in the project window 2. When prompted "Analyze now?" click **Yes** 3. Leave all default analysis options selected 4. Click **Analyze** 5. Wait for analysis to complete (watch bottom-right progress bar) ##### Step 5: Navigate to the Symbol Tree Look at the left panel for the **Symbol Tree**. Expand **Functions** to see the auto-detected functions: You should see function names like: - `FUN_1000019a` - `FUN_10000210` - `FUN_10000234` - Many more... These are auto-generated names because we're analyzing a raw binary without debug symbols. ##### Step 6: Identify the Main Function From Week 3, we know the typical boot sequence: 1. Reset handler copies data 2. `frame_dummy` runs 3. `main()` is called Click on `FUN_10000234` - this should be our `main()` function. **Look at the Decompile window:** ```c void FUN_10000234(void) { FUN_100030cc(); do { FUN_10003100("age: %d\r\n", 0x2b); } while (true); } ``` **Observations:** - `FUN_100030cc()` is likely `stdio_init_all()` - `FUN_10003100()` is likely `printf()` - The magic value `0x2b` appears (what is this?) ##### Step 7: Convert 0x2b to Decimal Let's figure out what `0x2b` means: **Manual calculation:** - `0x2b` in hexadecimal - `2 × 16 + 11 = 32 + 11 = 43` in decimal **In GDB (alternative method):** ```gdb (gdb) p/d 0x2b $1 = 43 ``` So `0x2b = 43`! This matches our `age = 43` from the source code! ##### Step 8: Rename Functions for Clarity Let's rename the functions to their actual names: **Rename FUN_10000234 to main:** 1. Right-click on `FUN_10000234` in the Symbol Tree 2. Select **Rename Function** 3. Enter: `main` 4. Press **Enter** **Update main's signature:** 1. In the Decompile window, right-click on `main` 2. Select **Edit Function Signature** 3. Change to: `int main(void)` 4. Click **OK** **Rename FUN_100030cc to stdio_init_all:** 1. Click on `FUN_100030cc` in the decompile window 2. Right-click → **Edit Function Signature** 3. Change name to: `stdio_init_all` 4. Change signature to: `bool stdio_init_all(void)` 5. Click **OK** **Rename FUN_10003100 to printf:** 1. Click on `FUN_10003100` 2. Right-click → **Edit Function Signature** 3. Change name to: `printf` 4. Check the **Varargs** checkbox (printf accepts variable arguments) 5. Click **OK** ##### Step 9: Examine the Optimized Code After renaming, the decompiled main should now look like: ```c int main(void) { stdio_init_all(); do { printf("age: %d\r\n", 0x2b); } while (true); } ``` **Critical observation:** Where did our `age` variable go? Original source code: ```c uint8_t age = 42; age = 43; ``` The compiler **optimized it completely away**! **Why?** 1. `age = 42` is immediately overwritten 2. The value `42` is never used 3. The compiler replaces `age` with the constant `43` (`0x2b`) 4. No variable allocation in memory is needed! ##### Step 10: Examine the Assembly Listing Click on the **Listing** window (shows assembly code): Find the instruction that loads `0x2b`: ```assembly 10000xxx movs r1, #0x2b 10000xxx ... 10000xxx bl printf ``` **What this does:** - `movs r1, #0x2b` - Moves the immediate value 0x2b (43) into register r1 - `bl printf` - Branches to printf, which expects format args in r1+ ##### Step 11: Document Your Findings Create a table of your observations: | Item | Value/Location | Notes | | --------------------- | -------------- | ------------------------------- | | Main function address | `0x10000234` | Entry point of program | | Age value (hex) | `0x2b` | Optimized constant | | Age value (decimal) | `43` | Original variable value | | Variable in memory? | No | Compiler optimized it away | | Printf address | `0x10003100` | Standard library function | | Format string | "age: %d\r\n" | Located in .rodata section | #### Expected Output After completing this exercise, you should be able to: - Successfully import and configure ARM binaries in Ghidra - Navigate the Symbol Tree and identify functions - Understand how compiler optimization removes unnecessary variables - Convert hexadecimal values to decimal - Rename functions for better code readability #### Questions for Reflection ###### Question 1: Why did the compiler optimize away the `age` variable? ###### Question 2: In what memory section would `age` have been stored if it wasn't optimized away? ###### Question 3: Where is the string "age: %d\r\n" stored, and why can't it be in RAM? ###### Question 4: What would happen if we had used `age` in a calculation before reassigning it to 43? #### Tips and Hints - Use **CTRL+F** in Ghidra to search for specific values or strings - The **Data Type Manager** window shows all recognized data types - If Ghidra's decompiler output looks wrong, try re-analyzing with different options - Remember: optimized code often looks very different from source code - The **Display** → **Function Graph** shows control flow visually #### Next Steps - Proceed to Exercise 2 to learn binary patching - Try analyzing `0x0008_uninitialized-variables.bin` to see how uninitialized variables behave - Explore the `.rodata` section to find string literals #### Additional Challenge Find the format string "age: %d\r\n" in Ghidra. What address is it stored at? How does the program reference this string in the assembly code? (Hint: Look for an `ldr` instruction that loads the string address into a register.)