Search code examples
c++winapidllreadprocessmemory

Win API ReadProcessMemory at base address of DLL returning unexpected data


I'm trying to read the contents of a DLL from memory for some academic research. Specifically, the NTDSA.DLL library for the purpose of mutating specific instructions to simulate programming errors to force the system to fail. The failure will then be recorded to train machine learning algorithms to predict future failures (this is an attempt to generalize previously published research seen here).

I'm getting what I believe to be the base address in virtual memory of the lsass.exe process (which loads the target DLL) through the process outlined here. I'm then calling ReadProcessMemory with an allocated buffer and the handle to lsass obtained by calling OpenProcess with 'PROCESS_ALL_ACCESS' set. The ReadProcessMemory returns with error code 299 80% of the time (partial read) with zero bytes read. My assumption is that the area I'm trying to access is in use when the call is made. Fortunately, it will occasionally return the number of bytes I'm requesting. Unfortunately, the data returned does not match what is on disk when compared to the static DLL in the System32 directory.

So the question is, is ReadProcessMemory doing something funny with the address that I give it, or is my virtual address wrong? Is there another way to figure out where that DLL gets loaded into memory? Any thoughts? Any help or suggestions would be greatly appreciated.

Adding Code:

// FaultInjection.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <windows.h>
#include <psapi.h>

#include <string>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <io.h>
#include <tchar.h>

using namespace std;

int _tmain(int argc, _TCHAR* argv[]) {

    // Declarations
    int pid = 0;
    __int64* start_addr;
    DWORD size_of_ntdsa;
    DWORD aProcesses[1024], cbNeeded, cProcesses;
    TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
    HMODULE hmods[1024];
    unsigned int i;

    // Get All pids
    if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)){
        cout << "Failed to get all PIDs: " << GetLastError() << endl;
        return -1;
    }

    // Find pid for lsass.exe
    cProcesses = cbNeeded / sizeof(DWORD);
    for (i = 0; i < cProcesses; i++) {
        if (aProcesses[i] != 0) {
            HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i]);
            if (hProc != NULL) {
                HMODULE hMod;
                DWORD cbNeededMod;
                if (EnumProcessModules(hProc, &hMod, sizeof(hMod), &cbNeededMod)) {
                    GetModuleBaseName(hProc, hMod, szProcessName, sizeof(szProcessName) / sizeof(TCHAR));
                }

                if (wstring(szProcessName).find(L"lsass.exe") != string::npos) {
                    pid = aProcesses[i];
                }
                CloseHandle(hProc);
            }
        }
    }

    cout << "lsass pid: " << pid << endl;

    HANDLE h_lsass = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!h_lsass) {
        cout << "Failed to open process (are you root?): " << GetLastError() << endl;
        return -1;
    }

    // Get Process Image File Name
    char filename[MAX_PATH];
    if (GetProcessImageFileName(h_lsass, (LPTSTR)&filename, MAX_PATH) == 0) {
        cout << "Failed to get image file name: " << GetLastError() << endl;
        CloseHandle(h_lsass);
        return -1;
    }

    // Enumerate modules within process
    if (EnumProcessModules(h_lsass, hmods, sizeof(hmods), &cbNeeded)) {
        for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
            TCHAR szModName[MAX_PATH];
            if (GetModuleFileNameEx(h_lsass, hmods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) {
                if (wstring(szModName).find(L"NTDSA.dll") != string::npos) {
                    _tprintf(TEXT("%s\n"), szModName);
                    MODULEINFO lModInfo = { 0 };
                    if (GetModuleInformation(h_lsass, hmods[i], &lModInfo, sizeof(lModInfo))){
                        cout << "\t Base Addr: " << lModInfo.lpBaseOfDll << endl;
                        cout << "\t Entry Point: " << lModInfo.EntryPoint << endl;
                        cout << "\t Size of image: " << lModInfo.SizeOfImage << endl;

                        start_addr = (__int64*)lModInfo.lpBaseOfDll;
                        size_of_ntdsa = lModInfo.SizeOfImage;
                    }
                    else {
                        cout << "Failed to Print enumerated list of modules: " << GetLastError() << endl;
                    }
                }
            } else {
                cout << "Failed to Print enumerated list of modules: " << GetLastError() << endl;
            }
        }
    }
    else {
        cout << "Failed to enum the modules: " << GetLastError() << endl;
    }

    // Ready to continue?
    string cont = "";
    cout << "Continue? [Y|n]: ";
    getline(cin, cont);
    if (cont.find("n") != string::npos || cont.find("N") != string::npos) {
        CloseHandle(h_lsass);
        return 0;
    }

    void* buf = malloc(size_of_ntdsa);
    if (!buf) {
        cout << "Failed to allocate space for memory contents: " << GetLastError() << endl;
        CloseHandle(h_lsass);
        return -1;
    }

    SIZE_T num_bytes_read = 0;
    int count = 0;

    if (ReadProcessMemory(h_lsass, &start_addr, buf, size_of_ntdsa, &num_bytes_read) != 0) {
        cout << "Read success. Got " << num_bytes_read << " bytes: " << endl;
    } else {
        int error_code = GetLastError();
        if (error_code == 299) {
            cout << "Partial read. Got " << num_bytes_read << " bytes: " << endl;
        } else  {
            cout << "Failed to read memory: " << GetLastError() << endl;
            CloseHandle(h_lsass);
            free(buf);
            return -1;
        }
    }

    if (num_bytes_read > 0) {
        FILE *fp;
        fopen_s(&fp, "C:\\ntdsa_new.dll", "w");
        SIZE_T bytes_written = 0;
        while (bytes_written < num_bytes_read) {
            bytes_written += fwrite(buf, 1, num_bytes_read, fp);
        }
        fclose(fp);
        cout << "Wrote " << bytes_written << " bytes." << endl;
    }

    CloseHandle(h_lsass);
    free(buf);

    return 0;
}

Solution

  • Code works as described minus my amateur mistake of sending the address of the variable I was using to store the address of the location in virtual memory of the target application. In above code, changed:

    if (ReadProcessMemory(h_lsass, &start_addr, buf, size_of_ntdsa, &num_bytes_read) != 0) {
    

    to

    if (ReadProcessMemory(h_lsass, start_addr, buf, size_of_ntdsa, &num_bytes_read) != 0) {
    

    Works like a charm. Thank you ssbssa for pointing out mistake, sorry for wasting anyone's time.