Авторские статьи Перехват апи функций.

Discussion in 'Статьи' started by DooD, 26 Dec 2011.

  1. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    Доброго времени суток ув. форумчане.Покопавшись на форуме я понял что не хватает статей по перехвату АПИ,да и я давно ничего толком не писал.Так что сегодня пойдет речь,я думаю,об одном из самых перспективынх методов перехвата АПИ в ring 3- сплайсинге.

    И так не много теории.

    Сплайсинг-метод перехвата API функций в режиме юзера (ring 3). Обычно изменяются первые 5 байт функции на длинный прыжок по адресу обработчика перехвата.В зависимости от ситуации может понадобится дизассемблер длин инструкций,однако M$ изменила пролог начиная с ХР SP2, позволяя при этом не анализировать длины.Длины в 5 байт будет достаточно(5байт- E9h- опкод прыжка jmp).Сплайсинг так же позволяет осуществлять глобальный перехват функций, тем самым охватывая все процессы в системе.

    Применение:
    Данный вид перехвата используется:
    1)Десктопное ПО производящее операции с ОС (например мониторы)
    2)Хуки
    3)Малварь (руткиты и т.п.)

    Инструменты:
    1)ЯП (в данном случае Delphi)
    2)музычка (kmfdm или rammstein сойдет)
    3)руки.голова

    И так начнем.
    Как вы поняли наш код будет размещаться в dll-ке которая будет загружаться во все процессы и творить там свои темные дела.Чтобы не парится я буду извращаться с мэссэджбоксом.

    код:
    Создаем две записи-структуры

    Code:
    type
    
    OC=packed record
    frst:dword;
    sec:word;
    end;
    
    fj= packed record
    pusho:byte;
    pushar:pointer;
    reto:byte;
    end;
    1-я будет содержать запись на оригинал кода.2-я длинный прыжок

    объявим переменные структур


    Code:
    var
    jmpmw,jmpma:fj;
    ocmw,ocma:oc;
    mwadr,maadr:pointer;
    я возьму функцию messageboxex- это считайте тот же messagebox только в нем присутствует идентификатор языка.

    Теперь нам нужно описать функции на которые будет происходить прыжок.
    Их будет две.Почему так?Мурка заключается в том что существуют две категории АПИ ansi и Unicode которые имеют соответствующее окончание MessageBoxA или например MessageBoxW. Соль в том что анси является 8 битной кодировкой и может предоставлять только 256 уникальных символов, в то время как юникод обеспечивает 65535 уникальных символов,это было сделано с целью работы со всеми языками мира.

    начнем описание функций:

    Code:
    function NMessageBoxExA(hWnd: HWND; lpText, lpCaption: PAnsiChar;
                              uType: UINT; wLanguageId: Word): Integer; stdcall;
    var txt,cap:pwidechar;
    txtl,capl:dword;
    begin
    txtl:=lstrlen(lptext)*sizeof(widechar)+2;
    capl:=lstrlen(lpcaption)*sizeof(widechar)+2;
    getmem(txt,txtl);
    getmem(cap,capl);
    stringtowidechar(lptext,txt,txtl);
    stringtowidechar(lpcaption,cap,capl);
    result:=MessageBoxExW(hWnd, txt, Cap, uType, wLanguageId);
    freemem(txt);
    freemem(cap);
    end;
    что к чему. новая функция имеет те же флаги что и обычная:
    дескриптор окна, текст сообщения,заголовок окна,стиль окна и язык.

    работаем с widechar выше разобрали почему(т.к. он поддерживает интернациональные символы).

    напрямую со string мы не работаем,а работаем с памятью.

    Получаем длину текста и длину заголовка.Выделяем кол-во динамической памяти= длине, получая данные в нужные переменные.Затем конвертируем строки и получаем готовые флаги ф-ии.вызываем ф-ю с нужными параметрами и освобождаем память.

    Следующей определяем оригинал

    Code:
    function TrueMessageBoxExW(hWnd: HWND; lpText, lpCaption: PWideChar;
                               uType: UINT; wLanguageId: Word): Integer; stdcall;
    var
    Written: dword;
    begin
    
    writeProcessMemory(INVALID_HANDLE_VALUE, mwadr,@ocmw, SizeOf(OC), Written);
    Result := MessageBoxExW(hWnd, lpText, lpCaption, uType, wLanguageId);
    WriteProcessMemory(INVALID_HANDLE_VALUE, mwadr,@Jmpmw, SizeOf(fj), Written);
    end;
    За ней идет юникодный msgbox


    Code:
    function NMessageBoxExW(hWnd: HWND; lpText, lpCaption: PWideChar;
                              uType: UINT; wLanguageId: Word): Integer; stdcall;
    var
     Atxt: PWideChar;
     ntxtt: PWideChar;
     nlen: dword;
     a:integer;
    begin
     a:=length('Happy new year asshole!');
     Atxt := stringtopwide('Happy new year asshole!',a);
    
     nlen := (lstrlenw(Atxt) + lstrlenw(lpText)) * SizeOf(WideChar) + 20;
     GetMem(ntxtt, nlen);
     lstrcpyw(ntxtt, lpText);
     lstrcatw(ntxtt, #10#13#10#13);
     lstrcatw(ntxtt, Atxt);
     FreeMem(Atxt);
     Result := TrueMessageBoxExW(hWnd, ntxtt, lpCaption, uType, wLanguageId);
     FreeMem(ntxtt);
    end;
    по анологии с ansichar заполняем Unicode. Вы можете наблюдать строку.Эта строка будет выдаваться теперь при вызовах msgbox. Но в самом начале кода нам потребуется две вспомогательные функции.
    вот они:

    Code:
    Function AllocMem(Size: Integer): Pointer;
    asm
    or eax,eax
    jz @exit
    push eax
    CALL System.@getmem
    pop edx
    push eax
    mov cl,0
    call System.@fillchar
    pop eax
    @exit:
    end;
    *написано с целью не подключать модуль sysutils
    эта функция выд-т область памяти,после чего мы применяем функцию конвертирования string строки в widechar

    Code:
    function stringtopwide(str:string;var a:integer):pwidechar;
    var
    pwc:pwidechar;
    d:integer;
    begin
    d:=length(str)+1;
    a:=d*2;
    pwc:=allocmem(a);
    multibytetowidechar(cp_acp,0,pchar(str),d,pwc,a);
    result:=pwc;
    end;
    Дальше мы получаем адреса функций ,заполняем записи и устанавливаем прыжок.

    Code:
    procedure SetHook;
    var
     hwnd: dword;
     Byte: dword;
    begin
      hwnd := GetModuleHandle('user32.dll');
      mwadr  := GetProcAddress(hwnd, 'MessageBoxExW');
      maadr  := GetProcAddress(hwnd, 'MessageBoxExA');
      ReadProcessMemory(INVALID_HANDLE_VALUE, mwadr, @ocmw, SizeOf(OC), Byte);
      ReadProcessMemory(INVALID_HANDLE_VALUE, maadr, @ocma, SizeOf(OC), Byte);
      jmpmw.pusho  := $68;
      jmpmw.PushAr := @NMessageBoxExW;
      jmpmw.RetO   := $C3;
      jmpma.pusho  := $68;
      jmpma.PushAr := @NMessageBoxExA;
      jmpma.RetO   := $C3;
      WriteProcessMemory(INVALID_HANDLE_VALUE, mwadr, @jmpmw, SizeOf(fj), Byte);
      WriteProcessMemory(INVALID_HANDLE_VALUE, maadr, @jmpma, SizeOf(fj), Byte);
    end;
    разбираем:
    получаем дескриптор системной dll-ки в которой хранятся наши функции.
    записываем их адреса в записи.
    сохраняем оригинальные начала. Формирование новых начал функций в структурах jmpmw и jmpma идет так: опкод 68h -push, затем ее аргумент – адрес нашей функции, которая заменит оригинальную, в конце идет опкод c3h-ret.И переписываются оригинальные начала на наши.
    когда надо подставить оригинал,возвращаем байты

    Code:
    Procedure Unhook;
    var
     Byte: dword;
    begin
      WriteProcessMemory(INVALID_HANDLE_VALUE, maadr, @ocma, SizeOf(OC), Byte);
      WriteProcessMemory(INVALID_HANDLE_VALUE, mwadr, @ocmw, SizeOf(OC), Byte);
    end;
    Далее следует процедура,которая поможет загрузить нашу dll-ку глобально


    Code:
    Function Msg(code : integer; wParam : word;
                        lParam : longint) : longint; stdcall;
    begin
     CallNextHookEx(0, Code, wParam, lparam);
     Result := 0;
    end;

    собсно сама процедура

    Code:
    Procedure SetGlobalHookO;
    begin
     SetWindowsHookEx(WH_GETMESSAGE, @Msg, HInstance, 0);
     Sleep(INFINITE);
    end;
    и оставляем в памяти.

    Code:
    Procedure SetGlobalHook;
    var
     hMutex: dword;
     TrId: dword;
    begin
     hMutex := CreateMutex(nil, false, 'Hook');
     if GetLastError = 0 then
     CreateThread(nil, 0, @SetGlobalHookO, nil, 0, TrId) else
     CloseHandle(hMutex);
    end;
    устанавливаем глобальный хук. проверяем запущен ли файл.Только поток владеющий мьютексом имеет домтуп к области памяти.

    Code:
    procedure DLL(dwReason: DWord);
    begin
    case dwReason of
    DLL_PROCESS_ATTACH:
    begin
    SetGlobalHook;
    SetHook;
    end;
    DLL_PROCESS_DETACH: UnHook;
    end;
    end;
    
    begin
    DllProc := @DLL;
    DLL(DLL_PROCESS_ATTACH);
    end.
    и смысл последней процедуры: при аттаче длл устанавливается хук. при детаче-хук соответственно снимается.

    Остается лишь написать лоадер.

    Code:
    .386
    .model flat,stdcall
    option casemap:none
    
    include include\windows.inc
    include include\kernel32.inc
    
    includelib lib\kernel32.lib
    
    .data
    libname db 'MSGSPLICE.dll',0
    
    start:
    invoke LoadLibrary,addr libname
    invoke Sleep,INFINITE
    
    end start
    грузим dll-ку и висим в памяти.


    и так алгоритм работы в кратце (итог типо)


    1) получаем дескриптор библиотеки , в которой содержатся функции которые необходимо перехватить.

    2) в соответствующие переменные записываем оригинальные адреса функций

    3) сохраняются оригинальные начала функций

    4) Формирование новых начал функций в структурах jmpmw и jmpma. В начале идет опкод команды push, затем ее аргумент – адрес нашей функции, которая заменит оригинальную, в конце идет оператор ret.

    5) переписываются начала оригинальных API-функции данными из структур



    Таким образом в начало оригинальных функций
    будут вставлены переходы на их функции-заменители.

    Рез-ат работы перехвата:
    [​IMG]
    ССЫЛАЛСЯ:
    На код перехвата createprocess ms-rema.

    Сильно не бейте!:)спасибо за внимание!
     
    #1 DooD, 26 Dec 2011
    Last edited: 26 Dec 2011
    marynli, PaBIIIaH, SHiNiGaMi and 7 others like this.
  2. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    ну,что скажете люди?
     
  3. Demon__666

    Demon__666 New Member

    Joined:
    26 Sep 2011
    Messages:
    16
    Likes Received:
    1
    Reputations:
    0
    Круто )
     
  4. shadowrun

    shadowrun Banned

    Joined:
    29 Aug 2010
    Messages:
    842
    Likes Received:
    170
    Reputations:
    84
    Спасибо, познавательно. На дельфячем языке мне норм понятно.
    +
     
  5. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    То, что на ачате нету статей по перехвату API, это нехорошо, конечно. Но и здесь не описано ничего нового. Все это можно найти в инете. Тот же MS-REM описал все намного лучше.

    Это также есть на самом деле нехорошо:
    Так как запросто может произойти ошибка в многопоточных приложениях, которые в свою очередь сразу же аварийно завершат работу.

    Код оформлен плохо. Эти INVALID_HANDLE_VALUE немного сбивают с толку.

    Также нужно сказать, что здесь перезаписывается не 5, а 6 байт.
     
    #5 Chrome~, 27 Dec 2011
    Last edited: 27 Dec 2011
  6. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    есть ли тогда смысл останавливать,а потом возобновлять потоки?Хотя я с этим не встречался.
     
    1 person likes this.
  7. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    Нет. Здесь нужен дизассемблер длин. Если использовать способ с перезаписыванием первых 5 байт, то алгоритм примерно такой:
    0. С помощью дизассемблера длин выделяем столько целых первых команд перехватываемой функции, длина которых >= 5. Запоминаем эту длину в переменную len.
    1. Запоминаем адрес следующей команды в переменную next_opcode.
    2. Выделяем len + 5 байт памяти, адрес этой памяти записываем в переменную old_func.
    3. В выделенный участок памяти копируем первые len байт из перехватываемой функции и дописываем команду jmp next_opcode (для нее мы выделели 5 дополнительных байт).
    4. Перезаписываем первые 5 байт перехватываемой функции командой jmp <адрес нашей функции>.
    5. Когда нужно вызвать старую функцию, делаем call old_func.
     
  8. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    гемморно.хотя я хз,тестил без дизасма-все прекрасно работает.
     
  9. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    В многопоточных приложениях можно увидеть проблему. Например, перехватываешь recv (Winsock) в браузере и открываешь несколько вкладок.
     
  10. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    а,я думал ты конкретно за эту фу-ю.возможно для других функций дизасм понадобится.надо бы юнит попробовать накатать.
     
  11. _nic

    _nic Elder - Старейшина

    Joined:
    5 May 2006
    Messages:
    651
    Likes Received:
    54
    Reputations:
    3
    Потоко опасный метод.Не нада ничего восстанавливать в теле оригинальной ф-ции.Кури детуры.
    ЗЫ: да и вообще ты люто бешанно боянишь http://forum.antichat.ru/thread32176.html :D
     
    #11 _nic, 27 Dec 2011
    Last edited: 27 Dec 2011
  12. Ptr

    Ptr New Member

    Joined:
    1 Oct 2011
    Messages:
    12
    Likes Received:
    0
    Reputations:
    0
    Chrome~ уже написал, что нужно использовать гейт.

    Суть хуков не раскрыта, вот если бы написал о VMT, IAT, HWBP и прочих популярных методах, да ещё и на C/C++, то было бы шикарно.
     
  13. DooD

    DooD Elder - Старейшина

    Joined:
    30 Sep 2010
    Messages:
    1,168
    Likes Received:
    450
    Reputations:
    288
    си я не знаю к сожалению,только намылился изучать. модификацию таблиц не рассматривал.
     
  14. _nic

    _nic Elder - Старейшина

    Joined:
    5 May 2006
    Messages:
    651
    Likes Received:
    54
    Reputations:
    3
    Суть раскрыта тут http://habrahabr.ru/blogs/cpp/90377/ .Только в детурах что там выложенны, в _AnalyzeFunctionCode нада впаять норм дизассемблер длин.И все будет зергуд работать)
     
  15. WmMariupol

    WmMariupol Banned

    Joined:
    16 Dec 2011
    Messages:
    1
    Likes Received:
    10
    Reputations:
    0
    А на каких языках это можно всё как бы... автоматизировать до вирусного процесса?