首页
论坛
课程
招聘
雪    币: 286
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:330 )
在线值:
发帖
回帖
粉丝

[原创]利用堆栈回溯来编写驱动防火墙

2010-11-17 10:04 16901

[原创]利用堆栈回溯来编写驱动防火墙

2010-11-17 10:04
16901
通过堆栈回溯来挂钩未导出函数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
		  }
	
}

效果示意图:
[ ATTACH ]clip_image002.jpg[ /ATTACH ]


HWS计划·2020安全精英夏令营来了!我们在华为松山湖欧洲小镇等你

上传的附件:
最新回复 (24)
雪    币: 535
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiamisun 活跃值 2010-11-17 13:11
2
0
sofa ?
雪    币: 71
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aaen 活跃值 2010-11-17 13:25
3
0
不错的一个软件
雪    币: 2076
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
exile 活跃值 1 2010-11-17 14:06
4
0
我是来打酱油的
雪    币: 288
活跃值: 活跃值 (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
antime 活跃值 2010-11-17 16:23
5
0
我是来替微点喊冤的。
拦截驱动不必要这么麻烦,瞎搞!
雪    币: 2648
活跃值: 活跃值 (110)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2010-11-17 22:06
6
0
这么复杂**~
话说,我一直是搜索的~
雪    币: 188
活跃值: 活跃值 (56)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
wujimaa 活跃值 1 2010-11-17 22:42
7
0
很好,学习。
雪    币: 141
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
SunV 活跃值 2010-11-17 23:40
8
0
精彩啊,学习~~
雪    币: 239
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yinning 活跃值 2010-11-18 23:16
9
0
和拦截ntcreatesection哪个效果好呀?
雪    币: 2648
活跃值: 活跃值 (110)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2010-11-19 05:26
10
0
正规拦截应该是NtCreateSection
雪    币: 461
活跃值: 活跃值 (69)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2010-11-19 15:49
11
0
引用:
但是通过我跟踪堆栈调用,发现要经过很多次KeWaitForSingleObject调用,才会进入


因为楼主没有主动触发,所以要多次.以下代码是主动触发,马上就能定位了啊

VOID
Get_MmLoadSystemImage_addr(
        )
/*++

Author: sudami [sudami@163.com]
Time  : 2008/12/23 [23:12:2008 - 10:23]

Routine Description:
  获取MmLoadSystemImage的地址

--*/

{
        NTSTATUS status ;
        UNICODE_STRING  uFileName;
    STRING                        ntNameString;
        CCHAR ntNameFile[128] = "\\??\\C:\\";

       
        // 得到 KeWaitForSingleObject 原始地址
        g_KeWaitForSingleObject_dwAddr = GetExportFuncOrigAddr( "KeWaitForSingleObject", FALSE ) ;
        dprintf( "KeWaitForSingleObject:0x%08lX \n", g_KeWaitForSingleObject_dwAddr ) ;
        if ( 0 == g_KeWaitForSingleObject_dwAddr ) {
                g_KeWaitForSingleObject_dwAddr = (ULONG) GetNativeFunctionBaseAddress( L"KeWaitForSingleObject" ) ;
        }

        // 得到 RtlImageNtHeader 原始地址
        g_RtlImageNtHeader_addr = GetExportFuncOrigAddr( "RtlImageNtHeader", FALSE ) ;
        dprintf( "RtlImageNtHeader:0x%08lX \n", g_RtlImageNtHeader_addr ) ;
        if ( 0 == g_RtlImageNtHeader_addr ) {
                g_RtlImageNtHeader_addr = (ULONG) GetNativeFunctionBaseAddress( L"RtlImageNtHeader" ) ;
        }
       
        // 今天才发现,不需要释放任何驱动,随便给ZwSetSystemInformation乱传一个路径,都会进入到
        // MmLoadSystemImage函数中,这就够了  by sudami 26-12-2008
//         GenerateRandomStrings(5) ; // 产生随机名
//         if ( NULL != szTmpName )
//         {
//                 sprintf( ntNameFile+strlen(ntNameFile), "%s.sys", szTmpName );
//                 RtlInitAnsiString( &ntNameString, ntNameFile);
//                 RtlAnsiStringToUnicodeString( &uFileName, &ntNameString, TRUE );
//
//                 // 释放测试驱动到C盘根目录
//                 status = PutFile( uFileName.Buffer, Test_sys_data, sizeof(Test_sys_data) ) ;
//
//                 RtlFreeUnicodeString(&uFileName);
//
//         } else {
//                 sprintf( ntNameFile+strlen(ntNameFile), "%s.sys", szTmpName );
//                 RtlInitAnsiString( &ntNameString, ntNameFile);
//                 RtlAnsiStringToUnicodeString( &uFileName, &ntNameString, TRUE );
//                
//                 wcscpy( g_wszName, uFileName.Buffer ) ;
//                
//                 RtlFreeUnicodeString(&uFileName);
//
//                 status = PutFile( g_wszName, Test_sys_data, sizeof(Test_sys_data) ) ;
//         }
//        
//         if ( STATUS_SUCCESS != status )
//         {
//                 dprintf( "PutFile(), Failed!\n" ) ;
//                 return ;
//         }
/*++

  先瞬间Inline Hook掉KeWaitForSingleObject,再迅速调用ZwSetSystemInformation加载释放到C盘
  根目录的测试驱动.这样会马上执行到钩子里,在此进行栈回溯,最终定位到NtSetSystemInformation
  函数调用MmLoadSystemImage的地方. 得到MmLoadSystemImage的地址.

  by sudami

--*/

        g_ntoskrnl_addr = g_ntoskrnl_info.ntoskrnl_addr ;
        g_ntoskrnl_size = g_ntoskrnl_info.ntoskrnl_size ;

        if ( !MmIsAddressValid( (PVOID)g_KeWaitForSingleObject_dwAddr )
                || g_KeWaitForSingleObject_dwAddr < g_ntoskrnl_addr )
        {
                dprintf( "g_KeWaitForSingleObject_dwAddr, INVALID,Failed!\n" ) ;
                return ;
        }

        if( !HookCode95( (PVOID)g_KeWaitForSingleObject_dwAddr, (PVOID)fake_KeWaitForSingleObject, InlineHookPre1) )
        {        // 前5字节JMP.
                dprintf( "HOOK KeWaitForSingleObject, Failed!\n" ) ;
                return ;
        }

        g_tmpThread_for_KeWaitForSingleObject = (ULONG)KeGetCurrentThread();

        load_sysfile( g_wszName ) ;

        g_tmpThread_for_KeWaitForSingleObject = 0 ;
       
        UnhookCode95( fake_KeWaitForSingleObject ) ;
}
雪    币: 461
活跃值: 活跃值 (69)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2010-11-19 15:52
12
0
VOID
fake_KeWaitForSingleObject (
        )
/*++

Author: sudami [sudami@163.com]
Time  : 2008/12/23 [23:12:2008 - 7:16]

Routine Description:

  栈回溯获取MmLoadSystemImage的地址
  win2k下的调用链如下:
  NtSetSystemInformation->MmLoadSystemImage->MiLoadSystemImage->KeWaitForSingleObject
  
  XP以后没有中间那个>MiLoadSystemImage了.在WIN2K里要多回溯一次
  
--*/

{
        PULONG PEBP ;
        UCHAR *pOpcode;

        __asm {
        pushfd
                pushad
        }

        g_bIsW2k = g_hardOffset.bIsW2k ;

        if ( g_tmpThread_for_KeWaitForSingleObject != (ULONG)KeGetCurrentThread() ) {
                goto _over_for_KeWaitForSingleObject ;
        }
   
        __asm
    {
//                int 3
                mov   PEBP, ebp
                mov   ebx, PEBP
                mov   ebx, [ebx]   ; 回溯1次到 call MmLoadSystemImage 空间
                cmp   g_bIsW2k, 1
                jnz   _xp_
                mov   ebx, [ebx]   ; WIN2K 要多回溯一次
_xp_:
                mov   ebx, [ebx+4] ; ebp+4 --> ret Addr
                sub   ebx, 5       ; ret-5 --> 由ret Addr到Call MmLoadSystemImage
                mov   edi, ebx     ; edi 保存Call MmLoadSystemImage处的地址
/*++

  ChildEBP RetAddr  Args to Child              
  f9df7804 805a370b 80554440 00000012 00000000 VirusAnalyse!fake_KeWaitForSingleObject+0x1f
  f9df79b0 80605cb2 f9df7b38 00000000 00000000 nt!MmLoadSystemImage+0x1a9     <-- 触发成功
  f9df7b60 8053da28 00000026 f9df7bf8 00000008 nt!NtSetSystemInformation+0x37c<-- 到达内核态Nt*
  f9df7b60 804ff711 00000026 f9df7bf8 00000008 nt!KiFastCallEntry+0xf8        <-- 经过中转站
  f9df7be4 f9ca5053 00000026 f9df7bf8 00000008 nt!ZwSetSystemInformation+0x11
  f9df7c0c f9ca4d0d f9df7c2c 816f5cf8 e13b815a VirusAnalyse!load_sysfile+0xc3 <-- 我们来触发它
  f9df7c50 6f526d65 535c746f 65747379 5c32336d VirusAnalyse!Get_MmLoadSystemImage_addr+0x16d

  nt!NtSetSystemInformation+0x365:
  
        80605ca5 6a01            push    1
        80605ca7 57              push    edi
        80605ca8 57              push    edi
        80605ca9 8d45d8          lea     eax,[ebp-28h]
        80605cac 50              push    eax  ; 我们关心的参数,里面包含加载模块的全路径
        80605cad e8b0d8f9ff      call    nt!MmLoadSystemImage (805a3562)  <-- 回溯到这里

        80605cb2 8945e4          mov     dword ptr [ebp-1Ch],eax
        80605cb5 3bc7            cmp     eax,edi
        80605cb7 0f8cb0040000    jl      nt!NtSetSystemInformation+0x837 (8060616d)
        80605cbd ff75ac          push    dword ptr [ebp-54h]
        80605cc0 e82b52f2ff      call    nt!RtlImageNtHeader (8052aef0)   <-- 校验

        80605cc5 898568ffffff    mov     dword ptr [ebp-98h],eax
        80605ccb 3bc7            cmp     eax,edi
        80605ccd 7512            jne     nt!NtSetSystemInformation+0x3ab (80605ce1)
        80605ccf ff75b4          push    dword ptr [ebp-4Ch]
        80605cd2 e8efd2f9ff      call    nt!MmUnloadSystemImage (805a2fc6)<-- 顺便可以把它的地址找到

        80605cd7 b87b0000c0      mov     eax,0C000007Bh
        80605cdc e98c040000      jmp     nt!NtSetSystemInformation+0x837 (8060616d)

--*/

                cmp   g_ntoskrnl_addr, 0
                jz    _Skip2Check_
                cmp   g_ntoskrnl_size, 0
                jz    _Skip2Check_

                mov   ecx, g_ntoskrnl_addr
                cmp   ebx, ecx
                jbe   short _over_for_KeWaitForSingleObject
                mov   edx, g_ntoskrnl_size
                lea   esi, [edx+ecx]
                cmp   ebx, esi
                jnb   short _over_for_KeWaitForSingleObject
                lea   edx, [ebx+30h]
                cmp   edx, ecx
                jbe   short _over_for_KeWaitForSingleObject
                cmp   edx, esi
                jnb   short _over_for_KeWaitForSingleObject

_Skip2Check_: //  是在内核地址范围内,进一步验证
                add   ebx, 5                        ; 到达 call nt!MmLoadSystemImage 的下一条指令                     
                xor   ecx, ecx                        ; ecx == 每次调用SizeOfCode得到的指令长度
_loop_:                                                        ; 以ebx为起始地址, 往下30h字节内搜索第一个CALL,进行校验
               
//                cmp   BYTE ptr [ecx+ebx], 0E8h // 这样找太危险了.刚好碰到一种情况,数据是E8,而不是指令
                // 从而导致地址不可读,BSOD了.这里还是应该带一个反汇编引擎

                push  ecx               ; -->保存ecx
                mov   pOpcode, 0
                lea   edx, pOpcode      ; UCHAR *pOpcode; [OUT] 指令内容                               
                lea   ecx, [ecx+ebx]    ; UCHAR *cPtr ;   [IN] 当前指令地址
                call  SizeOfCode        ; Length = SizeOfCode(cPtr, &pOpcode); // fastcall类型
                // 此时ecx的值变成0了.故之前需要保存下

                pop   ecx                                ; -->恢复ecx
                add   ecx, eax          ; ecx += Length
        cmp   eax,0            
                jnz    __dddd__                       
                inc   ecx                                ; 若反汇编引擎计算的长度为0,则地址也要往后累加
                jmp   _next_

__dddd__:
                mov   esi, [pOpcode]    ; esi得到当前指令的地址
                cmp   BYTE ptr [esi], 0E8h  ; 是否为CALL指令
                jnz   short _next_       
/*++

传进来的地址明明是正确的,但一调用MmIsAddressValid就崩,很奇怪很奇怪~~
STACK_TEXT:  
WARNING: Frame IP not in any known module. Following frames may be wrong.
f9defc54 535c746f 65747379 5c32336d 726b746e 0x6f526d65
f9defc7c 805767dc 815d8b10 814b7000 00000000 0x535c746f  <-- 明显是EIP执行到了非法地址
f9defd4c 805768eb 000000a8 00000001 00000000 nt!IopLoadDriver+0x66c
f9defd74 80534fe6 000000a8 00000000 817bbda8 nt!IopLoadUnloadDriver+0x45
f9defdac 805c5cce f7fe2cf4 00000000 00000000 nt!ExpWorkerThread+0x100
f9defddc 805421c2 80534ee6 00000001 00000000 nt!PspSystemThreadStartup+0x34
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16

//                 push  esi                                ; esi == call RtlImageNtHeader 处的地址
//                 call  MmIsAddressValid
//                 cmp   eax, 0
//                 jz    _next_
//
//                 lea   edx, [esi+1]
//                 push  edx
//                 call  MmIsAddressValid
//                 cmp   eax, 0
//                 jz    _next_

--*/

                lea   edx, [esi+1]                ; esi == call RtlImageNtHeader 处的地址
                mov   eax, [edx]                ; eax == offset
                lea   eax, [esi+eax+5]
                cmp   eax, g_RtlImageNtHeader_addr
                jz    _ok_
_next_:
                cmp   ecx, 30h
                jb    short _loop_
                jmp   _over_for_KeWaitForSingleObject

_ok_:
                lea   ecx, [edi+1]  ; edi 保存Call MmLoadSystemImage处的地址
                mov   edx, [ecx]
                lea   eax, [edx+edi+5]
                mov   g_MmLoadSystemImage_addr, eax ; OK! Well Done.

_over_for_KeWaitForSingleObject:
        popad                        // VOID 类型的申明,非naked.所以编译器会自动生成一些垃圾代码
        popfd                        // 这里的pop是为了平衡堆栈.必须的
                pop     edi
                pop     esi
        pop     ebx
//                pop     ecx     // 若只有一个局部变量,编译器可能会:push ecx,而不是sub esp,4
                                                // 故调用wy的引擎,在fake函数中若用到局部变量,最好>=2 counts

                add     esp, 8  // 清掉局部变量

        _emit   0x5D    // pop ebp
        _emit   0x68    // push xxxx
        _emit   0x00
        _emit   0x00
        _emit   0x00
        _emit   0x00
        ret
    }
}
雪    币: 326
活跃值: 活跃值 (16)
能力值: ( LV12,RANK:310 )
在线值:
发帖
回帖
粉丝
evilkis 活跃值 7 2010-11-19 15:56
13
0
我是来膜拜sudami的
雪    币: 461
活跃值: 活跃值 (69)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2010-11-19 15:59
14
0
BOOL
load_sysfile(
        IN WCHAR* szPath
        )
{
        PEPROCESS proc                = NULL ;
        BOOL      bSuccess        = FALSE ;
        NTSTATUS  status    = STATUS_SUCCESS ;
        SYSTEM_LOAD_AND_CALL_IMAGE GregsImage ;
       
        if ( NULL == szPath ) {
                return 0 ;
        }

        // 2次调用,得到csrss.exe的EPROCESS
        status =  GetEProcessByName_QueryInfo( L"CSRSS.EXE", &proc );
        if( !NT_SUCCESS(status) )
        {
                status = GetEProcessByName_QueryInfo( L"csrss.exe", &proc );
                if( !NT_SUCCESS(status) ) return 0 ;
        }

        GregsImage.ModuleName.Buffer = ExAllocatePoolWithTag(0, 100, ' kdD');
        if ( NULL == GregsImage.ModuleName.Buffer )
        {
                dprintf( "load_sysfile() ExAllocatePool() Failed\n" ) ;
                return 0 ;
        }

    wcscpy( GregsImage.ModuleName.Buffer, szPath);
        GregsImage.ModuleName.Length = 2 * wcslen( szPath );
    GregsImage.ModuleName.MaximumLength = 100 ;
       
//        RtlInitUnicodeString( &(GregsImage.ModuleName), szPath );
        KeAttachProcess (proc); // 附着到此进程

        status = ZwSetSystemInformation(
                SystemLoadAndCallImage,
                &GregsImage,
                sizeof(SYSTEM_LOAD_AND_CALL_IMAGE) ) ;

        KeDetachProcess();      // Detach

        if( !NT_SUCCESS( status ) )
        {
                dprintf( "load_sysfile() Failed: 0x%08lx\n", status ) ;
                ExFreePool( GregsImage.ModuleName.Buffer);
                return 0 ;
        }
       
        ExFreePool( GregsImage.ModuleName.Buffer);

        dprintf( "load_sysfile() ,OK!!!\n" ) ;
        return 1 ;
}
雪    币: 108
活跃值: 活跃值 (34)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
combojiang 活跃值 26 2010-11-22 09:58
15
0
呵呵,还是大米的代码看上去清然脱俗, 眼前一亮.
雪    币: 266
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
雪妖 活跃值 2010-11-22 14:30
16
0
强力 mark
雪    币: 286
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:330 )
在线值:
发帖
回帖
粉丝
liukeblue 活跃值 7 2010-11-25 15:12
17
0
刚刚回来就看到sudami牛的代码,代码写的果然清新脱俗,别具一格,十分感谢,继续向大米兄学习
雪    币: 160
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xianzq 活跃值 2010-11-25 15:17
18
0
回后再看.哈哈......
雪    币: 88
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gezz 活跃值 2010-11-25 17:03
19
0
这么好的文章我第一次看的时候居然没MARK,哎~
雪    币: 255
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
咪···WC 活跃值 2010-11-25 17:42
20
0
呵呵···不懂··········
雪    币: 50
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
weixinfu 活跃值 2010-11-26 12:43
21
0
是什么编程语言啊,看不大懂!!
雪    币: 489
活跃值: 活跃值 (30)
能力值: ( LV13,RANK:290 )
在线值:
发帖
回帖
粉丝
guxinyi 活跃值 5 2010-12-8 18:46
22
0
驱动层的HOOK,, 留贴备查,,,
雪    币: 29
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
君君寒 活跃值 2010-12-8 19:37
23
0
mark...........牛啊
雪    币: 55
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiongyifly 活跃值 2010-12-9 16:11
24
0
mark,学习了
雪    币: 384
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yangxingyu 活跃值 2010-12-10 10:32
25
0
精彩啊,学习~~
游客
登录 | 注册 方可回帖
返回