SPICE86 adalah alat untuk mengeksekusi, merekayasa terbalik, dan menulis ulang program DOS Mode Nyata yang kode sumbernya tidak tersedia.
Rilis tersedia di Nuget.
Pra-rilis juga tersedia di halaman rilis
Catatan: Ini adalah port, dan kelanjutan dari Java Spice86 asli.
Ini membutuhkan .NET 9 dan berjalan pada Windows, MacOS, dan Linux.
Menulis ulang program dari hanya biner adalah tugas yang sulit.
SPICE86 adalah alat yang membantu Anda melakukannya dengan pendekatan metodis dan menaklukkan.
Proses Umum:
Ini adalah program .net, Anda menjalankannya dengan baris perintah reguler atau run dotnet. Contoh dengan menjalankan program yang disebut File.exe:
Spice86 -e file.exe
File com dan file BIOS juga didukung.
Disarankan untuk mengatur variabel lingkungan SPICE86_DUMPS_FOLDER yang menunjuk ke tempat emulator harus membuang data runtime. Jika variabel diatur atau jika --cordedDatadirectory parameter dilewati, emulator akan membuang banyak informasi tentang menjalankan di sana. Jika tidak ada yang ditetapkan, data akan dibuang di direktori saat ini. Jika sudah ada data di sana, emulator akan memuatnya terlebih dahulu dan menyelesaikannya, Anda tidak perlu memulai dari nol setiap kali!
--Ems (Default: false) Enables EMS memory. EMS adds 8 MB of memory accessible to DOS programs through the EMM Page Frame.
--A20Gate (Default: false) Disables the 20th address line to support programs relying on the rollover of memory addresses above the HMA (slightly above 1 MB).
-m, --Mt32RomsPath Zip file or directory containing the MT-32 ROM files
-c, --CDrive Path to C drive, default is exe parent
-r, --RecordedDataDirectory Directory to dump data to when not specified otherwise. Working directory if blank
-e, --Exe Required. Path to executable
-a, --ExeArgs List of parameters to give to the emulated program
-x, --ExpectedChecksum Hexadecimal string representing the expected SHA256 checksum of the emulated program
-f, --FailOnUnhandledPort (Default: false) If true, will fail when encountering an unhandled IO port. Useful to check for unimplemented hardware. false by default.
-g, --GdbPort gdb port, if empty gdb server will not be created. If not empty, application will pause until gdb connects
-o, --OverrideSupplierClassName Name of a class that will generate the initial function information. See documentation for more information.
-p, --ProgramEntryPointSegment (Default: 4096) Segment where to load the program. DOS PSP and MCB will be created before it.
-u, --UseCodeOverride (Default: true) <true or false> if false it will use the names provided by overrideSupplierClassName but not the code
-i, --InstructionsPerSecond <number of instructions that have to be executed by the emulator to consider a second passed> if blank will use time based timer.
-t, --TimeMultiplier (Default: 1) <time multiplier> if >1 will go faster, if <1 will go slower.
-d, --DumpDataOnExit (Default: true) When true, records data at runtime and dumps them at exit time
-h, --HeadlessMode (Default: false) Headless mode. If true, no GUI is shown.
-l, --VerboseLogs (Default: false) Enable verbose level logs
-w, --WarningLogs (Default: false) Enable warning level logs
-s, --SilencedLogs (Default: false) Disable all logs
-i, --InitializeDOS (Default: true) Install DOS interrupt vectors or not.
--StructureFile Path to a C header file that describes the structures in the application. Works best with exports from IDA or Ghidra
--help Display this help screen.
--version Display version information.
SPICE86 berbicara tentang protokol jarak jauh GDB:
Anda perlu menentukan port untuk server GDB untuk memulai saat meluncurkan SPICE86:
Spice86 --GdbPort=10000
Spice86 akan menunggu GDB untuk terhubung sebelum memulai eksekusi sehingga Anda dapat mengatur breakpoint dan sebagainya.
Berikut adalah cara terhubung dari klien baris perintah GDB dan cara mengatur arsitektur:
(gdb) target remote localhost:10000
(gdb) set architecture i8086
Anda dapat menambahkan breakpoint, langkah, melihat memori dan sebagainya.
Contoh dengan breakpoint pada VGA VRAM menulis:
(gdb) watch *0xA0000
Majelis Melihat:
(gdb) layout asm
Menghapus breakpoint:
(gdb) remove 1
Mencari urutan byte dalam memori (alamat mulai 0, panjang f0000, ASCII byte dari string 'spice86'):
(gdb) find /b 0x0, 0xF0000, 0x53, 0x70, 0x69, 0x63, 0x65, 0x38, 0x36
GDB tidak mendukung X86 Real Mode Segmented Addressing, sehingga pointer perlu merujuk ke alamat fisik yang sebenarnya dalam memori. VRAM di Alamat A000: 0000 akan menjadi 0xA0000 di GDB.
Demikian pula, variabel $ PC di GDB akan diekspos oleh SPICE86 karena alamat fisik yang ditunjuk oleh CS: IP.
Daftar perintah khusus dapat ditampilkan seperti ini:
(gdb) monitor help
(gdb) monitor dumpall
Membuang semua yang dijelaskan di bawah dalam satu tembakan. File dibuat di folder dump seperti yang dijelaskan di sini beberapa file diproduksi:
Break setelah x siklus CPU yang ditiru:
(gdb) monitor breakCycles 1000
Istirahat di akhir program yang ditiru:
(gdb) monitor breakStop
Layar atau buffer #refreshing saat debugging
(gdb) monitor vbuffer refresh
Untuk pengalaman yang menyenangkan dan produktif dengan GDB, klien SeerGDB sangat dianjurkan.
Contoh konkret dengan Cryo Dune di sini.
Pertama -tama jalankan program Anda dan pastikan semuanya berfungsi dengan baik di SPICE86. Jika Anda mengalami masalah, itu bisa karena fitur perangkat keras / DOS / BIOS yang tidak diimplementasikan.
Saat Spice86 keluar, ia harus membuang data di folder saat ini atau di folder yang ditentukan oleh variabel Env
Buka data di Ghidra dengan Spice86-ghidra-plugin dan menghasilkan kode. Anda dapat mengimpor file yang dihasilkan dalam proyek templat yang Anda hasilkan melalui spice86-dotnet-templat:
dotnet new spice86.project
Anda dapat memberikan kode C# Anda sendiri untuk mengganti kode perakitan asli program.
SPICE86 dapat mengambil input contoh spice86.core.emulator.function.ioverridesupplier yang membangun pemetaan antara alamat memori fungsi dan overrides C# mereka.
Sebagai contoh lengkap, Anda dapat memeriksa kode sumber cryogenic.
Berikut ini adalah contoh sederhana bagaimana rasanya:
namespace My . Program ;
// This class is responsible for providing the overrides to spice86.
// There is only one per program you reimplement.
public class MyProgramOverrideSupplier : IOverrideSupplier {
public IDictionary < SegmentedAddress , FunctionInformation > GenerateFunctionInformations ( int programStartSegment ,
Machine machine ) {
Dictionary < SegmentedAddress , FunctionInformation > res = new ( ) ;
// In more complex examples, overrides may call each other
new MyOverrides ( res , programStartSegment , machine ) ;
return res ;
}
public override string ToString ( ) {
return "Overrides My program exe. class is " + GetType ( ) . FullName ;
}
}
// This class contains the actual overrides. As the project grows, you will probably need to split the reverse engineered code in several classes.
public class MyOverrides : CSharpOverrideHelper {
private MyOverridesGlobalsOnDs globalsOnDs ;
public MyOverrides ( IDictionary < SegmentedAddress , FunctionInformation > functionInformations , int segment , Machine machine ) {
// "myOverides" is a prefix that will be appended to all the function names defined in this class
base ( functionInformations , "myOverides" , machine ) ;
globalsOnDs = new MyOverridesGlobalsOnDs ( machine ) ;
// incUnknown47A8_0x1ED_0xA1E8_0xC0B8 will get executed instead of the assembly code when a call to 1ED:A1E8 is performed.
// Also when dumping functions, the name myOverides.incUnknown47A8 or instead of unknown
// Note: the segment is provided in parameter as spice86 can load executables in different places depending on the configuration
DefineFunction ( segment , 0xA1E8 , "incDialogueCount47A8" , IncDialogueCount47A8_0x1ED_0xA1E8_0xC0B8 ) ;
DefineFunction ( segment , 0x0100 , "addOneToAX" , AddOneToAX_0x1ED_0x100_0x1FD0 ) ;
}
public Action IncDialogueCount47A8_0x1ED_0xA1E8_0xC0B8 ( ) {
// Accessing the memory via accessors
globalsOnDs . SetDialogueCount47A8 ( globalsOnDs . GetDialogueCount47A8 ( ) + 1 ) ;
// Depends on the actual return instruction performed by the function, needed to be called from the emulated code as
// some programs like to mess with the stack ...
return NearRet ( ) ;
}
private Action AddOneToAX_0x1ED_0x100_0x1FD0 ( ) {
// Assembly for this would be
// INC AX
// RETF
// Note that you can access the whole emulator to change the state in the overrides.
state . AX ++ ;
return NearRet ( ) ;
}
}
// Memory accesses can be encapsulated into classes like this to give names to addresses and make the code shorter.
public class MyOverridesGlobalsOnDs : MemoryBasedDataStructureWithDsBaseAddress {
public DialoguesGlobalsOnDs ( Machine machine ) {
base ( machine ) ;
}
public void SetDialogueCount47A8 ( int value ) {
this . SetUint8 ( 0x47A8 , value ) ;
}
public int GetDialogueCount47A8 ( ) {
return this . GetUint8 ( 0x47A8 ) ;
}
}Ingat : Anda harus memberi tahu SPICE86 untuk menggunakan overrides kode perakitan Anda dengan argumen baris perintah "--useCodeoverride true" saat men-debug proyek Anda.
Seiring dengan jalur wajib ke program DOS Anda, disahkan dengan argumen -Exepath.
Spice86 hadir dengan debugger bawaan yang dapat digunakan untuk men-debug program yang ditiru. Ini adalah debugger sederhana yang memungkinkan Anda untuk memeriksa memori, pembongkaran, register, dan tumpukan.
Penampil struktur memungkinkan Anda untuk memeriksa memori dengan cara yang terstruktur. Berguna untuk memeriksa memori sebagai struktur, seperti PSP DOS, DOS MCB, register VGA, dll.
Pertama, Anda memerlukan file header C yang menjelaskan struktur dalam aplikasi. Anda dapat menghasilkan satu dengan Ghidra atau Ida. Kemudian Anda dapat memuatnya dengan argumen Commandline --StructureFile . Ini akan memungkinkan tombol "Struktur Tampilan" di tab Memori debugger.
Di sana Anda memasukkan Segmen: Alamat offset dan memilih struktur yang ingin Anda lihat. Struktur akan ditampilkan dalam tampilan pohon dan memori dalam tampilan hex.
Tampilan memperbarui setiap kali aplikasi dijeda, sehingga Anda dapat melangkah melalui program dan melihat bagaimana struktur berubah. Mengekspor file header C baru dari Ghidra atau IDA juga akan memperbarui penampil struktur dengan informasi baru waktu nyata.
Anda juga dapat memasuki tampilan struktur dengan memilih berbagai byte di tab memori dan mengklik kanan di atasnya.
Dimungkinkan untuk menyediakan C: Drive untuk fungsi DOS yang ditiru dengan opsi -Cdrive . Default adalah folder saat ini. Untuk beberapa game, Anda mungkin perlu mengatur drive C ke folder game.
Anda dapat lulus argumen (maks 127 chars!) Ke program yang ditiru dengan opsi -exeargs . Default kosong.
Perangkat keras pengatur waktu yang ditiru dari PC (Intel 8259) mendukung waktu pengukuran dari baik:
Layar disegarkan 30 kali per detik dan setiap kali tunggu retrace VGA terdeteksi (lihat renderer.cs).
CPU:
Ingatan:
Grafik:
DOS:
Input:
CD-ROM:
Suara:
Pada *sistem NIX, Anda harus menginstal LibportAudio. Tanpa itu, tidak akan ada suara.
Daftar kompatibilitas tersedia di sini.
dotnet build Spice86 -e < path to executable >Atau gunakan ini di mana spice86.csproj berada:
dotnet run -e < path to executable >Ini menggunakan Ghidra dan Java 17.
Sebelum menggunakannya, tentukan variabel lingkungan bernama SPICE86_DUMPS_FOLDER yang menunjuk ke folder tempat SPICE86 dibuang berada. Mereka dihasilkan saat keluar.
Prosedur Umum, Sesuai Rangka:
1. Skrip sendiri 'Importsymbolscript.py' (input yang digunakan adalah "spice86dumpghidrasymbols.txt")
2.Analisis otomatis GHIDRA (hanya mengaktifkan 'Poin Masuk Barang')
3. Sekarang, Anda dapat menggunakan plugin.
Ingat: Jika Ghidra menampilkan subrutin, gunakan tombol 'F' untuk mengubahnya menjadi fungsi. Generator kode hanya berfungsi dengan fungsi.
Juga, jika Anda memiliki perilaku aneh, pastikan Anda memiliki Java 17 dan hanya Java 17. Begitulah Ghidra menyukainya.
Cryo Dune:
UI ditenagai oleh Avalonia UI.
