X86/x64 CPUID в С#
В связи с моим другим вопросом, пожалуйста, помогите мне отладить "Необработанное исключение типа" System.AccessViolationException "произошло в Неизвестном модуле. Дополнительная информация: Попытка прочитать или это защищенная память. Это часто свидетельствует о том, что другая память повреждена". Выполняя код, все работает до фактического вызова del() и не работает в этой строке.
Этот код основан на этом примере статьи, и этот python код, который работает в python. Я не могу получить пример кода как есть - либо работать (одно и то же исключение), но я надеюсь, что он немного устарел или что-то в этом роде.
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()));
Ответы
Ответ 1
Я уверен, что вы заблокированы DEP. Массивы массива x_CPUIDy_INSNS
находятся в сегменте памяти, помеченном как данные и неисполняемом.
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
}
}
Ответ 2
Я решил улучшить ваш ответ. Ему больше не нужно небезопасно компилировать, и ему нужны только два сборных блока, чтобы иметь возможность считывать любые и все блоки 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;
}
}
}
}
Ответ 3
Я взял код @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
};
}
Ответ 4
Можно ли предложить следующую страницу: http://devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx
На этой странице вы увидите исходный код сборки для CPUID, как скомпилировать его в DLL и как вызвать его из С#.
Также, если вам нужны другие процедуры идентификации оборудования, могу ли я предложить эту страницу: http://www.codeproject.com/KB/system/GetHardwareInformation.aspx
На этой странице показано, как получить информацию, такую как информация о материнской плате, информация о жестком диске, информация о процессоре, информация о видеокарте и т.д. и т.д.
Ответ 5
Кроме того, для получения 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
};
Ответ 6
Спасибо @antiduh за его решение.
Я немного изменил бы подпись Invoke для лучшего удобства использования, поэтому вам не нужно выделять результат как набор регистров
// 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;
}