5.34M
Категория: ПрограммированиеПрограммирование

Реверс-инжиниринг

1.

ТВЕРСКОЙ КОЛЛЕДЖ ИМ. А.Н. КОНЯЕВА
РЕВЕРС-ИНЖИНИРИНГ
г. Тверь
2022 год

2.

ПОНЯТИЕ РЕВЕРС-ИНЖЕНЕРИНГА
Реверс-инжиниринг дословно переводится на русский
язык, как «обратная инженерия» или обратное
проектирование. Это способ исследовать некоторое
готовому устройство или программу, а также
документации на него с целью понять принцип его
работы. Другими словами — имеется программа,
написанная
на
высокоуровневом
языке
программирования. Цель реверс-инженера — это
понять, как работает это программа. Но исходного кода
может
не
быть,
следовательно,
используют
дизассемблер, и смотрят код программы на
низкоуровневом языке ассемблер.
2

3.

РЕВЕРС-ИНЖЕНЕРИНГ КАК ОДНО ИЗ НАПРАВЛЕНИЙ ИНФОРМАЦИОННОЙ
БЕЗОПАСНОСТИ
При
реверс-инжиниринге
программ,
специалист
пытается понять, как устроена программа. Перед тем,
как заниматься дизассемблированием программы
необходимо понять, как работает защита если она есть, а
только дальше анализировать весь код и разбираться,
как он работает.
Реверс инжиниринг тесно связан с исследованием
вредоносного ПО. Вирус — это тоже программа, которую
можно исследовать.
3

4.

С ЧЕГО НАЧИНАТЬ ИЗУЧЕНИЕ РЕВЕРС-ИНЖЕНЕРИНГА
ИЗУЧИТЬ:
• Работу компьютерных сетей
• Работу с ОС Linux.
• Основы программирования. Можно начать с Python.
Также необходим Ассемблер.
• Работу составляющих компьютера, в частности
процессора, оперативной памяти и т.д.
• Основы информационной безопасности.
4

5.

ОСНОВЫ АССЕМБЛЕРА
классический набор инструкций процессора 8088:
Инструкция mov присваивает число, которое указано справа, переменной, которая указана слева.
Переменная — это либо один из регистров процессора, либо ячейка в оперативной памяти. С регистрами процессор работает
быстрее, чем с памятью, потому что регистры расположены у него внутри. Но регистров у процессора мало, так что в любом случае
что-то приходится хранить в памяти.
При написании кода на ассемблере необходимо решить, какие переменные хранить в памяти, а какие в регистрах. В языках высокого
уровня эту задачу выполняет компилятор.
У процессора 8088 регистры 16-битные, их восемь штук (в скобках указаны типичные способы применения регистра):
AX — общего назначения (аккумулятор);
BX — общего назначения (адрес);
CX — общего назначения (счетчик);
DX — общего назначения (расширяет AX до 32 бит);
SI — общего назначения (адрес источника);
DI — общего назначения (адрес приемника);
BP — указатель базы (обычно адресует переменные, хранимые на стеке);
SP — указатель стека.
5

6.

ОСНОВЫ АССЕМБЛЕРА
некоторые примеры типичных операторов языка ассемблера
6

7.

ИНСТРУМЕНТЫ РЕВЕРС-ИНЖЕНЕРИНГА
ДЕКОМПИЛЯТОРЫ
ИНСТРУМЕНТЫ МОНИТОРИНГА СИСТЕМЫ
7

8.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Загрузим исполняемый CrackMe-файл.
Запустим этот файл на тестовой машине.
Выводимые
строки
программы
сохраняются в дизассемблированном
файле, и мы можем отыскать по ним
необходимые куски кода
8

