Hook engine
Движок для перехвата функций. Подойдет как для ring3 перехватов, так и для ring0. Проблемы могут возникнуть при копировании call в тело функции, но я думаю этого не будет.
Функции аллока и освобождения памяти намеренно убраны, т.к. для разных ring свои функции аллока памяти.
Работает так: дизассемблирует начальный код функции, и копирует в буфер столько кода, чтобы заместо него поместился call. После копирования, начало функции заменяется на call <> + nop для добивки остатков. При исполнении функции, вызывается колл, который вызывает функцию обработки, а затем переходит на исполнение первоначальных команд из функции.
Не будет работать, если сразу же в начале функции есть любые джампы или коллы. В остальных случаях все ок.
Код (ASM):
.686 .model flat, stdcall include windows.inc .data my_struct db 20h dup(4) ; Структура для одного перехвата hook_table dd 10h dup(3) ; Таблица с перехваченными функциями hook_table_c dd 0h ; .code _salc equ <db 0D6h> ;sbb al,al NrmTabLen equ 53 _aam macro num ;ah=al/num;al=al mod num db 0D4h, num endm _aad macro num ;al=ah*num+al;ah=0 db 0D5h, num endm VirXasm32 proc ; ==========[ disassemble instruction from esi ]=========== pushad ; ==========[ push packed tables to stack]========= push 000001510h push 0100101FFh push 0FFFFFF55h push 0FFFFFFF8h push 0F8FF7FA0h push 00F0EC40Dh push 007551004h push 001D005FFh push 0550D5D55h push 0555F0F88h push 0F3F3FFFFh push 00A0C1154h mov edx, esi ; store esi mov esi, esp ; esi = address of unpacked tables ; ==========[ push unpack info bits to stack]========== push 11001b push 10110000101011000000101110000000b push 10111111101100011111001100111110b push 00000000000100011110101001011000b mov ebx, esp ; ebx = address of unpack info bits sub esp, 110 ; reserve stack for unpacked tables mov edi, esp ; edi = address of buffer for unpacked tables ; ==========[ unpack tables to stack ]========== cld push 100 pop ecx ; total size of unpacked tables is 100 bytes xa_nxtIndx: ;*xor al,al bt [ebx], ecx ; get unpack bit to CF _salc ; if bit==0 then al=0 (salc==sbb al,al but not change flags) jnc xa_is0 ; if bit!=0 then ... lodsb ; ... load byte from tables in AL ... xa_is0: stosb ; ... else write zero loop xa_nxtIndx ; next byte mov esi, edx ; restore esi ; ==========[ process fucking opcodes ]========== push 2 pop ebx ; ebx=2 (current mode - 32 bits) mov edx, ebx ; edx=2 (current addressing mode - 32 bits) xa_NxtByte: lodsb push eax push eax ; double store AL cmp al, 66h ; if 66h present then ... cmove ebx, ecx ; ... ebx=ecx=0 (current mode - 16 bits) *jne n32;xor ebx,ebx;n32: cmp al, 67h ; if 67h present then ... cmove edx, ecx ; ... edx=ecx=0 (current addressing mode - 16 bits) cmp al, 0EAh ; JMP FAR je xa_jmp cmp al, 09Ah ; CALL FAR jne xa_nocall xa_cll: inc esi xa_jmp: lea esi, [esi+ebx+3] ; for JMP byte imm will be later xa_nocall: cmp al, 0C8h ; fucking ENTER i16,i8 :[ je xa_i16 and al, 0F7h ; C2h, CAh cmp al, 0C2h ; IRET i16 RET i16 :[ jne xa_no16 xa_i16: inc esi inc esi ; imm16 ; ==========[ process prefixes ]========== xa_no16: and al, 0E7h cmp al, 26h ; 26h,2Eh,36h,3Eh (ES,CS,SS,DS) pop eax ; first restore AL (don't change flags) je xa_PopNxt cmp al, 0F1h ; int1 je xa_F1 and al, 0FCh cmp al, 0A0h ; mov eax,[off16/32] jne xa_noMOV lea esi, [esi+edx+2] xa_noMOV: cmp al, 0F0h ; F0h-F3h (LOCK,..,REPE,REPNE) je xa_PopNxt xa_F1: cmp al, 64h ; 64h,65h,66h,67h (FS,GS,Prfx66,Prfx67) xa_PopNxt: pop eax ; second restore AL (don't change flags) je xa_NxtByte ; ==========[ prepare opcode ]========== mov edi, esp ; edi = normal table (line info bits) push edx ; store addressing mode flag push eax ; store opcode value cmp al, 0Fh jne xa_Nrm ; if ext. group code then ... lodsb ; ... load byte of ext. code xa_Nrm: pushfd ; store FLAGS _aam 10h ;*mov ah,al;shr ah,4;and al,0Fh xchg cl, ah ; high part of ecx still is zero cwde ;*nothing cdq ;*xor edx,edx (bicoz eax>0) xor ebp, ebp popfd ; restore FLAGS jne xa_NrmGroup ; ==========[ extended group ]========== xa_ExtGroup: add edi, NrmTabLen ; edi = extended table (line info bits) jecxz xa_@3 xa_@1: bt [edi], ebp jnc xa_@2 ; is not ModR/M only line? inc edx ; yeah xa_@2: inc ebp loop xa_@1 jc xa_@3 _salc ;*xor al,al cdq ;*xor edx,edx xa_@3: shl edx, 1 jmp xa_ProcOpcode ; ==========[ normal group ]========== xa_NrmGroup: sub cl, 4 jns xa_@4 mov cl, 0Ch ; bicoz 0xh,1xh,2xh,3xh are equal and al, 7 xa_@4: jecxz xa_4x xa_@5: adc dl, 1 ; in first pass CF==0 inc ebp bt [edi], ebp loop xa_@5 jc xa_ProcOpcode xa_4x: shr al, 1 ; al/2 ; ==========[ process additional fields ]========== xa_ProcOpcode: xchg cl, al lea edx, [edx*8+ecx]; edx=index of opcode in table pop ecx ; restore opcode value to CL pop ebp ; restore 67h flag bt [edi+2], edx ; edi+2=mod r/m info bits jnc xa_noModRM ; ==========[ process mod r/m bytes ]=========== xa_ModRM: lodsb _aam 8 ; ah=ModRO; al=RM shl ah, 4 ; CF=hi Mod; SF=lo Mod jnc xa_isModRM js xa_enModRM ; Mod==11 xa_isModRM: pushfd ; store FLAGS test ebp, ebp ; prefix 67 present jnz xa_addr32 sub al, 6 ; 16 bit addressing jnz xa_noSIB ; if RM==6(BP) then ... mov al, 5 ; ... offset 16 xa_addr32: ; 32 bit addressing cmp al, 4 jne xa_noSIB ; if RM==4 then ... (note: after addr16 al<2) lodsb ; ... get SIB and al, 7 ; al=BASE xa_noSIB: popfd ; restore FLAGS jc xa_iWD ; Mod==10 js xa_i8 ; Mod==01 cmp al, 5 ; if (RM==5BASE==5) && Mod==00 then ... (32 bit) jne xa_enModRM ; if RM==6 && Mod==00 then ... (16 bit) xa_iWD: add esi, ebp ; ... offset 16/32 inc esi xa_i8: inc esi ; offset 8 ; ==========[ fucking TEST!!! ]========== xa_enModRM: test ah, 60h ; if RO!=0 RO!=1 then ... jnz xa_noModRM ; ... go away xchg eax, ecx ; ... else al=cl=opcode cmp al, 0F6h ; TEST rm,i8 je xa_ti8 cmp al, 0F7h ; TEST rm,i16/i32 jne xa_noModRM add esi, ebx ; imm16/imm32 (66h prefix dependence) inc esi xa_ti8: inc esi ; imm8 ; ==========[ process immediate values ]========== xa_noModRM: shl edx, 1 ; edx*2 bt [edi+2+17], edx ; edi+2+17=immediate values info bits jnc xa_Exit inc edx bt [edi+2+17], edx jnc xa_im8 adc esi, ebx ; imm16/imm32, 66h prefix dependence (ebx) xa_im8: inc esi ; ==========[ return result and exit ]========== xa_Exit: add esp, 110+64 ; clear stack sub esi, [esp+4] ; esi=esi-old esi mov [esp+7*4], esi ; eax=esi popad ret VirXasm32 EndP Hook_Jump proc call Hook_Parse add esp, 4h ; inline of function call Hook_Parse db 0Bh dup (90h) Hook_Jump EndP Hook_Parse proc ret Hook_Parse EndP WriteHook proc Function:DWORD, HookCode:DWORD, HookCodeLen:DWORD, OldCodeBuffer:DWORD pushad mov ebx, HookCodeLen mov esi, Function loopme: call VirXasm32 add esi, eax sub ebx, eax jbe found_len jmp loopme found_len: neg ebx add ebx, HookCodeLen ; save to old buffer mov edi, OldCodeBuffer mov esi, Function call Copy_It mov edx, Function sub edx, ebx sub edx, edi add edx, 1h mov byte ptr [edi+ebx+00h], 0E9h mov dword ptr [edi+ebx+01h], edx ; write new code xchg esi, edi mov esi, HookCode call Copy_It mov dword ptr [esp+1ch], ebx popad ret WriteHook EndP Copy_It proc mov ecx, ebx cp_loop: mov al, byte ptr [esi+ecx-1] mov byte ptr [edi+ecx-1], al loop cp_loop retn Copy_It EndP Hook_Install proc function:DWORD ; Public function ; Нужно выделить память для структуры (8h + 4h + 4h + 10h байт) ; 8h code ; 10h oldcode + jmp back ; 4h real address ; 4h size mov eax, offset my_struct .if eax == 0 ;DbgPrnt "insufficient resources" jmp exit .endif ; Save in table mov ebx, hook_table_c mov dword ptr [hook_table+ebx*4], eax inc hook_table_c ; Prepare buffer mov ebx, dword ptr [Hook_Jump+00h] ; code mov dword ptr [eax+00h], ebx ; code mov ebx, dword ptr [Hook_Jump+04h] ; code mov dword ptr [eax+04h], ebx ; code mov ebx, offset Hook_Parse ; <call Hook_Parse> sub ebx, eax ; <call Hook_Parse> sub ebx, 5h ; <call Hook_Parse> mov dword ptr [eax+01h], ebx ; <call Hook_Parse> mov ebx, function ; function ptr mov dword ptr [eax+18h], ebx ; function ptr neg ebx ; prepare <call code> lea ecx, dword ptr [eax+ebx-05h] ; prepare <call code> mov dword ptr [Hook_Jump+09h], ecx ; prepare <call code> push eax add eax, 8h ; old code buffer invoke WriteHook, function, \ ; write hook addr Hook_Jump + 8h, 5, eax ; write hook pop ebx ; size mov dword ptr [ebx+1ch], eax ; size exit: ret Hook_Install EndP Hook_RemoveAll proc ; Public function pushad mov ecx, hook_table_c rm_loop: cmp ecx, 0 jle exit dec ecx push ecx mov eax, dword ptr [hook_table+ecx*4] lea esi, dword ptr [eax+08h] ; old code mov edi, dword ptr [eax+18h] ; function ptr mov ebx, dword ptr [eax+1ch] ; size call Copy_It ; Тут надо освободить память из eax pop ecx jmp rm_loop exit: mov hook_table_c, 0 popad ret Hook_RemoveAll EndP testcode proc ; Перехватываемая функция local teste:DWORD mov eax, 10 ret testcode EndP Testme proc invoke Hook_Install, addr testcode ; Устанавливаем хук invoke testcode ; Вызываем перехваченную функцию invoke Hook_RemoveAll ; Снимаем все хуки invoke testcode ; Проверяем снятие хуков ret Testme EndP End Testme
Hook engine. Может кому-то пригодится
Дата публикации 11 апр 2022
| Редактировалось 2 май 2022