Yönetilen bir .NET dilinde tamamen bir JIT derleyicisi (yerel koda) yazmak mümkün mü


84

Bir JIT derleyicisi yazma fikri ile oynuyorum ve sadece her şeyi yönetilen kodda yazmanın teorik olarak mümkün olup olmadığını merak ediyorum. Özellikle, bir bayt dizisi içinde assembler oluşturduktan sonra, yürütmeye başlamak için ona nasıl atlayabilirsiniz?


Olduğuna inanmıyorum - bazen yönetilen dillerde güvensiz bir bağlamda çalışabilseniz de , bir işaretçiden bir temsilciyi sentezleyebileceğinize inanmıyorum - ve üretilen koda başka nasıl atlarsınız?
Damien_The_Unbeliever

@Damien: güvenli olmayan kod bir işlev işaretçisine yazmanıza izin vermez mi?
Henk Holterman

2
"Kontrolün yönetilmeyen koda dinamik olarak nasıl aktarılacağı" gibi bir başlık ile kapanma riskiniz daha düşük olabilir. Konuya daha çok benziyor. Sorun kodu oluşturmak değil.
Henk Holterman

8
En basit fikir, bayt dizisini bir dosyaya yazmak ve işletim sisteminin onu çalıştırmasına izin vermektir. Sonuçta, bir yorumlayıcıya değil bir derleyiciye ihtiyacınız var (bu da mümkün olabilir ama daha karmaşıktır).
Vlad

3
JIT istediğiniz kodu derledikten sonra, Win32 API'lerini bir miktar yönetilmeyen bellek ayırmak için (yürütülebilir olarak işaretlenir) kullanabilir, derlenen kodu bu bellek alanına kopyalayabilir ve ardından calliderlenmiş kodu çağırmak için IL işlem kodunu kullanabilirsiniz.
Jack P.

Yanıtlar:


71

Ve kavramın tam kanıtı burada bir olduğunu tamamen yetenekli F # içine JIT Rasmus' yaklaşımının çeviri

open System
open System.Runtime.InteropServices

type AllocationType =
    | COMMIT=0x1000u

type MemoryProtection =
    | EXECUTE_READWRITE=0x40u

type FreeType =
    | DECOMMIT = 0x4000u

[<DllImport("kernel32.dll", SetLastError=true)>]
extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect);

[<DllImport("kernel32.dll", SetLastError=true)>]
extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, FreeType freeType);

let JITcode: byte[] = [|0x55uy;0x8Buy;0xECuy;0x8Buy;0x45uy;0x08uy;0xD1uy;0xC8uy;0x5Duy;0xC3uy|]

[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>] 
type Ret1ArgDelegate = delegate of (uint32) -> uint32

[<EntryPointAttribute>]
let main (args: string[]) =
    let executableMemory = VirtualAlloc(IntPtr.Zero, UIntPtr(uint32(JITcode.Length)), AllocationType.COMMIT, MemoryProtection.EXECUTE_READWRITE)
    Marshal.Copy(JITcode, 0, executableMemory, JITcode.Length)
    let jitedFun = Marshal.GetDelegateForFunctionPointer(executableMemory, typeof<Ret1ArgDelegate>) :?> Ret1ArgDelegate
    let mutable test = 0xFFFFFFFCu
    printfn "Value before: %X" test
    test <- jitedFun.Invoke test
    printfn "Value after: %X" test
    VirtualFree(executableMemory, UIntPtr.Zero, FreeType.DECOMMIT) |> ignore
    0

mutlu bir şekilde boyun eğdiren

Value before: FFFFFFFC
Value after: 7FFFFFFE

Ek oyuma rağmen, farklı olmak için yalvarıyorum: bu keyfi kod yürütme , JIT değil - JIT, "tam zamanında derleme " anlamına geliyor , ancak bu kod örneğinden "derleme" yönünü göremiyorum.
09'da rwong

4
@rwong: "derleme" yönü hiçbir zaman orijinal soru endişeleri kapsamında olmadı. IL -> yerel kod dönüşümünü gerçekleştirmenin yönetilen kod yeteneği oldukça belirgindir.
Gene Belitski

70

Evet yapabilirsin. Aslında bu benim işim :)

GPU.NET'i tamamen F # dilinde yazdım (birim testlerimizi modüle edin) - tıpkı .NET CLR'nin yaptığı gibi, aslında çalışma zamanında sökülür ve IL JIT'leri verir. Kullanmak istediğiniz her türlü temel hızlandırma cihazı için yerel kod yayıyoruz; şu anda yalnızca Nvidia GPU'ları destekliyoruz, ancak sistemimizi minimum işle yeniden hedeflenebilir olacak şekilde tasarladım, bu nedenle gelecekte diğer platformları da destekleyeceğiz.

Performansa gelince, teşekkür etmem gereken F # var - optimize edilmiş modda (arka çağrılarla) derlendiğinde, JIT derleyicimizin kendisi muhtemelen CLR (C ++, IIRC'de yazılmıştır) içindeki derleyici kadar hızlıdır.

