Skip to content

A .NET 10 library and Windows Forms test application for reading and writing the memory of other processes on Windows. ApexMemory wraps the Windows API functions ReadProcessMemory, WriteProcessMemory, VirtualQueryEx, and VirtualProtectEx into a straightforward C# class, and ships with a GUI that puts every feature at your fingertips.

License

Notifications You must be signed in to change notification settings

johnbrodowski/ApexMemory

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ApexMemory

A .NET 10 library and Windows Forms test application for reading and writing the memory of other processes on Windows. ApexMemory wraps the Windows API functions ReadProcessMemory, WriteProcessMemory, VirtualQueryEx, and VirtualProtectEx into a straightforward C# class, and ships with a GUI that puts every feature at your fingertips.


Warning — Read Before Use

Directly manipulating another process's memory is powerful but dangerous. Understand the following risks before you proceed:

  • Application crashes. Writing to the wrong address, or writing the wrong number of bytes, can corrupt a process's internal state and cause it to crash instantly or behave unpredictably later.
  • Data loss. If the target process has unsaved work (a document, a database transaction, game progress), a crash caused by a bad write will lose that data.
  • System instability. Writing into system-critical processes (services, the kernel-mode address space reachable via certain drivers) can blue-screen the machine or leave it in a state that requires a reboot.
  • Security software conflicts. Anti-cheat engines, antivirus products, and endpoint-detection tools actively monitor for cross-process memory access. Using this library may trigger alerts, quarantine your executable, or terminate the target process.
  • Legal and policy implications. Modifying the memory of online games violates virtually every game's terms of service and can result in permanent bans. In some jurisdictions, distributing tools designed to tamper with software may carry legal consequences.
  • No undo. There is no built-in way to roll back a memory write. Once bytes are overwritten, the original values are gone unless you saved them yourself beforehand.

Use this library only on processes you own or have explicit authorization to inspect, in environments where you accept the consequences of a bad write.


image

Solution Structure

ApexMemory.sln
  ApexMemory/          -- Class library (ApexMemory.dll)
    Memory.cs          -- Core read/write/scan API
    AlertMailer.cs     -- Email alert sender
  ApexMemoryTest/      -- Windows Forms test application
    MainForm.cs        -- Event handlers and logic
    MainForm.Designer.cs -- Form layout
    Program.cs         -- Entry point

Requirements

  • Windows (uses kernel32.dll P/Invoke calls)
  • .NET 10 SDK
  • Visual Studio 2022 or later (recommended), or the dotnet CLI
  • Administrator privileges (required to open handles to most processes)

Building

Visual Studio

  1. Open ApexMemory.sln in Visual Studio.
  2. Build the solution (Ctrl+Shift+B). The library compiles first; the test app references it via a project reference.
  3. Run ApexMemoryTest (F5). You will likely need to run Visual Studio as Administrator so the application can obtain process handles.

Command Line

dotnet build
dotnet run --project ApexMemoryTest

Run the terminal as Administrator so the application can obtain process handles.


Library API — ApexMemory.Memory

Attaching to a Process

var memory = new ApexMemory.Memory();

// Attach by PID
bool success = memory.GetProcessHandle(1234);

// Attach by name (takes the first match)
bool success = memory.GetProcessHandle("notepad");

// Retrieve the raw handle or PID later
IntPtr handle = memory.GetProcessHandle();
int pid = memory.GetProcessID();

// Close the handle when finished
memory.CloseProcessHandle();

GetProcessHandle opens the target with PROCESS_ALL_ACCESS. If the calling process lacks the necessary privileges, the call will fail and return false.

Reading Memory

// Read raw bytes
byte[] data = memory.ReadMemory(0x7FF600000000, 128);

// Read typed values
int    i = memory.ReadInt32(address);
float  f = memory.ReadFloat(address);   // alias for ReadSingle
float  s = memory.ReadSingle(address);
double d = memory.ReadDouble(address);
string t = memory.ReadString(address, 64);

// Follow a pointer chain and read the final Int32
int value = memory.ReadMemoryPointerInt(baseAddress, new int[] { 0x10, 0x24 });

// Resolve a pointer chain to its final address
long resolved = memory.GetPointerAddress(baseAddress, new int[] { 0x10, 0x24 });

Writing Memory

// Write typed values
memory.WriteMemory(address, 42);           // int
memory.WriteMemory(address, 3.14f);        // float
memory.WriteMemory(address, 2.718d);       // double
memory.WriteMemory(address, (byte)0xFF);   // single byte
memory.WriteMemory(address, "hello");      // ASCII string
memory.WriteMemory(address, byteArray);    // raw bytes

// Follow a pointer chain, then write
memory.WriteMemoryPointer(baseAddress, new int[] { 0x10, 0x24 }, 999);

// Check how many bytes the last write touched
int written = memory.GetBytesWritten();

Memory Protection

// Remove page protection (sets PAGE_EXECUTE_READWRITE on a 2048-byte region)
bool ok = memory.RemoveProtection(address);

Use this when a write fails because the target page is read-only or execute-only. The original protection flags are not automatically restored.

Scanning for a Byte Pattern

// Find the first occurrence of a byte pattern in all committed, readable regions
byte[] needle = Encoding.UTF8.GetBytes("TEST_STRING");
long found = memory.FindPatternAddress(needle);
// Returns the address of the first match, or 0 if not found.

The scan walks every committed memory region via VirtualQueryEx, skipping guarded and no-access pages.

Email Alerts — ApexMemory.AlertMailer

