สารบัญ:

ใช้ machine.net หากคุณต้องการสร้างเครื่องเสมือนที่ไม่ขึ้นกับแพลตฟอร์มด้วย Just C#
ตอนนี้รองรับ X64 เท่านั้นเช่นเดียวกับ Intel 8253, 8259 ชิปเซ็ตและ HPET ขณะนี้ฉันมุ่งเน้นไปที่การแก้ปัญหาที่มีอยู่กับตัวจำลอง X64 แทนที่จะเพิ่มความเป็นไปได้มากขึ้น คุณสมบัติใหม่จะถูกเพิ่มเมื่อ Emulator 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 คุณสามารถผ่านจำนวนหน่วยความจำ (เป็นไบต์) และจำนวนพอร์ต I/O ในกรณีของเรานี่คือหน่วยความจำ 64KB และ 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 โดยตรงลงใน RAM และโหลดจากที่นั่น:
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 เพื่อตรวจสอบการลงทะเบียน CPU และธงและแม้แต่แก้ไขได้ตลอดเวลา หากต้องการดูผลลัพธ์เราจะดู rax register:
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 ;
} ) ;ตัวอย่างเช่นหากเราเรียกใช้รหัสต่อไปนี้ใน CPU ที่เลียนแบบ:
mov eax , 7777
out 1 , eax
in eax , 1จากนั้นคุณจะเห็นได้ว่า CPU ส่ง 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 | ซอร์สโค้ดใน repo นี้ |
|---|---|---|
| machine.x64.Component.REGISTERS | คลิกเพื่อเปลี่ยนเส้นทางไปยังแหล่งที่มา |
ใบอนุญาต MIT ลิขสิทธิ์ (c) Winscripter, 2023-2024