Checking AES-NI Instructions from C #

I want to know if there is a way to check for the presence of AES-NI in the CPU of the main system with C # .NET.

Let me say that this question is not asking about how to use AES-NI from .NET. It turns out that just using AESCryptoServiceProvider will use AES-NI if it is available. This result is based on independent tests. I compared the characteristics of AESCryptoServiceProvider with the criteria contained in TrueCrypt that really support AES-NI. The results were surprisingly similar on both machines with and without AES-NI.

The reason I want to test it is to tell the user that their computer supports AES-NI. This would be relevant as it would reduce support cases related to issues such as β€œbut my friend also has Core i5, but it's much faster!” If the user interface of the program can indicate to the user that their system runs or does not support AES-NI, you can also specify that "lower performance is normal because this system does not support AES-NI."

(We can thank Intel for all the confusion with different processor stepping! :-))

Is there a way to detect this information, possibly through WMI?

+6
source share
2 answers

There seems to be the same question with this answer to SO: Build code to get the CPU ID .

But this answer requires some adjustments according to your needs.

Firstly, as I understand it, AES-NI can only be present on 64-bit processors, right? Then you can ignore all 32-bit codes in the answer above.

Secondly, you need the ECX register, or rather, its 25th bit, so you need to change the code a bit:

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

Finally, you need to save the ECX register in an 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 */ }; 

As far as I can see, all the changes.

+2
source

The answer from Mark above is wonderful and works well for me, however, I noticed that if the application starts in 32-bit mode, then the ecx register was not pulled out in x86 code, which would not lead to detection of AES-NI.

I added one line and changed another, mainly applying the changes made to x64 code to x86 code. This allows you to see the AES-NI bit from 32-bit mode. Not sure if this will help anyone, but I thought I would post it.

EDIT: While I was testing, I noticed that the registers returned by x64 were incorrect. EDX returned with an offset of 0x4, 0x8 and 0xC, in addition, the ECX and EDX registers were at different offsets with the x86 code, so you had to check IntPtr.Size more often to support work in both environments. To simplify things, I put the ECX register at 0x4 and EDX at 0x8, and thus the data is ordered correctly.

If someone asks, I can post the whole class, which is a working example of what I learned from this post and others.

 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); 
+1
source

Source: https://habr.com/ru/post/980902/


All Articles