Иногда требуется узнать, запущена ли ваша программа под учетной записью с правами Администратора, или же от обычного пользователя. Для чего это нужно? Например, некоторые операции с реестром или файлами требуют права Администратора. При попытке выполнить их обычному пользователю вернется ошибка ERROR_ACCESS_DENIED, но для более точного анализа ситуации надо будет проверить права доступа и уведомить об этом пользователя. Проверить, что программа запущена под Администратором, можно несколькими способами.
Первый способ наиболее универсальный и работает даже на старых операционных системах. Он заключается в том, что надо получить группы доступа для токена текущего процесса, а затем проверить, входит ли хоть одна из них в группу Администраторов локального компьютера. Вот пример реализации:
; Сегмент данных
section '.data' data readable writeable
SECURITY_NT_AUTHORITY = 5
TOKEN_READ = 0x00020008
SECURITY_BUILTIN_DOMAIN_RID = 0x00000020
DOMAIN_ALIAS_RID_ADMINS = 0x00000220
TokenGroups = 0x00000002
BUFF_SIZE = 1024h ; Размер буфера для групп доступа токена
NtAuthority db 0,0,0,0,0,SECURITY_NT_AUTHORITY
hTokenHandle dd ?
dInfoSize dd ?
psidAdmins dd ?
hHeap dd ?
pTokenGroups dd ?
;---------------------------------------------
; Сегмент кода
section '.code' code readable executable
...
; Получить токен текущего процесса
invoke GetCurrentProcess
invoke OpenProcessToken,eax,TOKEN_READ,hTokenHandle
; Выделить память для массива групп
invoke GetProcessHeap
mov [hHeap],eax
invoke HeapAlloc,eax,HEAP_ZERO_MEMORY,BUFF_SIZE
mov [pTokenGroups],eax
; Получить информацию о группах доступа токена
invoke GetTokenInformation,[hTokenHandle],TokenGroups,\
[pTokenGroups],dword BUFF_SIZE,dInfoSize
; Прибраться за собой
invoke CloseHandle,[hTokenHandle]
invoke AllocateAndInitializeSid,NtAuthority,2,\
SECURITY_BUILTIN_DOMAIN_RID,\
DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0,psidAdmins
; Количество записей в структуре TOKEN_GROUPS
mov esi,[pTokenGroups]
mov ebx,dword [esi]
; Указатель на массив SID_AND_ATTRIBUTES
add esi,4
@@:
; Проверить соответствие SID
mov eax,dword [esi]
invoke EqualSid,[psidAdmins],eax
or eax,eax
jnz loc_admin
; Следующая группа
add esi,8
dec ebx
or ebx,ebx
jnz @b
loc_not_admin:
; Пользователь не Администратор
...
loc_admin:
; Пользователь Администратор
...
Обратите внимание, что никаких проверок на ошибки не выполняется, оставлен только рабочий код. Полный вариант вы можете посмотреть в приложении к статье.
Второй вариант очень похож на предыдущий, но будет работать только на системах, начиная с Windows 2000. В нем используется функция
CheckTokenMembership, которая и выполняет все громоздкие проверки.
; Сегмент данных
section '.data' data readable writeable
SECURITY_NT_AUTHORITY = 5
SECURITY_BUILTIN_DOMAIN_RID = 0x00000020
DOMAIN_ALIAS_RID_ADMINS = 0x00000220
NtAuthority db 0,0,0,0,0,SECURITY_NT_AUTHORITY
psidAdmins dd ?
pbAdmin dd ?
;---------------------------------------------
; Сегмент кода
section '.code' code readable executable
...
invoke AllocateAndInitializeSid,NtAuthority,2,\
SECURITY_BUILTIN_DOMAIN_RID,\
DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0,psidAdmins
invoke CheckTokenMembership,NULL,[psidAdmins],pbAdmin
mov eax,[pbAdmin]
; Если EAX=1, то программа запущена под Администратором
...
Следующий вариант самый короткий, но он также будет работать только на новых системах. В нем используется функция IsUserAdmin из библиотеки setupapi.dll. Как вы можете догадаться из ее названия, результатом работы этой функции будет TRUE, если пользователь является Администратором, и FALSE, если нет.
...
invoke IsUserAdmin
; Если EAX=1, то программа запущена под Администратором
...
И последний способ, не совсем обычный, заключается в том, что сперва мы получаем логин текущего пользователя, а затем с помощью функции
NetUserGetInfo запрашиваем подробную информацию о нем (структура
USER_INFO_1). В поле usri1_priv хранится информация о правах доступа этого пользователя.
; Сегмент данных
section '.data' data readable writeable
struct USER_INFO_1
usri1_name dd ?
usri1_password dd ?
usri1_password_age dd ?
usri1_priv dd ?
usri1_home_dir dd ?
usri1_comment dd ?
usri1_flags dd ?
usri1_script_path dd ?
ends
dSize dd 100h
szUname rb 100h
info dd ?
NERR_SUCCESS = 0
USER_PRIV_ADMIN = 2
;---------------------------------------------
; Сегмент кода
section '.code' code readable executable
...
; Получить логин текущего пользователя
invoke GetUserName,szUname,dSize
; Получить информацию о пользователе
invoke NetUserGetInfo,NULL,szUname,1,info
cmp eax,NERR_SUCCESS
jne loc_error
; Указатель на структуру USER_INFO_1
mov eax,[info]
; Пользователь админ?
cmp dword [eax+USER_INFO_1.usri1_priv],USER_PRIV_ADMIN
je loc_admin
loc_not_admin:
; Пользователь не Администратор
...
loc_admin:
; Пользователь Администратор
...
Обратите внимание, что все функции, строки и другие ресурсы, использованные в последнем примере, должны быть юникодными. Также это очень ненадежный способ проверки, например, на домашнем компьютере под Windows 7 она работает нормально, а на работе под Windows XP функция NetUserGetInfo возвращает ошибку, что пользователя не существует.
Ну а в заключении еще один небольшой пример, не совсем относящийся к теме статьи, но очень близкий. Это проверка, загружена система в безопасном режиме или в нормальном.
; Получить информацию о загрузке системы
invoke GetSystemMetrics,SM_CLEANBOOT
; EAX=0 - нормальная загрузка
; EAX=1 - безопасный режим
; EAX=2 - безопасный режим с поддержкой сети
В приложении примеры программ, реализующие все четыре описанных способа проверки прав пользователя, и пример определения варианта загрузки системы.