x86 / x64 CPUID в C#

относится к мой другой вопрос, пожалуйста, помогите мне отладить "необработанное исключение типа" система.AccessViolationException ' произошло в неизвестном модуле. Дополнительные сведения: попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена."Шагая через код, все работает до фактического вызова del () и терпит неудачу в этой строке.

этот код основан на этом статьи и этого код, который работает в Python. Я не могу получить пример кода as-is для работы (то же исключение), но я надеюсь, что он просто немного устарел или что-то в этом роде.

EDIT: Посмотрите историю редактирования, если вас волнует, как мы попали сюда, что неинтересно.

закончил рабочую версию:

public static class CpuID
{
    public static byte[] Invoke(int level)
    {
        IntPtr codePointer = IntPtr.Zero;
        try
        {
            // compile
            byte[] codeBytes;
            if (IntPtr.Size == 4)
            {
                codeBytes = x86CodeBytes;
            }
            else
            {
                codeBytes = x64CodeBytes;
            }

            codePointer = VirtualAlloc(
                IntPtr.Zero,
                new UIntPtr((uint)codeBytes.Length),
                AllocationType.COMMIT | AllocationType.RESERVE,
                MemoryProtection.EXECUTE_READWRITE
            );

            Marshal.Copy(codeBytes, 0, codePointer, codeBytes.Length);

            CpuIDDelegate cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(codePointer, typeof(CpuIDDelegate));

            // invoke
            GCHandle handle = default(GCHandle);
            var buffer = new byte[16];

            try
            {
                handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                cpuIdDelg(level, buffer);
            }
            finally
            {
                if (handle != default(GCHandle))
                {
                    handle.Free();
                }
            }

            return buffer;
        }
        finally
        {
            if (codePointer != IntPtr.Zero)
            {
                VirtualFree(codePointer, 0, 0x8000);
                codePointer = IntPtr.Zero;
            }
        }
    }

    [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
    private delegate void CpuIDDelegate(int level, byte[] buffer);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType,
        MemoryProtection flProtect);

    [DllImport("kernel32")]
    private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType);

    [Flags()]
    private enum AllocationType : uint
    {
        COMMIT = 0x1000,
        RESERVE = 0x2000,
        RESET = 0x80000,
        LARGE_PAGES = 0x20000000,
        PHYSICAL = 0x400000,
        TOP_DOWN = 0x100000,
        WRITE_WATCH = 0x200000
    }

    [Flags()]
    private enum MemoryProtection : uint
    {
        EXECUTE = 0x10,
        EXECUTE_READ = 0x20,
        EXECUTE_READWRITE = 0x40,
        EXECUTE_WRITECOPY = 0x80,
        NOACCESS = 0x01,
        READONLY = 0x02,
        READWRITE = 0x04,
        WRITECOPY = 0x08,
        GUARD_Modifierflag = 0x100,
        NOCACHE_Modifierflag = 0x200,
        WRITECOMBINE_Modifierflag = 0x400
    }

    // Basic ASM strategy --
    // void x86CpuId(int level, byte* buffer) 
    // {
    //    eax = level
    //    cpuid
    //    buffer[0] = eax
    //    buffer[4] = ebx
    //    buffer[8] = ecx
    //    buffer[12] = edx
    // }

    private readonly static byte[] x86CodeBytes = {
        0x55,                   // push        ebp  
        0x8B, 0xEC,             // mov         ebp,esp
        0x53,                   // push        ebx  
        0x57,                   // push        edi

        0x8B, 0x45, 0x08,       // mov         eax, dword ptr [ebp+8] (move level into eax)
        0x0F, 0xA2,              // cpuid

        0x8B, 0x7D, 0x0C,       // mov         edi, dword ptr [ebp+12] (move address of buffer into edi)
        0x89, 0x07,             // mov         dword ptr [edi+0], eax  (write eax, ... to buffer)
        0x89, 0x5F, 0x04,       // mov         dword ptr [edi+4], ebx 
        0x89, 0x4F, 0x08,       // mov         dword ptr [edi+8], ecx 
        0x89, 0x57, 0x0C,       // mov         dword ptr [edi+12],edx 

        0x5F,                   // pop         edi  
        0x5B,                   // pop         ebx  
        0x8B, 0xE5,             // mov         esp,ebp  
        0x5D,                   // pop         ebp 
        0xc3                    // ret
    };

    private readonly static byte[] x64CodeBytes = {
        0x53,                       // push rbx    this gets clobbered by cpuid

        // rcx is level
        // rdx is buffer.
        // Need to save buffer elsewhere, cpuid overwrites rdx
        // Put buffer in r8, use r8 to reference buffer later.

        // Save rdx (buffer addy) to r8
        0x49, 0x89, 0xd0,           // mov r8,  rdx

        // Move ecx (level) to eax to call cpuid, call cpuid
        0x89, 0xc8,                 // mov eax, ecx
        0x0F, 0xA2,                 // cpuid

        // Write eax et al to buffer
        0x41, 0x89, 0x40, 0x00,     // mov    dword ptr [r8+0],  eax
        0x41, 0x89, 0x58, 0x04,     // mov    dword ptr [r8+4],  ebx
        0x41, 0x89, 0x48, 0x08,     // mov    dword ptr [r8+8],  ecx
        0x41, 0x89, 0x50, 0x0c,     // mov    dword ptr [r8+12], edx

        0x5b,                       // pop rbx
        0xc3                        // ret
    };
}

