Как ломать программы windows
Реверсивная инженерия — изучение того, как устроено какое-то программное обеспечение, с целью понять механизм его работы. Эта информация может использоваться в будущем для создания похожего продукта или внесения изменения в уже существующий.
Я хочу рассказать о том, как с помощью реверсивной инженерии можно вносить изменения в игры на ПК. Не обязательно с целью взлома, иногда это используется для исправления багов, которые разработчики не исправили по каким-то причинам. Так, например, существует проект Forged Alliance Forever, будучи участником которого я исправил пару багов в игре Supreme Commander, которая пользуется большой популярностью до сих пор, но из-за проблем между издателем и разработчиком поддержка игры была прекращена.
Эта статья будет интересна тем, кто занимается разработкой программного обеспечения на языках высокого уровня и интересуется тем, как оно работает на более низком уровне. А также всем тем, кто просто любит игры и хочет знать об их устройстве больше.
Когда мы пишем какое-то ПО, то чаще всего, мы выбираем инструментарий, подходящий лучше всего под эту задачу. В современных играх код пишется на компилируемых языках высокого уровня. Конечно, можно вспомнить Minecraft на Java, который в конечном итоге все-таки переписали на плюсах.
Здесь стоит уточнить, что часто та часть приложения, которая критична для производительности, пишется на компилируемом языке, в то время как логика может быть на чем-то скриптовом, типа Lua, который уже обращается к ядру движка.
Код на языке высокого уровня с помощью компилятора превращается в машинный код, состоящий из инструкций для процессора определенной архитектуры. При запуске, процессор последовательно выполняет инструкции, а наше приложение работает.
Для того, чтобы внести изменения в ход работы программы, нам необходимо отредактировать эти самые инструкции. Для этого нам потребуется определенный инструментарий, позволяющий нам внедряться в работу программы. Основным инструментом нам послужит дебаггер, я пользуюсь x64dbg.
Дебаггер — программа, которая подключается к (например) игре и позволяет ковыряться в ней во время ее работы. Она позволяет выполнять инструкции последовательно, менять содержимое регистров и стека.
Регистры — это небольшие участки памяти процессора, которые он использует при выполнении команд. У каждого регистра есть своя задача, например, один используется для возвращаемого функцией значения, другой хранит в себе аргументы, а третий — следующую инструкцию, которая будет выполнена.
Стек используется для хранения информации, которая используется программой. Он нам мало интересен, кроме его особенности в хранении информации в упорядоченном виде.
Вот так выглядит приложение под в режиме отладки (дебага) в x64dbgУже скоро, осталось только уяснить особенности взлома игр и способы достижения результата.
Тут все предельно просто — находим ячейку в памяти, в которой хранится количество патронов или жизней, меняем на то, которое позволяет собственная жадность. Главное, чтобы жадность не превышала размер типа данных, иначе приложение рухнет. Этим многие из нас баловались еще во времена ArtMoney.
Это посложнее, здесь мы уже перехватываем выполнение функций графической подсистемы. (Чаще всего DirectX или OpenGL). Для этого мы создаем динамическую библиотеку DLL, которая меняет адрес нужной нам функции на нашу, которая потом вызывает и оригинальную функцию, и наш код, рисующий врагов!
DLL подменяет адрес функции в таблице адресов программы, кажется, мы подсмотрели это поведение у кукушек.Здесь мы тоже получаем информацию о противниках из памяти игры, а разница между позицией игрока и противника и будет вектором, куда нам надо стрелять. Останется только повернуть игрока в нужном направлении, раз он сам не в состоянии.
Поскольку в реальной жизни отладка занимает просто колоссальное количество времени, мы опробуем свои силы на чем-то простом, чтобы уложиться в рамки статьи. У меня под рукой оказалась The Outer Worlds, над которой мы сегодня и будем паразитировать.
В игре есть механика заточки оружия, платим деньги — получаем улучшенное оружие.
Находим количество денег, тратим немного, фильтруем те значения, которые нас устраивают. В итоге остается одно значение, которое и является нашим количеством денег.Теперь берем адрес памяти, в котором хранятся деньги, подключаем дебаггер к игре и просим его оставить выполнение программы, если она попытается изменить значение переменной в следующий раз.
Синяя линия внизу — текущая команда процессора. Именно в этом месте и меняется значение денег. Огибающие линии сверху показывают, что мы находимся в нескольких уровнях вложенности, а команды jl как раз и являются проверками каких-то условий.Далее мы поднимаемся немного наверх, добираемся до начала функции и попеременно меняем условия, которые в дебаггере подсвечиваются желтым цветом. Опытным путем мы узнаем, что если третье условие сработает, то мы не потратим деньги. Почему? Хороший вопрос, для этого надо проследить ход выполнения программы более внимательно, но результат не хочет ждать, поэтому вместо условия jl ставим безусловный переход командой jmp и наслаждаемся бесплатными улучшениями.
Самое сложное в реверсивной инженерии то, что изначально мы как слепые котята тыкаемся и меняем условия с надеждой на достижение результата. Но если разобраться в том, что делают команды и как работают компиляторы, то со временем вы начнете находить логические последовательности, понимать, как передаются в функции аргументы и т.д.
Исправление ошибки с максимальным уровнем снаряженияЕще в игре есть досадная ошибка разработчиков, влияющая на баланс, — оружие можно улучшать только на пять уровней выше персонажа. Что ж, исправим это!
Первым делом необходимо найти в памяти уровень персонажа. Это можно сделать предварительно накрутив себе опыта. Как только у нас есть информация об уровне персонажа, мы снова любезно просим дебаггер приостановить работу игры в момент обращения к памяти по этому адресу.
Когда нужно что-то найти в памяти, Cheat Engine приходится очень кстати. Чаще всего необходимо найти базовый адрес, это делается довольно просто, но немного выходит за рамки статьи. Редактируем команду — заменяем обращение к адресу в памяти [rax+310] константным значением 1000После чего доверчивая игра позволяет нам ковать столько, сколько вздумается.
Обратите внимание на уровень персонажа и уровень винтовки, теперь можно отыгрывать Men In Black, у нас же все-таки рпг, не так ли?После того, как мы исправили все баги в игре, мы хотим чтобы они больше не появлялись ни у нас, ни у наших друзей. Для этого мы должны пропатчить исполняемый файл игры. Исправляем файл, кладем рядом с оригинальным и в следующий раз запускаем уже его.
Список изменений в программе, которые будут внесены в исполняемый файлТаким образом мы с вами познакомились с чудесным миром реверсивной инженерии, разобрались, как взламывают игры и, на примере The Outer Worlds, исправили несколько багов в игре. Конечно, в реальной жизни это занимает гораздо больше времени и чаще всего взлому подвергаются онлайн-игры, которые имеют свою специфику из-за клиент-серверной архитектуры, но принципы работы остаются те же.
Реверсивная инженерия довольно непростой инструмент, требующий огромного количества времени и усидчивости, но цена чаще всего оправдывает себя.
Я впервые за мильён лет в интернете вижу, чтобы реверс инжиниринг писали не так как я выше, лол.
Или обратная разработка, на худой конец
Это все круто, но можно гайд по руссификаций игр. Как например, извлекается текст, на чем делать перевод. И ссылки на полезные сайты.) Общий смысл то, я знаю. А вот как приступить с чем и с какой стороны, не ведаю. А напрягать какую нибудь кучку энтузиастов переводчиков, ну тоже не охота.
Насколько мне известно - зависит от движка. Как он пакует ресурсы. В некоторых случаях есть анпакеры. В некоторых танцы с бубном.
Не от движка, а от типа шифрования. Я не знаю, может ли движок зашифровать, по этому мне и интересен гайд на такую тему. Потому что, ведь есть более менее универсальные программы для расшифровки. Так же интересны и популярные методы шифрования и как их взламывают. Вот как то так.
Да текст игры обычно нет смысла шифровать, обычно пакуют данные чтобы легче было с ними работать и читать их, этим как раз анпакеры занимаются.
Гайд такой: гуглишь анпакер от игры, если не находишь, гуглишь от другой игры на том же движке, если не работает пишешь разработчику, если не находишь, пишешь задание фрилансеру.
Если ты фрилансер анпакеры делаются просто - реверсинженирится код, догадываешься что значат эти байты с данными, проверяешь, пишешь программу которая распакует, потом запакует данные. Это как раз та нудятина которая тут описана.
анпакеры делаются просто - реверсинженирится код, догадываешься что значат эти байты с данными, проверяешь, пишешь программу которая распакует, потом запакует данные.
Как два пальца ебать хаха
Сову тяжелее нарисовать
У тебя тоже есть какая-то старая игра, которую ты мечтаешь обмазать нормальными текстурами?
Это тоже можно, но сначала хотелось бы руссификаций поставить, ну естественно перед этим создать ее.)
Я раньше игры переводил. Unity очень просто переводиться, для нее даже существует утилита в духе Resourse Editor. Там просто ищешь английский текст в коде и заменяешь на русский. Если игра продуманней и поддерживает моды, находишь ресурсы, которые отвечают за язык и вставляешь как мод. Вообще вся инфа есть в Интернете, достаточно желания и гугла
Комментарий удален по просьбе пользователя
Денува шифрует . Хотя гораздо сложнее выдрать ее из ехе файла , чем обмануть ее.
К сожалению, общего подхода тут просто нет, каждый случай уникален. Как верно было замечено, зависит как минимум от движка игры. Мне лично приходилось иметь дело с No Man's Sky и Astroneer, и нюансов там куча.
Грусть. Ну хотя бы с чего начать можно?
Я бы начал с выяснения, на каком движке и какой его версии сделана игра. Делали ли уже русификацию или иные моды на эту игру до тебя, если да, то как, на какие проблемы наткнулись. Нужен ли анпакер, если да, то где достать. Скажем, в UE анпакер штатный, а вот на файлы локализации придётся искать нечто отдельно, и не факт, что найдёшь на используемую разрабами версию. Отдельно ситуацию усложняет то, что разработчики могут использовать хоть и не самописный движок (например тот же UE), но зато СИЛЬНО модифицированный. С включениями из разных версий, этакого монстра Франкенштейна. В общем, куча нюансов, как и говорил.
Т.е. до того, как ты непосредственно начнёшь что-то делать, придётся собрать немало инфы. Это сильно сэкономит время потом, но может и изрядно огорчить.
В принципе, всё это уже было сказано до меня, никакого общего алгоритма тут просто нет.
Окей, понимаю. Вообщем, есть толковые сайты по этому делу? Или только ZoG?
Возможно я ошибаюсь, но имхо ZoG это ресурс где надо искать в первую очередь сами переводы, а не методы их создания?
Честно говоря, какого-то отдельного сайта по этому вопросу я не нашёл, может быть, недостаточно искал. Но и задачи у меня были достаточно узкие. Скажем, NMS распаковывается элементарно, и текстовые ресурсы там лежат в открытом виде (зато самого текста завались, и чудовищное количество мусора, который никто не чистит). С другой стороны, Астронир распаковывается штатным анпакером UE, а вот файлы локализации приходится распаковывать дополнительно сторонней утилитой. И, в целом, UE считается не самым приятным движком для мододелов, особенно если разработчики не озаботились поддержкой оных изначально.
Поэтому ничего конкретного посоветовать не могу, только искать, концентрируясь на конкретно твоей задаче/игре.
Скачай сначала SoftICE, там уже походу разберёшься. Во всём, вообще.
Вот такой подход мне по душе
Извини бро, мои познания устарели, как выяснилось) SoftICE максимум с win xp работает.
А что тогда скачать?!
Ого, чет давно я его не запускал, гугль говорит - устарел.
Думаю ответ можно найти на ZOG.
Я вроде шарился и гайдов там таких не нашел.(
Там есть кураторы. Они уж точно знают.
Я из прошлого опыта посоветовал.
Сам переводы с ЗОГ давно не качал.
И такое чувство, что переводов там давно не делают.
Но инфа могла остаться.
Я зачем то заходил туда, там был один новый перевод и презентовали его как будто авторы диско перевели.
Ясненько.
Собсно и нужды в ЗОГ особой нет, т.к. почти все значимые игры переводят на русский. Обычно проблемы с переводом возникауют у РПГ, но и они все переводятся (пусть и не всегда достаточно хорошо).
Да мне тоже нравится как сейчас дела проходят с русификацией. Все стало намного лучше. Просто хочу себе такое маленькое хобби. Ибо при условий, что я могу перевести, что написано на экране, мне все равно тяжело воспринимать информацию, чем если это написано на русском.
Статья о реверс инжиринге.
Ожидание: дизассеблеры, программирование на ассемблере и си.
Реальность: Артмани.
А если серьезно, то оригинальное использование софта! В детстве баловался и Артманей и hex-редакторами, но использовать их совместно я не додумался.
Я, кажется, не использовал ArtMoney, да и вряд ли унылый разбор различий между соглашениями вызовов был бы интересен здесь, все-таки, не Хабр)
Я без всякой злобы) Понятно, что показан достаточно простой , но эффектный прием.
Cheat Engine не пользовался, но по скринам и описанию функционал вроде схож с Артманей - ищет конкретные значения в памяти.
Артманя - кастрированный читэнжин. В энжине встроенный HEX-редактор, например. Хоть по-человечески обращаться к памяти можно.
СОДЕРЖАНИЕ
1. Введение в ломание Windows-программ
2. Обзор SoftICE/Win 2.oo
3. Поиск регистрационных кодов
3.1 Task Lock 3.00 - простая защита на основе серийного номера
3.2 Command Line 95 - простая регситрация "имя-код"
4. Создание генератора ключей для Command Line 95
5. Как работают инструкции PUSH и CALL когда программа вызывает функцию
6. О программах, написанных на Visual Basic
ПРИЛОЖЕНИЯ
A. Как в SoftICE загружать символьные имена (имена функций etc)
B. Синтаксис функций GetWindowText, GetDlgItemText и GetDlgItemInt
C. Где найти программы
D. Как связаться с автором
1. ВВЕДЕНИЕ В ЛОМАНИЕ WINDOWS-ПРОГРАММ
Ломать программы Windows в большинстве случаев даже проще, чем ломать программы Dos. В Windows
сложно что-нибудь скрыть от того, кто ищет,особенно если программа использует стандартные функции
Windows.
Первая (и часто единственная) вещь, которая Вам потребуется - это SoftICE/Win 2.oo, мощный отладчик от фирмы NuMega. Некоторым людям он кажется очень сложным в использовании, но я расскажу Вам, как с ним управляться и, я надеюсь, Вы поверите мне. =) В приложении A я привел некоторую информацию, которую Вам следует прочитать. URL всех программ, которые Вам понадобятся, приведены в приложении C. - ED!SON, [email protected]
2. ОБЗОР SOFTICE/WIN 2.OO
Ниже приведен очень схематичный рисунок, демонстрирующий окно SoftICE:
|--------------------|
| Регистры | 'R' - правка значения регистров
|--------------------|
| Окно данных | 'D' - просмотр памяти, 'E' - правка памяти
|--------------------|
| Окно кода | 'U' - просмотр кода по адресу, 'A' - вставка кода
|--------------------|
| Окно команд | Здесь Вы набираете команды
|--------------------|
Другие важные клавиши (в стандартной настройке):
'H'/F1 - помощь
F5/Ctrl+D - запуск программы (или продолжение прерванной программы)
F8 - пошаговая отладка с заходом в тело функции
F10 - пошаговая отладка без захода в тело функции
F11 - выйти из функции (будет работать только до первого PUSH в функции)
3. ПОИСК РЕГИСТРАЦИОННЫХ КОДОВ
Возможно, наилучший способ попрактиковаться - это найти где-нибудь шареварную (shareware) программку и попытаться зарегистрировать ее.
3.1 Task Lock 3.00 - простая защита на основе серийного номера
Это очень простая защита: номер не зависит ни от каких факторов.
3.1.1 Медицинское обследование
Какой разрядности программа - 16 или 32 бит? Где вводится регистрационная информация? Даст ли мне справка какие-нибудь предположения о том, как устроена регистрация? Попробуйте ответить на эти вопросы перед тем, как мы продолжим. . Сейчас Вы должны быть заняты обследованием. Вы заняты обследованием? . Ну как, уже все. OK, теперь Вы знаете, что это 32-битное приложение, работающее под Windows 95 и что регистрация заключается в заполнении регистрационного номера в диалоговом окошке, которое появляется когда Вы выбираете меню "Register|Register. ". Из справки Вам также стало известно, что существует два типа регистрации: для индивидуального использования и для использования в "конторе" (в оригинале - site license). Поэтому очень вероятно, что в программе будет ДВЕ проверки регистрационных кодов.
3.1.2 Прерывание программы
Регистрационные коды чаще всего вводятся в обычных строчках ввода типа Windows Edit. Чтобы проверить код, программа должна прочитать содержимое строки ввода при помощи одной из функций:
16-бит 32-бит
GetWindowText GetWindowTextA, GetWindowTextW
GetDlgItemText GetDlgItemTextA, GetDlgItemTextW
Последняя буква в названии 32-битных функций говорит о том, какие строки использует эта функция: однобайтовые или двухбайтовые. Двухбайтовые строки используются ОЧЕНЬ редко. Возможно, что Вы уже уловили мою мысль. "Если бы можно было прерваться по вызову GetWindowText. " - и Вы МОЖЕТЕ это сделать. Но сперва Вы должны убедиться, что символьные имена (имена функций) загружены SoftICE'ом. Если Вы не знаете, как это сделать - см. приложение A.
Чтобы установить "ловушку" (на самом деле это называется точкой останова или брейкпоинтом) в SoftICE, Вы должны зайти в отладчик нажатием клавиш Ctrl-D и использовать команду BPX. В качестве параметра команды можно использовать либо имя функции, либо непосредственно адрес. Так как наш "объект изучения" (Task Lock) является 32-битным приложением, мы должны поставить брейкпоинт на функцию GetWindowTextA. Если это не поможет, попробуйте поставить брейкпоинт на другие функции.
В командной строке SoftICE наберите следующее:
В результате Вы увидите что-нибудь типа:
00) BPX USER32!GetWindowTextA C=01
Удалим старый брейкпоинт:
(0 - это номер брейкпоинта в списке брейкпоинтов)
И установим новый:
Ну что ж, попробуем еще раз.
3.1.3 В отладчике
Wow! Работает! Теперь вы в SoftICE, в самом начале функции GetDlgItemTextA. Чтобы попасть туда, откуда она была вызвана, нажмите F11. Теперь Вы внутри модуля SGLSET.EXE. Если Вы не уверены - посмотрите на строчку между окном кода и окном командной строки, она должна выглядеть так:
Сейчас Вы уже можете запретить реакцию на вызов функции:
Если Вам вдруг захочется снова разрешить ее, наберите:
Первая строка в окне кода выглядит так:
Чтобы посмотреть строчки над ней, нажимайте Ctrl+Up ("стрелка вверх") до тех пор, пока не увидите нижеприведенный кусок кода. Если Вы ничего не понимаете в Ассемблере, я добавил комментарии которые могут Вам помочь.
RET ; Конец функции
PUSH EBP ; Начало другой функции
MOV EBP, ESP ; .
SUB ESP, 0000009C ; .
PUSH ESI ; .
> LEA EAX, [EBP-34] ; EAX = EBP-34
PUSH EDI ; .
MOVE ESI, ECX ; .
PUSH 32 ; Макс. длина строки
> PUSH EAX ; Адрес текстового буфера
PUSH 000003F4 ; Идентификатор управления
PUSH DWORD PTR [ESI+1C] ; Идентификатор окна диалога
CALL [USER32!GetDlgItemTextA] ; Получить текст
Команды PUSH означают сохранение значений для последующего использования. Я пометил важные строчки символом '>'. Глядя на этот код, мы видим, что адрес текстового буфера хранился в регистре EAX и что EAX был EBP-34h. Поэтому нам стоит взглянуть на EBP-34h:
Вы должны были увидеть текст, который вы ввели в диалоговом окне. Теперь мы должны найти место, где Ваш номер сравнивается с реальным серийным номером. Поэтому мы пошагово трассируем программу при помощи F10 до тех пор, пока не встретим что-нибудь о EBP-34. Не пройдет и нескольких секунд, как Вы наткнетесь на следующий код:
> LEA EAX, [EBP+FFFFFF64] ; EAX = EBP-9C
LEA ECX, [EBP-34] ; ECX = EBP-34
PUSH EAX ; Сохраняет EAX
PUSH ECX ; Сохраняет ECX
> CALL 00403DD0 ; Вызывает функцию
ADD ESP, 08 ; Удаляет сохраненную информацию
TEST EAX, EAX ; Проверяет значение функции
JNZ 00402BC0 ; Прыгает, если не "ноль"
Мне кажется, что это выглядит как вызов функции сравнения двух строк. Эта функция работает так: на входе - две строки, на выходе - 0, если они равны и любое другое значание, если не равны.
А зачем программе сравнивать какую-то строчку с той, что Вы ввели в окне диалога? Да затем, чтобы проверить правильность Вашей строки (как Вы, возможно, уже догадались)! Так-так, значит этот номер скрывался по адресу [EBP+FFFFFF64]? SoftICE не совсем корректно работает с отрицательными числами и поэтому настоящий адрес следует посчитать:
100000000 - FFFFFF64 = 9C
Вы можете сделать это вычисление прямо в SoftICE:
Число 100000000 слишком велико для SoftICE, а вычитание из 0 дает тот же самый результат. Наконец пришло время взглянуть, что же скрывается по адресу EBP-9C.
В окне данных SoftICE Вы видите длинную строчку цифр - это серийный номер! Но Вы помните, что я говорил Вам раньше? Два типа регистрации - два разных серийных номера. Поэтому после того, как Вы записали на бумажечку первый серийный номер, продолжайте трассировать программу при помощи F10. Мы дошли до следующего куска кода:
> LEA EAX, [EBP-68] ; EAX = EBP-68
LEA ECX, [EBP-34] ; ECX = EBP-34
PUSH EAX ; Сохраняет EAX
PUSH ECX ; Сохраняет ECX
> CALL 00403DD0 ; Снова вызывает функцию
ADD ESP, 08 ; Удаляет сохраненную информацию
TEST EAX, EAX ; Проверяет значение функции
JNZ 00402BFF ; Прыгает если не "ноль"
И что Вы видите по адресу EBP-68? Второй серийный номер!
Вот и все. Я надеюсь, что у Вас все получилось как доктор прописал? =)
3.2 Command Line 95 - легкая регистрация "имя-код", создание генератора ключей
Это программа - хороший пример, с легким алгоритмом генерации кода.
3.1.1 "Обследование"
Вы осмотрели программу и увидели, что это 32-битное приложение, требующее имя и код в окне регистрации. Поехали!
3.1.2 Прерывание программы
Мы поступаем так же, как и с Task Lock'ом - ставим брейкпоинты. Можно даже поставить сразу два брейкпоинта на наиболее возможные функции: GetWindowTextA и GetDlgItemTextA. Нажмите Ctrl-D, чтобы вызвать отладчик и наберите в окне команд:
:bpx getwindowtexta
:bpx getdlgitemtexta
Теперь возвращайтесь в прерванную программу, идите в окно регистрации и вводите имя и какой-нибудь номер (обыкновенное целое число - это наиболее вероятный код). Я написал примерно следующее:
Name: ED!SON '96
Code: 12345
Программа остановилась на GetDlgItemTextA. Так же, как и в случае с Task Lock'ом, мы нажимаем F11 чтобы вернуться в вызывающюю функцию. Просматриваем окно кода при помощи Ctrl+Up. Вызов функции выглядит так:
MOV ESI, [ESP+0C]
PUSH 1E ; Максимальная длина
PUSH 0040A680 ; Адрес буфера
PUSH 000003ED ; Идентификатор управления
PUSH ESI ; Идентификатор окна диалога
CALL [User32!GetDlgItemTextA]
Число 40A680 кажется нам интересным, поэтому мы проверяем этот адрес:
Что же видно в окне данных, как не имя, которое мы ввели? =) А теперь взглянем на кусок кода под вышеприведенным:
PUSH 00 ; (не интересно)
PUSH 00 ; (не интересно)
PUSH 000003F6 ; Идентификатор управления
MOV EDI, 0040A680 ; Адрес буфера
PUSH ESI ; Идентификатор окна диалога
CALL [User32!GetDlgItemInt]
Функция GetDlgItemInt похожа на GetDlgItemTextA, но возвращает не строку, а целое число. Она возвращает его в регистре EAX, поэтому мы трассируем этот код (F10) и смотрим, что же у нас появилось в окне регистров после вызова функции. В моем случае оно выглядит так:
А что такое шестнадцатеричное 3039? Наберем:
И получим следующее:
00003039 0000012345 "09"
^ hex ^ dec ^ ascii
Как Вы видите (и, возможно, уже догадались) это код, который Вы ввели в диалоговом окне. Ok, что теперь? Посмотрим дальше:
MOV [0040A548], EAX ; Сохраняет рег. код
MOV EDX, EAX ; А также помещает его в EDX
3.1.3 Подсчитывание регистрационного кода
Мы достигли места, где подсчитывается реальный регистрационный код!
MOV ECX, FFFFFFFF ; Эти строчки подсчитывают
SUB EAX, EAX ; длину строки
REPNZ SCASB ; .
NOT ECX ; .
DEC ECX ; ECX теперь содержит длину
MOVSX EAX, BYTE PTR [0040A680] ; Получает байт по адр. 40A680h
IMUL ECX, EAX ; ECX = ECX * EAX
SHL ECX, 0A ; Сдвиг влево на 0Ah бит
ADD ECX, 0002F8CC ; Добавляет 2F8CC к результату
MOV [0040A664], ECX
. И где он проверяется
CMP ECX, EDX ; Сравнивает числа
JZ 00402DA6 ; Прыгает, если равны
Когда Вы дотрассировали до сравнения чисел, Вы можете посмотреть, каким должен был быть Ваш РЕАЛЬНЫЙ регистрационный код:
В моем случае это дало:
То есть, правильный код для меня: 901324. Нажмем F5 или Ctrl-D чтобы вернуться в программу и попробуем еще раз, но на этот раз с правильным кодом (в десятичной форме). Работает!
4. СОЗДАНИЕ ГЕНЕРАТОРА КЛЮЧЕЙ ДЛЯ COMMAND LINE 95
Взглянем на алгоритм генерации кода и попробуем перевести его на язык Си. Вот очень простая формула, по которой подсчитывается ключ:
code = ((uppercase_first_char * length_of_string) << 0x0A) + 0x2f8cc;
Целиком программа на Си выглядит так:
int main()
unsigned long code;
unsigned char buffer[0x1e];
printf("CommandLine95 Keymaker by ED!SON '96\n");
printf("Enter name: ");
gets(buffer);
strupr(buffer);
code = ( ((unsigned long)buffer[0] *
(unsigned long)strlen(buffer))
<< 0x0A) + 0x2f8cc;
printf("Your code is: %lu", code);
4. КАК РАБОТАЮТ PUSH И CALL КОГДА ПРОГРАММА ВЫЗЫВАЕТ ФУНКЦИЮ
Снова взглянем на кусок кода из Task Lock'а:
PUSH 32 ; Макс. длина строки
PUSH EAX ; Адрес текстового буфера
PUSH 000003F4 ; Идентификатор управления
PUSH DWORD PTR [ESI+1C] ; Идентификатор окна диалога
CALL [USER32!GetDlgItemTextA] ; Получает текст
Когда Вы вызываете функцию GetDlgItemTextA из программы на C, вызов выглядит так:
GetDlgItemTextA(hwndDlg, 0x3F4, buffer, 0x32);
^ [ESI+1C] ^ EAX
PUSH сохраняет данные в области памяти, называемой стеком. В результате каждого PUSH'а новый кусок данных помещается в верхушку стека и затем вызываемая функция проверяет, что лежит в стеке и использует эти данные по своему усмотрению.
5. О ПРОГРАММАХ НА VISUAL BASIC
EXE файлы, производимые Visual Basic'ом, не являются настоящими EXE. Они просто содержат код для вызова VBRUNxxx.DLL, который затем читает данные из EXE и выполняет программу. Такое устройство псевдо-EXE файлов является также причиной того, что программы на Visual Basic'е такие медленные. А так как EXE файлы не являются настоящими EXE файлами, Вы не можете трассировать и дизассемблировать их - Вы найдете вызов функции из DLL и кучу мусора. И когда Вы будете трассировать такую программу, Вы "заблудитесь" в DLL. Решением этой проблемы является декомпилятор. Существует декомпилятор для программ, написанных на Visual Basic'е версий 2 и 3, созданный кем-то, называющим себя DoDi. Эта программя является шареварной и ее можно найти в InterNet'е (см. Приложение C). Для программ, написанных на Visual Basic'е версии 4 (VB для Windows 95), не существует декомпилятора, насколько мне известно, хотя я бы хотел, чтобы он существовал. =)
Примечание: Настоящие программисты на пишут на Basic'е. =)
A. КАК В SOFTICE ЗАГРУЖАТЬ СИМВОЛЬНЫЕ ИМЕНА
Чтобы проверить, загрузил ли SoftICE символьные имена GetWindowText, Вы должны войти в отладчик нажатием на клавиши Ctrl-D и в окне команд ввести следующее:
Если Вы не получили списка всех функций GetWindowText, Вам нужно отредактировать файл \SIW95\WINICE.DAT, удалив символ комментария (';') перед одной из строчек 'exp=', которые следуют за текстом: "Examples of export symbols that can be included for chicago" в конце этого файла. Вы можете удалить комментарии из всех строчек 'exp=' или сохранить немножко памяти, раскомментировав только строчки с файлами kernel32.dll, user32.dll и gdi32.dll, которые являются самыми важными. После этого Вы должны перегрузить компьютер.
B. СИНТАКСИС НЕКОТОРЫХ ФУНКЦИЙ
Вам будет легче понять, как вызываются функции, о которых мы говорили, если Вы будете знать их описания (декларации):
int GetWindowText(int windowhandle, char *buffer, int maxlen);
int GetDlgItemText(int dialoghandle, int controlid, char *buffer, int maxlen);
int GetDlgItemInt(int dialoghandle, int controlid, int *flag, int type);
Если Вам нужна более подробная информация, посмотрите в руководстве программиста Windows/Win32.
Первая (и часто единственная) вещь, которая Вам
потребуется - это SoftICE/Win 2.oo, мощный отладчик от фирмы NuMega.
Некоторым людям он кажется очень сложным в использовании, но я расскажу
Вам, как с ним управляться и, я надеюсь, Вы поверите мне. =) В
приложении A я привел некоторую информацию, которую Вам следует
прочитать.
URL всех программ, которые Вам понадобятся, приведены в приложении C.
- ED!SON, [email protected]
2. ОБЗОР SOFTICE/WIN 2.OO
Ниже приведен очень схематичный рисунок, демонстрирующий окно SoftICE:
| Регистры | ‘R‘ - правка значения регистров
| Окно данных | ‘D‘ - просмотр памяти, ‘E’ - правка памяти
| Окно кода | ‘U‘ - просмотр кода по адресу, ‘A’ - вставка кода
| Окно команд | Здесь Вы набираете команды
Другие важные клавиши (в стандартной настройке):
‘H’/F1 - помощь
F5/Ctrl+D - запуск программы (или продолжение прерванной программы)
F8 - пошаговая отладка с заходом в тело функции
F10 - пошаговая отладка без захода в тело функции
F11 - выйти из функции (будет работать только до первого PUSH в функции)
3. ПОИСК РЕГИСТРАЦИОННЫХ КОДОВ
Возможно, наилучший способ попрактиковаться - это найти где-нибудь
шареварную (shareware) программку и попытаться зарегистрировать ее.
3.1 Task Lock 3.00 - простая защита на основе серийного номера
Это очень простая защита: номер не зависит ни от каких факторов.
3.1.1 Медицинское обследование
Какой разрядности программа - 16 или 32 бит? Где вводится
регистрационная информация? Даст ли мне справка какие-нибудь
предположения о том, как устроена регистрация? Попробуйте ответить на
эти вопросы перед тем, как мы продолжим.
….Сейчас Вы должны быть заняты обследованием….Вы заняты обследованием?
OK, теперь Вы знаете, что это 32-битное приложение,
работающее под Windows 95 и что регистрация заключается в заполнении
регистрационного номера в диалоговом окошке, которое появляется когда
Вы выбираете меню
“Register|Register…”. Из справки Вам также стало известно, что
существует два типа регистрации: для индивидуального использования и
для использования в “конторе” (в оригинале - site license). Поэтому
очень вероятно, что в программе будет ДВЕ проверки регистрационных
кодов.
3.1.2 Прерывание программы
Регистрационные коды чаще всего вводятся в обычных строчках ввода типа
Windows Edit. Чтобы проверить код, программа должна прочитать
содержимое строки ввода при помощи одной из функций:
GetWindowText GetWindowTextA, GetWindowTextW
GetDlgItemText GetDlgItemTextA, GetDlgItemTextW
Последняя буква в названии 32-битных функций говорит
о том, какие строки использует эта функция: однобайтовые или
двухбайтовые. Двухбайтовые строки используются ОЧЕНЬ редко.
Возможно, что Вы уже уловили мою мысль. “Если бы можно было прерваться
по вызову GetWindowText…” - и Вы МОЖЕТЕ это сделать. Но сперва Вы
должны убедиться, что символьные имена (имена функций) загружены
SoftICE’ом. Если Вы не знаете, как это сделать - см. приложение A.
Чтобы установить “ловушку” (на самом деле это
называется точкой останова или брейкпоинтом) в SoftICE, Вы должны зайти
в отладчик нажатием клавиш Ctrl-D и использовать команду BPX. В
качестве параметра команды можно использовать либо имя функции, либо
непосредственно адрес. Так как наш
“объект изучения” (Task Lock) является 32-битным приложением, мы должны поставить брейкпоинт на функцию GetWindowTextA. Если это не поможет, попробуйте поставить брейкпоинт на другие функции.
В командной строке SoftICE наберите следующее:
:bpx getwindowtexta
Вы можете проверить наличие брейкпоинтов командой:
:bl
В результате Вы увидите что-нибудь типа:
00) BPX USER32!GetWindowTextA C=01
Чтобы выйти из отладчика, нажмите Ctrl-D (или F5) еще раз.
Продолжим… Итак, Вы установили брейкпоинт и теперь SoftICE будет “выскакивать” при каждом вызове функции GetWindowTextA. Попробуем ввести какое-нибудь значение в окне регистрации и нажмем OK. Вы нажимаете OK…
Значит, это была не функция GetWindowTextA… Попробуем GetDlgItemTextA.
Удалим старый брейкпоинт:
:bc 0
(0 - это номер брейкпоинта в списке брейкпоинтов)
И установим новый:
:bpx getdlgitemtexta
Ну что ж, попробуем еще раз…
3.1.3 В отладчике
Wow! Работает! Теперь вы в SoftICE, в самом начале функции GetDlgItemTextA.
Чтобы попасть туда, откуда она была вызвана, нажмите F11. Теперь Вы
внутри модуля SGLSET.EXE. Если Вы не уверены - посмотрите на строчку
между окном кода и окном командной строки, она должна выглядеть так:
Сейчас Вы уже можете запретить реакцию на вызов функции:
:bd 0
Если Вам вдруг захочется снова разрешить ее, наберите:
:be 0
Первая строка в окне кода выглядит так:
CALL [USER32!GetDlgItemTextA]
Чтобы посмотреть строчки над ней, нажимайте Ctrl+Up
(”стрелка вверх”) до тех пор, пока не увидите нижеприведенный кусок
кода. Если Вы ничего не понимаете в Ассемблере, я добавил комментарии
которые могут Вам помочь.
Команды PUSH означают сохранение значений для последующего использования.
Я пометил важные строчки символом ‘>’. Глядя на этот код, мы видим, что адрес текстового буфера хранился в регистре EAX и что EAX был EBP-34h.
Поэтому нам стоит взглянуть на EBP-34h:
:d ebp-34
Вы должны были увидеть текст, который вы ввели в
диалоговом окне. Теперь мы должны найти место, где Ваш номер
сравнивается с реальным серийным номером.
Поэтому мы пошагово трассируем программу при помощи F10 до тех пор,
пока не встретим что-нибудь о EBP-34. Не пройдет и нескольких секунд,
как Вы
наткнетесь на следующий код:
Мне кажется, что это выглядит как вызов функции сравнения двух строк.
Эта функция работает так: на входе - две строки, на выходе - 0, если они равны и любое другое значание, если не равны.
А зачем программе сравнивать какую-то строчку с той,
что Вы ввели в окне диалога? Да затем, чтобы проверить правильность
Вашей строки (как Вы, возможно, уже догадались)! Так-так, значит этот
номер скрывался по адресу
[EBP+FFFFFF64]? SoftICE не совсем корректно работает с отрицательными числами и поэтому настоящий адрес следует посчитать:
100000000 - FFFFFF64 = 9C
Вы можете сделать это вычисление прямо в SoftICE:
0-FFFFFF64
Число 100000000 слишком велико для SoftICE, а вычитание из 0 дает тот же самый результат.
Наконец пришло время взглянуть, что же скрывается по адресу EBP-9C…
:d ebp-9c
В окне данных SoftICE Вы видите длинную строчку цифр - это серийный номер!
Но Вы помните, что я говорил Вам раньше? Два типа регистрации - два
разных серийных номера. Поэтому после того, как Вы записали на
бумажечку первый серийный номер, продолжайте трассировать программу при
помощи F10. Мы дошли до следующего куска кода:
И что Вы видите по адресу EBP-68? Второй серийный номер!
:d ebp-68
Вот и все… Я надеюсь, что у Вас все получилось как доктор прописал? =)
3.2 Command Line 95 - легкая регистрация “имя-код”, создание генератора ключей
Это программа - хороший пример, с легким алгоритмом генерации кода.
3.1.1 “Обследование”
Вы осмотрели программу и увидели, что это 32-битное приложение, требующее имя и код в окне регистрации. Поехали!
3.1.2 Прерывание программы
Мы поступаем так же, как и с Task Lock’ом - ставим брейкпоинты. Можно
даже поставить сразу два брейкпоинта на наиболее возможные функции:
GetWindowTextA и GetDlgItemTextA. Нажмите Ctrl-D, чтобы вызвать отладчик и наберите в окне команд:
:bpx getwindowtexta
Теперь возвращайтесь в прерванную программу, идите в
окно регистрации и вводите имя и какой-нибудь номер (обыкновенное целое
число - это наиболее вероятный код). Я написал примерно следующее:
Программа остановилась на GetDlgItemTextA.
Так же, как и в случае с Task Lock’ом, мы нажимаем F11 чтобы вернуться
в вызывающюю функцию. Просматриваем окно кода при помощи Ctrl+Up. Вызов
функции выглядит так:
Число 40A680 кажется нам интересным, поэтому мы проверяем этот адрес:
:d 40a680
Что же видно в окне данных, как не имя, которое мы ввели? =) А теперь взглянем на кусок кода под вышеприведенным:
Функция GetDlgItemInt похожа на GetDlgItemTextA,
но возвращает не строку, а целое число. Она возвращает его в регистре
EAX, поэтому мы трассируем этот код (F10) и смотрим, что же у нас
появилось в окне регистров после вызова функции… В моем случае оно
выглядит так:
А что такое шестнадцатеричное 3039? Наберем:
3039
И получим следующее:
^ hex ^ dec ^ ascii
Как Вы видите (и, возможно, уже догадались) это код, который Вы ввели в диалоговом окне. Ok, что теперь? Посмотрим дальше:
3.1.3 Подсчитывание регистрационного кода
Мы достигли места, где подсчитывается реальный регистрационный код!
…И где он проверяется
Когда Вы дотрассировали до сравнения чисел, Вы можете посмотреть, каким должен был быть Ваш РЕАЛЬНЫЙ регистрационный код:
ecx
В моем случае это дало:
То есть, правильный код для меня: 901324.
Нажмем F5 или Ctrl-D чтобы вернуться в программу и попробуем еще раз, но на этот раз с правильным кодом (в десятичной форме). Работает!
4. СОЗДАНИЕ ГЕНЕРАТОРА КЛЮЧЕЙ ДЛЯ COMMAND LINE 95
Взглянем на алгоритм генерации кода и попробуем перевести его на язык Си.
Вот очень простая формула, по которой подсчитывается ключ:
code = ((uppercase_first_char * length_of_string) << 0×0A) + 0×2f8cc;
Целиком программа на Си выглядит так:
4. КАК РАБОТАЮТ PUSH И CALL КОГДА ПРОГРАММА ВЫЗЫВАЕТ ФУНКЦИЮ
Снова взглянем на кусок кода из Task Lock’а:
Когда Вы вызываете функцию GetDlgItemTextA из программы на C, вызов выглядит так:
GetDlgItemTextA(hwndDlg, 0×3F4, buffer, 0×32);
PUSH сохраняет данные в области памяти, называемой стеком. В результате каждого PUSH‘а
новый кусок данных помещается в верхушку стека и затем вызываемая
функция проверяет, что лежит в стеке и использует эти данные по своему
усмотрению.
5. О ПРОГРАММАХ НА VISUAL BASIC
EXE файлы, производимые Visual Basic’ом, не являются настоящими EXE.
Они просто содержат код для вызова VBRUNxxx.DLL, который затем читает
данные из EXE и выполняет программу. Такое устройство псевдо-EXE файлов
является также причиной того, что программы на Visual Basic’е такие
медленные.
А так как EXE файлы не являются настоящими EXE файлами, Вы не можете
трассировать и дизассемблировать их - Вы найдете вызов функции из DLL и
кучу мусора. И когда Вы будете трассировать такую программу, Вы
“заблудитесь” в DLL.
Решением этой проблемы является декомпилятор. Существует декомпилятор
для программ, написанных на Visual Basic’е версий 2 и 3, созданный
кем-то, называющим себя DoDi. Эта программя является шареварной и ее
можно найти в InterNet’е (см. Приложение C). Для программ, написанных
на Visual Basic’е
версии 4 (VB для Windows 95), не существует декомпилятора, насколько мне известно, хотя я бы хотел, чтобы он существовал. =)
Примечание: Настоящие программисты на пишут на Basic’е. =)
ПРИЛОЖЕНИЯ
A. КАК В SOFTICE ЗАГРУЖАТЬ СИМВОЛЬНЫЕ ИМЕНА
Чтобы проверить, загрузил ли SoftICE символьные имена GetWindowText, Вы
должны войти в отладчик нажатием на клавиши Ctrl-D и в окне команд
ввести следующее:
:exp getwindowtext
Вы можете удалить комментарии из всех строчек ‘exp=’ или сохранить немножко памяти, раскомментировав только строчки с файлами kernel32.dll, user32.dll и gdi32.dll, которые являются самыми важными. После этого Вы должны перегрузить компьютер.
B. СИНТАКСИС НЕКОТОРЫХ ФУНКЦИЙ
Вам будет легче понять, как вызываются функции, о которых мы говорили, если Вы будете знать их описания (декларации):
Если Вам нужна более подробная информация, посмотрите в руководстве программиста Windows/Win32.
Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи.
Отладчики
Отладка приложения — это неотъемлемая часть процесса исследования, инструмент, который всегда под рукой у реверсера. В современном мире отладчик должен поддерживать обе интеловские архитектуры — x64 и x86, из этого мы и будем исходить.
Также у нас должна быть возможность отлаживать код, который работает в режиме ядра. Такая нужда периодически возникает, особенно если ты намерен искать zeroday-уязвимости в ядре ОС или реверсить драйверы вирусов. Основных претендентов два: x64dbg и WinDbg. Первый отладчик работает в режиме user mode, второй может отлаживать код в режиме kernel mode.
x64dbg
Этот современный отладчик с весьма приятным интерфейсом — достойный преемник OllyDbg. Поддерживает обе архитектуры — x64 и x86, обладает массой полезнейших плагинов.
x64dbg
Встроенный декомпилятор
Да, безусловно, он не лишен недостатков — в нем до сих пор есть несколько неприятных багов. Однако он активно поддерживается и развивается. Разумеется, из-за того что отладчик работает в пользовательском режиме, он остается уязвимым для многих техник обнаружения отладки. Но этот минус отчасти компенсируется разнообразием плагинов для сокрытия отладчика.
У x64dbg есть встроенный декомпилятор, поддерживается отображение кода в виде графа, можно делать точки останова на чтение, запись, выполнение и доступ, имеется встроенная утилита реконструкции импортов (как x64, так и x86). В общем, что говорить — этот отладчик использовался в узких кругах для того, чтобы победить небезызвестную игровую защиту Denuvo, и успешно справляется с этой задачей!
Почему не OllyDbg
В подборку не попал отладчик OllyDbg — по той причине, что он уже серьезно устарел. Он не поддерживает ни современные ОС, ни архитектуру x64. На официальном сайте приложения был анонс 64-битной версии и даже сообщалось о прогрессе в ее разработке, но сам сайт обновлялся в последний раз в 2014 году. Безусловно, с OllyDbg связана целая эпоха, но, по всей видимости, она прошла. Да и отладчиков kernel mode тоже поубавилось — разработчики забросили Syser Kernel Debugger, а он в свое время был преемником SoftICE.
WinDbg
Если нужно отлаживать ядро или драйвер, то WinDbg нет равных. Этот отладчик поддерживает сама Microsoft, и он входит в состав Windows Driver Kit (WDK). На данный момент это самое актуальное и мощное средство отладки кода ядра. Здесь нет такого приятного интерфейса, как в x64dbg, но и выбора у нас немного — другие отладчики не работают в kernel mode.
WinDbg поддерживает удаленную отладку и умеет скачивать отладочные символы напрямую с серверов Microsoft. Чтобы быстрее настроить его для отладки ядра ОС внутри виртуальных машин, существует надстройка VirtualKD. Безусловно, начинать путь реверсера с WinDbg строго противопоказано, но, когда наберешься опыта и начнешь пробовать разные интересные вещи, он становится необходимостью.
Именно в WinDbg можно запросто посмотреть, как выглядят те или иные системные структуры, и легко дизассемблировать функции NTAPI. Конечно, им можно отлаживать и «обычные» приложения, но лично я предпочитаю распаковывать столь могучий инструмент только при крайней необходимости! 🙂
Дизассемблеры
Сложно представить себе реверс без инструментов статического анализа кода. На сегодняшний день дела с дизассемблерами обстоят немногим лучше, чем с отладчиками, но все-таки можно выделить фаворитов в этой области. Признанный стандарт антивирусных лабораторий — это дизассемблер IDA Pro. Второе место по востребованности занимает фреймворк для реверс-инжиниринга Radare2 (хотя многие считают, что Radare2 не уступает IDA).
IDA Disassembler
Существует две версии IDA — платная (Pro) и бесплатная (Starter). Бесплатная версия урезана по количеству поддерживаемых архитектур — она понимает только x86, кроме того, она не поддерживает плагины. Платная версия лишена подобных ограничений: она поддерживает внушительное количество архитектур процессоров и позволяет подключать расширения.
В IDA есть встроенный отладчик, весьма простенький по набору функций, но к его самобытному интерфейсу придется приноровиться. Также IDA может быть укомплектован дополнением Hex-Rays — декомпилятором исходного кода приложения в код на C. Это полезнейшее дополнение, которое значительно ускоряет анализ программы.
В целом IDA — мощнейший и прекрасно отполированный инструмент, который развивался много лет. Жаль только, что профессиональная версия стоит в районе 500–1000 долларов в зависимости от вида лицензии и кому попало не продается. Кто попало в результате выкручивается как может. 🙂
Продолжение доступно только участникам
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Читайте также: