Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
![](https://habrastorage.org/getpro/habr/upload_files/5aa/bfa/805/5aabfa80564370657b28b9eab938d620.jpeg)
По мотивам
Часть 0x00
А что дальше?
Уважаемые хабровчане и хабровчушки, эта статья является долгожданным продолжением моей предыдущей статьи о взломе жесткого диска для собеседования в инфосек компанию RedBalloonSecurity. Любителей поковырять железяки я спешу разочаровать, поскольку все дальнейшие манипуляции с диском будут проводится только на уровне ассемблерного кода и логики. Поэтому, приготовьте чай/кофе или чего покрепче, ведь мы снова лезем в embeded дебри и опять пускаемся в неизвестность.
Также, спешу вам сообщить, что в этой статье будет оочень много низкоуровневой компьютерной математики. Поэтому, пока будет закипать чайник, советую запихнуть голову в морозилку на пару минут. Для полного понимания этого уровня и способа его взлома нужна холодная голова!
LEVEL2
Моему счастью не было предела, когда я залил пропатченую прошивку на Winbond Flash чип, и ядро моего Debian-неттопа распознало еще 1 раздел диска. Здесь будет намного больше информации, чем в предыдущем разделе. Ведь повышая левел, сложность нашей с вами задачи только увеличивается.
Содержимое раздела:
user@ubuntu:/media/user/LEVEL2$ file *
0001-keystone-armv5.patch: unified diff output, ASCII text
level_2.html: HTML document, ASCII text, with very long lines
level2_instructions.txt: ASCII text
level_2.lod: data
level_3.lod.7z.encrypted: 7-zip archive data, version 0.3
level_2.lod - это новый файл прошивки для диска. Прошиваемся так же, как и в предыдущей статье. Здесь ничего нового.
level_3.lod.7z.encrypted - это файл прошивки для следующего уровня. Судя по его разрешению, файл находится в запароленном 7z архиве. Нам нужно решить текущий уровень чтоб достать пароль от следующего.
level2_instructions.txt - это, собственно, инструкции что и как делать. Подсказки тоже имеются.
0001-keystone-armv5.patch - это патч для Keystone Assembler. Keystone это компилятор и набор С-шных библиотек для перевода ассемблерного кода в опкоды для процессора.
level_2.html - изюминка текущего уровня. Выглядит точ-в-точ как текст, который генерирует IDA Pro при загрузке бинарника.
Для тех, кто не в курсе, IDA Pro это легендарная программа для дизассемблирования. Она на столько легендарная, что можете открыть страничку https://www.hex-rays.com/cgi-bin/quote.cgi, глянуть ценники на лицензии (которые разбиты по архитектурам), тут же закрыть вкладку, и, посвистывая, продолжить чтение этой статьи. У IDA Pro есть аналоги в виде Hopper Disassembler & Binary Ninja, и сейчас ценовая политика hex-rays чуть попроще, чем раньше (есть фришные версии). Но, опять таки - стоимость индустрии определяет стоимость инструментов.
Дело в том, что когда мы пишем код на высокоуровневых языках (C, Python и тд) и подвергаем его компиляции, мы переводим наш +- human-readable текст в язык машинного кода. Тоесть, мы опускаем более понятную человеку логику в логику, которая больше понятна машине. Машина не понимает что такое функция, ведь функция, это скорее абстракция в голове у программиста. Процесс дизассемблирования позволяет сделать наоборот - поднять логику из машинного на человекопонятный уровень. Некоторые дизассемблеры позволяют поднять логику даже на C-шный уровень, хоть и не всегда делают это корректно (на самом деле очень даже корректно, но читать такой код порой бывает сложнее чем ассемблер). Если работаем с чем-то мелким, и надо кабанчиком понять что там происходит - этого хватит, но для более высокоточных вещей нужен ассемблер. Это мы и получили в виде level_2.html файла.
user@ubuntu:/media/user/LEVEL2$ cat level2_instructions.txt
Congratulations... you have made it to the other side
Back when I was an intern, I designed this key generation function. My boss hated it.
I hate my boss.
1. Invoke the function with command R<User_Input>
2. Find the key you must!!!!!
level2.html provides disassembly of a memory snapshot of the key generator function.
To help... guide... you in this adventure, you'll find a patchfile for the keystone
assembler to force the correct architecture.
Also, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASCII
В этой подсказке кто-то ненавидит своего босса. Но, помимо этого, мы понимаем, что для решения этой задачи надо ввести в консольник диска что-то, что начинается на "R" - так мы запустим код, который имеем в дампе. И, с помощью этого, заставить программу отдать ключи. Самой полезной подсказкой была последняя фраза. Она намекает нам на то, что мы должны заабьюзить overflow.
0001-keystone-armv5.patch
user@ubuntu:/media/user/LEVEL2$ cat 0001-keystone-armv5.patch
From 5532e7ccbc6c794545530eb725bed548cbc1ac3e Mon Sep 17 00:00:00 2001
From: mysteriousmysteries <mysteriousmysteries@redballoonsecurity.com>
Date: Wed, 15 Feb 2017 09:23:31 -0800
Subject: [PATCH] armv5 support
---
llvm/keystone/ks.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/keystone/ks.cpp b/llvm/keystone/ks.cpp
index d1819f0..8c66f19 100644
--- a/llvm/keystone/ks.cpp
+++ b/llvm/keystone/ks.cpp
@@ -250,7 +250,7 @@ ks_err ks_open(ks_arch arch, int mode, ks_engine **result)
if (arch < KS_ARCH_MAX) {
ks = new (std::nothrow) ks_struct(arch, mode, KS_ERR_OK, KS_OPT_SYNTAX_INTEL);
-
+
if (!ks) {
// memory insufficient
return KS_ERR_NOMEM;
@@ -294,7 +294,7 @@ ks_err ks_open(ks_arch arch, int mode, ks_engine **result)
TripleName = "armv7";
break;
case KS_MODE_LITTLE_ENDIAN | KS_MODE_THUMB:
- TripleName = "thumbv7";
+ TripleName = "armv5te";
break;
}
@@ -566,7 +566,7 @@ int ks_asm(ks_engine *ks,
Streamer = ks->TheTarget->createMCObjectStreamer(
Triple(ks->TripleName), Ctx, *ks->MAB, OS, CE, *ks->STI, ks->MCOptions.MCRelaxAll,
/*DWARFMustBeAtTheEnd*/ false);
-
+
if (!Streamer) {
// memory insufficient
delete CE;
@@ -594,7 +594,7 @@ int ks_asm(ks_engine *ks,
return KS_ERR_NOMEM;
}
MCTargetAsmParser *TAP = ks->TheTarget->createMCAsmParser(*ks->STI, *Parser, *ks->MCII, ks->MCOptions);
- if (!TAP) {
+ if (!TAP) {
// memory insufficient
delete Parser;
delete Streamer;
--
1.9.1
Патч для keystone говорит нам о том, на какой вариации ARM архитектуры работает наш IC на плате от жесткого диска - armv5te. Фотка IC есть в предыдущей статье.
Я прошил диск файлом level_2.lod. Подключившись в консольник диска, я, последовав подсказкам, нажал Ctrl+Z. После чего, меня ждала строка приглашения от Seagate F3 меню. Это меню является механизмом для диагностики HDD. Через него можно узнать сколько раз заводился сервопривод диска, количество и местоположение бэд секторов, засечь время поиска определенного сектора, и очень много прочих параметров. Через него с диском можно делать все, что угодно - даже записать какие-то данные по определенному LBA адресу. Я так понял, ребята, когда делали этот челлендж, переписали то, как должен реагировать диск на команду "R". Что же, сюда мы и будем вводить "R...".
Welcome to minicom 2.7.1
OPTIONS: I18n
Compiled on Aug 13 2017, 15:25:34.
Port /dev/ttyS0, 19:19:01
Press CTRL-A Z for help on special keys
Rst 0x08M
Servo Processor Is Reset.
RW: Disc Ctlr Initialization Completed.
ExecuteSpinRequest
(P) SATA Reset
ASCII Diag mode
F3 T>
CTRL-A Z for help | 38400 8N1 | NOR | Minicom 2.7.1 | VT102 | Offline | ttyS0
Серьезно? ASM?
В этом code-box приведены первые 20 строк из level_2.html файла. В конце публикации есть файл целиком, но в процессе статьи, давайте смотреть на него кусочками:
01. ROM:00332D00
02. ROM:00332D00 ; Segment type: Pure code
03. ROM:00332D00 AREA ROM, CODE, READWRITE, ALIGN=0
04. ROM:00332D00 ; ORG 0x332D00
05. ROM:00332D00 CODE16
06. ROM:00332D00
07. ROM:00332D00 ; =============== S U B R O U T I N E =======================================
08. ROM:00332D00
09. ROM:00332D00 ; prototype: generate_key(key_part_num, integrity_validate_table, key_table)
10. ROM:00332D00 ; Function called when serial console input is 'R'. Generates key parts in R0-R3.
11. ROM:00332D00 ; The next level to reach, the key parts to print you must!
12. ROM:00332D00
13. ROM:00332D00 generate_key
14. ROM:00332D00
15. ROM:00332D00 var_28 = -0x28
16. ROM:00332D00
17. ROM:00332D00 PUSH {R4-R7,LR}
18. ROM:00332D02 SUB SP, SP, #0x10
19. ROM:00332D04 MOVS R7, R1
20. ROM:00332D06 MOVS R4, R2
...
Колонка слева с ROM:XXXXXXXX - это адреса памяти. При загрузке бинарника в IDA Pro, мы должны убедиться, правильно ли IDA Pro поняла для какой архитектуры (ARM, x86, MIPS и тд) собран наш бинарник (в большинстве случаев, автоопределение работает очень хорошо, но если мы вгружаем не бинарник, а целый дамп памяти - прийдется вручную настроить IDA Pro). Считываем файл байт за байтом, и эта левая колонка автоинкрементируется каждый раз когда IDA Pro понимает что это за кусок данных. Данные в бинарнике могут быть поняты одним из следующих образов:
Данные. Самый низкий уровень понимания логики. Это когда кусок данных не представляет собой ничего конкретного. В дампе отображается как DCD, DCW, DCB - doubleword, word, byte соответственно. На такие штуки обычно есть ссылки из других участков кода. Назначение может быть самое разное.
Код. Это когда кусок данных представляет собой 1 атомарную единицу операции (записать данные из регистра в память, сравнить числа и тд).
Строка. Это когда IDA Pro натыкается на массив данных которые лежат в ASCII диапазоне. От 0x20 до 0x7E (ASCII стандарт также описывает числа ниже 0x20, но они не имеют "текстового" смысла). Вполне возможно, что диапазон расширяется и на другие кодировки, но это вне контекста данной статьи.
Подпроцедура (Subroutine). Это довольно высокий уровень понимания ассемблерной логики - когда IDA Pro видит функцию. В ней мы видим то же самое, как если бы видели код. Но, понимание того, что это не просто код, а функция дает невероятный скачок в осознании происходящего. Также, выдавать C-шный код IDA Pro может только для подпроцедур (или нет. Поправьте, если я не прав). В нашем level_2.html мы видим только подпроцедуры - имеем максимальный уровень понимания. Задачка для интерна, все-таки.
Как я и говорил, дизассемблер не всегда распознает вещи корректно. Поэтому, при работе с IDA Pro есть возможность переключать режимы понимания данных на разных участках памяти.
LSI B5502D0
На 5й строке мы видим надпись CODE16. Это очень важно, поскольку этот дамп снят с кода для ARM процессоров. У них есть 2 режима работы - ARM и Thumb.
В режиме ARM у нас есть доступ, наверное, ко всем ассемблерным операциям, но такие инструкции занимают 4 байта
В режиме Thumb мы немножко ограничены, но такие инструкции занимают 2 байта.
Причины по которым были созданы эти 2 режима мне неизвестны (знатоки в комментариях очень даже приветствуются), но могу сказать следующее:
В режиме ARM мы, скорее всего, будем исполнять желаемую логику быстрее. Как минимум, потому что, у нас есть доступ к инструкциям типа CMPEQ, которая выполнит операцию сравнения чисел только в том случае, когда результат предыдущего сравнения был успешным. Получается, что в этом режиме мы можем отдать логику if-else не просто на железо, а внутрь процессора. И сделать 2 операции (проверка предыдущего результата и выполнение новой операции) за 1 цикл (если я правильно это понимаю) без хождения по памяти. Но и размер инструкций будет больше, а значит, что нужно использовать больше памяти.
В режиме Thumb мы не можем использовать подобное, но размер инструкций будет в 2 раза меньше, а значит, и затраты памяти будут ниже.
Еще стоит сказать, что архитектура ARM (не путать с режимом) прекрасна тем, что размер инструкций у них фиксирован (2 или 4 байта), чего нельзя сказать об x86.
В общем, вот это CODE16 значит то, что в этом моменте процессор находится в Thumb режиме (CODE16 - 16 бит на инструкцию). И, как мы видим дальше, адреса памяти инкрементируются по 2 байта (конечно, только там, где есть инструкция).
Строка 13 являет собой адрес, на который есть отсылки в коде. Дело в том, что программы на ассемблере не исполняются линейно. Инструкции типа B, BL, BX, BLX переводят исполнение кода по новому адресу. И когда IDA Pro видит такие инструкции, она автоматически дает имя этому адресу. В нашем случае, ребята из RedBalloonSecurity переименовали этот адрес в generate_key. Переименовывать позиции в коде, на который есть ссылки является прекрасным способом оставить для себя заметку о том, что делает определенный кусок кода. При работе с дизассемблером, подобное переименование "отрефакторит" это имя на каждом референсе по этому адресу - это очень удобно.
На строке 15 мы видим переменную. Надеюсь, все помнят об области видимости в С? Реализация этого механизма на уровне ассемблера очень хитрая. Здесь используется структура данных типа стек.
Ëмае, вот к чему stack в stackoverflow
У каждого процессора, помимо кэша, есть встроенное хранилище временных данных - регистры. Это самая быстрая память компьютера, как такового. Все операции с регистрами выполняются невероятно быстро. А поэтому, программу нужно строить таким образом чтоб минимизировать обращения к памяти, и почаще использовать регистры. Благо, компиляторы умные - умеют оптимизировать с разной точностью, и располагают что куда, даже если С-шный код писал рак (и то, не всегда).
У ARM процессоров 15 регистров. Для большинства, можно писать код и использовать их как хочешь (хотя, внутри компилятора, все же есть определенные правила о том, какой регистр для чего использовать. А в некоторые писать напрямую запрещено). Они имеют имена вида R0, R1 ... R15. Последние имеют специальное назначение:
SP (R13) - Stack Pointer. В этом регистре хранится адрес вершины стека
BP (R7 в Thumb, R11 в ARM) - Base Pointer. Здесь находится адрес начала стека
LR (R14) - Link Register. Если у бренч (B) инструкции есть приставка L, это значит, что содержимое регистра PC нужно записать в регистр LR (там есть определенная специфика, которую я не совсем понимаю. К этому адресу в LR должно добавляться +2 байта при Thumb, или +4 байта при ARM. Но, это не точно). Потом это используется для возврата на предыдущее место в коде, например через BX LR. К примеру С-шный "return" отработает именно по такой логике (и то, если так решит компилятор). Из преимуществ - он быстрее, и часть программной логики падает на CPU без хождения по памяти. Недостаток - регистр всего один, и предыдущие адреса возврата все же прийдется складывать на стек.
PC (R15) - Program Counter. Здесь находится адрес инструкции, которую процессор выполняет в данный момент. Писать сюда напрямую, кажись, нельзя. Но этот регистр полезен, если мы хотим сделать прыжок на другой адрес. Если мы работаем с 32-битной архитектурой, мы, ну никак не можем сказать процессору прыгнуть на 32-битный адрес, имея 2 или 4 байта на 1 инструкцию. Большинство инструкций для прыжка используют сдвиг от того, что находится в PC.
CSPR - Current State Process Register. Это специфический регистр. К нему нельзя обратится целиком, как и записать сюда что-то. Данные внутри этого регистра формируются автоматически на основе того, какие инструкции выполняет процессор. Он разбит на сегменты, и хранит в себе информацию о текущем состоянии процессора. К примеру, если мы выполняем инструкцию CMP (сравнить числа), результат сравнения (0 или 1) пишется в один из битов этого регистра. А в дальнейшем это используется для условных операций.
Стек это определенное место в памяти, где хранятся значения локальных переменных, аргументы функций, адреса возвратов, а также предыдущее значение BP. Стек логически разбит на сегменты (stack frames). Каждый сегмент принадлежит какой-то определенной функции. И, когда мы вызываем из одной функции другую, мы, создавая новый stack frame, сдвигаем адреса BP & SP чуть ниже(!) в памяти. Но, перед тем как создавать новый сегмент на стеке, мы должны знать адрес, куда должен вернутся PC после завершения функции - он сохраняется на стеке (Return Address) перед вызовом новой функции. Также, чтоб при возврате, сегмент стека стал того же размера что и был, мы сохраняем предыдущее значение BP (Saved %ebp) на этот же стек. Это пример для x86, но и на ARM дела обстоят так же. Выглядит все это дело примерно так (%ebp = BP, %esp = SP):
![](https://habrastorage.org/getpro/habr/upload_files/e88/16e/bf3/e8816ebf3f2d67d69e4bb71244e248b8.png)
Стоит оговорится, что механизм по созданию и уничтожению stack frame'ов для меня до сих пор не совсем понятен. Поэтому, очень советую поискать что-то на ютубе. Для понимания таких вещей нужна визуальная картинка.
В предыдущем абзаце я сказал, что при создании нового сегмента стека, адреса BP & SP смещаются ниже. Дело в том, что "так сложилось исторически". Стек это FIFO конструкция, размер которой меняется в процессе исполнения программы. И компилятор не знает какого размера он может быть. То же самое касается кучи (heap) - памяти, которую мы запрашиваем у системы через семейство вызовов malloc (glibc). При выделении памяти в куче, ее адреса растут вверх, а вот когда растет стек, его адреса растут вниз. Стоит оговорится, что мы работаем с embeded устройством. Понятия кучи здесь, может и не быть (опять же, прошу экспертов меня поправить).
![](https://habrastorage.org/getpro/habr/upload_files/5b2/f3c/f5d/5b2f3cf5dfc0642a8b0248eb9a76381e.jpeg)
У ARM процессоров есть специальное семейство инструкций, которые работают со стеком: PUSH, POP, STMFD, LDMFD (уверен, есть еще, но для наших грязных делишек этого хватит).
PUSH кладет то, что в аргументе на стек, и увеличивает его (на самом деле уменьшает адрес)
POP снимает со стека данные, кладет туда, куда указывает аргумент, и уменьшает стек (на самом деле увеличивает адрес)
STMFD, LDMFD (store/load multiple) делают то же самое, но туда можно запихнуть несколько регистров, и снять со стека несколько значений в рамках одной инструкции. И, кажись, указать, стоит ли подстраивать значение в SP в зависимости от количества впихнутых/снятых значений со стека. Опять же, прелести ARM архитектуры!
Я бы не обьяснял это так детально, но понимание того, как устроен стек, и для чего он нужен - критично для взлома данного уровня. Без этого никуда!
Готовы? Ныряем!
Для того, чтоб что-то правильно сломать, нужно это правильно понять. Начнем с первых инструкций из level_2.html.
...
13. ROM:00332D00 generate_key
14. ROM:00332D00
15. ROM:00332D00 var_28 = -0x28
16. ROM:00332D00
17. ROM:00332D00 PUSH {R4-R7,LR}
18. ROM:00332D02 SUB SP, SP, #0x10
19. ROM:00332D04 MOVS R7, R1
20. ROM:00332D06 MOVS R4, R2
21. ROM:00332D08 MOVS R5, R0
22. ROM:00332D0A LDR R1, =0x6213600 ; "R"...
23. ROM:00332D0C LDRB R0, [R1,#1]
24. ROM:00332D0E CMP R0, #0x31
25. ROM:00332D10 BNE loc_332D1A
26. ROM:00332D12 ADDS R0, R1, #2
27. ROM:00332D14 BLX ahex2byte
...
Строка 17. Первая инструкция это PUSH. Она сохраняет на стек переменные из регистров R4-R7 и LR на стек, тоесть сохраняет предыдущий stack frame.
Дисклэймер: в отличии от всеми любимой bash консольки, где программы типа mv, cp используют схему аргументов command source destination, ассемблерные инструкции используют схему "instruction destination source". Единственным исключением является семейство инструкций для записи в память - STR.
Строка 18. Через SUB отнимаем 16 байт от SP (увеличиваем стек).
Строка 19-21. Копируем из регистров R0-R2 аргументы, которые были переданы в функцию generate_key в их "рабочие" места в рамках текущей функции. Как видим, в прототипе (строка 9) было 3 аргумента. Регистров столько же. В предыдущем разделе я говорил, что аргументы падают на стек - это правда только в том случае, когда аргументов больше, чем 3 (или 4, уже не помню). Опять таки, лучше использовать регистры, чем ходить в память.
На этом моменте заканчивается процедура под названием Function Prologue. Тоесть, не происходит ничего, что касается программной логики, но, выполняется подготовка стека и локальных переменных. Мне кажется, именно по подобным шаблонам IDA Pro отличает код от подпроцедур.
Строка 22. Грузим адрес, который будет указывать на первый символ того, что мы вводим в консольник диска в регистр R1. Комментарий здесь очень полезен.
Строка 23. Семейство инстркуций LDR предназначено для работы с памятью. Инструкция
LDRB R0, [R1,#1] (Load Register Byte)
берет то, что лежит в регистре R1, добавляет единицу (по сути, берет адрес второго вводимого символа), лезет в память по этому адресу (если значение в квадратных скобках, сначала надо интерпретировать эти данные как адрес памяти), берет 1 байт и сохраняет его в регистр R0. Мы забрали второй вводимый символ в R0.
Строка 24-25. Идет сравнение 2го вводимого символа с 0x31. В ASCII 0x31 это "1". Инструкция
BNE loc_332D1A (Branch If Not Equal)
выполнит прыжок на loc_332D1A только в том случае, если мы ввели не единичку.
Строка 26. Добавляем к адресу наших вводимых данных двойку и сохраняем в R0. Инструкция с тремя операндами
ADDS R0, R1, #2
возьмет значение из R1, добавит в него двойку, и сохранит результат в R0. Тоесть, теперь адрес в R0 указывает на третий вводимый символ - "R1_" (на то место, где underscore).
Строка 27. Здесь нас ждет безусловный прыжок на функцию ahex2byte. Но, это не просто прыжок. Инструкция
BLX ahex2byte (Branch Link Exchange)
кроме прыжка, делает еще 2 замечательные вещи - сохраняет адрес инструкции после текущего PC в LR (из-за "L" в BLX), и переключает нас в ARM режим (из-за "X" в BLX)! Внутри функции ahex2byte у нас будет 4 байта на инструкцию. В регистр R0 пишется первый аргумент при вызове функции. Получается, что мы запускаем функцию ahex2byte с одним единственным аргументом - адресом третьего вводимого символа.
Дабы подитожить, вот то, как предыдущий кусок выглядел бы в С-шном виде. Этот код не скомпилируеться и может быть неправильным. Прошу экспертов меня поправить. Он чисто для наглядности:
void generate_key (key_part_num, integrity_validate_table, key_table) {
char *input = "R1_";
if (input[1] == "1") {
&input = &input + 2;
ahex2byte(&input);
}
else {
goto: loc_332D1A;
}
}
ахекс2байт
В этом code-box представлен еще 1 кусок от level_2.html - функция ahex2byte. Здесь я настоятельно рекомендую читателям скопировать содержимое спойлера себе в блокнот, и расположить окно слева (или справа, черт вас знает) от этой статьи, иначе прийдется очень много скроллить. В процессе чтения, поглядывайте на ASM код, и вам станет все ясно.
ahex2byte
...
137. ROM:00332DF8 ; =============== S U B R O U T I N E =======================================
138. ROM:00332DF8
139. ROM:00332DF8
140. ROM:00332DF8 ahex2byte ; CODE XREF: generate_key+14p
141. ROM:00332DF8 STMFD SP!, {R4-R6,LR}
142. ROM:00332DFC MOV R4, R0
143. ROM:00332E00 MOV R6, R0
144. ROM:00332E04
145. ROM:00332E04 loc_332E04 ; CODE XREF: ahex2byte+6Cj
146. ROM:00332E04 LDRB R0, [R4]
147. ROM:00332E08 CMP R0, #0xD
148. ROM:00332E0C BEQ loc_332E68
149. ROM:00332E10 BL sub_332E70
150. ROM:00332E14 CMN R0, #1
151. ROM:00332E18 BNE loc_332E2C
152. ROM:00332E1C LDRB R0, [R4]
153. ROM:00332E20 BL sub_332E98
154. ROM:00332E24 CMN R0, #1
155. ROM:00332E28 BEQ locret_332E6C
156. ROM:00332E2C
157. ROM:00332E2C loc_332E2C ; CODE XREF: ahex2byte+20j
158. ROM:00332E2C MOV R5, R0
159. ROM:00332E30 LDRB R0, [R4,#1]
160. ROM:00332E34 BL sub_332E70
161. ROM:00332E38 CMN R0, #1
162. ROM:00332E3C BNE loc_332E50
163. ROM:00332E40 LDRB R0, [R4,#1]
164. ROM:00332E44 BL sub_332E98
165. ROM:00332E48 CMN R0, #1
166. ROM:00332E4C BEQ locret_332E6C
167. ROM:00332E50
168. ROM:00332E50 loc_332E50 ; CODE XREF: ahex2byte+44j
169. ROM:00332E50 MOV R5, R5,LSL#4
170. ROM:00332E54 ADD R0, R5, R0
171. ROM:00332E58 STRB R0, [R6]
172. ROM:00332E5C ADD R4, R4, #2
173. ROM:00332E60 ADD R6, R6, #1
174. ROM:00332E64 B loc_332E04
175. ROM:00332E68 ; ---------------------------------------------------------------------------
176. ROM:00332E68
177. ROM:00332E68 loc_332E68 ; CODE XREF: ahex2byte+14j
178. ROM:00332E68 STRB R0, [R6]
179. ROM:00332E6C
180. ROM:00332E6C locret_332E6C ; CODE XREF: ahex2byte+30j
181. ROM:00332E6C ; ahex2byte+54j
182. ROM:00332E6C LDMFD SP!, {R4-R6,PC}
183. ROM:00332E6C ; End of function ahex2byte
184. ROM:00332E6C
185. ROM:00332E70
186. ROM:00332E70 ; =============== S U B R O U T I N E =======================================
187. ROM:00332E70
188. ROM:00332E70
189. ROM:00332E70 sub_332E70 ; CODE XREF: ahex2byte+18p
190. ROM:00332E70 ; ahex2byte+3Cp
191. ROM:00332E70 CMP R0, #0xD
192. ROM:00332E74 BEQ loc_332E90
193. ROM:00332E78 CMP R0, #0x30
194. ROM:00332E7C BLT loc_332E90
195. ROM:00332E80 CMP R0, #0x39
196. ROM:00332E84 BGT loc_332E90
197. ROM:00332E88 SUB R0, R0, #0x30
198. ROM:00332E8C B locret_332E94
199. ROM:00332E90 ; ---------------------------------------------------------------------------
200. ROM:00332E90
201. ROM:00332E90 loc_332E90 ; CODE XREF: sub_332E70+4j
202. ROM:00332E90 ; sub_332E70+Cj ...
203. ROM:00332E90 MVN R0, #0
204. ROM:00332E94
205. ROM:00332E94 locret_332E94 ; CODE XREF: sub_332E70+1Cj
206. ROM:00332E94 BX LR
207. ROM:00332E94 ; End of function sub_332E70
208. ROM:00332E94
209. ROM:00332E98
210. ROM:00332E98 ; =============== S U B R O U T I N E =======================================
211. ROM:00332E98
212. ROM:00332E98
213. ROM:00332E98 sub_332E98 ; CODE XREF: ahex2byte+28p
214. ROM:00332E98 ; ahex2byte+4Cp
215. ROM:00332E98 CMP R0, #0x41
216. ROM:00332E9C BLT loc_332EB4
217. ROM:00332EA0 CMP R0, #0x46
218. ROM:00332EA4 BGT loc_332EB4
219. ROM:00332EA8 SUB R0, R0, #0x41
220. ROM:00332EAC ADD R0, R0, #0xA
221. ROM:00332EB0 B locret_332EB8
222. ROM:00332EB4 ; ---------------------------------------------------------------------------
223. ROM:00332EB4
224. ROM:00332EB4 loc_332EB4 ; CODE XREF: sub_332E98+4j
225. ROM:00332EB4 ; sub_332E98+Cj
226. ROM:00332EB4 MVN R0, #0
227. ROM:00332EB8
228. ROM:00332EB8 locret_332EB8 ; CODE XREF: sub_332E98+18j
229. ROM:00332EB8 BX LR
230. ROM:00332EB8 ; End of function sub_332E98
231. ROM:00332EB8
232. ROM:00332EB8 ; ---------------------------------------------------------------------------
...
Строка 141. С помощью STMFD, мы сохраняем значения регистров R4-R6 и LR на стек - сохраняем stack frame.
Строки 142-143. Копируем первый аргумент (адрес третьего вводимого символа) в регистры R4 и R6. Причина такого поведения будет ясна по ходу статьи.
Строка 146. Идем по адресу в R4, и забираем наш третий символ в R0.
Строка 147. Инструкция
CMP R0, #0xD
сравнивает его с 0x0D. 0x0D это символ новой строки. Результат сравнения (успешный, или не успешный) записывается в один из битов регистра CSPR (уже не помню какой).
Стоит оговорится, что символ новой строки на разных системах выглядит по разному, но пока что я встречал только три (и чертовски благодарен, что только три):
0x0D (Carriage Return) - Возврат Каретки (я, блин, не шучу)
0x0A (Line Feed) - Новая Строка
0x0A 0x0D (CR LF) - все вместе
В общем, тот кто клонировал git репу себе на виндовую машину, а потом скопировал папку на что-то nix*, поймет эту боль.
Строка 148. Инструкция
BEQ loc_332E68 (Branch If Equal)
является условным прыжком. Мы прыгнем только тогда, когда результат предыдущего сравнения будет успешным (Branch If Equal). Условие проверяется с помощью бита в регистре CSPR
В нашем случае, давайте допустим, что мы ввели 16 символов после R1:
R1AAAAAAAAAAAAAAAA
Если это так, прыжок на loc_332E68 не случится - третий вводимый символ не является новой строкой.
Строка 149. Здесь мы видим безусловный прыжок на sub_332E70. Этот прыжок сохраняет следующий адрес после PC в LR:
BL sub_332E70
Строка 191. Здесь, опять таки, проводится сравнение с символом новой строки, и если это так, на строке 192, мы прыгаем на loc_332E90. Судя по тому, что мы ввели, символа новой строки у нас нету. Поэтому, прыжок не произойдет.
Строка 193. Проводится сравнение нашего символа с 0x30 - это ASCII "0". Сейчас мы проверяем символ "А" - это 0x41. Сравнение не было успешным.
Строка 194. Инструкция BLT (Branch If Less Than) прыгнет на loc_332E90 только тогда, когда мы ввели значение меньше, чем 0x30 в предыдущем сравнении. В нашем случае, это не так. Прыжок не случится.
Строка 195. Проводится сравнение с 0x39 - это ASCII "9". Опять таки, сравнение не будет успешным, но инструкция BGT (Branch If Greater Than) на строке 196 отработает, и здесь, мы все-таки прыгаем на loc_332E90.
Строка 203. Здесь мы видим инструкцию:
MVN R0, #0
MVN (Move Negative) сделает логическое "не" с нулем, и запишет минус один в R0. Дело в том, что любое значение у нас размером не в 1 бит (даже наш ноль). Оно, скорее всего, 32х битное. И, если перевернуть каждый бит в 32х битном нуле, получим 32 бита из единиц. По правилам two's complement (вспоминаем, или гуглим, что это), такое значение будет равняться минус одному.
Строка 206. Прыжок со сменой режима на адрес в LR. Последний раз, LR был "залинкован" на строке 149 - возвращаемся туда (тоесть, на строку 150). Здесь стоит упомянуть, что смена режима - это довольно хитрая операция. Если адрес, куда мы прыгаем непарный, мы сменим режим на Thumb, а если он парный, переключимся в ARM (это касается только тех инструкций, которые содержат Exchange (X) параметр). Короче, хоть мы и видим BX, смена режима не произойдет, и мы останемся в ARM.
Строка 150. Инструкция
CMN R0, #1 (Compare Negative)
очень интересная. Внутренняя логика инструкций CMP и CMN реализована через отнимание, или добавление операндов. CMP работаем через отнимание. Процессор понимает, что сравниваемые числа равны, когда результат отнятия одного от другого будет 0. CMN реализован через добавление - тоесть, для сравнения чисел нужно добавить один операнд к другому. Если получим 0 - принимаем такой результат как успешное сравнение.
Здесь, мы добавляем 1 к -1. Получим ноль. Сравнение будет успешным.
Конечно, все эти манипуляции с негативными числами не комфортно воспринимать, но поскольку компилятор решил зарулить инструкции так - значит, машине "комфортнее" работать с таким кодом. Здесь, как и в жизни, ничего не бывает "просто так"!
Строка 151. У нас BNE. Поскольку предыдущее сравнение было успешным, прыжок на loc_332E2C не состоится.
Строка 152. Здесь, как и на строке 146, мы подгружаем 3й символ из нашего ввода в R0. Мы делаем это снова потому что дальше, будут случаи когда это необходимо.
Строка 153. Безусловный прыжок с "линковкой" в LR на sub_332E98
Строка 215. Здесь мы делаем +- то же самое, что и с цифрами на sub_332E70. Инструкция
CMP R0, #0x41
сравнит наш ввод с 0x41. 0x41 это ASCII "A". Наш 3й символ будет совпадать. Сравнение будет успешным!
Строка 216. Данный прыжок BLT не состоится, поскольку предыдущее сравнение было успешным.
Строка 217. Сравнение с 0x46 не увенчается успехом, и на строке 218 прыжок не состоится.
Строка 219-220. Здесь максимальное внимание! В итоге, на строке 215, мы наткнулись на успешное сравнение вводимых данных. Примерно здесь начинает отрабатывать логика функции ahex2byte. Инструкции
SUB R0, R0, #0x41
ADD R0, R0, #0xA
превратят наш вводимый hex символ в бинарный - в этом и смысл этой функции. Парочка примеров:
"A"
0x41 - 0x41 = 0x00
0x00 + 0x0A = 0x0A
"E"
0x45 - 0x41 = 0x04
0x04 + 0x0A = 0x0E
Результат этой операции сохранится в R0.
Если глянуть на строку 197, там мы отнимаем 0x30 - это в том случае, когда мы ввели ASCII цифру. Если глянете на ASCII таблицу, цифры от "0" до "9" имеют значения от 0x30 до 0x39. Тоесть, там происходит то же самое, что и с нашей буквой "А" здесь. Но, математика разная.
Строка 221. Здесь мы прыгнем на locret_332EB8, а потом прыгнем на содержимое в LR. Последний раз мы его "линковали" на строке 153. Что же, возвращаемся на строку 154.
Строка 154. Опять таки, сравниваем результат в R0 с минус единицей. Поскольку, сравнение с символом "A" было успешным, текущее сравнение успешным не будет. По сути, подобное сравнение является проверкой на ошибку в подпроцедуре. Подпроцедура вернет минус один какраз в случае ошибки.
Строка 155. Прыжок на locret_332E6C. Поскольку предыдущее сравнение не было успешным, он не состоится. Этот прыжок будет успешным только тогда, когда все сравнения чисел будут некорректными - к примеру, если после нашего "R1" мы введем символ "Q" (он вне диапазона всех наших сравнений). Данный прыжок указывает на инструкцию,
LDMFD SP!, {R4-R6, PC}
которая вернет предыдущий stack frame, и восстановит PC - это выход из функции ahex2byte.
Строка 158. В итоге, после конвертации ASCII числа в бинарное, мы записываем результат из R0 в регистр R5. На строке 21 мы копировали значение из R0 в R5. R0 является первым аргументом функции generate_key - key_part_num. И, честно говоря, в этот момент, я уже было подумал, что понял как взломать этот уровень. Судя по логике этой функции, первый аргумент отвечает за номер ключа, который мы генерируем. В Function Prologue мы поместили key_part_num именно в R5. Я думал, что могу подстроить ввод данных таким образом, что запихну в R5 желаемое значение, и программа сама зарулит исполнение по ключам. В этом участвует логика на строках 44-64. Но, скажу сразу - здесь я ошибся.
Строка 159. Помним, что R4 указывает на 3й символ нашего ввода. Здесь мы смещаем этот адрес на единицу вперед, и забираем байт из памяти в R0 - тоесть, берем уже 4й символ.
Строка 160. Прыжок на sub_332E70. Сюда мы уже прыгали, когда проверяли 3й символ. Там происходит процесс сравнения с ASCII цифрой, и ее конвертация в бинарную.
Строка 161. Сравнение с минус единицей. Как я и говорил, проверяем результат сравнения символа с цифрой на ошибку.
Строка 162. Если ошибка есть, мы не прыгнем на loc_332E50. Помним, что 4й символ нашего ввода - это ASCII "A". В нашем случае, ошибка будет.
Строка 163-164. Мы снова вгружаем тот же вводимый 4й символ в R0, и прыгаем на sub_332E98 для проверки ASCII буквы
Строка 165. Опять таки, проверка на ошибку. В нашем случае, ошибки не будет, и ASCII буква сконвертируеться в бинарную.
Строка 166. Прыжок на locret_332E6C не отработает, поскольку у нас не было ошибки
Строка 169. Здесь начинается самое интересное. Инструкция
MOV R5, R5,LSL#4
скопирует содержимое R5 само на себя, но перед этим совершит бинарный сдвиг влево на 4 позиции (Logical Shift Left). Как это выглядит на примере 32-битного значения цифры "A":
Before:
0000 0000 0000 0000 0000 0000 0000 1010 = 0x0A
After:
0000 0000 0000 0000 0000 0000 1010 0000 = 0xA0
Строка 170. Инструкция
ADD R0, R5, R0
добавит к R0 сдвинутое значение цифры в R5 и сохранит в R0
Получается, что после проверки и обработки 4го символа, на примере нашего ввода получится вот такое значение в R0:
0000 0000 0000 0000 0000 0000 1010 1010 = 0xAA
Строка 171. Инструкция
STRB R0, [R6]
возьмет значение из R0, и запишет 1 байт в адрес, куда указывает R6. Последний раз мы трогали R6 когда я только начал описывать всю функцию ahex2byte (во втором абзаце после спойлера). В нем содержится адрес 3го символа нашего ввода.
Мог бы понять суть по названию функции
Поняли, что здесь происходит? Мы обрабатываем и перезаписываем то, что ввели в консольник диска из hex значений в бинарные с шагом в 2 символа (при этом, первые 2 символа нашего ввода "R1AAA..." игнорируются). Здесь очень важный момент! Получается, что на 2 символа hex ввода (2 байта), мы в итоге получаем 1 бинарный байт. Пример:
ASCII "CE" (0x43 0x45) --> 0xCE
ASCII "F1" (0x46 0x31) --> 0xF1
Строка 172. Инструкции
ADD R4, R4, #2
ADD R6, R6, #1
сместят адреса наших вводимых символов соответственно. Для ASCII символов - 2 байта, а для уже обработанных, бинарных, на 1 байт.
Строка 174. Делаем безусловный прыжок на loc_332E04 и процесс начинается по новой, но уже для следующих вводимх символов.
В конце концов, наш ввод закончится символом новой строки, и мы упремся в символ 0x0D на строке 147, совершим прыжок на loc_332E68, где сохраним этот 0x0D в память, куда указывает R6, и сделаем полный возврат из ahex2byte.
Весь наш ввод сконвертирован из ASCII в бинарный, и записан поверх самого себя!
Дабы проиллюстрировать что же произошло с нашим вводом на примере с R1AAAAAAAAAAAAAAAA
:
ASCII "R" = 0x52
ASCII "1" = 0x31
ASCII "A" = 0x41
Before:
ASCII: " R" " 1" " A" " A" " A" " A" " A" " A" ...
HEX: 0x52 0x31 0x41 0x41 0x41 0x41 0x41 0x41 ...
After:
ASCII: " R" " 1" " A" " A" " A" " A" " A" " A" ...
HEX: 0x52 0x31 0x0A 0x0A 0x0A 0x0A 0x0A 0x0A ...
Ныряем еще глубже. Что за ключи?
Здесь советую сделать то же самое, что и с ahex2byte - скопируйте себе в блокнот и расположите рядом.
key_part*
...
065. ROM:00332D52 ; ---------------------------------------------------------------------------
066. ROM:00332D52
067. ROM:00332D52 key_part1
068. ROM:00332D52 LDR R0, [R4]
069. ROM:00332D54 MOVS R6, #1
070. ROM:00332D56 STR R6, [R7]
071. ROM:00332D58 BLX loc_332DEC
072. ROM:00332D5C CODE32
073. ROM:00332D5C
074. ROM:00332D5C key_part2
075. ROM:00332D5C LDR R6, [R7]
076. ROM:00332D60 CMP R6, #1
077. ROM:00332D64 LDREQ R1, [R4,#4]
078. ROM:00332D68 EOREQ R1, R1, R0
079. ROM:00332D6C MOVEQ R6, #1
080. ROM:00332D70 STREQ R6, [R7,#4]
081. ROM:00332D74 B loc_332DEC
082. ROM:00332D78 ; ---------------------------------------------------------------------------
083. ROM:00332D78
084. ROM:00332D78 key_part3
085. ROM:00332D78 LDR R6, [R7]
086. ROM:00332D7C CMP R6, #1
087. ROM:00332D80 LDREQ R6, [R7,#4]
088. ROM:00332D84 CMPEQ R6, #1
089. ROM:00332D88 LDREQ R2, [R4,#8]
090. ROM:00332D8C EOREQ R2, R2, R1
091. ROM:00332D90 MOVEQ R6, #1
092. ROM:00332D94 STREQ R6, [R7,#8]
093. ROM:00332D98 B loc_332DEC
094. ROM:00332D9C ; ---------------------------------------------------------------------------
095. ROM:00332D9C
096. ROM:00332D9C key_part4
097. ROM:00332D9C LDR R6, [R7]
098. ROM:00332DA0 CMP R6, #1
099. ROM:00332DA4 LDREQ R6, [R7,#4]
100. ROM:00332DA8 CMPEQ R6, #1
101. ROM:00332DAC LDREQ R6, [R7,#8]
102. ROM:00332DB0 CMPEQ R6, #1
103. ROM:00332DB4 LDREQ R3, [R4,#0xC]
104. ROM:00332DB8 EOREQ R3, R3, R2
105. ROM:00332DBC MOVEQ R6, #1
106. ROM:00332DC0 STREQ R6, [R7,#8]
107. ROM:00332DC4 LDR R4, =0x35A036 ; "Key Generated: %s%s%s%s"
108. ROM:00332DC8 BLX loc_332DDC
109. ROM:00332DCC MOV R1, SP
110. ROM:00332DD0 LDR R4, =0x35A05C ; "SP: %x"
111. ROM:00332DD4 BLX loc_332DDC
112. ROM:00332DD8 CODE16
113. ROM:00332DD8
114. ROM:00332DD8 loc_332DD8 ; CODE XREF: generate_key+2Ej
115. ROM:00332DD8 LDR R4, =0x35A020 ; "key not generated"
116. ROM:00332DDA NOP
117. ROM:00332DDC
118. ROM:00332DDC loc_332DDC ; CODE XREF: generate_key+C8p
119. ROM:00332DDC ; generate_key+D4p
120. ROM:00332DDC SUB SP, SP, #4
121. ROM:00332DDE STR R0, [SP,#0x28+var_28]
122. ROM:00332DE0 MOVS R0, R4
123. ROM:00332DE2 LDR R4, =0x68B08D
124. ROM:00332DE4 BLX R4
125. ROM:00332DE6 ADD SP, SP, #4
126. ROM:00332DE8 BLX loc_332DEC
127. ROM:00332DE8 ; End of function generate_key
...
Вспоминаем обьявление функции на строке 9:
generate_key(key_part_num, integrity_validate_table, key_table);
Перед запуском функции, аргументы расставились в эти регистры (таковы правила передачи аргументов в функцию):
key_part_num -> R0
integrity_validate_table -> R1
key_table -> R2
После Function Prologue, на строках 19-21, эти данные ушли в регистры:
key_part_num -> R5
integrity_validate_table -> R7
key_table -> R4
Каждая часть ключа (в адресе на R4) генерируется с проверкой через таблицу целостности (по адресу R7). К примеру, строка 68
LDR R0, [R4]
грузит первый ключ в R0, но после, на строках 69-70, сохраняет единицу в таблицу целостности на R7:
MOVS R6, #1
STR R6, [R7]
Второй ключ (это уже код в ARM режиме) генерируется через EOREQ (по сути, XOR) с первым ключом, на строках 77-78:
LDR R6, [R7]
CMP R6, #1
LDREQ R1, [R4,#4]
EOREQ R1, R1, R0
Помним, что инструкции с приставкой -EQ исполнятся только тогда, когда результат предыдущего сравнения был успешным. На строках 75-76 мы делаем проверку той единицы, которую записали при генерации первого ключа
Поняли, что здесь происходит? Мало того, что второй ключ зависим от первого из-за EOREQ, так еще и генерация второго ключа будет успешна только тогда, когда мы сгенерировали первый.
Все последующие ключи будут сгенерированы только в том случае, если мы сгенерировали предыдущие!
Если глянете на код каждого из ключей - в каждом из них вы увидите все больше проверок целостности. При чем, каждая из проверок имеет сдвиг на 4 байта. Расстановка ключей в регистры R0-R3 сразу бросается в глаза.
Это накладывает на нас определенные ограничения - нам надо сгенерировать ключи поочередно.
Самым интересным является четвертый ключ. Кроме расстановки в R3, там происходит печать всех ключей в консольник диска:
Строка 107. Здесь мы грузим адрес format string "Key Generated: %s%s%s%s" в R4.
Строка 108. Прыжок с линковкой в LR на loc_332DDC.
Строка 120. Готовимся прыгнуть на printf. Помните, я говорил, что если аргументов для функции больше, чем 4, нужно сохранить аргументы на стек? Здесь происходит именно это. От адреса стека отнимается 4 байта (стек увеличивается).
Строка 121. Здесь довольно длинная инструкция. На строке 15 мы видели переменную var_28, которая равнялась -0x28. Здесь происходит рассчет сдвига от адреса в SP, и мы добавляем 0x28 к -0x28. Какой смысл в этой операции? Да абсолютно никакого. Дизассемблер принял определенное значение как переменную, и думал, что она является частью логики программы. Но, это не так.
Эта инструкция сохранит первый ключ на стек. Можем интерпретировать эту инструкцию как
STR R0, [SP]
Строка 122. Задвигаем адрес нашего format string в R0. Это будет первый аргумент для дальнейшего printf.
Строки 123-126. Грузим неизвестный доселе адрес в R4 - это и есть адрес printf. Прыгаем на него с линковкой в LR. После возврата с printf возвращаем адрес стека обратно на 4 байта и прыгаем на loc_332DEC.
Где же дырка?
В этот момент я дал себе отчет, что понял как устроена программа, но найти то, как ее взломать было практически неподьемной задачей. Я копался с этим несколько дней пока не принял решение сдаться, и запросил у ребят из RedBalloonSecurity подсказку (которая у меня, всего на всего, одна!). Меня спросили единственный вопрос - что я вижу на строке 135. Один единственный вопрос решил судьбу. В моей голове тут же сложилась вся картина!
Строка 47. Помним, что после функции ahex2byte, наш ввод был сконвертирован в бинарный.
...
47. ROM:00332D14 BLX ahex2byte
48. ROM:00332D18 LDR R1, =0x6213600
49. ROM:00332D1A
50. ROM:00332D1A loc_332D1A ; CODE XREF: generate_key+10j
51. ROM:00332D1A MOV R2, SP
52. ROM:00332D1C
53. ROM:00332D1C loc_332D1C ; CODE XREF: generate_key+28j
54. ROM:00332D1C LDRB R6, [R1]
55. ROM:00332D1E ADDS R1, R1, #1
56. ROM:00332D20 CMP R6, #0xD
57. ROM:00332D22 BEQ loc_332D2A
58. ROM:00332D24 STRB R6, [R2]
59. ROM:00332D26 ADDS R2, R2, #1
60. ROM:00332D28 B loc_332D1C
61. ROM:00332D2A ; ---------------------------------------------------------------------------
...
Строка 48. Поскольку, перед вызовом ahex2byte мы сдвигали адрес нашего ввода на 2 байта вперед, мы снова помещаем адрес нашего ввода (начиная с первого символа) в R1.
Строка 51. Копируем адрес нашего SP в R2.
Строка 54. Берем первый символ нашего, уже сконвертированного, ввода, и грузим его в регистр R6
Строка 55. Сдвигаем адрес вводимого символа на 1 позицию вперед - по сути, в R1 уже адрес второго символа.
Строка 56. Сравниваем его с символом новой строки.
Строка 57. Если это новая строка, прыгаем на loc_332D2A. Этот адрес является куском логики нашей функции generate_key. То, что происходит на этих строках не имеет никакой связи со взломом, поэтому рассказывать детально, что там происходит, я не буду.
Строка 58. Сохраняем символ нашего ввода по адресу в R2. В последний раз мы записывали в R2 адрес нашего stack pointer. Тоесть, мы ввели что-то ручками в консольник, сконвертировали наш ASCII ввод в бинарный с помощью ahex2byte, а потом записали это на стек.
Строка 59. Добавляем к адресу вводимого символа единицу (переключаемся на следующий символ)
Строка 60. Это цикл. Благодаря этому безусловному прыжку, мы возвращаемся на строку 54 и процесс копирования ввода на стек продолжается. Поскольку единственным условием выхода из цикла является символ новой строки, мы можем вводить в консольник сколько угодно данных. В конце концов цикл упрется в символ новой строки, и программа продолжит свое исполнение.
После каждого ключа, на строках 71, 81, 93, и 126 мы видим прыжок вот сюда.
...
131. ROM:00332DEC loc_332DEC ; CODE XREF: generate_key+58p
132. ROM:00332DEC ; generate_key+74j ...
133. ROM:00332DEC ADD SP, SP, #0x20
134. ROM:00332DF0 LDR LR, [SP],#4
135. ROM:00332DF4 BX LR
...
Строка 133. Здесь происходит уничтожение stack frame - адрес SP увеличивается на 32 байта (здесь видим ADD, но, помним, да? Стек растет вниз! Адрес увеличивается, но сам стек уменьшается).
Строка 134. Грузим адрес, который сохраняли перед вызовом generate_key (сохранение этого адреса на стек происходило до запуска generate_key, и дизассемблированный код мы не видим) из SP (со сдвигом в 4 байта) в LR.
Строка 135. Прыгаем на этот адрес, который взяли со стека - это полный возврат из функции generate_key.
Если мы правильно рассчитаем количество вводимых данных, мы сможем перезаписать адрес на стеке, куда в итоге, должно вернутся исполнение кода после функции generate_key (строка 135).
А вот самый большой прикол! Прыжок на loc_332DEC есть в конце каждого ключа. Там мы уничтожаем stack frame и делаем возврат. Получается, что если мы абьюзим адрес возврата 1 раз, мы, так или иначе, опять прыгаем на loc_332DEC, где от стека снова отнимается 32 байта, опять снимается значение со стека в LR, и опять выполняется возврат. Получается, что мы можем ввести несколько адресов подряд так, чтобы несколько раз "вернуть" исполнение программы по желаемому адресу. Это и есть способ сгенерировать все ключи в нужном порядке.
Поскольку, пока мы доберемся к нашему заабьюзеному участку стека, первый ключ уже будет сгенерирован, нам нужно прыгнуть на 3 адреса - key_part2, key_part3 & key_part4.
Делаем математику
Для правильного расчета нам понадобятся все манипуляции со значением в SP.
Строка 18. Здесь значение SP уменьшается на 16 байт. Но, это можно игнорировать, поскольку оно происходит до начала записи нашего ввода на стек.
Строка 51. Здесь значение SP не изменяется.
Строки 109, 120, 121 и 125. Эти манипуляции с SP происходят на четвертом ключе, и мы их не коснемся пока не зарулим программу в нужное русло. Игнорируем.
Строки 141 и 182. Это является частью функции ahex2byte. Наш SP меняется при входе в функцию ровно так же, как и при выходе. Изменений нет - игнорируем.
Строка 133. А вот это уже имеет значение. Стек будет увеличиваться на эти 32 байта после каждого сгенерированного ключа.
Получается, что мы должны ввести:
[32 байта][адрес 2го ключа][32 байта][адрес 3го ключа][32 байта][адрес 4го ключа]
Думаем о следующем:
ahex2byte конвертирует 2 байта нашего ASCII ввода в 1 бинарный байт. Это значит, что для получения 32-х байт, нам необходимо ввести 64 символа.
Первые 2 вводимых символа мы не конвертировали - в памяти на стеке они представляют собой 2 байта, а не 1.
Мы работаем с little endian архитектурой. Все адреса ключей нужно писать в обратном порядке байт.
Адреса ключей:
key_part2:
00332D5C --> 5C2D3300
key_part3:
00332D78 --> 782D3300
key_part4:
00332D9C --> 9C2D3300
Получается:
["R1"][60 символов]["5C2D3300"][64 символа]["782D3300"][64 символа]["9C2D3300"]
Proof-Of-Concept
Итак, вводим следующее в консольник диска:
R1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5C2D3300AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA782D3300AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9C2D3300
Welcome to minicom 2.7.1
OPTIONS: I18n
Compiled on Aug 13 2017, 15:25:34.
Port /dev/ttyS0, 18:23:04
Press CTRL-A Z for help on special keys
Rst 0x08M
Servo Processor Is Reset.
RW: Disc Ctlr Initialization Completed.
ExecuteSpinRequest
(P) SATA Reset
ASCII Diag mode
F3 T>R1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5C2D3300AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0
Key generated: ${SORRY_HABR_DONT_WANT_TO_LEAK_THE_FLAG}
LED:00000067 FAddr:00000084
LED:00000067 FAddr:00000084
CTRL-A Z for help | 38400 8N1 | NOR | Minicom 2.7.1 | VT102 | Offline | ttyS0
Ура! Мы успешно заставили программу отдать ключи.
Строки, которые вы видите после ключа являются сообщениями об ошибке. Дело в том, что когда мы абьюзим адрес возврата (особенно несколько раз), возврат после key_part4 отправит процессор исполнять неизвестный нам код. И там, черт пойми что происходит - процессор пытается интерпретировать память как исполняемый код по тем адресам, где его может и не быть. В данном случае он был, и нас, каким-то образом, зарулило по адресу 0x00000084. Там он наткнулся на инструкцию, которая не является валидным кодом операции (опкодом). Такие случаи заранее определены в таблице Interrupt Vectors - эта таблица есть в самом начале памяти (адреса сразу после 0x00000000), и она определяет реакцию процессора на те, или иные инциденты. В нашем случае, запись в таблице Interrupt Vectors говорит процессору прыгнуть на тот код, который сформирует строку с адресом невалидного опкода, и покажет это сообщение (что такое LED я до сих пор не понял). Спастись от такого можно только подав hardware reset на какую-то из ножек процессора и, тем самым, перезагрузить плату на жестком диске. Datasheet по таким LSI чипам хранится под 7-мью замками и является конфиденциальной информацией. Как, на какую ножку, и сколько подавать питания - мне (да и никому) не известно. Просто обесточить диск и подать питание снова будет достаточно :D
Пробуем ключ в качестве пароля к архиву level_3.lod.7z.encrypted.
user@ubuntu:/media/user/LEVEL2$ 7z x level_3.lod.7z.encrypted
7-Zip [64] 17.03 : Copyright (c) 1999-2020 Igor Pavlov : 2017-08-28
p7zip Version 17.03 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64)
Scanning the drive for archives:
1 file, 654559 bytes (640 KiB)
Extracting archive: level_3.lod.7z.encrypted
--
Path = level_3.lod.7z.encrypted
Type = 7z
Physical Size = 654559
Headers Size = 143
Method = LZMA:20 7zAES
Solid = -
Blocks = 1
Enter password (will not be echoed): [Ctrl+V here]
Everything is Ok
Size: 1014784
Compressed: 654559
user@ubuntu:/media/user/LEVEL2$ file level_3.lod
level_3.lod: data
Ключ подошел и мы получили новый файл прошивки. Прошив диск этим level_3.lod файлом, и переподключив его, мой debian-неттоп распознал раздел LEVEL3. На нем и продолжится наше приключение в третьей статье этого цикла о взломе диска.
Поскольку в этой статье я давал лишь кусочки из файла level_2.html, думаю, вам будет интересно взглянуть на него целиком. Копируем, сохраняем в html и открываем в браузере:
level_2.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</head>
<body bgcolor="#ffffff">
<span style="white-space: pre; font-family: Courier; color: blue; background: #ffffff">
<span style="color:black">ROM:00332D00
ROM:00332D00 </span><span style="color:gray">; Segment type: Pure code
</span><span style="color:black">ROM:00332D00 </span>AREA ROM, CODE, READWRITE, ALIGN=0
<span style="color:black">ROM:00332D00 </span><span style="color:gray">; ORG 0x332D00
</span><span style="color:black">ROM:00332D00 </span>CODE16
<span style="color:black">ROM:00332D00
ROM:00332D00 </span><span style="color:gray">; =============== S U B R O U T I N E =======================================
</span><span style="color:black">ROM:00332D00
ROM:00332D00 </span>; prototype: generate_key(key_part_num, integrity_validate_table, key_table)
<span style="color:black">ROM:00332D00 </span>; Function called when serial console input is 'R'. Generates key parts in R0-R3.
<span style="color:black">ROM:00332D00 </span>; The next level to reach, the key parts to print you must!
<span style="color:black">ROM:00332D00
ROM:00332D00 </span>generate_key
<span style="color:black">ROM:00332D00
ROM:00332D00 </span><span style="color:green">var_28 </span><span style="color:navy">= -</span><span style="color:#008040">0x28
</span><span style="color:black">ROM:00332D00
ROM:00332D00 </span><span style="color:navy">PUSH {R4-R7,LR}
</span><span style="color:black">ROM:00332D02 </span><span style="color:navy">SUB SP, SP, #</span><span style="color:green">0x10
</span><span style="color:black">ROM:00332D04 </span><span style="color:navy">MOVS R7, R1
</span><span style="color:black">ROM:00332D06 </span><span style="color:navy">MOVS R4, R2
</span><span style="color:black">ROM:00332D08 </span><span style="color:navy">MOVS R5, R0
</span><span style="color:black">ROM:00332D0A </span><span style="color:navy">LDR R1, =</span><span style="color:#008040">0x6213600 </span>; "R"...
<span style="color:black">ROM:00332D0C </span><span style="color:navy">LDRB R0, [R1,#</span><span style="color:green">1</span><span style="color:navy">]
</span><span style="color:black">ROM:00332D0E </span><span style="color:navy">CMP R0, #</span><span style="color:green">0x31
</span><span style="color:black">ROM:00332D10 </span><span style="color:navy">BNE loc_332D1A
</span><span style="color:black">ROM:00332D12 </span><span style="color:navy">ADDS R0, R1, #</span><span style="color:green">2
</span><span style="color:black">ROM:00332D14 </span><span style="color:navy">BLX </span>ahex2byte
<span style="color:black">ROM:00332D18 </span><span style="color:navy">LDR R1, =</span><span style="color:#008040">0x6213600
</span><span style="color:black">ROM:00332D1A
ROM:00332D1A </span><span style="color:navy">loc_332D1A </span><span style="color:green">; CODE XREF: generate_key+10j
</span><span style="color:black">ROM:00332D1A </span><span style="color:navy">MOV R2, SP
</span><span style="color:black">ROM:00332D1C
ROM:00332D1C </span><span style="color:navy">loc_332D1C </span><span style="color:green">; CODE XREF: generate_key+28j
</span><span style="color:black">ROM:00332D1C </span><span style="color:navy">LDRB R6, [R1]
</span><span style="color:black">ROM:00332D1E </span><span style="color:navy">ADDS R1, R1, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D20 </span><span style="color:navy">CMP R6, #</span><span style="color:green">0xD
</span><span style="color:black">ROM:00332D22 </span><span style="color:navy">BEQ loc_332D2A
</span><span style="color:black">ROM:00332D24 </span><span style="color:navy">STRB R6, [R2]
</span><span style="color:black">ROM:00332D26 </span><span style="color:navy">ADDS R2, R2, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D28 </span><span style="color:navy">B loc_332D1C
</span><span style="color:black">ROM:00332D2A </span><span style="color:gray">; ---------------------------------------------------------------------------
</span><span style="color:black">ROM:00332D2A
ROM:00332D2A </span><span style="color:navy">loc_332D2A </span><span style="color:green">; CODE XREF: generate_key+22j
</span><span style="color:black">ROM:00332D2A </span><span style="color:navy">SUBS R5, #</span><span style="color:green">0x49
</span><span style="color:black">ROM:00332D2C </span><span style="color:navy">CMP R5, #</span><span style="color:green">9
</span><span style="color:black">ROM:00332D2E </span><span style="color:navy">BGT loc_332DD8
</span><span style="color:black">ROM:00332D30 </span><span style="color:navy">LSLS R5, R5, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D32 </span><span style="color:navy">ADDS R5, R5, #</span><span style="color:green">6
</span><span style="color:black">ROM:00332D34 </span><span style="color:navy">MOV R0, PC
</span><span style="color:black">ROM:00332D36 </span><span style="color:navy">ADDS R5, R0, R5
</span><span style="color:black">ROM:00332D38 </span><span style="color:navy">LDRH R0, [R5]
</span><span style="color:black">ROM:00332D3A </span><span style="color:navy">ADDS R0, R0, R5
</span><span style="color:black">ROM:00332D3C </span><span style="color:navy">BX R0
</span><span style="color:black">ROM:00332D3C </span><span style="color:gray">; ---------------------------------------------------------------------------
ROM:00332D3E </span><span style="color:navy">DCW </span><span style="color:#008040">0x15
</span><span style="color:gray">ROM:00332D40 </span><span style="color:navy">DCW </span><span style="color:#008040">0xA6
</span><span style="color:gray">ROM:00332D42 </span><span style="color:navy">DCW </span><span style="color:#008040">0xA4
</span><span style="color:gray">ROM:00332D44 </span><span style="color:navy">DCW </span><span style="color:#008040">0xA2
</span><span style="color:gray">ROM:00332D46 </span><span style="color:navy">DCW </span><span style="color:#008040">0xA0
</span><span style="color:gray">ROM:00332D48 </span><span style="color:navy">DCW </span><span style="color:#008040">0x9E
</span><span style="color:gray">ROM:00332D4A </span><span style="color:navy">DCW </span><span style="color:#008040">0x2E
</span><span style="color:gray">ROM:00332D4C </span><span style="color:navy">DCW </span><span style="color:#008040">0x50
</span><span style="color:gray">ROM:00332D4E </span><span style="color:navy">DCW </span><span style="color:#008040">0x98
</span><span style="color:gray">ROM:00332D50 </span><span style="color:navy">DCW </span><span style="color:#008040">0xC
</span><span style="color:black">ROM:00332D52 </span><span style="color:gray">; ---------------------------------------------------------------------------
</span><span style="color:black">ROM:00332D52
ROM:00332D52 </span>key_part1
<span style="color:black">ROM:00332D52 </span><span style="color:navy">LDR R0, [R4]
</span><span style="color:black">ROM:00332D54 </span><span style="color:navy">MOVS R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D56 </span><span style="color:navy">STR R6, [R7]
</span><span style="color:black">ROM:00332D58 </span><span style="color:navy">BLX loc_332DEC
</span><span style="color:black">ROM:00332D5C </span>CODE32
<span style="color:black">ROM:00332D5C
ROM:00332D5C </span>key_part2
<span style="color:black">ROM:00332D5C </span><span style="color:navy">LDR R6, [R7]
</span><span style="color:black">ROM:00332D60 </span><span style="color:navy">CMP R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D64 </span><span style="color:navy">LDREQ R1, [R4,#</span><span style="color:green">4</span><span style="color:navy">]
</span><span style="color:black">ROM:00332D68 </span><span style="color:navy">EOREQ R1, R1, R0
</span><span style="color:black">ROM:00332D6C </span><span style="color:navy">MOVEQ R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D70 </span><span style="color:navy">STREQ R6, [R7,#</span><span style="color:green">4</span><span style="color:navy">]
</span><span style="color:black">ROM:00332D74 </span><span style="color:navy">B loc_332DEC
</span><span style="color:black">ROM:00332D78 </span><span style="color:gray">; ---------------------------------------------------------------------------
</span><span style="color:black">ROM:00332D78
ROM:00332D78 </span>key_part3
<span style="color:black">ROM:00332D78 </span><span style="color:navy">LDR R6, [R7]
</span><span style="color:black">ROM:00332D7C </span><span style="color:navy">CMP R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D80 </span><span style="color:navy">LDREQ R6, [R7,#</span><span style="color:green">4</span><span style="color:navy">]
</span><span style="color:black">ROM:00332D84 </span><span style="color:navy">CMPEQ R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D88 </span><span style="color:navy">LDREQ R2, [R4,#</span><span style="color:green">8</span><span style="color:navy">]
</span><span style="color:black">ROM:00332D8C </span><span style="color:navy">EOREQ R2, R2, R1
</span><span style="color:black">ROM:00332D90 </span><span style="color:navy">MOVEQ R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332D94 </span><span style="color:navy">STREQ R6, [R7,#</span><span style="color:green">8</span><span style="color:navy">]
</span><span style="color:black">ROM:00332D98 </span><span style="color:navy">B loc_332DEC
</span><span style="color:black">ROM:00332D9C </span><span style="color:gray">; ---------------------------------------------------------------------------
</span><span style="color:black">ROM:00332D9C
ROM:00332D9C </span>key_part4
<span style="color:black">ROM:00332D9C </span><span style="color:navy">LDR R6, [R7]
</span><span style="color:black">ROM:00332DA0 </span><span style="color:navy">CMP R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332DA4 </span><span style="color:navy">LDREQ R6, [R7,#</span><span style="color:green">4</span><span style="color:navy">]
</span><span style="color:black">ROM:00332DA8 </span><span style="color:navy">CMPEQ R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332DAC </span><span style="color:navy">LDREQ R6, [R7,#</span><span style="color:green">8</span><span style="color:navy">]
</span><span style="color:black">ROM:00332DB0 </span><span style="color:navy">CMPEQ R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332DB4 </span><span style="color:navy">LDREQ R3, [R4,#</span><span style="color:green">0xC</span><span style="color:navy">]
</span><span style="color:black">ROM:00332DB8 </span><span style="color:navy">EOREQ R3, R3, R2
</span><span style="color:black">ROM:00332DBC </span><span style="color:navy">MOVEQ R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332DC0 </span><span style="color:navy">STREQ R6, [R7,#</span><span style="color:green">8</span><span style="color:navy">]
</span><span style="color:black">ROM:00332DC4 </span><span style="color:navy">LDR R4, =</span><span style="color:#008040">0x35A036 </span>; "Key Generated: %s%s%s%s"
<span style="color:black">ROM:00332DC8 </span><span style="color:navy">BLX loc_332DDC
</span><span style="color:black">ROM:00332DCC </span><span style="color:navy">MOV R1, SP
</span><span style="color:black">ROM:00332DD0 </span><span style="color:navy">LDR R4, =</span><span style="color:#008040">0x35A05C </span>; "SP: %x"
<span style="color:black">ROM:00332DD4 </span><span style="color:navy">BLX loc_332DDC
</span><span style="color:black">ROM:00332DD8 </span>CODE16
<span style="color:black">ROM:00332DD8
ROM:00332DD8 </span><span style="color:navy">loc_332DD8 </span><span style="color:green">; CODE XREF: generate_key+2Ej
</span><span style="color:black">ROM:00332DD8 </span><span style="color:navy">LDR R4, =</span><span style="color:#008040">0x35A020 </span>; "key not generated"
<span style="color:black">ROM:00332DDA </span><span style="color:navy">NOP
</span><span style="color:black">ROM:00332DDC
ROM:00332DDC </span><span style="color:navy">loc_332DDC </span><span style="color:green">; CODE XREF: generate_key+C8p
</span><span style="color:black">ROM:00332DDC </span><span style="color:green">; generate_key+D4p
</span><span style="color:black">ROM:00332DDC </span><span style="color:navy">SUB SP, SP, #</span><span style="color:green">4
</span><span style="color:black">ROM:00332DDE </span><span style="color:navy">STR R0, [SP,#</span><span style="color:green">0x28</span><span style="color:navy">+</span><span style="color:green">var_28</span><span style="color:navy">]
</span><span style="color:black">ROM:00332DE0 </span><span style="color:navy">MOVS R0, R4
</span><span style="color:black">ROM:00332DE2 </span><span style="color:navy">LDR R4, =</span><span style="background:red"><span style="color:navy">0x68B08D</span></span>
<span style="color:black">ROM:00332DE4 </span><span style="color:navy">BLX R4
</span><span style="color:black">ROM:00332DE6 </span><span style="color:navy">ADD SP, SP, #</span><span style="color:green">4
</span><span style="color:black">ROM:00332DE8 </span><span style="color:navy">BLX loc_332DEC
</span><span style="color:black">ROM:00332DE8 </span><span style="color:gray">; End of function generate_key
</span><span style="color:black">ROM:00332DE8
</span><span style="color:maroon">ROM:00332DEC </span>CODE32
<span style="color:maroon">ROM:00332DEC
ROM:00332DEC </span><span style="color:navy">loc_332DEC </span><span style="color:green">; CODE XREF: generate_key+58p
</span><span style="color:maroon">ROM:00332DEC </span><span style="color:green">; generate_key+74j ...
</span><span style="color:maroon">ROM:00332DEC </span><span style="color:navy">ADD SP, SP, #</span><span style="color:green">0x20
</span><span style="color:maroon">ROM:00332DF0 </span><span style="color:navy">LDR LR, [SP],#</span><span style="color:green">4
</span><span style="color:maroon">ROM:00332DF4 </span><span style="color:navy">BX LR
</span><span style="color:black">ROM:00332DF8
ROM:00332DF8 </span><span style="color:gray">; =============== S U B R O U T I N E =======================================
</span><span style="color:black">ROM:00332DF8
ROM:00332DF8
ROM:00332DF8 </span>ahex2byte <span style="color:green">; CODE XREF: generate_key+14p
</span><span style="color:black">ROM:00332DF8 </span><span style="color:navy">STMFD SP!, {R4-R6,LR}
</span><span style="color:black">ROM:00332DFC </span><span style="color:navy">MOV R4, R0
</span><span style="color:black">ROM:00332E00 </span><span style="color:navy">MOV R6, R0
</span><span style="color:black">ROM:00332E04
ROM:00332E04 </span><span style="color:navy">loc_332E04 </span><span style="color:green">; CODE XREF: ahex2byte+6Cj
</span><span style="color:black">ROM:00332E04 </span><span style="color:navy">LDRB R0, [R4]
</span><span style="color:black">ROM:00332E08 </span><span style="color:navy">CMP R0, #</span><span style="color:green">0xD
</span><span style="color:black">ROM:00332E0C </span><span style="color:navy">BEQ loc_332E68
</span><span style="color:black">ROM:00332E10 </span><span style="color:navy">BL sub_332E70
</span><span style="color:black">ROM:00332E14 </span><span style="color:navy">CMN R0, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332E18 </span><span style="color:navy">BNE loc_332E2C
</span><span style="color:black">ROM:00332E1C </span><span style="color:navy">LDRB R0, [R4]
</span><span style="color:black">ROM:00332E20 </span><span style="color:navy">BL sub_332E98
</span><span style="color:black">ROM:00332E24 </span><span style="color:navy">CMN R0, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332E28 </span><span style="color:navy">BEQ locret_332E6C
</span><span style="color:black">ROM:00332E2C
ROM:00332E2C </span><span style="color:navy">loc_332E2C </span><span style="color:green">; CODE XREF: ahex2byte+20j
</span><span style="color:black">ROM:00332E2C </span><span style="color:navy">MOV R5, R0
</span><span style="color:black">ROM:00332E30 </span><span style="color:navy">LDRB R0, [R4,#</span><span style="color:green">1</span><span style="color:navy">]
</span><span style="color:black">ROM:00332E34 </span><span style="color:navy">BL sub_332E70
</span><span style="color:black">ROM:00332E38 </span><span style="color:navy">CMN R0, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332E3C </span><span style="color:navy">BNE loc_332E50
</span><span style="color:black">ROM:00332E40 </span><span style="color:navy">LDRB R0, [R4,#</span><span style="color:green">1</span><span style="color:navy">]
</span><span style="color:black">ROM:00332E44 </span><span style="color:navy">BL sub_332E98
</span><span style="color:black">ROM:00332E48 </span><span style="color:navy">CMN R0, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332E4C </span><span style="color:navy">BEQ locret_332E6C
</span><span style="color:black">ROM:00332E50
ROM:00332E50 </span><span style="color:navy">loc_332E50 </span><span style="color:green">; CODE XREF: ahex2byte+44j
</span><span style="color:black">ROM:00332E50 </span><span style="color:navy">MOV R5, R5,LSL#4
</span><span style="color:black">ROM:00332E54 </span><span style="color:navy">ADD R0, R5, R0
</span><span style="color:black">ROM:00332E58 </span><span style="color:navy">STRB R0, [R6]
</span><span style="color:black">ROM:00332E5C </span><span style="color:navy">ADD R4, R4, #</span><span style="color:green">2
</span><span style="color:black">ROM:00332E60 </span><span style="color:navy">ADD R6, R6, #</span><span style="color:green">1
</span><span style="color:black">ROM:00332E64 </span><span style="color:navy">B loc_332E04
</span><span style="color:black">ROM:00332E68 </span><span style="color:gray">; ---------------------------------------------------------------------------
</span><span style="color:black">ROM:00332E68
ROM:00332E68 </span><span style="color:navy">loc_332E68 </span><span style="color:green">; CODE XREF: ahex2byte+14j
</span><span style="color:black">ROM:00332E68 </span><span style="color:navy">STRB R0, [R6]
</span><span style="color:black">ROM:00332E6C
ROM:00332E6C </span><span style="color:navy">locret_332E6C </span><span style="color:green">; CODE XREF: ahex2byte+30j
</span><span style="color:black">ROM:00332E6C </span><span style="color:green">; ahex2byte+54j
</span><span style="color:black">ROM:00332E6C </span><span style="color:navy">LDMFD SP!, {R4-R6,PC}
</span><span style="color:black">ROM:00332E6C </span><span style="color:gray">; End of function ahex2byte
</span><span style="color:black">ROM:00332E6C
ROM:00332E70
ROM:00332E70 </span><span style="color:gray">; =============== S U B R O U T I N E =======================================
</span><span style="color:black">ROM:00332E70
ROM:00332E70
ROM:00332E70 </span><span style="color:navy">sub_332E70 </span><span style="color:green">; CODE XREF: ahex2byte+18p
</span><span style="color:black">ROM:00332E70 </span><span style="color:green">; ahex2byte+3Cp
</span><span style="color:black">ROM:00332E70 </span><span style="color:navy">CMP R0, #</span><span style="color:green">0xD
</span><span style="color:black">ROM:00332E74 </span><span style="color:navy">BEQ loc_332E90
</span><span style="color:black">ROM:00332E78 </span><span style="color:navy">CMP R0, #</span><span style="color:green">0x30
</span><span style="color:black">ROM:00332E7C </span><span style="color:navy">BLT loc_332E90
</span><span style="color:black">ROM:00332E80 </span><span style="color:navy">CMP R0, #</span><span style="color:green">0x39
</span><span style="color:black">ROM:00332E84 </span><span style="color:navy">BGT loc_332E90
</span><span style="color:black">ROM:00332E88 </span><span style="color:navy">SUB R0, R0, #</span><span style="color:green">0x30
</span><span style="color:black">ROM:00332E8C </span><span style="color:navy">B locret_332E94
</span><span style="color:black">ROM:00332E90 </span><span style="color:gray">; ---------------------------------------------------------------------------
</span><span style="color:black">ROM:00332E90
ROM:00332E90 </span><span style="color:navy">loc_332E90 </span><span style="color:green">; CODE XREF: sub_332E70+4j
</span><span style="color:black">ROM:00332E90 </span><span style="color:green">; sub_332E70+Cj ...
</span><span style="color:black">ROM:00332E90 </span><span style="color:navy">MVN R0, #</span><span style="color:green">0
</span><span style="color:black">ROM:00332E94
ROM:00332E94 </span><span style="color:navy">locret_332E94 </span><span style="color:green">; CODE XREF: sub_332E70+1Cj
</span><span style="color:black">ROM:00332E94 </span><span style="color:navy">BX LR
</span><span style="color:black">ROM:00332E94 </span><span style="color:gray">; End of function sub_332E70
</span><span style="color:black">ROM:00332E94
ROM:00332E98
ROM:00332E98 </span><span style="color:gray">; =============== S U B R O U T I N E =======================================
</span><span style="color:black">ROM:00332E98
ROM:00332E98
ROM:00332E98 </span><span style="color:navy">sub_332E98 </span><span style="color:green">; CODE XREF: ahex2byte+28p
</span><span style="color:black">ROM:00332E98 </span><span style="color:green">; ahex2byte+4Cp
</span><span style="color:black">ROM:00332E98 </span><span style="color:navy">CMP R0, #</span><span style="color:green">0x41
</span><span style="color:black">ROM:00332E9C </span><span style="color:navy">BLT loc_332EB4
</span><span style="color:black">ROM:00332EA0 </span><span style="color:navy">CMP R0, #</span><span style="color:green">0x46
</span><span style="color:black">ROM:00332EA4 </span><span style="color:navy">BGT loc_332EB4
</span><span style="color:black">ROM:00332EA8 </span><span style="color:navy">SUB R0, R0, #</span><span style="color:green">0x41
</span><span style="color:black">ROM:00332EAC </span><span style="color:navy">ADD R0, R0, #</span><span style="color:green">0xA
</span><span style="color:black">ROM:00332EB0 </span><span style="color:navy">B locret_332EB8
</span><span style="color:black">ROM:00332EB4 </span><span style="color:gray">; ---------------------------------------------------------------------------
</span><span style="color:black">ROM:00332EB4
ROM:00332EB4 </span><span style="color:navy">loc_332EB4 </span><span style="color:green">; CODE XREF: sub_332E98+4j
</span><span style="color:black">ROM:00332EB4 </span><span style="color:green">; sub_332E98+Cj
</span><span style="color:black">ROM:00332EB4 </span><span style="color:navy">MVN R0, #</span><span style="color:green">0
</span><span style="color:black">ROM:00332EB8
ROM:00332EB8 </span><span style="color:navy">locret_332EB8 </span><span style="color:green">; CODE XREF: sub_332E98+18j
</span><span style="color:black">ROM:00332EB8 </span><span style="color:navy">BX LR
</span><span style="color:black">ROM:00332EB8 </span><span style="color:gray">; End of function sub_332E98
</span><span style="color:black">ROM:00332EB8
ROM:00332EB8 </span><span style="color:gray">; ---------------------------------------------------------------------------
ROM:00332EBC </span><span style="color:navy">dword_332EBC DCD </span><span style="color:#008040">0x6213600 </span><span style="color:#8080ff">; DATA XREF: generate_key+Ar
</span><span style="color:gray">ROM:00332EC0 </span><span style="color:navy">dword_332EC0 DCD </span><span style="color:#008040">0x6213600 </span><span style="color:#8080ff">; DATA XREF: generate_key+18r
</span><span style="color:gray">ROM:00332EC4 </span><span style="color:navy">dword_332EC4 DCD </span><span style="color:#008040">0x35A036 </span><span style="color:#8080ff">; DATA XREF: generate_key+C4r
</span><span style="color:gray">ROM:00332EC8 </span><span style="color:navy">dword_332EC8 DCD </span><span style="color:#008040">0x35A05C </span><span style="color:#8080ff">; DATA XREF: generate_key+D0r
</span><span style="color:gray">ROM:00332ECC </span><span style="color:navy">dword_332ECC DCD </span><span style="color:#008040">0x35A020 </span><span style="color:#8080ff">; DATA XREF: generate_key:loc_332DD8r
</span><span style="color:gray">ROM:00332ED0 </span><span style="color:navy">off_332ED0 DCD </span><span style="background:red"><span style="color:navy">0x68B08D</span></span> <span style="color:#8080ff">; DATA XREF: generate_key+E2r
</span><span style="color:gray">ROM:00332ED4 </span><span style="color:navy">DCB </span><span style="color:#008040">0</span><span style="color:navy">, </span><span style="color:#008040">0</span><span style="color:navy">, </span><span style="color:#008040">0</span><span style="color:navy">, </span><span style="color:#008040">0
</span><span style="color:gray">ROM:00332ED4 ; ROM ends
ROM:00332ED4
ROM:00332ED4 </span>END
</span></body></html>
Что может быть сложнее?
Друзья! Предыдущая статья была заплюсована до 2го месте в топе за сутки! Это просто феноменально! Я искренне благодарен каждому, кто ее посмотрел.
Статью, которую вы дочитали сейчас, было невероятно сложно писать. Я уделял ей время каждый вечер на протяжении последней недели. Обьяснять такие вещи на широкую публику чертовски сложно, поэтому я буду очень благодарен вашим плюсикам.
Моего понимания было достаточно (почти достаточно - подсказку я, все же просрал) для того чтоб решить эту задачу, но как видите, во многих местах я теряюсь. Все что я описал в этой статье является лишь моим видением происходящего, и если вы видите парочку бредовых мыслей, не стесняйтесь писать комментарий, или написать мне в личку. Я буду только рад тому, что пойму эти вещи лучше.
Также, подписывайтесь на мой инстаграм - @o.tkachuk
Последние 6 лет я работаю, и параллельно путешествую по Европе. Засирать ленту не буду. You have my word on it.
В следующей статье нас ждет самое сложное задание, с которым я боролся полтора месяца. Но, оно того стоило - я написал свой, первый в жизни, шеллкод. Так что, stay tuned!
P.S. Хочу передать привет Андрюхе. Спасибо, что давал root тогда, когда я ходил пешком под стол.