Есть готовая длл (engine.dll), она экспортирует методы классов. Пишу следующий код: Код (Text): #pragma comment(lib,"engine.lib") //... class UNetworkHandler { public: UNetworkHandler(class UNetworkHandler const &); virtual User* GetUser(int a1); virtual User* GetNextCreature(float a1,int a2); }; //... UNetworkHandler *g_NetworkHandler; //... User *U = g_NetworkHandler->GetUser(g_UserID); //... Компилю, и вижу, что код пытается вызвать GetUser через таблицу методов, а методы класса не импортируются. При этом прога вылетает ошибкой, т.к. в описании класса не все методы и не в том порядке, соответственно смещение в таблице методов получается неправильным. Заменяю "User *U = g_NetworkHandler->GetUser(g_UserID);" на UNetworkHandler NH(*g_NetworkHandler); User *U =NH.GetUser(g_UserID); - методы UNetworkHandler импортируются, вызов GetUser производится через таблицу импорта. Как сделать так, чтобы в 1м коде методы импортировались и вызов производился через таблицу импорта? ЗЫ: компилятор msvc2008
Такс... задача несколько изменилась. Статический импорт уже не катит, т.к. я хочу скрыть символьные имена используемых импортов. Кроме того, код пишется под длл конкретной версии, поэтому rva экспортов можно напрямую забить в код. GetProcAddress(hEngine,0x80000000+ord_xxx) вариант, но получать через API заранее известные rva неинтересно. Итак, я могу написать скрипт, который извлечет из длл rva и имена, "отдеманглит" их, и сформирует необходимый исходный код для импорта. Выглядеть это должно будет примерно так: Код (Text): /* engine.cpp */ class __single_inheritance UNetworkHandler; __declspec(naked) User* UNetworkHandler::GetUser(int a1) {__asm jmp 0x777}; __declspec(naked) User* UNetworkHandler::GetNextCreature(float a1,int a2) {__asm jmp 0x888}; ... /* init.cpp */ ... g_hEngine = GetModuleHandle("engine.dll"); //или LoadLibrary() ... *(int*)((char*)&UNetworkHandler::GetUser+1)+=(int)g_hEngine; *(int*)((char*)&UNetworkHandler::GetNextCreature+1)+=(int)g_hEngine; или так: Код (Text): /* engine.cpp */ #define RVA(rva) { \ __asm start: jmp patch \ __asm patch: mov eax,[g_hEngine] \ __asm add eax, rva \ __asm push eax \ __asm sub eax, patch \ __asm mov [start+1],eax \ __asm ret }; class __single_inheritance UNetworkHandler; __declspec(naked) User* UNetworkHandler::GetUser(int a1) RVA(0x777) __declspec(naked) User* UNetworkHandler::GetNextCreature(float a1,int a2) RVA(0x888) ... /* init.cpp */ ... g_hEngine = GetModuleHandle("engine.dll"); //или LoadLibrary() Конечно нормальной защитой от реверсирования это не назовешь, но с 1го взгляда отпугнуть может. Конечно более предпочтителен варинат 2, хоть и больше кода, но он перспективнее, особенно если сделать код базонезависимым, чтобы не палились xref'ы на g_hEngine. Опять же во втором варианте линкер сам удалит неиспользуемые импорты. Однако как нетрудно заметить, этот метод требует записи в секцию кода, а давать ей разрешение на запись, плохо, т.к. начнут орать всякие авиры. Лучше во время исполнения заюзать VirtualProtect, но нужна гарантия что все импорты попадут в одну 0x1000-байтную страницу памяти. А вопрос собственно такой - реально ли такое провернуть на С++ ?
Как выяснилось, реально) Код (Text): /*init.cpp*/ g_hEngine=(char*)GetModuleHandleA("engine.dll"); ... DWORD OldProt; int FirstSectionSize =*(int*)((char*)hModule+*(int*)((char*)hModule+0x3C)+0x18+0xE0+0x10); VirtualProtect((char*)hModule+0x1000,FirstSectionSize,PAGE_EXECUTE_READWRITE,&OldProt); /*imports.cpp*/ #define RVA(rva) { \ __asm start: _emit 0xE9 __asm _emit 0 __asm _emit 0 __asm _emit 0 __asm _emit 0 \ __asm patch: \ __asm mov eax,[g_hEngine] \ __asm add eax, rva \ __asm push eax \ __asm sub eax, patch \ __asm mov [start+1],eax \ __asm ret }; __declspec(naked) User* UNetworkHandler::GetUser(int a1) RVA(0x003E1554-0x3D0000) __declspec(naked) User* UNetworkHandler::GetNextCreature(float a1,int a2) RVA(0x003E0BE5-0x3D0000) ...