Runtime process memory patching for restoring stat

2019-01-20 09:22发布

I'm looking for a method for storing the process memory, and restore it later at certain conditions.

...

Actually I've read questions about it... It seems a big challenge!

So, let's analyse: The application is a distributed one, but many processes are stateless (request their state to a centralized server). Processes uses network connections and shared memory for communicating with other processes.

The central server shall save its state by dumping its process memory, which should be restored later a certain conditions. (1)

I known about ReadProcessMemory and WriteProcessMemory functions, which allow the process to read itself and overwrite already allocated memory, isn't it? So, which I need is address where I start to read/write, and the number of bytes to read/write. So... what addresses? Many code I've read uses the address returned by VirtualAlloc, but I don't known whether this could be useful to me.

I assume that the process executable segments are not changing, so they do not need red/written. At restore time, I could also assume that all process threads are in the same execution position when the memory was read by the main thread.

It remains the stack memory, and the heap memory, which are the memory segments what I'm interested in.

Is it possible?

(1) It is perfectly legal to ask why I'm trying to do this. The reason is... complicated, as usual. However, say that the application has a very complicated state, that requires a too complex state saving algorithm. The another alternative (which is in subject of analysis) is the implementation of a logger/replay mechanism able to reproduce every event which has contributed to the modified state.


It came to my mind the malloc & co. hook. So I can track the memory allocated by the process. But actually I noticed the _CrtMemState structure, but I don't known whether it could be useful to me.

2条回答
Juvenile、少年°
2楼-- · 2019-01-20 09:26

ReadProcessMemory is for reading the memory of another process. Inside of a process, it's unnecessary -- you can just dereference a pointer to read memory within the same process.

To find the blocks of memory in a process, you can use VirtualQuery. Each block will be tagged with a state, type, size, etc. Here's some code I wrote years ago to walk the block list for a specified process (using VirtualQueryEx). You use VirtualQuery pretty much the same way, except that you don't have to specify a process, since it always walks the process in which its running.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

unsigned long usage;

void show_modules(HANDLE process) {

    unsigned char *p = NULL;
    MEMORY_BASIC_INFORMATION info;

    for ( p = NULL;
        VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info);
        p += info.RegionSize ) 
    {
        printf("%#10.10x (%6uK)\t", info.BaseAddress, info.RegionSize/1024);

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

        if ((info.State == MEM_COMMIT) && (info.Type == MEM_PRIVATE))
            usage +=info.RegionSize;

        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);

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

        if (guard)
            printf("\tguard page");
        if (nocache)
            printf("\tnon-cachable");
        printf("\n");
    }
}

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

    int pid;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <process ID>", argv[0]);
        return 1;
    }

    sscanf(argv[1], "%i", &pid);

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

    show_modules(process);
    printf("Total memory used: %luKB\n", usage/1024);
    return 0;
}        
查看更多
欢心
3楼-- · 2019-01-20 09:39

The process memory does not represent the entire state of the process. The operating system will be holding objects on behalf of your process (e.g., file handles, synchronization objects, etc.) in places like non-paged pool that are outside the scope of your process.

I think you'd be better off refactoring until you can serialize and deserialize the relevant state with a manageable effort.

查看更多
登录 后发表回答