обратите внимание, что CPUID0 необходимо читать в правильном порядке:

//a twelve character ASCII string stored in EBX, EDX, ECX - in that order
var cpuid0s = new string(ASCIIEncoding.ASCII.GetChars(
    cpuid0.Skip(4).Take(4).Concat(
    cpuid0.Skip(12).Take(4)).Concat(
    cpuid0.Skip(8).Take(4)).ToArray()));

6 ответов


Я совершенно уверен, что вы блокируетесь DEP. The x_CPUIDy_INSNS байтовые массивы находятся в сегменте памяти, помеченном как данные и неисполняемые.

EDIT:

Это, как говорится, я получил версию, которая компилируется и работает, но я не думаю, что получает правильные значения. Возможно, это поможет вам на вашем пути.

EDIT 2:

Я думаю, что у меня правильные ценности обратно. Не стесняйтесь утверждать.

namespace CPUID
{
    using System;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Text;

    internal static class Program
    {
        [Flags]
        private enum AllocationTypes : uint
        {
            Commit = 0x1000,
            Reserve = 0x2000,
            Reset = 0x80000,
            LargePages = 0x20000000,
            Physical = 0x400000,
            TopDown = 0x100000,
            WriteWatch = 0x200000
        }

        [Flags]
        private enum MemoryProtections : uint
        {
            Execute = 0x10,
            ExecuteRead = 0x20,
            ExecuteReadWrite = 0x40,
            ExecuteWriteCopy = 0x80,
            NoAccess = 0x01,
            ReadOnly = 0x02,
            ReadWrite = 0x04,
            WriteCopy = 0x08,
            GuartModifierflag = 0x100,
            NoCacheModifierflag = 0x200,
            WriteCombineModifierflag = 0x400
        }

        [Flags]
        private enum FreeTypes : uint
        {
            Decommit = 0x4000,
            Release = 0x8000
        }

        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        private unsafe delegate void CPUID0Delegate(byte* buffer);

        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        private unsafe delegate void CPUID1Delegate(byte* buffer);

        private static void Main()
        {
            Console.WriteLine("CPUID0: {0}", string.Join(", ", CPUID0().Select(x => x.ToString("X2", CultureInfo.InvariantCulture))));
            Console.WriteLine("CPUID0: {0}", new string(ASCIIEncoding.ASCII.GetChars(CPUID0())));
            Console.WriteLine("CPUID1: {0}", string.Join(", ", CPUID1().Select(x => x.ToString("X2", CultureInfo.InvariantCulture))));
            Console.ReadLine();
        }

        private static unsafe byte[] CPUID0()
        {
            byte[] buffer = new byte[12];

            if (IntPtr.Size == 4)
            {
                IntPtr p = NativeMethods.VirtualAlloc(
                    IntPtr.Zero,
                    new UIntPtr((uint)x86_CPUID0_INSNS.Length),
                    AllocationTypes.Commit | AllocationTypes.Reserve,
                    MemoryProtections.ExecuteReadWrite);
                try
                {
                    Marshal.Copy(x86_CPUID0_INSNS, 0, p, x86_CPUID0_INSNS.Length);

                    CPUID0Delegate del = (CPUID0Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID0Delegate));

                    fixed (byte* newBuffer = &buffer[0])
                    {
                        del(newBuffer);
                    }
                }
                finally
                {
                    NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
                }
            }
            else if (IntPtr.Size == 8)
            {
                IntPtr p = NativeMethods.VirtualAlloc(
                    IntPtr.Zero,
                    new UIntPtr((uint)x64_CPUID0_INSNS.Length),
                    AllocationTypes.Commit | AllocationTypes.Reserve,
                    MemoryProtections.ExecuteReadWrite);
                try
                {
                    Marshal.Copy(x64_CPUID0_INSNS, 0, p, x64_CPUID0_INSNS.Length);

                    CPUID0Delegate del = (CPUID0Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID0Delegate));

                    fixed (byte* newBuffer = &buffer[0])
                    {
                        del(newBuffer);
                    }
                }
                finally
                {
                    NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
                }
            }

            return buffer;
        }

        private static unsafe byte[] CPUID1()
        {
            byte[] buffer = new byte[12];

            if (IntPtr.Size == 4)
            {
                IntPtr p = NativeMethods.VirtualAlloc(
                    IntPtr.Zero,
                    new UIntPtr((uint)x86_CPUID1_INSNS.Length),
                    AllocationTypes.Commit | AllocationTypes.Reserve,
                    MemoryProtections.ExecuteReadWrite);
                try
                {
                    Marshal.Copy(x86_CPUID1_INSNS, 0, p, x86_CPUID1_INSNS.Length);

                    CPUID1Delegate del = (CPUID1Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID1Delegate));

                    fixed (byte* newBuffer = &buffer[0])
                    {
                        del(newBuffer);
                    }
                }
                finally
                {
                    NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
                }
            }
            else if (IntPtr.Size == 8)
            {
                IntPtr p = NativeMethods.VirtualAlloc(
                    IntPtr.Zero,
                    new UIntPtr((uint)x64_CPUID1_INSNS.Length),
                    AllocationTypes.Commit | AllocationTypes.Reserve,
                    MemoryProtections.ExecuteReadWrite);
                try
                {
                    Marshal.Copy(x64_CPUID1_INSNS, 0, p, x64_CPUID1_INSNS.Length);

                    CPUID1Delegate del = (CPUID1Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID1Delegate));

                    fixed (byte* newBuffer = &buffer[0])
                    {
                        del(newBuffer);
                    }
                }
                finally
                {
                    NativeMethods.VirtualFree(p, 0, FreeTypes.Release);
                }
            }

            return buffer;
        }

        private static class NativeMethods
        {
            [DllImport("kernel32.dll", SetLastError = true)]
            internal static extern IntPtr VirtualAlloc(
                IntPtr lpAddress,
                UIntPtr dwSize,
                AllocationTypes flAllocationType,
                MemoryProtections flProtect);

            [DllImport("kernel32")]
            [return: MarshalAs(UnmanagedType.Bool)]
            internal static extern bool VirtualFree(
                IntPtr lpAddress,
                uint dwSize,
                FreeTypes flFreeType);
        }

        #region ASM
        private static readonly byte[] x86_CPUID0_INSNS = new byte[]
            {
                0x53,                      // push   %ebx
                0x31, 0xc0,                // xor    %eax,%eax
                0x0f, 0xa2,                // cpuid
                0x8b, 0x44, 0x24, 0x08,    // mov    0x8(%esp),%eax
                0x89, 0x18,                // mov    %ebx,0x0(%eax)
                0x89, 0x50, 0x04,          // mov    %edx,0x4(%eax)
                0x89, 0x48, 0x08,          // mov    %ecx,0x8(%eax)
                0x5b,                      // pop    %ebx
                0xc3                       // ret
            };

        private static readonly byte[] x86_CPUID1_INSNS = new byte[]
            {
                0x53,                   // push   %ebx
                0x31, 0xc0,             // xor    %eax,%eax
                0x40,                   // inc    %eax
                0x0f, 0xa2,             // cpuid
                0x5b,                   // pop    %ebx
                0xc3                    // ret
            };

        private static readonly byte[] x64_CPUID0_INSNS = new byte[]
            {
                0x49, 0x89, 0xd8,       // mov    %rbx,%r8
                0x49, 0x89, 0xc9,       // mov    %rcx,%r9
                0x48, 0x31, 0xc0,       // xor    %rax,%rax
                0x0f, 0xa2,             // cpuid
                0x4c, 0x89, 0xc8,       // mov    %r9,%rax
                0x89, 0x18,             // mov    %ebx,0x0(%rax)
                0x89, 0x50, 0x04,       // mov    %edx,0x4(%rax)
                0x89, 0x48, 0x08,       // mov    %ecx,0x8(%rax)
                0x4c, 0x89, 0xc3,       // mov    %r8,%rbx
                0xc3                    // retq
            };

        private static readonly byte[] x64_CPUID1_INSNS = new byte[]
            {
                0x53,                     // push   %rbx
                0x48, 0x31, 0xc0,         // xor    %rax,%rax
                0x48, 0xff, 0xc0,         // inc    %rax
                0x0f, 0xa2,               // cpuid
                0x5b,                     // pop    %rbx
                0xc3                      // retq
            };
        #endregion
    }
}

