Kadre de la kunveno 0x0A DC7831
En ĉi tiu artikolo ni priskribos kiel ruli la aparatan firmware en la emulilo, pruvi interagadon kun la erarserĉilo kaj fari malgrandan dinamikan analizon de la firmvaro.
antaŭhistorio
Antaŭ longe en galaksio tre malproksime
Antaŭ kelkaj jaroj en nia laboratorio estis bezono esplori la firmvaro de aparato. La firmvaro estis kunpremita kaj malpakita per ekŝargilo. Li faris tion en tre komplika maniero, ŝanĝante la datumojn en memoro plurfoje. Kaj la firmvaro mem tiam aktive interagis kun la ekstercentraj. Kaj ĉio ĉi sur la MIPS-kerno.
Pro objektivaj kialoj, la disponeblaj emuliloj ne konvenis al ni, sed ni ankoraŭ volis ruli la kodon. Tiam ni decidis fari nian propran emulilon, kiu farus la minimumon kaj permesus al ni malpaki la ĉefan firmware. Ni provis ĝin kaj ĝi funkciis. Ni pensis, kio se ni aldonas ekstercentrajn por ankaŭ plenumi la ĉefan firmware. Ĝi ne multe doloris - kaj ĝi ankaŭ funkciis. Ni denove pensis kaj decidis fari plentaŭgan emulilon.
La rezulto estis komputilsistemo emulilo
Kial Kopycat?
Estas vortludo.
- copycat (angle, substantivo [ˈkɒpɪkæt]) - imitanto, imitanto
- kato (angle, substantivo [ˈkæt]) - kato, kato - la plej ŝatata besto de unu el la kreintoj de la projekto
- La litero "K" estas el la programlingvo Kotlin
Kopiulo
Kreinte la emulilon, tre specifaj celoj estis fiksitaj:
- la kapablo rapide krei novajn ekstercentrajn, modulojn, procesorajn kernojn;
- la kapablo kunveni virtualan aparaton de diversaj moduloj;
- la kapablo ŝargi ajnajn binarajn datumojn (firmware) en la memoron de virtuala aparato;
- kapablo labori kun momentfotoj (fotoj de la sistema stato);
- la kapablo interagi kun la emulilo per la enkonstruita erarserĉilo;
- bela moderna lingvo por evoluo.
Kiel rezulto, Kotlin estis elektita por efektivigo, busarkitekturo (tio estas kiam moduloj komunikas kun unu la alian per virtualaj datenbusoj), JSON kiel aparatpriskriba formato, kaj GDB RSP kiel protokolo por interagado kun la erarserĉilo.
Evoluo daŭras iom pli ol du jarojn kaj aktive daŭras. Dum tiu tempo, MIPS, x86, V850ES, ARM, kaj PowerPC-procesorkernoj estis efektivigitaj.
La projekto kreskas kaj estas tempo prezenti ĝin al la larĝa publiko. Ni faros detalan priskribon de la projekto poste, sed nuntempe ni fokusos uzi Kopycat.
Por la plej senpaciencaj, reklama versio de la emulilo povas esti elŝutita de
Rinocero en la emulilo
Ni rememoru, ke pli frue por la konferenco SMARTRHINO-2018, oni kreis testan aparaton "Rinocero" por instruado de reversa inĝenierado. La procezo de senmova firmvaranalizo estis priskribita en
Nun ni provu aldoni "parolantoj" kaj ruli la firmware en la emulilo.
Ni bezonos:
1) Java 1.8
2) Python kaj modulo
Por Vindozo:
1)
2)
Por Linukso:
1) sokato
Vi povas uzi Eclipse, IDA Pro aŭ radare2 kiel GDB-klienton.
Kiel ĝi funkcias?
Por plenumi firmvaron en la emulilo, necesas "kunmeti" virtualan aparaton, kiu estas analogo de reala aparato.
La reala aparato ("rinocero") povas esti montrita en la blokdiagramo:
La emulilo havas modulan strukturon kaj la fina virtuala aparato povas esti priskribita en JSON-dosiero.
JSON 105 linioj
{
"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"]
]
}
Atentu la parametron firmware sekcio parametroj estas la nomo de dosiero, kiu povas esti ŝargita en virtuala aparato kiel firmvaro.
La virtuala aparato kaj ĝia interago kun la ĉefa operaciumo povas esti reprezentitaj per la sekva diagramo:
La nuna testokazaĵo de la emulilo implikas interagadon kun la COM-havenoj de la ĉefa OS (sencimigi UART kaj UART por la Bluetooth-modulo). Ĉi tiuj povas esti veraj havenoj al kiuj aparatoj estas konektitaj aŭ virtualaj COM-havenoj (por tio vi nur bezonas com0com/socat).
Nuntempe ekzistas du ĉefaj manieroj interagi kun la emulilo de ekstere:
- GDB RSP-protokolo (laŭ la iloj kiuj subtenas ĉi tiun protokolon estas Eclipse / IDA / radare2);
- interna emulila komandlinio (Argparse aŭ Python).
Virtualaj COM-havenoj
Por interagi kun la UART de virtuala aparato sur la loka maŝino per terminalo, vi devas krei paron da rilataj virtualaj COM-havenoj. En nia kazo, unu haveno estas uzata de la emulilo, kaj la dua estas uzata de fina programo (PuTTY aŭ ekrano):
Uzante com0com
Virtualaj COM-havenoj estas agorditaj uzante la agordan ilon de la kom0com-ilaro (konzolversio - C:Programdosieroj (x86)com0comsetupс.exe, aŭ GUI-versio - C:Programdosieroj (x86)com0comsetupg.exe):
Kontrolu la skatolojn ebligi bufron superfluon por ĉiuj kreitaj virtualaj havenoj, alie la emulilo atendos respondon de la COM-haveno.
Uzante socat
En UNIX-sistemoj, virtualaj COM-havenoj estas aŭtomate kreitaj de la emulilo uzante la socat-ilaĵon; por fari tion, simple specifu la prefikson en la havennomo dum lanĉado de la emulilo. socat:
.
Interna komandlinia interfaco (Argparse aŭ Python)
Ĉar Kopycat estas konzola aplikaĵo, la emulilo disponigas du komandliniajn interfacopciojn por interagi kun ĝiaj objektoj kaj variabloj: Argparse kaj Python.
Argparse estas CLI enkonstruita en Kopycat kaj ĉiam disponeblas por ĉiuj.
Alternativa CLI estas la Python-interpretilo. Por uzi ĝin, vi devas instali la Jep Python-modulon kaj agordi la emulilon por labori kun Python (la Python-interpretilo instalita sur la ĉefa sistemo de la uzanto estos uzata).
Instalante la Python-modulon Jep
Sub Linukso Jep povas esti instalita per pip:
pip install jep
Por instali Jep sur Vindozo, vi unue devas instali la Windows SDK kaj la respondan Microsoft Visual Studio. Ni iom plifaciligis al vi kaj
pip install jep-3.8.2-cp27-cp27m-win_amd64.whl
Por kontroli la instaladon de Jep, vi devas funkcii per la komandlinio:
python -c "import jep"
La sekva mesaĝo devus esti ricevita kiel respondo:
ImportError: Jep is not supported in standalone Python, it must be embedded in Java.
En la emulilo-dosiero por via sistemo (kopianto.bat - por Vindozo, kopianto - por Linukso) al la listo de parametroj DEFAULT_JVM_OPTS
aldonu plian parametron Djava.library.path
— ĝi devas enhavi la vojon al la instalita Jep-modulo.
La rezulto por Vindozo devus esti linio tia:
set DEFAULT_JVM_OPTS="-XX:MaxMetaspaceSize=256m" "-XX:+UseParallelGC" "-XX:SurvivorRatio=6" "-XX:-UseGCOverheadLimit" "-Djava.library.path=C:/Python27/Lib/site-packages/jep"
Lanĉante Kopycat
La emulilo estas konzola JVM-aplikaĵo. La lanĉo estas efektivigita per la operaciuma komandlinia skripto (sh/cmd).
Komando funkcii sub Vindozo:
binkopycat -g 23946 -n rhino -l user -y library -p firmware=firmwarerhino_pass.bin,tty_dbg=COM26,tty_bt=COM28
Komando funkcii sub Linukso uzante la ilon 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-haveno, kiu estos malfermita por aliro al la GDB-servilo;-n rhino
— nomo de la ĉefa sistema modulo (kunmetita aparato);-l user
— nomo de la biblioteko por serĉi la ĉefan modulon;-y library
— vojo por serĉi modulojn inkluzivitajn en la aparato;firmwarerhino_pass.bin
— vojo al la firmware-dosiero;- COM26 kaj COM28 estas virtualaj COM-havenoj.
Kiel rezulto, invito estos montrata Python >
(aŭ 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 >
Interago kun IDA Pro
Por simpligi testadon, ni uzas la Firmaron Rhino kiel la fontdosieron por analizo en IDA en la formo
Vi ankaŭ povas uzi la ĉefan firmvaro sen metainformoj.
Post lanĉo de Kopycat en IDA Pro, en la menuo de Erarserĉilo iru al la elemento "Ŝalti erarserĉilon..."Kaj elektu"Fora GDB-erarseĉilo". Poste, agordu la konekton: menuo Sencimigilo - Procezaj opcioj...
Agordu la valorojn:
- Apliko - ajna valoro
- Gastigantonomo: 127.0.0.1 (aŭ la IP-adreso de la fora maŝino kie Kopycat funkcias)
- Haveno: 23946
Nun la sencimiga butono fariĝas disponebla (F9-klavo):
Alklaku ĝin por konekti al la erarserĉimilo en la emulilo. IDA iras en sencimigan reĝimon, pliaj fenestroj iĝas disponeblaj: informoj pri registroj, pri la stako.
Nun ni povas uzi ĉiujn normajn funkciojn de la erarserĉilo:
- paŝo post paŝo plenumo de instrukcioj (Eniru и Paŝi super — klavoj F7 kaj F8 respektive);
- komenci kaj paŭzi la ekzekuton;
- kreante rompopunktojn por kaj kodo kaj datumoj (F2-klavo).
Konekti al erarserĉilo ne signifas ruli la firmvarkodon. La nuna ekzekutpozicio devas esti la adreso 0x08006A74
— komenco de funkcio Restarigi_Handler. Se vi rulumas malsupren la liston, vi povas vidi la funkciovokon ĉefa. Vi povas meti la kursoron sur ĉi tiun linion (adreso 0x08006ABE
) kaj plenumi la operacion Kuru ĝis kursoro (klavo F4).
Poste, vi povas premi F7 por eniri la funkcion ĉefa.
Se vi rulas la komandon Daŭrigu procezon (F9-klavo), tiam la fenestro "Bonvolu atendi" aperos kun ununura butono Interrompi:
Kiam vi premas Interrompi La ekzekuto de la firmware-kodo estas suspendita kaj povas esti daŭrigita de la sama adreso en la kodo kie ĝi estis interrompita.
Se vi daŭrigas ekzekuti la kodon, vi vidos la sekvajn liniojn en la terminaloj konektitaj al la virtualaj COM-havenoj:
La ĉeesto de la linio "ŝtata pretervojo" indikas, ke la virtuala Bluetooth-modulo ŝanĝis al la reĝimo ricevi datumojn de la COM-haveno de la uzanto.
Nun en la Bluetooth-terminalo (COM29 en la bildo) vi povas enigi komandojn laŭ la protokolo Rhino. Ekzemple, la komando "MEOW" resendos la ĉenon "mur-mur" al la Bluetooth-terminalo:
Imutu min ne tute
Konstruante emulilon, vi povas elekti la nivelon de detalo/emulado de aparta aparato. Ekzemple, la Bluetooth-modulo povas esti kopiita en malsamaj manieroj:
- la aparato estas plene kopiita kun plena aro de komandoj;
- AT-komandoj estas kopiitaj, kaj la datumfluo ricevas de la COM-haveno de la ĉefa sistemo;
- la virtuala aparato provizas kompletan datuman redirekton al la reala aparato;
- kiel simpla ĝermo, kiu ĉiam resendas "Bone".
La nuna versio de la emulilo uzas la duan aliron - la virtuala Bluetooth-modulo faras agordon, post kiu ĝi ŝanĝas al la reĝimo de "proxiki" datumoj de la COM-haveno de la ĉefa sistemo al la UART-haveno de la emulilo.
Ni konsideru la eblecon de simpla instrumentado de la kodo, se iu parto de la periferio ne estas efektivigita. Ekzemple, se ne estis kreita tempigilo respondeca pri kontrolo de datumtransdono al DMA (la kontrolo estas farita en la funkcio ws2812b_atendusituanta ĉe 0x08006840
), tiam la firmvaro ĉiam atendos ke la flago estos rekomencigita okupitasituanta ĉe 0x200004C4
kiu montras la okupadon de la DMA-datumlinio:
Ni povas ĉirkaŭiri ĉi tiun situacion permane restarigante la flagon okupita tuj post instalo de ĝi. En IDA Pro, vi povas krei Python-funkcion kaj nomi ĝin en rompopunkto, kaj meti la rompopunkton mem en la kodon post skribado de la valoro 1 al la flago. okupita.
Pritraktilo de rompopunkto
Unue, ni kreu Python-funkcion en IDA. Menuo Dosiero - Skripto-komando...
Aldonu novan fragmenton en la liston maldekstre, donu al ĝi nomon (ekzemple, CPM),
En la teksta kampo dekstre, enigu la funkciokodon:
def skip_dma():
print "Skipping wait ws2812..."
value = Byte(0x200004C4)
if value == 1:
PatchDbgByte(0x200004C4, 0)
return False
Post tio ni premas Kuri kaj fermu la skriptofenestron.
Nun ni iru al la kodo ĉe 0x0800688A
, starigu rompopunkton (F2 klavo), redakti ĝin (kunteksta menuo Redakti rompopunkton...), ne forgesu agordi la skriptspecon al Python:
Se la aktuala flagvaloro okupita egalas 1, tiam vi devus ekzekuti la funkcion skip_dma en la skriptolinio:
Se vi rulas la firmvaro por ekzekuto, la ekfunkciigo de la romppunkto pritraktilo kodo povas esti vidita en la IDA fenestro eligon per linio Skipping wait ws2812...
. Nun la firmvaro ne atendos, ke la flago estos rekomencigita okupita.
Interago kun la emulilo
Emulado pro emulado verŝajne ne kaŭzos ĝojon kaj ĝojon. Estas multe pli interese se la emulilo helpas la esploriston vidi la datumojn en memoro aŭ establi la interagadon de fadenoj.
Ni montros al vi kiel dinamike establi interagadon inter RTOS-taskoj. Vi unue devus paŭzi la ekzekuton de la kodo se ĝi funkcias. Se vi iras al la funkcio bluetooth_task_entry al la prilabora branĉo de la komando "LED" (adreso 0x080057B8
), tiam vi povas vidi kio unue estas kreita kaj poste sendita al la sistema vosto ledControlQueueHandle iun mesaĝon.
Vi devus agordi rompopunkton por aliri la variablon ledControlQueueHandlesituanta ĉe 0x20000624
kaj daŭrigu ekzekuti la kodon:
Kiel rezulto, la halto unue okazos ĉe adreso 0x080057CA
antaŭ voki la funkcion osMailAlloc, poste ĉe la adreso 0x08005806
antaŭ voki la funkcion osMailPut, poste post momento - al la adreso 0x08005BD4
(antaŭ ol voki la funkcion osMailGet), kiu apartenas al la funkcio leds_task_entry (LED-tasko), tio estas, la taskoj ŝanĝis, kaj nun la LED-tasko ricevis kontrolon.
Per ĉi tiu simpla maniero vi povas establi kiel RTOS-taskoj interagas unu kun la alia.
Kompreneble, en realeco, la interago de taskoj povas esti pli komplika, sed uzante emulilon, spuri ĉi tiun interagon fariĝas malpli peniga.
Lanĉu kun Radare2
Vi ne povas ignori tian universalan ilon kiel Radare2.
Por konektiĝi al la emulilo uzante r2, la komando aspektus jene:
radare2 -A -a arm -b 16 -d gdb://localhost:23946 rhino_fw42k6.elf
Lanĉo disponebla nun (dc
) kaj paŭzi la ekzekuton (Ctrl+C).
Bedaŭrinde, nuntempe, r2 havas problemojn kiam laboras kun la aparataro gdb-servilo kaj memoraranĝo; pro tio, rompopunktoj kaj Paŝoj ne funkcias (komando ds
). Ni esperas, ke ĉi tio estos riparita baldaŭ.
Kurante kun Eclipse
Unu el la ebloj por uzi la emulilon estas sencimigi la firmvaro de la evoluiga aparato. Por klareco, ni ankaŭ uzos la Firmaron Rhino. Vi povas elŝuti la firmware-fontojn
Ni uzos Eclipse de la aro kiel IDE
Por ke la emulilo ŝarĝu firmvaron rekte kompilitan en Eclipse, vi devas aldoni la parametron firmware=null
al la lanĉa komando de emulilo:
binkopycat -g 23946 -n rhino -l user -y modules -p firmware=null,tty_dbg=COM26,tty_bt=COM28
Agordi sencimigan agordon
En Eklipso, elektu la menuon Run - Sencimigi Agordojn... En la fenestro kiu malfermiĝas, en la sekcio GDB-Aparara Sencimigo vi devas aldoni novan agordon, tiam sur la langeto "Ĉefa" specifu la nunan projekton kaj aplikaĵon por senararigado:
Sur la langeto "Senĉimilo" vi devas specifi la GDB-komandon:
${openstm32_compiler_path}arm-none-eabi-gdb
Kaj ankaŭ enigu la parametrojn por konekti al la GDB-servilo (gastiganto kaj haveno):
En la langeto "Komenco", vi devas specifi la sekvajn parametrojn:
- ebligu markobutonon Ŝargi bildon (por ke la kunmetita firmvarbildo estas ŝarĝita en la emulilon);
- ebligu markobutonon Ŝarĝi simbolojn;
- aldonu lanĉan komandon:
set $pc = *0x08000004
(metu la komputilan registron al la valoro de memoro ĉe adreso0x08000004
- la adreso estas konservita tie ResetHandler).
Atentu, se vi ne volas elŝuti la firmvardosieron de Eclipse, tiam la opcioj Ŝargi bildon и Rulu komandojn ne necesas indiki.
Post klakado de Sencimigi, vi povas labori en sencimiga reĝimo:
- paŝo post paŝo kodo ekzekuto
- interagante kun rompopunktoj
Примечание. Eklipso havas, hmm... kelkajn strangaĵojn... kaj vi devas vivi kun ili. Ekzemple, se dum ekfunkciigo de la erarserĉilo aperas la mesaĝo "Neniu fonto disponebla por "0x0"", tiam ekzekutu la Paŝo-komandon (F5)
Anstataŭ konkludo
Emuli denaskan kodon estas tre interesa afero. Eblas por aparato-programisto sencimigi la firmware sen vera aparato. Por esploristo, ĝi estas ŝanco fari dinamikan kodan analizon, kio ne ĉiam eblas eĉ per aparato.
Ni volas provizi specialistojn per ilo oportuna, modere simpla kaj ne bezonas multe da penado kaj tempo por instali kaj funkcii.
Skribu en la komentoj pri via sperto uzante aparatajn emulilojn. Ni invitas vin diskuti kaj volonte respondos demandojn.
Nur registritaj uzantoj povas partopreni la enketon.
Por kio vi uzas la emulilon?
-
Mi evoluigas (sencimigi) firmware
-
Mi esploras firmware
-
Mi lanĉas ludojn (Dendi, Sega, PSP)
-
io alia (skribi en la komentoj)
Voĉdonis 7 uzantoj. 2 uzantoj sindetenis.
Kiun programaron vi uzas por kopii denaskan kodon?
-
QEMU
-
Motoro de unikorno
-
Proteus
-
io alia (skribi en la komentoj)
Voĉdonis 6 uzantoj. 2 uzantoj sindetenis.
Kion vi ŝatus plibonigi en la emulilo, kiun vi uzas?
-
Mi volas rapidecon
-
Mi volas facilecon de agordo/lanĉo
-
Mi volas pliajn eblojn por interagi kun la emulilo (API, hokoj)
-
Mi estas feliĉa pri ĉio
-
io alia (skribi en la komentoj)
Voĉdonis 8 uzantoj. 1 uzanto sindetenis.
fonto: www.habr.com