Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C++
Article

Performing a hex dump of another process's memory

Rate me:
Please Sign up or sign in to vote.
4.61/5 (27 votes)
6 Sep 20035 min read 230.6K   9.7K   85   34
An article on examining process memory

Image 1

Introduction

I was browsing The Code Project for information on using ReadProcessMemory to do a Hex dump of a process once it's loaded. While there are a number of utilities available for dumping a file image as it exists on disk, I didn't find much for dumping the memory image of an application once its loaded in memory.

Although the Win32 SDK Walker example does a nice job of this, I found it a bit too cumbersome to parse as an introduction program. I also had a few problems with it. One, it uses the debug procedures which load hook DLLs into the process thus altering the memory image we're looking at. Two, it crashes when you unload the process before terminating the Walker program. Three, the thumb tab on the vertical scroller doesn't work for browsing the memory details, and lastly it is somewhat hard to figure out where all of its structures get initialized since they happen all over the place.

Background

To start with, the MSDN documentation of ReadProcessMemory is a good thing to look at. A number of questions come up if you are just trying this sucker out. Where do I get a process handle? Once I have a process handle, it seems to blow up on me. How do I know what address to load in? The answer to the address question is the VirtualQueryEx function. This article covers the use of both functions and answers a number a questions pertaining to obtaining the proper variables to submit to the functions.

Using the code

The bulk of the MDump project simply displays the information it finds in a scrollable fashion. Aside from that, there are only three real questions which come up in writing an application like this:. how you get a process handle from outside your own process, how do you collect all the memory pages managed by this process, and how do you display this memory once found?

How do you get a process handle?

The simplest way I can think of to choose a process of interested is to click on its window. Given that, the question becomes how do I get a process handle by clicking on a window? Ideally, one would select File|Open, have the cursor change to a bulls eye, and return when you have selected your window.

As I'm sure most of you know, SetCapture got most of its utility removed a while back and no longer lets a window receive WM_MOUSEMOVE messages unless a mouse button is down. I find this awkward. The way around this is to throw up a transparent window which covers the entire screen, change the mouse cursor to let you know you're in this mode, and then change it back when you have your coordinates from a click.

Since this is a bit awkward, it bears a bit of discussion. Here is the function that get called when we select File|Open from the menu.

LRESULT TCWindow::OnFileOpen(HWND hWnd)
{
    HWND hTransparent;
    gWindow.hcurOld = GetCursor(); //Save our cursor
    ShowWindow(gWindow.m_hWnd, SW_SHOWMINIMIZED); //Minimize
    //Callback function to handle a mouse click and return to main window
    gwchild.fpCallback = cmdSelectWindow;
    //Cover the screen with transparent same-process window
    hTransparent = gwchild.Create(gWindow.m_hInst, SW_SHOW);
    return 0l;
}

Ok, all we're doing is changing the cursor and throwing up the transparent child window. I pass it a static function to use as a callback - which it will call as soon as it gets a mouse click. In order to get back inside our window class, SendMessage(gWindow.m_hWnd, WM_ONGETHWND, 0, (LPARAM)hParent); passes the top-level window handle back to our window object.

//Callback function for selecting the window under the cursor
void cmdSelectWindow(HWND hWnd, LPARAM lParam)
{
    ...
    //Ok restore the visual state of things
    DestroyWindow(gwchild.hWindow);
    SetCursor(gWindow.hcurOld);

    //Ok now get the window we clicked on before we possibly cover it.
    pt.x = LOWORD(lParam);    pt.y = HIWORD(lParam);
    hWndClicked = WindowFromPoint(pt);
    //Get outermost window
    hParent = hWndClicked;    
    hNewParent = GetParent(hWndClicked);
    while(hNewParent) {
        hParent = hNewParent;
        hNewParent = GetParent(hParent);
    }
    //Maximize our window
    ShowWindow(gWindow.m_hWnd, SW_RESTORE);
    SendMessage(gWindow.m_hWnd, WM_ONGETHWND, 0, 
        (LPARAM)hParent);    //Process handle
    return ;
}

