- Теорія перехоплення WinAPI
- хакерський модуль
- Ковбаса WinSock Hooker
- Код процедури GetAllProcess ()
- робимо захоплення
- код DLL
- тестуємо
- Оброблювач OnClick ()
- Все готово

Наша взаимовыгодная связь https://banwar.org/
В одному з номерів нашого журналу в рубриці FAQ було поставлено питання: «Як можна перехопити дані, що відправляються мережевим додатком?». У відповіді Step порекомендував використовувати функцію WinSock hook з пакету мережевих утиліт - IP Tools. Можливості WinSock hooker мені настільки сподобалися, що я вирішив написати свій варіант подібної програми. І в цій статті хочу поділитися з тобою досвідом, отриманим при розробці.
Теорія перехоплення WinAPI
Почнемо з теорії, яку, правда, і так знає більшість наших читачів. Все API функції визначені в динамічних бібліотеках. Відразу виникає питання: «Звідки додаток знає, в якій бібліотеці оголошена потрібна функція?». Відповідь проста - в будь-якому PE файлі є область, яка називається таблицею імпорту. У ній перераховано інформація про всі імпортованих функціях, необхідних для коректної роботи. Завантажувач PE зчитує цю інфу і завантажує необхідні бібліотеки в адресний простір процесу програми. Наприклад, щоб дізнатися список всіх DLL, підвантаженими в адресний простір процесу, можна скористатися утилітою від Марка Руссиновича «Process Explorer». Поглянь на малюнок, на ньому відображений список dll процесу Opera.exe.
Зверни увагу на виділену в нижній частині вікна «Process Explorer» бібліотеку dll з ім'ям WS2_32.dll. У ній визначено весь набір мережевих функцій WinSock API другої версії. Одну з функцій з цієї бібліотеки нам належить сьогодні навчитися перехоплювати. У перехопленні немає нічого нетривіального. Все, що потрібно від програміста, так це змусити процес жертви звертатися не до системної функції, а до нашої - підставний. Далі залишається тільки командувати. Перехопити API функції можна декількома способами. Ось найпопулярніші:
У методу є як плюси, так і мінуси. З переваг можна виділити можливість перехоплення абсолютно будь-яких функцій, тобто не тільки тих, що визначені в таблиці імпорту. Серед мінусів - ймовірність появи помилок в багатопотокових застосуваннях. Хоча при наявності голови на плечах це досить легко обходиться. Як спосіб лікування підходить банальна зупинка всіх потоків додатки і їх запуск після установки перехоплення. Перехоплення API найзручніше здійснювати в контексті процесу «жертви», тому необхідно впровадити свій код в віддалений процес. Існує кілька «устаканиться» варіантів вторгнення в чужі процеси:
У багатьох може скластися враження, що перехоплення - справа об'ємне і складне. Дійсно, реалізувати метод впровадження і перехоплення - завдання далеко не найпростіша, але Delphi програмістам сильно пощастило. З легкістю організувати перехоплення функцій і впровадитися в чужій процес допоможе модуль advHookApi, написаний геніальним програмістом Ms Rem. Модуль спроектований якісно, всі функції зручно описані, код оформлений красиво. Єдине розчарування в тому, що сьогодні вже не можна виразити респект автору. Ця людина мертвий. Дуже сумно, що геніальних людей так рано забирає смерть.
хакерський модуль
Отже, давай подивимося, які можливості може похвалитися даний модуль.
Впровадження Dll в чужій процес. Метод реалізується за допомогою функції InjectDll (), яка описана в такий спосіб:
function InjectDll (Process: dword; ModulePath: PChar): boolean;Як параметри потрібно передати дескриптор процесу, в який будемо впроваджувати dll, і шлях до самої бібліотеці. У разі успішного впровадження результат буде true. Приховане впровадження Dll. Функція InjectDllEx () впроваджує dll і виробляє шаманські дії над образом Dll в пам'яті. Після таких налаштувань багато програм (антивіруси, персональні firewall) починають нервово курити і не помічати чорних справ твоєї програми. Впровадження довільного Exe файлу. Здійснюється за допомогою функції InjectExe ().
function InjectExe (Process: dword; Data: pointer): boolean;Для роботи функції потрібно передати два параметри: Дескриптор (handle) процесу, в який будемо впроваджуватися і адреса образу файлу в поточному процесі.
Ін'єкція способу пов'язаних з поточною діяльністю. Функція InjectThisExe () буде корисна, коли не хочеться чи ні можливості юзати бібліотеки dll. Опис функції і параметрів наводити не буду, так як вони стандартні і нічим не відрізняються від опису попередньої.
У функції п'ять вхідних параметрів: 1. Process - дескриптор процесу. 2. Thread - покажчик на процедуру, яку будемо впроваджувати. 3. Info - адреса даних для процедури. 4. InfoLen - розмір переданих даних. 5. Results - необхідність повернення результату (якщо true, то функція поверне передані дані).
Функція встановлює перехоплення потрібної функції. Як параметри просить: 1. TargetProc - адреса перехоплюваних функції. 2. NewProc - адреса функції, яка буде викликатися замість перехоплюваних. 3. OldProc - змінна, в якій буде збережений адресу моста до оригінальної функції (знадобиться, коли потрібно зупинити перехоплення і повернути все на місце). Для перехоплення функцій експортованих з DLL в поточному процесі передбачена окрема функція:
function HookProc (lpModuleName, lpProcName: PChar; NewProc: pointer; var OldProc: pointer): boolean;Вхідних параметрів чотири: 1. Ім'я модуля (dll). 2. Ім'я функції; будь уважний, регістр у вказівці імені функції відіграє роль. 3. Покажчик на функцію-заміну. 4. Адреса до оригінальної функції.
Перехоплювати функцію без перепочинку не має сенсу, тому рано чи пізно потрібно зупиняти процес перехоплення. Для цього в AdvApiHook реалізована функція UnhookCode (), яка в якості єдиного параметра приймає покажчик на адресу моста до оригінальної функції.
Відключення захисту системних файлів. В ОС Windows, що базуються на ядрі NT, не можна просто взяти і змінити системні файли - захист System File Protection чіпати їх не дасть. Для вирішення цього завдання в модулі визначена функція DisableSFC (). Передавати параметри їй не потрібно. Як результат повертає булевское значення.
Завершення процесу через режим налагодження. Напевно, ти стикався з процесами, які важко «вбити». Стандартні функції на кшталт TerminateProceess () не допомагають. Для усунення цієї проблеми прийнято використовувати так звані налагоджувальні функції. Спочатку процес переводиться в оцінний режим, а потім знищується. Таким чином можна завершити практично будь-який «шкідливий» процес. Автор AdvApiHook реалізував просту надбудову для завершення процесу через оцінний режим.
function DebugKillProcess (ProcessId: dword): Boolean;В якості єдиного параметра функції потрібно передати pid процесу. У разі успішного завершення процесу функція поверне true.
Ковбаса WinSock Hooker
Досить теорії, пора переходити до практики. Зараз я розповім, як написати додаток для перехоплення функції send (). Для початку створи в Delphi новий проект і намалюй форму, хоча б віддалено схожу на мою. Як закінчиш творчу частину, вставляй наш DVD диск, копіюй з нього модуль AdvApiHook і негайно підключай до свого проекту.
Перше, що нам необхідно зробити - навчити програму отримувати список всіх запущених процесів і їх дескрипторів. Всі ці дані будуть відображатися в компоненті ListView. Саме з цього списку ми будемо вибирати процес-жертву. Для отримання списку поточних процесів існує кілька способів. Розглянемо найбільш простий з них - використання модуля tlHelp32, що входить в стандартну поставку Delphi. Заради отримання процесів я завів окрему процедуру - GetAllProcess (). Її код ти побачиш на врізки.
Код процедури GetAllProcess ()
var _SnapList: THandle; _ProcEntry: TProcessEntry32; begin If NOT (EnableDebugPrivilege ()) Then begin reLog.SelAttributes.Color: = clMaroon; reLog.Lines.Add ( 'Не вдалося отримати привілеї отладчика!'); End; lvProcessList.Items.Clear; _ProcEntry.dwSize: = SizeOf (TProcessEntry32); _SnapList: = CreateToolHelp32SnapShot (TH32CS_SNAPPROCESS, 0); If (Process32First (_SnapList, _ProcEntry)) Then begin Repeat with lvProcessList.Items.Add Do begin Caption: = IntToStr (_ProcEntry.th32ProcessID); SubItems.Add (ExtractFileName (_ProcEntry.szExeFile)); end; Until not (Process32Next (_SnapList, _ProcEntry)); end; CloseHandle (_SnapList); End;На самому початку цієї процедури я викликаю функцію EnableDebugPrivilige (). Функція ця самописна і її вигляд ти можеш подивитися, відкривши вихідні з диска. Скажу лише, що вона потрібна для отримання налагоджувальних привілеїв. З цими привілеями з'являється можливість отримувати handle навіть у системних процесів. Якщо функція повернула false, то я просто повідомляю про це в лог і продовжую виконання процедури. Отримання списку процесів зводиться до кількох кроків. Перший етап знаменується використанням API функції CreateToolHelp32SnapShot (). Вона отримує знімок об'єктів, визначених у першому параметрі. Я вказав константу TH32CS_SNAPPROCESS, яка має на увазі отримання знімка одних лише процесів, так як для сьогоднішнього прикладу цього цілком достатньо. Крім процесів ти можеш отримати:
Якщо функція CreateToolHelp32SnapShot () виконалася успішно, значить, потрібно пробігтися по списку отриманих об'єктів і вивести їх в ListView. Для «пробіжки» я використовую функції Process32First () і Process32Next (). Параметри у обох функцій однакові:
Отже, список процесів у нас є. Повісь виклик GetAllProcess () на подію OnCreate форми і запусти програму. Якщо ти не допустив в лістингу помилок, то після запуску ListView повинен заповнитися списком процесів.
робимо захоплення
Тепер, коли у нас є список процесів, можна приступати до реалізації найголовнішої частини - перехоплення функцій. Перехоплювати функції найзручніше з бібліотеки dll. Принцип такий: впроваджуємо бібліотеку в чужій процес, методом сплайсингу робимо перехоплення. Зараз все здається складним і заплутаним, але насправді все просто. Створюй в Delphi новий проект типу DLL і потихеньку перекочуй в нього бездушні рядки коду з врізки «Код DLL».
код DLL
library project1; uses Windows, advApiHook, Messages, SysUtils; type TSocket = integer; TSendProcedure = function (s: TSocket; var Buf; len, flags: Integer): Integer; stdcall; var _pOldSend: TSendProcedure; _hinst, _h: integer; procedure SendData (data: string; funcType: integer; Buff: pointer; len: integer); var d: TCopyDataStruct; begin case funcType of 10: begin d.lpData: = Buff; d.cbData: = len; d.dwData: = 10; end; 30: begin d.lpData: = pchar (data); d.cbData: = length (data); d.dwData: = 30; end; end; SendMessage (_h, WM_COPYDATA, 0, LongInt (@d)); End; function xSend (s: TSocket; var Buf; len, flags: Integer): Integer; stdcall; begin SendData ( '', 10, addr (string (buf)), len); result: = _ pOldSend (s, buf, len, flags); end; procedure DLLEntryPoint (dwReason: DWord); begin case dwReason of DLL_PROCESS_ATTACH: begin SendData ( 'Бібліотека завантажена. Починається підготовка до перехоплення ...', 30, nil, 0); _hinst: = GetModuleHandle (nil); StopThreads; HookProc ( 'WS2_32.dll', 'send', @ xSend, @ _ pOldSend); SendData ( 'Підміна функцій завершилася успіхом!', 30, nil, 0); RunThreads; end; DLL_PROCESS_DETACH: begin SendData ( 'Знімаємо перехоплення ...', 30, nil, 0); UnhookCode (@_ pOldsend); end; end; end; begin _h: = findwindow (nil, 'WinSock Sniffer'); if (_h = 0) then begin MessageBox (0, 'не знайдено вікно клієнтської частини програми!', 'Помилка!', 0); ExitThread (0); end; DllProc: = @DLLEntryPoint; DLLEntryPoint (DLL_PROCESS_ATTACH); end.Розглядати наведений код найзручніше з процедури DLLEntryPoint. Саме в ній відбувається реакція на події, пов'язані з DLL (завантаження / розвантаження бібліотеки). Під час завантаження бібліотеки виникає подія DLL_PROCESS_ATTACH. Для нас це знак до установки перехоплення. Перед тим, як встановити перехоплення, я відправляю клієнту (основному з додатком) інформацію про поточну ситуацію. У своєму прикладі я передавав цілі рядки, але на практиці краще відправляти коди подій / помилок, визначити які можна заздалегідь. Процес передачі інформації з DLL в основну програму здійснюється за допомогою самопісний функції SendData (). В теоретичній частині статті я описував мінуси перехоплення методом сплайсингу. Як ти пам'ятаєш, вони полягали в потоках. Рішення проблеми було також озвучено - це тимчасова зупинка всіх потоків. Для зупинки потоків чужого процесу в модулі AdvAPIHook є функція StopThreads (). Параметрів вона не вимагає. Зупинивши потоки, можна встановлювати перехоплення. Для цього я використовую функцію HookProc (). Як параметри я передаю їй:
Ім'я бібліотеки, в якій оголошена перехоплюється функція. Оскільки в прикладі мене цікавила лише функція send (), то я вказав W32_32.dll (саме в цій бібліотеці визначені всі функції другої версії WinSock API).
Назва функції. У прикладі я вказав «send». Це найпоширеніша функція для відправки даних по мережі, її використовують практично всі програми. Зверни увагу на регістр, який використовується в написанні імені функції. Ім'я функції повністю складається з маленьких літер. Чи не звернеш на це увагу - потрапиш на налагодження таємничих помилок «Access Violаtion».
Покажчик на функцію підстави. В якості опції підстави в моїй бібліотеці визначена функція xSend (). Покажчик на змінну, для збереження моста до оригінальної функції. Я вказую тут _pOldSend. Після виконання HookProc () в поточному процесі замість функції send () буде викликати xsend (). Метою статті було показати, як можна перехоплювати дані, передані будь-яким мережевим додатком, тому в підставний функції я просто передаю буфер з даними. Таким чином, ми отримуємо те, що потрібно нам, а додаток-жертва, ні про що не здогадуючись, продовжує виконувати свою роботу. Встановивши перехоплення, потрібно запустити зупинені раніше потоки. Для відновлення роботи потоків я використовую функції RunThreads (), якою також не потрібні параметри.
тестуємо
Можна вважати, що найпростіший приклад перехоплення мережевих функцій готовий. Точніше, реалізований процес перехоплення однієї лише функції - send (). Перехоплення інших ти зможеш реалізувати самостійно, тим більше що принцип буде повністю таким же. Перед тим, як ми почнемо тестувати, відкомпілюйте бібліотеку і повернися до нашого основного проекту. Створи обробник події OnClick () для кнопки, після натискання якої ми будемо впроваджувати бібліотеку, і перепиши в нього код з врізки «Оброблювач OnClick ()». Я не буду розписувати цей код цілком, так як в ньому немає нічого складного. Все, що в ньому відбувається - отримання handle процесу по його pid і впровадження створеної нами бібліотеки за допомогою функції InjectDll (), опис якої я вже наводив.
Оброблювач OnClick ()
_h: = OpenProcess (PROCESS_ALL_ACCESS, false, StrToInt (lvProcessList.Selected.Caption)); _dllPath: = ExtractFilePath (ParamStr (0)) + 'test.dll'; InjectDll (_h, pchar (_dllPath));Як тест я вирішив перехопити дані, які відправляє всім відомий TotalCommander при з'єднанні з FTP сервером. Впровадивши нашу хакерську бібліотеку в процес totalcmd.exe і запустивши в Total Commander'е процес з'єднання з ftp сервером, я спостерігав, як лог почав заповнюватися командами протоколу FTP. Оскільки протокол FTP не є безпечним, то всі важливі дані, що передаються серверу, були успішно перехоплені. Результат ти можеш побачити на малюнку.
Все готово
У простому прикладі я показав перехоплення тільки функції send (). Проте, мережевий набір WinSock API містить і інші функції для відправки даних, а значить, у тебе є полігон для нових випробувань. Не лінуйся ставити різні експерименти, адже тільки шляхом проб і помилок можна вирішити будь-яке завдання. Якщо у тебе виникли питання або пропозиції, то ласкаво прошу, я завжди відкритий для спілкування.
Стаття опублікована в журналі "Хакер" (http://xakep.ru). Май 2008 р
Посилання на журнал: http://goo.gl/nTsCWW
Відразу виникає питання: «Звідки додаток знає, в якій бібліотеці оголошена потрібна функція?