2015-01-13 15 views
6

Desidero sapere se esiste un modo per verificare la presenza dello di AES-NI nella CPU del sistema host da C# .NET.Test per istruzioni AES-NI da C#

Lasciatemi dire fin da subito che questa domanda è non chiedendo su come uso AES-NI da .NET. Risulta semplicemente utilizzando AESCryptoServiceProvider utilizzerà AES-NI se è disponibile. Questo risultato si basa su benchmark indipendenti che ho confrontato con le prestazioni dello AESCryptoServiceProvider rispetto ai benchmark forniti in TrueCrypt, che supporta infatti AES-NI. I risultati erano sorprendentemente simili su entrambe le macchine con e senza AES-NI.

Il motivo per cui voglio verificarlo è poter indicare all'utente che il proprio computer supporti AES-NI. Questo sarebbe rilevante dal momento che ridurrebbe gli incidenti di supporto che riguardano domande come "ma il mio amico ha anche un Core i5 ma il suo è molto più veloce!" Se l'interfaccia utente del programma potrebbe indicare all'utente che il proprio sistema supporta o non supporta AES-NI, sarebbe anche possibile indicare che "le prestazioni più lente sono normali poiché questo sistema non supporta AES-NI."

(possiamo ringraziare Intel per tutta la confusione con diverse stepping del processore! :-))

C'è un modo per rilevare queste informazioni, magari attraverso WMI?

+0

Non so la risposta, ma il primo posto che guarderei sarebbe anche una chiamata WMI del sistema operativo. –

+0

A differenza dei siti di forum, non utilizziamo "Grazie" o "Qualsiasi aiuto apprezzato" o firme su [so]. Vedi "[Se 'Hi', 'thanks', tagline e saluti saranno rimossi dai post?] (Http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be -removed-from-posts) –

+0

Notato per il futuro – fdmillion

risposta

2

Sembra che ci sia la domanda simile su SO: Inline Assembly Code to Get CPU ID con l'ottima risposta.

Ma questa risposta richiede alcune regolazioni in base alle proprie esigenze.

In primo luogo, come ho capito, AES-NI può essere presente solo con processori a 64 bit, giusto? Quindi potresti ignorare tutto il codice a 32 bit nella risposta sopra.

In secondo luogo, è necessario registrarsi ECX o meglio la sua parte 25 °, quindi è necessario modificare il codice un po ':

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; 
} 

Infine, è necessario memorizzare il registro ECX in array:

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 */ 
}; 

Per quanto come posso vedere, sono tutti cambiamenti.

+0

Come posso fare lo stesso in C++ (GCC) su Windows? –

+0

@Canadian_Republican, GCC supporta CPU ID: http://stackoverflow.com/questions/14266772/how-do-i-call-cpuid-in-linux –

1

La risposta di Marco sopra è fantastica e ha funzionato bene per me, tuttavia ho notato che se l'applicazione viene eseguita in modalità a 32 bit, il registro di ECX non è stato inserito nel codice x86 e non ha portato a nessun rilevamento di AES-NI.

Ho aggiunto una riga e ne ho modificata un'altra, applicando fondamentalmente le modifiche Mark apportate al codice x64 al codice x86. Ciò consente di vedere il bit AES-NI anche dalla modalità a 32 bit. Non sono sicuro se aiuterà qualcuno, ma ho pensato di pubblicarlo.

EDIT: Mentre eseguivo alcuni test, ho notato che i registri restituiti dal codice x64 non erano corretti. EDX veniva restituito con offset 0x4, 0x8 e 0xC, inoltre i registri ECX ed EDX erano a offset diversi con codice x86 quindi era necessario controllare IntPtr.Size più spesso per mantenere le cose funzionanti in entrambi gli ambienti. Per semplificare le cose metto il registro ECX a 0x4 e EDX a 0x8 e in questo modo i dati sono disposti correttamente.

Se qualcuno richiede posso postare l'intera classe che è un esempio funzionante di ciò che ho imparato da questo post e da altri.

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); 
Problemi correlati