2015-01-13 7 views
6

Ich möchte wissen, ob es eine Möglichkeit gibt, die Präsenz von AES-NI in der CPU des Hostsystems von C# .NET zu testen.Test für AES-NI-Anweisungen von C#

mich vorne Lassen Sie sagen, dass diese Frage ist nicht fragen, wie zu Verwendung AES-NI von .NET. Es stellt sich heraus, einfach mit AESCryptoServiceProvider wird AES-NI verwenden, wenn es verfügbar ist. Dieses Ergebnis basiert auf unabhängigen Benchmarks. Ich habe die Performances von AESCryptoServiceProvider mit den Benchmarks in TrueCrypt verglichen, die tatsächlich AES-NI unterstützen. Die Ergebnisse waren auf beiden Maschinen mit und ohne AES-NI überraschend ähnlich.

Der Grund, warum ich in der Lage sein soll, es zu testen, ist in der Lage, dem Benutzer anzuzeigen, dass ihr Computer AES-NI unterstützt. Dies wäre relevant, da Support-Anfragen mit Fragen wie "aber mein Freund hat einen Core i5 auch, aber er ist viel schneller!" Reduziert werden. Wenn die Benutzerschnittstelle des Programms dem Benutzer anzeigen könnte, dass ihr System AES-NI unterstützt oder nicht, könnte auch angegeben werden, dass "eine langsamere Leistung normal ist, da dieses System AES-NI nicht unterstützt."

(Wir danken Intel für all die Verwirrung mit verschiedenen Prozessor-Steppings! :-))

Gibt es eine Möglichkeit, diese Informationen zu erfassen, vielleicht durch WMI?

+0

Ich kenne die Antwort nicht, aber der erste Ort, an dem ich suchen würde, wäre ein OS WMI Anruf auch. –

+0

Im Gegensatz zu Forum-Sites verwenden wir nicht "Danke" oder "Jede Hilfe geschätzt" oder Signaturen auf [so]. Siehe "[Sollen 'Hallo', 'Danke', 'Slogans' und 'Anrede' aus Posts entfernt werden?] (Http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be "Removed-from-posts". –

+0

Für die Zukunft notiert. – fdmillion

Antwort

2

Es scheint, dass es die ähnliche Frage zu SO gibt: Inline Assembly Code to Get CPU ID mit der großen Antwort.

Aber diese Antwort erfordert einige Anpassungen an Ihre Bedürfnisse anzupassen.

Erstens, wie ich verstehe, AES-NI kann Präsenz nur bei 64-Bit-Prozessoren sein, oder? Dann könnten Sie den gesamten 32-Bit-Code in der obigen Antwort ignorieren.

Zweitens müssen Sie ECX registrieren oder besser gesagt seine 25-Bit, so dass Sie Code ein wenig ändern müssen:

private static bool IsAESNIPresent() 
{ 
    byte[] sn = new byte[16]; // !!! Here were 8 bytes 

    if (!ExecuteCode(ref sn)) 
     return false; 

    var ecx = BitConverter.ToUInt32(sn, 8); 
    return (ecx & (1 << 25)) != 0; 
} 

Schließlich müssen Sie speichern ECX-Register in der Reihe:

byte[] code_x64 = new byte[] { 
    0x53,          /* push rbx */ 
    0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, /* mov rax, 0x1 */ 
    0x0f, 0xa2,        /* cpuid */ 
    0x41, 0x89, 0x00,       /* mov [r8], eax */ 
    0x41, 0x89, 0x50, 0x04,     /* mov [r8+0x4], ebx !!! changed */ 
    0x41, 0x89, 0x50, 0x08,     /* mov [r8+0x8], ecx !!! added */ 
    0x41, 0x89, 0x50, 0x0C,     /* mov [r8+0xC], edx !!! added*/ 
    0x5b,          /* pop rbx */ 
    0xc3,          /* ret */ 
}; 

Soweit wie ich sehen kann, das sind alles Veränderungen.

