使用 ReadProcessMemory 获取字符串值的访问冲突

Posted

技术标签:

【中文标题】使用 ReadProcessMemory 获取字符串值的访问冲突【英文标题】:Access Violation using ReadProcessMemory to get a string value 【发布时间】:2020-09-28 00:08:04 【问题描述】:

我是逆向工程的新手,对 C++ 也很陌生。我正在尝试使用 Win32 API 中的ReadProcessMemory() 在游戏中收集玩家的角色名称,但每当我尝试读取它时都会引发异常:

if (procId != NULL) 
    hprocess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procId);
    ModuleBase = getModuleBaseAddr(procId, "ac_client.exe");
    localPlayerptr = ModuleBase + 0x10f4f4;
    healthaddr = findDMAaddy(hprocess, localPlayerptr,  0xf8 );
    //load to entitylist Array
    ReadProcessMemory(hprocess, (BYTE*)localPlayerptr, &Entitylist[0], sizeof(Entitylist[0]), 0);
    for (int i = 1; i < 10; ++i) 
        ReadProcessMemory(hprocess, (BYTE*)ModuleBase + 0x10f4f8, &Entitylist[i], sizeof(Entitylist[i]), 0);
        ReadProcessMemory(hprocess,(BYTE*) Entitylist[i]+(i*4), &Entitylist[i], sizeof(Entitylist[i]), 0);
        std::cout << std::hex << Entitylist[i] << std::endl;
        uintptr_t nameaddres;
        std::string name;
        nameaddres = Entitylist[i] + 0x225;
        //ReadProcessMemory(hprocess, (BYTE*)nameaddres, &name, sizeof(name), 0); 
        /*--> ** This is where I have a problem. ***/
        std::cout << name << std::endl;
    

【问题讨论】:

您不能将内存读入std::string 并期望它能够工作。你需要一个普通的缓冲区(例如wchar_t buf[100])。 除了完全缺乏错误处理之外,您没有为std::string 的字符数据分配内存以供ReadProcessMemory() 读入。您不能直接读入std::string,这将覆盖其内部数据成员。例如,您需要读入std::string 的字符缓冲区,其地址由std::string::data() 方法返回。但首先,您需要确定从另一个进程读取多少字节,您不能为此使用sizeof(std::string)。此代码在读取字符串之前不会尝试确定目标字符串的长度 顺便说一句,您应该只在调用OpenProcess 时指定您实际需要的访问权限。这将增加调用成功的机会。在这种情况下,PROCESS_VM_READ 就足够了。 wchar_t 一个工作表,但不知道如何转换它们,因为它们从每个实体收集相同的结果,是的,我使用进程的所有访问权限 使用std::wcout &lt;&lt; name 输出wchar_t* 字符串。 【参考方案1】:

首先需要确定要读取的字符串是宽字节(wchar)还是多字节(char)版本,然后可以使用std::string/char*std::wstring/wchar_t*

其次,不要使用sizeof(std::string),需要确定要读取的字符串的长度。 对于宽字节:

&amp;(std::string)name 不是可写地址,正如 cmets 指出的那样,您可以改用 wchat_t 数组:

wchat_t name[100];
BOOL ret = ReadProcessMemory(hprocess, (BYTE*)nameaddres, name, sizeof(name), 0);

或者你可以使用&amp;name[0](来自@Remy):

std::wstring name;
name.reserve(100);
BOOL ret = ReadProcessMemory(hprocess, (BYTE*)nameaddres, &name[0], 100*sizeof(wchar_t), 0);
std::wcout << name << std::endl;

然后,std::cout 不适用于wchar_t*,它只打印地址值。它是for循环中的一个局部变量,所以每次进来都会重新分配地址。由于某些规则,系统可能更喜欢选择相同的地址,所以你得到了相同的地址值。

使用std::wcout &lt;&lt; name输出wchar_t字符串,如果输出总是只有一个字符,那么可以考虑使用多字节版本来读取内存。

【讨论】:

"另外,std::string::data()现在也是const char*类型,所以不推荐使用reveice字符串" - 非const版本data() 在 C++17 中添加。在 C++11..14 中,您可以改用 &amp;name[0]通常,这也适用于早期版本)。 谢谢,我已经修改了答案。 你不是 resize()'ing std::wstring 在读入字符数据之前为字符数据分配内存 在第二个例子中,为什么保留100个16位元素,然后只读取50个元素?

以上是关于使用 ReadProcessMemory 获取字符串值的访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

Win32 ReadProcessMemory API 的问题

获取其他进程的命令行(ReadProcessMemory其它进程的PPROCESS_PARAMETERS和PEB结构体)

确定外部进程的主线程 ID(ReadProcessMemory - 错误 299)

ReadProcessMemory 整个过程

我可以使用 ReadProcessMemory 在 Windows 中读取进程的程序内存吗?

易语言中为啥调用API_ReadProcessMemory 来读字节集 要先取空白字节集()赋值给 缓冲区