Com a part de la reunió 0x0A DC7831
En aquest article descriurem com executar el microprogramari del dispositiu a l'emulador, demostrar la interacció amb el depurador i realitzar una petita anàlisi dinàmica del microprogramari.
prehistòria
Fa molt de temps en una galàxia molt llunyana
Fa un parell d'anys al nostre laboratori hi havia la necessitat d'investigar el firmware d'un dispositiu. El microprogramari es va comprimir i desempaquetar amb un carregador d'arrencada. Ho va fer d'una manera molt complicada, canviant les dades de la memòria diverses vegades. I el mateix firmware va interactuar activament amb els perifèrics. I tot això al nucli MIPS.
Per raons objectives, els emuladors disponibles no ens van bé, però encara volíem executar el codi. Aleshores vam decidir fer el nostre propi emulador, que faria el mínim i ens permetria desempaquetar el firmware principal. Ho vam provar i va funcionar. Vam pensar, què passaria si afegim perifèrics per realitzar també el firmware principal. No va fer gaire mal, i també va funcionar. Vam pensar de nou i vam decidir fer un emulador complet.
El resultat va ser un emulador de sistemes informàtics
Per què Kopycat?
Hi ha un joc de paraules.
- copycat (Anglès, substantiu [ˈkɒpɪkæt]) - imitador, imitador
- gat (Anglès, substantiu [ˈkæt]) - gat, gat - l'animal preferit d'un dels creadors del projecte
- La lletra "K" és del llenguatge de programació Kotlin
Copycat
En crear l'emulador, es van establir objectius molt concrets:
- la capacitat de crear ràpidament nous perifèrics, mòduls, nuclis de processador;
- la capacitat de muntar un dispositiu virtual a partir de diversos mòduls;
- la capacitat de carregar qualsevol dada binària (firmware) a la memòria d'un dispositiu virtual;
- capacitat de treballar amb instantànies (instantànies de l'estat del sistema);
- la capacitat d'interactuar amb l'emulador mitjançant el depurador integrat;
- un bon llenguatge modern per al desenvolupament.
Com a resultat, es va triar Kotlin per a la implementació, l'arquitectura de bus (és quan els mòduls es comuniquen entre ells mitjançant busos de dades virtuals), JSON com a format de descripció del dispositiu i GDB RSP com a protocol d'interacció amb el depurador.
El desenvolupament porta una mica més de dos anys i continua activament. Durant aquest temps, es van implementar nuclis de processadors MIPS, x86, V850ES, ARM i PowerPC.
El projecte creix i és hora de presentar-lo al gran públic. Més endavant farem una descripció detallada del projecte, però de moment ens centrarem a utilitzar Kopycat.
Per als més impacients, es pot descarregar una versió promocional de l'emulador
Rhino a l'emulador
Recordem que abans per a la conferència SMARTRHINO-2018, es va crear un dispositiu de prova "Rhinoceros" per ensenyar habilitats d'enginyeria inversa. El procés d'anàlisi del firmware estàtic es va descriure a
Ara intentem afegir "altaveus" i executar el microprogramari a l'emulador.
necessitem:
1) Java 1.8
2) Python i mòdul
Per a Windows:
1)
2)
Per a Linux:
1) socat
Podeu utilitzar Eclipse, IDA Pro o radare2 com a client GDB.
Com funciona?
Per dur a terme el firmware a l'emulador, cal "muntar" un dispositiu virtual, que és un anàleg d'un dispositiu real.
El dispositiu real ("rinoceront") es pot mostrar al diagrama de blocs:
L'emulador té una estructura modular i el dispositiu virtual final es pot descriure en un fitxer JSON.
JSON 105 línies
{
"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"]
]
}
Preste atenció al paràmetre microprogramari a la secció paràmetres és el nom d'un fitxer que es pot carregar en un dispositiu virtual com a microprogramari.
El dispositiu virtual i la seva interacció amb el sistema operatiu principal es poden representar amb el diagrama següent:
La instància de prova actual de l'emulador implica la interacció amb els ports COM del sistema operatiu principal (depuració UART i UART per al mòdul Bluetooth). Aquests poden ser ports reals als quals estan connectats els dispositius o ports COM virtuals (per a això només necessiteu com0com/socat).
Actualment hi ha dues maneres principals d'interactuar amb l'emulador des de l'exterior:
- Protocol GDB RSP (en conseqüència, les eines que admeten aquest protocol són Eclipse / IDA / radare2);
- línia d'ordres de l'emulador intern (Argparse o Python).
Ports COM virtuals
Per interactuar amb l'UART d'un dispositiu virtual a la màquina local mitjançant un terminal, heu de crear un parell de ports COM virtuals associats. En el nostre cas, un port és utilitzat per l'emulador i el segon per un programa de terminal (PuTTY o pantalla):
Utilitzant com0com
Els ports COM virtuals es configuren mitjançant la utilitat de configuració del kit com0com (versió de consola - C:Fitxers de programa (x86)com0comsetupс.exe, o versió GUI - C:Fitxers de programa (x86)com0comsetupg.exe):
Marqueu les caselles habilitar l'excés de memòria intermèdia per a tots els ports virtuals creats, en cas contrari l'emulador esperarà una resposta del port COM.
Utilitzant socat
En els sistemes UNIX, els ports COM virtuals els crea automàticament l'emulador mitjançant la utilitat socat per fer-ho, només cal que especifiqueu el prefix al nom del port quan s'iniciï l'emulador socat:
.
Interfície de línia d'ordres interna (Argparse o Python)
Com que Kopycat és una aplicació de consola, l'emulador ofereix dues opcions d'interfície de línia d'ordres per interactuar amb els seus objectes i variables: Argparse i Python.
Argparse és una CLI integrada a Kopycat i sempre està disponible per a tothom.
Una CLI alternativa és l'intèrpret de Python. Per utilitzar-lo, cal instal·lar el mòdul Jep Python i configurar l'emulador perquè funcioni amb Python (s'utilitzarà l'intèrpret Python instal·lat al sistema principal de l'usuari).
Instal·lant el mòdul Python Jep
Sota Linux, Jep es pot instal·lar mitjançant pip:
pip install jep
Per instal·lar Jep a Windows, primer heu d'instal·lar l'SDK de Windows i el Microsoft Visual Studio corresponent. T'ho hem posat una mica més fàcil i
pip install jep-3.8.2-cp27-cp27m-win_amd64.whl
Per comprovar la instal·lació de Jep, heu d'executar a la línia d'ordres:
python -c "import jep"
S'ha de rebre el missatge següent com a resposta:
ImportError: Jep is not supported in standalone Python, it must be embedded in Java.
Al fitxer per lots de l'emulador del vostre sistema (copycat.bat - per a Windows, imitador - per a Linux) a la llista de paràmetres DEFAULT_JVM_OPTS
afegir un paràmetre addicional Djava.library.path
— ha de contenir la ruta al mòdul Jep instal·lat.
El resultat per a Windows hauria de ser una línia com aquesta:
set DEFAULT_JVM_OPTS="-XX:MaxMetaspaceSize=256m" "-XX:+UseParallelGC" "-XX:SurvivorRatio=6" "-XX:-UseGCOverheadLimit" "-Djava.library.path=C:/Python27/Lib/site-packages/jep"
Llançament de Kopycat
L'emulador és una aplicació JVM de consola. El llançament es realitza mitjançant l'script de línia d'ordres del sistema operatiu (sh/cmd).
Ordre per executar-se a Windows:
binkopycat -g 23946 -n rhino -l user -y library -p firmware=firmwarerhino_pass.bin,tty_dbg=COM26,tty_bt=COM28
Ordre per executar-se sota Linux mitjançant la utilitat 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
— Port TCP que estarà obert per accedir al servidor GDB;-n rhino
— nom del mòdul del sistema principal (dispositiu muntat);-l user
— nom de la biblioteca per cercar el mòdul principal;-y library
— camí per cercar mòduls inclosos al dispositiu;firmwarerhino_pass.bin
— camí al fitxer del microprogramari;- COM26 i COM28 són ports COM virtuals.
Com a resultat, es mostrarà un missatge Python >
(O 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 >
Interacció amb IDA Pro
Per simplificar les proves, utilitzem el microprogramari Rhino com a fitxer font per a l'anàlisi a IDA en el formulari
També podeu utilitzar el microprogramari principal sense metainformació.
Després d'iniciar Kopycat a IDA Pro, al menú Depurador aneu a l'element "Canvia el depurador..."i seleccioneu"Depurador GDB remot". A continuació, configureu la connexió: menú Depurador: opcions de procés...
Estableix els valors:
- Aplicació: qualsevol valor
- Nom d'amfitrió: 127.0.0.1 (o l'adreça IP de la màquina remota on s'executa Kopycat)
- Port: 23946
Ara el botó de depuració està disponible (tecla F9):
Feu-hi clic per connectar-vos al mòdul depurador de l'emulador. IDA entra en mode de depuració, hi ha finestres addicionals disponibles: informació sobre registres, sobre la pila.
Ara podem utilitzar totes les funcions estàndard del depurador:
- execució pas a pas de les instruccions (Entrar a и Passar per sobre — tecles F7 i F8, respectivament);
- iniciar i aturar l'execució;
- creant punts d'interrupció tant per al codi com per a les dades (tecla F2).
Connectar-se a un depurador no significa executar el codi del microprogramari. La posició d'execució actual ha de ser l'adreça 0x08006A74
- inici de la funció Reset_Handler. Si us desplaceu cap avall per la llista, podreu veure la trucada de funció principal. Podeu col·locar el cursor en aquesta línia (adreça 0x08006ABE
) i realitzar l'operació Executeu fins al cursor (tecla F4).
A continuació, podeu prémer F7 per entrar a la funció principal.
Si executeu l'ordre Continuar el procés (tecla F9), llavors apareixerà la finestra "Espereu" amb un sol botó suspendre:
Quan premeu suspendre L'execució del codi del microprogramari es suspèn i es pot continuar des de la mateixa adreça del codi on es va interrompre.
Si continueu executant el codi, veureu les següents línies als terminals connectats als ports COM virtuals:
La presència de la línia "bypass d'estat" indica que el mòdul Bluetooth virtual ha canviat al mode de recepció de dades del port COM de l'usuari.
Ara al terminal Bluetooth (COM29 a la imatge) podeu introduir ordres d'acord amb el protocol Rhino. Per exemple, l'ordre "MEOW" retornarà la cadena "mur-mur" al terminal Bluetooth:
No em imita del tot
Quan creeu un emulador, podeu triar el nivell de detall/emulació d'un dispositiu concret. Per exemple, el mòdul Bluetooth es pot emular de diferents maneres:
- el dispositiu està totalment emulat amb un conjunt complet d'ordres;
- Les ordres AT s'emulen i el flux de dades es rep des del port COM del sistema principal;
- el dispositiu virtual proporciona una redirecció completa de dades al dispositiu real;
- com un simple taló que sempre retorna "OK".
La versió actual de l'emulador utilitza el segon enfocament: el mòdul Bluetooth virtual realitza la configuració, després de la qual cosa passa al mode de "proxy" de dades des del port COM del sistema principal al port UART de l'emulador.
Considerem la possibilitat d'una instrumentació senzilla del codi en cas que alguna part de la perifèria no estigui implementada. Per exemple, si no s'ha creat un temporitzador responsable de controlar la transferència de dades a DMA (la comprovació es realitza a la funció ws2812b_esperasituat a 0x08006840
), aleshores el microprogramari sempre esperarà que la bandera es restableixi ocupatsituat a 0x200004C4
que mostra l'ocupació de la línia de dades DMA:
Podem evitar aquesta situació reiniciant manualment la bandera ocupat immediatament després d'instal·lar-lo. A IDA Pro, podeu crear una funció Python i cridar-la en un punt d'interrupció, i posar el punt d'interrupció al codi després d'escriure el valor 1 a la bandera. ocupat.
Gestor de punt d'interrupció
Primer, creem una funció Python a IDA. Menú Fitxer: ordre d'script...
Afegiu un fragment nou a la llista de l'esquerra, doneu-li un nom (per exemple, CPM),
Al camp de text de la dreta, introduïu el codi de funció:
def skip_dma():
print "Skipping wait ws2812..."
value = Byte(0x200004C4)
if value == 1:
PatchDbgByte(0x200004C4, 0)
return False
Després d'això, feu clic Correr i tanqueu la finestra de l'script.
Ara anem al codi a 0x0800688A
, establiu un punt d'interrupció (tecla F2), editeu-lo (menú contextual Edita el punt d'interrupció...), no us oblideu d'establir el tipus d'script a Python:
Si el valor de la bandera actual ocupat és igual a 1, llavors hauríeu d'executar la funció skip_dma a la línia de guió:
Si executeu el microprogramari per a l'execució, l'activació del codi del controlador del punt d'interrupció es pot veure a la finestra IDA sortida per línia Skipping wait ws2812...
. Ara el microprogramari no esperarà que es restableixi la bandera ocupat.
Interacció amb l'emulador
L'emulació pel bé de l'emulació és poc probable que provoqui alegria i alegria. És molt més interessant si l'emulador ajuda l'investigador a veure les dades a la memòria o establir la interacció dels fils.
Us mostrarem com establir de manera dinàmica la interacció entre les tasques RTOS. Primer hauríeu d'aturar l'execució del codi si s'està executant. Si aneu a la funció entrada_tasca_bluetooth a la branca de processament de l'ordre "LED" (adreça 0x080057B8
), llavors podeu veure què es crea primer i després s'envia a la cua del sistema ledControlQueueHandle algun missatge.
Hauríeu d'establir un punt d'interrupció per accedir a la variable ledControlQueueHandlesituat a 0x20000624
i continueu executant el codi:
Com a resultat, la parada es produirà primer a l'adreça 0x080057CA
abans de cridar la funció osMailAlloc, després a l'adreça 0x08005806
abans de cridar la funció osMailPut, després d'un temps - a l'adreça 0x08005BD4
(abans de cridar la funció osMailGet), que pertany a la funció entrada_tasca_leds (Tasca LED), és a dir, les tasques es van canviar i ara la tasca LED va rebre el control.
D'aquesta manera senzilla podeu establir com interactuen les tasques RTOS entre elles.
Per descomptat, en realitat, la interacció de les tasques pot ser més complicada, però amb un emulador, el seguiment d'aquesta interacció es fa menys laboriós.
Llançament amb Radare2
No podeu ignorar una eina tan universal com Radare2.
Per connectar-se a l'emulador mitjançant r2, l'ordre seria així:
radare2 -A -a arm -b 16 -d gdb://localhost:23946 rhino_fw42k6.elf
Llançament disponible ara (dc
) i aturar l'execució (Ctrl+C).
Malauradament, de moment, r2 té problemes quan treballa amb el maquinari gdb i la disposició de la memòria per això, els punts d'interrupció i els passos no funcionen (comanda ds
). Esperem que això s'arregli aviat.
Córrer amb Eclipse
Una de les opcions per utilitzar l'emulador és depurar el microprogramari del dispositiu que s'està desenvolupant. Per a més claredat, també utilitzarem el firmware de Rhino. Podeu descarregar les fonts del firmware
Utilitzarem Eclipse del conjunt com a IDE
Perquè l'emulador carregui el microprogramari compilat directament a l'Eclipse, heu d'afegir el paràmetre firmware=null
a l'ordre d'inici de l'emulador:
binkopycat -g 23946 -n rhino -l user -y modules -p firmware=null,tty_dbg=COM26,tty_bt=COM28
Configuració de la configuració de depuració
A Eclipse, seleccioneu el menú Executar - Depurar configuracions... A la finestra que s'obre, a la secció Depuració de maquinari GDB heu d'afegir una nova configuració i, a continuació, a la pestanya "Principal", especifiqueu el projecte i l'aplicació actuals per a la depuració:
A la pestanya "Depurador", heu d'especificar l'ordre GDB:
${openstm32_compiler_path}arm-none-eabi-gdb
I també introduïu els paràmetres per connectar-vos al servidor GDB (amfitrió i port):
A la pestanya "Inici", heu d'especificar els paràmetres següents:
- activa la casella de selecció Carrega la imatge (de manera que la imatge del microprogramari muntada es carregui a l'emulador);
- activa la casella de selecció Símbols de càrrega;
- afegir l'ordre d'inici:
set $pc = *0x08000004
(configureu el registre del PC al valor de la memòria a l'adreça0x08000004
- l'adreça s'emmagatzema allà ResetHandler).
Preste atenció, si no voleu descarregar el fitxer de microprogramari d'Eclipse, les opcions Carrega la imatge и Executeu ordres no cal indicar.
Després de fer clic a Depurar, podeu treballar en mode depurador:
- execució de codi pas a pas
- interactuant amb els punts d'interrupció
Nota. Eclipse té, hmm... algunes peculiaritats... i has de viure amb elles. Per exemple, si en iniciar el depurador apareix el missatge "No hi ha font disponible per a "0x0"", executeu l'ordre Step (F5).
En lloc d'una conclusió
Emular codi natiu és una cosa molt interessant. Per a un desenvolupador de dispositius, és possible depurar el microprogramari sense un dispositiu real. Per a un investigador, és una oportunitat per dur a terme una anàlisi de codi dinàmic, que no sempre és possible fins i tot amb un dispositiu.
Volem oferir als especialistes una eina convenient, moderadament senzilla i que no requereixi molt d'esforç i temps per configurar-la i executar-la.
Escriu als comentaris sobre la teva experiència amb emuladors de maquinari. Et convidem a debatre i estarem encantats de respondre les preguntes.
Només els usuaris registrats poden participar en l'enquesta.
Per a què fas servir l'emulador?
-
Desenvolupo (depuro) firmware
-
Estic investigant el firmware
-
Llanço jocs (Dendi, Sega, PSP)
-
una altra cosa (escriu als comentaris)
Han votat 7 usuaris. 2 usuaris es van abstenir.
Quin programari feu servir per emular codi natiu?
-
QEMU
-
Motor unicorn
-
Proteu
-
una altra cosa (escriu als comentaris)
Han votat 6 usuaris. 2 usuaris es van abstenir.
Què t'agradaria millorar en l'emulador que fas servir?
-
Vull velocitat
-
Vull facilitat de configuració/llançament
-
Vull més opcions per interactuar amb l'emulador (API, ganxos)
-
Estic content amb tot
-
una altra cosa (escriu als comentaris)
Han votat 8 usuaris. 1 usuari es va abstenir.
Font: www.habr.com