天天看点

零环状态下,通过修改cr3,对用户层数据进行修改失败的情况分析

今天写代码的时,遇到一个问题,就是在r0的状态下通过修改cr3寄存器的值对用户层某个进程的数据或者代码段进行修改,一直出现修改不了的情况,首先看一下代码(网上找的,应该是某个大手子的代码)

ULONG64 pDTB = 0, OldCr3 = 0, vAddr = 0, OldCr0;
  //Get DTB
  pDTB = Get64bitValue((UCHAR*)Process + DIRECTORY_TABLE_BASE);
  if (pDTB == 0) {
    DbgPrint("Get64bitValue failed");
    return;
  }

  
  OldCr0 = __readcr0();
  _disable();
  OldCr3 = __readcr3();
  __writecr3(pDTB);
  __writecr0(OldCr0 & 0xfffffffffffeffff);
  _enable();
  //Read process memory
  if (MmIsAddressValid(Address)){
    RtlCopyMemory(Address, Buffer, Length);
    //memcpy(Address, Buffer, Length);
    //DbgPrint("i Am running !!!!!!!!\n");
  }
  
  _disable();
  __writecr0(OldCr0);
  __writecr3(OldCr3);
  _enable();      

代码看起来也没有什么问题,但是执行之后代码也没有被修改(我修改的是代码段的0x401000的位置),这里就用到了windbg,首先挂到需要修改的进程中,通过下面的方式观察当前的线性地址。

零环状态下,通过修改cr3,对用户层数据进行修改失败的情况分析

可以看到当前的pte下的地址是不可获得的,那可能是系统没有挂物理页,或者是把物理页给置换到pagefile里面去了,那么怎么解决这个问题,其实很简单,只需要在修改它的时候先访问一下它就行了(话是这样说,但是我找了很久的bug,我还以为是页面没有写权限,跑去修改pte的可写标志位),代码如下所示。

ULONG64 pDTB = 0, OldCr3 = 0, vAddr = 0, OldCr0;
  //Get DTB
  pDTB = Get64bitValue((UCHAR*)Process + DIRECTORY_TABLE_BASE);
  if (pDTB == 0) {
    DbgPrint("Get64bitValue failed");
    return;
  }

  
  OldCr0 = __readcr0();
  _disable();
  OldCr3 = __readcr3();
  __writecr3(pDTB);
  __writecr0(OldCr0 & 0xfffffffffffeffff);
  _enable();
  //Read process memory

  DbgPrint("ole %X  \n", *(DWORD32 *)Address);//需要先读一下,让他把物理页给挂上
  if (MmIsAddressValid(Address)){//第一次肯定会失败,当前的页面是没有物理地址的
    RtlCopyMemory(Address, Buffer, Length);
    //memcpy(Address, Buffer, Length);
    DbgPrint("i Am running !!!!!!!!\n");
  }
  
  _disable();
  __writecr0(OldCr0);
  __writecr3(OldCr3);
  _enable();      

之后执行代码可以正常的修改进程的内容,如下图所示

零环状态下,通过修改cr3,对用户层数据进行修改失败的情况分析