Exploring Free Memory Regions in Windows Processes
When analyzing software vulnerabilities, it’s crucial to understand the memory layout of a process. One aspect of this is identifying free memory regions, which can be exploited by attackers to execute arbitrary code. In this blog post, we’ll explore a program written in C that lists all processes running on a Windows system and identifies their free memory regions with respective permissions which is lastly saved in csv file.
Understanding the Code
Include Statements
1
2
3
4
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
#include <stdio.h>
These are standard C and Windows API headers necessary for the program’s functionality.
Helper Function: ProtectionToString
1
2
3
4
5
6
7
8
const TCHAR* ProtectionToString(DWORD protect) {
// Function to get a string representation of the memory protection flags
switch (protect) {
// Cases for different protection types
// (e.g., PAGE_READONLY, PAGE_READWRITE)
default: return _T("Unknown");
}
}
This function converts the memory protection flags returned by the Windows API into human-readable strings. These flags describe the access rights for a memory region (e.g., read, write, execute).
Helper Function: FindFreeMemoryRegions
1
2
3
4
5
void FindFreeMemoryRegions(FILE* fp, DWORD processID) {
// Function to find and store free memory regions in a given process
// Opens the process with required access rights
// Loops through all memory regions and prints information about free regions
}
This function finds and stores information about free memory regions in a given process. It opens the process using OpenProcess
, loops through all memory regions using VirtualQueryEx
, and prints information about free regions to a specified file.
Main Function: ListProcessesAndFindFreeMemoryRegions
1
2
3
4
5
6
void ListProcessesAndFindFreeMemoryRegions() {
// Function to list all processes and find free memory regions
// Opens a file for writing output
// Takes a snapshot of all processes in the system
// Retrieves information about each process and calls FindFreeMemoryRegions
}
This function lists all processes running on the system, opens a file for writing output, and then retrieves information about each process using CreateToolhelp32Snapshot
and Process32First/Process32Next
. For each process, it calls FindFreeMemoryRegions
to identify and store information about free memory regions.
Entry Point: main
1
2
3
4
5
int main(void) {
// Entry point of the program
ListProcessesAndFindFreeMemoryRegions();
return 0;
}
Running the Program
To run the program, compile it using a C compiler (e.g., GCC) and execute the generated executable. The program will list all processes running on the system and identify their free memory regions, storing the information in a CSV file named free_memory_regions.csv
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
#include <stdio.h>
// Function to get a string representation of the memory protection flags
const TCHAR* ProtectionToString(DWORD protect) {
switch (protect) {
case PAGE_NOACCESS: return _T("No Access");
case PAGE_READONLY: return _T("Read-Only");
case PAGE_READWRITE: return _T("Read/Write");
case PAGE_WRITECOPY: return _T("Write Copy");
case PAGE_EXECUTE: return _T("Execute");
case PAGE_EXECUTE_READ: return _T("Execute/Read");
case PAGE_EXECUTE_READWRITE: return _T("Execute/Read/Write");
case PAGE_EXECUTE_WRITECOPY: return _T("Execute/Write Copy");
case PAGE_GUARD: return _T("Guard Page");
case PAGE_NOCACHE: return _T("No Cache");
case PAGE_WRITECOMBINE: return _T("Write Combine");
default: return _T("Unknown");
}
}
// Function to find and store free memory regions in a given process
void FindFreeMemoryRegions(FILE* fp, DWORD processID) {
HANDLE hProcess;
MEMORY_BASIC_INFORMATION mbi;
LPVOID address = NULL;
SIZE_T result;
// Open the process with required access rights
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
if (hProcess == NULL) {
_tprintf(_T("Could not open process with PID %u. Error: %u\n"), processID, GetLastError());
return;
}
// Loop through all memory regions
while (address < (LPVOID)0x7FFFFFFF &&
(result = VirtualQueryEx(hProcess, address, &mbi, sizeof(mbi))) != 0) {
if (mbi.State == MEM_FREE) {
_ftprintf(fp, _T("%u,0x%p,0x%zx,%s\n"),
processID, mbi.BaseAddress, mbi.RegionSize, ProtectionToString(mbi.Protect));
}
address = (LPVOID)((DWORD_PTR)mbi.BaseAddress + mbi.RegionSize);
}
CloseHandle(hProcess);
}
// Function to list all processes and find free memory regions
void ListProcessesAndFindFreeMemoryRegions() {
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
FILE* fp = _tfopen(_T("free_memory_regions.csv"), _T("w"));
if (fp == NULL) {
_tprintf(_T("Failed to open output file.\n"));
return;
}
// Write CSV header
_ftprintf(fp, _T("PID,BaseAddress,RegionSize,Protection\n"));
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
_tprintf(_T("CreateToolhelp32Snapshot (of processes) failed.\n"));
fclose(fp);
return;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful.
if (!Process32First(hProcessSnap, &pe32)) {
_tprintf(_T("Process32First failed.\n")); // Show cause of failure
CloseHandle(hProcessSnap); // Clean the snapshot object
fclose(fp);
return;
}
// Now walk the snapshot of processes, and find free memory regions for each process.
do {
_tprintf(_T("Processing PID: %u (%s)\n"), pe32.th32ProcessID, pe32.szExeFile);
FindFreeMemoryRegions(fp, pe32.th32ProcessID);
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
fclose(fp);
}
int main(void) {
ListProcessesAndFindFreeMemoryRegions();
return 0;
}
How to Compile and Run Compile: Save the code in a file named find_free_memory_all.cpp
. Use a compiler like MinGW or MSVC to compile the code:
- Using MinGW:
gcc -o find_free_memory_all.exe find_free_memory_all.cpp
- Using MSVC: Open Developer Command Prompt and use
cl /EHsc find_free_memory_all.cpp
Run: Execute the compiled program:
find_free_memory_all.exe
Explanation:
ProtectionToString
: Converts the memory protection flags to a human-readable string.FindFreeMemoryRegions
: Finds and stores free memory regions of a given process in the CSV file.ListProcessesAndFindFreeMemoryRegions
: Takes a snapshot of all processes, iterates through each process, and callsFindFreeMemoryRegions
to find and store the free memory regions for each process.
The program creates a CSV file named free_memory_regions.csv
with the following columns:
PID
: The process ID.BaseAddress
: The base address of the free memory region.RegionSize
: The size of the free memory region.Protection
: The protection attribute of the memory region.
This way, the program will iterate through all processes, find free memory regions, and store the results in a CSV file.
Conclusion
Understanding the memory layout of a process is crucial for vulnerability researchers. This program provides a starting point for exploring memory regions in Windows processes, which can help identify potential vulnerabilities and improve overall system security.
Source
https://github.com/psyrun/Process-Privelege-Enumeration.git
#peacout #opensource