So now we have a window handle? Now what? Well, the following code will give us the process handle which is what we need to walk the process's memory pages.

GetWindowThreadProcessId(hWndToOpen, &dwProcessID);
// Get Process Handle
m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID);
//Map the address space
walkObj.memWalk(m_hProcess);
walkObj.mGetPageList(&iPages);

There is one catch here though. In order for this to work, I call the AdjustPrivileges function in the same module as WinMain to give this application debug rights. Otherwise OpenProcess with PROCESS_ALL_ACCESS fails. This code is straight out of the MSDN Knowledge base - Article ID: Q131065

In short, here is what AdjustPrivileges does:

OpenProcessToken(... TOKEN_QUERY, &hToken))    //Get hToken
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)    //Get Debug rights    
tp.Privileges[0].Luid = luid;                //Enable debug rights
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges (hToken, FALSE, &tp, ...

How do you examine the memory?

Ok, so we have m_hProcess now, the handle to the other process. Why did we need it again? You need it for this function:

VirtualQueryEx(
    hOtherProcess,
    lpMem,
    &mbi,
    sizeof(MEMORY_BASIC_INFORMATION)
);

VirtualQueryEx takes an address (lpMem) and returns information about it in the mbi structure. This is why we needed the process handle.

In the demo program, CMemWalker is the class that reads the other process's memory. All it does is set lpMem to 0 and increments it to scan all the memory address up to the top of the process's memory space. The simplified loop is as follows:

MEMORY_BASIC_INFORMATION    mbi;
/* Get maximum address range from system info */
GetSystemInfo(&si);
/* walk process addresses */
lpMem = 0;
while (lpMem < si.lpMaximumApplicationAddress) {
        VirtualQueryEx(...)
        /* increment lpMem to next region of memory */
        lpMem = (LPVOID)((DWORD)lpList->mbi.BaseAddress +
        (DWORD)lpList->mbi.RegionSize);
}

This information is then displayed in the main Window which responds to scroll events and such. Among other things, the information in the mbi structure holds a beginning address and a length for each section of managed memory. In the demo program, you double click on a line which then browses that chunk of memory in the standard hex dump fashion.

How do you read the memory?

To actually dump the individual bytes of memory that are available to be read, you use the following function:

ReadProcessMemory(m_hProcess, lpAddress,
    destBuffer, dwBytesToRead, &dwBytesRead);

This function takes a memory address in a foreign process, and copies it into memory in the current process (destBuffer). There is not much more to the demo program than that. Most of the other code in the TCHexWindow object merely allows the user to browse the bytes using the scroll widgets. For the sake of simplicity, I intentionally left out the sorting, heap probing, and searching functions which I miss dearly. These might be a few things to add if you're planning on enhancing this.

If you're still interested in probing memory segments, I suggest looking at the Walker source code in the Win32 SDK kit. I would love a clearer explanation as to how the thing figures out which chunks are DLLs etc. As a point to start from, if you open a process using the various Win32 Debug functions, you can trap debug events and have the loop notify your program whenever a DLL is loaded or unloaded by a process. Other information is also available in this fashion. This does, of course, load the Hook98.DLL which changes the memory so it may or may not work for your purposes.

History

  • Sep 1, 2003: Article published
  • Sep 7, 2003: Downloads updated

References

  • How to Obtain a Handle to Any Process with SeDebugPrivilege  - Microsoft Knowledge Base Article ID: Q131065
  • The PWalker source in samples\sdktools\winnt\walker - Microsoft Win32 SDK - August 1996

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralReplace the value Pin
mitkomk23-Jan-06 20:14
mitkomk23-Jan-06 20:14 
GeneralRe: Replace the value Pin
weariless24-Jan-06 7:01
weariless24-Jan-06 7:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.