Тема: Отримання адреси функцій без використання IAT та WinAPI (ч.1)
Сьогодні, з метою пропаганди низькорівневе програмування, я хочу розповісти про одну задачу, яка постає майже перед кожним shareware-писакою, що хоче захистити свою прогу від кракерів(а точніше від їх дурного різновида, що звуться "какерами"), або хулхацкером, що пише шеллкоди для використання вразливості переповнення буферу, або ж ви пишете демку, яка за розміром не повинна перевищувати 16кб (умова деяких демопаті).
Цією задачею є отримання хендлу(адреси) бібліотеки kernel32.dll та отримання адреси функцій без використання таблиці імпорту(IAT) \ WinAPI (LoadLibrary & GetProcAddress). Одразу попереджаю, що ловлевел гури для себе нічого нового не відкриють. Всі приклади розроблені для х86 (IА32) архітектури та чудово працюють в емуляторі WoW (WindowsOnWindows технологія, збоченці). Як я вже казав, одним із застосувань цієї технології є збивання з пантелику недовчених кракерів, що звуться какерами. Кракер завантажить вашу прогу в редактор ресурсів \ дизасемблер \ hex-редактор \ блокнот і перше, на що він погляне, буде таблиця імпорту (IAT), щоб знати які функції необхідно перехоплювати. Але наша таблиця імпорту (IAT) не буде відповідати дійсності, тому він піде хибним шляхом.
Спершу розглянемо функцію отримання хендлу(адреси) бібліотеки kernel32.dll.
Ця процедура написана за stdcall декларацією виклику. Тобто вертає результат в регістр eax.
Код різними мовами:
AT&T Assembler Syntax (GAS)
GetKernelHandle_x86:
mov %fs:0x30,%eax
mov 0xc(%eax),%eax
mov 0x1c(%eax),%eax
mov (%eax),%eax
mov 0x8(%eax),%eax
ret
Intel Assembler Syntax (FASM, NASM)
GetKernelHandle_x86:
mov eax, [fs:030h]
mov eax, [eax+0ch]
mov eax, [eax+01ch]
mov eax, [eax]
mov eax, [eax+08h]
ret
Якщо використовувати Intel Assembler Syntaxis (MASM), то треба ще додати рядок, наведений нижче, щоб асемблювалося без помилок.
ASSUME FS:NOTHING
Отриманий машинний код:
064h, 0A1h, 030h, 000h, 000h, 000h, 08Bh, 040h, 00Ch, 08Bh, 040h, 01Ch, 08Bh, 000h, 08Bh, 040h, 008h, 0C3h
Через велику кількість несумісних мов програмування леше одного разу асемблювати, а машинний код прямо в файл і вставляти, наприклад:
Intel Assembler Syntax (FASM, NASM, TASM, MASM)
GetKernelHandle_x86:
db 064h, 0A1h, 030h, 000h, 000h, 000h, 08Bh, 040h, 00Ch, 08Bh, 040h, 01Ch, 08Bh, 00h, 08Bh, 040h, 08h, 0C3h
AT&T Assembler Syntax (GAS)
GetKernelHandle_x86:
db 0x64, 0xA1, 0x30, 0x0, 0x0, 0x0, 0x8B, 0x40, 0x0C, 0x8B, 0x40, 0x1C, 0x8B, 0x0, 0x8B, 0x40, 0x8, 0xC3
Оскільки небагато людей пишуть софт на чистому асемблері, а якщо хто і пише, то це йому читати зайве Тому поговоримо про інтеграцію асемблера та машинного кода у високорівневі мови програмування.
С/С++ є однією з найпопулярніших мов системного та прикладного програмування. Розглянуті нижче можливості наявні в VisualC++ й PellesC.
__declspec(naked) - наказує компілятору не створювати пролог\епілог для функції.
__asm{} - використання вбудованого асемблеру.
_emit - директива для прямого включення машинного коду. На х86 платформі, з естетичних міркувань, краще використовувати директиву асемблера(вбудованого) db, бо директива _emit додає один байт (тобто один рядок додає один байт), а db одразу декілька.
Приклад використання цих директив у проекі:
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
__declspec(naked) HANDLE __stdcall GetKernel32_x86(){
__asm{
db 064h, 0A1h, 030h, 000h, 000h, 000h, 08Bh, 040h, 00Ch, 08Bh, 040h, 01Ch, 08Bh, 000h, 08Bh, 040h, 008h, 0C3h
}
}
int main(){
SetConsoleTitle("Matrix is Anywere!");
printf("\nAddress of Kernel32 = 0x%X\n", GetKernel32_x86());
_getch();
return(0);
}
Виконаємо програму:
+=[Matrix is Anywere!]=================================================[_][O][X]=+
| |
|Address of Kernel32 = 0x7C800000 |
| |
| |
| |
| |
| |
| |
| |
| |
+================================================================================+
Число може відрізнятися(на моїй вінді ХР SP3), але завжди воно буде вказувати на місце розташування kernel32.dll у віртуальній памяті процеса.
За допомогою директиви _emit ми б досягнули однакового результату, але...
Розглянемо процедуру на директиві _emit
__declspec(naked) HANDLE __stdcall GetKernel32_x86(){
__asm{
_emit 0x64
_emit 0xA1
_emit 0x30
_emit 0x0
_emit 0x0
_emit 0x0
_emit 0x8b
_emit 0x40
_emit 0x0C
_emit 0x8B
_emit 0x40
_emit 0x1C
_emit 0x8B
_emit 0x00
_emit 0x8B
_emit 0x40
_emit 0x08
_emit 0xC3
}
}
Як кажуть на міжнародних форум "Ur code not sexy!". Памятайте, що гламурний код полегшить роботу з проектом та це запорука вашої репутації в секті Open Source.
Ви можете сказати "Поєднати асемблер та машинний код з С\С++ кожен зможе, а от спробуй-но додати низькорівневі фічі до мов програмування, що компілюються в байт-код або ж взагалі інтерпретуються".
Це зробити не так вже й важко за допомогою однієї фічі в WinAPI функції CallWindowProc. Ця функція викликає процедуру вікна з заданими параметрами і повертає результат, що повернула процедура вікна. Так би мовити, оболонка Першим параметром функції є посилання на процедуру вікна. Але ми можемо замість процедури вікна підставити свою процедуру і функція CallWindowProc верне результат, що верне наша функція. Єдина умова, щоб наша функція не більше 4х разів клала щось в стек. За допомогою цих танців із бубном ми маємо змогу викликати свої функції, написані мовою асемблера, і попередньо скомпільовані, в таких мовах: VB6, AutoIt, C# (і инших його родичів за .NET).
AutoIt
#include <winapi.au3>
$strucGetKernelHandle_x86 = DllStructCreate("char[64]");
$GetKernelHandle_x86 = Chr(0x64)&Chr(0xA1)&Chr(0x30)&Chr(0x0)&Chr(0x0)&Chr(0x0)&Chr(0x8B)&Chr(0x40)&Chr(0x0C)&Chr(0x8B)
$GetKernelHandle_x86 &= Chr(0x40)&Chr(0x1c)&Chr(0x8B)&Chr(0x0)&Chr(0x8B)&Chr(0x40)&Chr(0x08)&Chr(0xC3)
DllStructSetData($strucGetKernelHandle_x86, 1, $GetKernelHandle_x86)
$hKernel32 = _WinAPI_CallWindowProc(DllStructGetPtr($strucGetKernelHandle_x86), 0, 0, 0, 0)
MsgBox(0, "Address of Kernel32.dll", Hex($hKernel32))
Виконаємо програму:
+=[Address of Kernel32.dll]====[_][O][X]=+
| 7C800000 |
| [OK] |
+========================================+
Корисні посилання:
Онлайн дизасемблер: http://www.onlinedisassembler.com/
Спільнота wasm.ru (найкраща на пострадянському просторі): http://www.wasm.ru/
Міжнародна(англомовна) спільнота imsecure (невеличка, проте одна з найкращих): http://www.imsecure.org/
На днях допишу