0

I am trying to display information about the virtual memory of each process on the system:

#include <windows.h>
#include <conio.h>
#include <tlhelp32.h>
#include <iostream>
using namespace std;


void main() {
HANDLE CONST hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
PROCESSENTRY32 proc;
TCHAR Buffer[1024];
TCHAR Buffer2[1024];
DWORD temp;


HANDLE CONST hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (INVALID_HANDLE_VALUE == hSnap)
{
    return;
}

proc.dwSize = sizeof(PROCESSENTRY32);

Process32First(hSnap, &proc);

do {
    MEMORY_BASIC_INFORMATION    mbi = {};
    wsprintf(Buffer, L" %s %d \n ", proc.szExeFile, proc.th32ProcessID);
    WriteConsole(hStdOut, Buffer, lstrlen(Buffer), &temp, NULL);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proc.th32ProcessID);
    VirtualQueryEx(hProcess, 0, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
    printf("Alloc = %p, base = %p , size = %d, type = %d, state = %p\n", mbi.AllocationBase, mbi.BaseAddress, mbi.RegionSize, mbi.Type,mbi.State);

} while (Process32Next(hSnap, &proc));

CloseHandle(hSnap);


}

Output looks like this:

output

I get base address and alloc equal to 0000000 and type 0 How do I get normal values? I mean size and state seem to be ok but the rest is "0000000" I do not know what's the problem

  • 1
    Try to check the return value of each function that comes before it and compare them against the documentation. Chances are the problem is not where you think it is. – IWonderWhatThisAPIDoes Jan 26 '21 at 07:44
  • 1
    Your printf format string does not correspond to the types of your parameters. Prefer `std::cout` instead. – john Jan 26 '21 at 07:56
  • Pay attention to compiler warnings, In compiling your code I get three different warning for that printf call. All related to incorrect types for the format string. But as I said the easy way is to use `std::cout`. – john Jan 26 '21 at 07:59
  • cout doesnt work – user15082198 Jan 26 '21 at 08:00
  • @DussyPestroyer Please elaborate, does it fail to compile? If so, what error does it generate? Or does it just not write anything? (also, tag people in your replies. That way, whoever you are talking to gets notified) – IWonderWhatThisAPIDoes Jan 26 '21 at 08:07
  • if I try cout< – user15082198 Jan 26 '21 at 08:08
  • and what you wait, when query memory at 0 address ? `hProcess ` you not check and not close – RbMm Jan 26 '21 at 08:10
  • @DussyPestroyer But that is perfectly normal. `mbi.BaseAddress` is a pointer, so it gets printed in this format (corresponding to `printf` with `%p` format specifier). What worries me more, though, is that it is zero - meaning some API call has failed and pasted a value of zero, instead of a valid pointer. – IWonderWhatThisAPIDoes Jan 26 '21 at 08:12
  • I believe the problem is where @RbMm says - as I suggested before, try to check the return values of all API calls. One of them is probably zero (and yes, it appears to be `OpenProcess` that fails. And you need to call `CloseHandle` on each new process you open). – IWonderWhatThisAPIDoes Jan 26 '21 at 08:15
  • [OpenProcess](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess): *"If the function fails, the return value is NULL. To get extended error information, call [GetLastError](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror)."* You aren't. And since you are requesting `PROCESS_ALL_ACCESS`, needlessly, that's entirely likely to fail. Likewise, you aren't following the instructions for [Process32First](https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-process32first). – IInspectable Jan 26 '21 at 08:23
  • For most processes, you do not have the `PROCESS_ALL_ACCESS` permission, so your `OpenProcess` function will often fail. Even for the process with this permission, you only check its memory area starting from 0, so all the information of the first memory area is printed. Add error checking and you will find your problem. – Zeus Jan 27 '21 at 06:16

1 Answers1

1

You're currently only retrieving information about the first block of memory for each process. A process will typically have a lot of memory blocks, not just one (thousands to tens of thousands would be pretty typical).

