C# 中的 x86/x64 CPUID
与我的其他问题相关,请帮忙me debug“未知模块中发生了类型为‘System.AccessViolationException’的未处理异常。其他信息:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”单步执行代码,一切正常,直到实际调用 del() 并在该行失败。
此代码基于此文章示例< /a> 和这个在 python 中工作的 python 代码 。我也无法让代码示例按原样工作(同样的例外),但我希望它只是有点过时或其他什么。
编辑:如果您关心我们如何到达这里,请查看编辑历史记录,这很无趣。
完成的工作版本:
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()));
Related to my other question, please help me debug "An unhandled exception of type 'System.AccessViolationException' occurred in Unknown Module. Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt." Stepping through the code, everything works up until the actual call to del() and fails in that line.
This code is based on this article's sample and this python code which works in python. I can't get the code example as-is to work either (same exception), but I'm hopeful that it's just a little outdated or something.
EDIT: See the edit history if you care about how we got here, which is uninteresting.
Finished working version:
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
};
}
Note that CPUID0 needs to be read in the right order:
//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()));
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我相当确定您被 DEP 阻止。 x_CPUIDy_INSNS 字节数组位于标记为数据且不可执行的内存段中。
编辑:
话虽这么说,我已经得到了一个可以编译和运行的版本,但我认为没有获得正确的值。也许这会让你一路走好。
编辑2:
我想我现在已经恢复了正确的价值观。请随意验证。
I'm fairly certain you're being blocked by DEP. The
x_CPUIDy_INSNS
byte arrays are in a segment of memory marked as data and non-executable.EDIT:
That being said, I've gotten a version that compiles and runs, but I don't think gets the right values. Perhaps this will get you along your way.
EDIT 2:
I think I have the right values coming back now. Feel free to validate.
我决定改进你的答案。它不再需要 unsafe 来编译,并且只需要两个汇编块就可以读出任何和所有 cpuid 块,因为它只是将 eax/ebx/ecx/edx 写入 16 字节字节数组。
I decided to improve your answer. It does not need unsafe to compile anymore, and it only needs two assembly blocks in order to be able to read out any and all cpuid blocks, because it just writes eax/ebx/ecx/edx to a 16-byte byte array.
我采用了 @antiduh 的代码并将其重构为静态方法,因此无需管理对象生命周期。这比较慢,因为 ASM 代码在调用 Invoke() 之间不会重用,但速度与简单性的权衡对于我的用例来说是有意义的。这个新版本在我的机器上可以在 15 毫秒内调用 CPUID 1000 次。
感谢各位提供的精彩代码!
I took @antiduh's code and refactored it into a static method, so there is no object lifecycle to manage. This is slower because the ASM code isn't reused between calls to Invoke(), but the tradeoff of speed for simplicity makes sense for my use case. This new version can call CPUID 1000 times in 15 milliseconds on my machine.
Thanks for the wonderful code guys!
我知道这个线程很旧,但我非常喜欢这个线程。
编码后,我发现在“EAX=7,ECX=0”时获取数据存在问题
所以,我在 x64CodeBytes 中添加“mov ecx, 0”。
i know this thread is old, but i like this thread very much.
after coding, I figure out there is problem to get data while "EAX=7,ECX=0"
so, I add "mov ecx, 0" in x64CodeBytes.
在 .NET 5 中,现在有一个内置的内在函数:https://learn.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.x86base.cpuid?view=net-5.0< /a>
With .NET 5, there is now a built-in intrinsic for this: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.x86.x86base.cpuid?view=net-5.0
我可以建议以下页面:http://community.devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx
该页面将向您展示 CPUID 的汇编源代码,如何将其编译为 DLL 以及如何从 C# 调用它。
另外,如果您需要其他硬件识别程序,我可以建议您访问此页面: http://www .codeproject.com/KB/system/GetHardwareInformation.aspx
此页面显示如何获取主板信息、硬盘信息、CPU 信息、显卡信息等信息。
Can I suggest the following page: http://community.devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx
This page will show you the assembly source code for CPUID, how to compile it into a DLL and how to call it from C#.
Also if you need other hardware identification procedures, may I suggest this page: http://www.codeproject.com/KB/system/GetHardwareInformation.aspx
This page shows how to get information like motherboard info, hard drive info, cpu info, video card info, etc etc.
此外,要获取 CPUID4,还需要一个参数。
以下是获取 CPUID0、CPUID1、CPUID2、CPUID4 的方法。
Additionally, to obtain CPUID4, one more argument is needed.
Here is how you get CPUID0, CPUID1, CPUID2, CPUID4.
感谢@antiduh 的解决方案。
我会稍微更改 Invoke 签名以获得更好的可用性,如下所示,因此您不需要将获取结果分配为一组寄存器
Thanks to @antiduh for his solution.
I'd change the Invoke signature a little bit for better usability as follows, so you don't need to allocate an get the result as a set of registers