# Executing Native Code in .NET Without External DLLs

This example demonstrates how to execute native x86 machine code directly from .NET and, conversely, how to call a managed .NET method from that native code — all without loading any external libraries.

### How it works

The core idea is to allocate a chunk of memory, mark it as executable, and then point a delegate to it.

1. **Get a Pointer to Managed Code:** We use `MethodHandle.GetFunctionPointer()` to get the memory address of `Math.Max`.
    
2. **Allocate Executable Memory:** Standard .NET memory is not executable for security reasons (DEP). We use the Win32 API `VirtualAlloc` to request a region with `ExecuteReadWrite` permissions.
    
3. **Calculate Relative Offsets:** The x86 `CALL` instruction (0xE8) uses relative addressing. We calculate the distance between our native buffer and the target `Math.Max` function.
    
4. **Write Shellcode:** We manually write the x86 opcodes into the allocated memory.
    
5. **Execute via Delegate:** `Marshal.GetDelegateForFunctionPointer` wraps our raw memory address into a callable .NET delegate.
    

```csharp
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate ulong LongFunction(uint arg1, uint arg2);

    static void Main(string[] args)
    {
        // 1. Get the address of the managed method Math.Max(uint, uint)
        MethodInfo methodInfo = typeof(Math).GetMethod("Max",
            BindingFlags.Static | BindingFlags.Public, null,
            new Type[] { typeof(uint), typeof(uint) }, null);

        IntPtr functionPtr = methodInfo.MethodHandle.GetFunctionPointer();
        Console.WriteLine($"Math.Max address: 0x{functionPtr.ToInt64():X}");

        // 2. Allocate executable memory (x86 architecture)
        IntPtr region = VirtualAlloc(IntPtr.Zero, (UIntPtr)4096,
            AllocationType.Commit, MemoryProtection.ExecuteReadWrite);

        // 3. Calculate relative address for the 'call' instruction
        // The offset is: TargetAddress - (CurrentInstructionAddress + InstructionLength)
        // 14 is the offset from the start of our code to the instruction following 'call'
        uint relativeAddr = (uint)functionPtr - (uint)region - 14;
        byte[] addrBytes = BitConverter.GetBytes(relativeAddr);

        // 4. Prepare x86 machine code: f(x, y) = max(x, y) * max(x, y)
        byte[] code = new byte[]
        {
            0x55,                         // push ebp
            0x89, 0xE5,                   // mov ebp, esp
            0x8B, 0x4D, 0x08,             // mov ecx, [ebp + 8]  (arg1)
            0x8B, 0x55, 0x0C,             // mov edx, [ebp + 12] (arg2)
            0xE8, addrBytes[0], addrBytes[1], addrBytes[2], addrBytes[3], // call Math.Max
            0xF7, 0xE0,                   // mul eax (square the result of Max)
            0x5D,                         // pop ebp
            0xC3                          // ret
        };

        // 5. Copy code to allocated memory and create a delegate
        Marshal.Copy(code, 0, region, code.Length);
        Console.WriteLine($"Native code memory: 0x{region.ToInt64():X}");

        LongFunction func = (LongFunction)Marshal.GetDelegateForFunctionPointer(region, typeof(LongFunction));

        // 6. Execute the magic
        ulong result = func(0x10, 0x20); // max(16, 32)^2 = 32^2 = 1024
        Console.WriteLine($"Result: {result}");

        // Cleanup
        VirtualFree(region, UIntPtr.Zero, ReleaseType.Release);
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr VirtualAlloc(IntPtr address, UIntPtr length, AllocationType allocationType, MemoryProtection memoryProtection);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool VirtualFree(IntPtr address, UIntPtr length, ReleaseType releaseType);

    [Flags]
    public enum AllocationType : uint 
    { 
        Commit = 0x1000 
    }

    [Flags]
    public enum ReleaseType : uint 
    { 
        Release = 0x8000
    }

    [Flags]
    public enum MemoryProtection : uint 
    { 
        ExecuteReadWrite = 0x40 
    }
}
```

### Important Considerations

* **Architecture:** This specific shellcode is **x86 (32-bit)**. It will crash on x64 due to different calling conventions and register sizes (e.g., `RAX` instead of `EAX`).
    
* **JIT Inlining:** In a real-world scenario, the JIT compiler might inline `Math.Max`, or its function pointer might change. For a stable "managed-to-native" bridge, you usually use `RuntimeHelpers.PrepareMethod`.
    
* **Security:** Writing to executable memory is a common technique in exploits. Modern systems with **Arbitrary Code Execution (ACE)** protection might block such operations.
