sg
-
Автор темы
- #1
Добрый день, форумчане сего форума. Так как в некоторых базах люди до сих пор получает интерфейсы через вызов функции CreateInterface. Я считаю это не целесообразным и хотелось бы положить этому конец на корню, представив вам гайд, как получить список всех интерфейсов игры без особых заморочек.
Так как вальве слегка изменили функцию CreateInterface, то и метод слегка поменялся. Обратимся к коду данной функции.
Здесь мы видим, что в самом начале функции происходит перемещение адреса всех интерфейсов в регистр r9. Тогда мы гетать данный указатель и читать из него всю нужную информацию. Разберём код функции в псевдо C виде.
Примем к сведению весь код и напишем свой парсер интерфейсов
Ну и полученный результат выглядит как-то так:
Так как вальве слегка изменили функцию CreateInterface, то и метод слегка поменялся. Обратимся к коду данной функции.
Здесь мы видим, что в самом начале функции происходит перемещение адреса всех интерфейсов в регистр r9. Тогда мы гетать данный указатель и читать из него всю нужную информацию. Разберём код функции в псевдо C виде.
C++:
__int64 __fastcall CreateInterface(__int64 a1, _DWORD *a2)
{
__int64 v2; // r9
unsigned __int8 *v5; // rax
__int64 v6; // r8
int v7; // ecx
int v8; // edx
// v2 это и есть наш регистр r9
v2 = g_pInterfaceList;
// Проверка на валидность
if ( g_pInterfaceList )
{
while ( 1 )
{
// Здесь происходит слегка непонятная хуйня ( по идее так же пробег по списку)
// v5 -- указатель на массив интерфейсов
v5 = *(unsigned __int8 **)(v2 + 8);
v6 = a1 - (_QWORD)v5;
do
{
v7 = v5[v6];
v8 = *v5 - v7;
if ( v8 )
break;
++v5;
}
while ( v7 );
if ( !v8 )
break;
// Получаем CreateFn
v2 = *(_QWORD *)(v2 + 0x10);
if ( !v2 )
goto LABEL_7;
}
if ( a2 )
*a2 = 0;
// Вызываем и возвращаем значение
return (*(__int64 (**)(void))v2)();
}
else
{
LABEL_7:
if ( a2 )
*a2 = 1;
return 0i64;
}
}
Примем к сведению весь код и напишем свой парсер интерфейсов
C++:
void ParseInterfaces()
{
// Здесь я получаю контейнер с модулями. Я их получаю через PEB,
// Но вы можете использовать Module32First и Module32Next
auto mapModules = g_pMemory->GetModuleList();
// Пробегаемся по модулям
for (auto pModule : mapModules) {
// Получаем адрес функции CreateInterface
auto pFuncAddr = reinterpret_cast<std::uintptr_t>(GetProcAddress(pModule.second, "CreateInterface"));
// Проверка на ненулевой адрес
if (!pFuncAddr)
continue;
// Укзатель на InterfaceList. OG naming
auto pGovno = *reinterpret_cast<std::int32_t*>(pFuncAddr + 3);
// Проверка на то, что мы перемещаем адрес в регистр R9
// Если не равен, то просто скипаем итерацию
// mov r9, [address]
// ^
// |
if (*(uint8_t*)(pFuncAddr + 2) != 0x0D)
continue;
// Получаем указатель на головной элемент списка интерфесов
auto pHeadInterface = *reinterpret_cast<InterfaceReg**>(pFuncAddr + pGovno + 7);
if (!pHeadInterface)
continue;
// Проходим по списку и заполняем контейнер
for (const InterfaceReg* pCurrentInterface = pHeadInterface; pCurrentInterface; pCurrentInterface = pCurrentInterface->m_pNext)
{
// Например так
m_mapInterfaces.try_emplace(pCurrentInterface->m_szName, pCurrentInterface->m_pCreateFn());
// Или просто выводить имя и адрес
g_pLogging->Print("%-20s -> %-29s -> 0x%p", pModule.first.c_str(), pCurrentInterface->m_szName, pCurrentInterface->m_pCreateFn());
}
}
}