Я может плохо понимаю, но вроде плагин это dll? Что мешает ставить свои обработчики, те же try/except? Или имеется в_виду исключения, которые возникают не в плагине?
KeSqueer Возможно это так и есть, по построению похож на dll, имеет 3 функции входа - инициализации, терминации и запуска. Я в функции инициализации назначаю свои кнопки на тулбаре и далее по нажатию кнопки вызывается обрабатывающая функция. Эти обработчики меня не интересуют, т.к. довольно сложно локализовать места их размещения и они усложняют код. Я API функцией SetUnhandledExceptionFilter(lpFilter) устанавливаю обработчик исключений верхнего уровня, но всё дело в том, что этот обработчик lpFilter никогда не вызывается, вместо него всегда идет вызов внутреннего обработчика исключений IDA. Вот в этом и вопрос - что препятствует вызову моего обработчика? Нет, меня интересуют только ситуации в моем коде, которые и должен анализировать обработчик.
Функция SetUnhandledExceptionFilter используется для установки фильтра необработанных исключений, которые достаются ему (фильтру) от VEH и SEH обработчиков, которые отрабатали первыми и отказались обрабатывать исключение. Код (Text): #include <stdio.h> #include <windows.h> LONG WINAPI UefHandler( __in struct _EXCEPTION_POINTERS *ExceptionInfo ) { printf("%s\n", "UefHandler"); return EXCEPTION_CONTINUE_SEARCH; } LONG CALLBACK VehHandler( __in struct _EXCEPTION_POINTERS *ExceptionInfo ) { printf("%s\n", "VehHandler"); return EXCEPTION_CONTINUE_SEARCH; } LONG WINAPI LocalHandler() { printf("%s\n", "LocalHandler"); return EXCEPTION_CONTINUE_SEARCH; } int main() { __try { PVOID VehHandle; LPTOP_LEVEL_EXCEPTION_FILTER TopLevelFilter; TopLevelFilter = SetUnhandledExceptionFilter(UefHandler); VehHandle = AddVectoredExceptionHandler(TRUE, VehHandler); __asm { xor eax, eax mov [eax], eax } } __except(LocalHandler()) { } return 0; } Подробнее см. Рихтера, главы 24 и 25.
xorrax Спасибо, я это знал, просто хотелось как попроще. Посмотрел список обработчиков исключений, которые сидят на процессе в IDA (перед вызовом любой моей функции плагина) - их оказалось около 12 штук, понятно, что какое-то из них обрабатывает все исключения и дальнейшим ничего не остается. Сделал таким образом: каждую функцию, вызываемую из IDA обернул в блок __try...__except(Handler) - и всё заработало.
Близится к завершению работа над последней (на сегодняшний день) частью проекта - создание и запись компилируемых модулей (это чаще всего пара файлов cpp и h). Пример полученного модуля во вложении. По завершению этой части предварительная версия программы будет предложена на тестирование на определенных условиях. Приведу некоторую статистику по работе предыдущей части проекта - декомпилятор функций. Тест проводился на 3-х экзешниках, результаты: Кол-во функций Кол-во отложенных декомпиляций Кол-во функций с ошибками 8946 41 43 4672 15 32 2893 8 19 Как видно из статистики, кол-во (на сегодняшний день) недекомпилируемых функций не превышает 1% от общего числа функций, остальные функции были успешно декомпилированы. В принципе можно сделать так, чтобы все функции были успешно декомпилированы, но это вопрос только времени. Главное здесь то, что процесс декомпиляции при возникновении исключения или любой другой ошибки не прерывается, а сохраняет все логи по "проблемной" функции и переходит к декомпиляции другой функции.
s0larian Конечно так, иначе откуда бы взялись имена локальный переменных? Я уже говорил, что все сказанное ниже относится только к первому этапу разработки, см. пост №1, и только после его логического завершения будут реализовываться следующие этапы, а так как помощников пока не нашлось, то это процесс не быстрый. Согласен, здесь есть неточность, но поясню, в коде встречается два вида memsetов и прочих mem...: 1. Реализованы через цепочечные команды (префикс rep) - в исходники они в таком виде не попадут, а будут или уничтожены (за ненадобностью), как вспомогательные или преобразованы в операцию присвоения структур или другие. Написание в промежуточном тексте mem..., размер в двордах. 2. Вызовы библиотечных функций, в исходниках будут как есть, написание _mem... Всему свое время, повторяться больше не буду. Если читали внимательно, то ответ на этот вопрос был - пока только VC. Не только вам, но терпение ещё никому не вредило... А вас юзать никто и не заставляет, юзать будет программа и создавать исходники так близко к оригиналу, насколько это возможно и применены здесь будут все доступные методы, в том числе и дебаг инфа и сигнатуры библиотек и прочее... (опять повторяюсь) Вопрос: чем или как может быть сгенерен идентификационный код вида {051E3FA4-A09D-41C2-A61B-350662962990}
или генератором из вижуал студио Visual Studio 6 C:\Program Files\Microsoft Visual Studio\Common\Tools\GUIDGEN.exe Visual Studio 8 C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\guidgen.exe C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\Bin\GuidGen.exe
Вопрос: Как сделать отрисовку (redraw, refresh) главного окна IDA из плагина? Декомпиляция большой программы может продолжаться до часа времени, если переключиться во время работы плагина на другое окно винды, то вернуться к IDA окну мы не сможем до окончания работы плагина. Вызов SDK функций refresh_idaview и refresh_idaview_anyway из плагина результата не дает.
Да, "знатоков" IDA SDK не видно, тогда поделюсь своими соображениями, может кому-нибудь это пригодится. Осталось сделать последний кусочек программы перед тестированием - управление и визуализация процесса декомпиляции, который к самой декомпиляции никакого отношения не имеет. В принципе визуализация уже существует - вывод в стандартное окно сообщений IDA. От управления пока требуется совсем немного - при запуске функции декомпиляции блокировать контролы интерфейса IDA и иметь возможность принудительного завершения декомпиляции пользователем. Причем, управление и визуализация должны работать стандартным образом - при запуске или переключении на другую программу в Винде декомпиляция должна продолжаться, а при возврате обратно мы должны получить и визуализацию и управление. В общем, это банальные вещи, и в любой самостоятельной программе реализуются на раз ... два, но не в IDA. Запустить функцию декомпиляции из плагина IDA можно тремя способами: 1. Из стандартной функции run() плагина. 2. Из меню IDA, предварительно добавив обработчик add_menu_item(...). 3. Из отдельного процесса _beginthread(...), который запускается способом 1 или 2. Разницы в работе между 1 и 2 практически нет, за исключением одного момента, когда запускаем 3 из 2. А теперь результаты экспериментов с этими тремя способами. Запуск декомпиляции через 1 и 2: Работа - основное окно IDA со всеми контролами блокируется, работает визуализация в окно вывода. Если дождемся, ничего не делая, окончания работы декомпилятора, то IDA разблокируется - и всё ОК. Недостатки - переключившись на другое окно, обратного возврата не будет и принудительно завершить работу декомпилятора не можем, т.к. все контролы заблокированы. Запуск декомпиляции отдельным процессом, способ 3: Работа - на однопроцессорных системах все работает при условии блокировки контролов IDA хотя бы вызовом show_wait_box(...), который можно использовать и для завершения работы декомпилятора, визуализация при переключении процессов сохраняется. Но, если во время декомпиляции начать работать, например в Word, то получаем мертвый цикл в IDA во время вызова из процесса декомпиляции идашной функции визуализации сообщения msg(...). С многопроцессорными системами всё намного хуже, причем наблюдается зависимость от способа запуска процесса - через 1 или 2. При способе 2 обработчик после вызова процесса может завершить работу с кодом 0 или 1 (не документировано). Если завершаем работу с кодом 0, то способ 2 аналогичен способу 1, если завершаем с кодом 1, то получаем сразу исключение в IDA с завершением работы. Если же процесс запущен способом 1 (или 2 с кодом выхода 0), то получаем исключение в IDA при вызове из процесса декомпиляции практически любой функции IDA. Почему так, не знаю, возможно работает распараллеливание разных процессов между процессорами системы, а методов синхронизации процессов в IDA нет. Вот такие "пироги", решения пока так и не нашел...
Не могу понять, зачем потоки называть процессами?.. Поскольку IDA вызывает фукцию плагина из основного цикла обработки сообщений, то для такого способа, видимо, нужно регистрировать callback'и и самостоятельно париться с синхронизацией работы плагина и различных изменений базы, так как в IDA ничего этого нет (я про синхронизацию к базе). Проще, как ты сказал, залочить контролы через show_wait_box(). Что-то ты где-то намудрил или я недомудрил, у меня работает: Код (Text): show_wait_box("%s", "Please wait..."); int i = 0; while (1) { if (wasBreak()) { hide_wait_box(); break; } Sleep(500); msg("msg %d\n", i++); } Хотя тут идёт периодический вызоыв wasBreak(), в котором IDA и перерисовывает свой интерфейс. Но даже если Sleep+msg засунуть в цикл, то всё равно всё работает. Причем тут Word, хз... Я ничего не понял, или тут снова "процесс" = "поток"?
Хм, действительно, я недомудрил, нужно просто цикл подольше сделать Но решение тоже - вызов wasBreak() для обновления интерфейса.
nester7 За ответ спасибо, очень помогло и всё заработало. До wasBreak() я не допер, кто бы мог подумать, что эта штука обновляет интерфейс, использовал для анализа Ctrl-Break функцию autoWait(), т.к. через автоанализ отображаю индикатор состояния и адрес обрабатываемой функции в строке статуса. Привычка такая, вредная. Это лишнее, т.к. обработка функции идет приличное время, Sleep также убрал для повышения быстродействия. Всё таки спрошу, ради просвещения, как грамотного в этих вопросах человека, потоки и IDA совместимы? Я раньше встречал примеры плагинов с использованием потоков, которые работали на однопроцессорной платформе, а на мультипроцессорной у меня не работают.
К сожалению, до грамотного человека мне далеко Это говорит о том, что и на однопроцессорной машине плагин работает неправильно, просто сложнее поймать момент модификации данных, особенно, если они (модификации) "короткие", аля gVar = SomeValue. На многопроцессорной машине проблема встает в полный рост из-за одновременного выполнения потоков, что увеличивает шансы косяков. К сожалению, я понятия не имею как работает ида - в каких потоках и что выполняется, откуда поступают запросы на реанализ и прочее, но мысль у меня такая: 1. ида не предоставляет функций для синхронизации доступа к базе. 2. Вызов плагина идет в основном цикле обработки сообщений. 3. Внутри PLUGIN->run() можем не бояться (?) за синхронизацию. 4. Плагин, выполняясь в отдельном (долгом) потоке, может посылать сообщения, ака запуск плагина. 5. Имеем два потока, которые нужно синхронизировать: из run "увидеть", что мы были вызываны для синхронизации, и просигнализировать это долгому потоку, затем дождаться, когда он закончит свою работу чтобы выйти из текущего (программного) вызова плагина. 6. Если пользователь модифицирует базу, ставить callback'и на это дело и совать сообщения в очередь для повторного анализа этого места нашему долгому потоку. Видимо да, но без непростых телодвижений, видимо, не обойтись. Хотя, хз - не интересовался, было бы лучше, если бы я ошибался
nester7 Согласен, при запуске декомпиляции отдельным потоком на однопроцессорной машине всё в общем работало, но при декомпиляции некоторых функций появлялись непонятные ошибки, которых не было при запуске через run() или меню. Думаю, что да, как и при запуске через меню. Интересен не этот момент, когда run(), т.е. плагин создает новый поток и производится сихронизация между ним и новым потоком (это в принципе не сложно реализовать), а другой - когда run() создает новый поток и завершает свою работу, а поток продолжает работу с ида через ui интерфейс и другие, как в этом случае обеспечить синхронизацию его с ида? Это было бы здорово при реализации интерактивности в плагине, например, идет процесс декомпиляции всей программы, а в это время мы можем работать в GUI и исправлять найденные декомпилятором ошибки или неточности. Аналогично автоанализу в Ида, когда пользователь и автоанализ работают одновременно.
Я как раз и имею ввиду, что "долгий" поток вызывает как бы сам себя, всего лишь для синхронизации доступа к базе. Нечто, вроде PostThreadMessage главному окну (потоку) иды. И когда она будет обрабатывать это сообщение, то и войдет в наш PLUGIN->run. В этом месте будет два потока: 1 - от иды, её основной поток, в котором мы через различные меню модифицируем базу. 2 - наш, который ждет сигнализации от PLUGIN->run (который должен "сказать" (просигнализировать) ждущему (второму) "работай, база залочена".
Я говорю про момент, когда плагин уже работает, он уже побывал в один раз в run и создал поток, который "продолжает работу с ида через ui интерфейс и другие". Возможно тут нужны корректировки, что именно он делает/вызывает, но основная мысль такая - поток, запущеный из плагина, должен дождаться, когда ида (её основной поток), снова войдет в какой-нибудь плагин, в его функцию run(). Ну, например, в наш же Код (Text): void run( int arg ) { static HANDLE hWorker; static HANDLE hEvent; if (!hWorker) { hWorker = CreateThread(...); hEvent = CreateEvent(...); } else { SetEvent(hEvent); } }
nester7 Это, конечно, интересно, надо будет как-нибудь попробовать, только каким образом заставить ида постоянно выполнять это . А может можно вместо второго и следующих заходов в плагин использовать для синхронизации IdleFunc активированную через callui(ui_setidle, IdleFunc);, при условии, что эта функция будет вызываться идой и после окнчания работы run().
См. #99 Хоткей на запуск плагина или доступ через меню Edit-> Plugins -> PluginName есть, по сути, посылка сообщения окну иды, в обработчике которого и идет вызов PLUGIN->run. Это же можно сделать программно.