9.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Откроем файл CrackMe в программном комплексе IDA Pro.
Открыть его можно как с использованием вкладки File — Open, так
и перетаскиванием файла в рабочую область.
Файл открывается в окне дизассемблера, любые виды можно
настроить с помощью вкладки View — Open subviews.
Дизассемблер может показать информацию как в виде графа, так и
в виде текста. Для переключения между ними нажмите правую
кнопку мыши на рабочей области и выберите Graph или Text.
9

10.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Посмотрим на информацию в виде графа и на левой панели выберем функцию
DialogFunc. Эта функция позволяет программе общаться с нами с помощью
диалогового окна:
10

11.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Для определения той части кода, которая проверяет ввод серийного номера,
найдём, где в памяти программы хранится строка с сообщением об ошибке,
воспользовавшись инструментов поиска. Воспользуемся вкладкой Search — Text
или просто нажмём комбинацию Alt + T:
11

12.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
По слову serial было найдено несколько строк. Выберем ту, которая содержит
выводимое предупреждение, и перейдём по ней к коду ассемблера:
На графе мы видим, что искомая нами строка находится в нижнем блоке функции DialofFunc,
как результат её работы.
12

13.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Рассмотрим работу функции DialofFunc внимательнее:
loc_140001191 — это метка для перехода, ниже видим строку «call cs:MessageBoxA».
Предположим, так вызывается функция, которая отрисовывает окошко с текстом
«Fail, Serial is invalid !!!».
13

14.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Нажав на метку, мы увидим, что метка встречается чуть выше. Судя по графу, происходит
условный переход, где в случае правильного ввода мы продолжаем оставаться на метке
loc_140001143, а при ошибке переключаемся на метку loc_140001191.
14

15.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
На предыдущей метке осуществляется вызов функции «GetDlgItemTextA». Можем предположить,
что эта функция читает введённый серийник. После того как функция отработала, команда «lea
rcx, [rsp+78h+String]» загружает в регистр rcх адрес чего-то из стека. Затем записывает в edx
значение из eax и вызывает функцию sub_140001000.
Таким образом осуществляется передача аргументов в функцию, а после того как функция
отработала, командой «test eax, eax» выполняется проверка содержимого регистра eax. Если в нём
не 0, то подгружаются адреса строк, оповещающих о корректном вводе, затем запускается
отрисовка окошка с соответствующим ответом.
15

16.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Теперь можем восстановить логику работы программы CrackMe:
1.
2.
3.
4.
5.
Вызывается функция GetDlgItemTextA, она предположительно считывает наш
серийник.
Затем вызывается функция sub_140001000, которая в качестве аргументов
получает набор данных, который нам пока неизвестен, — это адрес чего-то из стека
и значение регистра eax через регистр edx.
Если функция возвращает «не ноль», то был введён правильный серийник,
если
«ноль», то
неправильный. Предположим, что
именно
sub_140001000 проверяет наш ввод.
16

17.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Для удобства переименуем функцию sub_140001000 в check_func. Так она будет
бросаться в глаза и нам будет удобнее с ней работать. Для того чтобы переименовать
функцию, выберите её и нажмите клавишу N. Теперь она будет переименована во всех
строках кода. Добавим комментарии к двум предыдущим строкам для удобства. Сделать
это можно кликнув правой кнопкой мыши на строчку кода и выбрав Comment.
17

18.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Чтобы узнать, какие
аргументы
передаются в нашу
функцию
можно
запустить программу
в
дебаггере
(отладчике)
и
проверить значения
регистров. Кликаем
на нашу функцию
правой
кнопкой
мыши и выбираем
Run
to
cursor.
Дебаггер пройдётся
по программе до
выполнения функции
check_func.
Сменился вид рабочей области: теперь мы видим значение регистров, дамп памяти и стек. Но ещё
мы видим, что наш файл снова запустился. Введём туда номер 12345 и посмотрим, что произойдёт
18

19.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Выполнение остановилось на вызове функции проверки. Это мы можем проверить в левом окне рабочей области. Но
нас интересуют значения регистров rcx и edx (он является младшей частью регистра rdx).
Проверяем rcx, в нём лежит значение 14F440 (у вас может содержаться другое значение), и rdx, он содержит число 5
19

