А что дальше?
Хабровчане и хабровчушки, эта статья является долгожданным (ага,
в пару дней) продолжением моей предыдущей статьи о взломе жесткого
диска для собеседования в инфосек компанию 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
level_2.html
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
Строка