通过堆栈回溯来挂钩未导出函数MmLoadSystemImage来进行拦截驱动,在这里感谢sudami大牛的帮助,本文已发在2010《黑客防线》第11期杂志上,代码如下:

代码:
#include <ntddk.h>
#include <windef.h>
#include <stdio.h>
#include <string.h>
#include "MmLoadSystemImage.h"


#define pwszModuleName L"ntoskrnl.exe" //这里只考虑单核的情况

#define ObjectNameInformation  1

UCHAR oldcode[5]={0};
UCHAR jmpcode[5]={0xE9,0x00,0x00,0x00,0x00};

ULONG g_ntoskrnl_addr=0;

ULONG g_ntoskrnl_size=0;

ULONG oldExAllocatePoolWithTagaddr=0;

ULONG Address_MmLoadSystemImage=0;//通过栈回溯(技术难点)得到MmLoadSystemImage函数的地址

/*这里说明一下原先通过东方微点的Mp110003的驱动是通过Hook KeWaitForSingleObject,然后通过栈回溯来找到MmLoadSystemImage的地址,但是通过我跟踪堆栈调用,发现要经过很多次KeWaitForSingleObject调用,才会进入到
KeWaitForSingleObject
MmLoadSystemImage
NtSetSystemInformation
nt!KiFastCallEntry
nt!ZwSetSystemInformation
反正是猥琐,我就更猥琐,大米兄就是这么干的,调用流程中来找到MmLoadSystemImage地址,调用过程如下:
ExAllocatePoolWithTag
MmLoadSystemImage
NtSetSystemInformation
nt!KiFastCallEntry
nt!ZwSetSystemInformation
因此我直接HOOK ExAllocatePoolWithTag来进行栈回溯
*/
__declspec(naked) PVOID fake_ExAllocatePoolWithTag(
                                                   IN POOL_TYPE  PoolType,
                                                   IN SIZE_T  NumberOfBytes,
                                                   IN ULONG  Tag)
{
       ULONG *PEBP;
  
      _asm{
          mov  edi,edi
          push ebp    
          mov  ebp,esp
          mov eax,[ebp+0xc] //参数是NumberOfBytes
          cmp eax,0x100
          jnz end
          mov eax,[ebp+0x10] //第三个参数Tag
          cmp eax,0x6E4C6D4D //标志为"nLmM",也就是MmLoadSystemImage装载的镜像标志
       jnz end
      
          mov eax,[ebp]  //堆栈回溯的原理
       mov eax,[eax+4]
          push eax        //上一个函数的地址
      call StackTrace_for_ExAllocatePoolWithTag    
end:
         mov  eax,oldExAllocatePoolWithTagaddr
         add  eax,5
         jmp  eax
    }
}


//通过栈回溯找到 
ULONG StackTrace_for_ExAllocatePoolWithTag(ULONG RetAddress)
{
  ULONG CallAddress=0;
  ULONG JmpOffset=0;
  
  if (RetAddress)
  {
  CallAddress=(ULONG)RetAddress-  5 ;//Ret --->call Address
  //跳转的偏移量
  JmpOffset=*(ULONG*)((PUCHAR)CallAddress+1); //(PUCHAR)CallAddress+1--->指针CallAddress往后走一位,(PULONG)CallAddress+1,指针CallAddress往后走(sizeof(ULONG)=4)字节
  //MmLoadSystemImage函数的地址
  Address_MmLoadSystemImage=(ULONG)CallAddress + 5 +JmpOffset;
  KdPrint(("MmLoadSystemImage Address=0x%x\n",Address_MmLoadSystemImage));
  //加入判定如果ntoskrnl.Base<Address_MmLoadSystemImage<ntoskrnl.Base+size,则说明可以进行inlineHook_MmLoadSystemImage()
  if (Address_MmLoadSystemImage>g_ntoskrnl_addr&&Address_MmLoadSystemImage<(g_ntoskrnl_addr+g_ntoskrnl_size))
  {
    unlineHook_ExAllocatePoolWithTag(); 
  inlineHook_MmLoadSystemImage(); 
    KdPrint(("inlineHook MmLoadSystemImage Success!")); 
  }   
  }
  return 0;    
}



__declspec(naked) NTSTATUS OrigiMmLoadSystemImage(
                                                 IN PUNICODE_STRING ImageFileName,
                                                 IN PUNICODE_STRING NamePrefix OPTIONAL,
                                                 IN PUNICODE_STRING LoadedBaseName OPTIONAL,
                                                 IN ULONG LoadFlags,
                                                 OUT PVOID *ImageHandle,
                                                 OUT PVOID *ImageBaseAddress)
{
  __asm{
            push 174h 
      mov eax,Address_MmLoadSystemImage
      add eax,5
      jmp eax 
  }
}



