Форум программистов, компьютерный форум, киберфорум
_lunar_
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Исследование сервиса "Обнаружение интерактивных служб" (UI0Detect) Часть 2: Windows 8 - Windows 10

Запись от _lunar_ размещена 10.01.2021 в 12:16
Обновил(-а) _lunar_ 17.01.2021 в 15:51

Первая часть блога по исследованию сервиса UI0Detect в больше степени касается таких операционных систем, как Windows Vista и Windows 7.
Механизм переключения на Рабочий стол нулевой сессии в этих ОС одинаков (за исключением некоторых неважных мелочей).
Но в Windows 8/8.1 и Windows 10 ядро было переписано в угоду "безопасности". Коснулись эти изменения и сервиса Обнаружение интерактивных служб.

Немного освежим память и посмотрим как UI0Detect работает в Vista и 7.
Как мы знаем, механизм переключения в сессию 0 перекочевал из подпрограммы Winlogon в подпрограмму Wininit.
В точке входа Wininit (функция WinMain) определены две internal функции RegisterSession0ViewerWindowHookDll и UnRegisterSession0ViewerWindowHookDll (вторая функция не представляет особого интересна и рассматриваться не будет).
Описание кода функции RegisterSession0ViewerWindowHookDll можете найти в первой части, но для наглядности я приведу её прототип
C++
1
2
3
DWORD RegisterSession0ViewerWindowHookDll(
    VOID
);
Как видно, функция пустая и не принимает никаких аргументов.
Суть в том, что в Vista и 7 инструмент перехода в сессию 0 находился прям в Wininit.exe и не требовалось никаких дополнительных действий чтобы его задействовать.
Но в Microsoft посчитали, что это небезопасно и решили убрать функции RegisterSession0ViewerWindowHookDll и UnRegisterSession0ViewerWindowHookDll из ядра системы. Что же они сделали?

Гуляя в дизассемблере по Wininit.exe можно наткнуться на массивную функцию PrimaryTerminalAndHookWorker
Нажмите на изображение для увеличения
Название: 1.png
Просмотров: 1421
Размер:	110.0 Кб
ID:	6691
По косвенным признакам и вспоминая из первой части, что для перехода на рабочий стол нужен дескриптор этого рабочего стола (s_hdeskApplication), который функция RegisterSession0ViewerWindowHookDll получала из другой internal функции CreatePrimaryTerminal
C++
1
CreatePrimaryTerminal(s_hWinsta, lpMem, s_hdeskWinlogon, s_hdeskApplication, g_Context);
сразу становится ясно, что функция PrimaryTerminalAndHookWorker оперирует нужными дескрипторами для перехода на другие рабочие столы других сессий.

Вот её прототип (от версии к версии ОС функция постоянно меняется, я приведу код для Windows 10 build 1709)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
__int64 g_Context;
unsigned short g_usState = 5;
DWORD Period = 6;
__int16 v57;
 
DWORD PrimaryTerminalAndHookWorker(
    &s_hWinsta,
    &s_hdeskWinlogon,
    &s_hdeskApplication,
    &g_Context,
    &g_usState,
    Period,
    &v57,
    g_pSidSystem,
    g_pSidLocalService,
    g_pSidLocal,
    g_pSidWorld,
    g_pSidAdmin,
    g_pSidPowerUser,
    g_pSidCreator,
    g_pSidRestricted,
    g_pSidInteractive,
    g_pSidService,
    g_pSidWindowManager,
    g_pSidFontDriverHost,
    g_pSidAnyPackage,
    g_pSidAnyRestrictedPackage
);
Но функция PrimaryTerminalAndHookWorker не является внутренней (internal), она экспортируемая.
И экспортируется она из библиотеки wininitext.dll (wininitext.dll появилась именно в Windows 8 и последующих ОС, в Windows 7 и предыдущих ОС её не было).

