Skip to content

Instantly share code, notes, and snippets.

@chen3feng
Last active August 8, 2025 07:07
Show Gist options
  • Select an option

  • Save chen3feng/5ed6e09de7b84e99adfd3d54132561c9 to your computer and use it in GitHub Desktop.

Select an option

Save chen3feng/5ed6e09de7b84e99adfd3d54132561c9 to your computer and use it in GitHub Desktop.
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