//拦截驱动的过程
NTSTATUS fake_MmLoadSystemImage(
                           IN PUNICODE_STRING ImageFileName,
                           IN PUNICODE_STRING NamePrefix OPTIONAL,
                           IN PUNICODE_STRING LoadedBaseName OPTIONAL,
                           IN ULONG LoadFlags,
                           OUT PVOID *ImageHandle,
                           OUT PVOID *ImageBaseAddress)
{
  ANSI_STRING ImageName;
 
  RtlUnicodeStringToAnsiString(&ImageName,ImageFileName,TRUE);

  KdPrint(("MmLoadSystemImage LoadDriver :%s\n",ImageName.Buffer));
  
  return OrigiMmLoadSystemImage(ImageFileName,
                              NamePrefix,
                  LoadFlags,
                  LoadFlags,
                  ImageHandle,
                  ImageBaseAddress);

}



NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PIRP Irp)
{
   NTSTATUS status;
   
   PEPROCESS crsEProc; 
   ANSI_STRING TestSysName;
   SYSTEM_LOAD_AND_CALL_IMAGE sysImage;

   
   DriverObject->DriverUnload=DriverUnload;
   status=PsLookupProcessByProcessId((ULONG)GetCsrPid(),&crsEProc); 
   if (!NT_SUCCESS(status))
   {
     KdPrint(("PsLookupProcessByProcessId() error\n"));
     return status;
   }   
   
   //通过DriverObject->DriverSection获得加载驱动的模块
   if (GetBase((ULONG)DriverObject)&&inlineHook_ExAllocatePoolWithTag())
  {
     //下面是调用ZwSetSystemInformation来加载驱动,从而引发跳转到fake_ExAllocatePoolWithTag中,先依附进csrss.exe,再加载测试驱动
     KeAttachProcess(crsEProc);
     //加载测试驱动; 
     RtlInitAnsiString(&TestSysName,"\\??\\c:\\Hooksys.sys");
     RtlAnsiStringToUnicodeString(&(sysImage.ModuleName),&TestSysName,TRUE);
   status= ZwSetSystemInformation(SystemExtendServiceTableInformation,&sysImage,sizeof(sysImage));
     if (!NT_SUCCESS(status))
     {         
       KdPrint(("Driver STATUS=0X%x\n",status));
   }
  
  KeDetachProcess();  
  }
   
  return STATUS_SUCCESS;
}

NTSTATUS DriverUnload(IN PDRIVER_OBJECT DriverObject )
{

  unlineHook_MmLoadSystemImage();
  KdPrint(("DriverUnload!\n"));
  return   STATUS_SUCCESS;

}


//得到模块的基址和映像大小
int GetBase(IN ULONG  EntryAddress)
{
  ULONG  listHead;
  ULONG  currentList;
    ULONG  NameAddress;
  int reault=0;
   
   listHead=*(ULONG*)(EntryAddress+0x14);
   currentList=*(ULONG*)(listHead+0x4); //Blink
  
   
   if (currentList!=listHead)
   {
      while (1)
      {      
    NameAddress=*(ULONG*)(currentList+0x030); 
      if ((PCWSTR)NameAddress!=NULL)
      {
        KdPrint(("ModuleName=%ws\n",(PCWSTR)NameAddress));
    }
           
    if(!_wcsicmp((PCWSTR)NameAddress,pwszModuleName))
        {
           g_ntoskrnl_addr=*(ULONG*)(currentList+0x18);
       g_ntoskrnl_size=*(ULONG*)(currentList+0x20);
       KdPrint(("ntoskrnl.exe [base]=0x%X,[size]=0x%X\n",g_ntoskrnl_addr,g_ntoskrnl_size));
       reault=1;
       break;
    }
              
      currentList=*(DWORD*)(currentList + 0x4);//下一个Blink 
      if (currentList == listHead)
    {
          KdPrint(("链表查询结束!\n"));
      break;
    }

    }      
   }
    
    return reault;
 
}

PVOID GetInfoTable(ULONG ATableType)
{
    ULONG mSize=0x4000;
  PVOID mPtr=NULL;
  NTSTATUS status;

    do 
    {
       mPtr=ExAllocatePool(PagedPool,mSize);
     memset(mPtr, 0, mSize);
     if (mPtr)
     {
       status = ZwQuerySystemInformation(ATableType,mPtr,mSize,NULL);
     }else
    return NULL;
     if (status == STATUS_INFO_LENGTH_MISMATCH)
     {
       ExFreePool(mPtr);
     mSize *=2;
     }  
  } while(status == STATUS_INFO_LENGTH_MISMATCH);
       
   if (status == STATUS_SUCCESS)
   return mPtr;
   
   ExFreePool(mPtr);
   return NULL;  
}