Я решил улучшить свой ответ. Ему больше не нужно небезопасно компилировать, и ему нужны только два блока сборки, чтобы иметь возможность считывать любые и все блоки cpuid, потому что он просто записывает eax/ebx/ecx/edx в 16-байтовый массив байтов.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;

namespace CpuID
{
    public class CpuID : IDisposable
    {
        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        public delegate void CpuIDDelegate(int level, byte[] buffer);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr VirtualAlloc(
            IntPtr lpAddress,
            UIntPtr dwSize,
            AllocationType flAllocationType, 
            MemoryProtection flProtect
        );

        [DllImport("kernel32")]
        private static extern bool VirtualFree(
                IntPtr lpAddress,
                UInt32 dwSize,
                UInt32 dwFreeType
        );

        [Flags()]
        public enum AllocationType : uint
        {
            COMMIT = 0x1000,
            RESERVE = 0x2000,
            RESET = 0x80000,
            LARGE_PAGES = 0x20000000,
            PHYSICAL = 0x400000,
            TOP_DOWN = 0x100000,
            WRITE_WATCH = 0x200000
        }

        [Flags()]
        public enum MemoryProtection : uint
        {
            EXECUTE = 0x10,
            EXECUTE_READ = 0x20,
            EXECUTE_READWRITE = 0x40,
            EXECUTE_WRITECOPY = 0x80,
            NOACCESS = 0x01,
            READONLY = 0x02,
            READWRITE = 0x04,
            WRITECOPY = 0x08,
            GUARD_Modifierflag = 0x100,
            NOCACHE_Modifierflag = 0x200,
            WRITECOMBINE_Modifierflag = 0x400
        }

        private CpuIDDelegate cpuIdDelg;

        private IntPtr codePointer;

        // void x86CpuId(int level, byte* buffer) 
        // {
        //    eax = level
        //    cpuid
        //    buffer[0] = eax
        //    buffer[4] = ebx
        //    buffer[8] = ecx
        //    buffer[12] = edx
        // }
        private byte[] x86CodeBytes = 
        {
            0x55,                   // push        ebp  
            0x8B, 0xEC,             // mov         ebp,esp
            0x53,                   // push        ebx  
            0x57,                   // push        edi

            0x8B, 0x45, 0x08,       // mov         eax, dword ptr [ebp+8] (move level into eax)
            0x0F, 0xA2,              // cpuid

            0x8B, 0x7D, 0x0C,       // mov         edi, dword ptr [ebp+12] (move address of buffer into edi)
            0x89, 0x07,             // mov         dword ptr [edi+0], eax  (write eax, ... to buffer)
            0x89, 0x5F, 0x04,       // mov         dword ptr [edi+4], ebx 
            0x89, 0x4F, 0x08,       // mov         dword ptr [edi+8], ecx 
            0x89, 0x57, 0x0C,       // mov         dword ptr [edi+12],edx 

            0x5F,                   // pop         edi  
            0x5B,                   // pop         ebx  
            0x8B, 0xE5,             // mov         esp,ebp  
            0x5D,                   // pop         ebp 
            0xc3                    // ret
        };

