Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 获取CPU ID的内联程序集代码_C#_.net_Winapi_Assembly_64 Bit - Fatal编程技术网

C# 获取CPU ID的内联程序集代码

C# 获取CPU ID的内联程序集代码,c#,.net,winapi,assembly,64-bit,C#,.net,Winapi,Assembly,64 Bit,我发现了一段很好的代码,它使用API调用执行ASM指令,以获取CPU的序列号: using System; using System.Text; using System.Runtime.InteropServices; namespace ConsoleApplication1 { class Program { [DllImport("user32", EntryPoint = "CallWindowProcW", CharSet = CharSet.Uni

我发现了一段很好的代码,它使用API调用执行ASM指令,以获取CPU的序列号:

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        [DllImport("user32", EntryPoint = "CallWindowProcW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]         private static extern IntPtr ExecuteNativeCode([In] byte[] bytes, IntPtr hWnd, int msg, [In, Out] byte[] wParam, IntPtr lParam);

        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]         public static extern bool VirtualProtect([In] byte[] bytes, IntPtr size, int newProtect, out int oldProtect);

        const int PAGE_EXECUTE_READWRITE = 0x40;

        static void Main(string[] args)
        {
            string s = CPU32_SerialNumber();
            Console.WriteLine("CPU Serial-Number: " + s);
            Console.ReadLine();
        }

        private static string CPU32_SerialNumber()
        {
            byte[] sn = new byte[12];

            if (!ExecuteCode32(ref sn))
                return "ND";

            return string.Format("{0}{1}{2}", BitConverter.ToUInt32(sn, 0).ToString("X"), BitConverter.ToUInt32(sn, 4).ToString("X"), BitConverter.ToUInt32(sn, 8).ToString("X"));
        }

        private static bool ExecuteCode32(ref byte[] result)
        {
            // CPU 32bit SerialNumber -> asm x86 from c# (c) 2003-2011 Cantelmo Software
            // 55               PUSH EBP
            // 8BEC             MOV EBP,ESP
            // 8B7D 10          MOV EDI,DWORD PTR SS:[EBP+10]
            // 6A 02            PUSH 2
            // 58               POP EAX
            // 0FA2             CPUID
            // 891F             MOV DWORD PTR DS:[EDI],EBX
            // 894F 04          MOV DWORD PTR DS:[EDI+4],ECX
            // 8957 08          MOV DWORD PTR DS:[EDI+8],EDX
            // 8BE5             MOV ESP,EBP
            // 5D               POP EBP
            // C2 1000          RETN 10

            int num;

            byte[] code_32bit = new byte[] { 0x55, 0x8b, 0xec, 0x8b, 0x7d, 0x10, 0x6a, 2, 0x58, 15, 0xa2, 0x89, 0x1f, 0x89, 0x4f, 4, 0x89, 0x57, 8, 0x8b, 0xe5, 0x5d, 0xc2, 0x10, 0 };
            IntPtr ptr = new IntPtr(code_32bit.Length);

            if (!VirtualProtect(code_32bit, ptr, PAGE_EXECUTE_READWRITE, out num))
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

            ptr = new IntPtr(result.Length);

            return (ExecuteNativeCode(code_32bit, IntPtr.Zero, 0, result, ptr) != IntPtr.Zero);
        }
    }
}
我测试过了,对我来说效果很好。但我仍然有一些与之相关的问题:

1) 我想在一个可以在x86和x64环境中运行的应用程序中实现这段代码。如果在64x环境中运行此代码,则会得到AccessViolationException。代码的作者说,通过实现包含x64指令(RAX、RBX、RCX、RDX等)的字节码数组,可以很容易地实现这一点。我的问题是,我完全不知道如何将86x字节码转换成x64字节码,事实上我甚至不知道ASM。是否有任何转换表或实用程序可以这样做

2) 此代码段对任何类型的处理器都有效吗?我在使用英特尔内核的笔记本电脑上测试了它,它可以工作。。。但以AMD为例呢

3) 我不确定我得到的值是否正确。如果我运行以下代码:

string cpuInfo = String.Empty;

System.Management.ManagementClass mc = new System.Management.ManagementClass("Win32_Processor");
System.Management.ManagementObjectCollection moc = mc.GetInstances();

foreach (System.Management.ManagementObject mo in moc)
{
    if (cpuInfo == String.Empty)
        cpuInfo = mo.Properties["ProcessorId"].Value.ToString();
}

我得到的结果是“BFEBFBFF000306A9”。代码片段的结果是“F0B2FF0CA0000”。为什么?哪一个是正确的?

您发布的代码似乎调用了
CPUID
函数#2(由
EAX
寄存器给出,在
PUSH 2;POP EAX
之后)。根据不用于查询序列号的英特尔指令集参考:

当CPUID在EAX设置为2的情况下执行时,处理器返回 有关处理器内部TLB、缓存和预取的信息 EAX、EBX、ECX和EDX寄存器中的硬件


另请注意,此功能在AMD处理器上不可用,但代码执行时应无错误。

以下是修改后的代码,以获得与x64和x86上相同的结果:

我在没有编译代码的情况下对C#部分进行了微不足道的修改(我目前没有Windows开发机器设置),因此如果出现语法错误,请进行明显的修复

我想强调一个非常重要的点:您最初读取的代码不是CPU序列号:

cpuid_opcodes = [ 0x55, 0x8b, 0xec, 0x8b, ... ]
open('cpuid-x86.bin', 'w').write(''.join(chr(x) for x in cpuid_opcodes))
#include <stdio.h>
#include <stdint.h>

void __stdcall cpuid_wind_proc(uint32_t hWnd, uint32_t msg, uint8_t *wparam, uint32_t lparam);

enum {
    RESULT_SIZE = 2 * 4, /* Two 32-bit registers: EAX, EDX */
};

static unsigned int form_word_le(uint8_t a[])
{
    return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
}

int main()
{
    uint8_t r[RESULT_SIZE];
    memset(r, 0, sizeof(r));

    cpuid_wind_proc(0, 0, r, 0);

    printf("%08x%08x\n",  form_word_le(r + 4), form_word_le(r));
    return 0;
}
Set objProc = GetObject("winmgmts:root\cimv2:Win32_Processor='cpu0'")
WScript.echo objProc.ProcessorId
  • 您使用了CPUID函数2(在执行CPUID指令之前,将2放入EAX)。如果您阅读了和CPUID应用程序说明,您将看到这将读回缓存和TLB硬件配置,并且仅在Intel上受支持
  • 我修改了您的代码以使用CPUID函数1,它读回CPU的步进、型号和系列。这与
  • 现代x86 CPU没有一个序列号,在“从装配线滚出”的其他相同单元中是唯一的。处理器序列号仅通过CPUID函数3在奔腾3上可用
现在我将解释我使用的过程和工具

将操作码数组粘贴到Python脚本中,然后将操作码写入二进制文件(cpuid-x86.bin):

拆解cpuid-x86.bin。我使用了来自的udcli

一件很明显的事情是,当一个简单的“mov$0x2,%eax”就可以了时,为什么要使用“push$0x2;pop%eax”将值2移动到eax

我的猜测是“push$0x2”的指令编码,6a02,更容易以十六进制形式修改。手动和编程两种方式。我猜某个地方有人试图使用CPUID函数3获取处理器序列号,但发现它不受支持,然后切换到使用函数2

结尾的“ret$0x10”也不常见。RET指令的RET IMM16形式返回调用方,然后从堆栈中弹出IMM16字节。被调用方负责在函数返回后从堆栈中弹出参数,这意味着这不是使用标准x86调用约定

事实上,快速浏览一下C#代码就会发现它正在使用CallWindowProc()调用汇编函数。的文档显示汇编代码正在实现一个C函数,其签名如下:

__stdcall CpuIdWindowProc(hWnd, Msg, wParam, lParam);
是32位Windows API使用的特殊函数调用约定

汇编代码使用函数的第三个参数0x10(%ebp)作为字符数组来存储CPUID指令的输出。(在x86上的标准函数序言之后,8(%ebp)是第一个参数。0xc(%ebp)是第二个4字节参数,0x10(%ebp)是第三个参数)上面的窗口过程函数原型中的第三个参数是wParam。它用作out参数,是汇编代码中使用的唯一参数

关于汇编代码的最后一件有趣的事情是,它在不保存寄存器的情况下对寄存器EDIEBX进行了重击,违反了\uu stdcall调用约定。当通过CallWindowProc()调用函数时,此错误显然是潜在的,但如果您试图用C编写自己的主函数来测试汇编代码(cpuid main.C),则会暴露出来:

符号名称\u cpuid\u wind_proc@16是32位窗口上函数名被损坏的方式。@16是参数占用的字节数。(在32位窗口上,四个参数各占用四个字节,总计16个)

现在我准备将代码移植到x64

  • 通过查阅,我发现前四个参数在RCXRDXR8R9中传递,因此wParamR8
  • 英特尔文档告诉我,CPUID指令
    #include <stdio.h>
    #include <stdint.h>
    
    void __stdcall cpuid_wind_proc(uint32_t hWnd, uint32_t msg, uint8_t *wparam, uint32_t lparam);
    
    enum {
        RESULT_SIZE = 2 * 4, /* Two 32-bit registers: EAX, EDX */
    };
    
    static unsigned int form_word_le(uint8_t a[])
    {
        return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
    }
    
    int main()
    {
        uint8_t r[RESULT_SIZE];
        memset(r, 0, sizeof(r));
    
        cpuid_wind_proc(0, 0, r, 0);
    
        printf("%08x%08x\n",  form_word_le(r + 4), form_word_le(r));
        return 0;
    }
    
        .section .text
        .global _cpuid_wind_proc@16
    _cpuid_wind_proc@16:
        push %ebp
        mov %esp, %ebp
        push %edi
        mov 16(%ebp), %edi
        push $1
        pop %eax
        push %ebx
        cpuid
        mov %eax, (%edi)
        mov %edx, 0x4(%edi)
        pop %ebx
        pop %edi
        mov %ebp, %esp
        pop %ebp
        ret $16
    
        .section .text
        .global cpuid_wind_proc
    cpuid_wind_proc:
        push %rbx
        mov $1, %rax
        cpuid
        movl %eax, (%r8)
        movl %edx, 4(%r8)
        pop %rbx
        ret
    
    Set objProc = GetObject("winmgmts:root\cimv2:Win32_Processor='cpu0'")
    WScript.echo objProc.ProcessorId
    
    wscript cpuid.vbs
    
    string.Format("{0}{1}", BitConverter.ToUInt32(sn, 4).ToString("X8"), BitConverter.ToUInt32(sn, 0).ToString("X8"));