这篇文章算是我的学习笔记吧,贴之与菜鸟们一起学习.因为偶也是菜鸟,很多概念性的,技术性的讲得不好还请矫正,好了,开始:
  当我们的程序调用一个API时候,比如CreateWindowEx,在编译的时候生成代码
call        dword ptr 
[__imp__CreateWindowExA@48],这个API的地址就在__imp__CreateWindowExA@48中,在程序加载运行的时候,__imp__CreateWindowExA@48代表CreateWindowEx的内存地址,于是如果程序运行时候,我们把__imp__CreateWindowExA@48改成自己函数的地址,那当程序调用CreateWindowEx,实际上是跳到了我们的函数里,这就是IAT HOOK的基本思想,
  这篇文章是钩住驱动程序的IAT,不过这之前,先看一下用户态于IAT有关的一些信息,这有利于菜鸟理解接下来的驱动代码,(我也是菜鸟)因为驱动程序和一般的EXE程序一样,都是PE格式
  下面的这段用户代码基本是遍历遍历本程序的导入函数,然后打出来

#include <windows.h>
#include <stdio.h>
#include <imagehlp.h>
#pragma comment(lib,"imagehlp.lib")

int main()
{
  
  char *dll_name[30];
    char* function_name[30];
  HMODULE hInstance; 
    int outloop_index=0;
    
    PIMAGE_THUNK_DATA thunk;
  hInstance=GetModuleHandle(NULL);
    IMAGE_DOS_HEADER *dosheader= (IMAGE_DOS_HEADER *)hInstance;
    IMAGE_NT_HEADERS *ntheader= (IMAGE_NT_HEADERS *)((DWORD)hInstance + dosheader->e_lfanew);
    IMAGE_DATA_DIRECTORY *pSymbolTable= &ntheader->OptionalHeader.DataDirectory[1];
    IMAGE_IMPORT_DESCRIPTOR *pImportDesc =(IMAGE_IMPORT_DESCRIPTOR *)((DWORD)hInstance + pSymbolTable->VirtualAddress);
    
        printf("the module is load at:%d\n",hInstance);
      printf("the pointer to ImportDesc is: %d\n",pImportDesc);
   
   while(pImportDesc ->FirstThunk)
{
       dll_name[outloop_index]=(char*)((PBYTE)hInstance+pImportDesc->Name);
     printf("------%s-------\n",dll_name[outloop_index]);
     outloop_index++;

    thunk=( PIMAGE_THUNK_DATA)((PBYTE)hInstance+ pImportDesc->OriginalFirstThunk);

    int x=0;
    while(thunk->u1.Function)
    {
        function_name[x]=(char*)((PBYTE)hInstance +
                ( DWORD)thunk->u1.AddressOfData+2);
    printf("%s\n",function_name[x]);
        
    x++; 
    thunk++;
    }

   pImportDesc++;
}

   return 0 ;
}
好了,这里的hInstance是由hInstance=GetModuleHandle(NULL)获得,hInstance表示当前程序装载的基址,这个基地址很重要,没有它就不能正确定位调用函数在内存中的地址,
在驱动程序,这里我们假使要HOOK C:\WINDOWS\SYSTEM32\test_sysnap.sys,test_sysnap.sys
这个程序很简单,就是用DPC定时输出一个字符串:
VOID Myroutine(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)

{
    HANDLE id=PsGetCurrentProcessId();
 
    DbgPrint("the Current Process Id is:%x \n",id);

}
嗯,那test_sysnap.sys就一定导入了PsGetCurrentProcessId,我们就是要IAT HOOK这个函数,这样以后系统调用Myroutine执行到PsGetCurrentProcessId,都会转到我们自己的程序里运行

在讲用户程序的时候,我们说过hInstance很重要,那驱动程序的IAT HOOK,我们也需要知道test_sysnap.sys被加载在驱动程序里的什么地址,这里我们用ZwQuerySystemInformation这个函数就可以获得,前提,这个函数学没有被HOOK,这里我们就是这么假使滴,好了,先运行test_sysnap.sys,代码在后面在最后-----------------------------------------------------------------------------------------------------------------------
再看下面的驱动程序
#include "ntddk.h"
#include "hookiat.h"
#pragma comment(lib,"ntdll.lib")
  