        private byte[] x64CodeBytes = 
        {
            0x53,                       // push rbx    this gets clobbered by cpuid

            // rcx is level
            // rdx is buffer.
            // Need to save buffer elsewhere, cpuid overwrites rdx
            // Put buffer in r8, use r8 to reference buffer later.

            // Save rdx (buffer addy) to r8
            0x49, 0x89, 0xd0,           // mov r8,  rdx

            // Move ecx (level) to eax to call cpuid, call cpuid
            0x89, 0xc8,                 // mov eax, ecx
            0x0F, 0xA2,                 // cpuid

            // Write eax et al to buffer
            0x41, 0x89, 0x40, 0x00,     // mov    dword ptr [r8+0],  eax
            0x41, 0x89, 0x58, 0x04,     // mov    dword ptr [r8+4],  ebx
            0x41, 0x89, 0x48, 0x08,     // mov    dword ptr [r8+8],  ecx
            0x41, 0x89, 0x50, 0x0c,     // mov    dword ptr [r8+12], edx

            0x5b,                       // pop rbx
            0xc3                        // ret
        };

        public CpuID()
        {
            Compile();
        }

        ~CpuID()
        {
            Dispose(false);
        }

        private void Compile()
        {
            byte[] codeBytes;

            if (IntPtr.Size == 4)
            {
                codeBytes = x86CodeBytes;
            }
            else
            {
                codeBytes = x64CodeBytes;
            }

            this.codePointer = VirtualAlloc(
                IntPtr.Zero,
                new UIntPtr((uint)codeBytes.Length),
                AllocationType.COMMIT | AllocationType.RESERVE,
                MemoryProtection.EXECUTE_READWRITE
            );

            Marshal.Copy(codeBytes, 0, this.codePointer, codeBytes.Length);

            this.cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(this.codePointer, typeof(CpuIDDelegate));
        }

        public void Invoke(int level, byte[] buffer)
        {
            GCHandle handle = default(GCHandle);
            if (buffer.Length < 16)
            {
                throw new ArgumentException("buffer must be at least 16 bytes long");
            }

            try
            {
                handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);

                this.cpuIdDelg(level, buffer);
            }
            finally
            {
                if (handle != default(GCHandle))
                {
                    handle.Free();
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        public void Dispose(bool disposing)
        {
            if (this.codePointer != IntPtr.Zero)
            {
                VirtualFree(this.codePointer, 0, 0x8000);
                this.codePointer = IntPtr.Zero;
            }
        }

    }
}

Я взял код @antiduh и преобразовал его в статический метод, поэтому нет жизненного цикла объекта для управления. Это медленнее, потому что код ASM не используется повторно между вызовами Invoke(), но компромисс скорости для простоты имеет смысл для моего варианта использования. Эта новая версия может вызывать CPUID 1000 раз за 15 миллисекунд на моей машине.

Спасибо за замечательный код ребята!

public static class CpuID {

    public static byte[] Invoke(int level) {
        IntPtr codePointer = IntPtr.Zero;
        try {
            // compile
            byte[] codeBytes;
            if (IntPtr.Size == 4) {
                codeBytes = x86CodeBytes;
            } else {
                codeBytes = x64CodeBytes;
            }

            codePointer = VirtualAlloc(
                IntPtr.Zero,
                new UIntPtr((uint)codeBytes.Length),
                AllocationType.COMMIT | AllocationType.RESERVE,
                MemoryProtection.EXECUTE_READWRITE
            );

            Marshal.Copy(codeBytes, 0, codePointer, codeBytes.Length);

            CpuIDDelegate cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(codePointer, typeof(CpuIDDelegate));

            // invoke
            GCHandle handle = default(GCHandle);
            var buffer = new byte[16];

            try {
                handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                cpuIdDelg(level, buffer);
            } finally {
                if (handle != default(GCHandle)) {
                    handle.Free();
                }
            }

            return buffer;
        } finally {
            if (codePointer != IntPtr.Zero) {
                VirtualFree(codePointer, 0, 0x8000);
                codePointer = IntPtr.Zero;
            }
        }
    }

