首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从另一个进程读取内存--在C++ \Copilot的解决方案中

从另一个进程读取内存--在C++ \Copilot的解决方案中
EN

Stack Overflow用户
提问于 2022-02-17 04:17:10
回答 1查看 300关注 0票数 -1

正如标题所示,我试图从C++中的另一个进程读取内存,以检查来自另一个进程的值是否达到某个级别。因为我对此一无所知,所以我决定向GitHub副驾驶寻求帮助。在正常的基础上,我会搜索这些文档,但在不同意看来是这样的。由于我可以访问GitHub副驾驶,而且由于头版广告显然鼓励用户信任Copilot的编程能力,所以我选择让Copilot做这个功能。

因此,我以评论的形式给了它一个提示://A function that can grab an address from the memory of another process and store it as a double value

它给了我很好的感觉,但是我永远不会接受副驾驶所做的函数,并且盲目地使用它,除非我确信它会起作用(因为我不相信副驾驶做的每件事都不会引起问题,特别是在处理指针之类的问题时)。我想看看在C++中有过内存经验的人是否能告诉我这个函数是否能工作,以及为什么它会或不会工作,因为我对从另一个进程获取内存一无所知。

除了GitHub的声明之外,我不只是搜索文档有三个主要原因:

  • 由于这是一个复杂而真实的用例,这将真正地测试副驾驶的编程能力,并且它将让我深入了解我将来在一些我不知道如何做的事情上可以信任副驾驶(很明显,我不会让这件事失控,但如果知道我可以比现在更信任副驾驶,那就好了)。
  • 无论如何,尽管GitHub在他们的网站上发表的声明与Copilot应该帮助用户的内容截然相反,尽管我知道这是一个公共测试版,而且它还没有完成,但至少对于现实世界的用例来说,它应该足够好,而不是简单的编码用例。一个经验丰富的人的回答将真正显示它是否足够好的现实世界的编码案例。
  • 这些文档只告诉我函数做什么以及将什么作为它的参数,它没有告诉我如何使用它。如果我真的想知道如何使用它,我将不得不搜索网络。搜索网络很可能会给我带来一些与我的问题无关的复杂的例子,并使我与我真正想要完成的事情相距甚远。不仅如此,正如第二条理由中所述,这与副驾驶应该帮助用户的情况正好相反。

以下是Copilot为我生成的代码:

代码语言:javascript
复制
DWORD GetAddress(DWORD dwProcessId, LPCWSTR szModuleName, const char* szProcName)
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (hProcess == NULL)
        return 0;

    MODULEINFO modinfo;
    GetModuleInformation(hProcess, GetModuleHandle(szModuleName), &modinfo, sizeof(MODULEINFO));

    DWORD dwAddress = (DWORD)modinfo.lpBaseOfDll;
    DWORD dwSize = (DWORD)modinfo.SizeOfImage;

    MEMORY_BASIC_INFORMATION mbi;
    while (dwSize > 0)
    {
        VirtualQueryEx(hProcess, (LPVOID)dwAddress, &mbi, sizeof(mbi));
        if (mbi.State == MEM_COMMIT && !(mbi.Protect & PAGE_GUARD) && mbi.Protect & PAGE_EXECUTE_READWRITE)
        {
            DWORD dwOldProtect;
            VirtualProtectEx(hProcess, (LPVOID)dwAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);

            char* szBuffer = new char[mbi.RegionSize];
            ReadProcessMemory(hProcess, (LPVOID)dwAddress, szBuffer, mbi.RegionSize, NULL);

            for (DWORD dwIndex = 0; dwIndex < mbi.RegionSize - 4; dwIndex++)
            {
                if (szBuffer[dwIndex] == '\x55' && szBuffer[dwIndex + 1] == '\x8B' && szBuffer[dwIndex + 2] == 'E' && szBuffer[dwIndex + 3] == 'A')
                {
                    DWORD dwAddress2 = dwAddress + dwIndex + 7;
                    DWORD dwAddress3 = dwAddress2 + *(DWORD*)(dwAddress2);
                    if (strcmp((char*)dwAddress3, szProcName) == 0)
                    {
                        delete[] szBuffer;
                        CloseHandle(hProcess);
                        return dwAddress2 + 4;
                    }
                }
            }

            delete[] szBuffer;
            VirtualProtectEx(hProcess, (LPVOID)dwAddress, mbi.RegionSize, dwOldProtect, &dwOldProtect);
        }

        dwAddress += mbi.RegionSize;
        dwSize -= mbi.RegionSize;
    }

    CloseHandle(hProcess);
    return 0;
}

您可能会立即指出一个明显的错误:函数返回DWORD而不是double,这正是我要求Copilot返回的。我看到了这个错误,但是从我看到的例子来看(是的,我至少做了一些搜索),返回DWORD也是一样的。我本可以看到这些例子是错误的,如果我是正确的。

EN

回答 1

Stack Overflow用户

发布于 2022-02-17 04:53:10

