On-Line Библиотека www.XServer.ru - учебники, книги, статьи, документация, нормативная литература.
       Главная         В избранное         Контакты        Карта сайта   
    Навигация XServer.ru








 

Исследование ReGet 1.3.2.

  На безрыбье и рак - щука
Народная мудрость.

К чему такой эпиграф ? А к тому, что на развелось слишком много народу, считающих себя более умными, чем другие; оно, конечно, что-то в этом может быть и есть. Однако, что-то мало кто из них может написать описание препарирования программы в понятной форме1). В общем, это их проблемы, а свою проблему в виде этой статьи я выношу на всеобщее обозрение
Отчего бы не сломать более новую версию ? - спросит меня пытливый читатель. И будет совершенно неправ. Самая главная причина в том, что я живу в той же поганой стране 2), что и авторы сей не самой плохой программы на свете, и испытытываю на собственной заднице те же прелести проживания здесь, что и они. Короче, сокамерники мы, как бы. Так что зачем же так гадить людям - им ведь тоже нужно бабки делать как-то, вот когда следующая версия ReGet появится, тогда и 1.40, может быть, посмотрю. А пока, если шило в одном месте не даёт покоя, можете сами попробовать.
Значится, программа зовётся ReGet, v 1.3.2, взять можно много где, можно попробовать Две Коровы или KGB.ru. Умеет она докачивать порванные соединения по HTTP (если сервер на том конце провода двумя руками поддерживает протокол HTTP 1.1).
Всем хороша данная программа, но есть у неё маленький недостаток (как любят говорить ребята от Билли "feature") - начинает надоедать через 30 дней диалоговым окошком - дескать, зарегистрируй меня etc. А чтобы не было такого, просит зарегистрироваться. O`k.
Запускаем программу, выбираем в главном меню "?", "Регистрация ReGet"
Появляется диалоговое окно "Регистрационная информация". Заносим туда имя, свой e-mail - о-па, а что это за третье поле такое? "Регистрационный код" !
А сиё чудо где взять ? Нету ? Непорядок. Надо свою программу написать, чтоб такой генерила.
Однако, скоро сказка сказывается... Короче, как советуют на Fravia, налейте себе, если со вчерашнего осталось, и приступим.
Для начала, жизненно необходимо выяснить, с какого же места собственно и начинается обработка введённого пароля. Запускаем струмент - SI. Ставим стандартный набор 3)
 bpx GetWindowTextA
 bpx GetDlgItemTexA
 bpx MessageBoxA
 bpx MessageBoxIndirectA
Жмём педаль - и вот оно, всплываем по вызову GetWindowTextA; жмём F12 - и оказываемся по неважно какому адресу, зато важно, в каком модуле - mfc42. Значит, программа написана с применением MFC и это есть некая функция CWnd::DoDataExChange, в которой посредством DDX_ функций происходит извлечение введённых пользователем значенийRTFM).
Чтобы не всплывать зря ещё несколько раз на тех же граблях, отключим точку прерывания: bd 0. Жмём F12, пока не окажемся снова в модуле ReGet - адрес 0x4143B9. Самое время нажать F5 и запустить IDA Pro. Я не буду приводить листинг, который она сгенерировала для данного адреса, по следующей причине - это действительно переопределённая функция CWnd::DoDataExChange для данного конкретного диалога, в ней нет ничего интересного (несколько DDX_TextRTFM))
Ну что же, не много полезного мы выяснили на первый раз. Придётся повторить, но на этот раз жмём F12 дальше, пока снова не окажемся в модуле ReGet - на это раз по адресу 0x414492. Вот это уже интересно. Ну-ка, что нам IDA показывает (полный листинг я опять-таки не привожу4), только вызовы функций (комментарии, естественно, мои):
Уж сколько раз говорено было... Короче, старо как Zip, если по возврате из call 41410F регистр eax не есть 0 - мы хорошие парни, и неплохо было бы закрыть диалоговое окно вызовом CDialog::EndDialog, иначе - мы плохие парни, на нас обиделись и нужно непременно высказаться вызовом AfxMessageBox (MFC-вариант обычного MessageBox). Всё ясно, нужно препарировать процедуру 41410F
Для начала, давайте дадим ей имя позвучнее, скажем, VerifyUs. Для этого подведём курсор на строку, содержащую фразу "proc near" на 4144E2 после адреса и нажмём N и в появившемся окне дадим волю фантазии5)
Процедура, опять же, слишком длинная и мне было утомительно приводить её здесь полностью, так что приведены только необходимые для понимания моменты:
 41410F		push eax, offset loc_42003C
 414114		call __EH_prolog
Стандартная для C++ инициализация exception frame handlerа, в стек помещается адрес функции очистки стека, которая вызовется при возникновении неперехваченного исключения и вызывается функция инициализации нового фрейма обработки исключений
Дальше какая-то не относящаяся к делу лажа, ага, вот интересное местечко:
 414136		lea ecx, [ebp+10h]
 414139		push offset a_contik_
 41413E		call CString::Find(char const *)
 414143		cmp eax, 0ffffffffh
 414146		jz short loc_41415C
По адресу a_contik_ находится строка "_contik_". Если эта строка не найдена в некоей строковой переменной класса CString (то есть, вызов CString::FindRTFM) вернул -1), выполнение переходит на 41415C. Ну-ка проверим под SI, чего это тут делается... Ага, прикол от программистов (сами посмотрите, если не лениво - считайте это домашним заданием :-). Ну ладно, поstepали дальше Не разобраться без (нет, не без пол-литра) SoftIce. Путём нескольких прогонов под отладчиком выяснилось кое-что, что я описал в комментариях (уж придётся вам поверить моему внутреннему голосу). Просмотрев этот фрагмент даже бегло, уже можно выяснить формат "Регистрационного кода" (в дальнейшем РН) - он не должен содержать пробелов и быть в длину ровно 10h = 16 символов. Посмотрим на процедуру sub_4140ED - ей передаётся в качестве параметра байт из РН
В строке a92bc4hjkldfw5z (как IDA иногда развозит) "9#2BC4HJKLDFW5Z$6G8MNXPQR7S3ETAU". Значит, по возможности РН должен состоять из таких символов в верхнем или нижнем регистре (не забывайте про вызов toupperRTFM)).
Замечание: инструкция movsx делает следующее: в самую младшую часть регистра загружается байт, а остальная часть регистра заполняется знаковым битом этого байта, то есть в данном случае, при использовании символов, меньших 127 (латинских букв и/или цифр) это будет всегда 0.
Сечас преобразованный символы РН лежат в буфере по адресу [ebp-20h]. Далее
Какую любопытную вещь мы сдесь наблюдаем ! Сильно это похоже на проверку правильности введённого РН. Не иначе как функция sub_416691 считает hash-код по нашему преобразованному РН, затем результат обрезается до 5 младших бит и сравнивается с 7мым преобразованным символом. Если они не равны - переход на уже знакомый нам адрес, возвращающий 0 (признак наличия плохих парней). Переходим на функцию 416691 и называем её, скажем, get_hash (я также обозвал и все её аргументы для большей наглядности:
Происходит здесь следующее: цикл по байтам ключа, число итераций цикла - длина ключа, передаваемая как второй параметр, при этом происходит изменение некоторого initial_seed. После цикла его значение инвертируется, и возвращается как результат. Попутно можно заметить, что для подсчёта hash-code используется массив dword, максимальная длина которого равна 256 элементам (или 4 * 256 = 1024 байт)
Замечание 1: инструкция movzx загружает байт в самую младшую часть регистра, а остальные биты регистра заполняет нулём.
Замечание 2: команда not, в отличие от neg, не влияет на значения флагов, поэтому можно сначала сравнить, потом инвертировать, а затем воспользоваться результатами сравнения.
Хорошо, смотрим далее:
Я надеюсь, что после всех моих комментариев уже должно быть понятно, как написать Key Generator. Он должен преобразовывать имя пользователя и E-Mail в верхний регистр, затем с помощью get_hash вычисляется по ним hash, далее пятёрки бит складываются в некоторый буфер, он дополняется до 16 байт каким-нибудь значением из допустимых6), в седьмой сивол буфера помещается код 0x42, затем считаем hash по нашему заполненному таким хитрым образом буферу (опять с помощью get_hash), оставляем от него младшие 5 бит, помещаем их в 7 символ, и, наконец, делаем обратное преобразование из кодов в удобочитаемые символы.
Однако, для полноценного crackа нам необходимо иметь массив Array в нашем KeyGenerator. Размер этого массива составляет около 1 Kb. Можно ,конечно, набить его вручную, и, когда я окончательно впаду в старческий маразм, я именно так и буду поступать. А пока я написал небольшую процедуру, которая сильно облегчит мне жизнь.
Note Если вы не понимаете, что делает данная (не самая сложная из писанных мною) процедура, лучше Вам бросить занятия Reverse Engeneeringом и сначала попробовать научиться простому Engeneeringу, потому что довольно часто приходится писать подобные болванки (например, в настоящий момент я занят ломкой программки XLNT (высокоуровневый сетевой коммандный язык сценариев для NT), и мне уже пришлось написать таких процедур штук примерно 6, а конца сему действу ещё и не видно...), и как Вы будете дальше жить - мне неизвестно 7)
Note 2 Функция, конечно, не Бог весть какая, тем не менее имеет смысл использовать её для выгрузки массивов данных в других проектах. Для этого нужно заменить нужными значениями a1, a2, offset, progName, и, возможно, outFile, а также заменить название и/или тип массива в первом fprintf
Ну а дальше всё настолько просто, что засим и разрешите откланяться...
Исходники и сам KeyGenerator можно выгрузить здесь
Кстати, чтобы сделать crack и написать этот несчастный KeyGenerator, у меня ушло около 4х с половиной часов, а на написание данного опуса - два дня...
  Ломать - не строить
Народная мудрость


1). Я лично с удовольствием прочитал бы несколько essays HiJackа о взломе программ, защищённых dongles; но ведь их нету в природе :-(
Back
2). Не думаю, что ещё остались люди, живущие здесь в счастливом неведении, что местное правительство на протяжении уже 80 (восьмидесяти) лет проводит геноцид против местного населения (как они его в последнее время стали называть презрительно-брезливо - "электорат". Если же такие люди ещё остались - мне не хочется общаться с ними дабы объяснять совершенно очевидные вещи...
Back
3). На самом деле, это чрезвычайно простой случай. Я просто решил, что, поскольку программа запускается и под Windows 95, и под NT (а у меня стоит Window NT 4.0 Workstation Rus), то в ней используются функции, использующие обычный однобайтовые строки - поэтому я ловлю функции API с суффиксом A. Если бы был .exe special for NT, имело бы смысл ловить функции с суффиксом W (Wide Char).
В тяжёлых случаях можно порекомендовать следующие варианты:
  • ShowWindow - вызывается, когда нужно показать какое-либо окно. Так как все controls, присутсвующие в, скажем диалоговом окне, в свою очередь являются опять таки окнами, которые также должны быть show при вызове диалогого окна, то вызовов ShowWindow может быть чересчур много, например у меня множество раз отлавливался этот вызов из функции ImageList_Add в COMCTL32.DLL, так что я не стал использовать этот метод.
  • Запустить примочку трассирования оконных сообщений от Visual C++ - Spy++, найти в ней диалоговое окно, посмотреть в ней HWND (внутренней идентификаторы) окон и далее перехватывать либо wm_gettext, либо, если программа переопределяет оконную процедуру controlов и сама обрабатывает нажатия кнопок, wm_keydown. Того же можно достичь не выходя из SI:
    task - перечисляет имена загруженных процессов (к сожалению у меня на NT эта команда выдаёт "No LDT" - я так и не смог победить, да и не смертельно это при наличии других инструментов)
    hwnd имя_процесса
    в появившемся списке отыскиваем что-нть типа Edit etc, затем
    bmsg идентификатор_окна wm_сообщение_для_отлова
  • Также в особо тяжёлых случаях может помочь следующее:
    Находим одним из вышеперечисленных методом идентификатор самого диалогого окна,
    ставим bmsg идентификатор_окна wm_command. Всплываем по нажатию какой-либо кнопки.
Если нужно отловить не банальный MessageBox, а что-нть похлёще, могут помочь:
  • DialogBoxIndirectParam, причём с этой функцией вообще очень интересно. Она вызывается для создания модального диалога. Но ! Дело в том, что по крайней мере на NT есть аж три такие функции (95 под рукой нет, проверить не могу). Как вы могли ожидать, есть DialogBoxIndirectParamA для ASCII-строк, DialogBoxIndirectParamW для Wide-Chars, и ещё есть недокументированная (возможно, лишь в моей документации - я использую для помощи по Win32 API Visual C++ 5.0) - DialogBoxIndirectParamAorW - и она очень часто срабатывает в программах, написанных с применением MFC.
    Для создания немодального диалога используется CreateDialogIndirectParam с аналогичным набором суффиксов
  • SetWindowPos - используется для изменения размера, позиции и Z-порядка окон. К ней применимы все замечания, относящиеся к ShowWindow
  • Можно также найти строку, которая выдаётся в диалоговом окне. Если она лежит в сегменте данных, можно просто поставить bpm адрес_строки. Но очень часто такие строки расположены в сегменте ресурсов. Малоизвестный факт, но на строки ресурса также можно ставить bpm. Краткое пояснение: все функции API для манипуляции ресурсами (LoadString, LoadResource etc) принимают в качестве первого аргумента HINSTANCE, который есть ни что иное, как указатель на начало отображенного в память .exe файла. Далее такие функции обрабатывают PE (или NE) заголовок загруженного модуля, находят сегмент ресурсов, и по переданному идентификатору находят нужный ресурс, который затем просто копируется во вновь выделенную память. Есть и свои грабли: ресурсы обычно (точнее, почти всегда) хранятся в Unicode. Я поступаю так - в IDA всегда указываю загружать секцию ресурсов, а затем помогает binary search с нужными значениями.
    Если лениво выискивать адрес ресурса - можно поставить bpx на LoadString и смотреть её параметры и возвращаемые значения (и ещё неизвестно, какой способ более трудозатратный)
Если программа никак не реагирует на неправильный Serial Number, можно попробовать найти строку, которая могла бы выдаваться на правильный. Я понимаю, что звучит чересчур расплывчато и никто, кроме авторов программы ,точно не знает, как она выглядит (да и авторы, наверняка, забыли уже), но всё же это лучше, чем совсем ничего. Здесь может помочь только обострённая голодом и хронической нищетой интуиция...
Ну и если ничего из вышеперечисленного не помогло - в конце концов, меня же зовут не Иисус Христос...RTFM)
Back
4) Ну что поделать - ленив я. Вам никогда не говорили, что лень - двигатель прогресса ?
Back
5) есть замечательное essay на fravia о том, как можно настроить IDA. Если Вы умеете настраивать IDA, вполне вероятно, что некоторых элементов у Вас не будет, или будут какие-нть другие. Однако в последнем случае Вы, видимо, не нуждаетесь в моих подсказках о именовании имён функций, их аргументов и проч.
Back
6) Я выбрал индекс сивола "$" - интересно, что бы сказал на это доктор Фрейд ?
Back
7). Это только в западных высокохудожественных полуфантастических фильмах всё выглядит красиво и эстетично - сидит этакий хукер и ломает программу, которая представлена на гигантском дисплее в трёхмерном виде (видимо, для наглядности). В жизни, как всегда, всё значительно прозаичнее - нужно много работать и иметь неитощимое терпение - короче, чтобы достичь вершины хотя бы невысокой кочки в Reverse Engeneering, Вы должны быть совсем ненормальным человеком, например, как я...
Back

RTFM) Если вторую неделю ничего не получается, прочти ,наконец, документацию. Я не верю в то, что человека ,позавчера впервые увидевшего контупер, можно научить ломать программы "за 21 день". Для этого необходимо как минимум (кроме подразумевающегося обязательного знания ассемблера) ещё и знание Win32 API хотя бы на уровне прикладного программиста, а также знание того framework, на котором написана предполагаемая жертва взлома (Delphi VCL + M$ MFC - минимум), и соответствующих языков программирования.


Языки программирования: разное