20.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
перейдём в дамп памяти по адресу 14F440. Для этого нажмём правой кнопкой мыши в окне Hex View (слева внизу) и
выберем Synchronize with — RDX:
там сохранён наш ввод — 12345.Помним, что в регистре rdx лежит число 5. Из этого логично сделать вывод, что он
хранит длину введённой нами последовательности. Чтобы убедиться в этом, попробуйте перезапустить дебаггер и
ввести, например, число 12345678. Снова проверьте регистр RDX и убедитесь, что он хранит значение 8.
20

21.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Мы выяснили, что функция check_sum принимает на вход введённую
строку и её длину, а возвращает некоторое число (0 или не 0) для
проверки. То есть в высокоуровневом языке она могла бы выглядеть
примерно так: int check_func(* char, int).
Перейдём обратно в дизассемблер и изучим, что она проверяет.
Останавливаем дебаггер на верхней панели и выбираем в окне слева
функцию check_func. Переключимся в текстовый вид. Не забываем, что
регистр edx хранит в себе размер введённого серийника, а rcx — его
адрес в памяти.
21

22.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
В
первую
очередь
функция
check_func
проверяет
длину
введённого номера. Если
она не равна 19 (или 13 в
шестнадцатеричной
системе), то управление
переходит
по
метке
bad_serial, где командой
«xor eax, eax» регистр eax
обнуляется и происходит
выход из функции. Исходя
из этого делаем вывод:
корректный
серийный
номер состоит из 19
символов.
22

23.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Это облегчает задачу,
например,
для
атаки
перебором. Но полный
перебор такой длины
займёт
очень
много
времени.
Так
что
попробуем узнать что-то
ещё
о
правильном
формате
ввода.
практически сразу адрес
введённого
номера
помещается в регистр R8.
Если же длина номера
равна 19, то продолжаем
проверку, переходя на
метку
good_serial.
Перейдём к её коду.
23

24.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Информация о длине программе
больше не нужна, поэтому
регистр edx обнуляется. А вот
следующая команда для нас
важна. Мы помним, что в R8 был
записан
первый
элемент
серийного номера, значит по
адресу R8 + 4 будет храниться
адрес
5-го
элемента,
он
записывается в регистр RAX.
Дальше видим код,
встретится ещё не раз:
xchg
ax, ax
db
66h, 66h
xchg
ax, ax
который
«xchg ax, ax» — это эквивалент команды nop в платформе
х86. В целом, этот набор команд мы можем игнорировать.
24

25.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Команда cmp проверяет, что
именно находится в регистре rax,
и сравнивает его с числом 2D в
шестнадцатеричной системе. А в
rax у нас хранится 5-й элемент
серийника.
2D согласно ASCII эквивалентно
символу «-». Итак, если 5-й
символ не равен «-», происходит
переход на метку bad_serial. Если
же он верный, такую проверку мы
проводим ещё два раза, счётчиком
цикла у нас является регистр ecx.
Значение rax мы увеличиваем на
5, то есть он будет проверять,
являются ли 10-й и 15-й символ
дефисом.
25

26.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Отсюда мы можем сделать два вывода:
1)
На вход подаётся не число, а последовательность символов.
2)
Каждый 5-й символ должен быть «-».
Следовательно, формат последовательности
из
19
знаков
следующий: XXXX-XXXX-XXXX-XXXX. Если он верен, то проверка
продолжается.
26

27.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Дальше рассмотрим метку loc_140001053, тут она переименована в digit_check:
регистр R9 всегда указывает на первый элемент блока, а rcx хранит смещение внутри блока, поэтому
после проверки R9 увеличивается на 5, чтобы попасть в следующий блок, а rcx обнуляется после
проверки блока (помним, что в rdx лежит ноль, поэтому по метке zero_to_rcx происходит обнуление).
27

