PDA

Просмотр полной версии : Вопрос по hook'ам


csrss
16.04.2009, 22:09
Здрасте. У меня общий вопрос по хукам. Сразу замечу что темой занялся недавно ну и разумеется полный ламер(и в хуках и в асме к сожалению ), но хочу разобратся. Короче закачал себе мигбота(это собственно пример загрузки драйвера при помощи ZwSetSystemInformation) с руткит.ком, смотрю в сорци и пытаюсь понять чё к чему. Вот например такой фрагмент кода

NTSTATUS CheckFunctionBytesNtDeviceIoControlFile(){// тут всё ясно
int i=0;
char *p = (char *)NtDeviceIoControlFile;
//================================================== =======
// windows 2k
//The beginning of the NtDeviceIoControlFile function
//should match:
//55 PUSH EBP
//8BEC MOV EBP, ESP
//6A01 PUSH 01
//FF752C PUSH DWORD PTR [EBP + 2C]
//================================================== =======
// win xp sp 3
// 8bff | mov edi, edi
// 55 | push ebp
// 8bec | mov ebp, esp
// 6a01 | push 1
// char c[] = { 0x55, 0x8B, 0xEC, 0x6A, 0x01, 0xFF, 0x75, 0x2C }; // win 2k
char c[] = {0x8b, 0xff, 0x55, 0x8b, 0xec, 0x6a, 0x01}; // win xp sp3

while(i<7){
DbgPrint("NtDeviceIoControlFile - 0x%02X ", (unsigned char)p[i]);
if(p[i] != c[i]){
return STATUS_UNSUCCESSFUL;
}
i++;
}
return STATUS_SUCCESS;
}

__declspec(naked) my_function_detour_ntdeviceiocontrolfile()// тут не ясно
{
__asm
{
// exec missing instructions
// push ebp // win 2k
// mov ebp, esp
// push 0x01
// push dword ptr [ebp+0x2C]

mov edi, edi // win xp sp3
push ebp
mov ebp, esp
push 1

// jump to re-entry location in hooked function
// this gets 'stamped' with the correct address
// at runtime.
//
// we need to hard-code a far jmp, but the assembler
// that comes with the DDK will not poop this out
// for us, so we code it manually
// jmp FAR 0x08:0xAAAAAAAA
_emit 0xEA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0x08
_emit 0x00
}
}

VOID DetourFunctionNtDeviceIoControlFile()// тут всё ясно
{
char *actual_function = (char *)NtDeviceIoControlFile;
char *non_paged_memory;
unsigned long detour_address;
unsigned long reentry_address;
int i = 0;

// assembles to jmp far 0008:11223344 where 11223344 is address of
// our detour function, plus one NOP to align up the patch
// char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90 };// win 2k
char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00}; // win xp sp3
// reenter the hooked function at a location past the overwritten opcodes
// alignment is, of course, very important here
// reentry_address = ((unsigned long)NtDeviceIoControlFile) + 8; // win 2k
reentry_address = ((unsigned long)NtDeviceIoControlFile) + 7; // win xp sp3

non_paged_memory = ExAllocatePool(NonPagedPool, 256);

// copy contents of our function into non paged memory
// with a cap at 256 bytes (beware of possible read off end of page FIXME)
for(i=0;i<256;i++)
{
((unsigned char *)non_paged_memory)[i] = ((unsigned char *)my_function_detour_ntdeviceiocontrolfile)[i];
}

detour_address = (unsigned long)non_paged_memory;

// stamp in the target address of the far jmp
*( (unsigned long *)(&newcode[1]) ) = detour_address;

// now, stamp in the return jmp into our detour
// function
for(i=0;i<200;i++)
{
if( (0xAA == ((unsigned char *)non_paged_memory)[i]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+1]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+2]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+3]))
{
// we found the address 0xAAAAAAAA
// stamp it w/ the correct address
*( (unsigned long *)(&non_paged_memory[i]) ) = reentry_address;
break;
}
}

//TODO, raise IRQL

//overwrite the bytes in the kernel function
//to apply the detour jmp
// for(i=0;i < 8;i++) // win 2k
for(i=0;i < 7;i++) // win xp sp3
{
actual_function[i] = newcode[i];
}

//TODO, drop IRQL
}

NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ){

if(STATUS_SUCCESS != CheckFunctionBytesNtDeviceIoControlFile()){
DbgPrint("Match Failure on NtDeviceIoControlFile!");
return STATUS_UNSUCCESSFUL;
}
DetourFunctionNtDeviceIoControlFile();
}

Ну я так понимаю что смысл в том чтоб NtDeviceIoControlFile вывести из paged pool в nonpaged. Ок, идём дальше. В WinDbg перед исполнением вышепредставленного кода NtDeviceIoControlFile выглядет у меня следующим образом

nt!NtDeviceIoControlFile:
8058efad 8bff mov edi,edi
8058efaf 55 push ebp
8058efb0 8bec mov ebp,esp
8058efb2 6a01 push 1
8058efb4 ff752c push dword ptr [ebp+2Ch]
8058efb7 ff7528 push dword ptr [ebp+28h]
8058efba ff7524 push dword ptr [ebp+24h]
8058efbd ff7520 push dword ptr [ebp+20h]
8058efc0 ff751c push dword ptr [ebp+1Ch]
8058efc3 ff7518 push dword ptr [ebp+18h]
8058efc6 ff7514 push dword ptr [ebp+14h]
8058efc9 ff7510 push dword ptr [ebp+10h]
8058efcc ff750c push dword ptr [ebp+0Ch]
8058efcf ff7508 push dword ptr [ebp+8]
8058efd2 e8a8b8feff call nt!NtAddAtom+0x19b (8057a87f)

А после исполнения вот так




nt!NtDeviceIoControlFile:
8058efad ea3806c4850800 jmp 0008:85C40638
8058efb4 ff752c push dword ptr [ebp+2Ch]
8058efb7 ff7528 push dword ptr [ebp+28h]
8058efba ff7524 push dword ptr [ebp+24h]
8058efbd ff7520 push dword ptr [ebp+20h]
8058efc0 ff751c push dword ptr [ebp+1Ch]
8058efc3 ff7518 push dword ptr [ebp+18h]
8058efc6 ff7514 push dword ptr [ebp+14h]
8058efc9 ff7510 push dword ptr [ebp+10h]
8058efcc ff750c push dword ptr [ebp+0Ch]
8058efcf ff7508 push dword ptr [ebp+8]
8058efd2 e8a8b8feff call nt!NtAddAtom+0x19b (8057a87f)

Так вот, мой первый вопрос, что вобщем творится от
8058efad ea3806c4850800 jmp 0008:85C40638
до
8058efd2 e8a8b8feff call nt!NtAddAtom+0x19b (8057a87f)
И что делает функция my_function_detour_ntdeviceiocontrol()?

А второй вопрос следующий. Мигбот был написан под Win 2k , и исходя из общего концепта, стараюсь немного модифицировать код чтоб работал на Хр ну и на Висте потом. С NtDeviceIoControlFile всё вышло ок, но там собственно не сильно трудится нужно было. Следующая функция – SeAccessCheck. WinDbg view:

nt!SeAccessCheck:
80564948 8bff mov edi,edi
8056494a 55 push ebp
8056494b 8bec mov ebp,esp
8056494d 53 push ebx
8056494e 33db xor ebx,ebx
80564950 385d24 cmp byte ptr [ebp+24h],bl
80564953 0f8480300100 je nt!CcSetLogHandleForFile+0x263 (805779d9)
80564959 395d08 cmp dword ptr [ebp+8],ebx
8056495c 0f84a4cb0900 je nt!IoCheckFunctionAccess+0x2099f (80601506)
80564962 56 push esi
80564963 8b750c mov esi,dword ptr [ebp+0Ch]
80564966 391e cmp dword ptr [esi],ebx
80564968 0f85b72d0100 jne nt!RtlDeleteAtomFromAtomTable+0x3fc (80577725)
8056496e 57 push edi
.....

И модифицированный мною код:

NTSTATUS CheckFunctionBytesSeAccessCheck(){
int i=0;
char *p = (char *)SeAccessCheck;
//================================================== ===
// win 2k
//The beginning of the SeAccessCheck function
//should match:
//55 PUSH EBP
//8BEC MOV EBP, ESP
//53 PUSH EBX
//33DB XOR EBX, EBX
//385D24 CMP [EBP+24], BL
//================================================== ==
// win xp sp 3
// 8bff | mov edi, edi
// 55 | push ebp
// 8bec | mov ebp, esp
// 53 | push ebx
// 33db | xor ebx, ebx
// 385d24 | cmp byte ptr [ebp+24h], bl
// char c[] = { 0x55, 0x8B, 0xEC, 0x53, 0x33, 0xDB, 0x38, 0x5D, 0x24 }; // win 2k
char c[] = {0x8b, 0xff, 0x55, 0x8b, 0xec, 0x53, 0x33, 0xdb, 0x38, 0x5d, 0x24}; // win xp sp 3

// while(i<9)
while (i<11)
{
DbgPrint("SeAccessCheck - 0x%02X ", (unsigned char)p[i]);
if(p[i] != c[i])
{
return STATUS_UNSUCCESSFUL;
}
i++;
}
return STATUS_SUCCESS;
}

__declspec(naked) my_function_detour_seaccesscheck()
{
__asm
{
// exec missing instructions
// push ebp // win 2k
// mov ebp, esp
// push ebx
// xor ebx, ebx
// cmp [ebp+24], bl

mov edi, edi // win xp sp3
push ebp
mov ebp, esp
push ebx
xor ebx, ebx
cmp byte ptr [ebp+24h], bl
_emit 0xEA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0x08
_emit 0x00
}
}

VOID DetourFunctionSeAccessCheck()
{
char *actual_function = (char *)SeAccessCheck;
char *non_paged_memory;
unsigned long detour_address;
unsigned long reentry_address;
int i = 0;

// char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90, 0x90 };// win 2k
char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00 }; // win xp sp3
// reentry_address = ((unsigned long)SeAccessCheck) + 9; // win 2k
reentry_address = ((unsigned long)SeAccessCheck) + 11; // win xp sp3

non_paged_memory = ExAllocatePool(NonPagedPool, 256);
for(i=0;i<256;i++)
{
((unsigned char *)non_paged_memory)[i] = ((unsigned char *)my_function_detour_seaccesscheck)[i];
}

detour_address = (unsigned long)non_paged_memory;

*( (unsigned long *)(&newcode[1]) ) = detour_address;

for(i=0;i<200;i++)
{
if( (0xAA == ((unsigned char *)non_paged_memory)[i]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+1]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+2]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+3]))
{
*( (unsigned long *)(&non_paged_memory[i]) ) = reentry_address;
break;
}
}

for(i=0;i < 11;i++) // win xp sp3
{
actual_function[i] = newcode[i];
}
}

Вродебы всё как в оригинале но к сожалению не пашет. После исполнения кода, SeAccessCheck в ВинДбг выглядит так:

nt!SeAccessCheck:
80564948 ea3814e1850800 jmp 0008:85E11438
8056494f 8b9851730f84 mov ebx,dword ptr [eax-7BF08CAFh]
80564955 803001 xor byte ptr [eax],1
80564958 0039 add byte ptr [ecx],bh
8056495a 5d pop ebp
8056495b 080f or byte ptr [edi],cl
8056495d 84a4cb0900568b test byte ptr [ebx+ecx*8-74A9FFF7h],ah
80564964 750c jne nt!SeAccessCheck+0x2a (80564972)
...

А в детекторе руткитов картина следующая:

// Для NtDeviceIoControlFile
hooked address: 0x8058efad
relative to export: NtDeviceIoControlFile
redirect to: -
in exection path: NtDeviceIoControlFile

// для SeAccessCheck
hooked address: 0x80554948
relative to export: SeAccessCheck
redirect to: -
in exection path: NtAssignProcessToJobObject

А сам вызов функции – сразу BSOD.
т.е что-то я напартачил сильно и сам не знаю что. И вот хочу спросить что делаю не то и вообще правильным ли путём иду? Заранее спасибо

Flame of Soul
16.04.2009, 22:27
char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00 }; // win xp sp3
.....
reentry_address = ((unsigned long)SeAccessCheck) + 11; // win xp sp3
.....
.....
for(i=0;i < 11;i++) // win xp sp3
{
actual_function[i] = newcode[i];
}
не вникала толком в код (2 часа ночи), но почему 11 циклов? и где в newcode эти элементы?

csrss
17.04.2009, 01:47
Да, благодарю. nop'ы забыл, а должно выглядеть так

char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00 , 0x90, 0x90, 0x90, 0x90};

Но проблема дальше остаётся. Удачный hook получается только с функциями, которые в windbg выглядят примерно так (как NtCreateFile)

nt!NtCreateFile:
8056cdc0 8bff mov edi,edi
8056cdc2 55 push ebp
8056cdc3 8bec mov ebp,esp
8056cdc5 33c0 xor eax,eax
8056cdc7 50 push eax
8056cdc8 50 push eax
8056cdc9 50 push eax
8056cdca ff7530 push dword ptr [ebp+30h]
8056cdcd ff752c push dword ptr [ebp+2Ch]
8056cdd0 ff7528 push dword ptr [ebp+28h]
8056cdd3 ff7524 push dword ptr [ebp+24h]
8056cdd6 ff7520 push dword ptr [ebp+20h]
8056cdd9 ff751c push dword ptr [ebp+1Ch]
8056cddc ff7518 push dword ptr [ebp+18h]
8056cddf ff7514 push dword ptr [ebp+14h]
8056cde2 ff7510 push dword ptr [ebp+10h]
8056cde5 ff750c push dword ptr [ebp+0Ch]
8056cde8 ff7508 push dword ptr [ebp+8]
8056cdeb e87bfeffff call nt!IoCreateFile (8056cc6b)

//8056cdc0 8bff mov edi,edi
//8056cdc2 55 push ebp
//8056cdc3 8bec mov ebp,esp
//8056cdc5 33c0 xor eax,eax
//8056cdc7 50 push eax
//8056cdc8 50 push eax
//8056cdc9 50 push eax

NTSTATUS CheckFunctionBytesNtCreateFile(){
int i=0; char *p = (char *)NtCreateFile;
char c[] = {0x8b, 0xff, 0x55, 0x8b, 0xec, 0x33, 0xc0, 0x50, 0x50, 0x50}; // win xp sp3
while(i<10){DbgPrint("NtCreateFile - 0x%02X ", (unsigned char)p[i]);if(p[i] != c[i]){return STATUS_UNSUCCESSFUL; }i++;}
return STATUS_SUCCESS;
}

__declspec(naked) my_function_detour_NtCreateFile(){
__asm{
mov edi,edi
push ebp
mov ebp,esp
xor eax,eax
push eax
push eax
push eax
_emit 0xEA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0x08
_emit 0x00
}
}
VOID DetourFunctionNtCreateFile(){
char *actual_function = (char *)NtCreateFile;
char *non_paged_memory;
unsigned long detour_address;
unsigned long reentry_address;
int i = 0;

char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90, 0x90, 0x90}; // win xp sp3
reentry_address = ((unsigned long)NtCreateFile) + 10; // win xp sp3
non_paged_memory = ExAllocatePool(NonPagedPool, 256);

for(i=0;i<256;i++){
((unsigned char *)non_paged_memory)[i] = ((unsigned char *)my_function_detour_NtCreateFile)[i];
}

detour_address = (unsigned long)non_paged_memory;
*( (unsigned long *)(&newcode[1]) ) = detour_address;

for(i=0;i<200;i++){
if( (0xAA == ((unsigned char *)non_paged_memory)[i]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+1]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+2]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+3])){
*( (unsigned long *)(&non_paged_memory[i]) ) = reentry_address;
break;
}
}
for(i=0;i < 10 ;i++){
actual_function[i] = newcode[i];
}
}