    [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
    private delegate void CpuIDDelegate(int level, byte[] buffer);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType,
        MemoryProtection flProtect);

    [DllImport("kernel32")]
    private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType);

    [Flags()]
    private enum AllocationType : uint {
        COMMIT = 0x1000,
        RESERVE = 0x2000,
        RESET = 0x80000,
        LARGE_PAGES = 0x20000000,
        PHYSICAL = 0x400000,
        TOP_DOWN = 0x100000,
        WRITE_WATCH = 0x200000
    }

    [Flags()]
    private enum MemoryProtection : uint {
        EXECUTE = 0x10,
        EXECUTE_READ = 0x20,
        EXECUTE_READWRITE = 0x40,
        EXECUTE_WRITECOPY = 0x80,
        NOACCESS = 0x01,
        READONLY = 0x02,
        READWRITE = 0x04,
        WRITECOPY = 0x08,
        GUARD_Modifierflag = 0x100,
        NOCACHE_Modifierflag = 0x200,
        WRITECOMBINE_Modifierflag = 0x400
    }

    // Basic ASM strategy --
    // void x86CpuId(int level, byte* buffer) 
    // {
    //    eax = level
    //    cpuid
    //    buffer[0] = eax
    //    buffer[4] = ebx
    //    buffer[8] = ecx
    //    buffer[12] = edx
    // }

    private readonly static byte[] x86CodeBytes = {
        0x55,                   // push        ebp  
        0x8B, 0xEC,             // mov         ebp,esp
        0x53,                   // push        ebx  
        0x57,                   // push        edi

        0x8B, 0x45, 0x08,       // mov         eax, dword ptr [ebp+8] (move level into eax)
        0x0F, 0xA2,              // cpuid

        0x8B, 0x7D, 0x0C,       // mov         edi, dword ptr [ebp+12] (move address of buffer into edi)
        0x89, 0x07,             // mov         dword ptr [edi+0], eax  (write eax, ... to buffer)
        0x89, 0x5F, 0x04,       // mov         dword ptr [edi+4], ebx 
        0x89, 0x4F, 0x08,       // mov         dword ptr [edi+8], ecx 
        0x89, 0x57, 0x0C,       // mov         dword ptr [edi+12],edx 

        0x5F,                   // pop         edi  
        0x5B,                   // pop         ebx  
        0x8B, 0xE5,             // mov         esp,ebp  
        0x5D,                   // pop         ebp 
        0xc3                    // ret
    };

