Како дел од состанокот 0x0A DC7831
Во оваа статија ќе опишеме како да го стартувате фирмверот на уредот во емулаторот, да демонстрираме интеракција со дебагерот и да извршиме мала динамичка анализа на фирмверот.
праисторијата
Одамна во далечна галаксија
Пред неколку години во нашата лабораторија имаше потреба да се испита фирмверот на уредот. Фирмверот беше компресиран и отпакуван со подигнувач. Тој го направи тоа на многу комплициран начин, менувајќи ги податоците во меморијата неколку пати. И самиот фирмвер потоа активно комуницираше со периферните уреди. И сето тоа на јадрото MIPS.
Од објективни причини, достапните емулатори не ни одговараа, но сепак сакавме да го извршиме кодот. Потоа решивме да направиме сопствен емулатор, кој ќе го направи минимумот и ќе ни овозможи да го отпакуваме главниот фирмвер. Го пробавме и успеа. Помисливме, што ако додадеме периферни уреди за да го извршиме и главниот фирмвер. Не болеше многу - и успеа исто така. Повторно размислувавме и решивме да направиме полноправен емулатор.
Резултатот беше емулатор на компјутерски системи
Зошто Kopycat?
Има игра со зборови.
- copycat (англиски, именка [ˈkɒpɪkæt]) - имитатор, имитатор
- мачка (англиски, именка [ˈkæt]) - мачка, мачка - омиленото животно на еден од креаторите на проектот
- Буквата „К“ е од програмскиот јазик Котлин
Копирај
При креирањето на емулаторот, беа поставени многу специфични цели:
- способност за брзо создавање на нови периферни уреди, модули, процесорски јадра;
- можност за составување виртуелен уред од различни модули;
- можност за вчитување на какви било бинарни податоци (фирмвер) во меморијата на виртуелен уред;
- способност за работа со снимки (слики од состојбата на системот);
- способност за интеракција со емулаторот преку вградениот дебагер;
- убав модерен јазик за развој.
Како резултат на тоа, Kotlin беше избран за имплементација, архитектурата на магистралата (ова е кога модулите комуницираат едни со други преку виртуелни магистрали за податоци), JSON како формат за опис на уредот и GDB RSP како протокол за интеракција со дебагерот.
Развојот трае нешто повеќе од две години и е активно во тек. За тоа време беа имплементирани јадрата на процесорот MIPS, x86, V850ES, ARM и PowerPC.
Проектот расте и време е да се претстави пред пошироката јавност. Подоцна ќе направиме детален опис на проектот, но засега ќе се фокусираме на користење на Kopycat.
За најнетрпеливите, промо верзија на емулаторот може да се преземе од
Носорог во емулаторот
Да потсетиме дека претходно за конференцијата SMARTRHINO-2018 беше создаден тест-уред „Носорог“ за учење на вештини за обратно инженерство. Процесот на статичка анализа на фирмверот беше опишан во
Сега да се обидеме да додадеме „звучници“ и да го стартуваме фирмверот во емулаторот.
Ќе ни требаат:
1) Java 1.8
2) Пајтон и модул
За Windows:
1)
2)
За Linux:
1) сокат
Можете да ги користите Eclipse, IDA Pro или radare2 како GDB клиент.
Како тоа функционира?
За да се изврши фирмверот во емулаторот, неопходно е да се „собере“ виртуелен уред, кој е аналог на вистински уред.
Вистинскиот уред („носорог“) може да се прикаже на блок-дијаграмот:
Емулаторот има модуларна структура и конечниот виртуелен уред може да се опише во датотека JSON.
JSON 105 линии
{
"top": true,
// Plugin name should be the same as file name (or full path from library start)
"plugin": "rhino",
// Directory where plugin places
"library": "user",
// Plugin parameters (constructor parameters if jar-plugin version)
"params": [
{ "name": "tty_dbg", "type": "String"},
{ "name": "tty_bt", "type": "String"},
{ "name": "firmware", "type": "String", "default": "NUL"}
],
// Plugin outer ports
"ports": [ ],
// Plugin internal buses
"buses": [
{ "name": "mem", "size": "BUS30" },
{ "name": "nand", "size": "4" },
{ "name": "gpio", "size": "BUS32" }
],
// Plugin internal components
"modules": [
{
"name": "u1_stm32",
"plugin": "STM32F042",
"library": "mcu",
"params": {
"firmware:String": "params.firmware"
}
},
{
"name": "usart_debug",
"plugin": "UartSerialTerminal",
"library": "terminals",
"params": {
"tty": "params.tty_dbg"
}
},
{
"name": "term_bt",
"plugin": "UartSerialTerminal",
"library": "terminals",
"params": {
"tty": "params.tty_bt"
}
},
{
"name": "bluetooth",
"plugin": "BT",
"library": "mcu"
},
{ "name": "led_0", "plugin": "LED", "library": "mcu" },
{ "name": "led_1", "plugin": "LED", "library": "mcu" },
{ "name": "led_2", "plugin": "LED", "library": "mcu" },
{ "name": "led_3", "plugin": "LED", "library": "mcu" },
{ "name": "led_4", "plugin": "LED", "library": "mcu" },
{ "name": "led_5", "plugin": "LED", "library": "mcu" },
{ "name": "led_6", "plugin": "LED", "library": "mcu" },
{ "name": "led_7", "plugin": "LED", "library": "mcu" },
{ "name": "led_8", "plugin": "LED", "library": "mcu" },
{ "name": "led_9", "plugin": "LED", "library": "mcu" },
{ "name": "led_10", "plugin": "LED", "library": "mcu" },
{ "name": "led_11", "plugin": "LED", "library": "mcu" },
{ "name": "led_12", "plugin": "LED", "library": "mcu" },
{ "name": "led_13", "plugin": "LED", "library": "mcu" },
{ "name": "led_14", "plugin": "LED", "library": "mcu" },
{ "name": "led_15", "plugin": "LED", "library": "mcu" }
],
// Plugin connection between components
"connections": [
[ "u1_stm32.ports.usart1_m", "usart_debug.ports.term_s"],
[ "u1_stm32.ports.usart1_s", "usart_debug.ports.term_m"],
[ "u1_stm32.ports.usart2_m", "bluetooth.ports.usart_m"],
[ "u1_stm32.ports.usart2_s", "bluetooth.ports.usart_s"],
[ "bluetooth.ports.bt_s", "term_bt.ports.term_m"],
[ "bluetooth.ports.bt_m", "term_bt.ports.term_s"],
[ "led_0.ports.pin", "u1_stm32.buses.pin_output_a", "0x00"],
[ "led_1.ports.pin", "u1_stm32.buses.pin_output_a", "0x01"],
[ "led_2.ports.pin", "u1_stm32.buses.pin_output_a", "0x02"],
[ "led_3.ports.pin", "u1_stm32.buses.pin_output_a", "0x03"],
[ "led_4.ports.pin", "u1_stm32.buses.pin_output_a", "0x04"],
[ "led_5.ports.pin", "u1_stm32.buses.pin_output_a", "0x05"],
[ "led_6.ports.pin", "u1_stm32.buses.pin_output_a", "0x06"],
[ "led_7.ports.pin", "u1_stm32.buses.pin_output_a", "0x07"],
[ "led_8.ports.pin", "u1_stm32.buses.pin_output_a", "0x08"],
[ "led_9.ports.pin", "u1_stm32.buses.pin_output_a", "0x09"],
[ "led_10.ports.pin", "u1_stm32.buses.pin_output_a", "0x0A"],
[ "led_11.ports.pin", "u1_stm32.buses.pin_output_a", "0x0B"],
[ "led_12.ports.pin", "u1_stm32.buses.pin_output_a", "0x0C"],
[ "led_13.ports.pin", "u1_stm32.buses.pin_output_a", "0x0D"],
[ "led_14.ports.pin", "u1_stm32.buses.pin_output_a", "0x0E"],
[ "led_15.ports.pin", "u1_stm32.buses.pin_output_a", "0x0F"]
]
}
Обрнете внимание на параметарот фирмверот дел парами е името на датотеката што може да се вчита во виртуелен уред како фирмвер.
Виртуелниот уред и неговата интеракција со главниот оперативен систем може да се претстават со следниот дијаграм:
Тековниот тест пример на емулаторот вклучува интеракција со COM портите на главниот оперативен систем (дебагирајте ги UART и UART за модулот Bluetooth). Овие можат да бидат вистински порти на кои се поврзани уредите или виртуелни COM порти (за ова ви треба само com0com/socat).
Во моментов постојат два главни начини за интеракција со емулаторот однадвор:
- GDB RSP протокол (соодветно, алатките што го поддржуваат овој протокол се Eclipse / IDA / radare2);
- внатрешна командна линија за емулатор (Argparse или Python).
Виртуелни COM порти
За да комуницирате со UART на виртуелен уред на локалната машина преку терминал, треба да креирате пар поврзани виртуелни COM порти. Во нашиот случај, едната порта се користи од емулаторот, а втората од терминалната програма (PuTTY или екран):
Користење на com0com
Виртуелните COM порти се конфигурирани со помош на алатката за поставување од комплетот com0com (верзија на конзолата - C: Програмски датотеки (x86) com0comsetupс.exe, или верзија на GUI - C: Програмски датотеки (x86) com0comsetupg.exe):
Проверете ги полињата овозможи пречекорување на баферот за сите креирани виртуелни порти, во спротивно емулаторот ќе чека одговор од COM-портата.
Користење на сокат
На системите UNIX, виртуелните COM порти автоматски се креираат од емулаторот користејќи ја алатката socat за да го направите ова, само наведете го префиксот во името на портата кога го стартувате емулаторот socat:
.
Интерфејс за внатрешна командна линија (Argparse или Python)
Бидејќи Kopycat е конзолна апликација, емулаторот обезбедува две опции за интерфејс на командната линија за интеракција со неговите објекти и променливи: Argparse и Python.
Argparse е CLI вграден во Kopycat и е секогаш достапен за секого.
Алтернативен CLI е толкувачот на Python. За да го користите, треба да го инсталирате модулот Jep Python и да го конфигурирате емулаторот да работи со Python (ќе се користи Python толкувачот инсталиран на главниот систем на корисникот).
Инсталирање на модулот Python Jep
Под Linux Jep може да се инсталира преку пип:
pip install jep
За да го инсталирате Jep на Windows, прво мора да го инсталирате Windows SDK и соодветното Microsoft Visual Studio. Ви го олеснивме малку и
pip install jep-3.8.2-cp27-cp27m-win_amd64.whl
За да ја проверите инсталацијата на Jep, треба да извршите на командната линија:
python -c "import jep"
Како одговор треба да се прими следнава порака:
ImportError: Jep is not supported in standalone Python, it must be embedded in Java.
Во сериската датотека на емулатор за вашиот систем (копираат.лилјак - за Windows, копираат - за Linux) до списокот со параметри DEFAULT_JVM_OPTS
додадете дополнителен параметар Djava.library.path
— мора да ја содржи патеката до инсталираниот Jep модул.
Резултатот за Windows треба да биде линија како оваа:
set DEFAULT_JVM_OPTS="-XX:MaxMetaspaceSize=256m" "-XX:+UseParallelGC" "-XX:SurvivorRatio=6" "-XX:-UseGCOverheadLimit" "-Djava.library.path=C:/Python27/Lib/site-packages/jep"
Лансирање на Kopycat
Емулаторот е конзола JVM апликација. Лансирањето се врши преку скриптата на командната линија на оперативниот систем (sh/cmd).
Команда за извршување на Windows:
binkopycat -g 23946 -n rhino -l user -y library -p firmware=firmwarerhino_pass.bin,tty_dbg=COM26,tty_bt=COM28
Наредба да работи под Linux користејќи ја алатката socat:
./bin/kopycat -g 23946 -n rhino -l user -y library -p firmware=./firmware/rhino_pass.bin, tty_dbg=socat:./COM26,tty_bt=socat:./COM28
-g 23646
— TCP порта што ќе биде отворена за пристап до серверот GDB;-n rhino
— име на главниот системски модул (склопен уред);-l user
— име на библиотеката за пребарување на главниот модул;-y library
— патека за пребарување на модули вклучени во уредот;firmwarerhino_pass.bin
— патека до датотеката на фирмверот;- COM26 и COM28 се виртуелни COM порти.
Како резултат на тоа, ќе се прикаже известување Python >
(Или Argparse >
):
18:07:59 INFO [eFactoryBuilder.create ]: Module top successfully created as top
18:07:59 INFO [ Module.initializeAndRes]: Setup core to top.u1_stm32.cortexm0.arm for top
18:07:59 INFO [ Module.initializeAndRes]: Setup debugger to top.u1_stm32.dbg for top
18:07:59 WARN [ Module.initializeAndRes]: Tracer wasn't found in top...
18:07:59 INFO [ Module.initializeAndRes]: Initializing ports and buses...
18:07:59 WARN [ Module.initializePortsA]: ATTENTION: Some ports has warning use printModulesPortsWarnings to see it...
18:07:59 FINE [ ARMv6CPU.reset ]: Set entry point address to 08006A75
18:07:59 INFO [ Module.initializeAndRes]: Module top is successfully initialized and reset as a top cell!
18:07:59 INFO [ Kopycat.open ]: Starting virtualization of board top[rhino] with arm[ARMv6Core]
18:07:59 INFO [ GDBServer.debuggerModule ]: Set new debugger module top.u1_stm32.dbg for GDB_SERVER(port=23946,alive=true)
Python >
Интеракција со IDA Pro
За да го поедноставиме тестирањето, го користиме фирмверот Rhino како изворна датотека за анализа во IDA во форма
Можете исто така да го користите главниот фирмвер без мета информации.
Откако ќе го стартувате Kopycat во IDA Pro, во менито Дебагер одете на ставката „Префрли го дебагерот…"и изберете"Далечински дебагер на GDB“. Следно, поставете го менито за поврзување: Отстранувач на грешки - Опции за процесирање…
Поставете ги вредностите:
- Апликација - која било вредност
- Име на домаќин: 127.0.0.1 (или IP адресата на оддалечената машина каде работи Kopycat)
- Порт: 23946
Сега копчето за дебагирање станува достапно (копче F9):
Кликнете на него за да се поврзете со модулот за дебагер во емулаторот. IDA оди во режим за дебагирање, дополнителни прозорци стануваат достапни: информации за регистри, за стекот.
Сега можеме да ги користиме сите стандардни карактеристики на дебагерот:
- чекор-по-чекор извршување на инструкциите (Чекор во и Премини преку — копчињата F7 и F8, соодветно);
- започнување и паузирање на извршувањето;
- создавање точки на прекин и за кодот и за податоците (клуч F2).
Поврзувањето со дебагер не значи вклучување на кодот на фирмверот. Тековната позиција на извршување мора да биде адресата 0x08006A74
— почеток на функцијата Reset_Handler. Ако скролувате надолу по списокот, можете да го видите повикот на функцијата Главната. Можете да го поставите курсорот на оваа линија (адреса 0x08006ABE
) и извршете ја операцијата Стартувај до курсорот (клуч F4).
Следно, можете да притиснете F7 за да ја внесете функцијата Главната.
Ако ја извршите командата Продолжете со процесот (копче F9), потоа ќе се појави прозорецот „Ве молиме почекајте“ со едно копче Го суспендира:
Кога ќе притиснете Го суспендира извршувањето на кодот на фирмверот е суспендирано и може да се продолжи од истата адреса во кодот каде што бил прекинат.
Ако продолжите со извршување на кодот, ќе ги видите следните линии во терминалите поврзани со виртуелните COM порти:
Присуството на линијата „state bypass“ покажува дека виртуелниот Bluetooth модул се префрлил на режимот на примање податоци од COM-портата на корисникот.
Сега во терминалот Bluetooth (COM29 на сликата) можете да внесете команди во согласност со протоколот Rhino. На пример, командата „MEOW“ ќе ја врати низата „mur-mur“ на терминалот Bluetooth:
Имитирај ме не целосно
Кога градите емулатор, можете да го изберете нивото на детали/емулација на одреден уред. На пример, модулот Bluetooth може да се емулира на различни начини:
- уредот е целосно емулиран со целосен сет на команди;
- AT командите се емулираат, а протокот на податоци се прима од COM портот на главниот систем;
- виртуелниот уред обезбедува целосно пренасочување на податоците до вистинскиот уред;
- како обичен никулец кој секогаш враќа „OK“.
Тековната верзија на емулаторот го користи вториот пристап - виртуелниот Bluetooth модул врши конфигурација, по што се префрла на режимот на „прокси“ податоци од COM портот на главниот систем до портата UART на емулаторот.
Да ја разгледаме можноста за едноставна инструментација на кодот во случај некој дел од периферијата да не е имплементиран. На пример, ако не е создаден тајмер одговорен за контрола на преносот на податоци до DMA (проверката се врши во функцијата ws2812b_waitлоциран во 0x08006840
), тогаш фирмверот секогаш ќе чека знамето да се ресетира зафатенлоциран во 0x200004C4
што ја покажува зафатеноста на линијата за податоци DMA:
Можеме да ја заобиколиме оваа ситуација со рачно ресетирање на знамето зафатен веднаш по инсталирањето. Во IDA Pro, можете да креирате Python функција и да ја повикате во точка на прекин и да ја ставите самата точка на прекин во кодот откако ќе ја напишете вредноста 1 на знамето зафатен.
Ракувач со точка на прекин
Прво, ајде да создадеме функција на Python во IDA. Мени Датотека - команда за скрипта...
Додадете нов фрагмент во списокот лево, дајте му име (на пример, BPT),
Во полето за текст на десната страна, внесете го функцискиот код:
def skip_dma():
print "Skipping wait ws2812..."
value = Byte(0x200004C4)
if value == 1:
PatchDbgByte(0x200004C4, 0)
return False
После тоа, кликнете Испратена и затворете го прозорецот за скрипта.
Сега да одиме на кодот во 0x0800688A
, поставете точка на прекин (копче F2), изменете ја (контекстно мени Уреди точка на прекин...), не заборавајте да го поставите типот на скрипта на Python:
Ако моменталната вредност на знамето зафатен е еднакво на 1, тогаш треба да ја извршите функцијата skip_dma во линијата на сценариото:
Ако го стартувате фирмверот за извршување, активирањето на кодот на управувачот со точка на прекин може да се види во прозорецот IDA излез по линија Skipping wait ws2812...
. Сега фирмверот нема да чека да се ресетира знамето зафатен.
Интеракција со емулаторот
Емулацијата за доброто на емулација веројатно нема да предизвика задоволство и радост. Многу поинтересно е ако емулаторот му помага на истражувачот да ги види податоците во меморијата или да ја воспостави интеракцијата на нишките.
Ќе ви покажеме како динамички да воспоставите интеракција помеѓу задачите на RTOS. Прво треба да го паузирате извршувањето на кодот ако работи. Ако одите на функцијата bluetooth_task_entry до гранката за обработка на командата „LED“ (адреса 0x080057B8
), потоа можете да видите што е прво креирано, а потоа испратено до системската редица ledControlQueueHandle некоја порака.
Треба да поставите точка на прекин за да пристапите до променливата ledControlQueueHandleлоциран во 0x20000624
и продолжете со извршување на кодот:
Како резултат на тоа, застанувањето прво ќе се случи на адресата 0x080057CA
пред да ја повикате функцијата osMailAlloc, потоа на адресата 0x08005806
пред да ја повикате функцијата osMailPut, а потоа по некое време - на адресата 0x08005BD4
(пред да ја повикате функцијата osMailGet), која припаѓа на функцијата leds_task_entry (LED-задача), односно задачите се префрлија, а сега LED-задачата доби контрола.
На овој едноставен начин можете да утврдите како RTOS задачите комуницираат едни со други.
Се разбира, во реалноста, интеракцијата на задачите може да биде посложена, но со користење на емулатор, следењето на оваа интеракција станува помалку макотрпно.
Стартувајте со Radare2
Не можете да игнорирате таква универзална алатка како Radare2.
За да се поврзете со емулаторот користејќи r2, командата ќе изгледа вака:
radare2 -A -a arm -b 16 -d gdb://localhost:23946 rhino_fw42k6.elf
Стартувањето е достапно сега (dc
) и паузирајте го извршувањето (Ctrl+C).
За жал, во моментот, r2 има проблеми при работа со хардверскиот gdb сервер и распоредот на меморијата поради тоа, точките на прекин и чекорите не работат (команда ds
). Се надеваме дека ова ќе се поправи наскоро.
Трчање со Eclipse
Една од опциите за користење на емулаторот е дебагирање на фирмверот на уредот што се развива. За јасност, ќе го користиме и фирмверот Rhino. Можете да ги преземете изворите на фирмверот
Ќе го користиме Eclipse од сетот како IDE
За да може емулаторот да го вчита фирмверот директно компајлиран во Eclipse, треба да го додадете параметарот firmware=null
до командата за стартување на емулаторот:
binkopycat -g 23946 -n rhino -l user -y modules -p firmware=null,tty_dbg=COM26,tty_bt=COM28
Поставување конфигурација за отстранување грешки
Во Eclipse, изберете го менито Стартувај - конфигурации за отстранување грешки... Во прозорецот што се отвора, во делот Дебагирање на хардверот на GDB треба да додадете нова конфигурација, а потоа на табулаторот „Главна“ наведете го тековниот проект и апликацијата за дебагирање:
На табулаторот „Debugger“ треба да ја наведете командата GDB:
${openstm32_compiler_path}arm-none-eabi-gdb
И, исто така, внесете ги параметрите за поврзување со серверот GDB (домаќин и порта):
На табулаторот „Стартување“, мора да ги наведете следните параметри:
- овозможи поле за избор Вчитај слика (така што склопената слика на фирмверот е вчитана во емулаторот);
- овозможи поле за избор Симболи за вчитување;
- додадете команда за лансирање:
set $pc = *0x08000004
(поставете го компјутерскиот регистар на вредноста од меморијата на адресата0x08000004
- адресата е зачувана таму ResetHandler).
Обрни внимание, ако не сакате да ја преземете датотеката на фирмверот од Eclipse, тогаш опциите Вчитај слика и Изврши команди нема потреба да се укажува.
Откако ќе кликнете Debug, можете да работите во режим на дебагер:
- чекор по чекор извршување на код
- интеракција со точки на прекин
Имајте на ум. Затемнувањето има, хм... некои чуда... и мора да живееш со нив. На пример, ако при стартување на дебагерот се појави пораката „Нема достапен извор за „0x0″“, тогаш извршете ја командата Step (F5)
Наместо заклучок
Емулирањето на мајчин код е многу интересна работа. За развивач на уреди, станува возможно да се дебагира фирмверот без вистински уред. За истражувач, тоа е можност да се спроведе динамична анализа на кодот, што не е секогаш можно дури и со уред.
Сакаме да им обезбедиме на специјалистите алатка која е погодна, умерено едноставна и не бара многу напор и време за поставување и работа.
Напишете во коментарите за вашето искуство со користење на хардверски емулатори. Ве покануваме да разговараме и со задоволство ќе одговориме на прашања.
Само регистрирани корисници можат да учествуваат во анкетата.
За што го користиш емулаторот?
-
Развивам (дебагирам) фирмвер
-
Истражувам фирмвер
-
Стартувам игри (Денди, Сега, ПСП)
-
нешто друго (напишете во коментар)
Гласаа 7 корисници. 2 корисници се воздржаа.
Кој софтвер го користите за да имитирате мајчин код?
-
QEMU
-
Мотор еднорог
-
Proteus
-
нешто друго (напишете во коментар)
Гласаа 6 корисници. 2 корисници се воздржаа.
Што би сакале да подобрите во емулаторот што го користите?
-
Сакам брзина
-
Сакам лесно поставување/лансирање
-
Сакам повеќе опции за интеракција со емулаторот (API, куки)
-
Задоволен сум со се
-
нешто друго (напишете во коментар)
Гласаа 8 корисници. 1 корисник се воздржа.
Извор: www.habr.com