1 февраля 2013 г.

Inject методом import - Часть 2

Небольшое отступление от основной темы. Вообще для чего можно пользовать инжект. Ну сходу могу сказать несколько применений. Допустим есть некая программа с закрытым исходным кодом и очень хочется немного расширить ее функционал, например она работает только с COM  ортом, а хочется чтобы работала через USB, так как в новых ноутах COM порта уже нет. Реверсится алгоритм работы с ком портом, пишется коннектор, оформляется в виде DLL, а дальше инжектится в программу и за место функции работы с ком портом, подставляется своя функция.

Что еще, например нам нужно скрыть процесс от Task Manager'a(Диспетчера задач) и любопытных глаз пользователя/админа. Делаем инжект в программу которая постоянно висит в памяти и не вызывает подозрений - PROFIT! Далее, на основе инжектов можно писать ботов для программ и игр. Например перехватив управление некоторыми функциями DirectX можно рисовать внутри игры какие нибуть нужные и полезные данные.  Ну и конечно еще можно перехватывать некие данные, например пароли и логины в программах перехватывая функцию ввода с клавиатуры/мыши.
Но я отвлекся, продолжим первую часть
Остановились мы на том, что написали жертву и написали сам инжектор, а осталась сама DLL которая будет подгружаться.

Тут у меня кончилось воображение, по этому DLL будет называться просто test.dll
Повторим задачи этой длл.
1. Найти PE заголовок основного процесса.
2. В PE заголовке найти таблицу Data Directories
3. В таблице Data Directories найти ссылку на таблицу импорта(IAT)
4. Найти ту функцию которую будем подменять у нас она называется MessageBoxA
5. Подменить этот адрес своей функцией.
6. Както обработать данные полученные функцией MessageBoxA я заменю слово "Message" на "Successful inject!"

Ну и наконец код DLL с коментариями:

.386
.model flat, stdcall  ;32 bit memory model
option casemap :none  ;case sensitive

include test.inc

.code

start:
    
    
LibMain proc instance:dword,reason:dword,reserved:dword
    .IF reason == DLL_PROCESS_ATTACH ; проверяем процесс приатачился?
        invoke    GetFuncIAT ; ищем функцию MessageBoxA
        .if    eax != -1
;                invoke    MessageBox, 0, addr userdll, addr userdll,MB_OK ; тестовый мессадж бокс, который покажет что инжект произошел
                mov        ebx, DWORD PTR [eax] ; получаем ссылку на MessageBoxA
                mov        oldAMes, ebx ; сохраняем адрес MessageBoxA, он нам пригодится
                push    eax ; сохраним eax в стеке
                invoke    VirtualProtect, eax, 4,PAGE_READWRITE, addr op ; переводим кусок памяти в запись иначе сработает исключение
                pop        eax ; восстанавливаем адрес в в eax
                mov        ebx, offset Intercept_MessageBoxA ; запишем в ebx адрес своей функции
                mov        DWORD PTR [eax], ebx ; а теперь его в таблицу IAT
                invoke    VirtualProtect, eax, 4, op, NULL ; вернем права на память как було!
        .endif        
    .endif
    mov eax,1 ; системе вренем 1, указывая, что дллка успешно загружена
    ret
LibMain endp


GetFuncIAT    proc uses    ebx ecx
    invoke    GetModuleHandle, NULL ; получаем начало проекции процесса в котором мы находимся, так называемую базу
    mov        hMod, eax ; сохраним его в hMod
    add        eax, 3ch ; сдвинимся на 3ch, чтобы узнать где начинается NT Headers
    xor        ebx, ebx ; обнулим ebx
    mov        bl, BYTE PTR [eax] ; узнаем по какому смещению начинается NT Headers
    add        ebx, hMod ; прибавим к нему базу
    .if        WORD PTR [ebx] != 4550h ; проверим, это вообще PE?
        jmp        errorExit ; если нет - выход
    .endif
    add        ebx, 6 ; прибавим 6 байт, чтобы узнать сколько секций, зачем я это сделал незнаю, но пусть будет
    xor        eax, eax ; обнуляем eax
    mov        ax, WORD PTR [ebx] ; получим количество секций
    mov        cSec, eax ; запишем в cSec
    add        ebx, 14 ; прибавим еще 14 байт
    xor        eax, eax ; обнуляем eax
    mov        ax, WORD PTR [ebx] ; получим размер заголовка Optional Header, тоже незнаю зачем это сделал - привычка похоже
    mov        sizeOpt, eax ; сохраним размер заголовка Optional Header в sizeOpt, 
    add        ebx, 4 ; прибавим 4 байта и окажемся в начале Optional Header
    mov        hOpt, ebx ; сохраним это знание в hOpt
    add        ebx, 104 ; прибавим 104 байта и окажемся в Data Directories на строчке Import Directory RVA
    mov        eax, DWORD PTR [ebx] ; указатель запишем в eax
    add        eax, hMod  ; прибавим базу
    mov        import, eax ; это и есть таблицы IAT указывающая на первую DLL в списке

