Last active
August 8, 2025 07:07
-
-
Save chen3feng/5ed6e09de7b84e99adfd3d54132561c9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ctypes | |
| import struct | |
| import sys | |
| from ctypes import wintypes | |
| def cpuid(leaf: int, subleaf: int = 0) -> tuple[int, int, int, int]: | |
| # Define VirtualAlloc and VirtualFree | |
| kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) | |
| VirtualAlloc = kernel32.VirtualAlloc | |
| VirtualAlloc.restype = wintypes.LPVOID | |
| VirtualAlloc.argtypes = (wintypes.LPVOID, ctypes.c_size_t, wintypes.DWORD, wintypes.DWORD) | |
| VirtualFree = kernel32.VirtualFree | |
| VirtualFree.restype = wintypes.BOOL | |
| VirtualFree.argtypes = (wintypes.LPVOID, ctypes.c_size_t, wintypes.DWORD) | |
| # Constants | |
| MEM_COMMIT = 0x1000 | |
| MEM_RESERVE = 0x2000 | |
| PAGE_EXECUTE_READWRITE = 0x40 | |
| MEM_RELEASE = 0x8000 | |
| # x86-64 cpuid machine code | |
| # See https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention | |
| machine_code = bytearray([ | |
| 0x8B, 0xC1, # mov eax, ecx ; EAX = leaf | |
| 0x8B, 0xCA, # mov ecx, edx ; ECX = subleaf | |
| 0x0F, 0xA2, # cpuid | |
| 0x41, 0x89, 0x00, # mov [r8], eax | |
| 0x41, 0x89, 0x58, 0x04, # mov [r8+4], ebx | |
| 0x41, 0x89, 0x48, 0x08, # mov [r8+8], ecx | |
| 0x41, 0x89, 0x50, 0x0C, # mov [r8+12], edx | |
| 0xC3 # ret | |
| ]) | |
| size = len(machine_code) | |
| # Allocate executable memory. | |
| ptr = VirtualAlloc(None, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE) | |
| if not ptr: | |
| raise ctypes.WinError(ctypes.get_last_error()) | |
| addr = ctypes.addressof(ctypes.c_char.from_buffer(machine_code)) | |
| # Copy machine code. | |
| ctypes.memmove(ptr, addr, size) | |
| FUNC_TYPE = ctypes.CFUNCTYPE(None, ctypes.c_uint32, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32)) | |
| func = FUNC_TYPE(ptr) | |
| out = (ctypes.c_uint32 * 4)() | |
| func(leaf, subleaf, out) | |
| if not VirtualFree(ptr, 0, MEM_RELEASE): | |
| raise ctypes.WinError(ctypes.get_last_error()) | |
| return out[0], out[1], out[2], out[3] | |
| def get_vendor(): | |
| eax, ebx, ecx, edx = cpuid(0, 0) | |
| vendor = struct.pack("<III", ebx, edx, ecx).decode() | |
| return vendor, eax # 返回厂商字符串和最大 leaf | |
| def get_brand(): | |
| brand = b"" | |
| for leaf in (0x80000002, 0x80000003, 0x80000004): | |
| regs = cpuid(leaf, 0) | |
| brand += struct.pack("<IIII", *regs) | |
| return brand.decode().strip() | |
| def get_basic_features(): | |
| eax, ebx, ecx, edx = cpuid(1, 0) | |
| stepping = eax & 0xF | |
| model = (eax >> 4) & 0xF | |
| family = (eax >> 8) & 0xF | |
| ext_model = (eax >> 16) & 0xF | |
| ext_family = (eax >> 20) & 0xFF | |
| if family == 0xF: | |
| family += ext_family | |
| if family == 0x6 or family == 0xF: | |
| model += (ext_model << 4) | |
| return { | |
| "stepping": stepping, | |
| "model": model, | |
| "family": family, | |
| "ecx_flags": ecx, | |
| "edx_flags": edx | |
| } | |
| def get_extended_features(): | |
| # Leaf 7, subleaf 0 | |
| eax, ebx, ecx, edx = cpuid(7, 0) | |
| return {"eax": eax, "ebx": ebx, "ecx": ecx, "edx": edx} | |
| def get_amd_extra(): | |
| # Leaf 0x80000001 | |
| eax, ebx, ecx, edx = cpuid(0x80000001, 0) | |
| return {"ecx": ecx, "edx": edx} | |
| def dump_flags(): | |
| # 这里只列出部分常见 flags 位(低位到高位) | |
| flag_bits = { | |
| # edx flags from leaf 1 | |
| "edx": [ | |
| (0, "fpu"), (1, "vme"), (2, "de"), (3, "pse"), | |
| (4, "tsc"), (5, "msr"), (6, "pae"), (7, "mce"), | |
| (8, "cx8"), (9, "apic"), (11, "sep"), (12, "mtrr"), | |
| (13, "pge"), (14, "mca"), (15, "cmov"), (16, "pat"), | |
| (17, "pse36"), (19, "clflush"), (23, "mmx"), (24, "fxsr"), | |
| (25, "sse"), (26, "sse2"), (28, "ht") | |
| ], | |
| # ecx flags from leaf 1 | |
| "ecx": [ | |
| (0, "sse3"), (1, "pclmulqdq"), (9, "ssse3"), (12, "fma"), | |
| (13, "cx16"), (19, "sse4_1"), (20, "sse4_2"), | |
| (22, "movbe"), (23, "popcnt"), (25, "aes"), | |
| (26, "xsave"), (27, "osxsave"), (28, "avx"), | |
| (29, "f16c"), (30, "rdrand") | |
| ] | |
| } | |
| return flag_bits | |
| def query_all(): | |
| vendor, max_leaf = get_vendor() | |
| brand = get_brand() | |
| basic = get_basic_features() | |
| ext = get_extended_features() | |
| amd_ext = get_amd_extra() | |
| print(f"Vendor: {vendor}") | |
| print(f"Model name: {brand}") | |
| print(f"Family: {basic['family']}, Model: {basic['model']}, Stepping: {basic['stepping']}") | |
| print(f"Max basic leaf: {max_leaf:#x}") | |
| # 解析 flags | |
| flag_map = dump_flags() | |
| all_flags = [] | |
| for bit, name in flag_map["edx"]: | |
| if (basic["edx_flags"] >> bit) & 1: | |
| all_flags.append(name) | |
| for bit, name in flag_map["ecx"]: | |
| if (basic["ecx_flags"] >> bit) & 1: | |
| all_flags.append(name) | |
| print("Flags:", " ".join(sorted(all_flags))) | |
| print("Leaf 7 (ext features):", ext) | |
| print("AMD extra features (0x80000001):", amd_ext) | |
| def main(): | |
| if len(sys.argv) == 1: | |
| return query_all() | |
| leaf, subleaf = 0, 0 | |
| if len(sys.argv) > 1: | |
| leaf = int(sys.argv[1]) | |
| if len(sys.argv) > 2: | |
| subleaf = int(sys.argv[2]) | |
| eax, ebx, ecx, edx = cpuid(leaf, subleaf) | |
| print(f"CPUID({leaf}, {subleaf}): EAX={eax:08x} EBX={ebx:08x} ECX={ecx:08x} EDX={edx:08x}") | |
| if leaf == 0: | |
| vendor_id = ( | |
| ebx.to_bytes(4, 'little') + | |
| edx.to_bytes(4, 'little') + | |
| ecx.to_bytes(4, 'little') | |
| ).decode('ascii', errors='ignore') | |
| print("Vendor ID:", vendor_id) | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment