从C#测试AES-NI指令

时间:2015-01-13 04:07:29

标签: c# .net aes

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

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

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

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

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

2 个答案:

答案 0 :(得分:2)

似乎在SO上有类似的问题:Inline Assembly Code to Get 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 */
};

据我所见,这一切都有所改变。

答案 1 :(得分:1)

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