Blog. Just Blog

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

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Как получить реальную версию Windows из режима совместимости
Как получить реальную версию Windows из режима совместимости

Одной из основных политик компании Microsoft является поддержка обратной совместимости программ, то есть программы для Windows, написанные даже очень давно, теоретически должны работать на современных системах. Один из механизмов обеспечения такой работоспособности - запуск программ в режиме совместимости. Достаточно в свойствах исполняемого файла указать, какую версию Windows надо использовать, и система при запуске программы будет эмулировать для нее окружение именно этой версии. Если при этом из программы попытаться получить версию Windows при помощи функции GetVersion, GetVersionEx или даже RtlGetVersion, то результат вернет версию эмулируемой ОС. А можно ли как-то определить, что программа запущена в режиме совместимости и получить реальную версию Windows, на которой она работает? Можно!

Реальная информация о версии Windows записывается в PEB (Process Environment Block) - специальную структуру, которая заполняется системой при создании любого процесса. Официально структура PEB документирована чуть лучше, чем никак, практически все поля запрятаны за пустышками "Reserved". К счастью, есть и неофициальные источники информации, в которых все подробнейшим образом расписано. Нас интересуют два поля структуры - OSMajorVersion и OSMinorVersion. Получить адрес PEB очень просто:
  1. ; Адрес PEB для 32-битных систем
  2. mov     eax,dword [fs:30h]
  3.  
  4. ; Адрес PEB для 64-битных систем
  5. mov     rax,qword [gs:60h]
Если не хочется таскать в исходниках описание PEB целиком, можно воспользоваться абсолютными адресами нужных полей. Для 32-битных приложений это будут +0A4h и +0A8h соответственно. Дальше все просто. Если значения OSMajorVersion и OSMinorVersion из PEB не совпадают с результатами выполнения функций GetVersionEx или GetVersion, значит программа запущена в режиме совместимости.
  1.         ; Получить реальную версию OS из PEB
  2.         mov     eax,[fs:30h]
  3.         ; dwMajorVersion
  4.         mov     ecx,[eax+0A4h]
  5.         ; dwMinorVersion
  6.         mov     edx,[eax+0A8h]
  7.         ; ECX:EDX - реальная версия OS
  8.  
  9.         ; Получить версию OS через GetVersionEx
  10.         mov     [osi.dwOSVersionInfoSize],sizeof.OSVERSIONINFO
  11.         invoke  GetVersionEx,osi
  12.         mov     ecx,[osi.dwMajorVersion]
  13.         mov     edx,[osi.dwMinorVersion]
  14.         ; ECX:EDX - версия OS, полученная из GetVersionEx
  15.  
  16.         ; Получить версию OS через GetVersion
  17.         invoke  GetVersion
  18.         movzx   ecx,al
  19.         movzx   edx,ah
  20.         ; ECX:EDX - версия OS, полученная из GetVersion
  21.  
  22.         ; Проверить режим, в котором запущена программа
  23.         mov     eax,[fs:30h]
  24.         ; Сравниваем значения из PEB и GetVersionEx
  25.         mov     ecx,dword [eax+0A4h]
  26.         cmp     ecx,[osi.dwMajorVersion]
  27.         ; Не равно, программа запущена в режиме совместимости
  28.         jne     loc_compat
  29.         mov     ecx,dword [eax+0A8h]
  30.         cmp     ecx,[osi.dwMinorVersion]
  31.         ; Не равно, программа запущена в режиме совместимости
  32.         jne     loc_compat
  33. loc_normal:
  34.         ; Программа запущена в нормальном режиме
  35.         ...
  36.         ...
  37. loc_compat:
  38.         ; Программа запущена в режиме совместимости
  39.         ...
  40.         ...
UPD: как правильно подсказали в комментариях, на последних версиях Windows поведение функций GetVersion и GetVersionEx находится за гранью здравого смысла. Если в манифесте приложения принудительно не прописать, что оно совместимо с Windows 8.1 и Windows 10, то обе эти функции будут возвращать версию системы 6.2, что соответствует Windows 8. Опытным путем установлено, что при обычном запуске приложения без манифеста правильную версию системы возвращает функция RtlGetVersion. При запуске в режиме совместимости, соответственно, эта функция вернет версию виртуальной системы. Таким образом, для определения запуска вашей программы в режиме совместимости на последних версиях Windows надо сравнивать значения из PEB с результатами функции RtlGetVersion, а не GetVersionEx, как было сказано выше.

