• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Серединный вызов API функций

xmyriy

HDD-drive
Пользователь
Регистрация
18.01.2020
Сообщения
46
Реакции
35
Перечитывал давеча Криса Касперски и его шедевральную (для своего времени) книгу ТиФХА и захотелось более детальнее пощупать такую технику, как Серединный вызов API функций. Желающих прочитать полную версию отсылаю к упомянутому труду, а тут приведу краткое содержание.

Точки останова (breakpoints) ставятся на начало API функции, поэтому их можно обмануть, если начать выполнение не с первой машинной команды. Автор (КК) предлагает «выдрать» из функции несколько байт и поместить их в собственный буфер, после чего совершить переход на оставшийся «хвост» ф-ции. КК проводил эксперименты под Win2k, где, по его подсчетам, не менее 75% функций начинается с классического пролога «push ebp/mov ebp,esp», который в машинном коде выглядит как 55h 8Bh ECh. Если бряк на функцию дебаггер уже поставил (CCh), просто выходим.

Пример ф-ции, копирующей пролог API-функции в локальный стековый буфер (с) КК

Код:
ZenWay(char *p, char *dst)
{
int f = 0; // кол-во скопированных в буфер байт
// ОДНОБАЙТОВЫЕ ШАБЛОНЫ
switch(*(unsigned char *)p)
{
case 0xCC: 
printf("hello, hacker!\n");
exit(0);
break;

case 0x6A: // засылка в стек непосредственного значения
memcpy(dst, p, 2); f += 2;
break;
case 0x57: // PUSH EDI
*dst = 0x57; f += 1;
break;
default: f+=0;
}

// ОДНОСЛОВНЫЕ ШАБЛОНЫ
switch(*(WORD *)p)
{
case 0x8B55: // стандартный пролог
*((DWORD*)dst) = 0x00EC8B55; f += 3;
break;
case 0xD22B: // SUB EDX, EDX
*((WORD*)dst) = 0xD22B; f += 2;
break;
case 0x448B: // mov eax, [esp+xx]
case 0x74FF: // PUSH что-то-там
memcpy(dst, p, 4); f += 4;
break;
default:
f+=0;
}

// ШАБЛОН РАСПОЗНАН?
if (f==0) return 0; // нет ни одного совпадения
// ФОРМИРОВАНИЕ ПЕРЕХОДА НА ХВОСТ ФУНКЦИИ
strcpy((dst+f), "\xB8HACK\xFF\xE0");
*((DWORD *)(++dst+f)) = (DWORD) (p+f);
// УСПЕШНОЕ ЗАВЕРШЕНИЕ
return f;
}
Попробуем реализовать вызов MessageBox и посмотреть, что скажет IDA и x32dbg (я мучал tcc)
Код:
HINSTANCE hdll;
char ZMessageBoxA[MAX_CODE_SIZE];

int(WINAPI *XMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

hdll = LoadLibrary("USER32.DLL"); if (!hdll) return 0;

XMessageBoxA =(int (WINAPI*)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)) GetProcAddress(hdll, "MessageBoxA"); if (!XMessageBoxA) return 0;

// Копируем первые команды функции и корректируем указатели
if (ZenWay((char *) XMessageBoxA, (char *)ZMessageBoxA)!=0)
XMessageBoxA = (int (WINAPI*)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)) ZMessageBoxA;

// Вызываем
XMessageBoxA(NULL,"Hello","Caption",MB_OK);

Приложение отработало, окошко посмотрели.
666.jpg

Заглянем в IDA, import section
1.jpg


Импорт MessageBoxA отсутствует.
Теперь посмотрим сам вызов MessageBoxA

2.jpg


Бряк на вызов MessageBoxA, установленный в x32dbg не срабатывает по описанным выше причинам.

Техника старая, но вполне имеет место быть.
 
Да, вот только более продвинутые реверсеры знают эти приколы, я ставлю всегда бряк в начале функции, в конце функции, незадолго до ret, и еще где-то перед первым условным\безусловным переходом, если он есть
Если бряк первый не сработал, а останов случился в середине или в конце, то сразу на лицо подобные махинации

PS Хочешь беспалевно показать MessageBox, используй ZwRaiseHardError, это выкинет уведомление напрямую из ядра, особенно если дернешь ее вручную по номеру сискола
Со всеми остальными функциями так же, хочешь что бы не было перехватов и не срабатывали бряки, дергай сисколы напрямую из своего кода, в обход всяких там kernel32\kernelbase\ntdll
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх