Ломаем зашифрованный диск для собеседования от RedBalloonSecurity. Part 0x01

    По мотивам
    Часть 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
    1. level_2.lod - это новый файл прошивки для диска. Прошиваемся так же, как и в предыдущей статье. Здесь ничего нового.

    2. level_3.lod.7z.encrypted - это файл прошивки для следующего уровня. Судя по его разрешению, файл находится в запароленном 7z архиве. Нам нужно решить текущий уровень чтоб достать пароль от следующего.

    3. level2_instructions.txt - это, собственно, инструкции что и как делать. Подсказки тоже имеются.

    4. 0001-keystone-armv5.patch - это патч для Keystone Assembler. Keystone это компилятор и набор С-шных библиотек для перевода ассемблерного кода в опкоды для процессора.

    5. 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 <[email protected]>
    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):

    Стоит оговориться, что механизм по созданию и уничтожению stack frame'ов для меня до сих пор не совсем понятен. Поэтому, очень советую поискать что-то на ютубе. Для понимания таких вещей нужна визуальная картинка.

    В предыдущем абзаце я сказал, что при создании нового сегмента стека, адреса BP & SP смещаются ниже. Дело в том, что "так сложилось исторически". Стек это LIFO конструкция, размер которой меняется в процессе исполнения программы. И компилятор не знает какого размера он может быть. То же самое касается кучи (heap) - памяти, которую мы запрашиваем у системы через семейство вызовов malloc (glibc). При выделении памяти в куче, ее адреса растут вверх, а вот когда растет стек, его адреса растут вниз. Стоит оговориться, что мы работаем с embeded устройством. Понятия кучи здесь, может и не быть (опять же, прошу экспертов меня поправить).

    У ARM процессоров есть специальное семейство инструкций, которые работают со стекомPUSHPOPSTMFDLDMFD (уверен, есть еще, но для наших грязных делишек этого хватит).

    • PUSH кладет то, что в аргументе на стек, и увеличивает его (на самом деле уменьшает адрес)

    • POP снимает со стека данные, кладет туда, куда указывает аргумент, и уменьшает стек (на самом деле увеличивает адрес)

    • STMFDLDMFD (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>R1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5C2D3300AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA782D3300AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9C2D3300
    
    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 &#039;R&#039;. 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>; &quot;R&quot;...
    <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>; &quot;Key Generated: %s%s%s%s&quot;
    <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>; &quot;SP: %x&quot;
    <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>; &quot;key not generated&quot;
    <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!

    Благодарочка пользователю @raven19 за вычитку этой статьи и ликбез по русскому языку.

    P.S. Хочу передать привет Андрюхе. Спасибо, что давал root тогда, когда я ходил пешком под стол.

    Средняя зарплата в IT

    120 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 5 525 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 29

      0
      Ничего не понятно, но очень интересно!
        0
        Я старался сделать статью понятной для большинства. Видимо не достарался)
        Спасибо! Говорю сразу, чтоб понять третью часть, понимание этой обязательно.
        +1
        Причины по которым были созданы эти 2 режима мне неизвестны (знатоки в комментариях очень даже приветствуются)
        Денис Юричев «Reverse Engineering для начинающих»:
        «x86 всегда был архитектурой с инструкциями переменной длины, так что когда пришла 64-битная эра, расширения x64 не очень сильно повлияли на ISA. ARM это RISC4-процессор разработанный с учетом инструкций одинаковой длины, что было некоторым преимуществом в прошлом. Так что в самом начале все инструкции ARM кодировались 4-мя байтами. Это то, что сейчас называется «режим ARM». Потом они подумали, что это не очень экономично. На самом деле, самые используемые инструкции процессора на практике могут быть закодированы c использованием меньшего количества информации. Так что они добавили другую ISA с названием Thumb, где каждая инструкция кодируется всего лишь 2-мя байтами. Теперь это называется «режим Thumb».»

        Т.е. похоже все дело в простой экономии памяти.
          0
          Вау. Спасибо!
          +1
          «Шикарное» изложение! Осталось только разбить все слова на слоги и обозначить ударные слоги в словах (проставить/обозначить ударение). И сразу станет всё предельно ясно и понятно!
            0
            Опять таки, спасибо за правки по грамматике. Из меня тот еще писатель)
            –1
            Зачем проходить такой квест, если можно было взять оригинальный образ флешки для этого диска, «доработать» его для того, чтобы он смог работать с гермоблоком, и сразу прочитать все данные? Любой DR специалист сделает это за пару минут.
              0

              А что это за процедура с гермоблоком? Имеешь в виду образ флеш памяти с платы hdd?

                0
                А вы подумайте. Диск с оригинальной прошивкой дает доступ к полной «поляне». А вам прислали с модифицированной варью, где размер юзер-области «подрезан». Но в ПЗУ кроме собственно исполняемого кода есть адаптивные параметры конкретного гермоблока. Вам это знать необязательно, а любая DR контора в курсе, и сделает вам эту операцию по «анлоку» за пару минут
                0
                данные на блинах наверняка тоже шифрованы
                  0
                  Нет, эти диски не умеют в FDE
                    0
                    Судя по подсказкам в предыдущей статье — в процессе загрузки диска участвуют 3 составляющие — IC, Winbond Flash и блины.
                    Мне кажется, что если, к примеру, прикрутить плату к HDD с другого похожего HDD, диск заблокируется. Данные, наверное, не шифрованы. Но, происходит сверка каких-то данные между этими тремя компонентами. И, если все гуд, диск работает как надо. Но, если соответствия нет, на блины запишутся данные, которые его заблокируют. А, кроме платы, ничто не может сменить данные на блинах.

                    Плюс, мне кажется, что код, который отвечает за генерацию ключей лежит исключительно внутри IC, или Winbond Flash. На блинах нету ничего, что связано с этим уровнем. Так или иначе, мы работаем с debug меню диска. И, раз это debug меню, было бы глупо располагать его на блины: блин сломался — дебега нету.
                      0
                      никаких сверок. Просто в содержимом микросхемы флеш (это не обязательно будет винбонд) есть и код, и данные. Данные — необходимы для нормального старта диска, так как нет двух одинаковых по характеристикам дисков (параметры головок и поверхностей). Эти параметры тоже лежат в микросхеме флеш. Естественно, в отличии от микропрограммы — эти параметры индивидуальны. Поэтому просто переставить плату — не прокатит, но и ничего не заблокируется. А вот модифицировать/перенести код — никто не мешает.
                        0
                        Я пытался прогнать дизассемблером по прошивке с флеш, но там все запаковано. Я вряд-ли смог бы достать ключи вот таким способом.

                        Короче, я уверен, что были и другие способы взломать этот челлендж. И, ребятам из редбалун были бы точно интересны другие способы взлома. Но, мои познания в железяках ограничены. Это мой первый прошитый чип.
                          0
                          а зачем вам эти ключи? они же нужны только для дальнейшего секаса с прошивкой, но не нужны для кошелька, верно я понял условие задачи?
                            0
                            Для кошелька как такового они не нужны — они нужны лишь для перехода на следующий левел. В процессе взлома я понятия не имел в каком формате я получу кошелек. Он мог лежать как угодно — мог просто располагаться в памяти, мог быть где-то в каком-то зипе на одном из разделов, или я мог бы вообще в конце концов получить список адресов памяти с флеш-чипа, по которым бы и собрал публичный и приватный ключи, или вообще — в формате ссылки на какой-то мегазакрытый nginx. Какие-то обходные варианты наверное были, но я шел «верным» путем.
                              0
                              ИМХО — идти предопределенным путём — плохой способ. Нужно уметь глядеть шире и видеть другие возможности, дающие результат с бОльшей вероятностью и быстрей. Мне очень интересно, чем закончится ваш квест, пишите обязательно, и особенно — закончится ли он приглашением на работу.
                                0
                                Здесь полностью согласен. Ребятам был как раз интересен нестандартный путь решения.
                            0
                            Я пытался прогнать дизассемблером по прошивке с флеш, но там все запаковано.

                            возьмите помощь зала. Их есть у нас :)
                    +1
                    Стек это FIFO конструкция
                    FIFO — это «первым зашёл — первым вышел». По этому принципу работают очереди. У стека несколько другой принцип: «последним зашёл — первым вышел», то есть LIFO. Прошу прощения за занудство.
                      0
                      Точняк. Поправляю.
                      +1
                      что такое LED я до сих пор не понял

                      Это специфичное для дисков Сигейт состояние firmware. LED является аналогом try… catch с переходом в abort, где собственно abort и будет состоянием LED. т.е. диск падает в контролируемый exception, сообщая либо адрес, где произошло исключение, либо код ошибки, который подскажет что именно произошло.

                      Для контролируемой передачи управления на вектор обработки LED исключений (на Сигейтах) используется ARM-инструкция SVC (aka Supervisor Call), которая пошлет диск в обработчик SVC (тоже есть в таблице векторов)
                        0
                        Опа. Человек из data recovery. Это задание как-то можно было решить слив данные с блинов, или как-то из flash чипа?
                          0
                          Это задание как-то можно было решить слив данные с блинов, или как-то из flash чипа?

                          это зависит от цели вашего квеста.
                          Если вы всего лишь хотите добраться до последнего раздела с данными (который вероятно не виден из-за обрезанного размера диска), то да, достаточно влить правильную прошивку и вы увидите все пространство диска, со всеми разделами.
                          Если же цель пройти все задания, то нет. Так как ключ генерится посредством самописного алгоритма и не хранится в открытом виде.
                            0
                            Целью было добратся до битков и возможный переезд в NYC. А как я пойму, какую прошивку лить? если солью с другого диска, я не смогу «завести» этот. верно?
                              0
                              скиньте содержимое флешки в виде файла. И какой-нибудь из рабочих lod файлов
                                0
                                В предыдущей статье я решил выставить фотку консоли по той причине, что мне влом опять повторять процесс включения 4х медных жил в 5 отверствий и крепить их скотчем. К сожалению, то, что я считывал удалилось из-за давности) Но, мне интересно, что Вы скажете. Сброшу на выходных.
                                  0
                                  Если лень читать флешку, то можно просто level_1_makesureitsnotcorrupted.lod файл, я попробую ограничиться только им.
                                  Хотя содержимое флешки сильно облегчило бы задачу.
                        0
                        Ох блин, давно не встречались такие замороченные истории с передвиганием байтиков. Спасибо, очень интересно и более чем напрягает мозг.

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

                        Самое читаемое