Знімаємо пароль з тестів MyTestX і easyQuizzy
Для початку, розберемо саму тривіальну проблему: як витягти з .exe-файлу файл з тестом - .mtf (мова про MyTestX).
Беремо .exe-файл, який містить тест (я скористався екземпляром з форуму) і відкриваємо його в CFF Explorer. Відразу ж переходимо в розділ "Resource Directory" в колонці зліва, далі, в списку праворуч, шукаємо "Resource Directory Entry. AKA: RCData", відкриваємо випадає і в ньому шукаємо "Resource Directory Entry. AKA: MTA".
Цей ресурс фактично є .mtf-файлом, який нас цікавить. Тепер нам необхідно витягти його. Для цього звернемося до дочірньому елементу останнього вищезгаданого ресурсу - "Resource Data Entry". Виділимо його і побачимо в нижній половині вікна його атрибути, нас цікавлять поля "OffsetToData" і "Size".
Запам'ятаємо значення поля "OffsetToData" і перемкнемося на "Address Converter" в списку ліворуч. Тепер введемо значення в поле RVA - ми отримали фізичне усунення даного нас ресурсу щодо початку файлу ( "File Offset").
Натискаємо Debug-> Run і відновлюємо роботу додатка. Далі натискаємо праву клавішу де-небудь в лівому верхньому вікні відладчика. У меню вибираємо Go to-> Expression, у вікні вводимо ReadFile (ім'я функції WinAPI, яка швидше за все буде використовуватися для читання вмісту файлу з тестом).
У списку знизу вибираємо kernel32.ReadFile і натискаємо "Follow expression". Ми знаходимося на початку функції ReadFile, тепер нам необхідно поставити breakpoint, щоб відстежити звернення програми до функції. Ставимо breakpoint, для цього натискаємо правою клавішею на підсвічується лінії ассемблерного коду і вибираємо Breakpoint-> Toggle. Взагалі, перш ніж ставити breakpoint, краще спочатку в MyTestEditor викликати діалог відкриття тесту, інакше доведеться пропускати багато звернень (F9) до ReadFile з не цікавить нас місць, їх можна визначити за значенням на верхівці стек-фрейма (праве нижнє вікно відладчика, рядок з текстом "Return from kernel32.ReadFile to."). Що нас цікавить виклик буде виглядати приблизно так:
Як тільки ми зловили потрібне звернення (воно станеться, коли ми в діалозі відкриття тесту виберемо наш тест, захищений паролем, і натиснемо "Відкрити"), подивимося уважніше в праве нижнє вікно відладчика (в ньому ми бачимо вміст стека), прокрутимо вікно вниз в пошуках рядки, яка представляє собою повний шлях до файлу з тестом (нас цікавить цей стек-фрейм, оскільки швидше за все він був сформований в цікавій для нас функції).
Продовжимо виконання програми (F9 або Debug-> Run). Через якийсь час програма в черговий раз зупиниться, спрацює наш свежепоставленний hardware breakpoint. У мене місце спрацювання виглядало якось так:
Нагадує якусь проміжну функцію для роботи з файлом, тому скористаємося кілька разів опцією Debug-> Execute till return (Ctrl + F9), поки не опинимося в більш "високорівневої" ділянці коду, де будується основна логіка обробки файлу з тестом.
Погортаємо код. Нас цікавлять всілякі умовні переходи (je, jne, jz, jnz і так далі), які перестрибують більш-менш значні фрагменти коду, скажімо від 5 інструкцій. Деякі ділянки я відразу пропустив, де, як мені здалося, виробляються нецікаві дії, як, наприклад, в цьому фрагменті:
Нагадує перевірку версії тесту. Мені так відразу здалося, але можна переконатися і досвідченим шляхом, поставивши breakpoint на інструкції умовного переходу і помінявши значення Z-прапора у вікні регістрів справа, коли виконання програми перерветься на цій ділянці. Перегорнемо трохи вниз і натрапимо на наступну групу умовних переходів:
Спробуємо в лоб поміняти Z-прапор на кожному переході. Тобто в MyTestStudent у нас відкритий діалог вибору файлу з тестом, ми ставимо breakpoint на одному з переходів, в діалозі вибираємо файл з тестом, який захищений паролем, спостерігаємо вікно, що запрошує введення пароля, вводимо туди довільний текст, після цього у нас повинен спрацювати раніше встановлений breakpoint, міняємо Z-прапор і продовжуємо виконання програми натисканням F9.
Ми побачимо, що на вищезазначених двох переходах, при зміні Z-прапора програма завершує свою роботу, після цього запускається браузер, де відкривається сайт з фрагментом законодавства: якась самопальна "захист" від простого злому. Однак, при зміні логіки умовного переходу, який розташований трохи нижче, ми бачимо, що в програмі попередньо відкривається тест, хоча після цього програма все одно завершує свою роботу і відкриває браузер.
Для відкриття браузера швидше за все використовується функція WinAPI ShellExecute. Перевіримо наше припущення: знову натискаємо праву клавішу де-небудь в лівому верхньому вікні відладчика, в меню вибираємо Go to-> Expression, у вікні вводимо ім'я нашої функції, переходимо в початок функції і ставимо там breakpoint. Знову виконаємо модифікацію логіки останнього, що цікавить нас, умовного переходу, і наш breakpoint спрацьовує:
Скористаємося кілька разів Debug-> Execute till return (або Ctrl + F9), щоб повернутися з надр shell32.dll і потрапити в модуль MyTestEditor (стежимо за заголовком вікна відладчика, там де в даний момент написано "[CPU - main thread, module shell32 ] "). Майже відразу потрапляємо в подібне місце:
Тут ми бачимо ще один умовний перехід, який нам необхідно виправити (на льоту, або замінивши умовний перехід на безумовний - jmp). Виходить, що нам потрібно підправити два умовних переходу, щоб отримати можливість відкривати захищений тест, вводячи будь-який пароль. Замінимо і перевіримо:
Перемкнемося на основний виконуваний модуль easyQuizzy в отладчике (View-> Executable modules, подвійний клік по імені модуля, переконайтеся, що в заголовку вікна присутній текст: ". Module easyQuizzy"). Натиснемо правою клавішею миші у вікні відладчика і знайдемо всі строкові ресурси, на які існують посилання в виконуваному коді.
Перед нами з'явиться перелік рядків в одній великій таблиці, знайдемо в ній рядки з повідомленням про некоректне паролі і поставимо на них breakpoint'и.
Пробуємо відкрити захищений тест і тут же ловимо спрацьовування breakpoint'а.
Ми опинилися всередині цікавої функції, де можемо спостерігати пропуск задоволеного великого фрагмента коду за умовним переходу, до того ж пропускається фрагмент містить посилання на рядок "Incorrect password.".
Але як ми взагалі потрапляємо в цей фрагмент коду? Прокрутимо лістинг трохи вгору і побачимо набагато цікавіший умовний перехід: він дозволяє пропустити навіть виклик функції, яка відображає нам діалог введення пароля.
Пробуємо змінити логіку його роботи (наприклад, забиваємо умовний перехід NOP'амі) - вуаля, захищений тест відкривається не запрошуючи пароль.