28.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Дальше рассмотрим метку loc_140001053, тут она переименована в digit_check:
Строчка «add eax, 0FFFFFFD0h» осуществляет вычитание. 0FFFFFFD0h для компьютера — это число
—30h. Использование вместо команды sub команды сложения «add eax, 0FFFFFFD0h» оптимальнее с
точки зрения компилятора чем «sub eax, 30h».
28

29.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Мы уже выяснили, что наш ввод — это
последовательность
символов.
Следовательно, в eax хранится ASCII код
элемента, из него вычитают 30h и
сравнивают с 9. Кодам ASCII с 30h до 39h
соответствуют цифры, поэтому если из кода
цифры вычесть 30h, то результат не превысит
9, если же превысит, значит в eax лежал код
не цифры, а какого-то другого знака, и тогда
мы переходим по метке wrong_serial.
Теперь мы точно знаем, что наш серийник
должен состоять из цифр и дефисов.
29

30.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Далее складываются коды первых трёх цифр одного блока, затем ещё 3 раза прибавляется код 4-й
цифры. Затем к этой сумме снова прибавляется код 4-й цифры и вычитается 150h. Результат
записывается в стек, суммы всех блоков складываются в регистр R10 и конечный результат делится
на 4.
30

31.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Теперь проверяются суммы для всех четырёх блоков. Значение для каждого блока должно быть равно сумме их кодов,
делённой на 4. Иными словами, сумма ASCII кода первого знака плюс код второго, третьего плюс 4 раза код
четвёртого и минус 150h должна быть одинаковой для всех блоков.
Рассмотрим пример для двух блоков 5873 и 7763. Для быстрого вычисления воспользуемся калькулятором (не
забудьте выбрать шестнадцатеричную систему счисления). Складываем ASCII коды:
35h + 38h + 37h + 33h + 33h + 33h + 33h - 150h = 20h
35h + 39h + 36h + 33h + 33h + 33h + 33h - 150h = 20h
31

32.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Остался последний этап проверки. Здесь мы выясняем, равны ли цифры блока друг другу. Смысл этой проверки в том,
что в серийном номере не должно быть одинаковых блоков. На примере выше у нас совпадают четвёртые цифры,
следовательно, такая последовательность не пройдет проверку.
R8 хранит адрес нашего ввода, RAX здесь используется для выбора знака внутри блока, а R8 — для выбора блока
внутри серийного номера.
32

33.

ПРОЦЕСС РЕВЕРС-ИНЖИНИРИНГА НА ПРИМЕРЕ CRACKME ФАЙЛА
Итак, мы собрали достаточно информации о том, каким должен быть серийный номер, чтобы пройти проверку.
Теперь можно выписать все условия и написать keygen-программу, которая будет генерировать такие коды для
данного CrackMe:
1)
Формат кода, проходящего проверку, имеет вид ХХХХ-ХХХХ-ХХХХ-ХХХХ, где Х -- цифра от 0 до 9;
2)
ASCII коды каждого блока из четырёх символов складываются, код четвёртой цифры складывается еще
3 раза и из этой суммы вычитается 150h. Этот результат должен быть одинаковым для всех блоков
3)
Код не должен содержать одинаковых блоков. Более того, цифры блоков, находящиеся на одних и тех
же позициях (от первой до четвёртой) не должны быть одинаковыми.
Пример валидного серийного номера для данного CrackMe: 9492-2805-3126-0774.
33

34.

ТВОРЧЕСКОЕ ЗАДАНИЕ
Разобрать пример реверс-инжиниринга файла CrackMe:
внимательно изучить процесс обратной разработки для файла
crackme.exe, используя программный комплекс IDA Pro.
В качестве ответа к заданию необходимо прислать валидный
серийный номер, который проходит проверку файлом и который
отличается от того, что показан в примере.
34
English     Русский Правила