Посмотрим на мнемонику функции PrimaryTerminalAndHookWorker в дизассемблере (код для Windows 10 build 1709)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
__int64 __fastcall PrimaryTerminalAndHookWorker(__int64 a1, __int64 a2, HDESK *a3, __int64 a4, _WORD *a5, __int16 a6, _WORD *a7, __int64 a8, __int64 a9, __int64 a10, __int64 a11, __int64 a12, __int64 a13, __int64 a14, __int64 a15, __int64 a16, __int64 a17, __int64 a18, __int64 a19, __int64 a20, __int64 a21)
{
  bool v21; // zf
  __int64 result; // rax
  unsigned int v27; // esi
  HANDLE v28; // rax
  LPVOID lpMem[5]; // [rsp+30h] [rbp-28h] BYREF
 
  v21 = dword_18000970C == 0;
  lpMem[0] = 0i64;
  *a7 = 0;
  if ( v21 )
  {
    dword_180009710 = GetRespecializeFlag();
    dword_180009708 = GetSetupType();
    dword_18000970C = 1;
    if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control
      && (*((_BYTE *)WPP_GLOBAL_Control + 28) & 1) != 0
      && *((_BYTE *)WPP_GLOBAL_Control + 25) >= 4u )
    {
      WPP_SF_dd(*((_QWORD *)WPP_GLOBAL_Control + 2));
    }
  }
  g_pSidSystem = a8;
  g_pSidLocalService = a9;
  g_pSidLocal = a10;
  g_pSidWorld = a11;
  g_pSidAdmin = a12;
  g_pSidPowerUser = a13;
  g_pSidCreator = a14;
  g_pSidRestricted = a15;
  g_pSidInteractive = a16;
  g_pSidService = a17;
  g_pSidWindowManager = a18;
  g_pSidFontDriverHost = a19;
  g_pSidAnyPackage = a20;
  g_pSidAnyRestrictedPackage = a21;
  result = CreatePrimaryTerminal(a1, lpMem, a2, a3, a4);
  v27 = result;
  if ( (_DWORD)result )
  {
    *a7 = 1;
  }
  else
  {
    *a5 = a6;
    v28 = GetProcessHeap();
    HeapFree(v28, 0, lpMem[0]);
    UpdatePerUserSystemParameters(0i64);
    if ( dword_180009708 || dword_180009710 != v27 )
    {
      result = v27;
    }
    else
    {
      result = RegisterSession0ViewerWindowHookDll(*a3); // вот она и попалась!!!
      if ( (_DWORD)result )
        *a7 = 2;
    }
  }
  return result;
}
И что же мы видим - функция RegisterSession0ViewerWindowHookDll перекочевала в функцию PrimaryTerminalAndHookWorker (библиотеки wininitext.dll) и вызывается ядром в подпрограмме Wininit.exe (функция UnRegisterSession0ViewerWindowHookDll теперь также располагается в библиотеке wininitext.dll).
При этом изменился её прототип
C++
1
2
3
DWORD RegisterSession0ViewerWindowHookDll(
    HDESK s_hdeskApplication
);
RegisterSession0ViewerWindowHookDll принимает один аргумент - дескриптор рабочего стола, который далее передаётся в функции SetThreadDesktop и SwitchDesktop.
Есть ещё один важный момент - чтобы механизм перехода в Windows 8/8.1 и Windows 10 заработал, нужно изменить в реестре значение NoInteractiveServices на ноль (0), которое находится по следующему пути
Цитата:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows
по умолчанию значение NoInteractiveServices установлено в единицу (1).

