Я работаю над проектом C++, который внедряется (через нашу собственную программу-загрузчик, которая создает процесс и загружает код и исправления из DLL, в которую скомпилирован проект) в игровой движок с закрытым исходным кодом (который является реверс-инжинирингом; также он предназначен для Windows) с целью исправления ошибок и расширения логики игрового движка. Для взаимодействия с игрой мне, в частности, нужна своего рода библиотека с объявлениями функций игрового движка, глобальных переменных и т. д., что я делаю, переходя к фиксированным/абсолютным адресам (всегда действительным, поскольку DLL находится в том же адресном пространстве, а базовый EXE-файл никогда не перемещается). Типы легко определить, и даже сами переменные (
Код: Выделить всё
reinterpret_castКак это выглядит в исходном коде:
Код: Выделить всё
class BeaconClass
{
public:
BeaconClass() JMP_THIS(0x430210)
void Draw(Surface* pSurface, RectangleStruct bounds) JMP_THIS(0x430250)
void SetCoordAndHouse(CoordStruct coord, int houseId) JMP_THIS(0x430590)
//...
};
Код: Выделить всё
class BeaconClass
{
public:
BeaconClass()
{
_asm{pop ecx};
_asm{mov esp, ebp};
_asm{pop ebp};
_asm{mov eax, 0x430210};
_asm{jmp eax};
}
// ...
Код: Выделить всё
.text:100D7290
.text:100D7290 ; =============== S U B R O U T I N E =======================================
.text:100D7290
.text:100D7290 ; Attributes: bp-based frame
.text:100D7290
.text:100D7290 ; BeaconClass *__thiscall BeaconClass::BeaconClass(BeaconClass *this)
.text:100D7290 ??0BeaconClass@@QAE@XZ proc near ; CODE XREF: _somehook+1F↓p
.text:100D7290
.text:100D7290 this = dword ptr -4
.text:100D7290
.text:100D7290 000 55 push ebp
.text:100D7291 004 8B EC mov ebp, esp
.text:100D7293 004 51 push ecx
.text:100D7294 008 89 4D FC mov [ebp+this], ecx
.text:100D7297 008 59 pop ecx
.text:100D7298 004 8B E5 mov esp, ebp
.text:100D729A 004 5D pop ebp
.text:100D729B 000 B8 10 02 43 00 mov eax, 430210h
.text:100D72A0 000 FF E0 jmp eax
.text:100D72A2 ; ---------------------------------------------------------------------------
.text:100D72A2 004 8B 45 FC mov eax, [ebp+this]
.text:100D72A5 004 8B E5 mov esp, ebp
.text:100D72A7 000 5D pop ebp
.text:100D72A8 -04 C3 retn
.text:100D72A8 ??0BeaconClass@@QAE@XZ endp
Код: Выделить всё
class NOVTABLE CCFileClass : public CDFileClass
{
// ...
//Constructor
CCFileClass(const char* pFileName)
: CCFileClass(noinit_t())
{ JMP_THIS(0x4739F0); }
protected:
explicit __forceinline CCFileClass(noinit_t)
: CDFileClass(noinit_t())
{ }
//Properties
public:
MemoryBuffer Buffer;
// ...
};

noinit_t() — это бездействующий оператор, поэтому компилятор ничего не делает вместо вызова вектора базового класса, который «связан» с другим адресом. Довольно уродливый хак. И MemoryBuffer даже не должен вызываться, но компилятор вызывает его. И все это не работает.
Очевидно, что это сильно противоречит замыслу и поэтому является довольно нечистым способом...
Вопрос
Можно ли каким-то образом объявить символ с адресом типа extern и позволить компоновщику просто вызывать/использовать этот адрес при обнаружении вызовов/ссылок на него адрес?
Написав что-то вроде
Код: Выделить всё
class NOVTABLE BuildingClass : public TechnoClass
{
//...
[[address(0x43EF90)]]
int GetCurrentFrame();
//...
Код: Выделить всё
mov ecx, pBuilding
call 0x43EF90
Предположим, цепочки инструментов Clang/MSVC. В настоящее время проект работает на MSVC, но рассматривается возможность перехода на clang с совместимостью с MSVC (если это вообще возможно).
Рассматриваемые альтернативы
- Определение символов в ассемблере с использованием искаженного имени — работает, но чрезвычайно громоздко для довольно простой задачи, не может быть записано в режиме реального времени, а также создает функцию преобразования.
- Использовать как есть - очень непереносимо, происходит сбой при переходе на clang, очень сильно зависит от неясного недокументированного поведения, которое может сломаться.
- Использование голых функций - невозможно для ctors, dtors.
- Использование символов, определенных из компоновщик - может работать, но я бы предпочел, чтобы каждое определение символа было в соответствии с его объявлением, такое разделение является громоздким.
- Использование переменных указателей статических функций - на самом деле не применимо с ctors, dtors, наследованием, виртуальными объектами.
Подробнее здесь: https://stackoverflow.com/questions/797 ... ed-address
Мобильная версия