大家有没有想过,一些系统监控软件是如何得知我们所进行的操作的?杀软启发式分析是如何对病毒行为进行拦截和监控的?外挂又是如何读取到游戏的内部数据的?这些功能的实现,基本都有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活动图是:

wKioL1Vi6N3T5fDDAAGsIxgoUuU722.jpg

    至此,一个API HOOK基本逻辑已经被实现,可是这段代码仅对本程序有效,那么,如何让其他程序有效呢?这就需要DLL注入技术,实现DLL注入的方法很多,可以利用操作系统消息钩子、远程线程注入等多种方法完成注入,温馨提示,要注入到远程进程进行拦截,因为需要DLL注入技术,因此需要将API HOOK 代码写入DLL后,通过远程加载DLL完成拦截操作,是非DLL之前记得恢复API HOOK设置,否则将导致异常。

    看到这里,相信大家大致已经知道API HOOK技术实现的具体细节了,现在动动你的手指,相信你也可以自己实现一个API HOOK