Prerequisites
- Security OS like Kali/Parrot
- Ubuntu/Debian Linux environment
- Basic understanding of assembly language
- The target binary named
memory_execution Objectives of this analysis
Step 1: Installation and Setup
Install Required Tools
# Update package list
sudo apt update
# Install DDD (Data Display Debugger)
sudo apt install ddd
# Install additional debugging tools
sudo apt install gdb binutils gcc-multilib
# Install hex editor (optional but useful)
sudo apt install bless hexeditMemory Analysis Program for DDD Tutorial
/** * memory_execution.c
*
* A program designed for teaching memory analysis with DDD.
* Contains various memory operations for students to analyze:
* - Stack operations
* - Heap allocations
* - Memory corruption vulnerabilities
* - Function calls and stack frames
* - Dynamic memory management
*
* Compile with: gcc -g -fno-stack-protector -z execstack -no-pie -o memory_exec memory_execution.c
* For analysis only - contains intentional vulnerabilities!
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Global variables - in data segment */
int global_initialized = 42;
int global_uninitialized;
char global_buffer[32] = "Global String";
/* Function prototypes - ADDED MISSING PROTOTYPE HERE */
void stack_operations();
void heap_operations();
void vulnerable_function(char *input);
void safe_function();
int recursive_function(int n);
void memory_layout_demo();
void modify_via_pointer(int *ptr); /* ADDED THIS LINE - FIXES THE WARNING */
int main(int argc, char *argv[]) {
printf("=== Memory Execution Analysis Program ===\n");
printf("PID: %d\n", getpid());
printf("Compile command: gcc -g -fno-stack-protector -z execstack -no-pie\n");
/* Part 1: Basic Stack Operations */
printf("\n[1] Analyzing Stack Operations...\n");
stack_operations();
/* Part 2: Heap Operations */
printf("\n[2] Analyzing Heap Operations...\n");
heap_operations();
/* Part 3: Vulnerable Function (for buffer overflow analysis) */
printf("\n[3] Analyzing Vulnerable Function...\n");
char small_buffer[16];
strcpy(small_buffer, "NormalInput");
vulnerable_function(small_buffer);
/* Try with longer input to trigger overflow */
printf("\n[4] Triggering Buffer Overflow...\n");
char overflow_input[64];
memset(overflow_input, 'A', 63);
overflow_input[63] = '\0';
vulnerable_function(overflow_input);
/* Part 5: Recursive Function (stack growth analysis) */
printf("\n[5] Analyzing Recursive Stack Growth...\n");
printf("Factorial(5) = %d\n", recursive_function(5));
/* Part 6: Safe Function */
printf("\n[6] Analyzing Safe Function...\n");
safe_function();
/* Part 7: Memory Layout */
printf("\n[7] Displaying Memory Layout...\n");
memory_layout_demo();
/* Part 8: Interactive Analysis */
printf("\n[8] Interactive Analysis Mode\n");
printf("Program paused for manual analysis in DDD.\n");
printf("Set breakpoints and examine:\n");
printf(" - Stack pointers\n");
printf(" - Heap allocations\n");
printf(" - Register values\n");
printf(" - Memory contents\n");
printf("\nPress Ctrl+C in DDD to pause execution and inspect memory.\n");
/* Infinite loop for interactive analysis */
int counter = 0;
while(1) {
printf("Running... Cycle %d (Press Ctrl+C in DDD to interrupt)\n", counter++);
sleep(3); /* Give time for analysis */
}
return 0;
}
/**
* Helper function to demonstrate pointer modification
* MOVED THIS FUNCTION BEFORE stack_operations TO AVOID PROTOTYPE NEED
*/
void modify_via_pointer(int *ptr) {
printf(" [modify_via_pointer] Original value: %d\n", *ptr);
*ptr = *ptr * 2;
printf(" [modify_via_pointer] Modified value: %d\n", *ptr);
}
/**
* Demonstrates stack frame creation and local variables
*/
void stack_operations() {
/* Local variables on stack */
int local_int = 100;
char local_buffer[32] = "Stack String";
int local_array[5] = {1, 2, 3, 4, 5};
char *local_pointer = "Pointer on stack";
printf(" Local int address: %p, value: %d\n", (void*)&local_int, local_int);
printf(" Local buffer address: %p, value: %s\n", (void*)&local_buffer, local_buffer);
printf(" Local array address: %p\n", (void*)&local_array);
printf(" Local pointer address: %p, points to: %p\n", (void*)&local_pointer, (void*)local_pointer);
/* Nested scope - different stack frame area */
{
int nested_var = 999;
printf(" Nested var address: %p, value: %d\n", (void*)&nested_var, nested_var);
}
/* Pass by reference - analyze pointer usage */
modify_via_pointer(&local_int);
/* Demonstrate stack growing downward */
int another_local = 777;
printf(" Another local address: %p\n", (void*)&another_local);
/* Show stack pointer values */
register void *sp asm ("rsp");
register void *bp asm ("rbp");
printf(" Current RSP (stack pointer): %p\n", sp);
printf(" Current RBP (base pointer): %p\n", bp);
printf(" Stack frame size (approx): %ld bytes\n", (long)bp - (long)sp);
}
/**
* Demonstrates dynamic memory allocation on heap
*/
void heap_operations() {
/* Allocate memory on heap */
int *heap_int = (int*)malloc(sizeof(int));
char *heap_buffer = (char*)malloc(64 * sizeof(char));
int *heap_array = (int*)malloc(5 * sizeof(int));
/* Check allocation success */
if (!heap_int || !heap_buffer || !heap_array) {
fprintf(stderr, "Memory allocation failed!\n");
exit(1);
}
/* Initialize heap memory */
*heap_int = 999;
strcpy(heap_buffer, "Heap Allocated String");
for (int i = 0; i < 5; i++) {
heap_array[i] = i * 10;
}
printf(" Heap int address: %p, value: %d\n", (void*)heap_int, *heap_int);
printf(" Heap buffer address: %p, value: %s\n", (void*)heap_buffer, heap_buffer);
printf(" Heap array address: %p\n", (void*)heap_array);
/* Show heap metadata (glibc specific - for educational purposes) */
printf(" Heap metadata analysis:\n");
printf(" Actual allocation size may be larger due to alignment and metadata\n");
/* Demonstrate use-after-free (intentional for analysis) */
printf("\n Demonstrating use-after-free scenario:\n");
free(heap_buffer);
printf(" Freed heap buffer at: %p\n", (void*)heap_buffer);
printf(" WARNING: heap_buffer now points to freed memory!\n");
/* Access freed memory - UAF vulnerability */
printf(" Accessing freed memory (dangerous!): %c\n", heap_buffer[0]);
/* Demonstrate double-free vulnerability (commented out for safety) */
/*
printf("\n Attempting double-free (will crash if enabled):\n");
free(heap_buffer); // Double free
*/
/* Memory leak (intentional) - heap_int and heap_array are not freed */
printf("\n Memory leak created: heap_int (%p) and heap_array (%p) not freed\n",
(void*)heap_int, (void*)heap_array);
/* Show heap growth */
void *new_allocation = malloc(100);
printf(" New allocation address: %p\n", (void*)new_allocation);
free(new_allocation);
}
/**
* Intentionally vulnerable function for buffer overflow analysis
*/
void vulnerable_function(char *input) {
char buffer[16]; /* Small buffer */
int canary = 0xDEADBEEF; /* Stack canary (ineffective without protection) */
int control_flow = 0;
char *buffer_pointer = buffer;
printf("\n [vulnerable_function] Analysis:\n");
printf(" Input string length: %lu\n", strlen(input));
printf(" Buffer address: %p\n", (void*)buffer);
printf(" Canary address: %p, value: 0x%08X\n", (void*)&canary, canary);
printf(" Control flow address: %p, value: %d\n", (void*)&control_flow, control_flow);
printf(" Return address is at higher memory address\n");
/* Show stack layout before overflow */
printf("\n Stack layout before strcpy:\n");
printf(" [Lower addresses]\n");
printf(" buffer[0-15]: 16 bytes for input\n");
printf(" buffer_pointer: 8 bytes\n");
printf(" canary: 4 bytes (0xDEADBEEF)\n");
printf(" control_flow: 4 bytes (0)\n");
printf(" [Higher addresses] Saved RBP, Return Address...\n");
/* Vulnerable strcpy - no bounds checking! */
printf("\n Executing: strcpy(buffer, input);\n");
strcpy(buffer, input);
printf("\n After overflow analysis:\n");
printf(" Buffer contents: ");
for (int i = 0; i < 32; i++) {
if (buffer[i] >= 32 && buffer[i] <= 126)
printf("%c", buffer[i]);
else
printf(".");
}
printf("\n");
printf(" Canary after copy: 0x%08X\n", canary);
printf(" Control flow after copy: %d\n", control_flow);
/* Check if overflow occurred */
if (canary != 0xDEADBEEF) {
printf(" [ALERT] Stack canary corrupted! Buffer overflow detected!\n");
printf(" Overwrite distance: %ld bytes into canary\n",
(char*)&canary - buffer + 4 - strlen(input));
}
if (control_flow != 0) {
printf(" [ALERT] Control flow variable modified! Potential code execution!\n");
}
/* Calculate approximate return address location */
void *ret_addr_approx = (char*)&control_flow + sizeof(control_flow) + sizeof(void*) + sizeof(void*);
printf(" Approximate return address location: %p\n", ret_addr_approx);
}
/**
* Safe function with bounds checking
*/
void safe_function() {
char buffer[16];
char input[] = "SafeInputStringThatIsTooLong";
printf(" [safe_function] Demonstrating safe coding practices:\n");
printf(" Buffer size: 16 bytes\n");
printf(" Input length: %lu bytes\n", strlen(input));
/* Safe copy with bounds checking */
strncpy(buffer, input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; /* Ensure null termination */
printf(" Safe buffer contents: %s\n", buffer);
printf(" Input truncated safely.\n");
}
/**
* Recursive function to demonstrate stack growth
*/
int recursive_function(int n) {
int local_recursive = n;
char recursive_buffer[256]; /* Large buffer to see stack growth */
/* Fill buffer to see in memory */
memset(recursive_buffer, 'R', 255);
recursive_buffer[255] = '\0';
register void *sp asm ("rsp");
printf(" Recursion depth %d - Stack frame at: %p, RSP: %p\n",
n, (void*)&local_recursive, sp);
if (n <= 1) {
printf(" Base case reached\n");
return 1;
}
/* Show stack consumption */
int result = n * recursive_function(n - 1);
printf(" Returning from depth %d with result: %d\n", n, result);
return result;
}
/**
* Display memory addresses of various segments
*/
void memory_layout_demo() {
/* Code segment - function addresses */
printf(" Code segment (.text):\n");
printf(" main() address: %p\n", (void*)main);
printf(" stack_operations() address: %p\n", (void*)stack_operations);
printf(" vulnerable_function() address: %p\n", (void*)vulnerable_function);
/* Data segment - global variables */
printf("\n Data segment (.data, .bss):\n");
printf(" global_initialized address: %p, value: %d\n",
(void*)&global_initialized, global_initialized);
printf(" global_uninitialized address: %p, value: %d\n",
(void*)&global_uninitialized, global_uninitialized);
printf(" global_buffer address: %p, value: %s\n",
(void*)&global_buffer, global_buffer);
/* Heap segment - allocate something */
printf("\n Heap segment:\n");
void *heap_demo = malloc(1);
printf(" Heap allocation address: %p\n", heap_demo);
free(heap_demo);
/* Stack segment - local variable */
printf("\n Stack segment:\n");
int stack_demo;
printf(" Stack variable address: %p\n", (void*)&stack_demo);
/* Shared libraries */
printf("\n Shared libraries:\n");
printf(" printf() address: %p\n", (void*)printf);
printf(" malloc() address: %p\n", (void*)malloc);
/* Demonstrate ASLR effect */
printf("\n ASLR note: Addresses will change on each run due to ASLR\n");
printf(" Compile with '-no-pie' to reduce ASLR effect on code segment\n");
}Compilation Instructions
For Basic DDD Analysis (Recommended)
# Compile with debug symbols and disabled protections
gcc -g -fno-stack-protector -z execstack -no-pie -o memory_execution memory_execution.c
# Or use the makefile
make memory_executionFor Different Analysis Scenarios
# 1. With all security features (for comparison)
gcc -g -fstack-protector-strong -z noexecstack -D_FORTIFY_SOURCE=2 -o memory_execution_secure memory_execution.c
# 2. With Address Sanitizer (detects memory errors)
gcc -g -fsanitize=address -o memory_execution_asan memory_execution.c
# 3. Stripped binary (no symbols - advanced analysis)
gcc -g -fno-stack-protector -z execstack -o memory_execution_debug memory_execution.c
strip --strip-all memory_execution_strippedMemory Analysis ExercisesPart 1: Stack Analysis 1. What are the addresses of local variables in `stack_operations()`? 2. How much space is allocated for the stack frame? 3. Track the value of RSP as you step through functions. Part 2: Heap Analysis 1. Where are heap allocations located relative to the stack? 2. What happens during `free()`? Monitor the memory after freeing. 3. Identify the memory leak in the program. Part 3: Buffer Overflow 1. What is the exact input length that corrupts the canary? 2. Can you modify the control flow variable through the overflow? 3. What would happen if you overflowed the return address? Part 4: Function Calls 1. How are arguments passed to functions (registers vs stack)? 2. What is the purpose of the prologue and epilogue in functions? 3. Trace the recursive calls and observe stack growth. Part 5: Memory Layout 1. Map all memory segments: text, data, heap, stack. 2. What is the address range of each segment? 3. How does ASLR affect these addresses?Advanced Challenges
1. Can you write shellcode into the buffer and execute it? 2. How would you fix the vulnerabilities in this program? 3. Implement a stack canary protection mechanism.
Verify Binary Properties
# Check file type
file memory_execution# Check for security features checksec memory_execution # List sections readelf -S memory_execution
# View program headers (like we did for calc)
readelf -l memory_executionStep 2: Launch DDD with the Program
Method A: Direct Launch
# Open DDD with the binary
ddd memory_executionMethod B: Load Program After Launch
# Start DDD first
ddd
# Then in DDD: File → Open Program → Select "memory_execution"Step 3: Initial DDD Interface Tour
DDD Windows Layout:
- Source Window (Top-left): Shows source code if available, otherwise assembly
- Data Window (Bottom-left): Memory and variable display
- Console Window (Bottom-right): GDB command interface
- Machine Code Window (Right): Assembly instructions
- Register Window (Top-right): CPU register values
Important DDD Menu Items:
- Program → Run: Start execution
- Program → Interrupt: Pause execution
- View → Machine Code Window: Show assembly
- View → Register Window: Show CPU registers
- View → Memory Window: Examine memory
Step 4: Setting Up the Debugging Session
In DDD's Console (GDB commands):
# Disassemble main function (if symbols exist) disas main
# Or find entry point
info file
# Set breakpoint at entry
break *_start
# Set breakpoint at main (if available)
break main
# Break at specific address
break *0x1040
# Show all breakpoints
info breakpointsStep 5: Step-by-Step Analysis Process
Phase 1: Initial Exploration
Start the program:
- Click "Run" button or type
runin console - Pause immediately after start:
- Click "Interrupt" or press Ctrl+C in console
- Examine initial state:
- gdb
# Show current instruction
x/i $pc
# Show next 10 instructions
x/10i $pc
# View stack pointer
print/x $rsp
# View base pointer
print/x $rbpPhase 2: Control Flow Analysis
Step through instructions:
- Step Into (F7): Execute one assembly instruction
- Next Instruction (F8): Step over function calls
- Continue (F9): Run until next breakpoint
- Trace function calls:
- gdb
# Backtrace (call stack)
bt
# Step into function
stepi
# Step over function
nextiPhase 3: Memory Analysis
Open Memory Windows:
- View → Memory → New Memory Window
- Enter address to examine (e.g.,
$rspfor stack) - Configure display format (hex, ASCII, etc.)
Common Memory Examinations:
# Examine stack (20 hex words)
x/20x $rsp
# Examine heap (if malloc is called)
# First, set breakpoint at malloc
break malloc
# After hitting breakpoint, examine returned address
x/20x $rax # rax contains malloc return value
# Examine specific memory region
x/100x 0x402150 # Start of .text sectionPhase 4: Register Analysis
- Open Register Window: View → Register Window
- Monitor changes to key registers:
- $rax: Return value
- $rdi, $rsi, $rdx, $rcx: Function arguments
- $rip: Instruction pointer$rsp: Stack pointer
- Watch specific registers: gdb
# Display register value continuouslydisplay/x $rax
display/x $rsp
display/x $ripPhase 5: Breakpoint Strategy
Set Strategic Breakpoints:
# System calls
catch syscall
# Memory allocation
break malloc
break calloc
break mmap
# String operations
break strcpy
break strcat
break memcpy
# File operations (if any)
break open
break read
break write
# Process operations
break fork
break execveStep 6: Advanced Analysis Techniques
A. Dynamic Function Tracing
# Trace all function callsset logging on
rbreak .
run
# Or trace specific library
rbreak libc:* # All libc functionsB. Memory Mapping Analysis
# Show memory maps during execution
info proc mappings
# Or from shell while program runs:
# In another terminal:
# ps aux | grep memory_execution
# cat /proc/<PID>/mapsC. Data Flow Analysis
Watchpoints for data changes:
# Watch when specific memory address changes
watch *0x7fffffffde00
# Watch when register points to changing memory
watch *(int*)0x7fffffffde00D. Code Injection Detection
# Check for self-modifying code
# Set breakpoint at memory write to code section
break *0x555555554000 if $_wp("0x555555554000", 8)Step 7: Creating Analysis Scripts
Save GDB Commands to File:
Create analysis.gdb:
# Initial breakpoints
break _start
break main
# Display settings
display/x $rax
display/x $rbx
display/x $rcx
display/x $rdx
display/x $rsi
display/x $rdi
display/x $rsp
display/x $rbp
display/x $rip
# Run
runLoad in DDD:
ddd -x analysis.gdb memory_executionStep 8: Documentation and Reporting
What to Document:
- Program Flow: Control flow graph
- Memory Operations: Allocation, deallocation patterns
- System Calls: Which syscalls are made and when
- Anomalies: Unusual code patterns
- Data Structures: How data is organized in memory
Use DDD's Built-in Tools:
- Edit → Copy to capture screenshots
- File → Save Window As to save memory dumps
- Use Graphical History to visualize execution path
Step 9: Common Analysis Scenarios
Scenario 1: Buffer Overflow Detection
# Watch for stack corruption
watch $rsp
# Single-step through string operations
stepi # Watch each instructionScenario 2: Code Injection Analysis
# Monitor executable memory regions
info proc mappings
# Look for RWX permissions (bad!)Scenario 3: Anti-Debugging Detection
# Break on ptrace (common anti-debug)
break ptrace
# Check for timing checks
break gettimeofday
break clock_gettimeStep 10: Cleanup and Best Practices
Before Closing:
Save your breakpoints:
save breakpoints breakpoints.txtSave command history:
show commands # Last 10 commands
set history filename gdb_history.txt
set history save onBest Practices for Students:
- Start Simple: Begin with basic stepping before advanced techniques
- Take Notes: Document each interesting finding
- Use Multiple Views: Cross-reference registers, memory, and code
- Validate Hypotheses: Test assumptions with additional breakpoints
- Collaborate: Share findings with classmates for different perspectives
Quick Reference Commands
| Action | DDD Button | GDB Command |
|---|---|---|
| Run | F5 | run |
| Step Instruction | F7 | stepi |
| Next Instruction | F8 | nexti |
| Continue | F9 | continue |
| Interrupt | Ctrl+C | interrupt |
| View Registers | View→Register Window | info registers |
| View Memory | View→Memory Window | x/20x $rsp |
| View Assembly | View→Machine Code | disas |
| Set Breakpoint | Right-click→Set Breakpoint | break *0x1234 |
Common Pitfalls and Solutions
- Program exits too quickly: Set breakpoint at
_startormain - No symbols available: Use
disasto view assembly directly - Can't see memory changes: Use watchpoints instead of breakpoints
- DDD crashes: Use
gdbdirectly for complex operations, then return to DDD
This comprehensive guide should help you systematically analyze the "memory execution" program using DDD. The key is to be methodical and document each step of the analysis.
