//得到csrss.exe的PID
HANDLE GetCsrPid()
{
  HANDLE Process,hObject;
  HANDLE CsrssId=(HANDLE)0;
  OBJECT_ATTRIBUTES obj;
  CLIENT_ID cid;
  UCHAR buff[0x100];
  POBJECT_NAME_INFORMATION objName=(PVOID)&buff;
  PSYSTEM_HANDLE_INFORMATION_EX Handles;
  ULONG i;

  
   Handles=GetInfoTable(SystemHandleInformation);
   if (!Handles) return CsrssId;
   
   for (i=0;i<Handles->NumberOfHandles;i++)
   {
      if (Handles->Information[i].ObjectTypeNumber == 21)
      {
        InitializeObjectAttributes(&obj,NULL,OBJ_KERNEL_HANDLE,NULL,NULL);
      cid.UniqueProcess=(HANDLE)Handles->Information[i].ProcessId;
    cid.UniqueThread = 0;
      
    if (NT_SUCCESS(NtOpenProcess(&Process,PROCESS_DUP_HANDLE,&obj,&cid)))
    {
        if (NT_SUCCESS(ZwDuplicateObject(Process,(HANDLE)Handles->Information[i].Handle,NtCurrentProcess(),&hObject,0,0,DUPLICATE_SAME_ACCESS)))
        {
           if (NT_SUCCESS(ZwQueryObject(hObject, ObjectNameInformation, objName, 0x100, NULL)))
         {
                   if (objName->Name.Buffer&& !wcsncmp(L"\\Windows\\ApiPort",objName->Name.Buffer,20))
                   {
                     
             CsrssId=(HANDLE)Handles->Information[i].ProcessId;
           
           }

         }     
      ZwClose(hObject); 
      }

        ZwClose(Process);
    }    
   
    }
   
   }
  
ExFreePool(Handles);

return CsrssId;
}


//inline Hook ExAllocatePoolWithTag
BOOLEAN inlineHook_ExAllocatePoolWithTag()
{
    KIRQL  oldIrql;
  DWORD distance; 
  UNICODE_STRING unamestr;
    BOOLEAN result;

    RtlInitUnicodeString(&unamestr,L"ExAllocatePoolWithTag");
    oldExAllocatePoolWithTagaddr=(ULONG)MmGetSystemRoutineAddress(&unamestr);
    if (oldExAllocatePoolWithTagaddr)
    {
  //保存原来开始的5个字节
   RtlCopyMemory(oldcode,(BYTE*)oldExAllocatePoolWithTagaddr,5); 
   distance=(BYTE*)fake_ExAllocatePoolWithTag - (BYTE*)oldExAllocatePoolWithTagaddr -5;
   RtlCopyMemory(jmpcode+1,&distance,4);
   WPOff();
   oldIrql=KeRaiseIrqlToDpcLevel();
   RtlCopyMemory((BYTE*)oldExAllocatePoolWithTagaddr,jmpcode,5);
   KeLowerIrql(oldIrql);
   WPOn();
   result=TRUE;  
  }else
    result=FALSE;

   return result;
}


VOID unlineHook_ExAllocatePoolWithTag()
{
  KIRQL  oldIrql;
  
  WPOff();
  oldIrql=KeRaiseIrqlToDpcLevel();
  RtlCopyMemory((BYTE*)oldExAllocatePoolWithTagaddr,oldcode,5);
  KeLowerIrql(oldIrql);
  WPOn();

  return;
}


BOOLEAN inlineHook_MmLoadSystemImage()
{
   KIRQL  oldIrql;
   DWORD distance;  
   BOOLEAN result;

   if (Address_MmLoadSystemImage)
   {
   //保存原来开始的5个字节
   RtlCopyMemory(oldcode,(BYTE*)Address_MmLoadSystemImage,5);
   distance=(BYTE*)fake_MmLoadSystemImage - Address_MmLoadSystemImage -5;
   RtlCopyMemory(jmpcode+1,&distance,4);
   WPOff();
   oldIrql=KeRaiseIrqlToDpcLevel();
   RtlCopyMemory((BYTE*)Address_MmLoadSystemImage,jmpcode,5);
   KeLowerIrql(oldIrql);
   WPOn();
   result=TRUE;
   
   }else
    result=FALSE;
   
  return result;
}

VOID unlineHook_MmLoadSystemImage()
{
  KIRQL  oldIrql;
  
  WPOff();
  oldIrql=KeRaiseIrqlToDpcLevel();
  RtlCopyMemory((BYTE*)Address_MmLoadSystemImage,oldcode,5);
  KeLowerIrql(oldIrql);
  WPOn();

  return;



  
}




VOID WPOn()
{
     __asm{
          mov eax,cr0
          or eax,0x10000
          mov cr0,eax
          STI
  }
 
}

VOID WPOff()
{
  __asm{
          cli  
      mov eax, cr0
          and eax,not 0x10000
      mov cr0,eax
      }
  
}

效果示意图: