Simple Memory Scanner Example

Wrote this for someone that was asking for help on Cheat Engine’s forums. This is a very very basic and light-weight memory scanner that will scan for 4 byte values.

/** 
 * Simple Memory Scanner Example 
 * (c) 2014 atom0s [atom0s@live.com] 
 */ 

#include <Windows.h> 
#include <string> 
#include <TlHelp32.h> 

/** 
 * @brief The target process to scan within. 
 */ 
#define TARGET_NAME "winmine.exe" 

/** 
 * @brief Obtains the process id of the given target. 
 * 
 * @return The process id if found, 0 otherwise. 
 */ 
unsigned int getTargetProcessId() 
{ 
    PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; 

    // Obtain a snapshot of the current process list.. 
    auto handle = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
    if (handle == INVALID_HANDLE_VALUE) 
        return 0; 

    // Obtain the first process.. 
    if (!::Process32First(handle, &pe32)) 
    { 
        ::CloseHandle(handle); 
        return 0; 
    } 

    // Loop each process looking for the target.. 
    do 
    { 
        if (!_stricmp(pe32.szExeFile, TARGET_NAME)) 
        { 
            ::CloseHandle(handle); 
            return pe32.th32ProcessID; 
        } 
    } while (::Process32Next(handle, &pe32)); 

    // Cleanup.. 
    ::CloseHandle(handle); 
    return 0; 
} 

/** 
 * @brief Entry point of this application. 
 * 
 * @param argc  The count of arguments passed to this application. 
 * @param argv  The array of arguments passed to this application. 
 * 
 * @return Non-important return. 
 */ 
int __cdecl main(int argc, char* argv[]) 
{ 
    // Obtain the target process id.. 
    auto processId = getTargetProcessId(); 
    if (processId == 0) 
        return 0; 

    // Open a handle to the target.. 
    auto handle = ::OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId); 
    if (handle == INVALID_HANDLE_VALUE) 
        return 0; 

    // Obtain the current system information.. 
    SYSTEM_INFO sysInfo = { 0 }; 
    ::GetSystemInfo(&sysInfo); 

    auto addr_min = (long)sysInfo.lpMinimumApplicationAddress; 
    auto addr_max = (long)sysInfo.lpMaximumApplicationAddress; 

    auto found = 0; 

    // Loop the pages of memory of the application.. 
    while (addr_min < addr_max) 
    { 
        MEMORY_BASIC_INFORMATION mbi = { 0 }; 
        if (!::VirtualQueryEx(handle, (LPCVOID)addr_min, &mbi, sizeof(mbi))) 
        { 
            printf_s("Failed to query memory.\n"); 
            break; 
        } 

        // Determine if we have access to the page.. 
        if (mbi.State == MEM_COMMIT && ((mbi.Protect & PAGE_GUARD) == 0) && ((mbi.Protect & PAGE_NOACCESS) == 0)) 
        { 
            // 
            // Below are flags about the current region of memory. If you want to specifically scan for only 
            // certain things like if the area is writable, executable, etc. you can use these flags to prevent 
            // reading non-desired protection types. 
            // 

            auto isCopyOnWrite = ((mbi.Protect & PAGE_WRITECOPY) != 0 || (mbi.Protect & PAGE_EXECUTE_WRITECOPY) != 0); 
            auto isExecutable = ((mbi.Protect & PAGE_EXECUTE) != 0 || (mbi.Protect & PAGE_EXECUTE_READ) != 0 || (mbi.Protect & PAGE_EXECUTE_READWRITE) != 0 || (mbi.Protect & PAGE_EXECUTE_WRITECOPY) != 0); 
            auto isWritable = ((mbi.Protect & PAGE_READWRITE) != 0 || (mbi.Protect & PAGE_WRITECOPY) != 0 || (mbi.Protect & PAGE_EXECUTE_READWRITE) != 0 || (mbi.Protect & PAGE_EXECUTE_WRITECOPY) != 0); 

            // Dump the region into a memory block.. 
            auto dump = new unsigned char[mbi.RegionSize + 1]; 
            memset(dump, 0x00, mbi.RegionSize + 1); 
            if (!::ReadProcessMemory(handle, mbi.BaseAddress, dump, mbi.RegionSize, NULL)) 
            { 
                printf_s("Failed to read memory of location: %08X\n", mbi.BaseAddress); 
                break; 
            } 

            // Scan for 4 byte value of 1337.. 
            for (auto x = 0; x < mbi.RegionSize - 4; x += 4) 
            { 
                if (*(DWORD*)(dump + x) == 1337) 
                    found++; 
            } 

            // Cleanup the memory dump.. 
            delete[] dump; 
        } 

        // Step the current address by this regions size.. 
        addr_min += mbi.RegionSize; 
    } 

    printf_s("Found %d results!\n", found); 

    // Cleanup.. 
    ::CloseHandle(handle); 
    return ERROR_SUCCESS; 
}

Leave a comment