Как определить момент, когда статические dll загружены процессом?

Тема в разделе "WASM.WIN32", создана пользователем ss, 13 авг 2010.

  1. ss

    ss New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2010
    Сообщения:
    5
    Адрес:
    Россия
    Есть программа, которая запускает процесс при помощи CreateProcess, и выполняет инжект dll в этот процесс, используя SetContext. Однако возникает проблема, когда процесс быстро завершается естетственным образом в силу заложенного в него алгоритма. Если проводить инжект на начальных сталиях загрузки процесса (например, в процессе инициализации статических dll), то все работает очень нестабильно. А как поймать момент, когда dll инициализированны и программа перешла на entry point и далее?
    Можно конечно через IAT определить какие dll будет загружать процесс и в цикле определять модули, загруженные процессом в текущий момент, пока не будут загружены все... но имхо это извращение)
    Нет ли какого простого и эффективного способа (при этом желательно действовать пассивно, т.е. не влиять напрямую на процесс)?

    Рассматривается только usermode...
    ОС: WinXP SP3
     
  2. ziral2088

    ziral2088 New Member

    Публикаций:
    0
    Регистрация:
    16 авг 2009
    Сообщения:
    283
    Создайте процесс остановленным - потом инжект - потом Resume.
     
  3. ss

    ss New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2010
    Сообщения:
    5
    Адрес:
    Россия
    Инжект осуществляется следующим образом:
    1) Suspend
    2) ищется страница доступная для записи (ну или выделяется новая, либо изменяется защита страницы - не суть)
    3) туда записывается путь к dll (NtWriteVirtualMemory) и другие необходимые данные
    4) запрашивается адрес стека (GetContext) и туда записываются параметры вызова LdrLoadDll
    5) через SetContext управление передается LdrLoadDll
    6) Resume

    Думаю, такого рода инжект не прокатит, если его проводить с остановленным сразу после создания процессом, хотя бы потому, что процесс еще не подгрузил ntdll.dll и вызов LdrLoadDll не получится! Да и после загрузки ntdll.dll (но еще во время инициализации других библиотек) код работает очень нестабильно (хотя изредка прокатывает). Чаще всего процесс завершается с ошибкой инициализации и даже без подгрузки требуемой dll. Вообще погуглив я понял, что процесс инициализации статических dll очень чувствительный и малейшая ошибка на этом этапе приводит к немедленному аварийному завершению процесса, поэтому я и хотел начать инжект на более поздней стадии...
     
  4. ziral2088

    ziral2088 New Member

    Публикаций:
    0
    Регистрация:
    16 авг 2009
    Сообщения:
    283
    Вы это проверяли? Что то мне кажется, что нет...
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    ss
    Загрузчик использует кс LdrpLoaderLock. Если она не освобождена, то какойто поток находится в загрузчике, это просто к сведению.
    Выполнить бактрейс, последний фрейм будет частью структуры:
    Код (Text):
    1. APC_FRAME struct
    2. Frame           STACK_FRAME <>  ; Ip ~ KiUserApcDispatcher(), !Next
    3. pContext        PCONTEXT ?  ; OPTIONAL
    4. NtBase          PVOID ?     ; ntdll
    5. Reserved1       DWORD ?
    6. Context         CONTEXT <>
    7. APC_FRAME ends
    Контекст содержит необходимые линки на стартап код(Eip/Eax).
     
  6. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Только сейчас вспомнил про семп(по сути задача изменения OEP из DllInitRoutine() таже что и ваша):
    http://files.virustech.org/indy/Temp/Th/
    [​IMG]
    зы: иногда полезно читать свои посты и кодесы %.
     
  7. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    ss

    ntdll.dll проецируется ядром. Если процесс есть, в нем всегда есть ntdll.
    А вот другие длл уже проецируются при инициализации. Так что за это не переживай.
     
  8. ss

    ss New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2010
    Сообщения:
    5
    Адрес:
    Россия
    Clerk, спасибо! В итоге удалось реализовать предложенный тобой метод с бектрейсом. В цикле с периодом считывается контекст запущенного процесса, бектрейсятся стековые кадры и определяется EP, которая сравнивается со взятой из exe-файла. Если EP совпадают, значит процесс уже инициализировал библиотеки и прошел EP. Конечно, иногда цикл все равно проскакивает нужный момент, но это уже вопрос выбора периода считывания контекста, а так в целом метод работает.
    Вот только странно... у меня получилось, что EP это не что иное как pContext из структуры APC_FRAME. Разве так должно быть?
    и еще вопрос про LdrpLoaderLock... Можно ли как-то определить залочена ли секция в запущенном процессе из другого процесса?


    Да, проверил) Но все равно, если попробовать сменить контекст на этом этапе, то процесс просто тихо умирает без инжекта dll. Хотя возможно я что-то делаю не так, но тот же код прекрасно работает с уже запущенными процессами...
     
  9. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    ss
    Нет. Загрузчик вызывается как APC(LdrInitializeThunk()), после чего выполняется загрузка контекста в процессор и переход на EP, либо стаб(BaseProcessStart() etc.). Пока поток не выполнил возврат из APC последний фрейм это структура APC_FRAME, иначе этой структуры уже нет. Например для BaseProcessStart() в фрейме по [Ebp + 8] лежит линк на EP, это просто параметр этой функции. APC_FRAME может использоваться если поток находится в загрузчике(LdrpLoaderLock не освобождена).
    Да. Это структура:
    Код (Text):
    1. typedef struct _RTL_CRITICAL_SECTION {
    2.     PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    3.     LONG LockCount;
    4.     LONG RecursionCount;
    5.     HANDLE OwningThread;
    6.     HANDLE LockSemaphore;
    7.     ULONG_PTR SpinCount;
    8. } RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
    Если кс свободна, то LockCount = -1, !OwningThread. Ссылка на эту кс находится в PEB.LoaderLock(+0xA0).
     
  10. ss

    ss New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2010
    Сообщения:
    5
    Адрес:
    Россия
    Ага, понятно - видимо это я и нашел...
    Также попробовал реализовать этот способ... Все работает и даже похоже он значительно попроще)
    И все же у этих двух способов есть один минус - нет гарантии, что момент после инициализации библиотек будет пойман, если процесс после перехода на EP очень быстро завершится.

    Поэтому развивая тему, прочитал http://wasm.ru/forum/viewtopic.php?id=38310.
    И хотелось бы окончательно выяснить с созданием процесса с флагом CREATE_SUSPENDED. Я так понял, что на этом этапе вход в LdrInitializeThunk() еще не произошел? и менять контекст потока, способом описанным в моем втором сообщении, с инжектом dll не получится? Как же тогда гарантированно поймать момент после загрузки статических dll? Может быть ответ в этом?
     
  11. fsd

    fsd New Member

    Публикаций:
    0
    Регистрация:
    4 июл 2010
    Сообщения:
    353
    можно попробовать CreateProcess(DEBUG_ONLY_THIS_PROCESS) + DebugEvent.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT
     
  12. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    ss
    Это ведь асинхронно всё.
    Получится изменить контекст разумеется, но только для пользовательской APC, а не ядерной(стартап APC ядерная).
    fsd
    Отладчик получает уведамление при проецировании секции, тоесть как только проекция будет отображена ядром. Далее возникнет останов в DbgBreakPoint(), до нотификации модулей.
     
  13. ss

    ss New Member

    Публикаций:
    0
    Регистрация:
    13 авг 2010
    Сообщения:
    5
    Адрес:
    Россия
    В итоге реализовал следующий способ инжекта:
    1) создается процесс с флагом CREATE_SUSPENDED
    2) в памяти процесса ищется страница, доступная для записи и туда записывается путь к инжектируемой dll
    3) посылается QueueUserAPC с указателем на LoadLibraryA и адресом, записанного на предыдущем шаге пути к dll
    4) ResumeThread
    После загрузки статических dll и до перехода на entry point ГАРАНТИРОВАННО вызывается APC и подгружается инжектируемая библиотека, что собственно и требовалось! В отличие от двух других способов, которые обсуждались в топике, этот прекрасно работает в том числе с процессами, которые в силу заложенного в них алгоритма очень быстро завершаются.
    Всем спасибо, а особенно Clerk! Благодаря твоим постам узнал много нового)
    Думаю тему можно закрывать...