恢复2k xp得win32k.sys得KeServiceDescriptorTableShadow

本文介绍了一种定位Win32k.sys和ntoskrnl.exe中API的方法,通过获取服务表地址并解析PE文件来找到真实的服务入口点。讨论了如何避免因访问无效系统内存而引发的BUGCHECK 0x50错误,并提供了几种实现方案。
 

/**
* @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 <sunwangme@hotmail.com>
*/
#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;
}

基于51单片机,实现对直流电机的调速、测速以及正反转控制。项目包含完整的仿真文件、源程序、原理图和PCB设计文件,适合学习和实践51单片机在电机控制方面的应用。 功能特点 调速控制:通过按键调整PWM占空比,实现电机的速度调节。 测速功能:采用霍尔传感器非接触式测速,实时显示电机转速。 正反转控制:通过按键切换电机的正转和反转状态。 LCD显示:使用LCD1602液晶显示屏,显示当前的转速和PWM占空比。 硬件组成 主控制器:STC89C51/52单片机(与AT89S51/52、AT89C51/52通用)。 测速传感器:霍尔传感器,用于非接触式测速。 显示模块:LCD1602液晶显示屏,显示转速和占空比。 电机驱动:采用双H桥电路,控制电机的正反转和调速。 软件设计 编程语言:C语言。 开发环境:Keil uVision。 仿真工具:Proteus。 使用说明 液晶屏显示: 第一行显示电机转速(单位:转/分)。 第二行显示PWM占空比(0~100%)。 按键功能: 1键:加速键,短按占空比加1,长按连续加。 2键:减速键,短按占空比减1,长按连续减。 3键:反转切换键,按下后电机反转。 4键:正转切换键,按下后电机正转。 5键:开始暂停键,按一下开始,再按一下暂停。 注意事项 磁铁和霍尔元件的距离应保持在2mm左右,过近可能会在电机转动时碰到霍尔元件,过远则可能导致霍尔元件无法检测到磁铁。 资源文件 仿真文件:Proteus仿真文件,用于模拟电机控制系统的运行。 源程序:Keil uVision项目文件,包含完整的C语言源代码。 原理图:电路设计原理图,详细展示了各模块的连接方式。 PCB设计:PCB布局文件,可用于实际电路板的制作。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值