Add files via upload

This commit is contained in:
Adam - Zyph. Tech.
2026-06-17 19:01:00 +02:00
committed by GitHub
parent e56a256c7b
commit d5c520e888
14 changed files with 517 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X86Emulator
{
internal class EmulatorException : Exception
{
public EmulatorException()
{
}
public EmulatorException(string message)
: base(message)
{
}
public EmulatorException(string message, Exception inner)
: base(message, inner)
{
}
}
}
+24
View File
@@ -0,0 +1,24 @@
using SharpDisasm;
using System.Collections.Generic;
namespace X86Emulator.Instructions
{
internal class Add : Instruction
{
private Operand[] Operands;
public Add(Operand[] operands)
{
Operands = operands;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
int src = ReadOperand(Operands[1], registers);
int dst = ReadOperand(Operands[0], registers);
int result = dst + src;
registers.SetValue(Operands[0].Base, result);
}
}
}
+45
View File
@@ -0,0 +1,45 @@
using SharpDisasm;
using System.Collections.Generic;
namespace X86Emulator.Instructions
{
internal class IMul : Instruction
{
private Operand[] Operands;
public IMul(Operand[] operands)
{
Operands = operands;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
if (Operands.Length == 1)
{
long eax = registers.GetValue(Registers.Register.EAX);
long val = ReadOperand(Operands[0], registers);
long result = eax * val;
registers.SetValue(SharpDisasm.Udis86.ud_type.UD_R_EDX, (int)(result >> 32));
registers.SetValue(SharpDisasm.Udis86.ud_type.UD_R_EAX, (int)result);
}
else if (Operands.Length == 2)
{
int dst = registers.GetValue(Operands[0].Base);
int src = ReadOperand(Operands[1], registers);
int result = dst * src;
registers.SetValue(Operands[0].Base, result);
}
else
{
int val0 = ReadOperand(Operands[1], registers);
int val1 = ReadOperand(Operands[2], registers);
int result = val0 * val1;
registers.SetValue(Operands[0].Base, result);
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using SharpDisasm;
using SharpDisasm.Udis86;
namespace X86Emulator.Instructions
{
internal abstract class Instruction
{
public abstract void Emulate(Stack<int> stack, Registers registers);
protected static int ReadOperand(Operand operand, Registers registers)
{
return operand.Type == ud_type.UD_OP_REG
? registers.GetValue(operand.Base)
: operand.LvalSDWord;
}
}
}
+23
View File
@@ -0,0 +1,23 @@
using SharpDisasm;
using System.Collections.Generic;
namespace X86Emulator.Instructions
{
internal class Mov : Instruction
{
private Operand[] Operands;
public Mov(Operand[] operands)
{
Operands = operands;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
Operand to = Operands[0];
int val = ReadOperand(Operands[1], registers);
registers.SetValue(to.Base, val);
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using SharpDisasm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X86Emulator.Instructions
{
internal class Neg : Instruction
{
private Operand Operand;
public Neg(Operand operand)
{
Operand = operand;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
int val = registers.GetValue(Operand.Base);
val = -val;
registers.SetValue(Operand.Base, val);
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using SharpDisasm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X86Emulator.Instructions
{
internal class Not : Instruction
{
private Operand Operand;
public Not(Operand operand)
{
Operand = operand;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
int val = registers.GetValue(Operand.Base);
val = ~val;
registers.SetValue(Operand.Base, val);
}
}
}
+29
View File
@@ -0,0 +1,29 @@
using SharpDisasm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X86Emulator.Instructions
{
internal class Pop : Instruction
{
private Operand? Operand = null;
public Pop(Operand? operand)
{
Operand = operand;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
int val = stack.Pop();
if (Operand != null)
{
registers.SetValue(Operand.Base, val);
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
using SharpDisasm;
using System.Collections.Generic;
namespace X86Emulator.Instructions
{
internal class Push : Instruction
{
private Operand Operand;
public Push(Operand operand)
{
Operand = operand;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
stack.Push(ReadOperand(Operand, registers));
}
}
}
+24
View File
@@ -0,0 +1,24 @@
using SharpDisasm;
using System.Collections.Generic;
namespace X86Emulator.Instructions
{
internal class Sub : Instruction
{
private Operand[] Operands;
public Sub(Operand[] operands)
{
Operands = operands;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
int src = ReadOperand(Operands[1], registers);
int dst = ReadOperand(Operands[0], registers);
int result = dst - src;
registers.SetValue(Operands[0].Base, result);
}
}
}
+24
View File
@@ -0,0 +1,24 @@
using SharpDisasm;
using System.Collections.Generic;
namespace X86Emulator.Instructions
{
internal class Xor : Instruction
{
private Operand[] Operands;
public Xor(Operand[] operands)
{
Operands = operands;
}
public override void Emulate(Stack<int> stack, Registers registers)
{
int dst = registers.GetValue(Operands[0].Base);
int src = ReadOperand(Operands[1], registers);
int result = src ^ dst;
registers.SetValue(Operands[0].Base, result);
}
}
}
+83
View File
@@ -0,0 +1,83 @@
using SharpDisasm.Udis86;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace X86Emulator
{
internal class Registers
{
public int[] Values = new int[(int)Register.MAX];
public int GetValue(Register register)
{
return Values[(int)register];
}
public int GetValue(ud_type register)
{
return register switch
{
ud_type.UD_R_EAX => Values[(int)Register.EAX],
ud_type.UD_R_ECX => Values[(int)Register.ECX],
ud_type.UD_R_EDX => Values[(int)Register.EDX],
ud_type.UD_R_EBX => Values[(int)Register.EBX],
ud_type.UD_R_ESP => Values[(int)Register.ESP],
ud_type.UD_R_EBP => Values[(int)Register.EBP],
ud_type.UD_R_ESI => Values[(int)Register.ESI],
ud_type.UD_R_EDI => Values[(int)Register.EDI],
_ => throw new EmulatorException($"Unhandled register type {register}"),
};
}
public void SetValue(ud_type register, int value)
{
switch (register)
{
case ud_type.UD_R_EAX:
Values[(int)Register.EAX] = value;
break;
case ud_type.UD_R_ECX:
Values[(int)Register.ECX] = value;
break;
case ud_type.UD_R_EDX:
Values[(int)Register.EDX] = value;
break;
case ud_type.UD_R_EBX:
Values[(int)Register.EBX] = value;
break;
case ud_type.UD_R_ESP:
Values[(int)Register.ESP] = value;
break;
case ud_type.UD_R_EBP:
Values[(int)Register.EBP] = value;
break;
case ud_type.UD_R_ESI:
Values[(int)Register.ESI] = value;
break;
case ud_type.UD_R_EDI:
Values[(int)Register.EDI] = value;
break;
default:
throw new EmulatorException($"Unhandled register type {register}");
};
}
public enum Register
{
EAX,
ECX,
EDX,
EBX,
ESP,
EBP,
ESI,
EDI,
MAX
}
}
}
+13
View File
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="dnlib" Version="4.4.0" />
<PackageReference Include="SharpDisasm" Version="1.1.11" />
</ItemGroup>
</Project>
+133
View File
@@ -0,0 +1,133 @@
using dnlib.DotNet;
using dnlib.PE;
using System.Collections.Generic;
using X86Emulator.Instructions;
using System.Linq;
using SharpDisasm.Udis86;
namespace X86Emulator
{
public class X86Method
{
private ModuleDefMD Module;
private MethodDef Method;
private List<Instruction> Instructions = new();
private int NumArgs = 0;
public X86Method(ModuleDefMD module, MethodDef method)
{
Module = module;
Method = method;
Initialize();
}
public int Emulate(int[] args)
{
if (args.Length != NumArgs)
{
throw new EmulatorException($"Incorrect number of args passed to method. Expected {NumArgs} got {args.Length}");
}
var stack = new Stack<int>();
var registers = new Registers();
if (args.Length == 1)
{
registers.SetValue(ud_type.UD_R_ECX, args[0]);
stack.Push(args[0]);
}
else if (args.Length > 1)
{
registers.SetValue(ud_type.UD_R_ECX, args[0]);
registers.SetValue(ud_type.UD_R_EDX, args[1]);
foreach (var arg in args.Skip(2))
{
stack.Push(arg);
}
}
foreach (var instr in Instructions)
{
instr.Emulate(stack, registers);
}
return registers.GetValue(Registers.Register.EAX);
}
private void Initialize()
{
NumArgs = Method.Parameters.Count;
var body = ReadNativeBody();
var disassembler = new SharpDisasm.Disassembler(body, SharpDisasm.ArchitectureMode.x86_32);
var instructions = disassembler.Disassemble();
foreach (var instr in instructions)
{
if (instr.Mnemonic == ud_mnemonic_code.UD_Iret)
{
// Need to remove the final pops before a ret
Instructions.RemoveRange(Instructions.Count - 3, 3);
break;
}
switch (instr.Mnemonic)
{
case ud_mnemonic_code.UD_Ipush:
Instructions.Add(new Push(instr.Operands[0]));
break;
case ud_mnemonic_code.UD_Ipop:
Instructions.Add(new Pop(instr.Operands.Length == 0 ? null : instr.Operands[0]));
break;
case ud_mnemonic_code.UD_Imov:
Instructions.Add(new Mov(instr.Operands));
break;
case ud_mnemonic_code.UD_Inot:
Instructions.Add(new Not(instr.Operands[0]));
break;
case ud_mnemonic_code.UD_Iimul:
Instructions.Add(new IMul(instr.Operands));
break;
case ud_mnemonic_code.UD_Ineg:
Instructions.Add(new Neg(instr.Operands[0]));
break;
case ud_mnemonic_code.UD_Ixor:
Instructions.Add(new Xor(instr.Operands));
break;
case ud_mnemonic_code.UD_Isub:
Instructions.Add(new Sub(instr.Operands));
break;
case ud_mnemonic_code.UD_Iadd:
Instructions.Add(new Add(instr.Operands));
break;
case ud_mnemonic_code.UD_Inop:
// no-op ...
break;
default:
throw new EmulatorException($"Unhandled instruction type {instr.Mnemonic}");
}
}
}
private byte[] ReadNativeBody()
{
var reader = Module.Metadata.PEImage.CreateReader();
var fileOffset = Module.Metadata.PEImage.ToFileOffset(Method.NativeBody.RVA) + 0x14;
reader.Position = (uint)fileOffset;
Module.TablesStream.TryReadMethodRow(Method.Rid + 1, out var nextMethod);
var size = Module.Metadata.PEImage.ToFileOffset((RVA)nextMethod.RVA) - fileOffset;
var bytes = ((nextMethod.ImplFlags & (ushort)MethodImplAttributes.Native) != 0) ? new byte[size] : new byte[2048];
reader.ReadBytes(bytes, 0, bytes.Length);
return bytes;
}
}
}