flat assembler
Message board for the users of flat assembler.

flat assembler > Windows > Calling UEFI from ms win

Thread Post new topic Reply to topic

Joined: 23 Mar 2005
Posts: 445
Location: Czech republic, Slovak republic
I created some samples which call uefi from ms win. It is possible from ring3 applications using GetFirmwareEnvironmentVariable/SetFirmwareEnvironmentVariable
If the system is UEFI based but the OS is booted via BIOS emulation the system calls fail (return 0).

By my opinion here is a dangerous security hole - it is possible to write malicious bootloader into e.g. removable media like USB stick (into something like EFI\boot\bootx64.efi), write new Bootxxxx variable, change BootOrder so newly created Bootxxxx is the first in boot order and reboot PC...
Anyway turning ON Secure Boot (if it is available) closes this security hole because preventing to load unsigned bootloaders (as it also complicates life for Linux UEFI bootloaders, time will show whether Secure Boot becomes widespread)

here the sample which obtains boot order
format PE64 GUI at (1 shl 32) on 'nul'

include '%fasminc%\win64a.inc'
include 'WinNT.h.inc'

section '.code' code readable executable

entry $

    push    rbx rbp
     sub     rsp,8*(4+11)

    lea     r8,[rsp+8*(4+10)]
   mov     edx,MAXIMUM_ALLOWED             ; desired access
;   mov     edx,TOKEN_ADJUST_PRIVILEGES     ; desired access
    or      rcx,-1                          ; hCurrentProcess
   call    [OpenProcessToken]
  or      eax,eax
     jz      exit

    pxor    xmm0,xmm0
   movdqa  [rsp+8*(4+0)],xmm0              ; 5th and 6th param
 xor     r9,r9
       lea     r8,[rsp+8*(4+2)]
virtual at r8
end virtual
     mov     [tp.PrivilegeCount],1
       mov     [tp.Privileges.Luid.usedpart],SE_SYSTEM_ENVIRONMENT_PRIVILEGE
       and     [tp.Privileges.Luid.ignorehigh32bitpart],0
  mov     [tp.Privileges.Attributes],SE_PRIVILEGE_ENABLED
     xor     edx,edx
     mov     rcx,[rsp+8*(4+10)]
  call    [AdjustTokenPrivileges]
     mov     ebx,eax

 mov     rcx,[rsp+8*(4+10)]
  call    [CloseHandle]

   or      ebx,ebx                 ; AdjustTokenPrivileges success?
    jz      exit

    lea     rcx,[kernel_name]
   call    [LoadLibraryA]
      or      rax,rax
     jz      exit

    lea     rdx,[_GetFirmwareEnvironmentVariableA]
      mov     rcx,rax
     call    [GetProcAddress]
    or      rax,rax
     jz      exit

    mov     rbp,rax

; DWORD WINAPI GetFirmwareEnvironmentVariable(
;   __in   LPCTSTR lpName,
;   __in   LPCTSTR lpGuid,
;   __out  PVOID pBuffer,
;   __in   DWORD nSize
; );
; Parameters
; lpName [in]
; The name of the firmware environment variable. The pointer must not be NULL.
; lpGuid [in]
; The GUID that represents the namespace of the firmware environment variable. The GUID must be a string in the format "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where 'x' represents a hexadecimal value. 
; pBuffer [out]
; A pointer to a buffer that receives the value of the specified firmware environment variable.
; nSize [in]
; The size of the pBuffer buffer, in bytes.
; Return value
; If the function succeeds, the return value is the number of bytes stored in the pBuffer buffer.
; If the function fails, the return value is zero. To get extended error information, call {GetLastError}. Possible error codes include ERROR_INVALID_FUNCTION.
; On a legacy BIOS-based system, or on a system that supports both legacy BIOS and UEFI where Windows was installed using legacy BIOS, the function will fail with ERROR_INVALID_FUNCTION.
; On a UEFI-based system, the function will fail with an error specific to the firmware, such as ERROR_NOACCESS, to indicate that the dummy GUID namespace does not exist.

     mov     r9d,variable_buffer_max_size
        lea     r8,[variable_buffer]
        lea     rdx,[EFI_GLOBAL_VARIABLE_Vendor_GUID]
       lea     rcx,[EfiVariableName]
       call    rbp
 or      eax,eax
     jz      exit

; eax = size of variable

    lea     r11,[variable_buffer]
       lea     r10,[show_buf]

  movdqa  xmm3,dqword [Sum1]
  movdqa  xmm4,dqword [Comp1]
 movdqa  xmm2,dqword [Mask1]
 movdqa  xmm5,dqword [Num1]
  mov     dl,'.'

L0: movq    xmm0,[r11]
  movdqa  xmm1,xmm0
   psrlq   xmm0,4
      pand    xmm0,xmm2
   pand    xmm1,xmm2
   punpcklbw       xmm0,xmm1
   movdqa  xmm1,xmm0
   pcmpgtb xmm0,xmm4
   pand    xmm0,xmm5
   paddb   xmm1,xmm3
   paddb   xmm1,xmm0
   movdqu  dqword [r10],xmm1

       mov     byte [r10+16],' '
 movq    xmm0,[r11+8]
        movdqa  xmm1,xmm0
   psrlq   xmm0,4
      pand    xmm0,xmm2
   pand    xmm1,xmm2
   punpcklbw       xmm0,xmm1
   movdqa  xmm1,xmm0
   pcmpgtb xmm0,xmm4
   pand    xmm0,xmm5
   paddb   xmm1,xmm3
   paddb   xmm1,xmm0
   movdqu  dqword [r10+16+1],xmm1

  add     r10,16+1+16
 mov     byte [r10],' '
    inc     r10

repeat 16
        mov     cl,[r11]
    inc     r11
;        or      cl,cl
;      cmovs   ecx,edx
; better to exclude 7Fh also
     cmp     cl,7Fh
      cmovnc  ecx,edx
     cmp     cl,' '
    cmovc   ecx,edx
     mov     [r10],cl
    inc     r10
end repeat

       mov     word [r10],0A0Dh
    add     r10,2

   sub     eax,16
      jnbe    L0

if MB_OK = 0
      xor     r9d,r9d
     mov     r9d,MB_OK
end if
 lea     r8,[EfiVariableName]
        lea     rdx,[show_buf]
      xor     ecx,ecx
     call    [MessageBoxA]

exit:  xor     ecx,ecx
     call    [ExitProcess]

   add     rsp,8*(4+11)
        pop     rbp rbx
     xor     eax,eax

section '.data' data readable writeable
align 10h
Sum1                   dq      3030303030303030h, 3030303030303030h
Mask1                   dq      0f0f0f0f0f0f0f0fh, 0f0f0f0f0f0f0f0fh
Comp1                   dq      0909090909090909h, 0909090909090909h
Num1                    dq      0707070707070707h, 0707070707070707h

align 10h
EFI_GLOBAL_VARIABLE_Vendor_GUID        db      '{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}',0

; N.B. boot into UEFI Shell and use command:
; dumpstore -b
; look into UEFI spec, chapter 3, Table 10. Global Variables

align 10h
EfiVariableName            db      'BootOrder',0
; db 'Boot0000',0
; db 'ConOut',0

; this is maybe my ASUS vendor:
; {4599D26F-1A11-49B8-B91F-858745CFF824}, 'StdDefaults',0

align 10h
_GetFirmwareEnvironmentVariableA        db      'GetFirmwareEnvironmentVariableA',0

section '.bss' data readable writeable
variable_buffer_max_size       =       1000h
variable_buffer                        rb      variable_buffer_max_size

show_buf                        rb      variable_buffer_max_size*7

section '.idata' import data readable writeable

                             dd      0,0,0,RVA kernel_name,  RVA kernel_table
                            dd      0,0,0,RVA user_name,    RVA user_table
                              dd      0,0,0,RVA advapi_name,  RVA advapi_table
                            dd      0,0,0,0,0

CloseHandle                       dq      RVA _CloseHandle
ExitProcess                 dq      RVA _ExitProcess
GetProcAddress                      dq      RVA _GetProcAddress
LoadLibraryA                     dq      RVA _LoadLibraryA
                           dq      0

MessageBoxA                 dq      RVA _MessageBoxA
                            dq      0

AdjustTokenPrivileges             dq      RVA _AdjustTokenPrivileges
OpenProcessToken          dq      RVA _OpenProcessToken
                               dq      0

kernel_name                    db      'KERNEL32.DLL',0
user_name                 db      'USER32.DLL',0
advapi_name                 db      'ADVAPI32.DLL',0

; kernel32.dll:
_CloseHandle                   db      0,0,'CloseHandle',0
_ExitProcess                   db      0,0,'ExitProcess',0
_GetProcAddress                        db      0,0,'GetProcAddress',0
_LoadLibraryA                       db      0,0,'LoadLibraryA',0

; user32.dll
_MessageBoxA                      db      0,0,'MessageBoxA',0

; advapi32.dll
_AdjustTokenPrivileges           db      0,0,'AdjustTokenPrivileges',0
_OpenProcessToken            db      0,0,'OpenProcessToken',0    

the first word reported by boot order is the first bootxxxx when booting (it is usually Boot0000, but it is possible to change it)
then change this:
EfiVariableName             db      'BootOrder',0    

to this:
EfiVariableName           db      'Boot0000',0    
or what is reported from BootOrder (may be e.g. Boot0001, Boot0002, ...)
recompile and run the app again
you will see where is the ms win bootloader (ms creates hidden FAT32 partition for its bootloader, the path and file should be \efi\Microsoft\Boot\bootmgfw.efi)
advanced malware may be written into this hidden partition but it requires more effort than USB stick method with CreateFile\WriteFile\CloseHandle (raw access to disk e.g. using \\.\PhysicalDrive0' and reading/writing FAT32 structures of hidden partition by hand)

include file required to compile the above sample:
; Privilege attributes

SE_PRIVILEGE_ENABLED               =       00000002h
SE_PRIVILEGE_REMOVED               =       00000004h
SE_PRIVILEGE_USED_FOR_ACCESS       =       80000000h

; Privilege Set Control flags


;typedef struct _PRIVILEGE_SET {
;    ULONG PrivilegeCount;
;    ULONG Control;

; These must be converted to LUIDs before use.
SE_MIN_WELL_KNOWN_PRIVILEGE          =       2
SE_CREATE_TOKEN_PRIVILEGE          =       2
SE_LOCK_MEMORY_PRIVILEGE           =       4
SE_INCREASE_QUOTA_PRIVILEGE                =       5
SE_MACHINE_ACCOUNT_PRIVILEGE               =       6
SE_TCB_PRIVILEGE                   =       7
SE_SECURITY_PRIVILEGE                      =       8
SE_TAKE_OWNERSHIP_PRIVILEGE                =       9
SE_LOAD_DRIVER_PRIVILEGE           =       10
SE_SYSTEM_PROFILE_PRIVILEGE               =       11
SE_SYSTEMTIME_PRIVILEGE                   =       12
SE_INC_BASE_PRIORITY_PRIVILEGE            =       14
SE_CREATE_PAGEFILE_PRIVILEGE              =       15
SE_CREATE_PERMANENT_PRIVILEGE             =       16
SE_BACKUP_PRIVILEGE                       =       17
SE_RESTORE_PRIVILEGE                      =       18
SE_SHUTDOWN_PRIVILEGE                     =       19
SE_DEBUG_PRIVILEGE                        =       20
SE_AUDIT_PRIVILEGE                        =       21
SE_CHANGE_NOTIFY_PRIVILEGE                =       23
SE_REMOTE_SHUTDOWN_PRIVILEGE              =       24
SE_UNDOCK_PRIVILEGE                       =       25
SE_SYNC_AGENT_PRIVILEGE                   =       26
SE_MANAGE_VOLUME_PRIVILEGE                =       28
SE_IMPERSONATE_PRIVILEGE          =       29
SE_CREATE_GLOBAL_PRIVILEGE                =       30
SE_RELABEL_PRIVILEGE                      =       32
SE_INC_WORKING_SET_PRIVILEGE              =       33
SE_TIME_ZONE_PRIVILEGE                    =       34

TOKEN_ASSIGN_PRIMARY                       =       0001h
TOKEN_DUPLICATE                                =       0002h
TOKEN_IMPERSONATE                      =       0004h
TOKEN_QUERY                            =       0008h
TOKEN_QUERY_SOURCE                     =       0010h
TOKEN_ADJUST_PRIVILEGES                        =       0020h
TOKEN_ADJUST_GROUPS                    =       0040h
TOKEN_ADJUST_DEFAULT                   =       0080h
TOKEN_ADJUST_SESSIONID                 =       0100h

TOKEN_ALL_ACCESS                   =       STANDARD_RIGHTS_REQUIRED        or \
                                               TOKEN_ASSIGN_PRIMARY            or \
                                               TOKEN_DUPLICATE                 or \
                                               TOKEN_IMPERSONATE               or \
                                               TOKEN_QUERY                     or \
                                               TOKEN_QUERY_SOURCE              or \
                                               TOKEN_ADJUST_PRIVILEGES         or \
                                               TOKEN_ADJUST_GROUPS             or \
                                               TOKEN_ADJUST_DEFAULT            or \

TOKEN_READ                                =       STANDARD_RIGHTS_READ            or \

TOKEN_WRITE                          =       STANDARD_RIGHTS_WRITE           or \
                                               TOKEN_ADJUST_PRIVILEGES         or \
                                               TOKEN_ADJUST_GROUPS             or \

TOKEN_EXECUTE                               =       STANDARD_RIGHTS_EXECUTE

struct       LUID
usedpart                dd      ?
ignorehigh32bitpart        dd      ?

Luid                     LUID
Attributes              dd      ?

PrivilegeCount              dd      ?
Privileges         LUID_AND_ATTRIBUTES

STATUS_VARIABLE_NOT_FOUND        =       0C0000100h

and here you have some reading about possible vulnerabilities of ms win when booting via UEFI
Post 25 Sep 2012, 12:59
View user's profile Send private message Visit poster's website ICQ Number Reply with quote

Joined: 04 Mar 2008
Posts: 563
Location: Germany
nice exemplar.
thanks for the link.

Post 25 Sep 2012, 13:26
View user's profile Send private message Visit poster's website Reply with quote
When all else fails, read the source

Joined: 24 Aug 2004
Posts: 16226
Location: The West Pole
Being able to create boot media is not a security hole if only an administrator can do it. Normal users can't get access to privileged sectors of a disk.

Having secure boot turned off is the key piece of information here. If you have no security in the boot chain then of course you can boot anything, viruses and all. It is the users responsibility to make sure bad things don't happen.

With the secure boot turned on then the user is relinquishing control to a vendor/software company to take over responsibility for the security of the system.
Post 25 Sep 2012, 13:27
View user's profile Send private message Visit poster's website Reply with quote

Joined: 16 Jan 2007
Posts: 294
oh I missed this one, either I'm too far behind the times or Feryno is way ahead.

Feryno wrote:
I created some samples which call uefi from ms win. It is possible from ring3
call    [AdjustTokenPrivileges]
     mov     ebx,eax
   or      ebx,ebx                 ; AdjustTokenPrivileges success?
    jz      exit

Doesn't this need admin priv? Shouldn't AdjustTokenPrivileges be checked with GetLastError in case only partial priv's may be assigned?

Interesting concept and a lot of things to still come yet with EFI I would think but I'd have to agree with Rev on the admin part. Of course there are people who would grant any software admin rights if requested so still really down to the user in that respect. Not sure that secure boot really achieves any extra security above that on x86 or that UEFI makes things any better, perhaps worse even.
Post 23 Apr 2013, 02:29
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum

Copyright © 1999-2019, Tomasz Grysztar.

Powered by rwasa.