
我们接着上一次来讲。上次提到Entity_Admin,首先我们需要在这个位置读到实体。通过IDA进行逆向工程,查找游戏用到entity_admin的地方,我们可以轻松找到Entity_Admin位于base+35F5300(这一偏移不具有时效性!仅适用于一个版本)
-
uintptr_t entity_list = SDK->RPM<uint64_t>(SDK->dwGameBase + offset::Address_entity_base);这一行通过在
dwGameBase游戏基地址的基础上执行读取操作(RPM),获取游戏内存中实体列表的基址。变量entity_list将保存此基址。 -
MEMORY_BASIC_INFORMATION mbi{};VirtualQueryEx(SDK->hProcess, (LPCVOID)entity_list, &mbi, sizeof(mbi));这些行查询与给定句柄(
SDK->hProcess)关联的进程的虚拟内存信息,查询的地址为entity_list。检索到的信息存储在mbi结构中。这是为后续读取实体做准备 -
SIZE_T entity_list_size = mbi.RegionSize / sizeof(Entity);这计算了实体列表的大小,基于内存信息结构中的
RegionSize和单个Entity对象的大小。 -
Entity* raw_list = new Entity[entity_list_size];在这里,执行动态内存分配,创建了一个大小为
entity_list_size的Entity对象数组。 -
if (ReadProcessMemory(SDK->hProcess, (LPCVOID)entity_list, raw_list, mbi.RegionSize, nullptr))此行尝试使用
ReadProcessMemory函数从游戏进程中读取内存内容。它从entity_list地址开始读取一个大小为mbi.RegionSize的内存块,将内容存储在raw_list数组中。 -
随后的代码片段处理已从内存中读取的实体:
- 它遍历每个
raw_list中的Entity。 - 对于每个实体,它通过验证
cur_entity是否非零来检查其是否为有效实体。 - 如果实体有效,它使用
DecryptComponent函数解密一个组件,并检查common_linker是否非零。 - 如果
common_linker有效,它从内存中检索一个unique_id。 - 然后,它再次遍历
raw_list,以查找具有相同unique_id的另一个实体。 - 如果找到这样的实体,则将
(possible_common, cur_entity)对添加到result集合中。
- 它遍历每个
-
最后,
delete[] raw_list;释放了动态分配的raw_list数组的内存,函数返回result集合
uintptr_t entity_list = SDK->RPM<uint64_t>(SDK->dwGameBase + offset::Address_entity_base);
MEMORY_BASIC_INFORMATION mbi{};
VirtualQueryEx(SDK->hProcess, (LPCVOID)entity_list, &mbi, sizeof(mbi));
SIZE_T entity_list_size = mbi.RegionSize / sizeof(Entity);
Entity* raw_list = new Entity[entity_list_size];
if (ReadProcessMemory(SDK->hProcess, (LPCVOID)entity_list, raw_list, mbi.RegionSize, nullptr))
{
for (size_t i = 0; i < entity_list_size; ++i)
{
uint64_t cur_entity = raw_list[i].entity;
if (cur_entity)
{
uint64_t common_linker = DecryptComponent(cur_entity, TYPE_LINK);
if (common_linker)
{
uint32_t unique_id = SDK->RPM<uint32_t>(common_linker + 0xD4);
for (size_t x = 0; x < entity_list_size; ++x)
{
uint64_t possible_common = raw_list[x].entity;
if (possible_common && SDK->RPM<uint32_t>(possible_common + 0x118) == unique_id)
{
result.emplace_back(possible_common, cur_entity);
break;
}
}
}
}
}
}
delete[] raw_list;
return result;
}
我们可以新建一个进程,一直用来获取游戏内的实体。由于这一部分获取的只是parent和Link,所以不需要一直执行,只需要在新实体生成(有新玩家、新物体)的时候执行一次。所以我们可以设置在这个进程中一秒或者两秒执行一次。
后面我们再新建一个进程,从上面获取到的内容中,对所有可能用到的组件进行解密。解密完成之后,存储到我们的entities Vector中。注意涉及到相同Vector的两个进程一定要加互斥锁,不然一定会导致进程异常结束。
文章详细描述了如何通过逆向工程和内存操作技术,如RPM和ReadProcessMemory,从游戏中获取Entity/Admin数据,包括实体列表、内存映射和解密组件的过程。同时强调了多进程协作时互斥锁的重要性。
1047





