我们接着上一次来讲。上次提到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的两个进程一定要加互斥锁,不然一定会导致进程异常结束。