جدول المحتويات:

استخدم machine.net إذا كنت ترغب في إنشاء جهاز افتراضي مستقل عن النظام الأساسي مع فقط C#.
في الوقت الحالي ، يتم دعم X64 فقط ، بالإضافة إلى مجموعة شرائح Intel 8253 و 8259. أنا أركز حاليًا على حل المشكلات الحالية مع محاكي X64 بدلاً من إضافة المزيد من الاحتمالات. سيتم إضافة ميزات جديدة بمجرد بدء محاكي X64 في إظهار النجاح ، ومع ذلك ، لا يزال بإمكانك اقتراح ميزات جديدة إذا كنت ترغب في ذلك.
يرجى دعم المشروع عن طريق وضع نجم على هذا المستودع.
عند استخدام machine.net ، يتم تثبيت حزمة nuget التي تسمى ICED أيضًا. هذه حزمة شهيرة لفك تشفير التعليمات والترميز ، ويتم استخدامها في machine.net لفك تشفير التعليمات.
للبدء ، استخدم iced.intel لتجميع التعليمات التي نحتاجها. في حالتنا ، هو:
mov rcx, 150
rep add rax, 4
سيكون ذلك:
using Iced . Intel ;
using Machine . X64 . Runtime ;
using static Iced . Intel . AssemblerRegisters ;
var assembler = new Assembler ( 64 ) ;
assembler . mov ( rcx , 150 ) ;
assembler . rep . add ( rax , 4 ) ;
var stream = new MemoryStream ( ) ;
var streamCodeWriter = new StreamCodeWriter ( stream ) ;
assembler . Assemble ( streamCodeWriter , rip : 0uL ) ;
stream . Position = 0 ;
var reader = new StreamCodeReader ( stream ) ;
var decoder = Decoder . Create ( 64 , reader ) ;
decoder . IP = 0 ;
var instrs = new List < Instruction > ( ) ;
while ( stream . Position < stream . Length )
{
decoder . Decode ( out var instr ) ;
instrs . Add ( instr ) ;
}والآن ، ما عليك سوى إنشاء مثيل جديد لفئة Cpuruntime. يمكنك تمرير كمية الذاكرة (بالبايت) وعدد منافذ الإدخال/الإخراج. في حالتنا ، هذا هو ذاكرة 64 كيلو بايت و 8 منافذ I/O:
var runtime = new CpuRuntime ( memorySize : 65536 , ioPortCount : 8 ) ; يمكنك استدعاء طريقة .Run(in Instruction) على CpuRuntime لاستدعاء التعليمات. دعونا ندعو جميع التعليمات:
foreach ( var instr in instrs )
{
runtime . Run ( in instr ) ;
} يمكن أن تكون طريقة .Run محدودة قليلاً في حال كنت ترغب في دعم تعليمات القفز والفرع. في هذه الحالة ، من الممكن تحميل رمز bytecode المباشر في ذاكرة الوصول العشوائي وتحميله من هناك:
var cpu = new CpuRuntime ( ioPortCount : 8 ) ;
ulong x = 0uL ;
cpu . IOPorts [ 1 ] = new InputOutputPort (
read : ( ) =>
{
return 1234uL ;
} ,
write : ( value ) =>
{
x = value ;
} ) ;
byte [ ] code = CodeGen . MakeBranchTestCode_1 ( ) ;
cpu . LoadProgram ( code , 0uL ) ;
cpu . ProcessorRegisters . Cs = 0 ;
cpu . ProcessorRegisters . Rip = 0 ;
cpu . Use8086Compatibility ( ) ;
cpu . SetRsp ( 0x400uL ) ;
try
{
cpu . RunUntilNotBusy ( 35 ) ;
}
catch ( ArithmeticException )
{
throw new InvalidOperationException ( cpu . LastOrExecutingInstruction . Code . ToString ( ) ) ;
}
Assert . Equal ( 42uL , x ) ;
static class CodeGen
{
public static byte [ ] MakeBranchTestCode_1 ( )
{
var assembler = new Assembler ( 64 ) ;
Label lblA = assembler . CreateLabel ( "A" ) ;
Label lblC = assembler . CreateLabel ( "C" ) ;
Label lblB = assembler . CreateLabel ( "B" ) ;
assembler . Label ( ref lblA ) ;
assembler . mov ( ax , 42 ) ;
assembler . @out ( 1 , ax ) ;
assembler . call ( lblB ) ;
assembler . Label ( ref lblC ) ;
assembler . mov ( ax , bx ) ;
assembler . @out ( 1 , ax ) ;
assembler . hlt ( ) ;
assembler . Label ( ref lblB ) ;
assembler . mov ( bx , ax ) ;
assembler . call ( lblC ) ;
return Assemble ( assembler ) ;
}
private static byte [ ] Assemble ( Assembler assembler )
{
using var memoryStream = new MemoryStream ( ) ;
assembler . Assemble ( new StreamCodeWriter ( memoryStream ) , 0uL ) ;
return memoryStream . ToArray ( ) ;
}
}في الواقع ، X هي 42. تبدأ طريقة .rununtilnotbusy في تشغيل التعليمات من الذاكرة في CS: RIP أو RIP بشكل افتراضي. يحتوي على اثنين من الأحمال الزائدة: واحد يأخذ int والآخر لا. المعلومات التي تمثل الحد الأقصى لمقدار التعليمات التي يجب تشغيلها ، وهو أكثر أمانًا للاستخدام إذا كنت تقلق في حالة حلقة لا حصر لها. الشخص الذي لا يأخذ أي معلمات سيستمر في التشغيل حتى تعليمات HLT.
يمكنك أيضًا الوصول إلى خاصية .ProcessorRegisters لفئة CpuRuntime لفحص سجلات وعلامات وحدة المعالجة المركزية وحتى تعديلها في أي وقت. لرؤية النتيجة ، سنشاهد سجل rax :
Console . WriteLine ( runtime . ProcessorRegisters . Rax ) ;هذا ينتج عنه 600 ، وهو صحيح.
لإرفاق الأجهزة الخارجية ، يمكنك إنشاء منفذ I/O الخاص بك ووضع ما تريد في عمليات القراءة/الكتابة (نعم ، حتى إنشاء نافذة جديدة وعرضه ، إذا كنت تريد).
var cpu = new CpuRuntime ( ioPortCount : 8 ) ;
ulong x = 42uL ;
cpu . IOPorts [ 1 ] = new InputOutputPort (
read : ( ) =>
{
return 1234uL ;
} ,
write : ( value ) =>
{
x = value ;
} ) ;على سبيل المثال ، إذا قمنا بتنفيذ الكود التالي على وحدة المعالجة المركزية المحاكاة:
mov eax , 7777
out 1 , eax
in eax , 1بعد ذلك ، يمكنك أن ترى أن وحدة المعالجة المركزية أرسلت 7777 إلى منفذ I/O المنفول 1 (يتم فهرسة منافذ I/O بدءًا من 0) ، و EAX تساوي 1234 (تحقق من اختبار الوحدة هذا ، إنه رائع جدًا):
Assert . Equal ( 7777uL , x ) ;
Assert . Equal ( 1234uL , cpu . ProcessorRegisters . Eax ) ;
// No failuresلبناء machine.net ، تحتاج إلى تثبيت .NET 8.0. يمكنك تنزيله من موقع .NET الرسمي.
إذا كنت تفضل مع Visual Studio:
إذا كنت تفضل مع .NET CLI:
dotnet build . أو اكتب dotnet build -c Release للبناء في وضع الإصدار (على سبيل المثال ، إذا كنت ترغب في استخدام machine.net في تطبيقات العالم الحقيقي مع تمكين التحسين).| اسم المكتبة | NUGET URL | رمز المصدر على هذا الريبو |
|---|---|---|
| machine.x64.component.registers | انقر لإعادة التوجيه إلى المصدر |
رخصة معهد ماساتشوستس للتكنولوجيا. حقوق الطبع والنشر (C) Winscripter ، 2023-2024.