Here's some code that retrieves and prints out the status of each block of data in a specified process.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
#include <string>

unsigned long show_module(MEMORY_BASIC_INFORMATION info) {
    unsigned long usage = 0;

    std::cout << info.BaseAddress << "(" << info.RegionSize / 1024 << ")\t";

    switch (info.State) {
    case MEM_COMMIT:
        std::cout << "Committed";
        break;
    case MEM_RESERVE:
        std::cout << "Reserved";
        break;
    case MEM_FREE:
        std::cout << "Free";
        break;
    }
    std::cout << "\t";
    switch (info.Type) {
    case MEM_IMAGE:
        std::cout << "Code Module";
        break;
    case MEM_MAPPED:
        std::cout << "Mapped     ";
        break;
    case MEM_PRIVATE:
        std::cout << "Private    ";
    }
    std::cout << "\t";

    int guard = 0, nocache = 0;

    if ( info.AllocationProtect & PAGE_NOCACHE)
        nocache = 1;
    if ( info.AllocationProtect & PAGE_GUARD )
        guard = 1;

    info.AllocationProtect &= ~(PAGE_GUARD | PAGE_NOCACHE);

    if ((info.State == MEM_COMMIT) && (info.AllocationProtect == PAGE_READWRITE || info.AllocationProtect == PAGE_READONLY))
        usage += info.RegionSize;

    switch (info.AllocationProtect) {
    case PAGE_READONLY:
        std::cout << "Read Only";
        break;
    case PAGE_READWRITE:
        std::cout << "Read/Write";
        break;
    case PAGE_WRITECOPY:
        std::cout << "Copy on Write";
        break;
    case PAGE_EXECUTE:
        std::cout << "Execute only";
        break;
    case PAGE_EXECUTE_READ:
        std::cout << "Execute/Read";
        break;
    case PAGE_EXECUTE_READWRITE:
        std::cout << "Execute/Read/Write";
        break;
    case PAGE_EXECUTE_WRITECOPY:
        std::cout << "COW Executable";
        break;
    }

    if (guard)
        std::cout << "\tguard page";
    if (nocache)
        std::cout << "\tnon-cacheable";
    std::cout << "\n";
    return usage;
}

unsigned long show_modules(HANDLE process) {

    unsigned long usage = 0;

    unsigned char* p = NULL;
    MEMORY_BASIC_INFORMATION info;

    for ( p = NULL;
        VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info);
        p += info.RegionSize )
    {
        usage += show_module(info);
    }
    return usage;
}

int main(int argc, char **argv) {

    int pid;

    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " <process ID>\n";
        return EXIT_FAILURE;
    }

    pid = std::stoi(argv[1]);

    HANDLE process = OpenProcess(
        PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
        false,
        pid);

    unsigned long mem_used = show_modules(process);
    std::cout << "Total memory used: " << mem_used / 10224 << "KB\n";
}

To give an idea of the result, here are the first few lines of output from a process on my system:

0000000000000000(64)    Free
0000000000010000(64)    Committed       Mapped          Read/Write
0000000000020000(4)     Committed       Mapped          Read Only
0000000000021000(60)    Free
0000000000030000(4)     Committed       Private
0000000000031000(60)    Reserved        Private

But be aware: you're likely to get a lot more output than that for most typical processes. That particular process (Thunderbird) produced a total of 3,686 lines of output. A quick test with Chrome (using a couple gigabytes of memory) produces over 46,000 lines of output (i.e., over 46,000 separate memory blocks being tracked by the system for it).

If you're going to print something out for every process in the system, you'll probably want to summarize the data quite a bit (but without knowing why you want this, it's hard to guess what sort of result you're likely to want).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • bit more correct use `p = (PBYTE)info.BaseAddress + info.RegionSize;` because `RegionSize` from `BaseAddress` and sometime (rarely) can be `p != BaseAddress`. and `return usage;` also typo – RbMm Jan 26 '21 at 10:56