Spice86 هي أداة لتنفيذ ، عكس الهندسة وإعادة كتابة برامج الوضع الحقيقي DOS والتي لا تتوفر رمز المصدر.
الإصدار متاح على Nuget.
تتوفر أيضًا إصدار ما قبل الإفراج على صفحة الإصدار
ملاحظة: هذا منفذ ، واستمرار من Java Spice86 الأصلي.
يتطلب .NET 9 ويعمل على Windows و MacOS و Linux.
إعادة كتابة برنامج من الثنائي فقط هي مهمة صعبة.
Spice86 هي أداة تساعدك على القيام بذلك من خلال مقاربة الفجوة المنهجية والقهر.
العملية العامة:
هذا هو برنامج .NET ، يمكنك تشغيله باستخدام سطر الأوامر العادي أو تشغيل dotnet. مثال مع تشغيل برنامج يسمى file.exe:
Spice86 -e file.exe
يتم دعم ملفات com وملفات BIOS أيضًا.
يوصى بتعيين SPICE86_DUMPS_FORDER البيئة المتغير الذي يشير إلى المكان الذي يجب على المحاكي تفريغ بيانات وقت التشغيل. إذا تم تعيين المتغير أو إذا تم تمرير معلمة RecordDatadirectory ، فسيقوم المحاكي بإلقاء مجموعة من المعلومات حول التشغيل هناك. إذا لم يتم تعيين أي شيء ، فسيتم إلقاء البيانات في الدليل الحالي. إذا كانت هناك بيانات بالفعل ، فسيقوم المحاكي بتحميلها أولاً وإكمالها ، فلن تحتاج إلى البدء من الصفر في كل مرة!
--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 يتحدث عن بروتوكول GDB عن بُعد:
تحتاج إلى تحديد منفذ لبدء خادم GDB عند بدء تشغيل Spice86:
Spice86 --GdbPort=10000
سينتظر Spice86 حتى يتصل GDB قبل بدء التنفيذ بحيث يمكنك إعداد نقاط التوقف وما إلى ذلك.
فيما يلي كيفية الاتصال من عميل سطر أوامر GDB وكيفية تعيين الهندسة المعمارية:
(gdb) target remote localhost:10000
(gdb) set architecture i8086
يمكنك إضافة نقاط التوقف ، الخطوة ، عرض الذاكرة وما إلى ذلك.
مثال مع نقطة توقف على VGA VRAM يكتب:
(gdb) watch *0xA0000
عرض التجميع:
(gdb) layout asm
إزالة نقطة توقف:
(gdb) remove 1
البحث عن سلسلة من البايتات في الذاكرة (عنوان بدء 0 ، الطول F0000 ، بايت ASCII لسلسلة "spice86"):
(gdb) find /b 0x0, 0xF0000, 0x53, 0x70, 0x69, 0x63, 0x65, 0x38, 0x36
لا يدعم GDB العنوان المقطوع في الوضع الحقيقي X86 ، لذلك تحتاج المؤشرات إلى الرجوع إلى العنوان الفعلي الفعلي في الذاكرة. VRAM في العنوان A000: 0000 سيكون 0xA0000 في GDB.
وبالمثل ، سيتم تعريض متغير $ pc في GDB بواسطة Spice86 حيث أن العنوان الفعلي يشير إليه CS: IP.
يمكن عرض قائمة الأوامر المخصصة مثل هذا:
(gdb) monitor help
(gdb) monitor dumpall
تفريغ كل ما هو موضح أدناه في طلقة واحدة. يتم إنشاء الملفات في مجلد Dump كما هو موضح هنا يتم إنتاج العديد من الملفات:
كسر بعد دورات وحدة المعالجة المركزية المحاكاة x:
(gdb) monitor breakCycles 1000
كسر في نهاية البرنامج المحاكاة:
(gdb) monitor breakStop
#شاشة أو مخازن مؤقتة أثناء تصحيح الأخطاء
(gdb) monitor vbuffer refresh
لتجربة ممتعة ومثمرة مع GDB ، يوصى بشدة عميل SEERGDB.
مثال ملموس مع الكثبان الرملية هنا.
قم بتشغيل برنامجك أولاً وتأكد من أن كل شيء يعمل بشكل جيد في Spice86. إذا واجهت مشكلات ، فقد يكون ذلك بسبب ميزات الأجهزة / DOS / BIOS التي لا تنفذ.
عندما يخرج Spice86 ، يجب أن يتفريغ البيانات في المجلد الحالي أو في المجلد المحدد بواسطة متغير ENV
افتح البيانات في Ghidra باستخدام spice86-ghidra-plugin وإنشاء رمز. يمكنك استيراد الملفات التي تم إنشاؤها في مشروع القالب الذي تنشئه عبر spice86-dotnet-templates:
dotnet new spice86.project
يمكنك توفير رمز C# الخاص بك لتجاوز رمز التجميع الأصلي للبرنامج.
يمكن أن يأخذ Spice86 إدخال مثيل لـ spice86.core.emulator.function.ioverridesupplier الذي يبني رسم خرائط بين عنوان ذاكرة الوظائف وتجاوزاتها C#.
للحصول على مثال كامل ، يمكنك التحقق من رمز المصدر للبرد.
فيما يلي مثال بسيط على كيفية ظهوره:
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 ) ;
}
}تذكر : يجب أن تخبر Spice86 باستخدام تجاوزات رمز التجميع الخاص بك مع وسيطة سطر الأوامر "-useCodeOverride True" عند تصحيح مشروعك.
جنبا إلى جنب مع المسار الإلزامي لبرنامج DOS الخاص بك ، تم تمريره مع حجة -exepath.
يأتي Spice86 مع مصحح تصحيح مدمج يمكن استخدامه لتصحيح البرنامج المحاكاة. إنه مصحح تصحيح بسيط يسمح لك بتفتيش الذاكرة ، والتفكيك ، والسجلات ، والمكدس.
يتيح لك عارض الهيكل فحص الذاكرة بطريقة منظمة. من المفيد فحص الذاكرة كهيكل ، مثل DOS PSP ، و DOS MCB ، وسجلات VGA ، إلخ.
أولاً ، تحتاج إلى ملف رأس C يصف الهياكل الموجودة في التطبيق. يمكنك توليد واحدة مع Ghidra أو IDA. ثم يمكنك تحميله باستخدام وسيطة سطر الأوامر --StructureFile . سيؤدي ذلك إلى تمكين زر "عرض الهيكل" في علامة تبويب الذاكرة في مصحح الأخطاء.
هناك إدخال جزء: عنوان الإزاحة واختر الهيكل الذي تريد عرضه. سيتم عرض الهيكل في عرض شجرة والذاكرة في عرض عرافة.
يتم تحديث الشاشة كلما تم إيقاف التطبيق ، بحيث يمكنك التنقل عبر البرنامج ومعرفة كيف يتغير الهيكل. سيؤدي تصدير ملف رأس C جديد من Ghidra أو IDA أيضًا إلى تحديث عارض الهيكل بالمعلومات الجديدة في الوقت الفعلي.
يمكنك أيضًا إدخال طريقة عرض الهيكل عن طريق تحديد مجموعة من البايتات في علامة التبويب الذاكرة والنقر بزر الماوس الأيمن عليها.
من الممكن توفير محرك C: Drive لوظائف DOS المحاكاة مع الخيار -cdrive . الافتراضي هو المجلد الحالي. بالنسبة لبعض الألعاب ، قد تحتاج إلى تعيين محرك C على مجلد اللعبة.
يمكنك تمرير الوسائط (بحد أقصى 127 chars!) إلى البرنامج المحاكاة مع الخيار -exeargs . الافتراضي فارغ.
يدعم أجهزة المؤقت المقطوع للكمبيوتر (Intel 8259) قياس الوقت من:
تم تحديث الشاشة 30 مرة في الثانية وفي كل مرة يتم اكتشاف انتظار Retrace VGA (انظر Ranterer.cs).
وحدة المعالجة المركزية:
ذاكرة:
الرسومات:
دوس:
مدخل:
القرص المضغوط:
صوت:
على *NIX Systems ، ستحتاج إلى تثبيت libportaudio. بدونها ، لن يكون هناك صوت.
قائمة التوافق المتاحة هنا.
dotnet build Spice86 -e < path to executable >أو استخدم هذا حيث يوجد spice86.csproj:
dotnet run -e < path to executable >هذا يستخدم Ghidra و Java 17.
قبل استخدامه ، حدد متغيرًا للبيئة يدعى spice86_dumps_folder يشير إلى مجلد حيث توجد مقالب Spice86. يتم إنشاؤها عند الخروج.
الإجراء العام ، بالترتيب:
1. البرنامج النصي الخاص بـ GHIDRA "OperSymbolscript.py" (المدخلات المستخدمة هي "Spice86Dumpghidrasymbols.txt")
2.GHIDRA'S AUTOALISE (تمكين فقط "نقاط دخول dissasemble")
3. الآن ، يمكنك استخدام البرنامج المساعد.
تذكر: إذا كان Ghidra يعرض الروتين الفرعي ، فاستخدم مفتاح "F" لتحويلها إلى وظائف. يعمل مولد الرمز فقط مع الوظائف.
أيضًا ، إذا كان لديك أي سلوك غريب ، فتأكد من أن لديك Java 17 و Java 17 فقط. هكذا يحبها Ghidra.
كرب الكثبان الرملية:
واجهة المستخدم مدعومة من Avalonia UI.
