OutFile "cpuid.exe"
!include "LogicLib.nsh"
; cpuid
; Executes CPUID instruction with the query code [EAX value] in $1
; Params: query code [EAX value] in $1
; Returns: 0 in $0 if function succeeded and the results of CPUID
; in $1 [EAX], $2 [EBX], $3 [ECX] and $4 [EDX]
; non-zero value in $0 if CPUID instruction is not available.
Function cpuid
; int cpuid(unsigned int query_code, unsigned int regs[4])
; cpuid:
; 0: 55 push ebp
; 1: 89 e5 mov ebp,esp
; 3: 53 push ebx
;
; ; CPUID instruction available?
; ; yes if ID flag (= EFLAGS:21) can be modified
;
; 4: 9c pushfd
; 5: 58 pop eax
; 6: 89 c1 mov ecx,eax
; 8: 35 00 00 20 00 xor eax,$0x200000
; d: 50 push eax
; e: 9d popf
; f: 9c pushf
; 10: 58 pop eax
; 11: 39 c8 cmp eax,ecx
; 13: 74 17 je 2c <cpuid_end>
;
; ; Execute CPUID instruction with the passed query code
; ; and store the results in the integer array
;
; 15: 56 push esi
; 16: 8b 45 08 mov eax,[ebp+8]
; 19: 0f a2 cpuid
; 1b: 8b 75 0c mov esi,dword ptr [ebp+12]
; 1e: 89 06 mov dword ptr [esi],eax
; 20: 89 5e 04 mov dword ptr [esi+4],ebx
; 23: 89 4e 08 mov dword ptr [esi+8],ecx
; 26: 89 56 0c mov dword ptr [esi+12],edx
; 29: 5e pop esi
; 2a: 31 c0 xor eax,eax
;
;
; cpuid_end:
; 2c: 5b pop ebx
; 2d: 5d pop ebp
; 2e: c3 ret
; 2f: 90 nop
;
; Little endian => combine 4 bytes from last to first one to an integer
;
Push $5
Push $6
; cpuid function in x86 machine code
; (System::Call supports a maximum of 100 parameters)
System::Call "*( \\
i 0x53e58955, \\
i 0xc189589c, \\
i 0x20000035, \\
i 0x9c9d5000, \\
i 0x74c83958, \\
i 0x458b5617, \\
i 0x8ba20f08, \\
i 0x06890c75, \\
i 0x89045e89, \\
i 0x5689084e, \\
i 0xc0315e0c, \\
i 0x90c35d5b \\
) i.r5"
; Allocate integer array
System::Call "*(i 0, i 0, i 0, i 0) i.r6"
; Execute cpuid machine code
System::Call "::$5(i r1, i r6) i.r0"
; Read data from integer array
System::Call "*$6(i .r1, i .r2, i .r3, i .r4)"
; Free previously allocated memory
System::Free $6
System::Free $5
Pop $6
Pop $5
FunctionEnd
; Get Vendor Id via CPUID instruction
; Returns Vendor Id in $0
Function VendorId
Push $1
Push $2
Push $3
Push $4
Push $5
Push 0
Pop $1
Call cpuid
${If} $0 != 0
StrCpy $0 "CPUID instruction not available!"
${Else}
; Concatenate vendor string: 12 character ASCII string stored in EBX, EDX and ECX
System::Call "*(i r2, i r4, i r3, i 0) i.r5"
System::Call "*$5(&m13 .r0)"
System::Free $5
${EndIf}
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
FunctionEnd
; Determine if CPU supports 64-bit address space
; Returns "64-bit" or "32-bit" in $0
Function Is64bit
Push $1
Push $2
Push $3
Push $4
; 32-bit by default
Push "32-bit"
Push 0
Pop $1
Call cpuid
${If} $0 == 0
${AndIf} $1 != 0
; Get Highest Extended Function Supported
Push 0x80000000
Pop $1
Call cpuid
${If} $0 == 0
${AndIf} $1 > 0x80000000
; Extended Processor Info and Feature Bits
Push 0x80000001
Pop $1
Call cpuid
${If} $0 == 0
; Check long mode bit (EDX:29)
IntOp $4 $4 & 0x20000000
${If} $4 != 0
Pop $0
Push "64-bit"
${EndIf}
${EndIf}
${EndIf}
${EndIf}
; Return value
Pop $0
Pop $4
Pop $3
Pop $2
Pop $1
FunctionEnd
Function .onInit
InitPluginsDir
Call VendorId
StrCpy $1 $0
Call Is64bit
MessageBox MB_OK "Vendor=$1 [$0 capable]"
Quit
FunctionEnd
Section
SectionEnd Proof of concept: Detect 64-bit capable CPU - execute CPUID opcode from System plugin
WARNING: The following script should merely illustrate that it is possible to execute x86 machine code embedded in the script via the System plugin. It is likely to fail if the page containing the embedded machine code is flagged as non-executable. So the better approach would be to write a dedicated plugin.