    private readonly static byte[] x64CodeBytes = {
        0x53,                       // push rbx    this gets clobbered by cpuid

        // rcx is level
        // rdx is buffer.
        // Need to save buffer elsewhere, cpuid overwrites rdx
        // Put buffer in r8, use r8 to reference buffer later.

        // Save rdx (buffer addy) to r8
        0x49, 0x89, 0xd0,           // mov r8,  rdx

        // Move ecx (level) to eax to call cpuid, call cpuid
        0x89, 0xc8,                 // mov eax, ecx
        0x0F, 0xA2,                 // cpuid

        // Write eax et al to buffer
        0x41, 0x89, 0x40, 0x00,     // mov    dword ptr [r8+0],  eax
        0x41, 0x89, 0x58, 0x04,     // mov    dword ptr [r8+4],  ebx
        0x41, 0x89, 0x48, 0x08,     // mov    dword ptr [r8+8],  ecx
        0x41, 0x89, 0x50, 0x0c,     // mov    dword ptr [r8+12], edx

        0x5b,                       // pop rbx
        0xc3                        // ret
    };
}

могу ли я предложить следующую страницу:http://devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx

эта страница покажет вам исходный код сборки для CPUID, как скомпилировать его в DLL и как вызвать его из C#.

также, если вам нужны другие процедуры идентификации оборудования, могу я предложить эту страницу:http://www.codeproject.com/KB/system/GetHardwareInformation.aspx

эта страница показывает, как получить информацию, как материнская плата информация, информация жесткого диска, процессора, видео карты и т. д. и т. п.


кроме того, чтобы получить CPUID4, необходим еще один аргумент. Вот как вы получаете CPUID0, CPUID1, CPUID2, CPUID4.

byte[] cpuid0 = Invoke(0, 0);
byte[] cpuid1 = Invoke(1, 0);
byte[] cpuid2 = Invoke(2, 0);

List<byte[]> cpuid4L = new List<byte[]>();
for (int i = 0; true; i++)
{
    byte[] cpuid4 = Invoke(4, (uint)i);
    if ( (cpuid4[0] & 0x0F) == 0)
        break;
    cpuid4L.Add(cpuid4);
}

private static byte[] Invoke(uint functionNum, uint ecx)
{
    IntPtr codePointer = IntPtr.Zero;

    try
    {
        // Select a code
        byte[] codeBytes;
        if (IntPtr.Size == 4)
            codeBytes = x86CodeBytes;
        else
            codeBytes = x64CodeBytes;

        codePointer = NativeMethods.VirtualAlloc(IntPtr.Zero, new UIntPtr((uint)codeBytes.Length), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        Marshal.Copy(codeBytes, 0, codePointer, codeBytes.Length);
        CpuIdDelegate cpuIdDelg = (CpuIdDelegate)Marshal.GetDelegateForFunctionPointer(codePointer, typeof(CpuIdDelegate));

        // Invoke the code
        GCHandle handle = default(GCHandle);
        var buffer = new byte[16];

        try
        {
            handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            cpuIdDelg(ecx, functionNum, buffer);  // Run the assembly code.
        }
        finally
        {
            if (handle != default(GCHandle))
            {
                handle.Free();
            }
        }

        return buffer;
    }
    finally
    {
        if (codePointer != IntPtr.Zero)
        {
            NativeMethods.VirtualFree(codePointer, (UIntPtr) 0, MEM_RELEASE);
            codePointer = IntPtr.Zero;
        }
    }
}

private readonly static byte[] x86CodeBytes = {
    0x55,                   
    0x8B, 0xEC,             
    0x53,                   
    0x57,                   
    0x8B, 0x4D, 0x08,
    0x8B, 0x45, 0x0C,
    0x0F, 0xA2,      
    0x8B, 0x7D, 0x10,
    0x89, 0x07,      
    0x89, 0x5F, 0x04,
    0x89, 0x4F, 0x08,
    0x89, 0x57, 0x0C,
    0x5F,                   
    0x5B,                   
    0x8B, 0xE5,             
    0x5D,                   
    0xc3                    
};

private readonly static byte[] x64CodeBytes = {
    0x53,
    0x89, 0xD0,
    0x0F, 0xA2,
    0x41, 0x89, 0x40, 0x00,
    0x41, 0x89, 0x58, 0x04,
    0x41, 0x89, 0x48, 0x08,
    0x41, 0x89, 0x50, 0x0c,
    0x5b,
    0xc3
};

спасибо @antiduh за его решение. Я бы немного изменил подпись Invoke для лучшего удобства использования следующим образом, поэтому вам не нужно выделять get результат как набор регистров

    // This is a modification to https://stackoverflow.com/a/7964376/725903
    [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
    private delegate void CpuIDDelegate(int level, IntPtr ptr);

    [StructLayout(LayoutKind.Sequential, Size = 16)]
    public struct CpuIdResult
    {
        public int Eax;
        public int Ebx;
        public int Ecx;
        public int Edx;
    }

    public CpuIdResult Invoke(int level)
    {
        CpuIdResult result;
        IntPtr buffer = Marshal.AllocHGlobal(16);
        try
        {
            this.cpuIdDelg(level, buffer);
            result = (CpuIdResult)Marshal.PtrToStructure(buffer, typeof(CpuIdResult));
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }
        return result;
    }