Теперь я приведу мнемонику функции PrimaryTerminalAndHookWorker из ОС Windows 10 build 2009 (20H2)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
__int64 __fastcall PrimaryTerminalAndHookWorker(__int64 a1, __int64 a2, __int64 a3, __int64 a4, _WORD *a5, __int16 a6, _WORD *a7, __int64 a8, __int64 a9, __int64 a10, __int64 a11, __int64 a12, __int64 a13, __int64 a14, __int64 a15, __int64 a16, __int64 a17, __int64 a18, __int64 a19, __int64 a20, __int64 a21)
{
  bool v21; // zf
  __int64 result; // rax
  HANDLE v27; // rax
  LPVOID lpMem[5]; // [rsp+30h] [rbp-28h] BYREF
 
  v21 = dword_180009778 == 0;
  lpMem[0] = 0i64;
  *a7 = 0;
  if ( v21 )
  {
    dword_18000977C = GetRespecializeFlag();
    dword_180009774 = GetSetupType();
    dword_180009778 = 1;
    if ( WPP_GLOBAL_Control != &WPP_GLOBAL_Control
      && (*((_BYTE *)WPP_GLOBAL_Control + 28) & 1) != 0
      && *((_BYTE *)WPP_GLOBAL_Control + 25) >= 4u )
    {
      WPP_SF_dd(*((_QWORD *)WPP_GLOBAL_Control + 2));
    }
  }
  g_pSidSystem = a8;
  g_pSidLocalService = a9;
  g_pSidLocal = a10;
  g_pSidWorld = a11;
  g_pSidAdmin = a12;
  g_pSidPowerUser = a13;
  g_pSidCreator = a14;
  g_pSidRestricted = a15;
  g_pSidInteractive = a16;
  g_pSidService = a17;
  g_pSidWindowManager = a18;
  g_pSidFontDriverHost = a19;
  g_pSidAnyPackage = a20;
  g_pSidAnyRestrictedPackage = a21;
  result = CreatePrimaryTerminal(a1, lpMem, a2, a3, a4);
  if ( (_DWORD)result )
  {
    *a7 = 1;
  }
  else
  {
    *a5 = a6;
    v27 = GetProcessHeap();
    HeapFree(v27, 0, lpMem[0]);
    UpdatePerUserSystemParameters(0i64);
    result = 0i64;
  }
  return result;
}
Как можно видеть функция RegisterSession0ViewerWindowHookDll просто отсутствует в коде PrimaryTerminalAndHookWorker (UnRegisterSession0ViewerWindowHookDll также удалена из библиотеки wininitext.dll).

На данный момент я ищу способ вернуть функциональность RegisterSession0ViewerWindowHookDll и Microsoft немного способствует этому (хукать библиотеку всё же проще, чем защищенный процесс).

И ещё один важный момент. В Windows 10 Microsoft переписала драйвер клавиатуры и мыши в сессии 0.
Перейдя в сессию 0 у вас не будет работать клава-мышь!

Они переписали драйвер win32kfull.sys, точнее 2 функции - RawInputThread (отвечающую за ввод клавиатуры) и xxxDesktopThread (отвечающую за ввод мыши).

Утрированно, в коде это выглядит следующим образом:
в Windows 8/8.1
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mouseInputThread()
{
    LODWORD(Object) = 0;
    RegisterHotKey(struct tagWND *)1, (ULONG_PTR)Object);
    SetDebugHotKeys();
    SetWinlogonHotKeys();
    SetWindowArrangementHotKeys();
    SetPenHotKeys();
 
    CreatePointerDeviceProcessEvents();
    v10 = (struct _KEVENT *)v57[8];
 
    EnterCrit(0, 1);
    gpkeRITEvent = 0;
    ObfDereferenceObject((PVOID)v57[8]);
    UserSessionSwitchLeaveCrit();
}
в Windows 10
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mouseInputThread()
{
    if (gSessionId != gServiceSessionId) // if (gSessionId != 0)
    {
        LODWORD(Object) = 0;
        RegisterHotKey((struct tagWND *)1, (ULONG_PTR)Object);
        SetDebugHotKeys();
        SetWinlogonHotKeys();
        SetWindowArrangementHotKeys();
        SetPenHotKeys();
 
        CreatePointerDeviceProcessEvents();
        v10 = (struct _KEVENT *)v57[8];
 
        EnterCrit(0, 1);
        gpkeRITEvent = 0;
        ObfDereferenceObject((PVOID)v57[8]);
        UserSessionSwitchLeaveCrit();
    }
}
Так что переходя в сессию 0 в Windows 10 не забудьте поставить таймер на возврат, ну или напишите свой дров для клава-мыши
Размещено в Без категории
Просмотров 1172 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.