A megbeszélés részeként 0x0A DC7831
Ebben a cikkben leírjuk, hogyan futtassa az eszköz firmware-ét az emulátorban, bemutatjuk a hibakeresővel való interakciót, és hogyan végezzük el a firmware rövid dinamikus elemzését.
őstörténet
Nagyon régen egy messzi galaxisban
Néhány éve laboratóriumunkban szükség volt egy készülék firmware-ének vizsgálatára. A firmware-t tömörítették és bootloaderrel kicsomagolták. Ezt nagyon bonyolult módon tette, többször eltolta az adatokat a memóriában. És maga a firmware ezután aktívan együttműködött a perifériákkal. És mindez a MIPS magon.
Objektív okokból a rendelkezésre álló emulátorok nem feleltek meg nekünk, de a kódot továbbra is le akartuk futtatni. Aztán úgy döntöttünk, hogy elkészítjük saját emulátorunkat, amely a minimumot teszi, és lehetővé teszi a fő firmware kicsomagolását. Kipróbáltuk és bevált. Arra gondoltunk, mi lenne, ha perifériákat adnánk hozzá a fő firmware végrehajtásához. Nem nagyon fájt – és bevált is. Újra gondoltuk, és úgy döntöttünk, hogy készítünk egy teljes értékű emulátort.
Az eredmény egy számítógépes rendszerek emulátora lett
Miért Kopycat?
Van játék a szavakkal.
- másolt (angol, főnév [ˈkɒpɪkæt]) - utánzó, utánzó
- hogyan (angolul, főnév [ˈkæt]) - macska, macska - a projekt egyik alkotójának kedvenc állata
- A „K” betű a Kotlin programozási nyelvből származik
Copycat
Az emulátor létrehozásakor nagyon konkrét célokat tűztek ki:
- új perifériák, modulok, processzormagok gyors létrehozásának képessége;
- virtuális eszköz összeállításának képessége különböző modulokból;
- bármilyen bináris adat (firmware) betöltésének képessége egy virtuális eszköz memóriájába;
- pillanatképekkel (a rendszer állapotáról készült pillanatképekkel) való munka képessége;
- az emulátorral való interakció képessége a beépített hibakeresőn keresztül;
- szép modern nyelv a fejlődéshez.
Ennek eredményeként a Kotlint választották a megvalósításhoz, a busz architektúrát (ez az, amikor a modulok virtuális adatbuszokon keresztül kommunikálnak egymással), a JSON-t az eszközleírás formátumaként, és a GDB RSP-t a hibakeresővel való interakció protokolljaként.
A fejlesztés alig több mint két éve folyik, és aktívan folyik. Ez idő alatt MIPS, x86, V850ES, ARM és PowerPC processzormagokat valósítottak meg.
A projekt növekszik, és ideje bemutatni a szélesebb nyilvánosságnak. A projektről később részletes leírást készítünk, de egyelőre a Kopycat használatára koncentrálunk.
A legtürelmetlenebbek számára az emulátor promóciós verziója letölthető innen
Rhino az emulátorban
Emlékezzünk vissza, hogy korábban a SMARTRHINO-2018 konferenciára egy „Orrszarvú” tesztkészüléket készítettek a reverse engineering ismeretek oktatására. A statikus firmware-elemzés folyamatát a
Most próbáljuk meg hozzáadni a „hangszórókat”, és futtatni a firmware-t az emulátorban.
Szükségünk van:
1) Java 1.8
2) Python és modul
Windows esetén:
1)
2)
Linux esetén:
1) socat
GDB-kliensként használhatja az Eclipse-t, az IDA Pro-t vagy a radare2-t.
Hogyan működik?
A firmware emulátorban történő végrehajtásához egy virtuális eszközt kell „összeszerelni”, amely egy valódi eszköz analógja.
A valódi eszköz („orrszarvú”) a blokkdiagramon látható:
Az emulátor moduláris felépítésű, és a végső virtuális eszköz egy JSON-fájlban írható le.
JSON 105 sorok
{
"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"]
]
}
Ügyeljen a paraméterre firmware szakasz params egy virtuális eszközre firmware-ként betölthető fájl neve.
A virtuális eszköz és interakciója a fő operációs rendszerrel a következő diagrammal ábrázolható:
Az emulátor jelenlegi tesztpéldánya interakciót foglal magában a fő operációs rendszer COM-portjaival (az UART és az UART hibakeresése a Bluetooth modulhoz). Ezek lehetnek valódi portok, amelyekhez eszközök csatlakoznak, vagy virtuális COM portok (ehhez csak szüksége van com0com/socat).
Jelenleg két fő módja van az emulátorral való külső interakciónak:
- GDB RSP protokoll (ennek megfelelően a protokollt támogató eszközök az Eclipse / IDA / radare2);
- belső emulátor parancssora (Argparse vagy Python).
Virtuális COM portok
A helyi gépen lévő virtuális eszköz UART-jával terminálon keresztül történő interakcióhoz létre kell hoznia egy pár kapcsolódó virtuális COM-portot. Esetünkben az egyik portot az emulátor, a másodikat pedig egy terminálprogram (PuTTY vagy képernyő) használja:
com0com használatával
A virtuális COM portok a com0com készlet telepítő segédprogramjával konfigurálhatók (konzol verzió - C:Program Files (x86)com0comsetupс.exe, vagy GUI verzió - C:Program Files (x86)com0comsetupg.exe):
Jelölje be a négyzeteket puffertúllépés engedélyezése az összes létrehozott virtuális porthoz, ellenkező esetben az emulátor a COM-port válaszára vár.
A socat segítségével
UNIX rendszereken a virtuális COM portokat az emulátor automatikusan hozza létre a socat segédprogrammal; ehhez csak adja meg az előtagot a port nevében az emulátor indításakor socat:
.
Belső parancssori felület (Argparse vagy Python)
Mivel a Kopycat egy konzolalkalmazás, az emulátor két parancssori interfész lehetőséget biztosít az objektumokkal és változókkal való interakcióhoz: Argparse és Python.
Az Argparse a Kopycatba épített CLI, és mindig mindenki számára elérhető.
Alternatív CLI a Python interpreter. Használatához telepítenie kell a Jep Python modult, és be kell állítania az emulátort, hogy működjön a Pythonnal (a felhasználó fő rendszerére telepített Python értelmező kerül felhasználásra).
A Python Jep modul telepítése
Linux alatt a Jep pip-en keresztül telepíthető:
pip install jep
A Jep Windows rendszerre történő telepítéséhez először telepítenie kell a Windows SDK-t és a megfelelő Microsoft Visual Studio-t. Egy kicsit megkönnyítettük a dolgát, és
pip install jep-3.8.2-cp27-cp27m-win_amd64.whl
A Jep telepítésének ellenőrzéséhez futtassa a parancssorban:
python -c "import jep"
Válaszként a következő üzenetet kell megérkezni:
ImportError: Jep is not supported in standalone Python, it must be embedded in Java.
A rendszer emulátor kötegfájljában (copycat.bat - Windowshoz, másoló - Linux esetén) a paraméterek listájához DEFAULT_JVM_OPTS
adjon hozzá egy további paramétert Djava.library.path
— tartalmaznia kell a telepített Jep modul elérési útját.
A Windows eredményének egy ehhez hasonló sornak kell lennie:
set DEFAULT_JVM_OPTS="-XX:MaxMetaspaceSize=256m" "-XX:+UseParallelGC" "-XX:SurvivorRatio=6" "-XX:-UseGCOverheadLimit" "-Djava.library.path=C:/Python27/Lib/site-packages/jep"
Kopycat elindítása
Az emulátor egy konzolos JVM alkalmazás. Az indítás az operációs rendszer parancssori szkriptjén (sh/cmd) keresztül történik.
Windows alatti futtatási parancs:
binkopycat -g 23946 -n rhino -l user -y library -p firmware=firmwarerhino_pass.bin,tty_dbg=COM26,tty_bt=COM28
Linux alatt futtatandó parancs a socat segédprogrammal:
./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 port, amely nyitva lesz a GDB szerver eléréséhez;-n rhino
— a fő rendszermodul (összeszerelt eszköz) neve;-l user
— a fő modul kereséséhez szükséges könyvtár neve;-y library
— az eszközben található modulok keresési útvonala;firmwarerhino_pass.bin
— a firmware fájl elérési útja;- A COM26 és COM28 virtuális COM portok.
Ennek eredményeként egy felszólítás jelenik meg Python >
(vagy 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 >
Kölcsönhatás az IDA Pro-val
A tesztelés egyszerűsítése érdekében a Rhino firmware-t használjuk forrásfájlként az IDA-ban az űrlapon végzett elemzéshez
A fő firmware-t metainformációk nélkül is használhatja.
A Kopycat elindítása után az IDA Pro programban a Hibakereső menüben lépjen a „Hibakereső váltása…"És válassz"Távoli GDB hibakereső". Ezután állítsa be a kapcsolatot: menü Hibakereső – Folyamatbeállítások…
Állítsa be az értékeket:
- Alkalmazás - bármilyen érték
- Gazdanév: 127.0.0.1 (vagy annak a távoli gépnek az IP-címe, amelyen a Kopycat fut)
- Port: 23946
Most elérhetővé válik a hibakereső gomb (F9 billentyű):
Kattintson rá az emulátor hibakereső moduljához való csatlakozáshoz. Az IDA hibakereső módba lép, további ablakok válnak elérhetővé: információk a regiszterekről, a veremről.
Most már használhatjuk a hibakereső összes szabványos funkcióját:
- az utasítások lépésről lépésre történő végrehajtása (Belép и Átlép — F7 és F8 billentyűk);
- a végrehajtás elindítása és szüneteltetése;
- töréspontok létrehozása a kódhoz és az adatokhoz is (F2 billentyű).
A hibakeresőhöz való csatlakozás nem jelenti a firmware kód futtatását. Az aktuális végrehajtási pozíciónak a címnek kell lennie 0x08006A74
— a funkció kezdete Reset_Handler. Ha lefelé görget a listán, láthatja a függvényhívást fő-. A kurzort erre a sorra helyezheti (cím 0x08006ABE
) és hajtsa végre a műveletet Futtassa a kurzorig (F4 billentyű).
Ezután az F7 megnyomásával léphet be a funkcióba fő-.
Ha lefuttatja a parancsot Folytassa a folyamatot (F9 billentyű), akkor egyetlen gombbal megjelenik a „Kérem, várjon” ablak Felfüggesztés:
Amikor megnyomja Felfüggesztés a firmware kód végrehajtása felfüggesztésre kerül, és ugyanarról a címről folytatható, ahol a kódban megszakadt.
Ha folytatja a kód végrehajtását, a következő sorokat fogja látni a virtuális COM-portokhoz csatlakoztatott terminálokon:
Az „állapot bypass” vonal jelenléte azt jelzi, hogy a virtuális Bluetooth-modul átváltott a felhasználó COM-portjáról érkező adatok fogadásának módjára.
Most a Bluetooth terminálban (a képen COM29) a Rhino protokollnak megfelelő parancsokat adhat meg. Például a „MEOW” parancs a „mur-mur” karakterláncot adja vissza a Bluetooth terminálnak:
Utáljon engem ne teljesen
Emulátor készítésekor kiválaszthatja egy adott eszköz részletességi/emulációs szintjét. Például a Bluetooth modul többféleképpen emulálható:
- az eszköz teljes mértékben emulálva van a parancsok teljes készletével;
- Az AT parancsok emulálódnak, és az adatfolyam a fő rendszer COM portjáról érkezik;
- a virtuális eszköz teljes adatátirányítást biztosít a valós eszközre;
- egyszerű csonkként, amely mindig az "OK"-t adja vissza.
Az emulátor jelenlegi verziója a második megközelítést használja - a virtuális Bluetooth modul elvégzi a konfigurálást, majd átvált az adatok „proxy” módjára a fő rendszer COM-portjáról az emulátor UART-portjára.
Tekintsük a kód egyszerű műszerezésének lehetőségét arra az esetre, ha a periféria egy része nincs implementálva. Például, ha a DMA-ba történő adatátvitel vezérléséért felelős időzítő nincs létrehozva (az ellenőrzés a funkcióban történik ws2812b_waittalálható 0x08006840
), akkor a firmware mindig megvárja a zászló visszaállítását elfoglalttalálható 0x200004C4
amely a DMA adatvonal foglaltságát mutatja:
Megkerülhetjük ezt a helyzetet a zászló manuális visszaállításával elfoglalt közvetlenül a telepítés után. Az IDA Pro-ban létrehozhat egy Python-függvényt és meghívhatja azt egy töréspontban, és magát a töréspontot a kódba helyezheti, miután beírta az 1 értéket a zászlóba. elfoglalt.
Töréspontkezelő
Először is hozzunk létre egy Python függvényt az IDA-ban. Menü Fájl - Script parancs...
Adjon hozzá egy új kódrészletet a bal oldali listához, nevezze el (például BPT),
A jobb oldali szövegmezőbe írja be a funkció kódját:
def skip_dma():
print "Skipping wait ws2812..."
value = Byte(0x200004C4)
if value == 1:
PatchDbgByte(0x200004C4, 0)
return False
Ezt követően megnyomjuk futás és zárja be a script ablakot.
Most menjünk a kódhoz 0x0800688A
, állítson be egy töréspontot (F2 billentyű), szerkessze (helyi menü Töréspont szerkesztése...), ne felejtse el Pythonra állítani a szkript típusát:
Ha az aktuális zászlóérték elfoglalt egyenlő 1-gyel, akkor végre kell hajtania a függvényt skip_dma a script sorban:
Ha futtatja a firmware-t végrehajtáshoz, akkor az IDA ablakban láthatja a töréspontkezelő kód aktiválását teljesítmény vonal szerint Skipping wait ws2812...
. Most a firmware nem várja meg a zászló visszaállítását elfoglalt.
Interakció az emulátorral
Az emuláció kedvéért való emuláció valószínűleg nem okoz örömet és örömet. Sokkal érdekesebb, ha az emulátor segít a kutatónak a memóriában lévő adatok megtekintésében vagy a szálak kölcsönhatásának megállapításában.
Megmutatjuk, hogyan hozhat létre dinamikus interakciót az RTOS feladatok között. Ha fut, először szüneteltesse a kód végrehajtását. Ha a funkcióhoz megy bluetooth_task_entry a „LED” parancs feldolgozó ágához (cím 0x080057B8
), akkor láthatja, hogy mi kerül először létrehozásra, majd elküldésre a rendszersorba ledControlQueueHandle valami üzenetet.
A változó eléréséhez töréspontot kell beállítani ledControlQueueHandletalálható 0x20000624
és folytassa a kód végrehajtását:
Ennek eredményeként a megállás először a címen történik 0x080057CA
a függvény meghívása előtt osMailAlloc, majd a címen 0x08005806
a függvény meghívása előtt osMailPut, majd egy idő után - a címre 0x08005BD4
(a függvény meghívása előtt osMailGet), amely a funkcióhoz tartozik leds_task_entry (LED-task), vagyis a feladatok váltottak, és most a LED-feladat kapott irányítást.
Ezzel az egyszerű módon megállapíthatja, hogy az RTOS feladatok hogyan hatnak egymásra.
Természetesen a valóságban a feladatok interakciója bonyolultabb lehet, de egy emulátor használatával ennek az interakciónak a követése kevésbé fáradságos.
Indítsa el a Radare2-vel
Nem hagyhatja figyelmen kívül az olyan univerzális eszközt, mint a Radare2.
Az emulátorhoz való csatlakozáshoz az r2 használatával a parancs így néz ki:
radare2 -A -a arm -b 16 -d gdb://localhost:23946 rhino_fw42k6.elf
Az indítás már elérhető (dc
) és szüneteltesse a végrehajtást (Ctrl+C).
Sajnos jelenleg az r2-nek problémái vannak a hardveres gdb szerverrel és a memóriaelrendezéssel való munka során, emiatt a töréspontok és a lépések nem működnek (parancs ds
). Reméljük, hogy ez hamarosan megoldódik.
Futás az Eclipse-szal
Az emulátor használatának egyik lehetősége a fejlesztés alatt álló eszköz firmware-jének hibakeresése. Az egyértelműség kedvéért a Rhino firmware-t is használjuk. Letöltheti a firmware-forrásokat
A készletből származó Eclipse-t fogjuk használni IDE-ként
Ahhoz, hogy az emulátor betölthesse az Eclipse-ben közvetlenül lefordított firmware-t, hozzá kell adni a paramétert firmware=null
az emulátor indító parancsához:
binkopycat -g 23946 -n rhino -l user -y modules -p firmware=null,tty_dbg=COM26,tty_bt=COM28
Hibakeresési konfiguráció beállítása
Az Eclipse-ben válassza ki a menüt Futtatás – Konfigurációk hibakeresése... A megnyíló ablakban a szakaszban GDB hardver hibakeresés hozzá kell adnia egy új konfigurációt, majd a „Fő” lapon adja meg az aktuális projektet és alkalmazást a hibakereséshez:
A „Debugger” lapon meg kell adnia a GDB parancsot:
${openstm32_compiler_path}arm-none-eabi-gdb
Adja meg a GDB-kiszolgálóhoz való csatlakozás paramétereit is (gazdagép és port):
Az „Indítás” lapon a következő paramétereket kell megadnia:
- engedélyezése jelölőnégyzet Kép betöltése (hogy az összeállított firmware kép betöltődik az emulátorba);
- engedélyezése jelölőnégyzet Szimbólumok betöltése;
- indítási parancs hozzáadása:
set $pc = *0x08000004
(Állítsa be a PC-regisztert a cím memóriájából származó értékre0x08000004
- a cím ott van tárolva ResetHandler).
Figyeljen oda, ha nem akarod letölteni a firmware fájlt az Eclipse-ről, akkor a lehetőségek Kép betöltése и Parancsok futtatása nem kell jelezni.
A Debug gombra kattintás után hibakereső módban dolgozhat:
- lépésről lépésre kódvégrehajtás
- kölcsönhatásba lép a töréspontokkal
Megjegyzés. Az Eclipse-nek, hmm... vannak furcsaságai... és együtt kell élned velük. Például, ha a hibakereső indításakor megjelenik a „Nincs forrás a „0x0”-hoz” üzenet, akkor hajtsa végre a Step parancsot (F5).
Ahelyett, hogy egy következtetés
A natív kód emulálása nagyon érdekes dolog. Lehetővé válik az eszközfejlesztő számára, hogy valódi eszköz nélkül is hibakeresse a firmware-t. Egy kutató számára ez egy lehetőség dinamikus kódelemzés elvégzésére, ami még egy eszközzel sem mindig lehetséges.
Olyan eszközt szeretnénk biztosítani a szakembereknek, amely kényelmes, közepesen egyszerű, beállítása és működtetése nem igényel sok erőfeszítést és időt.
Írja meg a megjegyzésekben a hardveres emulátorokkal kapcsolatos tapasztalatait. Megbeszéljük Önt, és szívesen válaszolunk a kérdésekre.
A felmérésben csak regisztrált felhasználók vehetnek részt.
Mire használod az emulátort?
-
Firmware-t fejlesztek (debug).
-
Firmware-t kutatok
-
játékokat indítok (Dendi, Sega, PSP)
-
valami más (írd meg kommentben)
7 felhasználó szavazott. 2 felhasználó tartózkodott.
Milyen szoftvert használ a natív kód emulálására?
-
QEMU
-
Unikornis motor
-
Proteus
-
valami más (írd meg kommentben)
6 felhasználó szavazott. 2 felhasználó tartózkodott.
Mit szeretnél javítani a használt emulátoron?
-
sebességet akarok
-
Könnyebb beállítást/indítást szeretnék
-
Több lehetőséget szeretnék az emulátorral való interakcióhoz (API, hookok)
-
Mindennek örülök
-
valami más (írd meg kommentben)
8 felhasználó szavazott. 1 felhasználó tartózkodott.
Forrás: will.com