Qt Reversing Application
Мне захотелось немного повозится с реверсингом кода, написанного на С++ с применением великолепной библиотеки Qt. Как мне показалось, единственная путная статья, описывающая внутреннее устройство классов «Egg Hunt in Tesla Infotainment: A First Look at Reverse Engineering of Qt Binaries» 2023 года. Дело оказалось проще, чем казалось.
Открыв проект в IDA мы перейдем в Strings, чтобы посмотреть на текстовые ресурсы.
Qt использует механизм передачи событий, при помощи которого связываются сигналы и слоты объектов. Имена событий в программе начинаются с действия «add», «use», «init».
Проследим для примера за событием «doCharacterization», который находится в блоке состоящим из строк.
IDA подсказывает, что обращение к первой строке «PVTAnalysisDialog» происходит из sub_12766, тогда как к другим строкам прямого обращения не обнаружено. Такая ситуация напоминает таблицу, к элементам которой происходит косвенное обращение.
Проследим, как используется нулевой элемент таблицы. Единственная ссылка приводит к процедуре, в которой не происходит ничего интересного,
Поднимаясь выше по иерархии вызовов, мы попадаем на виртуальную таблицу класса PVTAnalysisDialog.
Источник подсказывает, что первые три функции для класса, который использует сигнально-слотовую систему Qt называются,
::metaObject()
::qt_metaCast()
::qt_metaCall()
Первая функция sub_6965 (metaObject) через один промежуточный прыжок приводит к функции sub_12442, из которой мы получим адрес структуры,
Структура называется staticMetaObject и состоит из четырех полей,
Первое поле dq offset qword_162078 хранит ссылку на родительский объект.
Второе поле dq offset unk_1168112 таблица хранения строчных данных
Третье поле dq offset unk_1168113 мета-данные объекта
Четвертое поле dq offset sub_3433 процедура обработки событий.
Разбор структуры начнем с таблицы строк. Однако по адресу не обнаруживаются сначала никакие строки.
Выбрав тип данных через Alt+D как Double word, можно заметить повторяющуюся комбинацию 0xFFFFFFFF. Источники упоминают, что второе после 0xFFFFFFFF значение отвечает за длину строковой переменной.
Информация о длине строки мне не пригодилась и мы можем листать дальше, до появления наконец то самой таблицы строк, которая и есть та таблица с которой всё началось.
Копируем строковые переменные в excel и индексируем,
Нулевой элемент таблицы всегда отвечает за имя класса, что упрощает изучение внутренней структуры программы. Вернемся назад на шаг и перейдем к структуре хранящей мета-данные объекта.
Мета данные состоят из двух частей.
Первый блок это описательный блок, где первая цифра 8 отвечает за внутренний номер версии Qt. Для разных версий Qt используется разная логика, в целом похожая, но отличная в деталях, поэтому трудной найти версию статьи, которая посвящена именно текущей версии.
Следующий блок представлен как массив из пяти элементов. Чтобы понять с какого элемента начинать формировать массив, можно обратить внимание на последнее число, которое обычно 6 или 8. Это флаговое число, которым обозначаются сигналы, слоты и другие элементы. Таблица продолжается до следующей метки.
Полученную мета таблицу копируем в excel, индексацию начинаем с единицы.
Первое число в мета таблице это номер строки. Таким образом формируется соответствие внутреннего кода сигнальной системы и текстового имени.
Возвращаясь опять назад мы перейдем к процедуре обработки событий, где нам и понадобятся полученные коды.
Сверяясь с таблицей excel, находим, что код 2 отвечает за updateNParaffinCutsStatus. Можно переименовать sub_6918, но предварительно конечно поставив точку прерывания внутри функции, пытаясь с gui активировать сигнал.
Обнаружив интересующий класс в программе, можно также поставить точку прерывания на ключе переменной a3 блока switch и прочитать какой код поступил на обработку.
Так у меня получилось переходить от нажатий в интерфейсе к местам обработки событий и у вас я думаю всё получится.