PVOID GetDriverBaseAdress(char* driverName)
{
  ULONG size,index;
  PULONG buf;
  PSYSTEM_MODULE_INFORMATION module;
  PVOID driverAddress=0;

  ZwQuerySystemInformation(SystemModuleInformation,&size, 0, &size);

  if(NULL==(buf = (PULONG)ExAllocatePool(PagedPool, size)))
  {
    DbgPrint("failed alloc memory failed  \n");
    return 0;
  }
     
  status=ZwQuerySystemInformation(SystemModuleInformation,buf, size , 0);
  if(!NT_SUCCESS( status ))
  {
       DbgPrint("failed  query\n");
     return 0;
  }

  module = (PSYSTEM_MODULE_INFORMATION)(( PULONG )buf + 1);
  
  for (index = 0; index < *buf; index++)
  if (_stricmp(module[index].ImageName + module[index].ModuleNameOffset, driverName) == 0)  
  {
    driverAddress = module[index].Base;
        DbgPrint("Module found at:%x\n",driverAddress);
  }
  ExFreePool(buf);
  return driverAddress;
}


VOID Unload(PDRIVER_OBJECT  DriverObject)
{    
    DbgPrint("Unload Called \r\n");
}


NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING str)
{
  
  PVOID base = NULL;
        base = GetDriverBaseAdress("test_sysnap.sys.sys");
        DriverObject->DriverUnload = Unload;
  return STATUS_SUCCESS;
}


编译一下,确实真实 DbgPrint了test_sysnap.sys加载的地址,/////好了,我们的目标是找到call        dword ptr 
[__imp__PsGetCurrentProcessId@XX],修改之
首先我们要定位IAT,找出PsGetCurrentProcessId在其中的RAV////,最后目的就是得到(DWORD*)( (BYTE*)base + Thunk ) + RVA,修改之,这里base已经得到了,接下来就是Thunk和RVA,
首先我们用ZwOpenFile,ZwCreateSection,ZwMapViewOfSection几个函数来映射test_sysnap.sys,其实跟方法就是跟用户程序使用CreateFile,CreateFileMapping,MapViewOfFile一样

PVOID CreateMapFileAndReturnBaseAddress(PUNICODE_STRING pDriverName)
{
  HANDLE  hFile;
//HANDLE  hSection看需要在别的地方ZWclose(hSection),若不用.那定义成局部变量就可以了,或者做为数参数传递
    char *pszModName;
    PVOID MapFileBaseAddress = NULL;
  SIZE_T size=0;
    IO_STATUS_BLOCK stataus;
    OBJECT_ATTRIBUTES oa ;
    
  InitializeObjectAttributes(
    &oa,
  pDriverName,
    OBJ_CASE_INSENSITIVE,
    0,
    0
    );

ZwOpenFile(&hFile, 
       FILE_EXECUTE | SYNCHRONIZE, 
       &oa,
       &stataus, 
       FILE_SHARE_READ, 
       FILE_SYNCHRONOUS_IO_NONALERT);
 oa.ObjectName = 0;

 ZwCreateSection(&hSection,
        SECTION_ALL_ACCESS,
        &oa,
        0,
        PAGE_EXECUTE, 
        SEC_IMAGE, 
        hFile);
 ZwMapViewOfSection(hSection,
           PsGetCurrentProcessId(),
           &MapFileBaseAddress, 
           0, 
           1024,
           0, 
           &size,
           ViewShare,
           MEM_TOP_DOWN, 
           PAGE_READWRITE); 
ZwClose(hFile);
DbgPrint("baseadress:%x\n",MapFileBaseAddress);
return MapFileBaseAddress;
  
}

