Привет всем, коснулся с одной задачкой. Требуется: После подгрузки моей длл, на место ImageBase подгрузить другую. Это делают многие крипторы. Как я это хотел сделать При вызове DllMain PROCESS_ATTACH, переношу свою длл с ImageBase на VirtualAlloc(PAGE_READWRITE_EXECUTE). Далше передаю управление. на некую функцию которая освобождает памать а потом снова создает и мапит нужную длл, попутно настраивая релоки,импорт,... Значит такие вопросы: 1) После этих махинаций требуется подправить структуру _LDR_PEB_ENTRY ? 2) Какие функи можно вызывать чтобы самому не писать ProcessRelocs,ProcessImport,ProcessExport,ProcessSections. Как понятно из названий идет настройка релоков,импорта,экспорта,секций.?
вообще говоря, если ты переносишь dll на другое место, то для корретной его работы тебе потребуется поправлять ему релоки... так как код ориентирован на старый imagebase... или же функции освобождения и ремаппинга нового образа на старый imagebase должны быть независимы от базы... да не нужно в принципе... в этих функциях нет ничего сложного, можешь сам написать... а вообще говоря, ты можешь вызвать какую-нить LdrLoadLibrary, но тогда она загрузится на другую базу...
на семерке не экспортируется походу, на висте может тоже. юзай самописную. Не легче изменить ImageBase без всяких переносов кода туда-сюда? Грузишь с пом. LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES), меняешь ImageBase в PEB, вызываешь EP.
onSide Это приватная функция(внутренняя не экспортируемая), что показано префиксом "p" после типа, так во всех функциях принято.
19841204 Смотрите. Имеется к примеру код: Код (Text): Pr1: [...] Call Pr2 [...] - Pr2: [...] Ref: Call Target [...] Вам нужен функционал процедуры Pr1 полностью, но с изменённым Target(). o Можно выполнить всё что делает Pr1() вручную. Но это большая не нужная работа, учитывая что она уже за нас проделана. o Можно распарсить код, найти линк на Target() и заменить его на свой. Это не желательно, так как патч. o Можно отморфить, но это сложно. o Можно выполнить часть кода до вызова Target(), после чего выполнить свой код и продолжить исполнение Pr2(). Вот последний способ реализуется посредством трассировки. По сути трассировочный калбэк будет обрабатывать часть графа, только динамически(иначе это делается при компиляции графа, где нужные места кода изменяются). Когда он встретит останов на Ref или Target() просто изменить Ip на свой код. Этимже способом иногда выполняется поиск различных внутренних переменных, которые не удаётся найти статически, а использование графа не приемлимо по разным причинам. Причём не обязательно трассировать полностью весь код. Может быть сгенерирован останов гдето в окрестности Ref(это системные ошибки, исключения изза инвалидных структур и пр.), после чего код трассируется. Также можно использовать откат функции, это когда например диспетчер исключений получает управление гдето вначале Target(), всё что она сделала критичное приводится в исходное состояние(например критические секции освобождаются), стек очищается и передаётся управление на свой код, при этом последний стековый фрейм содержит параметры функции. Это не обязательно вызов именно процедуры, можно изменять части кода, тоесть макро. Можно регистры и пр. Именно этот способ я использовал в надстройке для загрузчика.
medstrax1 Для начала её нужно както найти. Во вторых патч не приемлим. Вот пример на тойже LdrpWalkImportDescriptor(). Эта функция весьма глубоко лежит в загрузчике. Можно искать по ссылкам/релокам, но это не относится к вопросу про динамический поиск. Эта функция в начале своей работы ищет создаёт активации(манифесты обрабатывает), после этого выводит загрузочные логи, если ShowSnaps установлена в TRUE. Для примера этого достаточно. В первом случае регистрируем свой калбэк посредством LdrSetDllManifestProber(). Когда он вызывается можно вернуть управление из LdrpWalkImportDescriptor(), либо сгенерить исключение/останов, рассмотрим второй останов. Во втором способе загрузчик логгирует все важные действия посредством отладочного вывода сообщений. В обоих случаях наш диспетчер исключений(VEH) будет получать управление при этих событиях. В первом случае с определённым кодом останова, во втором случае мы фильтруем сообщения, извлекая из их потока чтото типа "LDR: %wZ has correct binding to %s", тем самым выполнив привязку сообщения к целевой функции. Когда останов сработает, либо будет получено нужное сообщение выполняем бактрейс, находим стековый фрейм относящийся к целевой функции, а из него непосредственно линк на LdrpWalkImportDescriptor(). После этого исключение не обрабатываем, а передаём далее. Его обработает загрузчик - выполнит откат, выгрузит всё что загрузил, очистит память, освободит кс и пр., после чего управление возвратит в вызывающий код и ошибку. Можно поступить есчо проще - потрассировать весь загрузчик, при встрече сигнатуры завершить трассировку. Это не желательный для применения способ именно в этом случае, но как пример хороший.
Есть вопрос по трассировке. Когда имеет смысл менять пошаговую трассировку на single-step on branches? И имеет ли вообще?
medstrax1 Не имеет смысла, более того это ядро должно выполнять, не имеет никакого отношения к данной теме.
medstrax1 Это не софтварная, а железячная трассировка. Сложная и не эффективная реализация получится. Этот отладочный механизм не для применения в софте, лишь в отладчиках.
Ок, я услышал, только мне непонятно в чем неэффективность. Трейсить пошагово гораздо более неэффективно, чем трейсить по бранчам (если конечно не надо ловить конкретный опкод)
medstrax1 Обычно нужно трассировать все инструкции, а не только ветвления. Для инициализации нужно использовать IPI. При системной трассировке в ядре(не ISR) места с повышенным IRQL и пр. критичные обходятся програмно. И с совместимостью проблемы, у разных камней свои нюансы. Вобще я не вижу смысла использовать аппаратную трассировку.
Единственно с чем безоговорочно соглашусь - зависит от камня. А жаль. Хотя судя по манам различных имплементаций штук 5-6. Это можно предусмотреть. Правда хз, что там у амд-шников. В их маны я вобще заглядывал пару раз всего