研究心得-恢复2k xp得win32k.sys得KeServiceDescriptorTableShadow

/**
 * @file sdtreset.cpp
 *
 * @brief sdtreset.cpp, v 1.0.0 2005/10/25 11:48:42 sunwang
 *
 * details here.
 * 草稿代码,命名混乱,重构时候使用java风格,getKiServiceTableAddrMM
 * 其实找到了KeServiceDescriptorTable/Shadow的真实entry地址,直接调用就是了,没有必要去restore,
 * restore的好处是禁止这个hooker干坏事如cnnic禁止别人的hooker
 *
 * @author sunwang <[email protected]>
 */
#include "oshack.h"      //common header
#include "krnlio.h"      //krnl file io 
#include "fsdreset.h"     //pe analysis and fsd restore routine

PBYTE getKiServiceTableAddrMM()
{
 return KeServiceDescriptorTable[0].ServiceTable;
}

//xp是KeServiceDescriptorTableShadow=KeServiceDescriptorTable-40h
//2k是KeServiceDescriptorTableShadow=KeServiceDescriptorTable+E0h
PBYTE getW32pServiceTableAddrMM()
{
 PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTableShadow;

 //for xp
 if(gKernelVersion==WINXP)
  KeServiceDescriptorTableShadow=(PCHAR)KeServiceDescriptorTable-0x40; 
 //for 2k
 else if(gKernelVersion==WIN2K)
  KeServiceDescriptorTableShadow=(PCHAR)KeServiceDescriptorTable+0xE0;
 //noway here now,maybe WIN2K3
 else
  return NULL;

 return KeServiceDescriptorTableShadow[1].ServiceTable;
}

PBYTE getW32kBaseAddrMM()
{
 return MyGetModuleBaseAddress("win32k.sys");
}

PBYTE getKernalBaseAddrMM()
{
 return MyGetModuleBaseAddress("ntoskrnl.exe");
}

//xp下win32k并不是长驻内存的,直接用它的内存镜像分析PE有问题
//ntoskrnl.exe是长驻内存的,直接用它的内存镜像分析PE没问题
//问题不是这个,见下面老长的注释,读文件也可以,读内存也可以
//只要不读win32k
/*
Bug Check 0x50: PAGE_FAULT_IN_NONPAGED_AREA
The PAGE_FAULT_IN_NONPAGED_AREA bug check has a value of 0x00000050.
This indicates that invalid system memory has been referenced.
*/
/*
 直接读硬盘文件算了,SizeOfHeaders<=0x380,我读个4k出来总可以了吧,4k=0x1000
 然后分析头
*/
PBYTE getW32kBaseAddrHD(PBYTE imageRawAddrMM)
{
 return GetDriverBaseAddrHD(imageRawAddrMM);
}

PBYTE getKernalBaseAddrHD(PBYTE imageRawAddrMM)
{
 return GetDriverBaseAddrHD(imageRawAddrMM);
}