Если по каким-то причинам вы не можете использовать в вашей программе данные из PEB, есть по меньшей мере два нормальных документированных способа получить реальную версию операционной системы даже из режима совместимости. Что немаловажно, оба способа безошибочно работают в том числе и на последних версиях Windows и не требуют никаких дополнительных манипуляций с манифестом приложения. Первый способ - использование функции NetServerGetInfo. Она требует предварительного описания структуры, про которую не знает FASM:
  1. ; Описание структуры
  2. struct SERVER_INFO_101
  3.         sv101_platform_id   dd ?
  4.         sv101_name          dd ?
  5.         sv101_version_major dd ?
  6.         sv101_version_minor dd ?
  7.         sv101_type          dd ?
  8.         sv101_comment       dd ?
  9. ends
  10.  
  11. ; Указатель на структуру
  12. wsi     dd ?
  1.         ; Получить версию OS через NetServerGetInfo
  2.         invoke  NetServerGetInfo,NULL,101,wsi
  3.         mov     eax,[wsi]
  4.         mov     ecx,[eax+SERVER_INFO_101.sv101_version_major]
  5.         mov     edx,[eax+SERVER_INFO_101.sv101_version_minor]
  6.         invoke  NetApiBufferFree,[wsi]
  7.         ; ECX:EDX - реальная версия OS, полученная из NetServerGetInfo
Второй способ - через функцию NetWkstaGetInfo, для которой также потребуется заранее описать структуру для работы.
  1. ; Описание структуры
  2. struct WKSTA_INFO_100
  3.         wki100_platform_id  dd ?
  4.         wki100_computername dd ?
  5.         wki100_langroup     dd ?
  6.         wki100_ver_major    dd ?
  7.         wki100_ver_minor    dd ?
  8. ends
  9.  
  10. ; Указатель на структуру
  11. wsi     dd ?
  1.         ; Получить версию OS через NetWkstaGetInfo
  2.         invoke  NetWkstaGetInfo,NULL,100,wsi
  3.         mov     eax,[wsi]
  4.         mov     ecx,[eax+WKSTA_INFO_100.wki100_ver_major]
  5.         mov     edx,[eax+WKSTA_INFO_100.wki100_ver_minor]
  6.         invoke  NetApiBufferFree,[wsi]
  7.         ; ECX:EDX - реальная версия OS, полученная из NetWkstaGetInfo
После определения реальной версии Windows можно действовать по обстановке. Например, если ваша программа изначально не рассчитана на работу на более старых системах или по какой-то иной причине не может работать в режиме совместимости, надо уведомить об этом пользователя.

В приложении пример программы с исходным текстом, которая получает реальную версию операционной системы описанными в статье способами и определяет режим своего запуска.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Real.OS.Version.Demo.zip (2,181 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 2927 | Комментариев: 12

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

Комментарии

Отзывы посетителей сайта о статье
El Sanchez (30.10.2023 в 10:23):
ЦитатаРеальная информация о версии Windows записывается в PEB (Process Environment Block).
Опытным путем установлено, что при обычном запуске приложения без манифеста правильную версию системы возвращает функция RtlGetVersion. При запуске в режиме совместимости, соответственно, эта функция вернет версию виртуальной системы.

RtlGetVersion тот же PEB использует через вызов NtCurrentTeb, стало быть в режиме совместимости ручной разбор PEB должен привести к версии виртуальной системы. Уж лучше RtlGetNtVersionNumbers использовать, там версии хардкодом вшиты.
ManHunter (08.10.2018 в 22:20):
ЦитатаА для чего может быть нужно узнавать реальную версию винды из-под режима совместимости?

Ну вот как раз в дополнении я про это написал. Windows 10 выставляет фейковые версии даже в обычном режиме, а если надо выяснить, что программа запущена именно на десятке, то придется расчехлять ритуальный бубен.
ManHunter (08.10.2018 в 17:46):
Дополнил статью данными по последним версиям Windows.
ManHunter (06.10.2018 в 17:40):
Цитатаесть жи RtlGetVersion

Те же яйца, только названы иначе. Как тут уже сказали, функция возвращает виртуальную версию.

Чтобы не быть голословным, дополнил пример в исходнике вызовом 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, но это же нефэншуйно?..
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;
wet (26.09.2018 в 08:56):
Цитатаесть жи RtlGetVersion

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 (24.09.2018 в 10:43):
Начиная с W8 функции GetVersion и GetVersionEx могут работать неверно и в обычном режиме, если разработчик явно не задекларирует совместимость приложения с операционными системами семейства Windows от версии 8 и выше через манифест приложения.

Следовательно, описанный подход часто будет давать сбои на W8 и выше. Хотя для вас, в вашей нелюбовью к этим ОС, это наверное совсем не критично...

Тут немного об этом написано - http://decoding.dax.ru/practic..._not_W8.html (не является рекламой, просто для справки!!!)

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.09 сек. / MySQL: 2 (0.0088 сек.) / Память: 4.5 Mb
Наверх