使用 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 << name
输出wchar_t*
字符串。
【参考方案1】:
首先需要确定要读取的字符串是宽字节(wchar)还是多字节(char)版本,然后可以使用std::string/char*
或std::wstring/wchar_t*
。
其次,不要使用sizeof(std::string)
,需要确定要读取的字符串的长度。
对于宽字节:
&(std::string)name
不是可写地址,正如 cmets 指出的那样,您可以改用 wchat_t 数组:
wchat_t name[100];
BOOL ret = ReadProcessMemory(hprocess, (BYTE*)nameaddres, name, sizeof(name), 0);
或者你可以使用&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 << name
输出wchar_t字符串,如果输出总是只有一个字符,那么可以考虑使用多字节版本来读取内存。
【讨论】:
"另外,std::string::data()
现在也是const char*
类型,所以不推荐使用reveice字符串" - 非const版本data()
在 C++17 中添加。在 C++11..14 中,您可以改用 &name[0]
(通常,这也适用于早期版本)。
谢谢,我已经修改了答案。
你不是 resize()
'ing std::wstring
在读入字符数据之前为字符数据分配内存
在第二个例子中,为什么保留100个16位元素,然后只读取50个元素?以上是关于使用 ReadProcessMemory 获取字符串值的访问冲突的主要内容,如果未能解决你的问题,请参考以下文章
Win32 ReadProcessMemory API 的问题
获取其他进程的命令行(ReadProcessMemory其它进程的PPROCESS_PARAMETERS和PEB结构体)
确定外部进程的主线程 ID(ReadProcessMemory - 错误 299)