Итак, поговорим том, как сделать inject в чужой процесс своей DLLки и собственно перехват функции через импорт на ассемблере.
Для начала, что нам необходимо знать:
1. Формат PE файлов, так как придется искать таблицу импортов(Import Address Table, в народе IAT)
2. Небольшое знание устройства памяти и WinAPI
Порядок действий ижекта у нас получается следующий:
1. Ищем в памяти необходимый процесс.
2. Открываем этот процесс на запись.
3. Резервируем в процессе необходимое количество памяти
4. Копируем туда исполняемый код который при помощи loadlibrary загрузит нужную DLL
5. Вызываем удаленный тред с адреса как раз который прописали в 4ом пункте
6. Завершаем удаленый тред.
После внедрения дллки, теперь должна работать сама DLL, что она делает ниже:
1. При загрузке DLLки автоматически стартует LibMain.
2. Ищем IAT.
3. В IAT ищем ссылку ту функцию которую будем подменять.
4. Сохраняем адрес ссылки на старую функцию и записываем адрес ссылки на свою.
5. Что то делаем при вызове собственно перехваченной функции.
Подготовительный момент.
Создадим простенькое WinAPI приложение, с кнопкой ОК, при нажатии на которую будет вываливаться мессадж бокс с надписью "Message".
Выглядеть оно будет так:
Называется приложение victim.exe
Перехватывать будем собственно как раз функцию MessageBoxA, а подменять будем слово Message.
Эта программа в исходниках выглядит следующим образом, ничего сложного, все из шаблона, по этому комментировать особа нечего тут.
.386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include victim.inc .code start: invoke GetModuleHandle,NULL mov hInstance,eax invoke InitCommonControls invoke DialogBoxParam,hInstance,IDD_MAIN,NULL,addr DlgProc,NULL invoke ExitProcess,0 ;######################################################################## DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM mov eax,uMsg .if eax==WM_INITDIALOG ;initialization here .elseif eax==WM_COMMAND mov edx,wParam movzx eax,dx shr edx,16 .if edx==BN_CLICKED .if eax==IDOK invoke MessageBox, hWin, addr wMessage, addr wTitle, MB_OK .elseif eax==IDCANCEL invoke SendMessage,hWin,WM_CLOSE,NULL,NULL .endif .endif .elseif eax==WM_CLOSE invoke EndDialog,hWin,0 .else mov eax,FALSE ret .endif mov eax,TRUE ret DlgProc endp end startЯ только добавил сюда сам MessageBox чтобы он вызывался по нажатию на кнопку OK.
Посмотрим что в импортах появилось у этой простенькой программки при помощи CFF Explorer:
Функция MessageBoxA находится в системной дллке User32.dll, это мы запомним, так как будем дальше искать в таблице IAT этого приложения. Можно конечно жестко прописать где и как ее менять, но это не наш принцип, так как смещения могут поплыть в новых версиях программы.
По этому поиск будет осуществляться динамически по PE заголовку.
Итак, дальше готовим сам инжектор, фаил будет называться inject.exe. У него не будет никакого интерфейса и его задача заставить думать систему, что жертва загрузила DLL.
Кодъ достаточно простой, с пояснениями:
Для начала объявим для удобства свою структуру(можно конечно сразу байткод записать, но для наглядности - структура):
Опять же для удобства я посчитал сколько она занимает в памяти.
jcode struct lpush db ? ; 0 ; код команды push pname dd ? ; 1 ; указатель на имя DLL acall dw ? ; 5 ; код команды call llpoint dd ? ; 7 ; ссылка на указатель системной функции LoadLibrary etpush db ? ; 11 ; код команды push dd ? ; 12 ; ноль bcall dw ? ; 16 ; код команды Call etpoint dd ? ; 18 ; ссылка на указатель системной функции ExitThread lladdr dd ? ; 22 ; указатель на LoadLibrary etaddr dd ? ; 26 ; указатель на ExitThread lname db 64h dup (?) ;30 ; имя dll jcode endsПосмотрим глазками на заполненую структуру:
HEX:
А теперь дизасемблированное:
Как видно, все достаточно просто и никакого гениального кода.
Теперь сам код инжектора:
.386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include inject.inc ; подцепим необходимые данные и переменные .code start: mov eax, sizeof lprocentry ; вычисляем размет структуры PROCESSENTRY32 mov lprocentry.dwSize, eax ; запихаем этот размер в структуру PROCESSENTRY32 invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS OR TH32CS_SNAPMODULE, NULL ; получим хэндл списка процессов .if eax == NULL ; если нулевой jmp errorExit ; что то пошло не так, выход .endif mov hSnap, eax ; запишим хэндл в переменную hSnap invoke Process32First, hSnap, offset lprocentry ; запишем данные первого процесса в структуру PROCESSENTRY32 .if eax == NULL ; если нулевой jmp errorExit ; что то пошло не так, выход .endif mainsearch: ; так как касперский не люит lstrcmp изобретаем свой велосипед mov ecx, sizeof processname ; запишем размер имени которое ищем в ecx mov edi, offset lprocentry.szExeFile ; в edi запишем текущее имя mov esi, offset processname ; в esi запишем то с чем сравниваем nextsimb: mov al, BYTE PTR [esi] ; запишем байт из того что сравниваем в al .if al != BYTE PTR [edi] ; сравним его с байтом в названии текущего процесса jmp nextproc ; если не совпадает, прыгаем на получение имени следующего процесса .endif inc esi ; если совпало, то смещаем указатель позиции в имени на 1 inc edi ; если совпало, то смещаем указатель позиции в имени на 1 loop nextsimb ; зацикливаем пока не сравним целиком имя mov eax, lprocentry.th32ProcessID ; копируем Process ID в eax mov pid, eax ; перемещаем его в переменную в pid jmp processfound ; процесс найден, прыгаем на обработку его nextproc: invoke Process32Next, hSnap, offset lprocentry ; это имя процесса не подходит, получаем следующее .if eax == NULL ; если нулевой jmp errorExit ; что то пошло не так, выход .endif jmp mainsearch ; повторяем основной цикл processfound: ; процесс найден invoke OpenProcess, PROCESS_CREATE_THREAD OR PROCESS_VM_WRITE OR PROCESS_VM_OPERATION, NULL, pid ; открываем его на чтение mov hProc, eax ; сохраняем хеадер, он нам понадобится еще invoke VirtualAllocEx, hProc, NULL, sizeof jcode, MEM_COMMIT,PAGE_EXECUTE_READWRITE ; выделяем память причем в режиме записи mov mAddr, eax ; записываем адрес с которого начинается выделенная память invoke fillStruct, eax ; вызываем процесс который заполняет структуру invoke WriteProcessMemory, hProc, mAddr, addr jjcode, sizeof jcode, NULL ; записываем структуру в выделенную память invoke CreateRemoteThread, hProc, NULL, NULL, mAddr, NULL,NULL, NULL ; создаем тред от имени жертвы invoke WaitForSingleObject, eax, INFINITE ; ждем когда он отработает invoke VirtualFreeEx, hProc, mAddr, sizeof jcode, MEM_RELEASE ; корректно на всякий случай освобождаем память errorExit: invoke ExitProcess,0 ; завершаем работу fillStruct proc memaddr:DWORD ; заполнение структуры mov jjcode.lpush, 68h ; копируем код push, он равняется 68h mov eax, memaddr ; копируем начальный адрес выделенной памяти add eax, 1Eh ; прибавим к нему 1Eh чтобы он указывал на имя DLL mov jjcode.pname, eax ; копируем в указатель на имя mov jjcode.acall, 15ffh ; записываем код call он равняется 15ffh invoke LoadLibrary, offset kendll ; получаем базу kernel32.dll mov pkernel, eax; сохраним в переменной pkernel invoke GetProcAddress, eax, offset llibr ; получаем адрес функции LoadLibrary mov jjcode.lladdr, eax ; копируем в ячейку на которую будем ссылатся mov eax, memaddr ; копируем начальный адрес выделенной памяти add eax, 16h ; прибавим к нему 16h чтобы он ссылался на указатель функции LoadLibraryA mov jjcode.llpoint, eax ; записываем его в ссылку mov jjcode.etpush, 68h ; копируем код push, он равняется 68h mov jjcode.bcall, 15ffh ; записываем код call он равняется 15ffh invoke GetProcAddress, pkernel, offset exth ; получаем адрес функции ExitThread mov jjcode.etaddr, eax ; копируем в ячейку на которую будем ссылатся mov eax, memaddr ; копируем начальный адрес выделенной памяти add eax, 1Ah ; прибавим к нему 1Ah чтобы он ссылался на указатель функции ExitThread mov jjcode.etpoint, eax ; записываем его в ссылку invoke lstrcpy, addr jjcode.lname, addr libname ; копируем имя DLL в структуру ret fillStruct endp end startНу вот, на сегодня все, в ближайшее время я выложу вторую часть статьи, в которой будет описание как сделать DLL для инжекта и как она работает. Там же будут исходники и готовые бинарники для тестов.
Комментариев нет:
Отправить комментарий