//id = *(导出函数的地址+1)
//内部函数的话,就只能手动填了,如NtUserSetWindowsHookEx,2k:1212,xp:1225
ULONG getServiceEntryAddrReal(ULONG id)
{
 char imagePath[256];
 int handle;
 ULONG rawoffset,value,rva;
 PBYTE imageBaseAddrMM,imageBaseAddrHD,serviceTableAddrMM,imageRawAddrMM;

 //init
 value=0;
 RtlZeroMemory(imagePath,256);

 //win32k
 if((id&0x3fff)>>12 == 1)
 {
  DebugPrint(("getAPIAddrReal of win32k/n"));

  //reloc后的baseAddr
  imageBaseAddrMM=getW32kBaseAddrMM();
  DebugPrint(("imageBaseAddrMM:0x%08x/n",imageBaseAddrMM));

  //reloc后的W32pServiceTable
  serviceTableAddrMM=getW32pServiceTableAddrMM(); //va,should change to raw-addr
  DebugPrint(("serviceTableAddrMM:0x%08x/n",serviceTableAddrMM));

  //reloc后的entry的rva,注意不是函数本身的rva
  rva=serviceTableAddrMM+(id&0xfff)*4-imageBaseAddrMM;//rva
  DebugPrint(("rva:0x%08x/n",rva));

  //读取文件头
  //MyGetModuleImageName("win32k.sys",imagePath);
  strcpy(imagePath,gSystemRoot);
  strcat(imagePath,"//system32//win32k.sys");
  DebugPrint(("imagePath:%s/n",imagePath));
  imageRawAddrMM=ExAllocatePool(NonPagedPool,0x1000);
  handle = krnl_open(imagePath,0,KernelMode);
  if(handle)
  {
   DebugPrint(("krnl_open ok/n"));
   krnl_read_offset(handle,imageRawAddrMM,0x1000,0);//va (related to imageBaseAddrHD)
   krnl_close(handle);
  }
  DebugPrint(("imageRawAddrMM:0x%08x/n",imageRawAddrMM));

  //下面遍历所有的section,看rva在哪个section,然后根据section的RVA-VirtualAddress(RVA)+PointerToRawData就
  //得到了rawoffset
  rawoffset=RVA2RawOffset(imageRawAddrMM,rva);
  DebugPrint(("rawoffset:0x%08x/n",rawoffset));

  //读取value,这是reloc前的va
  handle = krnl_open(imagePath,0,KernelMode);
  if(handle)
  {
   DebugPrint(("krnl_open ok/n"));
   krnl_read_offset(handle,&value,4,rawoffset);//va (related to imageBaseAddrHD)
   krnl_close(handle);
  }
  DebugPrint(("value va (related to imageBaseAddrHD):0x%08x/n",value));

  //reloc前的baseAddr
  imageBaseAddrHD=getW32kBaseAddrHD(imageRawAddrMM);
  DebugPrint(("imageBaseAddrHD:0x%08x/n",imageBaseAddrHD));

  //reloc后的地址
  value=value-(ULONG)imageBaseAddrHD;//rva
  DebugPrint(("value rva:0x%08x/n",value));
  value=value+(ULONG)imageBaseAddrMM;//va (relate to iamgeBaseAddrMM)
  DebugPrint(("value va (related to iamgeBaseAddrMM real):0x%08x/n",value));

  //下面这句有问题,直接读了serviceTableAddrMM的entry地址,真他妈妈的要命,why???
  //其实不用读什么硬盘文件,直接使用内存的文件头就是了
  //就是不能引用KeServiceDescriptorTableShadow中win32k的地址,我日
  
  /*
  Hello:

  I have a problem related to the theme you have been speaking of. I hook
  both KeServiceDescriptorTable and KeServiceDescriptorTableShadow because I
  need to monitorize some ntoskrnl and user/gdi services. The driver works
  fine until I tested it in WXP. The problem es that the second entry in
  KeServiceDescriptorTableShadow (that for NtUserxxx services for example)
  has an address (some similar to B79Fxxxx) and when I try to access this
  for patching the shadow service table, I get a Bug Check 0x50:
  PAGE_FAULT_IN_NONPAGED_AREA.

  I have disassembled Mark Russinovich's RegMon and I have tried to map that
  memory through and MDL and then MmBuildMdlForNonPagedPool and
  MmMapLockedPages as he does but I get the same bug check in
  MmBuildMdlForNonPagedPool. Does anybody how to acomplish this task? I have
  not the same problem when I map the index 0 table (NtXxxx services)... it
  works fine.

  Thanks in advance,
  Jose Vicente.
  */

  /*
  Try not mapping it in System process context, or any process context which does
  not use GDI/user interface. I believe the address is part of the session space.
  If you try mapping it in the process context of an app or service which uses
  user32 or gdi calls it works fine. Hope this helps you.
  */

  /*
  Thank you very much, Srin

  The only problem is that I don't know how to do that inside a driver. How
  can I ensure that I'm running within the context of a given application?

  Best regards,
  Jose Vicente.
  */

  /*
  Jose,
  I believe you have a service or app along with the driver in your
  product. When either of these is starting send an IOCTL to your driver and in
  the dispatch routine of this IOCTL do the required. As you are sending the
  IOCTL directly to your driver/device object, in the driver's dispatch routine
  you can be sure that you are executing in the process context which sent you
  the IOCTL.
  
  Have fun,
  Srin.
  */

  /*
  Srin,

  Thank you very much for your answer but I have found an alternative
  solution (in any case actually I haven't a helper application since my
  driver reads the configuration from system registry and that information is
  updated through domain policies). I have exploited the fact that I'm
  monitorizing the process creation/destruction and image loading to hook the
  loading of CSRSS process and thus touching the system services table at that
  moment.

  Best regards,
  Jose Vicente.
  */
  //PsSetCreateProcessNotifyRoutine / PsSetCreateThreadNotifyRoutine / PsSetLoadImageNotifyRoutine
  //并不需要映射,只要在上下文就可以了,奶奶的。
  {
   PCHAR tempEntry=serviceTableAddrMM+(id&0xfff)*4;
   if(MmIsAddressValid(tempEntry))
   {
    DebugPrint(("value va (related to iamgeBaseAddrMM now):0x%08x/n",*(ULONG*)tempEntry));
   }
   //并不需要映射,只要在上下文就可以了,奶奶的。
   else
   {
//    ULONG attachedGDIProcessID=1144;
//    EPROCESS attachedGDIProcess;
    PMDL mdl;
    PCHAR kernelVA;

    DebugPrint(("fuck,a page fault will occur for a read or write operation at a given virtual address"
     ":0x%08x/n",tempEntry));

    //AttachProcess这个方法可以,但是在DriverEntry里面不好测试,faint
/*
    if(NT_SUCCESS(PsLookupProcessByProcessId((PVOID)attachedGDIProcessID,&attachedGDIProcess)))
    {
     KeAttachProcess(&attachedGDIProcess);
     //
     // map
     //

     mdl=IoAllocateMdl(tempEntry,4,0,0,0);
     if(mdl)
     {
      DebugPrint(("IoAllocateMdl ok/n"));
      MmBuildMdlForNonPagedPool(mdl);
      kernelVA=MmMapLockedPages(mdl,KernelMode);
      if(kernelVA)
      {
       if(MmIsAddressValid(kernelVA))
       {
        DebugPrint(("value va (related to iamgeBaseAddrMM now):0x%08x/n",*(ULONG*)kernelVA));
       }
       else
       {
        DebugPrint(("fuck,a page fault will occur for a read or write operation at a given virtual address"
        ":0x%08x/n",kernelVA));
       }
       MmUnmapLockedPages(kernelVA,mdl);
      }
     }

     KeDetachProcess();
    }
*/
    //procmon,希望能成,测试成功.
    mdl=IoAllocateMdl(tempEntry,4,0,0,0);
    if(mdl)
    {
     DebugPrint(("IoAllocateMdl ok/n"));
     //如果不是gdi进程空间,一样死,fuck
     MmBuildMdlForNonPagedPool(mdl);
     kernelVA=MmMapLockedPages(mdl,KernelMode);
     if(kernelVA)
     {
      if(MmIsAddressValid(kernelVA))
      {
       DebugPrint(("value va (related to iamgeBaseAddrMM now):0x%08x/n",*(ULONG*)kernelVA));
      }
      else
      {
       DebugPrint(("fuck,a page fault will occur for a read or write operation at a given virtual address"
        ":0x%08x/n",kernelVA));
      }
      MmUnmapLockedPages(kernelVA,mdl);
     }
    }
   }
  }
 
  ExFreePool(imageRawAddrMM);
 }
 //ntoskrnl
 else if((id&0x3fff)>>12 == 0)
 {
  DebugPrint(("getAPIAddrReal of ntoskrnl/n"));

  //reloc后的baseAddr
  imageBaseAddrMM=getKernalBaseAddrMM();
  DebugPrint(("imageBaseAddrMM:0x%08x/n",imageBaseAddrMM));

  //reloc后的W32pServiceTable
  serviceTableAddrMM=getKiServiceTableAddrMM(); //va,should change to raw-addr
  DebugPrint(("serviceTableAddrMM:0x%08x/n",serviceTableAddrMM));

  //reloc后的entry的rva,注意不是函数本身的rva
  rva=serviceTableAddrMM+(id&0xfff)*4-imageBaseAddrMM;//rva
  DebugPrint(("rva:0x%08x/n",rva));

  //读取文件头
  strcpy(imagePath,gSystemRoot);
  strcat(imagePath,"//system32//ntoskrnl.exe");
  DebugPrint(("imagePath:%s/n",imagePath));
  imageRawAddrMM=ExAllocatePool(NonPagedPool,0x1000);
  handle = krnl_open(imagePath,0,KernelMode);
  if(handle)
  {
   DebugPrint(("krnl_open ok/n"));
   krnl_read_offset(handle,imageRawAddrMM,0x1000,0);//va (related to imageBaseAddrHD)
   krnl_close(handle);
  }
  DebugPrint(("imageRawAddrMM:0x%08x/n",imageRawAddrMM));

  //下面遍历所有的section,看rva在哪个section,然后根据section的RVA-VirtualAddress(RVA)+PointerToRawData就
  //得到了rawoffset
  rawoffset=RVA2RawOffset(imageRawAddrMM,rva);
  DebugPrint(("rawoffset:0x%08x/n",rawoffset));

  //读取value,这是reloc前的va
  handle = krnl_open(imagePath,0,KernelMode);
  if(handle)
  {
   DebugPrint(("krnl_open ok/n"));
   krnl_read_offset(handle,&value,4,rawoffset);//va (related to imageBaseAddrHD)
   krnl_close(handle);
  }
  DebugPrint(("value va (related to imageBaseAddrHD):0x%08x/n",value));

  //reloc前的baseAddr
  imageBaseAddrHD=getKernalBaseAddrHD(imageRawAddrMM);
  DebugPrint(("imageBaseAddrHD:0x%08x/n",imageBaseAddrHD));

  //reloc后的地址
  value=value-(ULONG)imageBaseAddrHD;//rva
  DebugPrint(("value rva:0x%08x/n",value));
  value=value+(ULONG)imageBaseAddrMM;//va (relate to iamgeBaseAddrMM)
  DebugPrint(("value va (related to iamgeBaseAddrMM real):0x%08x/n",value));
  //下面这句有问题,直接读了serviceTableAddrMM的entry地址,真他妈妈的要命,why???
  //其实不用读什么硬盘文件,直接使用内存的文件头就是了
  //就是不能引用KeServiceDescriptorTableShadow中win32k的地址,我日
  {
   PCHAR tempEntry=serviceTableAddrMM+(id&0xfff)*4;
   if(MmIsAddressValid(tempEntry))
   {
    DebugPrint(("value va (related to iamgeBaseAddrMM now):0x%08x/n",*(ULONG*)tempEntry));
   }
   else
   {
    DebugPrint(("fuck,a page fault will occur for a read or write operation at a given virtual address"
     ":%0x%08x/n",tempEntry));
   }
  }

  ExFreePool(imageRawAddrMM);
 }

 return value;
}

你可能感兴趣的:(XP,service,table,System,hook,Descriptor)