splice ws2_32

Тема в разделе "WASM.WIN32", создана пользователем 63F45EF45RB65R6VR, 9 ноя 2011.

  1. 63F45EF45RB65R6VR

    63F45EF45RB65R6VR New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2011
    Сообщения:
    70
    Всем привет

    надо потокобезопасно просплайсить запущенный процесс план такой находим 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

    я наверное нагенерил много бреда я новичок поэтому не судите
    строго а лучше посоветуйте как сделать хорошо и красиво.
    спасибо.
     
  2. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    ЗАГРУКА
    Делаешь инжект, в 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 надо отлавить это состояние и сделать опять таки дикремент.
    И да придется проверять, а не находятся ли потоки на траплайнах?



    В целом вопросы сплайсинга это сложной вопрос, много материала что бы грамотно реализовать. Так что это на статью.
     
  3. deLight

    deLight New Member

    Публикаций:
    0
    Регистрация:
    26 май 2008
    Сообщения:
    879
    > SusPendAllThreads() //практика говорит это делать не обезательно
    атомарно же. cmpxchg8b
     
  4. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    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).
     
  5. onSide

    onSide New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2008
    Сообщения:
    476
    Сплайсингу сто лет уже, и статей уже куча. Единственная проблема из всего что тут написано это снятие хуков. Так сделайте в своем перехватичке проверку какого-то сигнала по которому просто возвращайте управление на оригинальную ф-цию. А трамплины пусть остаются)) И вообще вы уверены что вам их надо будет снимать?
     
  6. 63F45EF45RB65R6VR

    63F45EF45RB65R6VR New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2011
    Сообщения:
    70
    Код (Text):
    1. 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)
     
  7. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    >поток исполняет
    >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'.
     
  8. deLight

    deLight New Member

    Публикаций:
    0
    Регистрация:
    26 май 2008
    Сообщения:
    879
    Mika0x65
    Почему сразу не запатчить одним lock cmpxchg8b ?
     
  9. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    Mika0x65
    jmp short :)
     
  10. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    deLight
    '5 dup nop'/'mov edi, edi' -- вполне официальный механизм для hot-patch'а. cmpxchg8b переписывает восемь байт. Не факт, что ф-ия начинается с инструкции длиной 8 байт.
     
  11. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    l_inc
    Да, точно. Вечно путаю...
     
  12. 63F45EF45RB65R6VR

    63F45EF45RB65R6VR New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2011
    Сообщения:
    70
    хм честно говоря я не знал про этот механизм поэтому и придумал весь этот геморой а что за imm8 и почему именно push не могли бы рассказать ?
     
  13. l_inc

    l_inc New Member

    Публикаций:
    0
    Регистрация:
    29 сен 2005
    Сообщения:
    2.566
    63F45EF45RB65R6VR
    Вообще стандартный механизм подготовки к хотпатчингу — вставка mov edi,edi в начале функции. Но беглый просмотр ws2_32 показал, что некоторые даже подготовленные функции (т.е. имеющие предпролог в пять nop'ов) начинаются с push imm8. Это мало что меняет, т.к. инструкция занимает те же два байта, но её всё-таки важно исполнить/проэмулировать (в отличие от не имеющей эффекта mov edi,edi).
     
  14. 63F45EF45RB65R6VR

    63F45EF45RB65R6VR New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2011
    Сообщения:
    70
    а можно поподробнее про сигнал как это работать должно ?
    снимать хотелось бы например каждый перехватчик шлет центру через Mailslot "письма" с даннымы конкретно в spy.exe если не снимать и переключиться на другой процесс (заинжектить в другой процесс) то они все вместе забъют память ядра которая выделяетсяь под "сообщения" mailslot да и вообще хотелось бы DLL выгружать если выбран другой процесс и инжектить в новый процесс
     
  15. 63F45EF45RB65R6VR

    63F45EF45RB65R6VR New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2011
    Сообщения:
    70
    как я уже говорил для меня это было открытием в этом треде так что тот
    бред который я придумал от незнания спасибо за помощь и всем кто помог спасибо
     
  16. K10

    K10 New Member

    Публикаций:
    0
    Регистрация:
    3 окт 2008
    Сообщения:
    1.590
    deLight
    Если какой нибудь поток в это время исполняет этот участок?
     
  17. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    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, как будем восстонавливать расщепленные страницы ?
     
  18. shchetinin

    shchetinin Member

    Публикаций:
    0
    Регистрация:
    27 май 2011
    Сообщения:
    715
    K10
    Да на по *** на него так как это будет в кеши , так что на просто по ... а атомарные операции сдесь на ** как не нужны.
     
  19. 63F45EF45RB65R6VR

    63F45EF45RB65R6VR New Member

    Публикаций:
    0
    Регистрация:
    26 окт 2011
    Сообщения:
    70
    так надо или не надо lock xchg использовать или и без него потокобезопасно будет ?
     
  20. Mika0x65

    Mika0x65 New Member

    Публикаций:
    0
    Регистрация:
    30 июл 2005
    Сообщения:
    1.384
    63F45EF45RB65R6VR
    Хороший вопрос. Скажем так, хуже от lock xchg не станет, особенно, если использовать префикс не очень активно. Использовать или нет -- совсем другой и долгий разговор и у меня знаний на эту тему мало. Насколько я понимаю, в ситуации, когда процессор считывает инструкцию 'mov edi, edi' по невыравненному адресу, а другой процессор в этот момент эту инструкцию переписывает, может получиться так, что часть данных (один байт) будет записана, а другой байт еще нет. В итоге процессор, который читал инструкцию дл яисполнения, может считать некорректное значение и получится совсем не 'mov edi, edi'. С другой стороны, процессор имеет отдельный кэш для инструкций и при записи в него (если я правильно себе это представляю и помню) происходит инвалидация линейки кэша и, вероятно, конвейера. Ну это так, общие соображения, особо верить в них не надо. Если leo эту тему прочитает, вероятно, опишет более подробно, как там это все работает.

    Интереснее будет, если представить, что другой процессор тоже исполняет код установки хука и они "встретятся" при перезаписи nop'ов. (Перезапись 'mov edi, edi' не так страшна, т.к. перезаписывать будут, вероятно, одинаковым значением). Тут вообще, наверное, ничего не сделаешь кроме как позиционирования кода так, чтобы imm32 инструкции 'jmp' был правильно выравнен.

    Короче, я бы поставил :). Хуже не будет.