Всем привет надо потокобезопасно просплайсить запущенный процесс план такой находим pid процесса который будем сплайсить вызываем NtSuspendProcess создаем в нем поток с точкой входа LoadLibrary которая грузит нашу DLL в DllMain только что созданного потока получаем список адресов функций которые надо просплайсить перебираем все замороженные потоки текущего процесса вызываем GetThreadContext для каждого смотрим Eip если хоть у какого то потока он указывает на адрес для сплайса + disasm(min(6)) байт (push ret) то сплайсинг можно или отменить или сделать NtResumeProcess NtSuspendProcess и снова попробовать если ни один поток не выполняет заменяемый код то ставим на все функции push ret и запускаем все потоки NtResumeProcess выходим из DllMain завершается поток созданный через CreateRemoteThread теперь все вызовы должны идти через хуки но тут может быть проблема после того как мы заморозим процесс и создадим удаленный поток он может не стартануть если один из замороженных потоков в процессе был в DllMain какой нить DLL вкратце прога аттачится к процессу любому и сплайс ставится на все функции одной библиотеки (ws2_32.dll) правильно ли я думаю или нужно как то по другому делать ? и вот еще что как безопасно снять хуки и выгрузить длл ? тут усложняется тем что любой поток в этот момент может выполнять код функции перехватчика в DLL или выполнять код в "трамлине" (тоже в DLL) или так же выполнять push ret готовясь перепрыгнуть в перехватчик по сути надо такое состояние чтобы ни один поток не выполнял код функции перехватчика или не выполнял код в "трамлине" или так же не выполнял push ret готовясь перепрыгнуть в перехватчик тогда можно снять перехваты разбудить процесс и выгрузить DLL хм можно конечно разместить в DLL еще одну процедуру запускать ее когда надо выгрузить DLL сперва заморозить процесс потом запустить ее через CreateRemoteThread она первым делом проверит Eip каждого потока чтоб не указывал на VA .text + size своей же секции .text и не на адрес функции + disasm(min(6)) байт для любой функции из ws2_32.dll и только тогда снимет хуки запустит процесс NtReasumeProcess и вызывет FreeLibraryAndExitThread и поток прибъет и DLL выгрузит напоследок надо только трамплины в DLL разместить в секции .text #pragma data_seg(push, r1, ".text") BYTE xxx[16]; #pragma data_seg(pop, r1) и в таком роде все остальные трамплины тогда надо только будет проверить чтоб Eip != .text + size и Eip != func_addr + disasm(min(6)) и можно снимать вот тут тоже проблема что эта функция может не стартануть если в замараживаемом процессе какой то поток уснул в DllMain я наверное нагенерил много бреда я новичок поэтому не судите строго а лучше посоветуйте как сделать хорошо и красиво. спасибо.
ЗАГРУКА Делаешь инжект, в DllMain Notificaiotn ATTACH to Proc^ DisableThreadLibraryCalls(hInstance); CloseHandle( CreateThread( ,, StartupRoutine,,,)); void StartupRoutine() { SusPendAllThreads() //практика говорит это делать не обезательно for () //By splice table { GetProcAddress(); len = 0; do { len += disasm() ; } while (no jmp, no ret, ... && len < LenJmpTrapline); if ( len < LenJmpTrapline ) { if (IsDebuggerPresent) {DebugBreak(); } //else skip } trapliceCode = genTrapline(); if ( trapliceCode ) //check success { WriteProcessMemory(GetCurrentProcess(), trapliceCode); // Это будет рулить } } } ПО ПОВОДУ ВЫГРУЗКИ А если поток будет ниже ws2_32.dll ? то и есть Magic!ntdll.dll recv!ws2_32.dll ? То и есть вам нужно будет счетчик вхождений в перехваты (при вы ходе -> траплайн возврата делаем дикременту счетчика ) С одной стороны красиво и правильно с другой стороны побочные дефекты которые на до фиксить: Перехвачиная функция генерирует сепшен(надо сделать декременты если факту раскрутки стека), И еще поток сделали Terminate надо отлавить это состояние и сделать опять таки дикремент. И да придется проверять, а не находятся ли потоки на траплайнах? В целом вопросы сплайсинга это сложной вопрос, много материала что бы грамотно реализовать. Так что это на статью.
63F45EF45RB65R6VR А может не обязательно именно на все, а достаточно только на 95% таких, которые уже прекомпилированы с готовностью к хотпатчингу? Тогда можно забить на потокобезопасность и преспокойно вписывать стандартные jmp ws2_32_func_hook по адресу ws2_32_func-5 и jmp $-5 по адресу ws2_32_func. Естесственно предварительно нужно удостовериться в наличии mov edi,edi или push imm8 в начале функции, а также пяти nop'ов перед ней. В случае push imm8 надо не забыть его себе в ws2_32_func_hook скопировать (хотя лучше даже просто проэмулировать этот единственный push imm8).
Сплайсингу сто лет уже, и статей уже куча. Единственная проблема из всего что тут написано это снятие хуков. Так сделайте в своем перехватичке проверку какого-то сигнала по которому просто возвращайте управление на оригинальную ф-цию. А трамплины пусть остаются)) И вообще вы уверены что вам их надо будет снимать?
Код (Text): SusPendAllThreads() //практика говорит это делать не обезательно если это делать через CreateToolhelp32Snapshot то ненадежно например с момента снимка до заморозки успеет еще куча потоков родиться а потом когда начнет писать push ret может быть крах каких то потоков например есть там пролог такой .text:71A9948E mov edi, edi .text:71A99490 push ebp .text:71A99491 mov ebp, esp .text:71A99493 pop ebp поток исполняет mov edi, edi и тут мы меняем пролог на push hooker ret в итоге когда он продолжит то начнет выполнять с середины команды push hooker я поэтому и хотел NtSuspendProcess заюзать она вроде как гарантирует что живых потоков не будет после нее но появляется другая проблема если какой то поток уснул в чьей нибудь DllMain то наш поток просто не сможет стартануть так как он должен вначале вызвать все DllMain всех библиотек я как планировал прога например spy.exe вызывает NtSuspendProcess для процесса например iexplore.exe потом вызывает CreateRemoteThread с точкой входа kernel32.dll!LoadLibrary для того что бы подгрузить мою DLL в iexplore.exe но возможен deadlock и тогда наш новоиспеченный поток зависнет и никогда не вызовет kernel32.dll!LoadLibrary если бы этой проблемы не существовало то дальше мы бы проверили Eip каждого потка на неравентство Eip != func_addr + disasm(min(6)) и если для всех это условие истинно то установили бы патчи push ret и перед выходом вызвали NtResumeProcess а если ложно то вернули бы FALSE из DllMain код в spy.exe что создал поток висевший на WaitForSingleObject проснулся бы увидел что код возврата FALSE вызвал бы NtResumeProcess NtSuspendProcess и повторил попытку максимум раза 3 если все три раза не удалось то вывести MessageBox типа "очень сложная ситуация" или сопроводить этот поток что застрял на прологе на disasm(min(6)) байт вперед кстати как заставить поток выполнить несколько байт и уснуть дальше ну что бы он ушел с пролога ? это я упустил из виду да можно завести счетчик и вертеть его через interlocked интринсики в итоге для снятия хуков и выгрузки DLL условие должно будет принять вид Eip != .text + size && Eip != ws2_32.dll!Magic + disasm(min(6)) && !Counter ну вообще если такое случится то максимум мы просто не сможем выгрузить DLL так как Counter всегда будет больше нуля а вообще интересно как это можно задетектить что процесс затерминатили (TerminateThread) или сепшен и счетчик надо задекрементить (_InterlockedDecrement)
>поток исполняет >mov edi, edi >и тут мы меняем пролог на >push hooker >ret При чем здесь push/ret? 'mov edi, edi' занимает два байта, как раз для 'jmp near' (тоже два байта) на пять байт в сторону младших адресов. Там приготовлено пять байт (nop'ы), чтобы вписать туда прыжок на требуемый адрес. Т.е. сначала мы заисываем 'jmp XXXXXXXX' в пять nop'ов, а затем меняем 'mov edi, edi' на короткий прыжок на записанный ранее 'jmp'. При желании можно использовать 'lock xchg' для перезаписи 'mov edi, edi'.
deLight '5 dup nop'/'mov edi, edi' -- вполне официальный механизм для hot-patch'а. cmpxchg8b переписывает восемь байт. Не факт, что ф-ия начинается с инструкции длиной 8 байт.
хм честно говоря я не знал про этот механизм поэтому и придумал весь этот геморой а что за imm8 и почему именно push не могли бы рассказать ?
63F45EF45RB65R6VR Вообще стандартный механизм подготовки к хотпатчингу — вставка mov edi,edi в начале функции. Но беглый просмотр ws2_32 показал, что некоторые даже подготовленные функции (т.е. имеющие предпролог в пять nop'ов) начинаются с push imm8. Это мало что меняет, т.к. инструкция занимает те же два байта, но её всё-таки важно исполнить/проэмулировать (в отличие от не имеющей эффекта mov edi,edi).
а можно поподробнее про сигнал как это работать должно ? снимать хотелось бы например каждый перехватчик шлет центру через Mailslot "письма" с даннымы конкретно в spy.exe если не снимать и переключиться на другой процесс (заинжектить в другой процесс) то они все вместе забъют память ядра которая выделяетсяь под "сообщения" mailslot да и вообще хотелось бы DLL выгружать если выбран другой процесс и инжектить в новый процесс
как я уже говорил для меня это было открытием в этом треде так что тот бред который я придумал от незнания спасибо за помощь и всем кто помог спасибо
deLight А если это 64 бита ? 25 байт тоже атомарно ? А как на счет того что в момент патчей либа выгрузилась? аахаха onSide То и есть выделенная память для траплайн пусть весит ? получается если корявые руки то пусть будет деградировать система , а руки равнять не станем? Вообще все равно не кто реализовать не сможет. RoutineSplice() { При загрузке вешается VEH. Делаем EnumThread с установкой TF. //дожидаемся состояния когда все треды влитят в VEHHandler по TFу . RemoveVEH(); //Цикл навала патчей for () { } SignalRoutineSpliceIsEnd(); } VEHHandler(EXCEPTION_POINTER lpException) { // // Делаем провеку lpException r\eip , что в принципе не обязательно // Формируем временные траблайн путем граббинга первых N байт от r\eip c возможностью возврата (Кто это сможет сделать ? ахахахах в //особенности под x64) TimedTrapline // // PulseThreadHaveTF(); WaitRoutineSpliceIsEnd(); goto TimedTrapline(); } И почти таким же путем делаем де спайс ... Теперь задачку усложним специально для onSide, как будем восстонавливать расщепленные страницы ?
K10 Да на по *** на него так как это будет в кеши , так что на просто по ... а атомарные операции сдесь на ** как не нужны.
63F45EF45RB65R6VR Хороший вопрос. Скажем так, хуже от lock xchg не станет, особенно, если использовать префикс не очень активно. Использовать или нет -- совсем другой и долгий разговор и у меня знаний на эту тему мало. Насколько я понимаю, в ситуации, когда процессор считывает инструкцию 'mov edi, edi' по невыравненному адресу, а другой процессор в этот момент эту инструкцию переписывает, может получиться так, что часть данных (один байт) будет записана, а другой байт еще нет. В итоге процессор, который читал инструкцию дл яисполнения, может считать некорректное значение и получится совсем не 'mov edi, edi'. С другой стороны, процессор имеет отдельный кэш для инструкций и при записи в него (если я правильно себе это представляю и помню) происходит инвалидация линейки кэша и, вероятно, конвейера. Ну это так, общие соображения, особо верить в них не надо. Если leo эту тему прочитает, вероятно, опишет более подробно, как там это все работает. Интереснее будет, если представить, что другой процессор тоже исполняет код установки хука и они "встретятся" при перезаписи nop'ов. (Перезапись 'mov edi, edi' не так страшна, т.к. перезаписывать будут, вероятно, одинаковым значением). Тут вообще, наверное, ничего не сделаешь кроме как позиционирования кода так, чтобы imm32 инструкции 'jmp' был правильно выравнен. Короче, я бы поставил . Хуже не будет.