А что дальше?
Хабровчане и хабровчушки, эта статья является долгожданным (ага, в пару дней) продолжением моей предыдущей статьи о взломе жесткого диска для собеседования в инфосек компанию RedBalloonSecurity. Любителей поковырять железяки я спешу разочаровать, поскольку все дальнейшие манипуляции с диском будут проводится только на уровне ассемблерного кода и логики. Поэтому, приготовьте чай/кофе или чего покрепче, ведь мы снова лезем в embeded дебри и опять пускаемся в неизвестность.
LEVEL2
Моему счастью не было предела когда я залил пропатченую прошивку на Winbond Flash чип и ядро моего Debian-неттопа распознало еще 1 раздел диска. Здесь будет немного больше информации, чем в предыдущем разделе. Ведь повышая уровень, сложность нашей с вами задачи только увеличивается.
Содержимое раздела:
tkchk@ubuntu:/media/user/LEVEL2$ file *0001-keystone-armv5.patch: unified diff output, ASCII textlevel_2.html: HTML document, ASCII text, with very long lineslevel2_instructions.txt: ASCII textlevel_2.lod: datalevel_3.lod.7z.encrypted: 7-zip archive data, version 0.3
-
level_2.lod - это новый файл прошивки для диска. Прошиваемся так же, как и в предыдущей статье. Здесь ничего нового.
-
level_3.lod.7z.encrypted - это файл прошивки для следующего уровня. Судя по его разрешению, файл находится в запароленном 7z архиве. Нам нужно решить текущий уровень чтоб достать пароль от слудующего.
-
level2_instructions.txt - это, собственно, инструкции что и как делать. Подсказки тоже имеются.
-
0001-leystone-armv5.patch - это патч для Keystone Assembler. Keystone это компилятор и набор С-шных библиотек для перевода ассемблерного кода в опкоды для процессора. Об этом чуть позже.
-
level_2.html - изюминка текущего уровня. Выглядит точ-в-точ как текст, который генерирует IDA Pro при загрузке бинарника.
Для тех, кто не в курсе, IDA Pro это программа для дизассемблирования. Дело в том, что когда мы пишем код на высокоуровневых языках (C, Python и тд) и подвергаем его компиляции, мы переводим наш +- human-readable текст в язык машинного кода. Тоесть, мы опускаем более понятную человеку логику в логику, которая больше понятна машине. Машина не понимает что такое функция, ведь функция, это скорее абстракция в голове у программиста. Процесс дизассемблирования позволяет сделать наоборот - поднять логику из машинного на человекопонятный уровень. Некоторые дизассемблеры позволяют поднять логику даже на C-шный уровень, хоть и не всегда делают это корректно (на самом деле очень даже корректно, но читать такой код порой бывает сложнее чем ассемблер). Если работаем с чем-то мелким, и надо кабанчиком понять что там происходит - этого хватит, но для более высокоточных вещей нужен уровень ассемблера. Это мы и получили в виде level_2.html файла.
tkchk@ubuntu:/media/user/LEVEL2$ cat level2_instructions.txt Congratulations... you have made it to the other sideBack 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 keystoneassembler to force the correct architecture.Also, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASCII
0001-keystone-armv5.patch
tkchk@ubuntu-mac:/media/tkchk/LEVEL2$ cat 0001-keystone-armv5.patch From 5532e7ccbc6c794545530eb725bed548cbc1ac3e Mon Sep 17 00:00:00 2001From: mysteriousmysteries <mysteriousmysteries@redballoonsecurity.com>Date: Wed, 15 Feb 2017 09:23:31 -0800Subject: [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.cppindex 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
ROM:00332D00ROM:00332D00 ; Segment type: Pure codeROM:00332D00 AREA ROM, CODE, READWRITE, ALIGN=0ROM:00332D00 ; ORG 0x332D00ROM:00332D00 CODE16ROM:00332D00ROM:00332D00 ; =============== S U B R O U T I N E =======================================ROM:00332D00ROM:00332D00 ; prototype: generate_key(key_part_num, integrity_validate_table, key_table)ROM:00332D00 ; Function called when serial console input is 'R'. Generates key parts in R0-R3.ROM:00332D00 ; The next level to reach, the key parts to print you must!ROM:00332D00ROM:00332D00 generate_keyROM:00332D00ROM:00332D00 var_28 = -0x28ROM:00332D00ROM:00332D00 PUSH {R4-R7,LR}ROM:00332D02 SUB SP, SP, #0x10ROM:00332D04 MOVS R7, R1ROM:00332D06 MOVS R4, R2ROM:00332D08 MOVS R5, R0ROM:00332D0A LDR R1, =0x6213600 ; "R"...ROM:00332D0C LDRB R0, [R1,#1]ROM:00332D0E CMP R0, #0x31ROM:00332D10 BNE loc_332D1AROM:00332D12 ADDS R0, R1, #2ROM:00332D14 BLX ahex2byteROM:00332D18 LDR R1, =0x6213600ROM:00332D1AROM:00332D1A loc_332D1A ; CODE XREF: generate_key+10jROM:00332D1A MOV R2, SPROM:00332D1CROM:00332D1C loc_332D1C ; CODE XREF: generate_key+28jROM:00332D1C LDRB R6, [R1]ROM:00332D1E ADDS R1, R1, #1ROM:00332D20 CMP R6, #0xDROM:00332D22 BEQ loc_332D2AROM:00332D24 STRB R6, [R2]ROM:00332D26 ADDS R2, R2, #1ROM:00332D28 B loc_332D1CROM:00332D2A ; ---------------------------------------------------------------------------ROM:00332D2AROM:00332D2A loc_332D2A ; CODE XREF: generate_key+22jROM:00332D2A SUBS R5, #0x49ROM:00332D2C CMP R5, #9ROM:00332D2E BGT loc_332DD8ROM:00332D30 LSLS R5, R5, #1ROM:00332D32 ADDS R5, R5, #6ROM:00332D34 MOV R0, PCROM:00332D36 ADDS R5, R0, R5ROM:00332D38 LDRH R0, [R5]ROM:00332D3A ADDS R0, R0, R5ROM:00332D3C BX R0ROM:00332D3C ; ---------------------------------------------------------------------------ROM:00332D3E DCW 0x15ROM:00332D40 DCW 0xA6ROM:00332D42 DCW 0xA4ROM:00332D44 DCW 0xA2ROM:00332D46 DCW 0xA0ROM:00332D48 DCW 0x9EROM:00332D4A DCW 0x2EROM:00332D4C DCW 0x50ROM:00332D4E DCW 0x98ROM:00332D50 DCW 0xCROM:00332D52 ; ---------------------------------------------------------------------------ROM:00332D52ROM:00332D52 key_part1ROM:00332D52 LDR R0, [R4]ROM:00332D54 MOVS R6, #1ROM:00332D56 STR R6, [R7]ROM:00332D58 BLX loc_332DECROM:00332D5C CODE32ROM:00332D5CROM:00332D5C key_part2ROM:00332D5C LDR R6, [R7]ROM:00332D60 CMP R6, #1ROM:00332D64 LDREQ R1, [R4,#4]ROM:00332D68 EOREQ R1, R1, R0ROM:00332D6C MOVEQ R6, #1ROM:00332D70 STREQ R6, [R7,#4]ROM:00332D74 B loc_332DECROM:00332D78 ; ---------------------------------------------------------------------------ROM:00332D78ROM:00332D78 key_part3ROM:00332D78 LDR R6, [R7]ROM:00332D7C CMP R6, #1ROM:00332D80 LDREQ R6, [R7,#4]ROM:00332D84 CMPEQ R6, #1ROM:00332D88 LDREQ R2, [R4,#8]ROM:00332D8C EOREQ R2, R2, R1ROM:00332D90 MOVEQ R6, #1ROM:00332D94 STREQ R6, [R7,#8]ROM:00332D98 B loc_332DECROM:00332D9C ; ---------------------------------------------------------------------------ROM:00332D9CROM:00332D9C key_part4ROM:00332D9C LDR R6, [R7]ROM:00332DA0 CMP R6, #1ROM:00332DA4 LDREQ R6, [R7,#4]ROM:00332DA8 CMPEQ R6, #1ROM:00332DAC LDREQ R6, [R7,#8]ROM:00332DB0 CMPEQ R6, #1ROM:00332DB4 LDREQ R3, [R4,#0xC]ROM:00332DB8 EOREQ R3, R3, R2ROM:00332DBC MOVEQ R6, #1ROM:00332DC0 STREQ R6, [R7,#8]ROM:00332DC4 LDR R4, =0x35A036 ; "Key Generated: %s%s%s%s"ROM:00332DC8 BLX loc_332DDCROM:00332DCC MOV R1, SPROM:00332DD0 LDR R4, =0x35A05C ; "SP: %x"ROM:00332DD4 BLX loc_332DDCROM:00332DD8 CODE16ROM:00332DD8ROM:00332DD8 loc_332DD8 ; CODE XREF: generate_key+2EjROM:00332DD8 LDR R4, =0x35A020 ; "key not generated"ROM:00332DDA NOPROM:00332DDCROM:00332DDC loc_332DDC ; CODE XREF: generate_key+C8pROM:00332DDC ; generate_key+D4pROM:00332DDC SUB SP, SP, #4ROM:00332DDE STR R0, [SP,#0x28+var_28]ROM:00332DE0 MOVS R0, R4ROM:00332DE2 LDR R4, =0x68B08DROM:00332DE4 BLX R4ROM:00332DE6 ADD SP, SP, #4ROM:00332DE8 BLX loc_332DECROM:00332DE8 ; End of function generate_keyROM:00332DE8ROM:00332DEC CODE32ROM:00332DECROM:00332DEC loc_332DEC ; CODE XREF: generate_key+58pROM:00332DEC ; generate_key+74j ...ROM:00332DEC ADD SP, SP, #0x20ROM:00332DF0 LDR LR, [SP],#4ROM:00332DF4 BX LRROM:00332DF8ROM:00332DF8 ; =============== S U B R O U T I N E =======================================ROM:00332DF8ROM:00332DF8ROM:00332DF8 ahex2byte ; CODE XREF: generate_key+14pROM:00332DF8 STMFD SP!, {R4-R6,LR}ROM:00332DFC MOV R4, R0ROM:00332E00 MOV R6, R0ROM:00332E04ROM:00332E04 loc_332E04 ; CODE XREF: ahex2byte+6CjROM:00332E04 LDRB R0, [R4]ROM:00332E08 CMP R0, #0xDROM:00332E0C BEQ loc_332E68ROM:00332E10 BL sub_332E70ROM:00332E14 CMN R0, #1ROM:00332E18 BNE loc_332E2CROM:00332E1C LDRB R0, [R4]ROM:00332E20 BL sub_332E98ROM:00332E24 CMN R0, #1ROM:00332E28 BEQ locret_332E6CROM:00332E2CROM:00332E2C loc_332E2C ; CODE XREF: ahex2byte+20jROM:00332E2C MOV R5, R0ROM:00332E30 LDRB R0, [R4,#1]ROM:00332E34 BL sub_332E70ROM:00332E38 CMN R0, #1ROM:00332E3C BNE loc_332E50ROM:00332E40 LDRB R0, [R4,#1]ROM:00332E44 BL sub_332E98ROM:00332E48 CMN R0, #1ROM:00332E4C BEQ locret_332E6CROM:00332E50ROM:00332E50 loc_332E50 ; CODE XREF: ahex2byte+44jROM:00332E50 MOV R5, R5,LSL#4ROM:00332E54 ADD R0, R5, R0ROM:00332E58 STRB R0, [R6]ROM:00332E5C ADD R4, R4, #2ROM:00332E60 ADD R6, R6, #1ROM:00332E64 B loc_332E04ROM:00332E68 ; ---------------------------------------------------------------------------ROM:00332E68ROM:00332E68 loc_332E68 ; CODE XREF: ahex2byte+14jROM:00332E68 STRB R0, [R6]ROM:00332E6CROM:00332E6C locret_332E6C ; CODE XREF: ahex2byte+30jROM:00332E6C ; ahex2byte+54jROM:00332E6C LDMFD SP!, {R4-R6,PC}ROM:00332E6C ; End of function ahex2byteROM:00332E6CROM:00332E70ROM:00332E70 ; =============== S U B R O U T I N E =======================================ROM:00332E70ROM:00332E70ROM:00332E70 sub_332E70 ; CODE XREF: ahex2byte+18pROM:00332E70 ; ahex2byte+3CpROM:00332E70 CMP R0, #0xDROM:00332E74 BEQ loc_332E90ROM:00332E78 CMP R0, #0x30ROM:00332E7C BLT loc_332E90ROM:00332E80 CMP R0, #0x39ROM:00332E84 BGT loc_332E90ROM:00332E88 SUB R0, R0, #0x30ROM:00332E8C B locret_332E94ROM:00332E90 ; ---------------------------------------------------------------------------ROM:00332E90ROM:00332E90 loc_332E90 ; CODE XREF: sub_332E70+4jROM:00332E90 ; sub_332E70+Cj ...ROM:00332E90 MVN R0, #0ROM:00332E94ROM:00332E94 locret_332E94 ; CODE XREF: sub_332E70+1CjROM:00332E94 BX LRROM:00332E94 ; End of function sub_332E70ROM:00332E94ROM:00332E98ROM:00332E98 ; =============== S U B R O U T I N E =======================================ROM:00332E98ROM:00332E98ROM:00332E98 sub_332E98 ; CODE XREF: ahex2byte+28pROM:00332E98 ; ahex2byte+4CpROM:00332E98 CMP R0, #0x41ROM:00332E9C BLT loc_332EB4ROM:00332EA0 CMP R0, #0x46ROM:00332EA4 BGT loc_332EB4ROM:00332EA8 SUB R0, R0, #0x41ROM:00332EAC ADD R0, R0, #0xAROM:00332EB0 B locret_332EB8ROM:00332EB4 ; ---------------------------------------------------------------------------ROM:00332EB4ROM:00332EB4 loc_332EB4 ; CODE XREF: sub_332E98+4jROM:00332EB4 ; sub_332E98+CjROM:00332EB4 MVN R0, #0ROM:00332EB8ROM:00332EB8 locret_332EB8 ; CODE XREF: sub_332E98+18jROM:00332EB8 BX LRROM:00332EB8 ; End of function sub_332E98ROM:00332EB8ROM:00332EB8 ; ---------------------------------------------------------------------------ROM:00332EBC dword_332EBC DCD 0x6213600 ; DATA XREF: generate_key+ArROM:00332EC0 dword_332EC0 DCD 0x6213600 ; DATA XREF: generate_key+18rROM:00332EC4 dword_332EC4 DCD 0x35A036 ; DATA XREF: generate_key+C4rROM:00332EC8 dword_332EC8 DCD 0x35A05C ; DATA XREF: generate_key+D0rROM:00332ECC dword_332ECC DCD 0x35A020 ; DATA XREF: generate_key:loc_332DD8rROM:00332ED0 off_332ED0 DCD 0x68B08D ; DATA XREF: generate_key+E2rROM:00332ED4 DCB 0, 0, 0, 0ROM:00332ED4 ; ROM endsROM:00332ED4ROM:00332ED4 END
Серьезно? ASM?
Дабы сделать эту статью более понятной широкому кругу лиц, наверное стоит дать понять что же это за дамп из IDA Pro. Опишем все до мелочей :)
В этом code-box приведены первые 20 строк из level_2.html файла:
01. ROM:00332D0002. ROM:00332D00 ; Segment type: Pure code03. ROM:00332D00 AREA ROM, CODE, READWRITE, ALIGN=004. ROM:00332D00 ; ORG 0x332D0005. ROM:00332D00 CODE1606. ROM:00332D0007. ROM:00332D00 ; =============== S U B R O U T I N E =======================================08. ROM:00332D0009. 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:00332D0013. ROM:00332D00 generate_key14. ROM:00332D0015. ROM:00332D00 var_28 = -0x2816. ROM:00332D0017. ROM:00332D00 PUSH {R4-R7,LR}18. ROM:00332D02 SUB SP, SP, #0x1019. ROM:00332D04 MOVS R7, R120. ROM:00332D06 MOVS R4, R2...
Колонка слева с ROM:XXXXXXXX - это адреса памяти. При загрузке бинарника в IDA Pro, мы должны препроверить, правильно ли IDA Pro поняла для какой архитектуры (ARM, x86, MIPS и тд. Их, ну прям очень большое количество) собран наш бинарник (в большинстве случаев, автоопределение работает очень хорошо, но если мы вгружаем не бинарник, а целый дамп памяти - некоторые вещи могут распознатся некорректно), считываем файл байт за байтом, и эта левая колонка автоинкрементируется каждый раз когда IDA Pro понимает что это за кусок данных. Данные в бинарнике могут быть поняты одним из следующих образов:
-
Данные. Самый низкий уровень понимания логики. Это когда кусок данных не представляет собой ничего конкретного. В дампе отображается как DCD, DCW, DCB - doubleword, word, byte соответственно (эти типы данных могут быть разных размеров на разных системах. Здесь советую погуглить и почитать. Сам это не сильно шарю). На такие штуки обычно есть ссылки из других участков кода. Назначение может быть самое разное. Далее будет понятно.
-
Код. Это когда кусок данных представляет собой 1 атомарную единицу операции (записать данные из регистра в память, сравнить числа и тд).
-
Строка. Это когда IDA Pro натыкается на массив данных которые лежат в ASCII диапазоне. От 0x20 до 0x7E (ASCII стандарт также описывает числа ниже 0x20, но они не имеют "текстового" смысла). Вполне возможно, что диапазон расширяется и на другие кодировки, но это вне контекста данной статьи.
-
Подпроцедура (Subroutine). Это самый высокий уровень понимания ассемблерной логики - когда IDA Pro видит функцию. Дальше мы видим то же самое, как если бы видели код. Но понимание того, что это не просто код, а функция дает невероятный скачок в осознании происходящего.
На 5й строке мы видим надпись CODE16. Это очень важно, поскольку этот дамп снят с кода для ARM процессоров (IC от LSI, который есть на плате от жесткого диска построен именно на этой архитектуре). У ARM процессоров есть 2 режима работы - ARM и Thumb.
-
В режиме ARM у нас есть доступ, наверное, ко всем ассемблерным операциям, но такие инструкции занимают 4 байта
-
В режиме Thumb мы немножко ограничены, но такие инструкции занимают 2 байта.
Причины по которым были созданы эти 2 режима мне неизвестны (знатоки в комментариях очень даже приветствуются), но могу сказать следующее.
-
В режиме ARM мы, скорее всего, будем исполнять желаемую логику быстрее. Как минимум потому что у нас есть доступ к инструкциям типа CMPEQ, которая выполнит операцию сравнения чисел только в том случае, когда результат предыдущго сравнения был успешным. Получается, что в этом режиме мы можем отдать логику if-else на железо. И сделать 2 операции (проверка предыдущего результата и выполнение новой операции) за 1 цикл процессора. Но и размер инструкций будет больше, а значит нужно использовать больше памяти.
-
В режиме Thumb мы не можем использовать подобное, но размер инструкций будет в 2 раза меньше, а значит затраты памяти будут ниже.
-
Еще стоит сказать, что архитектура ARM (не путать с режимом) прекрасна тем, что размер инструкций у них фиксирован (2 или 4 байта), чего нельзя сказать об x86
В общем, вот это CODE16 значит что в этом моменте процессор находится в Thumb режиме. И, как мы видим дальше, адреса памяти инкрементируются по 2 (конечно, там, где есть инструкция).
На строках 9-11 мы видим нарошно оставленные комментарии. Они говорят нам, что этот код исполняется тогда, когда мы вводим в серийник диска букву R и что-то после нее. Также, мы видим, что суть задачи - зарулить исполнение код таким образом, чтоб диск отдал нам части ключей. Совмещая эти ключи, мы получаем пароль от level_3.lod.7z.encrypted.
Строка 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. По сути, это аналог подхода, когда мы сохраняем адрес возврата на стек, но здесь используем регистр. Из преисуществ - он быстрее и часть програмной логики падает на CPU без хождения по памяти. Недостаток - регистр всего один, и предыдущие адреса возврата все же прийдется складывать на стек.
-
PC (R15) - Program Counter. Здесь находится адрес инструкции, которую процессор выполняет в данный момент. Писать сюда напрямую, кажись, нельзя. Но этот регистр полезен если мы хотим сделать прыжок на другой адрес. Даже если мы работаем с 16-битной архитектурой, мы, ну никак не можем сказать процессору прыгнуть на 16-битный адрес имея 2 байта на 1 инструкцию. Большинство инструкций для прыжка используют именно сдвиг от того, что находится в PC.
-
CSPR - Current State Process Register. Это специфический регистр. К нему нельзя обратится целиком, как и записать сюда что-то. Данные внутри этого регистра формируются автоматически на основе того, какие инструкции выполняет процессор. Он разбит на сегменты, и хранит в себе информацию о текущем состоянии процессора. К примеру, если мы делаем инструкцию CMP (сравнить числа), результат сравнения (0 или 1) пишется в один из битов этого регистра. А в дальнейшем это используется для условных операций.
Стек это определенное место в памяти, где хранятся значения локальных переменных, аргументы функций, адреса возвратов, а также предыдущее значение BP. Стек логически разбит на сегменты (stack frames). Каждый сегмент принадлежит какой-то определенной функции. И, когда мы вызываем из одной функции другую, мы по сути, сдвигаем адреса BP & SP чуть ниже(!) в памяти. Но перед тем как создавать новый сегмент на стеке, мы должны знать адрес, куда должен вернутся PC после завершения функции - он сохраняется на стеке (Return Address) перед вызовом функции. Также, чтоб при возврате, сегмент стека стал того же размера что и был, мы сохраняем предыдущее значение BP (Saved %ebp) на этот же стек. Выглядит все это дело примерно так (%ebp = BP, %esp = SP):

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

