[C++] Инжектирование функции в чужой процесс.

Тема в разделе "LANGS.C", создана пользователем MuForum, 13 май 2009.

  1. MuForum

    MuForum Member

    Публикаций:
    0
    Регистрация:
    11 мар 2007
    Сообщения:
    109
    Доброе время суток.

    # Задача: Реализовать инжектирование функции обработки таблицы импорта в чужой процесс.
    -- Мне необходимо обработать таблицу импорта в чужом процессе.

    # План действий:
    1. Узнаём размер функции.
    -- Как узнать размер функции?
    2. Осуществляем вызов VirtualAllocEx() в другом процессе.
    -- Освобождаем определённый объём памяти под функцию в чужом процессе.
    3. Записываем туда нашу функцию.
    -- При помощи какой функции это реализовать? WriteProcessMemory() ?
    4. Передаём управление функции.
    -- Передаю управление на точку входу, но указываю адрес начала функции, чтобы функция обработала.


    P.S. -> Прошу помочь с этой задачей.
    -- Советы, подсказки, ссылками, кодом.
     
  2. ohne

    ohne New Member

    Публикаций:
    0
    Регистрация:
    28 фев 2009
    Сообщения:
    431
    что значит обработать?
    пиши все функции что нужны в обычную длл, и сделай экспортируемыми нужные функции,
    потом просто сохрани нуные адреса и скопируй секцию кода.
     
  3. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    Лучше использовать dll, читай Рихтора.

    1. Поиск по форуму, было. (но лучше юзать dll)
    2 В чем проблемма, про первый параметр функции почитай.
    3. Да использовать WriteProcessMemory
    4. CreateRemoteThread (опять читаем Рихтора)
     
  4. MuForum

    MuForum Member

    Публикаций:
    0
    Регистрация:
    11 мар 2007
    Сообщения:
    109
    Дядю рихтера я читал.
    -- Там пример на загрузке файлов с жесткого диска.

    1. Проблема в том, что мне необходимо реализовать это именно с процесса программы.
    -- То есть, с одного процесса инжекнуть код в другой процесс. (Без использования .dll)
    2. С этой функцией проблем нету. (Описание есть на MSDN)
    3. Ок.
    4. Антивирусы падла ругаются =(
     
  5. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    он и будет ругаться
     
  6. MuForum

    MuForum Member

    Публикаций:
    0
    Регистрация:
    11 мар 2007
    Сообщения:
    109
    Альтернативы какие-то есть?
     
  7. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    Если и есть тебе никто за спасибо не даст.
     
  8. MuForum

    MuForum Member

    Публикаций:
    0
    Регистрация:
    11 мар 2007
    Сообщения:
    109
    Главное что бы альтернатива была, а там можно и договорится ;)
     
  9. ohne

    ohne New Member

    Публикаций:
    0
    Регистрация:
    28 фев 2009
    Сообщения:
    431
    MuForum
    Если целевой софт есть возможность пропатчить, то проще добавить в ее импорт свою длл.
     
  10. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    MuForum
    Это будет не так дешево как ты думаешь...
     
  11. AndreyMust19

    AndreyMust19 New Member

    Публикаций:
    0
    Регистрация:
    20 окт 2008
    Сообщения:
    714
    Хм...

    Насчет внедрения функции. Ты используешь Visual C++, так ведь?
    Для Си / Си++. Во-первых, компилируй Release-версию. В ней &func() совпадает с адресом функции в секции кода. Открываешь hex-редактор и спускаешься вниз по этому адресу - первый ret будет концом функции. Затем желательно скопировать эту функцию в отдельный файл или перевести в Си-массив (BYTE arr_func[] = {0x80, 0x64, 0x90, 0x86 ...}; ).
    Для асма. Если напишешь функцию на fasm'е, то будет все проще (машинный код функции будет совпадать с кодом fasm'а).

    Компилировать прогу надо так, чтобы код был перемещаемым - с этим нет проблем, если ты используешь Release-версию. Проблемы вот в чем - внедряемая ф-я не должна использовать никаких API-функций, иметь только локальные переменные (хранить их только на стеке). Впрочем... функция собирается обрабатывать таблицу импорта - тогда можешь в таблице импорта:
    - найти адрес функции GetProcAddress и использовать ее для вызова нужных тебе kernel32 или ntdll-функций
    - или находить адреса самих этих функций (но в секции импорта их может не быть)
    И еще. После завершения функции тебе как-то надо будет вернуть результат... подумай над этим.

    Как альтернативные варианты передачи управления внедренной функции, можно изменять контекст одного из потоков. Например:
    1) Заморозить все потоки, сохранить регистры, направить один из потоков на внедренную функцию и после ret'а восстановить регистры и вернуть управление назад. Можно заморозить только один поток, но это более опасно.
    2) Присоединиться к процессу в виде отладчика с помощью AttachProcess. И также манипулировать одним из потоков.
    3) Найти в процессе начало какой-нибудь функции и внедрить туда call на свою функцию, а к концу функции дописать затертые команды. Опасно.
    4) Установить перехват одной из API-функций (н-р, DispatchMessage). Подозрительно и ненадежно.
     
  12. AndreyMust19

    AndreyMust19 New Member

    Публикаций:
    0
    Регистрация:
    20 окт 2008
    Сообщения:
    714
    Сорри, не AttachProcess, а DebugActiveProcess.
     
  13. MuForum

    MuForum Member

    Публикаций:
    0
    Регистрация:
    11 мар 2007
    Сообщения:
    109
    AndreyMust19 - Благодарю за развёрнутый ответ.
    - Буду думать.
     
  14. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    NtUnmapViewOfSection/NtMapViewOfSection кстати таблица импорта в проекции файловой секции, вот и решение. И никакой контекст и потоки трогать не нужно. Каспер молчит, вот только легальным способом хэндл процесса с нужными правами достать не просто..
     
  15. AndreyMust19

    AndreyMust19 New Member

    Публикаций:
    0
    Регистрация:
    20 окт 2008
    Сообщения:
    714
    Clerk
    Думаю, Clerk прав. MuForum, а правда, зачем тебе внедрять код в чужой процесс? Нельзя ли прочитать таблицу импорта из адресного пространства процесса или из самого исполняемого файла?
    Вот если тебе надо обработать таблицы импорта всех модулей, загруженных в процесс, тогда да.
     
  16. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    AndreyMust19
    Ты не понял, это инжект - уже баян, зачем изменять контексты, писать в память и пр., если можно скопировать данные с прокции, создать свою в которой помещаем указатель или код наш и проецируем на место оригинальной прокции, это для модулей. Работает проверено.
     
  17. AndreyMust19

    AndreyMust19 New Member

    Публикаций:
    0
    Регистрация:
    20 окт 2008
    Сообщения:
    714
    А, понял. Это не CreateFileMapping, да? А это не ядерное случайно?
     
  18. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    Clerk
    Хм, спасибо за способ, я бы даже статейку почитал. А так интересный, как полчучить хендел с правами конечно не понятно, а так вроде интересный способик.
     
  19. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    блин, я чет не уловил суть... расскажи пожалуйста поподробнее, интересно почитать... и кстати, в юзермоде сработает?
     
  20. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Сервисы для работы с приватной памятью мониторятся проактивкой, но ведь можно выделить память спроецировав секцию. Освободить проекцию секции тоже можно. А писать в проекцию не нужно - это ведь разделяемая между процессами память, изменение в проеции секции в одном процессе тутже отображается в проекции этой секции в другом процессе. Какбы окно в памяти между процессами, это для не файловых секций.
    Например загруженный модуль - проекция файловой секции, где для каждой секции файла(не путать термины) свои атрибуты доступа, как например для файловой секции кода RE, или для секции данных RW. Освободить память под одну файловую секцию нельзя, можно только всю проекцию освободить. Выполняем NtUnmapViewOfSection передав адрес выбранного модуля - вся проекция освобождается. Так как необходимо сохранить атрибуты для файловых секций в этой проекции, то необходимо проецировать нашу не файловую секцию размером равным размеру исходной проекции частями на места, где находились файловые секции. Тоесть проецируются части нашей секции, со смещением в секции и размером равным RVA и размеру файловых секций выравненых в памяти. Для этого нужно создать таблицу секций. Если заменяется проекция системных модулей, то читать память освобождаемого модуля не нужно, ибо в процессе из которого выполняется инжект в памяти проекции модулей(например для kernel32.dll) одинаковы для секций кода.
    Тоесть механизм следующий.
    - Выбираем модуль, проецию которого будем заменять. Это Kernel32 или ntdll. Пусть будет ntdll, тогда в нативные процессы можно инжектится.
    - Суспендим все потоки в процессе посредством NtSuspendProcess.
    - Создаём локальную(без имени), не файловую секцию размером равным размеру проекции модуля ntdll.
    - Проецируем эту секцию в текущий процесс.
    - Для восстановления состояния модуля после инжекта необходимо восстановить все данные в нём. Для этого перечисляем файловые секции модуля ntdll в текущем процессе и читаем память по адресу каждой файловой секции из процесса куда внедряемся, размером равным размеру каждой файловой секции в нашу проекцию по смещению равному RVA каждой файловой секции. Этим мы сохраним полное состояние модуля.
    - Записываем наш код в свободное место проекции нашей секции(может быть увеличено, либо в гиперпространстве секция, то что остаётся изза выравнивания в памяти).
    - Выполняем перехват кода в проекции секции в нашем процессе. Это захват кода, который будет вызван при вызове любого сервиса. Например стуб KiFastSystemCall или заглушку для возврата KiFastSystemCallRet, записывая инструкцию передающую управление на наш код.
    - Освобождаем прокцию модуля ntdll в процессе куда внедряемся.
    - Перечисляем все файловые секции для модуля ntdll, определяем их адрес(RVA + ImageBase) и размер каждой, проецируем часть нашей секции по адресу этому адресу и с этим размером. Атрибуты доступа установив соответственно. После чего не нужно будет выполнять NtProtectVirtualMemory.
    - Для секции в которой находится захваченный код атрибуды RW, либо в идеале следует выполнить захват какого либо кода не в секции кода, а в секции данных.
    - Выполняем NtResumeProcess.
    - Если есть хоть один рабочий тред сразу управление получит наш обработчик. Атомарно снимаем перехват в нём, так все рабочие потоки перейдут на исполнение нашего обработчика.
    Это только базовый принцип, разумеется можно придумать множество модификаций этого способа. Например для некоторых процессов есть другие проекции секций помимо файловых подходящие для этого инжекта. Также следует срузу сказать что PEB, TEB и UsSharedData не получится освободить. Для обмена данными с инжектяжим процессом никаких лишних действий предпринимать не нужно, ибо память в разделяемая между обоими проекциями в двух процессах.