大家有没有想过,一些系统监控软件是如何得知我们所进行的操作的?杀软启发式分析是如何对病毒行为进行拦截和监控的?外挂又是如何读取到游戏的内部数据的?这些功能的实现,基本都有API HOOK存在。API HOOK分为ring0和ring3层,这里我们以ring3层API HOOK 进行讲解分析。
API HOOK 在ring 3的实现,分为inline 和修改导入表2种方法,所谓inline,是指直接写入并覆盖函数开头字节汇编码的方法,这种方法有一个问题,便是他被杀软重点监控,成功率极低,而修改导入表的方法,则是指直接通过修改导入表,拦截特定函数调用序列的方法,优缺点不一,由于本文只关注过导入表修改法的API HOOK,因此本文重点讲解修改导入表方法。
首先让我们讲讲导入表的结构吧,这种表的目的是记录外部DLL导入函数地址的,通过修改导入表的表项指针, 能够使用户的实际调用序列转入你的函数中,此时对用户来说,你的函数便是他要调用的“API”,映像数据目录的结构如下:
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
根据这个结构,我们知道,导入表每一个项,有2个数据构成,一个是相对虚拟地址的地址,一个表项的大小,导入表的结构是:
typedef struct _IMAGE_THUNK_DATA32 { union { PBYTE ForwarderString; PDWORD Function; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; } u1; } IMAGE_THUNK_DATA32;
导入表分别记录着:
ForwarderString 指向一个转向者字符串的RVA
Function 被输入的函数的内存地
Ordinal 被输入的API的序数
AddressOfData 指向IMAGE_IMPORT_BY_NAME
首先,第一个问题来了,如何获取IMAGE_DATA_DIRECTORY的指针呢,可以通过ImageDirectoryEntryToData获取,该函数定义为
PVOID WINAPI ImageDirectoryEntryToData( _In_ PVOID Base, _In_ BOOLEAN MappedAsImage, _In_ USHORT DirectoryEntry, _Out_ PULONG Size );
函数一共4个参数分别为一下含义:
Base:映像基地址
MappedAsImage:它为true时,系统将该模块以映像文件的形式映射,否则以数据文件的形式映射。
DirectoryEntry:要获得的段的索引,可以为以下值
Size:这是一个输出参数,表示输出的信息的 大小
根据函数形式,我们将执行
Import=(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(Module,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&Size);
现在,我们得到了导入表,如果返回值有效,我们将执行一个循环以进行遍历导入表,首先先看看这个循环:
while(Import->Name) { PSTR ModuleName=(PSTR)((PBYTE)Module+Import->Name); if(lstrcmpA(ModuleName,APIModuleName)==0) { PIMAGE_THUNK_DATA Thunk=(PIMAGE_THUNK_DATA)((PBYTE)Module+Import->FirstThunk); while(Thunk->u1.Function) { PROC *Proc=(PROC *)&Thunk->u1.Function; BOOL bFound=(*Proc==APIFunName); if(bFound) { if(!WriteProcessMemory(GetCurrentProcess(),Proc,&Function,sizeof(Function),NULL)&&(ERROR_NOACCESS==GetLastError())) { DWORD OldProtect=0; if(VirtualProtect(Proc,sizeof(Function),PAGE_WRITECOPY,&OldProtect)) { WriteProcessMemory(GetCurrentProcess(),Proc,&Function,sizeof(Function),NULL); VirtualProtect(Proc,sizeof(Function),OldProtect,&OldProtect); } } break; } Thunk++; } } Import++; }
这段代码转换成UML活动图是:
至此,一个API HOOK基本逻辑已经被实现,可是这段代码仅对本程序有效,那么,如何让其他程序有效呢?这就需要DLL注入技术,实现DLL注入的方法很多,可以利用操作系统消息钩子、远程线程注入等多种方法完成注入,温馨提示,要注入到远程进程进行拦截,因为需要DLL注入技术,因此需要将API HOOK 代码写入DLL后,通过远程加载DLL完成拦截操作,是非DLL之前记得恢复API HOOK设置,否则将导致异常。
看到这里,相信大家大致已经知道API HOOK技术实现的具体细节了,现在动动你的手指,相信你也可以自己实现一个API HOOK