Yürütme için, jitted kodu çalıştırmak için denetimi donanım sürücülerine geçirme avantajına sahibiz; bununla birlikte, .NET, yönetilmeyen / yerel koda yönelik işlev işaretçileri desteklediğinden (normalde .NET tarafından sağlanan herhangi bir güvenlik / güvenliği kaybedersiniz), bunu CPU üzerinde yapmak daha zor olmaz.


4
NoExecute'un tüm amacı, kendi oluşturduğunuz koda atlayamamak değil mi? Bir işlev işaretçisi aracılığıyla yerel koda atlamak mümkün olmaktan ziyade: bir işlev işaretçisi aracılığıyla yerel koda atlamak mümkün değil mi?
Ian Boyd

Harika bir proje, kar amacı gütmeyen uygulamalar için ücretsiz yaparsanız çok daha fazla maruz kalacağınızı düşünüyorum. "Enthusiast" seviyesindeki chump-change'i kaybedersiniz, ancak onu kullanan daha fazla insanın artan maruz kalması için buna değecektir (kesinlikle yapacağımı biliyorum;)) !
BlueRaja - Danny Pflughoeft

@IanBoyd NoExecute, tampon taşmalarından ve ilgili sorunlardan kaynaklanan sorunları önlemenin çoğunlukla başka bir yoludur. Kendi kodunuzdan bir koruma değil, yasadışı kod yürütmeyi azaltmaya yardımcı olacak bir şey.
Luaan

51

Hüner olmalıdır VirtualAlloc ile EXECUTE_READWRITE-flag ve (P / ınvoke gerekiyor) Marshal.GetDelegateForFunctionPointer .

Döndürme tamsayı örneğinin değiştirilmiş bir sürümü aşağıda verilmiştir (burada güvenli olmayan koda gerek olmadığını unutmayın):

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint Ret1ArgDelegate(uint arg1);

public static void Main(string[] args){
    // Bitwise rotate input and return it.
    // The rest is just to handle CDECL calling convention.
    byte[] asmBytes = new byte[]
    {        
      0x55,             // push ebp
      0x8B, 0xEC,       // mov ebp, esp 
      0x8B, 0x45, 0x08, // mov eax, [ebp+8]
      0xD1, 0xC8,       // ror eax, 1
      0x5D,             // pop ebp 
      0xC3              // ret
    };

    // Allocate memory with EXECUTE_READWRITE permissions
    IntPtr executableMemory = 
        VirtualAlloc(
            IntPtr.Zero, 
            (UIntPtr) asmBytes.Length,    
            AllocationType.COMMIT,
            MemoryProtection.EXECUTE_READWRITE
        );

    // Copy the machine code into the allocated memory
    Marshal.Copy(asmBytes, 0, executableMemory, asmBytes.Length);

    // Create a delegate to the machine code.
    Ret1ArgDelegate del = 
        (Ret1ArgDelegate) Marshal.GetDelegateForFunctionPointer(
            executableMemory, 
            typeof(Ret1ArgDelegate)
        );

    // Call it
    uint n = (uint)0xFFFFFFFC;
    n = del(n);
    Console.WriteLine("{0:x}", n);

    // Free the memory
    VirtualFree(executableMemory, UIntPtr.Zero, FreeType.DECOMMIT);
 }

Tam örnek (artık hem X86 hem de X64 ile çalışıyor).


30

Güvenli olmayan kodu kullanarak, bir temsilciyi "hackleyebilir" ve bir dizide oluşturduğunuz ve depoladığınız rastgele bir derleme koduna işaret etmesini sağlayabilirsiniz. Buradaki fikir, delegenin _methodPtrYansıma kullanılarak ayarlanabilen bir alana sahip olmasıdır. İşte bazı örnek kodlar:

Bu, elbette, .NET çalışma zamanı değiştiğinde herhangi bir anda çalışmayı durdurabilecek kirli bir saldırıdır.

Prensipte, tam olarak yönetilen güvenli kodun JIT'i uygulamasına izin verilemeyeceğini tahmin ediyorum, çünkü bu, çalışma zamanının dayandığı tüm güvenlik varsayımlarını kıracaktır. (Oluşturulan montaj kodu, varsayımları ihlal etmediğine dair makine tarafından kontrol edilebilir bir kanıtla gelmediği sürece ...)


1
Güzel hile. Daha sonra bozuk bağlantılarla ilgili sorunları önlemek için kodun bazı bölümlerini bu gönderiye kopyalayabilirsiniz. (Veya bu gönderiye sadece küçük bir açıklama yazın).
Felix K.

AccessViolationExceptionÖrneğinizi çalıştırmaya çalışırsam bir alırım . Sanırım sadece DEP devre dışı bırakıldığında işe yarıyor.
Rasmus Faber

1
Ancak, EXECUTE_READWRITE bayrağıyla bellek ayırırsam ve bunu _methodPtr alanında kullanırsam iyi çalışıyor. Rotor-koduna bakıldığında, temelde Marshal.GetDelegateForFunctionPointer (), yığını kurmak ve güvenliği idare etmek için kodun etrafına fazladan bazı etkiler eklemesi dışında, yaptığı şey gibi görünüyor.
Rasmus Faber

Sanırım bağlantı öldü, ne yazık ki onu düzenlerdim, ancak orijinalin yerini değiştirmeyi bulamadım.
Abel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.