namespace WhiteMagic.Assembly; /// /// The default backend. Hand-emits calling-convention /// trampolines and injection stubs using deterministic byte emitters /// (, , ). Has /// no native or third-party dependency — no FASM, no Iced. /// /// /// is not supported by this backend (it is a parse-free /// emitter, not a text assembler). Use (Phase 8) for /// arbitrary mnemonics. /// public class StubAssembler : IAssembler { /// public byte[] Assemble(string assemblyText, ulong origin = 0) { throw new NotSupportedException( "StubAssembler does not parse text assembly. " + "Use IcedAssembler (Phase 8) for arbitrary mnemonics."); } // ── Emit primitives ──────────────────────────────────────────────────── public void EmitU8(List buffer, byte value) => buffer.Add(value); public void EmitU32(List buffer, uint value) { buffer.Add((byte)value); buffer.Add((byte)(value >> 8)); buffer.Add((byte)(value >> 16)); buffer.Add((byte)(value >> 24)); } public void EmitU64(List buffer, ulong value) { EmitU32(buffer, (uint)value); EmitU32(buffer, (uint)(value >> 32)); } // ── Call-stub builders ───────────────────────────────────────────────── public byte[] BuildCallStub(IntPtr stubAddress, IntPtr targetAddress, uint[] arguments, int pointerSize, CallingConvention convention) { var buffer = new List(64); if (pointerSize == 4) BuildX86Stub(buffer, (uint)stubAddress, (uint)targetAddress, arguments, convention); else BuildX64Stub(buffer, (ulong)stubAddress, (ulong)targetAddress, arguments); return buffer.ToArray(); } private void BuildX86Stub(List buffer, uint stubAddr, uint target, uint[] args, CallingConvention convention) { uint current = stubAddr; switch (convention) { case CallingConvention.Thiscall when args.Length >= 1: buffer.Add(0xB9); // mov ecx, arg0 EmitU32(buffer, args[0]); current += 5; args = args[1..]; break; case CallingConvention.Fastcall: if (args.Length >= 1) { buffer.Add(0xB9); // mov ecx, arg0 EmitU32(buffer, args[0]); current += 5; args = args[1..]; } if (args.Length >= 1) { buffer.Add(0xBA); // mov edx, arg1 EmitU32(buffer, args[0]); current += 5; args = args[1..]; } break; } // Push remaining args in reverse order for (int i = args.Length - 1; i >= 0; i--) { buffer.Add(0x68); // push imm32 EmitU32(buffer, args[i]); current += 5; } // call rel32 uint rel32 = target - (current + 5); buffer.Add(0xE8); EmitU32(buffer, rel32); // Caller cleanup (cdecl only) if (convention == CallingConvention.Cdecl && args.Length > 0) { buffer.Add(0x83); // add esp, imm8 buffer.Add(0xC4); buffer.Add((byte)(args.Length * 4)); } buffer.Add(0xC3); // ret } private static void BuildX64Stub(List buffer, ulong stubAddr, ulong target, uint[] args) { throw new NotImplementedException("x64 stubs (task 3.7-3.8)"); } }