var mailer = new ApexMemory.AlertMailer
{
    SmtpHost = "smtp.gmail.com",
    SmtpPort = 587,
    UseSsl = true,
    FromAddress = "alerts@example.com",
    FromPassword = "app-password",
    ToAddress = "it-team@example.com"
};

if (mailer.IsConfigured())
{
    mailer.SendAlert("notepad", 1234, "prohibited_string", 0x7FF600001000);
}

The email includes the machine name, username, timestamp, process details, the prohibited string that triggered the alert, and the memory address where it was found.

Enumerating Memory Regions

IntPtr handle = memory.GetProcessHandle();
long address = 0;
var infoSize = (IntPtr)Marshal.SizeOf(typeof(ApexMemory.Memory.MEMORY_BASIC_INFORMATION));

while (true)
{
    ApexMemory.Memory.MEMORY_BASIC_INFORMATION info;
    var result = ApexMemory.Memory.VirtualQueryEx(handle, new IntPtr(address), out info, infoSize);
    if (result == IntPtr.Zero)
        break;

    long regionBase = info.BaseAddress.ToInt64();
    long regionSize = info.RegionSize.ToInt64();

    // info.State  -- 0x1000 = MEM_COMMIT, 0x2000 = MEM_RESERVE, 0x10000 = MEM_FREE
    // info.Protect -- PAGE_READONLY, PAGE_READWRITE, etc.
    // info.Type   -- 0x20000 = MEM_PRIVATE, 0x40000 = MEM_MAPPED, 0x1000000 = MEM_IMAGE

    long next = regionBase + regionSize;
    if (next <= address)
        break;
    address = next;
}

Test Application — ApexMemoryTest

The GUI is a Windows Forms application split into two panels:

Left Panel — Process List

  • Search box filters the process list by name or PID as you type.
  • Refresh reloads the full process list from the system.
  • Click a process to attach. The status bar and "Attached" label confirm success.

Right Panel — Memory Tools

Find Text — Enter a string and click Find Text. The application searches the target's memory for a UTF-8 match first, then UTF-16. If found, the address is loaded and the surrounding bytes are read automatically.

Module — After attaching, the dropdown is populated with the process's loaded modules. Click "Use Module Base" to load that module's base address into the address field.

Address / End Address / Read Bytes — Specify a start address (hex with 0x prefix, or decimal), an optional end address for range reads, and the number of bytes to read.

Action Buttons:

Button Description
Read Address Reads N bytes at the specified address and displays the Int32 value, hex dump, float/double interpretations, and UTF-8 text
Read Next 64 Ints Reads 64 consecutive 32-bit integers starting at the address
Read Int Range Reads integers from the start address through the end address
List Memory Regions Enumerates all committed memory regions and their protection flags

Write Int32 — Enter an integer and click Write Int32. The value is written as 4 bytes at the current address, then the address is re-read to verify.

Write Text — Enter a string and click Write Text. The string is written as null-terminated UTF-8 bytes at the current address.

Output Tabs

The bottom of the right panel has five tabs that resize with the window:

  • Text Preview (UTF-8) — Shows a character-by-character text representation of the bytes at the current address, with non-printable bytes replaced by .
  • Int Range — Displays address-value pairs from range reads
  • Memory Regions — Lists all committed regions with base address, size, and protection flags
  • Address View — A ListView showing a configurable number (1-4096) of consecutive 4-byte memory addresses starting from the current address. Each row shows the address, hex bytes, Int32 value, and ASCII representation. Automatically populated when a process is selected; click "Read Addresses" to refresh.
  • Security Monitor — A control panel for monitoring process memory for prohibited strings (see below)

Security Monitor

The Security Monitor tab is a split panel:

Left side — Prohibited Strings List: Add strings that should not appear in the target process's memory. Use the text box and "Add" button to build the list. Select an entry and click "Remove Selected" to delete it.

Right side — Controls and Log:

Control Description
Scan Now Immediately scans all readable memory regions for every prohibited string (UTF-8 and UTF-16)
Auto-Kill Process When checked, the attached process is terminated immediately if a prohibited string is found
Auto-Scan / Interval Enable timed scanning at a configurable interval (1-3600 seconds)
Start / Stop Start or stop the automatic scan timer
Email Alerts group Configure SMTP settings (host, port, from/password/to) and check "Send email on detection" to email IT when a prohibited string is found
Scan Log Timestamped log of all scan activity, detections, kills, and email results

Status Bar

The bottom of the right panel shows the result of the last operation, including error messages if a read or write fails.


Typical Workflow

  1. Launch ApexMemoryTest as Administrator.
  2. Find and click the target process in the list (use the search box to filter).
  3. Select a module from the dropdown and click "Use Module Base" to populate the address field, or enter an address manually.
  4. Click "Read Address" to inspect the memory at that location.
  5. Use "Find Text" to locate a known string in memory.
  6. Modify values with "Write Int32" or "Write Text" as needed.
  7. Use "List Memory Regions" to explore the process's memory layout.
  8. Switch to the "Address View" tab to see a table of consecutive memory addresses.
  9. Switch to the "Security Monitor" tab, add prohibited strings, optionally enable Auto-Kill and email alerts, then click "Start" to begin continuous monitoring.

License

This project is provided as-is for educational and authorized testing purposes. See the warnings above before using it on any process.

About

A .NET 10 library and Windows Forms test application for reading and writing the memory of other processes on Windows. ApexMemory wraps the Windows API functions ReadProcessMemory, WriteProcessMemory, VirtualQueryEx, and VirtualProtectEx into a straightforward C# class, and ships with a GUI that puts every feature at your fingertips.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages