
Как получить реальную версию Windows из режима совместимости

Как получить реальную версию Windows из режима совместимости
Одной из основных политик компании Microsoft является поддержка обратной совместимости программ, то есть программы для Windows, написанные даже очень давно, теоретически должны работать на современных системах. Один из механизмов обеспечения такой работоспособности - запуск программ в режиме совместимости. Достаточно в свойствах исполняемого файла указать, какую версию Windows надо использовать, и система при запуске программы будет эмулировать для нее окружение именно этой версии. Если при этом из программы попытаться получить версию Windows при помощи функции GetVersion, GetVersionEx или даже RtlGetVersion, то результат вернет версию эмулируемой ОС. А можно ли как-то определить, что программа запущена в режиме совместимости и получить реальную версию Windows, на которой она работает? Можно!
Реальная информация о версии Windows записывается в PEB (Process Environment Block) - специальную структуру, которая заполняется системой при создании любого процесса. Официально структура PEB документирована чуть лучше, чем никак, практически все поля запрятаны за пустышками "Reserved". К счастью, есть и неофициальные источники информации, в которых все подробнейшим образом расписано. Нас интересуют два поля структуры - OSMajorVersion и OSMinorVersion. Получить адрес PEB очень просто:
Code (Assembler) : Убрать нумерацию
- ; Адрес PEB для 32-битных систем
- mov eax,dword [fs:30h]
- ; Адрес PEB для 64-битных систем
- mov rax,qword [gs:60h]
Code (Assembler) : Убрать нумерацию
- ; Получить реальную версию OS из PEB
- mov eax,[fs:30h]
- ; dwMajorVersion
- mov ecx,[eax+0A4h]
- ; dwMinorVersion
- mov edx,[eax+0A8h]
- ; ECX:EDX - реальная версия OS
- ; Получить версию OS через GetVersionEx
- mov [osi.dwOSVersionInfoSize],sizeof.OSVERSIONINFO
- invoke GetVersionEx,osi
- mov ecx,[osi.dwMajorVersion]
- mov edx,[osi.dwMinorVersion]
- ; ECX:EDX - версия OS, полученная из GetVersionEx
- ; Получить версию OS через GetVersion
- invoke GetVersion
- movzx ecx,al
- movzx edx,ah
- ; ECX:EDX - версия OS, полученная из GetVersion
- ; Проверить режим, в котором запущена программа
- mov eax,[fs:30h]
- ; Сравниваем значения из PEB и GetVersionEx
- mov ecx,dword [eax+0A4h]
- cmp ecx,[osi.dwMajorVersion]
- ; Не равно, программа запущена в режиме совместимости
- jne loc_compat
- mov ecx,dword [eax+0A8h]
- cmp ecx,[osi.dwMinorVersion]
- ; Не равно, программа запущена в режиме совместимости
- jne loc_compat
- loc_normal:
- ; Программа запущена в нормальном режиме
- ...
- ...
- loc_compat:
- ; Программа запущена в режиме совместимости
- ...
- ...
Если по каким-то причинам вы не можете использовать в вашей программе данные из PEB, есть по меньшей мере два нормальных документированных способа получить реальную версию операционной системы даже из режима совместимости. Что немаловажно, оба способа безошибочно работают в том числе и на последних версиях Windows и не требуют никаких дополнительных манипуляций с манифестом приложения. Первый способ - использование функции NetServerGetInfo. Она требует предварительного описания структуры, про которую не знает FASM:
Code (Assembler) : Убрать нумерацию
- ; Описание структуры
- struct SERVER_INFO_101
- sv101_platform_id dd ?
- sv101_name dd ?
- sv101_version_major dd ?
- sv101_version_minor dd ?
- sv101_type dd ?
- sv101_comment dd ?
- ends
- ; Указатель на структуру
- wsi dd ?
Code (Assembler) : Убрать нумерацию
- ; Получить версию OS через NetServerGetInfo
- invoke NetServerGetInfo,NULL,101,wsi
- mov eax,[wsi]
- mov ecx,[eax+SERVER_INFO_101.sv101_version_major]
- mov edx,[eax+SERVER_INFO_101.sv101_version_minor]
- invoke NetApiBufferFree,[wsi]
- ; ECX:EDX - реальная версия OS, полученная из NetServerGetInfo
Code (Assembler) : Убрать нумерацию
- ; Описание структуры
- struct WKSTA_INFO_100
- wki100_platform_id dd ?
- wki100_computername dd ?
- wki100_langroup dd ?
- wki100_ver_major dd ?
- wki100_ver_minor dd ?
- ends
- ; Указатель на структуру
- wsi dd ?
Code (Assembler) : Убрать нумерацию
- ; Получить версию OS через NetWkstaGetInfo
- invoke NetWkstaGetInfo,NULL,100,wsi
- mov eax,[wsi]
- mov ecx,[eax+WKSTA_INFO_100.wki100_ver_major]
- mov edx,[eax+WKSTA_INFO_100.wki100_ver_minor]
- invoke NetApiBufferFree,[wsi]
- ; ECX:EDX - реальная версия OS, полученная из NetWkstaGetInfo
В приложении пример программы с исходным текстом, которая получает реальную версию операционной системы описанными в статье способами и определяет режим своего запуска.
Просмотров: 3147 | Комментариев: 12

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье

ManHunter
(08.10.2018 в 22:20):
Ну вот как раз в дополнении я про это написал. Windows 10 выставляет фейковые версии даже в обычном режиме, а если надо выяснить, что программа запущена именно на десятке, то придется расчехлять ритуальный бубен.

ManHunter
(08.10.2018 в 17:46):
Дополнил статью данными по последним версиям Windows.

ManHunter
(06.10.2018 в 17:40):
Те же яйца, только названы иначе. Как тут уже сказали, функция возвращает виртуальную версию.
Чтобы не быть голословным, дополнил пример в исходнике вызовом RtlGetVersion.

Мойша
(29.09.2018 в 03:48):
pawel97, mov rax, [gs:qword 60h]

pawel97
(28.09.2018 в 16:10):
"mov rax,qword [gs:60h]"
Что-то у меня в 64-битной dll на fasm подобная конструкция не канает.
processed: mov rax,qword[gs:30h]
error: invalid use of symbol.
Скопировал сырые байты из IsDebuggerPresent, но это же нефэншуйно?..
Что-то у меня в 64-битной dll на fasm подобная конструкция не канает.
processed: mov rax,qword[gs:30h]
error: invalid use of symbol.
Скопировал сырые байты из IsDebuggerPresent, но это же нефэншуйно?..

toor
(26.09.2018 в 10:01):
NetServerGetInfo выдает реальную версию даже в режиме совместимости (но проверял на Win7, что на более старших версиях будет, понятия не имею).
type
NET_API_STATUS = DWORD;
_SERVER_INFO_101 = record
sv101_platform_id: DWORD;
sv101_name: LPWSTR;
sv101_version_major: DWORD;
sv101_version_minor: DWORD;
sv101_type: DWORD;
sv101_comment: LPWSTR;
end;
SERVER_INFO_101 = _SERVER_INFO_101;
PSERVER_INFO_101 = ^SERVER_INFO_101;
LPSERVER_INFO_101 = PSERVER_INFO_101;
function NetServerGetInfo(servername: LPWSTR; level: DWORD; var bufptr): NET_API_STATUS;
stdcall; external 'Netapi32.dll';
function NetApiBufferFree(Buffer: LPVOID): NET_API_STATUS; stdcall; external 'Netapi32.dll';
const
MAJOR_VERSION_MASK = $0F;
procedure TForm1.Button1Click(Sender: TObject);
var
Buffer: PSERVER_INFO_101;
begin
if NetServerGetInfo(nil, 101, Buffer) = NO_ERROR then
try
ShowMessage(Format('NetServerGetInfo: %d.%d',
[Buffer.sv101_version_major and MAJOR_VERSION_MASK, Buffer.sv101_version_minor]));
finally
NetApiBufferFree(Buffer);
end;
end;
type
NET_API_STATUS = DWORD;
_SERVER_INFO_101 = record
sv101_platform_id: DWORD;
sv101_name: LPWSTR;
sv101_version_major: DWORD;
sv101_version_minor: DWORD;
sv101_type: DWORD;
sv101_comment: LPWSTR;
end;
SERVER_INFO_101 = _SERVER_INFO_101;
PSERVER_INFO_101 = ^SERVER_INFO_101;
LPSERVER_INFO_101 = PSERVER_INFO_101;
function NetServerGetInfo(servername: LPWSTR; level: DWORD; var bufptr): NET_API_STATUS;
stdcall; external 'Netapi32.dll';
function NetApiBufferFree(Buffer: LPVOID): NET_API_STATUS; stdcall; external 'Netapi32.dll';
const
MAJOR_VERSION_MASK = $0F;
procedure TForm1.Button1Click(Sender: TObject);
var
Buffer: PSERVER_INFO_101;
begin
if NetServerGetInfo(nil, 101, Buffer) = NO_ERROR then
try
ShowMessage(Format('NetServerGetInfo: %d.%d',
[Buffer.sv101_version_major and MAJOR_VERSION_MASK, Buffer.sv101_version_minor]));
finally
NetApiBufferFree(Buffer);
end;
end;

wet
(26.09.2018 в 08:56):
RtlGetVersion в режиме совместимости показывает совместимую ОС. Проверено.
WMI не врёт ни при каких обстоятельствах:
SELECT Version FROM Win32_OperatingSystem
Но наверно доступ к WMI из FASM то ещё удовольствие?

Мойша
(26.09.2018 в 00:09):
есть жи RtlGetVersion

Евгений
(25.09.2018 в 15:28):
Приветствую!
Я бесконечно далекий от програмирования человек, но очень уж захотелось спросить - не судите строго. А для чего может быть нужно узнавать реальную версию винды из-под режима совместимости? Ну я в том смысле, что если прогу запускают в этом режиме, то это не из вредности и не от жизни хорошей, а потому что надо и, в таком случае, зачем ей "выводить винду на чистую воду", а не довольствоваться тем, что ей сообщают?
Я бесконечно далекий от програмирования человек, но очень уж захотелось спросить - не судите строго. А для чего может быть нужно узнавать реальную версию винды из-под режима совместимости? Ну я в том смысле, что если прогу запускают в этом режиме, то это не из вредности и не от жизни хорошей, а потому что надо и, в таком случае, зачем ей "выводить винду на чистую воду", а не довольствоваться тем, что ей сообщают?

ManHunter
(24.09.2018 в 23:00):
Вот же неймется мелкомягким :( Потестирую на реальном железе, допишу результаты.
toor, спасибо!
toor, спасибо!

toor
(24.09.2018 в 10:43):
Начиная с W8 функции GetVersion и GetVersionEx могут работать неверно и в обычном режиме, если разработчик явно не задекларирует совместимость приложения с операционными системами семейства Windows от версии 8 и выше через манифест приложения.
Следовательно, описанный подход часто будет давать сбои на W8 и выше. Хотя для вас, в вашей нелюбовью к этим ОС, это наверное совсем не критично...
Тут немного об этом написано - http://decoding.dax.ru/practic..._not_W8.html (не является рекламой, просто для справки!!!)
Следовательно, описанный подход часто будет давать сбои на W8 и выше. Хотя для вас, в вашей нелюбовью к этим ОС, это наверное совсем не критично...
Тут немного об этом написано - http://decoding.dax.ru/practic..._not_W8.html (не является рекламой, просто для справки!!!)

Добавить комментарий
Заполните форму для добавления комментария

RtlGetVersion тот же PEB использует через вызов NtCurrentTeb, стало быть в режиме совместимости ручной разбор PEB должен привести к версии виртуальной системы. Уж лучше RtlGetNtVersionNumbers использовать, там версии хардкодом вшиты.