nextlib:
    mov        curDll, eax ; сохраним eax в переменной curDll
    add        eax, 12 ; прибавим 12 байтов
    mov        ebx, DWORD PTR [eax] ; получим указатель без базы на название DLL
    add        ebx, hMod ; прибавим базу
    invoke    lstrcmp, ebx, offset userdll ; сравним, а не та ли дллка по имени user32.dll которую мы ищем
    .if        eax != 0 ; ежели не та
        mov        eax, import ; в eax записываем начальный адрес импорта
        add        eax, 20 ; прибавляем к нему 20 байт, чтобы перейти на следующую DLL
        mov        import, eax ; записываем в переменную import
        .if        DWORD PTR [eax] == 0 ; ежели по эту адресу 0
            jmp        errorExit ; то IAT закончилась - аварийный выход
        .endif
        jmp        nextlib ; прыгаем на метку nextlib
    .endif
    mov        eax, curDll ; в eax пишем ссылку на список функций
    mov        ebx, DWORD PTR [eax] ; в ebx записываем адрес на начало списка
    add        ebx, hMod ; прибавляем базу
    xor        ecx, ecx ; обнуляем счетчик ecx
nextfunc:
    mov        curFunc, ebx ; в переменной curFunc сохраняем ссылку на текущее имя функции
    mov        eax, DWORD PTR [ebx] ; получаем адрес строки с именем функции
    add        eax, hMod ; прибавляем базу
    add        eax, 2 ; прибавляем 2 байта, чтобы получить начало имени и отрезать техническую инфу
    push    ecx ; сохраним счетчик, он нам пригодится
    invoke    lstrcmp, eax, offset func ; сравниваем, а не та ли это функция MessageBoxA
    pop        ecx ; восстанавливаем ecx
    .if        eax != 0 ; ежели не та
        mov        ebx, curFunc ; в ebx восстанавливаем ссылку на адреса функций
        add        ebx, 4 ; смещаемся на 4 байта
        .if        DWORD PTR [ebx] == 0 ; если там 0, то список функций кончился
            jmp        errorExit ; аварийный выход
        .endif
        inc        ecx ; если не ноль добавляем к ecx 1
        jmp        nextfunc и прыгаем на обработку следующей функции
    .endif
    mov        eax, curDll ; восстанавливаем данные в переменной curDll
    add        eax, 16 ; прибавляем 16 и получим указатель на список указателей на функции
    mov        ebx, DWORD PTR [eax] ; получаем указатель на начало указателей на функции
    add        ebx, hMod ; прибавляем к нему базу
    imul    ecx, ecx, 4 ; вычисляем на какой позиции в списке указателей на функции находится наш
    add        ebx, ecx ; прибавляем к началу списка указателей на функции
    mov        eax, ebx ; скопируем в eax дабы вернуть указатель
    
    ret
    
errorExit:
    mov        eax, -1 ; аварийный выход, в eax подставим -1

    ret
GetFuncIAT endp

Intercept_MessageBoxA    proc ; фейковый MEssageBoxA
    pop        ebx ; сохраним точку возврата, она нам еще понадобится
    mov        eax, offset newStr ; получим адрес новой строчки которая выведется в MessageBoxA
    mov        DWORD PTR [esp+4], eax ; запишем адрес новой строчки в стек, где находится старая "Message" делая собственно подмен на "Successful inject!"
    mov        eax, oldAMes ; всопмним оригинальный адрес MessageBoxA
    call    eax ; вызываем MessageBoxA, но уже с подмененным текстом сообщения
    push    ebx ; восстанавливаем точку возврата
    ret
Intercept_MessageBoxA endp

end LibMain

Работает это следующим образом, test.dll кладется рядом с inject.exe, запускается victim.exe, если потыкать кнопку ОК, то видно что окошко со словом "Message" после чего не закрывая victim.exe, запускается inject.exe, если после этого потыкать на кнопку ОК, то сообщение уже будет "Successful inject!". Если оно появилось, то значит инжект сработал.
Вот в принципе и все, как видно - абсолютно ничего сложно, все достаточно элементарно.
Сегодня на pastebin выкладывать ничего не буду, ибо лень мне что то копипастой заниматься, все исходники и работающие программы можно скачать на депозите.
Удачи в дальнейшем иследовнии :)

Комментариев нет:

Отправить комментарий