У ARM процессоров есть специальные семейство инструкций, которые работают со стеком: PUSH, POP, STMFD, LDMFD (уверен, есть еще, но для наших делишек этого хватит).
-
PUSH кладет то, что в аргументе на стек, и увеличивает стек (на самом деле уменьшает адрес)
-
POP снимает со стека данные и кладет в то, что в аргументе (зачастую регистр, но может быть и адрес памяти) и уменьшает стек (на самом деле увеличивает адрес)
-
STMFD, LDMFD делают то же самое, но туда можно запихнуть несколько регистров, и снять со стека несколько значений в рамках одной инструкции. И, кажись, указать, стоит ли подстраивать SP в зависимости от количества впихнутых регистров. Опять же, прелести ARM архитектуры!
Готовы? Ныряем!
Для того, чтоб что-то правильно сломать, нужно это правильно понять. Начнем с первого куска.
...13. ROM:00332D00 generate_key14. ROM:00332D0015. ROM:00332D00 var_28 = -0x2816. ROM:00332D0017. ROM:00332D00 PUSH {R4-R7,LR}18. ROM:00332D02 SUB SP, SP, #0x1019. ROM:00332D04 MOVS R7, R120. ROM:00332D06 MOVS R4, R221. ROM:00332D08 MOVS R5, R022. ROM:00332D0A LDR R1, =0x6213600 ; "R"...23. ROM:00332D0C LDRB R0, [R1,#1]24. ROM:00332D0E CMP R0, #0x3125. ROM:00332D10 BNE loc_332D1A26. ROM:00332D12 ADDS R0, R1, #227. ROM:00332D14 BLX ahex2byte...
Строка 17-21. Первая инструкция, это PUSH. Она сохраняет на стек переменные из регистров R4-R7 и LR на стек, тоесть сохраняет предыдущий stack frame. Потом, отнимаем 16 байт от SP (увеличиваем стек). Копируем из регистров R0-R2 аргументы, которые были переданы в функцию generate_key в их "рабочие" места. Как видим, в прототипе было 3 аргумента. Регистров столко же. В предыдущем разделе я говорил, что аргументы падают на стек - это правда только в том случае, когда аргументов больше, чем 3 (или 4, уже не помню).
Процедура в предыдущем абзаце называется Function Prologue. Тоесть, не происходит ничего, что касается программной логики, но выполняется подготовка стека и регистров. Мне кажется, именно по подобным шаблонам IDA Pro отличает код от подпроцедур.
Строка 22-23. Грузим адрес, который будет указывать на то, что мы вводим в консольник диска в R1. Инструкция
LDRB R0, [R1,#1]
берет то, что лежит в R1, добавляет единицу, лезет в память по этому адресу (если значение в квадратных скобках, сначала надо интерпретировать эти данные как адрес), берет 1 байт и сохраняет его в R0.
Строка 24-25. Идет сравнение 2го вводимого символа с 0x31. В ASCII 0x31 это "1". Если мы ввели после буквы R единичку, выполнение кода не пойдет на loc_332D1A. Позже разберем что это за место.
Строка 26-27. Добавляем к адресу наших вводимых данных двойку и сохраняем в R0. По сути, мы смещаем адрес на 2 байта вперед. Тоесть, теперь он указывает на первый символ после R1 - "R1_" (на underscore). Далее, нас ждет безусловный прыжок на функцию ahex2byte. Но, это не просто прыжок. BLX, кроме прыжка, делает еще 2 замечательные вещи - сохраняет текущий адрес PC в LR, и переключает нас в ARM режим! внутри функции ahex2byte у нас теперь 4 байта на инструкцию. Помним, что R0 является первым аргументом при вызове функции.
Дабы подитожить, вот то, как пердыдущий кусок выглядел бы в С-шном виде. Этот код не правильный. Он чисто для наглядности:
void main () { char *input = "R10000"; if (input[1] == "1") { &input = &input + 2; ahex2byte(&input); } else { goto: loc_332D1A; }}
ахекс2байт
138. ROM:00332DF8 ; =============== S U B R O U T I N E =======================================139. ROM:00332DF8140. ROM:00332DF8141. ROM:00332DF8 ahex2byte ; CODE XREF: generate_key+14p142. ROM:00332DF8 STMFD SP!, {R4-R6,LR}143. ROM:00332DFC MOV R4, R0144. ROM:00332E00 MOV R6, R0145. ROM:00332E04146. ROM:00332E04 loc_332E04 ; CODE XREF: ahex2byte+6Cj147. ROM:00332E04 LDRB R0, [R4]148. ROM:00332E08 CMP R0, #0xD149. ROM:00332E0C BEQ loc_332E68150. ROM:00332E10 BL sub_332E70151. ROM:00332E14 CMN R0, #1152. ROM:00332E18 BNE loc_332E2C153. ROM:00332E1C LDRB R0, [R4]154. ROM:00332E20 BL sub_332E98155. ROM:00332E24 CMN R0, #1156. ROM:00332E28 BEQ locret_332E6C157. ROM:00332E2C158. ROM:00332E2C loc_332E2C ; CODE XREF: ahex2byte+20j159. ROM:00332E2C MOV R5, R0160. ROM:00332E30 LDRB R0, [R4,#1]161. ROM:00332E34 BL sub_332E70162. ROM:00332E38 CMN R0, #1163. ROM:00332E3C BNE loc_332E50164. ROM:00332E40 LDRB R0, [R4,#1]165. ROM:00332E44 BL sub_332E98166. ROM:00332E48 CMN R0, #1167. ROM:00332E4C BEQ locret_332E6C168. ROM:00332E50169. ROM:00332E50 loc_332E50 ; CODE XREF: ahex2byte+44j170. ROM:00332E50 MOV R5, R5,LSL#4171. ROM:00332E54 ADD R0, R5, R0172. ROM:00332E58 STRB R0, [R6]173. ROM:00332E5C ADD R4, R4, #2174. ROM:00332E60 ADD R6, R6, #1175. ROM:00332E64 B loc_332E04176. ROM:00332E68 ; ---------------------------------------------------------------------------177. ROM:00332E68178. ROM:00332E68 loc_332E68 ; CODE XREF: ahex2byte+14j179. ROM:00332E68 STRB R0, [R6]180. ROM:00332E6C181. ROM:00332E6C locret_332E6C ; CODE XREF: ahex2byte+30j182. ROM:00332E6C ; ahex2byte+54j183. ROM:00332E6C LDMFD SP!, {R4-R6,PC}184. ROM:00332E6C ; End of function ahex2byte185. ROM:00332E6C186. ROM:00332E70187. ROM:00332E70 ; =============== S U B R O U T I N E =======================================188. ROM:00332E70189. ROM:00332E70190. ROM:00332E70 sub_332E70 ; CODE XREF: ahex2byte+18p191. ROM:00332E70 ; ahex2byte+3Cp192. ROM:00332E70 CMP R0, #0xD193. ROM:00332E74 BEQ loc_332E90194. ROM:00332E78 CMP R0, #0x30195. ROM:00332E7C BLT loc_332E90196. ROM:00332E80 CMP R0, #0x39197. ROM:00332E84 BGT loc_332E90198. ROM:00332E88 SUB R0, R0, #0x30199. ROM:00332E8C B locret_332E94200. ROM:00332E90 ; ---------------------------------------------------------------------------201. ROM:00332E90202. ROM:00332E90 loc_332E90 ; CODE XREF: sub_332E70+4j203. ROM:00332E90 ; sub_332E70+Cj ...204. ROM:00332E90 MVN R0, #0205. ROM:00332E94206. ROM:00332E94 locret_332E94 ; CODE XREF: sub_332E70+1Cj207. ROM:00332E94 BX LR208. ROM:00332E94 ; End of function sub_332E70209. ROM:00332E94210. ROM:00332E98211. ROM:00332E98 ; =============== S U B R O U T I N E =======================================212. ROM:00332E98213. ROM:00332E98214. ROM:00332E98 sub_332E98 ; CODE XREF: ahex2byte+28p215. ROM:00332E98 ; ahex2byte+4Cp216. ROM:00332E98 CMP R0, #0x41217. ROM:00332E9C BLT loc_332EB4218. ROM:00332EA0 CMP R0, #0x46219. ROM:00332EA4 BGT loc_332EB4220. ROM:00332EA8 SUB R0, R0, #0x41221. ROM:00332EAC ADD R0, R0, #0xA222. ROM:00332EB0 B locret_332EB8223. ROM:00332EB4 ; ---------------------------------------------------------------------------224. ROM:00332EB4225. ROM:00332EB4 loc_332EB4 ; CODE XREF: sub_332E98+4j226. ROM:00332EB4 ; sub_332E98+Cj227. ROM:00332EB4 MVN R0, #0228. ROM:00332EB8229. ROM:00332EB8 locret_332EB8 ; CODE XREF: sub_332E98+18j230. ROM:00332EB8 BX LR231. ROM:00332EB8 ; End of function sub_332E98
Строка