函数返回double,但它只返回存储在内存中的值,而实际上不是双类型数据。如果对double进行转换,就会得到原始数据。

除了64位系统上的字节指针之外,您不能在内存中进行搜索:http://msdn.microsoft.com/en-us/library/aa746449%28v=vs.85%29.aspx

在内存中搜索字符串有不同的方法,具体取决于您要寻找的内容:http://www.catatonicsoft.com/blog/need-to-read-and-write-strings-and-data-in-a-processs-memory/

(在这里阅读更多:https://github.com/MicrosoftArchiveOrgMember/copilot)

记忆刮板

该程序在运行时使用几种技术从进程和内存中获取信息,以便在Cofactor终止目标进程(默认情况下)或手动终止程序时(使用CTRL+C)时可以将其添加到证据文件中。

这段代码主要是从http://www.codeproject.comhttps://forums.hak5.org的各种示例中拼凑而成的,并进行了一些重大修改,以获得有用的格式的输出。

不断检查进程内存使用情况,并在日志文件更改时将其添加到日志文件中。这是通过获取指向进程内存区域的指针来完成的,然后在引用过程中检查其所有页面。当它们被更改时,该页的内容将被读取并作为证据添加到日志文件中。如果进程进入或退出休眠模式,或由于其他原因而停止,此程序将检测到该模式,并相应地将其添加到日志文件中。

由进程加载的当前DLL每5秒记录一次,以便如果DLLCofactor终止其目标后加载,它仍将作为证据包含在日志文件中。还每5秒检查一次进程是否生成新线程,以便子进程也包含在我们的证据文件中。

我们可以通过检查两个模块来扩展这个程序:

代码语言:javascript
复制
1) A module containing functions that correspond to debug breakpoints (which would detect whether a debugger was attached).
2) A module containing crash signatures - integers that would trigger an alert if they were found written to memory in any of our processes (like stack smashing protections might provide).

为了做到这一点而不使事情变得过于复杂,我可能会使用每个模块中带有地址的CreateRemoteThread继续从该线程执行到您自己的代码中,在这里您可以检查断点或崩溃签名并相应地执行。

结论

如果您需要调试一个进程,并且由于任何原因无法使它停止,这个程序仍然能够在任何时候抓取进程内存,以便您可以搜索所需的任何东西。

为了使用创建的日志文件,您需要做一些额外的工作,比如使用您选择的解析器解析它并搜索它(使用regexes或其他什么),我认为它超出了Copilot设计的范围。

如果你最终使用了这个程序,请告诉我!我很想知道有多少人认为这个程序有用。

我从其他程序中学到了有趣的技巧

查找进程加载的DLL

下面的C++代码使用Windows ()获取加载DLL的完整路径,并将其解析为split(),只提取文件名,而不是整个路径。该代码的其余部分只是尽量避免重复,同时又足够简单,这样就不会在不同进程和文件系统用例之间产生太大的混淆(希望如此)。

代码语言:javascript
复制
// Code Example: Finding DLLs Loaded by a Process
#include <stdio.h>
#include <string.h>
#include <tchar.h>

#define BUFF_SIZE 200

// Find the full path to a loaded DLL by process ID (PID) and its filename (first 8 characters)
void GetModuleFileNameEx(int pid, const char* szName, char* buff, int buffSize)
{
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);

    if (hProcess == NULL) { return; }

    HMODULE hMods[1024];
    DWORD cbNeeded;

    if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { return; }

    for (int i = 0; i < (int)(cbNeeded / sizeof(HMODULE)); i++) {
        TCHAR szModName[MAX_PATH];

        if (!GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName))) { continue; }

        strcat_s((char*)buff, buffSize - 1 , (char*)szModName);

        // Check if the first 8 characters of the filename in the process matches
        // with what we are looking for and avoid adding duplicates
        char* chPtr = strchr((char*)buff, '\\');

        if (chPtr != NULL) {
            *chPtr = 0;
            strcat_s((char*)buff, buffSize - 1 , "\\");
            strcat_s((char*)buff, buffSize - 1 , szName);

            HANDLE hFile = CreateFileA((LPCSTR)buff, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

            if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); return; }
        } else { break; }

    }
}

int main() {
    char szDllName[8];       // Maximum length of a module name is MAXPATH - 1 bytes including the NULL terminator. However we only need 8 characters to store the DLL name so use this limit to save memory.

    int pid; scanf("%d", &pid);

    if (pid == 0) { return 0; }

    char buff[BUFF_SIZE];

    GetModuleFileNameEx(pid, szDllName, buff, BUFF_SIZE);

    char* chPtr = strchr(buff, '\\');

    // Change the path separator character to a null terminator so we can split it
    if (chPtr != NULL) { *chPtr = 0; }

    chPtr = strtok(buff, "\\");
    while (chPtr != NULL) {
        printf("%s\n", chPtr);
        chPtr = strtok(NULL, "\\");
    }

    return 0;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71152461

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档