1

I'm trying to read a variable from another process' memory in C and upon first query, for which I'm using VirtualQueryEx function, some potentially matching values are returned. However, when I change the value of the variable in the process which I'm querying and query the addresses of the potentially matching values, none of them match.

The queried process is a simple test program (also written in C) that allows you to change the value in a variable and print the value.

I tried changing the variable in the queried program to be stored on the heap, not the stack, but to no avail.

I expected the values queried to change according to them changing in the target process, but instead, after the first query and upon changing the queried value to what it is now in the target process, none of the values from the first query match the following queries.

linked_list.c is a file which contains functions to use a linked list to store the addresses of the potentially matching values.

Here's the code:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <windows.h>

#include "linked_list.h"

// FOR COMPILATION: -linked_list.c

int ask_current_value(){
    char buffer[128];
    printf("Current value: ");
    fgets(buffer, sizeof(buffer), stdin);
    *((char*)(memchr((void*)buffer, '\n', sizeof(buffer)))) = '\0';
    return atoi(buffer);

}

void print_memory_from_linked_list(Node* pProcessMemoryPtrList, HANDLE hOpenedProcess){
    // Simply formats the printing to not be so ugly
    int value;
    void* value_address;
    // (First element of list is garbage data, not to be included)
    for (int i = 1; i < linked_list_len(pProcessMemoryPtrList); i++){
        value_address = fetch_linked_list_element(pProcessMemoryPtrList, i);
        if (ReadProcessMemory(hOpenedProcess, value_address, &value, sizeof(value), NULL) != 0){
            printf("%d@%p\n", value, value_address);
        } else { printf("Func ReadProcessMemory failed non fatally\n"); }
    }
}

int main(){
    printf("Pid: ");
    char str_pid[7];  // up to 99999 + \n and \0
    fgets(str_pid, sizeof(str_pid), stdin);
    *((char*)(memchr((void*)str_pid, '\n', sizeof(str_pid)))) = '\0';
    int pid = atoi(str_pid);

    // Get process handle
    HANDLE hOpenedProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, false, pid);
    if (hOpenedProcess == NULL){
        printf("Smth went wrong, did u insert pid correct?\n");
        exit(1);
    }

    Node* pProcessMemoryPtrList = init_linked_list((void*)&pid);  // First element of list DO NOT TOUCH 
    
    int current_value;
    current_value = ask_current_value();

    MEMORY_BASIC_INFORMATION mbi;
    unsigned char* addr = 0;  // Addr to increment according to RegionSize
    int value;  // This stores the actual value queried from memory
    // Query memory until reach end of memory region and increment addr to query each loop
    while (VirtualQueryEx(hOpenedProcess, addr, &mbi, sizeof(mbi)) != 0){
        // Check if memory region is commited and readable, no use if these arent true
        if (mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_READONLY)){
            char* pMemoryBuffer = malloc(mbi.RegionSize);  // Allocate region size sized buffer to store read memory
            if (ReadProcessMemory(hOpenedProcess, addr, pMemoryBuffer, mbi.RegionSize, NULL) != 0){
                // Loop accross the memory and search for possible matches
                for (int i = 0; i <= mbi.RegionSize - sizeof(value); i++){
                    memcpy(&value, &pMemoryBuffer[i], sizeof(value));
                    // If value obtained from memory matches, add its addr to linked list
                    if (value == current_value){
                        uintptr_t offset = (uintptr_t)&pMemoryBuffer[i] - (uintptr_t)&pMemoryBuffer[0];  // Compute offset from buffer start
                        void* value_address = (void*)((uintptr_t)mbi.BaseAddress + offset);  // Add offset to current address
                        pProcessMemoryPtrList = add_to_linked_list(pProcessMemoryPtrList, value_address);
                    }
                }
            } else { printf("Func ReadProcessMemory failed non fatally\n"); }   
            free(pMemoryBuffer);  // Free the allocated buffer (RegionSize is dynamic)
        } else { printf("Memory region does not match supplied properties (commited and readable)\n"); }

        addr += mbi.RegionSize;
    }

    print_memory_from_linked_list(pProcessMemoryPtrList, hOpenedProcess);

    while (1){
        current_value = ask_current_value();
        // linked_list_len() gets length of linked list
        for (int i = 1; i < linked_list_len(pProcessMemoryPtrList); i++){
            // fetch_linked_list_element() gets element at index n
            void* value_address = fetch_linked_list_element(pProcessMemoryPtrList, i);
            if (ReadProcessMemory(hOpenedProcess, value_address, &value, sizeof(value), NULL) != 0){
                if (value == current_value){
                    printf("%d@%p\n", value, value_address);
                } else {
                    pProcessMemoryPtrList = delete_element_at_index(pProcessMemoryPtrList, i);
                }

            } else { printf("Func ReadProcessMemory failed non fatally\n"); }
        }
    }

    for (int i = linked_list_len(pProcessMemoryPtrList) - 1; i >= 0; i--){
        delete_element_at_index(pProcessMemoryPtrList, i);
    }

    CloseHandle(hOpenedProcess);    
    return 0;
}
1
  • On a side note: 1) you don't need to zero out \n before calling atoi(). It will simply stop converting characters when it encounters \n, no error reported. 2) you should not be opening the target process with PROCESS_VM_WRITE permission since you never call WriteProcessMemory(). Commented Aug 20 at 16:51

1 Answer 1

3

You are checking each memory page in the target process for the PAGE_READONLY protection flag being enabled, which means you are only allowing your code to look at read-only pages. But, your target variable is writable, so it can't be stored in a read-only page.

You need to use ((mbi.Protect & PAGE_READONLY) == 0) instead to check for writable pages.

Also, you should avoid touching any pages that have the PAGE_GUARD or PAGE_NOACCESS protection flag(s) enabled. You do not want to trigger a page fault or an access violation in the target process!

IsBadXxxPtr should really be called CrashProgramRandomly

ReadProcessMemory is not a preferred IPC mechanism

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.