把这个函数加上去, DriverEntry添加:
UNICODE_STRING driverName;
RtlInitUnicodeString(&driverName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\drivers\\test_sysnap.sys");
    BaseAddress= CreateMapFileAndReturnBaseAddress(&driverName);
DbgPrint("MapFile Return Address:%x",BaseAddress);
编译一下,运行后没问题,GOOD,继续,到这里我们已经获得了MapFileBaseAddress,接着就是用它来获得ImageImportDescriptor

DWORD GetImageImportDescriptorPointer(IN OUT HANDLE* hMod, IN OUT IMAGE_IMPORT_DESCRIPTOR** pImportDesc)
  {
    
  IMAGE_DOS_HEADER * dosheader;
  IMAGE_OPTIONAL_HEADER * optheader;
    PVOID BaseAddress = NULL;
    UNICODE_STRING driverName;
    
  RtlInitUnicodeString(&driverName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\drivers\\test_sysnap.sys");
    BaseAddress=  CreateMapFileAndReturnBaseAddress(&driverName);
    *hMod = BaseAddress;
    
  dosheader= (IMAGE_DOS_HEADER *)BaseAddress;
    optheader =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)BaseAddress+dosheader->e_lfanew+24);
    *pImportDesc = (IMAGE_IMPORT_DESCRIPTOR *)((BYTE*)dosheader+ optheader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
    if( NULL == (*pImportDesc)) return 0;
  else
    return 1;
    DbgPrint("DataEntryAddress:%x\n",pImportDesc);
  
  }
照样把该函数学添加上去,自己加点测试的,编译一下,运行没什么问题,这样我们就得到了
MapFileBaseAddress和ImageImportDescriptor,接着就是用他们来找 Thunk和RVA

DWORD GetFunctionThunkAndRav(IN char* lpFunctionName, IN char* lpFunctionLibrary, OUT DWORD* pThunk, OUT DWORD* pRVA) 
{
  
  HANDLE   hMod;
  IMAGE_IMPORT_DESCRIPTOR * pImportDesc;
  IMAGE_THUNK_DATA* thunk;
  char *pszModName;
  DWORD firstThunkList;
    DWORD ret;
  BOOLEAN isOrdinal;
    BOOLEAN foundIt;
  
  int x=0;
    SIZE_T size=0;
   
  ret=GetImageImportDescriptorPointer(&hMod,&pImportDesc);
  if(ret==0)
  {
       DbgPrint("GetImageImportDescriptorPointer return NULL");
    return 0;
  }
      
  //遍历IMPORT DIRECTORY TABLE,找到ntoskrnl.exe对应的IMAGE_IMPORT_DESCRIPTOR
  while (pImportDesc->FirstThunk)
  {    
    pszModName = (PSTR) ((PBYTE) hMod + pImportDesc->Name);
    if (_stricmp(pszModName, lpFunctionLibrary) == 0 ) 
    {
      foundIt = TRUE;
            DbgPrint("name:%s\n",pszModName);
      break;
    }
    pImportDesc++;
  }

  if(foundIt==FALSE)
  {
    return 0;
  }

  
  
  //得到ntoskrnl.exe的IMAGE_IMPORT_DESCRIPTOR,就可以其IAT,IAT中就可以找到导出函数了
  
  thunk = (IMAGE_THUNK_DATA*)( (BYTE*)hMod + pImportDesc->OriginalFirstThunk);
    firstThunkList = (DWORD)((PBYTE)hMod + pImportDesc->FirstThunk);
    foundIt = FALSE;
while(thunk->u1.Function)
  {
    
    isOrdinal = 0;
    //IMAGE_THUNK_DATA其实就是一个DWORD,它要么是Ordinal,要么是AddressOfData
    if(thunk->u1.Function >= 0x01000000) isOrdinal = TRUE;
    if(!isOrdinal) // 以名字到处而不是序号
    {
      //IMAGE_IMPORT_BY_NAME
      char* functionName = (char*)( (BYTE*)hMod + (DWORD)thunk->u1.AddressOfData + 2 );
      if (_stricmp(functionName, lpFunctionName) == 0 ) 
      {
        *pThunk = pImportDesc->FirstThunk;
        *pRVA = x;
        DbgPrint("%x",( DWORD *)((PBYTE)hMod +pImportDesc->FirstThunk)+x);
          ZwClose(hSection);
        return 1;
      }
    }
    if(isOrdinal)
    {
      ZwClose(hSection);
      return (DWORD) NULL;
    }

    x++;
    thunk++;
    firstThunkList++;
  }
  

if(foundIt==FALSE)
  {
    ZwClose(hSection);
    return 0;
  }
  ZwClose(hSection);
  return 0;

}
添加这个函数,稍微测试下,没什么问题,好的,这里我们已经得到(DWORD*)( (BYTE*)base + Thunk ) + RVA,接着就是最后的工作,修改之
 g_FunctionInMemory = (DWORD*)( (BYTE*)base + Thunk ) + RVA;
.
.  
_asm
  {
    CLI          
    MOV  EAX, CR0    
    AND EAX, NOT 10000H 
    MOV  CR0, EAX    
  }

  *(PVOID*)g_FunctionInMemory = MyPsGetCurrentProcessId;
    DbgPrint("HOOK SUCESS");
  _asm 
  {
    MOV  EAX, CR0    
    OR  EAX, 10000H      
    MOV  CR0, EAX      
    STI          
  }
test_sysnap.sys的代码可以在http://hi.baidu.com/sysnap找到
到此为止,我们的工作就完成了,结果就是这样:

上传的附件 钩住驱动程序导入表.rar