Step-by-Step Guide: Analyzing "memory execution" Program with DDD

Prerequisites

  1. Security OS like Kali/Parrot 
  2. Ubuntu/Debian Linux environment
  3. Basic understanding of assembly language
  4. The target binary named memory_execution
  5. 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 hexedit

Memory 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_execution

For 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_stripped
Memory Analysis Exercises
Part 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_execution

Step 2: Launch DDD with the Program

Method A: Direct Launch

# Open DDD with the binary
ddd memory_execution

Method 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:

  1. Source Window (Top-left): Shows source code if available, otherwise assembly
  2. Data Window (Bottom-left): Memory and variable display
  3. Console Window (Bottom-right): GDB command interface
  4. Machine Code Window (Right): Assembly instructions
  5. Register Window (Top-right): CPU register values

Important DDD Menu Items:

  • ProgramRun: Start execution
  • ProgramInterrupt: Pause execution
  • ViewMachine Code Window: Show assembly
  • ViewRegister Window: Show CPU registers
  • ViewMemory Window: Examine memory

Step 4: Setting Up the Debugging Session

In DDD's Console (GDB commands):

gdb
# 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 breakpoints

Step 5: Step-by-Step Analysis Process

Phase 1: Initial Exploration

  1. Start the program:

    • Click "Run" button or type run in console
  2. Pause immediately after start:
    • Click "Interrupt" or press Ctrl+C in console
  3. 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 $rbp

Phase 2: Control Flow Analysis

  1. Step through instructions:

    • Step Into (F7): Execute one assembly instruction
    • Next Instruction (F8): Step over function calls
    • Continue (F9): Run until next breakpoint
  2. Trace function calls:
    • gdb

# Backtrace (call stack)
bt

# Step into function stepi # Step over function nexti

Phase 3: Memory Analysis

Open Memory Windows:

  1. ViewMemoryNew Memory Window
  2. Enter address to examine (e.g., $rsp for stack)
  3. Configure display format (hex, ASCII, etc.)

Common Memory Examinations:

gdb
# 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 section

Phase 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 continuously
display/x $rax display/x $rsp display/x $rip

Phase 5: Breakpoint Strategy

Set Strategic Breakpoints:

gdb
# 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 execve

Step 6: Advanced Analysis Techniques

A. Dynamic Function Tracing

gdb
# Trace all function calls
set logging on rbreak . run # Or trace specific library rbreak libc:* # All libc functions

B. Memory Mapping Analysis

gdb
# 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>/maps

C. Data Flow Analysis

  1. Watchpoints for data changes:

        gdb
# Watch when specific memory address changes
watch *0x7fffffffde00

# Watch when register points to changing memory
watch *(int*)0x7fffffffde00

D. Code Injection Detection

gdb
# 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:

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
run

Load in DDD:

ddd -x analysis.gdb memory_execution

Step 8: Documentation and Reporting

What to Document:

  1. Program Flow: Control flow graph
  2. Memory Operations: Allocation, deallocation patterns
  3. System Calls: Which syscalls are made and when
  4. Anomalies: Unusual code patterns
  5. Data Structures: How data is organized in memory

Use DDD's Built-in Tools:

  • EditCopy to capture screenshots
  • FileSave Window As to save memory dumps
  • Use Graphical History to visualize execution path

Step 9: Common Analysis Scenarios

Scenario 1: Buffer Overflow Detection

gdb
# Watch for stack corruption
watch $rsp
# Single-step through string operations
stepi  # Watch each instruction

Scenario 2: Code Injection Analysis

gdb
# Monitor executable memory regions
info proc mappings
# Look for RWX permissions (bad!)

Scenario 3: Anti-Debugging Detection

gdb
# Break on ptrace (common anti-debug)
break ptrace
# Check for timing checks
break gettimeofday
break clock_gettime

Step 10: Cleanup and Best Practices

Before Closing:

  1. Save your breakpoints:

        gdb
save breakpoints breakpoints.txt

Save command history:

gdb
show commands  # Last 10 commands
set history filename gdb_history.txt
set history save on

Best Practices for Students:

  1. Start Simple: Begin with basic stepping before advanced techniques
  2. Take Notes: Document each interesting finding
  3. Use Multiple Views: Cross-reference registers, memory, and code
  4. Validate Hypotheses: Test assumptions with additional breakpoints
  5. Collaborate: Share findings with classmates for different perspectives

Quick Reference Commands

ActionDDD ButtonGDB Command
RunF5run
Step InstructionF7stepi
Next InstructionF8nexti
ContinueF9continue
InterruptCtrl+Cinterrupt
View RegistersView→Register Windowinfo registers
View MemoryView→Memory Windowx/20x $rsp
View AssemblyView→Machine Codedisas
Set BreakpointRight-click→Set Breakpointbreak *0x1234

Common Pitfalls and Solutions

  1. Program exits too quickly: Set breakpoint at _start or main
  2. No symbols available: Use disas to view assembly directly
  3. Can't see memory changes: Use watchpoints instead of breakpoints
  4. DDD crashes: Use gdb directly 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.