The x86 and x86-64 instruction sets have an instruction called CPUID
that tells the program who made the CPU and what features it may have.
We try to get that info using x86-64
assembly in this tutorial.
When we set EAX
to 0, and call the CPUID
, instruction, the processor
fills the ECX, EBX, EDX
registers with parts of the string
containing the name of the processor, e.g. AuthenticAMD
or GenuineIntel
. The PUSH
instruction on the x86-64 instruction set pushes the 64-bit versions of the registers. We need to concatenate
the strings in the 3 registers and print them on screen, using
the write()
system call.
So what we do is shift the lower 32-bit part of RDX
(i.e. EDX
) to the upper 32-bit part of
RDX
and move the lower 32-bit part of RBX
(i.e. EBX
) into the
lower 32-bit part of RDX
, thus placing the two of the parts of the string into the RDX
register.
The remaining part of the string is in the lower 32-bit
part of RCX
, which when gets pushed connects to the
remaining string in the RDX
register. Then we push both the registers on the stack, and then
call the write()
system call.
For doing a write()
system call, we place the number of the system
call as given in unistd.h
into RAX
and then place the arguments
into RDI, RSI & RDX
registers. RDI
is the file descriptor where we want to
write to. RSI
holds the address of the string and RDX
holds the
length of the string. If the value of RDI
is 0x1
, the string obtained from the CPUID
call will be printed on screen (stdout
).
We then do an exit()
system call to exit the program. This program
does not use the C library and uses the kernel interface directly.
Hence we do not have to define a main()
function. We can directly
use the _start
symbol that the operating system uses to invoke every
application. We have to declare it global
so that it is noted as a
symbol in the application’s final binary that has been created and
can be called by the operating system.
section .text
global _start
_start:
xor eax,eax ; place 0x0 in EAX for getting the name of the processor
cpuid
shl rdx,0x20 ; shifting lower 32-bits into upper 32-bit of RDX
xor rdx,rbx ; moving EBX into EDX
push rcx ; push the string on the stack
push rdx
mov rdx, 0x10 ; since we are pushing 2 registers, the length is not more than 16 bytes.
mov rsi, rsp ; The address of the string is RSP because the string is on the stack
push 0x1 ; The system call write() has the value 0x1 in the sytem call table
pop rax
mov rdi, rax ; Since we are printing to stdout, the value of the file descriptor is also 0x1
syscall ; make the system call
mov rax, 0x3c ; We now make the exit() system call here.
xor rdi, rdi ; the argument is 0x0
syscall ; this exits the application and gives control back to the shell or the Operating system
Download cpuid.asm.
The command to compile the above code is as follows:
$ yasm -f elf64 cpuid.asm
$ ld -o cpuid.out cpuid.o