从C#测试AES-NI指令

我想知道是否有办法在C#.NET的主机系统CPU中测试AES-NI的存在

让我先说这个问题不是询问如何使用 .NET中的AES-NI。 事实certificate,简单地使用AESCryptoServiceProvider将使用AES-NI(如果可用)。 这个结果基于独立的基准测试,我将AESCryptoServiceProvider的性能与TrueCrypt中提供的基准进行了比较,后者确实支持AES-NI。 在使用和不使用AES-NI的两台机器上,结果令人惊讶地相似。

我希望能够测试它的原因是能够向用户表明他们的计算机支持AES-NI。 这是相关的,因为它会减少支持事件,包括“但我的朋友也有Core i5,但他的速度要快得多!” 如果程序的用户界面可以向用户指示他们的系统支持或不支持AES-NI,则还可以指示“由于该系统不支持AES-NI,因此性能较低是正常的”。

(我们可以感谢英特尔对不同处理器步进的所有困惑!:-))

有没有办法通过WMI检测这些信息?

似乎在SO上有类似的问题: 内联汇编代码以获得具有良好答案的CPU ID 。

但这个答案需要进行一些调整以满足您的需求。

首先,据我所知,AES-NI只能出现在64位处理器上,对吧? 然后你可以忽略上面答案中的所有32位代码。

其次,你需要ECX寄存器或者它的第25位,所以你必须改变一下代码:

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

最后,您需要在数组中存储ECX寄存器:

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

据我所知,这都是变化。

Mark上面的答案很棒,让我的工作得很好,但是我注意到如果应用程序以32位模式运行,那么ecx寄存器没有被x86代码拉入,从而导致没有检测到AES-NI 。

我添加了一行并更改了另一行,基本上将对x64代码所做的更改应用于x86代码。 这允许您从32位模式查看AES-NI位。 不确定它是否会对某人有所帮助,但我想我会发布它。

编辑:当我做一些测试时,我注意到x64代码返回的寄存器不正确。 EDX在偏移量0x4,0x8和0xC处返回,另外ECX和EDX寄存器与x86代码处于不同的偏移量,因此您需要更频繁地检查IntPtr.Size以保持两种环境中的工作。 为简化起见,我将ECX寄存器置于0x4,将EDX置于0x8,这样就可以正确排列数据。

如果有人要求我可以发布整个课程,这是我从这篇文章和其他人那里学到的一个实例。

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