- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 347
- Реакции
- 7
Народ, решил скинуть актуальный дампер под последние билды APB. Старые методы отвалились, сигнатуры у многих поплыли, так что пришлось набросать свою реализацию. Внимание: для работы нужен свой драйвер для чтения памяти (в коде я его вырезал, подставьте свой интерфейс).
Этот код позволяет выгрузить актуальные GNames, GObjects и список пакетов в логи. Полезно, если собираете актуальный SDK или ищете оффсеты для своего external-проекта.
Работать будет до тех пор, пока не поменяют структуру UObject, так что если после патча крашит — первым делом смотрите оффсеты в utils/offsets.h.
Кто уже пробовал прокинуть это под последние апдейты, отпишитесь, не отлетели ли сигнатуры?
Этот код позволяет выгрузить актуальные GNames, GObjects и список пакетов в логи. Полезно, если собираете актуальный SDK или ищете оффсеты для своего external-проекта.
- Метод GetObjectName: берет индекс из памяти и вытягивает имя через GNames.
- DumpGNames: итерирует по массиву имен, учитывая флаг 0x4000.
- DumpGObjects: выгружает индексы, указатели и полные имена объектов в лог.
- DumpPackages: использует unordered_set для уникализации списка пакетов.
Код:
#pragma once
#include <windows.h>
#include <string>
#include <fstream>
#include <sstream>
#include <unordered_set>
#include <stdexcept>
#include <driver/Memory.hpp>
#include <iostream>
#include <filesystem>
#include "utils/offsets.h"
class ApbTools {
private:
public:
uint64_t GetClass(uint64_t objectPtr) {
return driver->read<uint64_t>(objectPtr + 0x18);
}
uint64_t GetOuter(uint64_t objectPtr) {
return driver->read<uint64_t>(objectPtr + 0x2C);
}
std::string GetObjectName(uint64_t objectPtr) {
if (objectPtr == 0) {
return "invalid object";
}
int32_t fNameIndex = driver->read<int32_t>(objectPtr + 0x24);
return GetNameByIdx(fNameIndex);
}
uint64_t GetPackageObject(uint64_t objectPtr) {
uint64_t outerPtr = GetOuter(objectPtr);
uint64_t lastValidPtr = outerPtr;
while (outerPtr != 0) {
outerPtr = GetOuter(outerPtr);
if (outerPtr != 0) {
lastValidPtr = outerPtr;
}
}
return lastValidPtr;
}
std::string GetObjectFullName(uint64_t objectPtr) {
uint64_t classPtr = GetClass(objectPtr);
uint64_t outerPtr = GetOuter(objectPtr);
if (classPtr == 0 || outerPtr == 0) {
return "(null)";
}
std::stringstream sb;
sb << GetObjectName(classPtr) << " ";
std::string name = GetObjectName(objectPtr);
sb << name;
while (outerPtr != 0) {
name = GetObjectName(outerPtr);
sb.seekp(0);
sb << name << "." << sb.str();
outerPtr = GetOuter(outerPtr);
}
return sb.str();
}
ApbTools() {}
~ApbTools() {
// Destructor handles cleanup (equivalent to Dispose)
}
void DumpGNames() {
int32_t num = driver->read<int32_t>(driver->image_base + cached.Offset_GNames.load() + 8);
int32_t max = driver->read<int32_t>(driver->image_base + cached.Offset_GNames.load() + 12);
std::cout << "num " << num << " max " << max << std::endl;
std::ofstream sw("D:\\APB_Names.log");
if (!sw.is_open()) {
printf("%s\n", "Failed to open APB_Names.log");
return;
}
for (uint64_t i = 0; i < static_cast<uint64_t>(num); ++i) {
uint64_t namePtr = driver->read<uint64_t>(driver->read<uint64_t>(driver->image_base + cached.Offset_GNames.load()) + i * 8);
if (namePtr == 0) {
continue;
}
int32_t flag = driver->read<int32_t>(namePtr);
uint64_t strPtr = (flag == 0x4000) ? driver->read<uint64_t>(namePtr + 0x30) : namePtr + 0x18;
std::vector<char> buffer(1024);
if (!driver->read(strPtr, buffer.data(), 1024)) {
printf("%s\n", ("Failed to read string at address: " + std::to_string(strPtr)).c_str());
continue;
}
// Find null terminator
size_t len = 0;
while (len < 1024 && buffer[len] != '\0') {
++len;
}
std::string str = std::string(buffer.data(), len);
sw << "idx: " << std::setw(5) << std::setfill('0') << i << " name: " << str << "\n";
}
sw.close();
}
std::string GetNameByIdx(int32_t idx) {
int32_t num = driver->read<int32_t>(driver->image_base + cached.Offset_GNames.load() + 8);
if (idx < 0 || idx >= num) {
return "invalid index";
}
uint64_t namePtr = driver->read<uint64_t>(driver->read<uint64_t>(driver->image_base + cached.Offset_GNames.load()) + static_cast<uint64_t>(idx) * 8);
if (namePtr == 0) {
return "invalid name";
}
int32_t flag = driver->read<int32_t>(namePtr);
uint64_t strPtr = (flag == 0x4000) ? driver->read<uint64_t>(namePtr + 0x30) : namePtr + 0x18;
std::vector<char> buffer(1024);
if (!driver->read(strPtr, buffer.data(), 1024)) {
printf("%s\n", ("Failed to read string at address: " + std::to_string(strPtr)).c_str());
return "error:GetNameByIdx";
}
// Find null terminator
size_t len = 0;
while (len < 1024 && buffer[len] != '\0') {
++len;
}
std::string str = std::string(buffer.data(), len);
return str;
}
void DumpSDK(){
std::ofstream sw("D:\\APB_sdk.h");
//apb_sdk::UObject::Class
int32_t num = driver->read<int32_t>(driver->image_base + cached.Offset_GObjects.load() + 8);
int32_t max = driver->read<int32_t>(driver->image_base + cached.Offset_GObjects.load() + 12);
std::cout << "num " << num << " max " << max << std::endl;
for (uint64_t i = 0; i < static_cast<uint64_t>(num); ++i) {
uint64_t objectPtr = driver->read<uint64_t>(driver->read<uint64_t>(driver->image_base + cached.Offset_GObjects.load()) + i * 8);
if (objectPtr == 0) {
continue;
}
//uintptr_t _class = driver->read<uintptr_t>(objectPtr + apb_sdk::UObject::Class);
//while (_class) {
// int id = driver->read<int>(objectPtr + apb_sdk::UObject::Name);
// std::string name = GetNameByIdx(id);
// if (id == 0 || name.empty()) {
// break;
// }
// std::string fullName = GetObjectFullName(objectPtr);
// sw << "Class " << std::left << std::setw(25) << fullName << "\n";
// sw << std::dec; // Reset to decimal
//}
}
return;
}
void DumpGObjects() {
int32_t num = driver->read<int32_t>(driver->image_base + cached.Offset_GObjects.load() + 8);
int32_t max = driver->read<int32_t>(driver->image_base + cached.Offset_GObjects.load() + 12);
std::cout << "num " << num << " max " << max << std::endl;
std::ofstream sw("D:\\APB_Objects.log");
if (!sw.is_open()) {
printf("%s\n", "Failed to open APB_Objects.log");
return;
}
for (uint64_t i = 0; i < static_cast<uint64_t>(num); ++i) {
uint64_t objectPtr = driver->read<uint64_t>(driver->read<uint64_t>(driver->image_base + cached.Offset_GObjects.load()) + i * 8);
if (objectPtr == 0) {
continue;
}
std::string name = GetObjectName(objectPtr);
std::string fullName = GetObjectFullName(objectPtr);
sw << "idx " << std::setw(10) << std::setfill('0') << i
<< " ptr: " << std::hex << std::setw(16) << std::setfill('0') << objectPtr
<< " name: " << std::left << std::setw(40) << std::setfill(' ') << name
<< " fullName: " << std::setw(40) << fullName << "\n";
sw << std::dec; // Reset to decimal
}
sw.close();
}
void DumpPackages() {
std::unordered_set<std::string> packagesSet;
int32_t num = driver->read<int32_t>(driver->image_base + cached.Offset_GObjects.load() + 8);
std::ofstream sw("D:\\APB_Packages.log");
if (!sw.is_open()) {
printf("%s\n", "Failed to open APB_Packages.log");
return;
}
for (uint64_t i = 0; i < static_cast<uint64_t>(num); ++i) {
uint64_t objectPtr = driver->read<uint64_t>(driver->read<uint64_t>(driver->image_base + cached.Offset_GObjects.load()) + i * 8);
if (objectPtr == 0) {
continue;
}
uint64_t package = GetPackageObject(objectPtr);
if (package == 0) {
continue;
}
std::string packageName = GetObjectName(package);
if (packagesSet.insert(packageName).second) {
sw << packageName << "\n";
}
}
sw.close();
}
};
Работать будет до тех пор, пока не поменяют структуру UObject, так что если после патча крашит — первым делом смотрите оффсеты в utils/offsets.h.
Кто уже пробовал прокинуть это под последние апдейты, отпишитесь, не отлетели ли сигнатуры?