> For the complete documentation index, see [llms.txt](https://yutewiyof.gitbook.io/intro-rev-ida-pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yutewiyof.gitbook.io/intro-rev-ida-pro/chast-19.md).

# Часть 19

[\[Используемые материалы\]](https://github.com/yutewiyof/intro-rev-ida-pro/tree/e1367e11cc661f3d69c02ae5f733b7dd168bc5ab/.gitbook/assets/files/19.zip)

В этой главе, у нас уже достаточно знаний и умений, чтобы реверсить оригинальный крекми **CRUEHEAD**а. Поэтому открываем его в **ЗАГРУЗЧИКЕ**, выключая опцию **MANUAL LOAD**. Исходный файл не упакован, так что нет необходимости загружать его вручную.

![](/files/-Ljtu1LwWFlu6J9v6Xjp)

Загрузчик остановился здесь. В нашем случае, поскольку это не консольное приложение, то нужно не только анализировать функцию **MAIN**. Мы знаем, что в оконных приложениях существует **ЦИКЛ**сообщений, который обрабатывает взаимодействие пользователя с окном, его выполненные щелчки, нажатия клавиш, движения мыши и т.д. и согласно каждому действию пользователя, цикл запрограммирован для выполнения различных действий с помощью этого кода.

Уже знаем, что первую вещь, которую мы должны сделать - это попробовать найти строки. Если из этого ничего не выйдет, мы должны искать **API** функции или функции, которые использует программа. В нашем случае, строки хорошо видны. Так что будем следовать этому пути.

![](/files/-LjqtGWC3KuEIIzBNKqg)

Переходя в строку **NO LUCK**, мы попадаем в область, где программа принимает какое-то решение. Для этого делаем двойной щелчок по этой строке и попадаем в это место.

![](/files/-LjqtGWEYE-EYplSWA_O)

Я могу видеть перекрёстные ссылки с помощью нажатия на клавишу **X** или **CTRL + X**

После нажатия на эту клавишу, видно, что существует две перекрестные ссылки на эту строку.

![](/files/-LjqtGWGQ9YhHupSyZWi)

Давайте посмотрим первую из них.

![](/files/-LjqtGWIGC_8Q0iRP9Wz)

Я закрашиваю этот блок в красный цвет, так как из-за этих инструкций происходит ошибка или плохое сообщение.

![](/files/-Ljtu1NDrX-ij5mBRtSX)

Давайте посмотрим, как в него можно попасть из программы.

![](/files/-Ljtu1NRvaaWgRhl6JpC)

Из этой картинки видно, что соседний блок должен вести в хорошее сообщение. Давайте посмотрим, что внутри этой функции по адресу **0x40134D**.

![](/files/-Ljtu1N_2r6xMozF-zGc)

После закрашивания, листинг будет выглядеть так.

![](/files/-Ljtu1NiAOlZu4JSp9ea)

Другая перекрёстная ссылка на строку **NO LUCK** указывает сюда.

![](/files/-Ljtu1NtylWpiPms1GC-)

В другое плохое сообщение можно попасть отсюда.

![](/files/-Ljtu1O4vKLzS1-eKptQ) ![](/files/-LjqtGWWgCcxbYZpZ0TG)

Видим, что аргумент, который передаётся в эту функцию, это адрес (**OFFSET, СМЕЩЕНИЕ, прим. Яши**) глобальной переменной, которую мы будем называть **STRING**. Если сделаем щелчок по этой переменной, то перейдём к адресу где она размещена в программе.

![](/files/-LjqtGWYhbuIYFoc44ld)

Видно, что это буфер длиной **3698** байт расположенный по адресу **0x40218E** находящийся в секции **DATA**.

Он имеет две ссылки. Чтобы увидеть, где в коде идёт работа с этой переменной, нажимаем **X**.

![](/files/-LjqtGW_TrIw0-GPQ-l6)

Видим, что существует перекрёстная ссылка, которая ведёт сюда.

![](/files/-Ljtu1OpDFz_uN7VRYVN)

**API** Функция **GetDlgItemTextA** используется для ввода каких либо данных в программу. Давайте посмотрим информацию про неё в **MSDN**.

![](/files/-Ljtu1P-ontcmQJR5u_0)

Из описания понимаем, что функция помещает в буфер некоторый текст, который введён в контрол с помощью клавиатуры.

![](/files/-LjqtGWfrvay4VlxqUWC)

Видно, что существует две записи с тем же дескриптором **HWND**. Следовательно, я предполагаю, что они должны быть полями для ввода имени пользователя и пароля, которые поступают в крэкми, когда мы нажимаем кнопку в окне **REGISTER**.

![](/files/-Ljtu1PL8-tJfGQL8G9C)

Также, видно, что они имеют такие номера контрола **nIDDlgItem** : **0x3E8** и **0x3E9**.

![](/files/-Ljtu1PSfATdkHNlDY5X)

Используя программу **GREATIS WINDOWSE**, можно получить информацию о тех окнах, над которыми находится курсор. Взять её можно здесь.

<http://www.greatis.com/wdsetup.exe>

![](/files/-Lmttt-I-8EvSC47toHg)

Я вижу, что верхний **EDIT BOX CONTROL** равен **0x3E8**, а нижний - **0x3E9**.

Также, я могу переименовать буферы, в которые попадают введённые строки. Первый будет называться **STRING\_USER**, а второй **STRING\_PASSWORD**. Оба допускают только максимум **0x0B**символов, несмотря на то, что имеют буферы намного больше.

![](/files/-Ljtu1Pau3u1CVE9BMwm)

Хорошо, мы уже знаем где программа сохраняет введенный аккаунт, который я вводил. Посмотрим, что она будет с ним делать.

Мы уже видели, что программа обрабатывает буфер **STRING\_USER** здесь.

![](/files/-Ljtu1PmcdT5LnWIx52l)

Давайте анализировать, что программа делает здесь с этим буфером, но прежде, мы можем изменить имена функций, так как по-видимому первая обрабатывает буфер **STRING\_USER**, а вторая функция обрабатывает буфер **STRING\_PASSWORD**.

![](/files/-Ljtu1PyemUChxsPsjms)

Сейчас давайте анализировать функцию **PROCESA\_USER**.

![](/files/-Ljtu1Q9kMa8H5XbwfHC)

При переименовании переменной, я использую то же самое имя, которое выбрала **IDA**, хотя я мог бы написать **P**\_**STRING**\_**USER**, так как это также указатель на буфер.

С помощью **SET TYPE** я меняю тип функции и её аргументы и обращаю внимание, чтобы аргументы распространились в комментарии.

![](/files/-Ljtu1QKr6va3PuW19G4)

Видим, что пояснение, которое я добавил, совпадает с именем аргумента.

![](/files/-Ljtu1QVx5BS6aNTUDrB)

Видно, что существует **ЦИКЛ**, который будет читать **БАЙТЫ** буфера **STRING\_USER**. **ЦИКЛ** будет повторяться, пока он не будет равен нулю, т.е. пока не закончится строка и тогда программа сможет перейти на **ЗЕЛЁНУЮ** стрелку.

Здесь Вы видите **ЦИКЛ**. Он увеличивает **ESI**, чтобы читать побайтно каждый символ буфера **STRING\_USER**, и сравнивает каждый из них с числом **0x41**.

![](/files/-LjqtGWxqN_9hh5IcWj6)

![](/files/-Ljtu1QsXOJ5RQrOnrF_)

Мы можем сделать правый щелчок по числу **0x41** и изменить его на символ **A**, который является символом **ASCII** для этого значения.

![](/files/-LjqtGX0IExBemMJszIN)

Если значение в **AL** ниже, чем символ **A**, программа перебросит нас в зону **NO LUCK**. Если мы посмотрим в таблицу **ASCII**, увидим, что программа не принимает числа в имени **ПОЛЬЗОВАТЕЛЯ**, а только буквы, так как они больше или равны **A**.

![](/files/-LjqtGX26W1PLvTIN4L7)

Так что, программа проверяет, чтобы все символы буфера **STRING\_USER** были больше чем **0x41**, т.е. больше или равны символу **A**.

![](/files/-LjqtGX4K3ZVeTDxH16Y)

Программа, также, с помощью инструкции **JNB** проверяет, чтобы символ был не ниже символа **Z** и если это так передаёт управление на **БЛОК** по адресу **0x401394**. А если иначе, берет следующий символ и повторяет цикл.

Таким образом, программа обрабатывает все заглавные символы за исключением **Z**. Если символ больше или равен **Z**, то программа переходят в блок по адресу **0x401394**. Давайте посмотрим, что там делает программа.

![](/files/-LjqtGX6Yk4wZVmteb87)

Я назвал эту функцию **RESTA\_20**, потому что это то, что она делает. Если символ больше символа **Z**, программа вычитает из него число, сохраняет результат и выходит из функции.

![](/files/-Ljtu1RpzkYMIicw-NJP)

Другими словами, если вы введете символ с кодом **0x61**, что является маленькой буквой "**a**", программа вычтет из значения **0x20**, и получится значение **0x41**, что является большой буквой "**A**". Программа делает то же самое со всеми символами, которые больше или равны **Z**.

Если символ равен **Z**, то вычитая из него значение **0x20**, получим результат **0x3A**, что является символом двух точек "**:**"

![](/files/-Ljtu1Rz3Z_5wxFrq1zl)

Вопрос таков. Можем ли мы уже создать скрипт для **PYTHON**, чтобы получить кейген?

```python
user=raw_input()
largo=len(user)

if (largo > 0xB):
    exit()

USERMAY=""
for i in range(largo):
    if (ord(user[i]) < 0x41):
        print "CARACTER INVALIDO"
        exit()
    if (ord(user[i]) >= 0x5A):
        USERMAY+= chr(ord(user[i])-0x20)
    else:
        USERMAY+= chr(ord(user[i]))

print "USER",USERMAY
```

Мы видим, что скрипт делает то же самое, что и программа. Он берет по одному символы строки **ПОЛЬЗОВАТЕЛЯ** и сравниваем их с кодом **0x41**. Если символ меньше, он говорит нам, что это недопустимый символ и переносит нас на **ВЫХОД**. Но если он больше или равен **0x5A**, то программа вычитает из него **0x20** и добавляет его к строке **USERMAY**.

Мы видим, что если я введу имя **pePP**, программа транслирует его в имя **PEPP**.

![](/files/-Ljtu1S9GniK49OWijls)

А если я ввожу символ **Z**, скрипт преобразовывает его в символ **":"** как мы и говорили выше.

![](/files/-LjqtGXEbssw9LeVDFZf)

До этого момента, скрипт делает то же самое, что и программа. Посмотрим, что делает программа после выхода из **ЦИКЛА**. Она продолжает выполняться здесь.

![](/files/-LjqtGXG1ak26LiXeFpm)

Когда крекми найдёт символ, который равен нулю, он покинет **ЦИКЛ** и перейдёт к блоку по адресу **0x40139C**.

![](/files/-Ljtu1Sb6j2aovwyEnFJ)

Видим, что перед увеличением **ESI**, крекми **КЛАДЕТ** его в стек, чтобы сохранить исходное значение, которое указывает на начало строки, и затем с помощью инструкции **POP ESI** программа восстанавливает регистр **ESI**, перед тем как войти в функцию по адресу **0x4013С2**.

![](/files/-Ljtu1Sm_kmZW_bIvFjY)

Мы видим, что это **ЦИКЛ**, который складывает все байты, поэтому я буду называть его **SUMATORIA**(**СУММА**), так что мы можем добавить этот блок в наш скрипт.

![](/files/-LjqtGXM7MqfHuoCzu-r)

![](/files/-LjqtGXOKjGHhD49Jbv0)

Скрипт суммирует все байты и печатает сумму.

Чтобы проверить правилен ли скрипт, я помещаю **BP** на следующей строке после вызова функции **SUMATORIA** и ввожу **pepe** в поле **user** и **989898** в поле **password**.

![](/files/-LjqtGXQGHKu_MkKUMm3)

И вижу, что получается сумма равная **0x12A**, так что всё работает правильно.

В этой строке, сумма **XOR**ится с помощью ключа **0x5678**, поэтому я добавляю это выражение также в скрипт.

![](/files/-Ljtu1TQLFiRe66_khB5)

Если я выполню эту строчку, скрипт про**XOR**ит сумму и результат будет такой же, как и в программе.

![](/files/-Ljtu1TbmB8GFTragbRK)

![](/files/-Ljtu1Tl-51fnTuomxUb)

Затем крекми переносит результат из **EDI** в регистр **EAX** и выходит из этого блока.

![](/files/-Ljtu1Twao-DBrAYro23)

Затем программа **ПОМЕЩАЕТ** регистр **EAX** в стек и восстанавливает его с помощью инструкции **POP EAX** перед окончательным сравнением. Другими словами в инструкции **CMP EAX**, **EBX**, первым членом будет это значение, которое поступает из функции **PROCESA\_USER**.

![](/files/-LjqtGX_EvEMFhYjEbum)

Сейчас давайте посмотрим, что программа будет делать с паролем в функции **PROCESA\_PASS**.

Войдя в неё, мы увидим такой код.

![](/files/-LjqtGXbdvP57QTmh9xD)

Здесь программа считывает каждый байт и помещает его в регистр **BL** и вычитает из этого значения число **0x30**, которое остается в регистре в **EBX**, затем умножает **EDI** на **0x0A** и суммирует полученное значение с **EBX**.

Я дополняю следующую часть скрипта с помощью этого кода.

![](/files/-Ljtu1US909zPBHmlAY_)

Я не ввожу пароль с клавиатуры. Я просто так его тестирую, потому что в кейгене, мы просто вводим пользователя, но я вижу, что если мой пароль равен например числу **989898**.

**SUM2**, это переменная, в которой хранится сумма умноженная на **0xA** и затем к ней прибавляется очередной байт, из которого вычитается значение **0x30**.

![](/files/-Ljtu1UbFypRTwr9IKRi)

Выполнив скрипт, я вижу, что для пароля **989898** у меня получается результат **f1aca**, что является **HEX** значением строки **989898**.

![](/files/-Ljtu1UmTDsvMfiV1pgA)

Всё это в нашем скрипте может сводиться в конвертирование строки в **HEX** с помощью функции **HEX()**.

![](/files/-LjqtGXjrbn5nzJCnDI0)

Скрипт даёт мне точно такой же результат.

![](/files/-LjqtGXlwxJ9uNqK3F-Y)

Наконец, скрипт **XOR**ит этот результат с помощью ключа **0x1234** и выходит из блока, чтобы сравнить результат со значением, которое возвратила функция **PROCESA\_USER** в **EAX**.

![](/files/-LjqtGXn3Fim95PrbLVS)

Так что общая формула будет такой:

**HEX(пароль) ^ 0x1234 = XOR**

Где **XOR** - это результат, который вернула функция **PROCESA\_USER**.

Немного изменим уравнение:

**HEX(пароль) = XOR ^ 0x1234**

Другими словами, если я **XOR**ю результат ключом **0x1234**, я уже почти получаю ответ.

![](/files/-Ljtu1VUZ6X7LvB2tcC6)

Если запустим скрипт со строкой **PEPE**.

![](/files/-LjqtGXrKuzOesH0oY5O)

![](/files/-LjqtGXtNOs9Orp9jEi1)

![](/files/-MX3mIu8jLgfBKLqeFo7)

Теперь, у нас уже есть кейген. И нет необходимости преобразовывать результат в десятичную форму, потому что **PYTHON** уже сделал это преобразование.

Здесь я копирую код в кейген.

```python
sum = 0
user = raw_input()
largo = len(user)
if (largo > 0xB):
    exit()
USERMAY = ""
for i in range(largo):
    if (ord(user) < 0x41):
        print "CARACTER INVALIDO"
        exit()
    if (ord(user) >= 0x5A):
        userMAY += chr(ord(user) - 0x20)
    else:
        USERMAY += chr(ord(user))

print "USER",USERMAY

for i in range(len(userMAY)):
    sum += ord (userMAY)

print "SUMATORIA", hex(sum)

xoreado= sum ^ 0x5678

print "XOREADO", hex(xoreado)

TOTAL= xoreado ^ 0x1234

print "PASSWORD", TOTAL
```

Даже в редких случаях с символом **Z**, получаем такой результат:

![](/files/-Ljtu1W3eHqt3QXNkcjb)

![](/files/-LjqtGXzb40oxexwZglh)

![](/files/-LjqtGY0B2EZ6umb1R13)

Таким образом, мы отреверсили и сделали кейген для крекми **CRUEHEAD**. Теперь мы увидимся с Вами в **20**-й главе.

До следующей главы, друзья.

Автор оригинального текста — Рикардо Нарваха.

Перевод и адаптация на английский язык — IvinsonCLS.

Перевод и адаптация на русский язык — Яша Яшечкин.

Перевод специально для форума системного и низкоуровневого программирования - WASM.IN

22.10.2017

[**Источник: ricardonarvaja.info**](http://ricardonarvaja.info/WEB/IDA%20DESDE%20CERO/CURSO%20DE%20IDA%20TUTES/19-INTRODUCCION%20AL%20REVERSING%20CON%20IDA%20PRO%20DESDE%20CERO.docx)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://yutewiyof.gitbook.io/intro-rev-ida-pro/chast-19.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
