Что такое GWorld?

При реверс-инжиниринге игр использующих движок Unreal Engine 4, как и при большинстве таких случаев, логичным способом будет прибегнуть к соответствующему способу взаимодействия с объектами хранящимися в памяти. Его суть заключается в взаимодействии с типичной для серии этих движков иерархии, во главе которой стоит объект класса UWorld. Вы так же можете встретить название GWorld, в действительности он называется именно GWorld, а UWorld — это имя класса в исходном коде UE. Этот класс и следующие за ним содержат большинство игровой информации, которая может вам понадобится в разработке.

Так же для работы с дамперами, которые позволяют извлечь структуры данных из памяти и сгенерировать SDK, тоже будет нужен этот оффсет.

Способ поиска GWorld

Нам будет нужна IDA, бинарник игры или её дамп. Суть подхода к поиску состоит в использовании открытого кода движка, строковые литералы из которого можно сопоставлять со скомпилированной логикой.

После открытия в IDA и завершения анализа файла генерируем список строк, при этом должна быть включена поддержка строк юникода - Unicode C-style (16 bits).

Переходим к исходному коду движка и ищем вхождения подстроки "= GWorld". Это те моменты, где идёт обращение к GWorld. Поиск нужно осуществлять в тех файлах, которые компилируются в бинарник и не имеют отношения к окружению разработки, то есть по пути Engine\Source\Runtime. Далее находим рядом стоящие строки в коде. Желательно найти их в одной и той же функции, но в некоторых случах такой подход может потребовать поиск через вложенные вызовы.

Вот один из примеров, функция IsServerDelegateForOSS, которая содержит в своём теле следующую логику

Исходный код функции IsServerDelegateForOSS
Исходный код функции IsServerDelegateForOSS

Тут мы видим и обращение к GWorld и строковый литерал в одной функции.

Переходим в окно поиска строк и выполняем поиск по строке и находим её, ищем xref'ы и по счастливой случайности у нас оказалась только одна ссылка на это значение. В случае если у вас есть возможность найти уникальную константу - нужно этим пользоваться, ну а если у вас там несколько десятков ссылок то придётся перейти по каждой и анализировать код либо искать более уникальное значение.

Переходим по ссылке и наблюдаем совсем маленький граф потока управления.

Предположительно граф потока IsServerDelegateForOSS
Предположительно граф потока IsServerDelegateForOSS

На этом шаге если мы хоть немного знаем ассемблер, то мы можем увидеть обращение к статической секции данных, а именно запись в регистр rbx значения по адресу qword_1443795D8 в случае, если флаг ZF = 0. Можно основываясь на этом предположить, что это скомпилированная проверка истинности значения в GIsPlayInEditorWorld и последующие присваивание World = GWorld. Чтобы убедится в этом окончательно переходим в вывод декомпилятора и наблюдаем следующий код:

Восстановленная из ассемблера функция IsServerDelegateForOSS
Восстановленная из ассемблера функция IsServerDelegateForOSS

Снизу текстовая константа, выше проверка истинности значения в статической секции - byte_1441FE851, а в случае истинности присваивание значения локальной переменной из статической секции, то есть это именно то, от чего мы отталкивались в исходном коде движка. Говоря простыми словами World = GWorld это v1 = qword_1443795D8. Исходя из этого можно вычислить оффсет вычитая из значения 1443795D8 базовый адрес:

0x1443795D80x140000000=0x43795D8\mathtt{0x1443795D8} - \mathtt{0x140000000} = \mathtt{0x43795D8}

Из кода при external подходе взаимодействие с оффсетом будет выглядеть так:

#define GWORLD_OFFSET 0x43795D8
uint64_t moduleBase = GetModuleBaseAddress(PROCESS_NAME);
uint64_t GWorldAddress = moduleBase + GWORLD_OFFSET;
UWorld GWorld = UWorld(GWorldAddress);

Заключение

Таким образом, теперь вы знаете как находить GWorld оффсет для игр на движке Unreal Engine 4.