This tutorial provides step-by-step instructions for analyzing a simple C program using x64dbg, focusing on memory allocation, initialization, and deallocation. The program dynamically allocates an array, initializes it with squared values, prints the fifth element, and frees the memory. We'll use x64dbg to set breakpoints, track heap allocations, observe array initialization, examine memory contents, and analyze the memory state after deallocation.
- x64dbg: Download and install from https://x64dbg.com.
- C Compiler: Use a compiler like MinGW to compile the provided C program into a 64-bit executable.
- Program Code:
#include <stdio.h> #include <stdlib.h> int main() { int* numbers = malloc(10 * sizeof(int)); for (int i = 0; i < 10; i++) { numbers[i] = i * i; } printf("Fifth element: %d\n", numbers[4]); free(numbers); return 0; }
- Compile the program (e.g.,
gcc -o memory_analysis.exe memory_analysis.c) to generate a 64-bit executable.
- Open x64dbg (ensure you're using the 64-bit version, x64dbg.exe).
- Go to File > Open and select the compiled
memory_analysis.exe. - The debugger pauses at the program entry point. The CPU window displays the disassembled code, and the Registers window shows the current state of CPU registers.
- Locate the
malloccall:- In the CPU window, press
Ctrl+Gto open the "Go to Expression" dialog. - Type
mallocand press Enter to jump to themallocfunction's address in the linked library (e.g.,msvcrt.dll). - Alternatively, if you know the source code, find the
callinstruction formallocin themainfunction by stepping through the code or searching for the pattern (e.g.,call <address>after amovinstruction setting up the size argument).
- In the CPU window, press
- Set a breakpoint at the end of the
malloccall:- Scroll to the instruction just after the
call malloc(typically aretor the next instruction inmain). - The return value of
malloc(the allocated memory address) is stored in theRAXregister. - Right-click the instruction after the
call mallocand select Toggle Breakpoint (or pressF2). - Verify the breakpoint in the Breakpoints tab (View > Breakpoints).
- Scroll to the instruction just after the
- Locate the
freecall:- In the CPU window, press
Ctrl+Gagain and typefreeto jump to thefreefunction. - Alternatively, step through the
mainfunction to find thecall freeinstruction.
- In the CPU window, press
- Set a breakpoint:
- Right-click the
call freeinstruction and select Toggle Breakpoint (or pressF2). - Confirm the breakpoint in the Breakpoints tab.
- Right-click the
- Run the program:
- Press
F9to run the program until the first breakpoint (aftermalloc). - The debugger pauses after the
malloccall, with the allocated address inRAX.
- Press
- Inspect the
RAXregister:- In the Registers window, note the value in
RAX. This is the base address of the allocated heap memory for thenumbersarray (e.g.,0x000001A2C3D04000).
- In the Registers window, note the value in
- View the allocated memory in the Dump window:
- In the Dump window (bottom-left in x64dbg), right-click and select Follow in Dump > RAX.
- The Dump window now shows the memory at the address returned by
malloc. It may initially contain uninitialized data (random values).
- Step through the loop:
- Press
F8(Step Over) to execute instructions one by one, or set a breakpoint at the loop's store instruction (e.g.,mov [rax + offset], value). - To focus on
numbers[4] = 16:- The loop calculates
i * iand stores it innumbers[i]. - For
i = 4, the value is4 * 4 = 16(0x10 in hexadecimal).
- The loop calculates
- Find the instruction that writes to
numbers[4](e.g.,mov [rax + 0x10], 0x10, since4 * sizeof(int) = 16 bytesoffset). - Set a breakpoint on this instruction by right-clicking and selecting Toggle Breakpoint.
- Press
- Run to the breakpoint:
- Press
F9to continue execution until the breakpoint. - When the debugger pauses, verify in the Dump window that the value
0x10(16) is written to the memory addressRAX + 0x10.
- Press
- Inspect the array in the Dump window:
- After the loop completes (step through or set a breakpoint after the loop), go to the Dump window.
- Follow the address in
RAXagain (right-click > Follow in Dump > RAX). - The memory should contain the initialized values:
numbers[0] = 0(0x00)numbers[1] = 1(0x01)numbers[2] = 4(0x04)numbers[3] = 9(0x09)numbers[4] = 16(0x10)numbers[5] = 25(0x19)numbers[6] = 36(0x24)numbers[7] = 49(0x31)numbers[8] = 64(0x40)numbers[9] = 81(0x51)
- Each value is a 4-byte integer, so the values are stored at 4-byte intervals (e.g.,
RAX,RAX+4,RAX+8, etc.).
- Verify in the Stack or Memory Map:
- In the Memory Map tab (View > Memory Map), locate the heap segment containing the allocated memory (look for a
.heapregion). - Double-click the heap region to view its contents, confirming the initialized values.
- In the Memory Map tab (View > Memory Map), locate the heap segment containing the allocated memory (look for a
- Run to the
freebreakpoint:- Press
F9to continue execution until thecall freebreakpoint.
- Press
- Step over the
freecall:- Press
F8to execute thefreecall. - The memory at the address in
RAXis now deallocated.
- Press
- Inspect the memory in the Dump window:
- Go back to the Dump window and follow the same address (
RAXfrom themallocstep). - The memory may still contain the previous values (e.g.,
0x10fornumbers[4]), asfreedoes not zero out the memory. However, this memory is no longer valid for the program to access. - In some cases, the heap manager may overwrite the memory with debug patterns (e.g.,
0xFEEEFEEEin Windows debug builds).
- Go back to the Dump window and follow the same address (
- Check the Memory Map:
- In the Memory Map tab, verify if the heap region is still marked as allocated or if it has been released. The exact behavior depends on the heap manager.
- Review findings:
- You tracked the heap allocation by observing the address in
RAXaftermalloc. - You confirmed the initialization of
numbers[4] = 16in the memory dump. - You examined the full array contents after the loop.
- You observed that after
free, the memory may retain its values but is no longer valid.
- You tracked the heap allocation by observing the address in
- Optional: Test for use-after-free:
- If you modify the program to access
numbersafterfree, you can set a breakpoint on that access and observe undefined behavior in x64dbg (e.g., crashes or garbage data).
- If you modify the program to access
- x64dbg Shortcuts:
F2: Toggle breakpoint.F7: Step into (follow function calls).F8: Step over (execute function calls without stepping into them).F9: Run until the next breakpoint.Ctrl+G: Go to address or symbol.
- Heap Behavior: The heap manager may not immediately release memory to the OS after
free. The memory contents may remain unchanged until reused. - Debugging Symbols: If available, debugging symbols (e.g., PDB files) make it easier to locate
mallocandfreecalls. Usegcc -gwhen compiling to include symbols. - Safety: Ensure you’re running the program in a safe, isolated environment, as debugging involves low-level memory access.
This exercise demonstrated how to use x64dbg to analyze memory allocation, initialization, and deallocation in a C program. By setting breakpoints on malloc and free, tracking the heap address in RAX, and inspecting the memory dump, you gained insight into the program’s memory management. These skills are essential for reverse engineering and debugging memory-related issues in real-world applications.