+0

Wie kann ich das gleiche in C++ (GCC) auf Windows? –

+0

@ Canadian_Republican, GCC unterstützt CPU ID: http://stackoverflow.com/questions/14266772/how-do-icall-cpuid-in-linux –

1

Die Antwort von Mark oben ist fantastisch und hat Dinge gut für mich funktioniert, jedoch habe ich festgestellt, dass wenn die Anwendung im 32-Bit-Modus ausgeführt wird, das ecx-Register nicht in den x86-Code gezogen wurde, was zu keiner Erkennung führt von AES-NI.

Ich fügte eine Zeile hinzu und änderte eine andere, wobei ich die Änderungen, die der x64-Code an den x86-Code vorgenommen hatte, grundsätzlich anwendete. Dadurch können Sie das AES-NI-Bit auch im 32-Bit-Modus sehen. Ich bin mir nicht sicher, ob es jemandem helfen wird, aber ich dachte, ich würde es posten.

EDIT: Während ich einige Tests machte, bemerkte ich, dass die vom x64-Code zurückgegebenen Register falsch waren. EDX wurde bei Offset 0x4, 0x8 und 0xC zurückgegeben, außerdem hatten die ECX- und EDX-Register unterschiedliche Offsets mit x86-Code. Daher mussten Sie IntPtr.Size öfter überprüfen, um die Funktionalität in beiden Umgebungen aufrechtzuerhalten. Um die Dinge zu vereinfachen, platziere ich das ECX-Register bei 0x4 und EDX bei 0x8 und auf diese Weise sind die Daten korrekt angeordnet.

Wenn jemand anfragt, kann ich die gesamte Klasse veröffentlichen, die ein funktionierendes Beispiel für das ist, was ich aus diesem Beitrag und anderen gelernt habe.

public static bool ExecuteCode(ref byte[] result) { 
    byte[] code_x86 = new byte[] { 
     0x55,      /* push ebp */ 
     0x89, 0xE5,    /* mov ebp, esp */ 
     0x57,      /* push edi */ 
     0x8b, 0x7D, 0x10,   /* mov edi, [ebp+0x10] */ 
     0x6A, 0x01,    /* push 0x1 */ 
     0x58,      /* pop eax */ 
     0x53,      /* push ebx */ 
     0x0F, 0xA2,    /* cpuid */ 
     0x89, 0x07,    /* mov [edi], eax */ 
     0x89, 0x4F, 0x04,   /* mov [edi+0x4], ecx Changed */ 
     0x89, 0x57, 0x08,   /* mov [edi+0x8], edx Changed */ 
     0x5B,      /* pop ebx */ 
     0x5F,      /* pop edi */ 
     0x89, 0xEC,    /* mov esp, ebp */ 
     0x5D,      /* pop ebp */ 
     0xC2, 0x10, 0x00,   /* ret 0x10 */ 
    }; 
    byte[] code_x64 = new byte[] { 
     0x53,          /* push rbx */ 
     0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00, /* mov rax, 0x1 */ 
     0x0f, 0xA2,        /* cpuid */ 
     0x41, 0x89, 0x00,       /* mov [r8], eax */ 
     0x41, 0x89, 0x48, 0x04,     /* mov [r8+0x4], ecx Changed */ 
     0x41, 0x89, 0x50, 0x08,     /* mov [r8+0x8], edx Changed*/ 
     0x5B,          /* pop rbx */ 
     0xC3,          /* ret */ 
    }; 

    int num; 
    byte[] code = (IntPtr.Size == 4) ? code_x86 : code_x64; 
    IntPtr ptr = new IntPtr(code.Length); 

    if (!VirtualProtect(code, ptr, PAGE_EXECUTE_READWRITE, out num)) 
     Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); 

    ptr = new IntPtr(result.Length); 

    return (ExecuteNativeCode(code, IntPtr.Zero, 0, result, ptr) != IntPtr.Zero);