목차 :

C#만으로 플랫폼 독립적 인 가상 머신을 만들려면 machine.net을 사용하십시오.
현재 X64 만 지원되고 Intel 8253, 8259 칩셋 및 HPET이 지원됩니다. 현재 저는 더 많은 가능성을 추가하기보다는 X64 에뮬레이터의 기존 문제를 해결하는 데 중점을 둡니다. X64 에뮬레이터가 성공하기 시작하면 새로운 기능이 추가되지만 원하는 경우 새로운 기능을 제안 할 수 있습니다.
이 저장소에 별을 배치하여 프로젝트를 지원하십시오.
machine.net을 사용하면 Iced라는 Nuget 패키지도 설치됩니다. 이것은 지침을 디코딩 및 인코딩하는 데 인기있는 패키지이며 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 ) ; CpuRuntime 에서 .Run(in Instruction) 메소드를 호출하여 명령어를 호출 할 수 있습니다. 모든 지침을 발산합시다.
foreach ( var instr in instrs )
{
runtime . Run ( in instr ) ;
} 점프 및 분기 지침을 지원하려는 경우 .Run 메소드가 약간 제한 될 수 있습니다. 이 경우 직접 바이트 코드를 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 명령까지 계속 실행됩니다.
CpuRuntime 클래스의 .ProcessorRegisters 속성에 액세스하여 CPU 레지스터 및 플래그를 검사하고 언제든지 수정할 수도 있습니다. 결과를 보려면 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 ;
} ) ;예를 들어, 에뮬레이션 된 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 failuresmachine.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.