Расширение функциональности не имея исходного кода

    Думаю, у каждого из вас было ощущение, что в той, или иной программе не хватает какой-нибудь must have фичи. Если программа идет с исходным кодом, то проблем не возникает. Любой желающий может дописать нужную функциональность. А что если программа закрытая? Не стоит отчаиваться, это не пропащий случай. Сейчас расскажу, как можно дописать за автора то, чего не хватает.

    Отступление

    Моей жертвой вновь стал отладчик OllyDbg. После выпуска версии 2.00 он мне стал нравится все больше, появилось много интересных вещей. Но автор категорически отказывался интегрировать в него систему плагинов, а это можно сказать главное, что делало отладчик настоящим рабочим инструментом.
    И вот в один момент, я для себя понял, что пора самому добавлять эту возможность.

    Приступим

    Первым вопросом встает — как добавить свой код?
    Основных способов два:
    • Добавить новую секцию, и поместить весь свой код в ней.
    • Вынести код в dll, и потом в свободном месте программы поместить небольшой код вызова.

    Есть и другие более изощренные варианты, но я их рассматривать не буду.

    Я выбрал второй вариант, потому что он удобней в плане программирования, и не нужно каждый раз копировать свой код в exe-шник. Для подгрузки своей dll можно поменять точку входа на свой код, или же загрузить чуть позже. Но лучше грузиться в самом начале, тогда у нас будет доступ ко всем API, до того как ими воспользуется родительский процесс.
    Патч мне делать не пришлось, нашел уже патченную, поменял только имя модуля загружаемого.
    Было:
    image
    Стало:
    image
    И наш код. Хитрость первого call'а в том, что он одной командой и помещает в стек адрес строки, и перепрыгивает ее.
    image
    Jmp по адресу 00401000 это OEP от Borland C

    Схема подгрузки плагинов банальная:
    Читаем из ini путь до папки с плагинами, если ее нет, то берем путь по-умолчанию и проверяем через GetFileAttributes наличие папки. Ну а дальше по маске *.dll, при помощи FindFirstFile/FindNextFile, перечисляем все файлы.
    Загружаем каждый через LoadLibrary. А плагин в свою очередь будет дергать экспортируемые нами функции. Так как в 1.10 версии все функции экспортировались из exe, то нам нужно сделать у себя такую же таблицу экспорта, с такими же ординалами, чтоб для плагинов тоже все было прозрачно. Но об этом позднее.

    Так как почти все плагины управлялись из меню, нужно было как-то создать и обрабатывать свою ветку в главном меню. Для обработки сообщений нам нужно достать адрес оконной функции. А где его взять? Простой способ — перехватить RegisterClass и вытащить из поля структуры WNDCLASS->lpfnWndProc адрес на нее. Ну и конечно же на его место записать свой.
    Второй вариант — найти в коде, и сделать jmp на себя. Я конечно люблю извращенные методы, но не в этот раз.

    Любой перехват функций я буду делать напрямую в таблице импорта, подставляя вместо адреса API, свой на процедуру-обработчик, а из нее уже в API. При данном методе для самого отладчика все будет прозрачно, и он будет думать, что работает напрямую с системой.
    Не забудьте перед любой правкой чужой памяти выставлять права на запись в эту область, иначе будут сыпаться ошибки доступа.
    Так выглядит вызов API до правки:

    И через несколько мгновений позже:

    Даже повторный ре-анализ не помогает, оля так и думает что там вызов RegisterClassW, однако в обеих областях ниже можно убедиться, что по адресу 005EEA44 лежит далеко не адрес API.

    Итак, «где» обрабатывать сообщения мы знаем, теперь бы нужно создать, то что обрабатывать. Для этого тем же способом перехватываем AppendMenu. И в обработчике ожидаем добавление меню, перед которым хотим воткнуть свое меню.
    Как только появилось — нам простирается большой выбор по созданию меню. Но я ограничился пока вариантами MF_STRING для вывода одиночного пункта, MF_POPUP для выпадающих вложенных меню, ну и MF_SEPARATOR для разделителей. Все пункты меню добавлял через немного устаревший InsertMenu. Во-первых так меньше кода, во-вторых Олег (разработчик OllyDbg) сам использует данный способ.
    Не забудем в самом конце вызвать оригинальную API AppendMenu для себя и для последующей за нами ветки меню.
    Все добавление будет выглядеть обобщенно следующим образом:
    Запрос от OllyDbg на AppendMenu(Help) -> наш CreateMenu() -> Несколько наших InsertMenu() -> наше AppendMenu(Plugins) -> выполняем запрос AppendMenu(Help).

    Создавая меню, нужно заранее посмотреть, какие ID свободные, и которые не будут пересекаться. В WndProc для меню мы будем ловить WM_COMMAND. Отсеиваем по диапазону только наши и например используя ассоциативный массив по ID=>адрес_обработчика передаем управление нужному плагину.

    Вот так выглядят окна OllyDbg 2.00c версии до моего вмешательства и после:


    Теперь самый краеугольный вопрос — нужно найти адреса функций, которые должны экспортироваться плагинам. Не надейтесь найти в релизных версиях приложений debug-информацию, которая могла бы значительно помочь.
    Поэтому пойдем следующим путем, грузим в Ida Pro:
    • Ищем текстовые строки, которые выводятся при ошибках, они могут нам подсказать назначение функций.
    • После того как все возможное выжали из строк, есть смысл найти обработчики различных меню, они так же значительно сужают круг поисков.
    • И наконец неприятный и требующий значительных временных затрат способ. Загружаем программу в отладчик, и ставим бряк на низкоуровневых специфических (или не очень) API. И постепенно поднимаемся на верхние уровни абстракции, параллельно поглядывая в Ida. И так, пока не выйдем на функции, когда можно четко обозначить что в целом она делает. Именуем для удобства функции в Ida, а себе отдельно сохраняем найденные адреса.


    Весь мини менеджер плагинов я написал за день на коленке на Fasm'е. Потом еще за пару недель расковырял порядка 30 функций. Но на тот момент он не был совместим с PDK 1.10, что не мешало мне написать свои для скрытия отладчика, загрузки символов из map-файлов, генерируемых IdaPro, и простенького дампера.

    Ложка дегтя

    Вероятно Олег обратил внимание на мою затею, и все-таки сделал собственный PDK в 2.01 alpha 4, что должно было разом убить мой проект. Но есть пару моментов. Новый билд стал IMHO перегруженным и неудобным, да и старые плагины не поддерживаются.

    На данный момент плагин-менеджер полностью с нуля переписывается на C++, с планами на совместимость со всеми версиями. И перехватом нескольких функций тут уже не отделаться...

    Ну а вот тут можно посмотреть, как НЕ надо писать код, даже на коленке за один день.
    Косяки и так знаю, потому и переписывается с нуля.
    Сорец на fasm
    Обновленный вариант будет позже, когда будет допилен.

    Отдельная благодарность автору OllyDbg и участникам форума exel@b за активную поддержку, и хабровчанам за поправки.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 31

      +28
      Тяжёлая жизнь, да. На фоне этого apt-get source выглядит как откровенный чит и профанация.
        +9
        В прикручивании табов к линуксоскайпу мне этот apt-get source (кстати, apt-src на порядок удобнее) ооочччень сильно помог, да.
          0
          А в кде, например, (не говоря про более специфические оконные менеджеры) можно любые окна в табы объединять

          А вообще, поддерживать такие модификации будет непросто.
            +1
            Ну не подходит KWin'овский механизм по ряду причин, я уже писал об этом же.
            0
            А где можно посмостреть на этот скайп с табами? :)
        +2
        Скил конечно хороший, но костылём это было и останется. Не живуче.
          +1
          Ну отчего же «костылем». Нормальная практика перехвата вызовов API. Даже красивая в чем-то.
          К слову, такие решения порой работают лучше и эффективнее, чем официальные «правильные» интерфейсы от разработчиков.
            0
            sOlid, OCP говорит что так нельзя.
            все таки это костыль. так как мы модифицируем логику в попытке расширить.
              +6
              Жизненные реалии говорят, что если нельзя, но очень хочется — то можно.
                0
                Уровень абстракции не тот, чтобы строго этого принципа придерживаться. К тому же все мэтры признают, что строгое следование всем этим принципам и законам невозможно, всегда приходится искать компромисс.
                А вообще, описанный способ сродни декоратору, поэтому я и назвал его красивым. И нет в нем никакой модификации логики, а лишь ее дополнение.
              +1
              Вы не видели, сколько программ похожим образом модифицируются под Mac OS… TotalFinder, плагины к Mail.app/Safari, MobileEnchancer & K, SIMBL…

              (там, правда, Obj-C, где рантайм на ходу позволяет подменять методы классов)
              +26
              Вот могут же люди, а… снимаю шляпу.
                +2
                Спасибо вам за полезную статью, я думал что максимум, что можно сделать, это Restoratorom руссифицировать. Буду знать
                  –13
                  руссифицировать

                  руссефецировать
                    –1
                    Возможно, вы имели в виду: русифицировать
                      0
                      Нет, не имел. Просто минусующие иронию не понимаю, если рядом с каментом нету смайлика или поясняющего комментария.
                        –1
                        *не понимаю -> не понимают
                          0
                          Похоже, либо я, либо вы не знаете, что такое ирония.
                            –1
                            Именно об этом я сказал в своём предыдущем комментарии.
                      0
                      cracklab.ru — профильный ресурс по теме
                      +3
                      Ну, все закономерно ) Джаверы берут декомпилятор и либо переопределяют часть методов, либо заменяют класс целиком, втаскивая его себе в jar. Дотнетчики пересобирают сборки в IL, добавляя модификаторы virtual к методам и правя код прямо в IL, ну а тем, кто довольствуется компилируемыми языками программирования — приходится работать по-старинке в дизассемблере и hex-редакторе.
                        0
                        Джаверы, пожалуйста, возьмите декомпилятор и добавьте поддержку прокси (или хотя бы изменение адреса сервера) в Opera Mini. Некий полосатый оператор ловко придумал «ошибку тарификации», которой и пользуется уже не первый месяц: habrahabr.ru/blogs/telecom/126536/ (в свою, разумеется, пользу). Предполагаю, что смена IP сервера, к которому обращается Opera Mini, позволит её деидентифицировать.
                        0
                        Хм, только в большинстве случаев такие модификации будут нарушением лицензии, нет?
                          0
                          Это такой edge case, что в реальности никто не сможет доказательно поставить точку в вашем вопросе.
                            0
                            Несомненно такой способ будет нарушением, но есть и другие — написать загрузчик, использовать ключи реестра для запуска нас как отладчик для этого приложения, или вообще драйвер для перехвата функций. Тогда оригинальный ехе останется нетронутым. А доказать факт анализа пост-фактум теоретически невозможно. Может я все адреса от балды взял и просто так совпало.
                              0
                              тем более что по нашим законам вроде как «адаптация» программы разрешена…
                            0
                            О, мы какие-то такие штуки пробовали делать на лабах в универе :)

                            Я всегда думала, что это может использоваться только как зарядка для ума… Ан-нет, и в жизни применяют, оказывается! :)
                              0
                              Не верится, что все-таки вышла финальная Олька 2.0, думал так и останется ветка 1.х.
                              Печально, что от плагинов отказались, ведь в 1.10 они очень сильно помогали в распаковке и анализе пакеров/крипторов.
                              Вам большой респект за исследования, побольше бы статей по reverse engineering!
                                0
                                Финальный релиз второй ольки был 4 июня 2010 года. А оффициальная поддержка пдагинов во второй ольке появилась 3 августа этого года.
                                0
                                Хорошая статья.
                                Есть следующий этап Дао — добавление своих функций эмбеддед системы)
                                Например, так изменить прошивку SD-карточки, чтобы она стала поддерживать формирование скрытой запароленной зоны и т.п.
                                Там возникают специфические трудности, в частности, необходимость аппаратного копания до прошивки (выпайка/впайка микросхем памяти и т.п.), определение центрального процессора и его системы команд, и, главное, что меня всегда расстраивало — в отличие от exe, никакой дополнительной инфы о секциях, сегментах, точке входа и т.п. в бинарнике прошивки нет. Просто кирпич кода, часто еще и заксоренный ключом и разбитый на перемешанные блоки…

                                Only users with full accounts can post comments. Log in, please.