После чего имеем
nt!NtCreateFile:
8056cdc0 ea005fe1850800 jmp 0008:85E15F00
8056cdc7 90 nop
8056cdc8 90 nop
8056cdc9 90 nop
8056cdca ff7530 push dword ptr [ebp+30h]
8056cdcd ff752c push dword ptr [ebp+2Ch]
8056cdd0 ff7528 push dword ptr [ebp+28h]
8056cdd3 ff7524 push dword ptr [ebp+24h]
8056cdd6 ff7520 push dword ptr [ebp+20h]
8056cdd9 ff751c push dword ptr [ebp+1Ch]
8056cddc ff7518 push dword ptr [ebp+18h]
8056cddf ff7514 push dword ptr [ebp+14h]
8056cde2 ff7510 push dword ptr [ebp+10h]
8056cde5 ff750c push dword ptr [ebp+0Ch]
8056cde8 ff7508 push dword ptr [ebp+8]
8056cdeb e87bfeffff call nt!IoCreateFile (8056cc6b)
И всё работает ОК. А вот например с этим SeAccessCheck ну просто ни как :(

slesh
17.04.2009, 10:13
Довольно простой код реализующеий перехват через сплайсинг.
Принцип работы этого чуда таков:
1) проверяется ровное кол-во команда по адресу функции которую ты собираешься перехватывать. Чтобы точно знать что там не стоит чьихто уже хуков ну и проверка на версию ядра такаяже. Это всё делает CheckFunctionBytesNtDeviceIoControlFile
2) DetourFunctionNtDeviceIoControlFile как раз делает подменяет первое целое кол-во команд на адерс перехода на твою функцию.
ТАм reentry_address будет содержать адрес кода который идет после этих команд
Тебе достаточно только 7 байт подменить (чтобы засунуть свой jmp)
Вот к примеру есть у тебя

nt!NtDeviceIoControlFile:
8058efad 8bff mov edi,edi
8058efaf 55 push ebp
8058efb0 8bec mov ebp,esp
8058efb2 6a01 push 1
8058efb4 ff752c push dword ptr [ebp+2Ch] <-------reentry_address будет ссылатьс яна этоместо
8058efb7 ff7528 push dword ptr [ebp+28h]

То заменим 7 байт на JMP на твою функцию
8058efad ea3806c4850800 jmp 0008:85C40638
8058efb4 ff752c push dword ptr [ebp+2Ch]
8058efb7 ff7528 push dword ptr [ebp+28h]

И теперь при выполненнии данной функции управление перейдет по твоему адресу.
Затем ты чтото делает в своём обработчики. Далее тебе нужно выполнить команды которые ты затер. Вот там как раз и видно где они выполняются.
А далее идет переход на reentry_address. Причем оформлено это в виде

_emit 0xEA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0xAA
_emit 0x08
_emit 0x00

3) non_paged_memory = ExAllocatePool(NonPagedPool, 256); ты юзаешь чтобы выделить память гденить в ядре, скопировать туда свой код обработчика. т.к. память доступка для записи, то тымета в обработчике где стояли _emit забиваешь адресов содержащимся в reentry_address и в этоге конец обработчика выглядит как
JMP XXXX:XXXXXXXX
Выделяется память только для того чтобы хук работал после выгрузки драйвера, ну и также чтобы была возможность записать вконце JMP на оригенальное продолжение функии которую ты перехватываешь

slesh
17.04.2009, 10:24
my_function_detour_NtCreateFile - это какраз код который будет выполняться при вызове функции которую ты перехватываеш.
А на счет ошибки то обрати внимание:

ДО

nt!SeAccessCheck:
80564948 8bff mov edi,edi
8056494a 55 push ebp
8056494b 8bec mov ebp,esp
8056494d 53 push ebx
8056494e 33db xor ebx,ebx
80564950 385d24 cmp byte ptr [ebp+24h],bl
80564953 0f8480300100 je nt!CcSetLogHandleForFile+0x263 (805779d9) <---0f8480300100
80564959 395d08 cmp dword ptr [ebp+8],ebx

ПОСЛЕ
nt!SeAccessCheck:
80564948 ea3814e1850800 jmp 0008:85E11438
8056494f 8b9851730f84 mov ebx,dword ptr [eax-7BF08CAFh] <- 0f848
80564955 803001 xor byte ptr [eax],1 <- 0300100
80564958 0039 add byte ptr [ecx],bh
8056495a 5d pop ebp
8056495b 080f or byte ptr [edi],cl
8056495d 84a4cb0900568b test byte ptr [ebx+ecx*8-74A9FFF7h],ah
80564964 750c jne nt!SeAccessCheck+0x2a (80564972)
...


Из этого следует что после 7 байт JMP ты записал 4 байта мусора - 8b985173
А нужно было туда нопы кидыть

csrss
17.04.2009, 13:51
Пасиб Slesh, концепт усвоил.