欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]重载内核之三获取原始地址

[原创]重载内核之三获取原始地址

  重载内核的相关文章实在是太多了,鉴于还是有很多初学者研究这一块,本文仅作为一个引导作用,文笔不好,见谅。
  我的博客:http://blog.csdn.net/sidyhe
  开发环境:VS2010 + WinDDK
  测试环境:VirtualDDK + VMware + Win7 sp1 x86
  第一部分http://bbs.pediy.com/showthread.php?t=187863
  第二部分http://bbs.pediy.com/showthread.php?t=187919
小结与修正
  目前已经实现了一份简单的重载内核代码,但是如果你也跟着我实现了此部分,会发现此代码根本不能使用,甚至不能拿到本机来测试,是的,新的内核还是有大量的问题,驱动加载后会导致一些程序打不开,但是并不蓝屏,有意思的现象。
  写上一篇文章的时候没有测试的那么完善,但是我拿出我之前写的重载内核代码,并没有上述问题,仔细分析代码并回忆,问题还是出在了重定位以及需要额外的处理。那么关于修复重定位的部分就需要重新写了,把修复过程分为两部分,第一次全部重定位到新模块上,第二次有选择的重定位到原模块上,为什么需要那么麻烦?这就涉及到了原始地址的获取方式问题,原始地址都存储在一个叫KiServiceTable的变量中,详见WRK。为什么要获取原始地址?因为当驱动加载的时候你无法确定当前的SSDT表是否被HOOK,所以在第一次修复重定位之后去找KiServiceTable,然后再进行第二次重定位的修复(我无法保证第二次修复不会破坏KiServiceTable中的地址)。
  重新写一份代码,当然大部分还是从原来的代码复制过来,这样有利于逻辑上的思考,先不进行HOOK,把重载部分先理顺清楚,重定位修复代码修改为这个样子:


PVOID ReloadNtModule(PKLDR_DATA_TABLE_ENTRY PsLoadedModuleList)

{

  PVOID lpImageAddress = NULL;

  PKLDR_DATA_TABLE_ENTRY NtLdr = (PKLDR_DATA_TABLE_ENTRY)PsLoadedModuleList->InLoadOrderLinks.Flink;

  PVOID lpFileBuffer;

  DbgPrint("Nt Module File is %wZ\n", &NtLdr->FullDllName);

  if (lpFileBuffer = KeGetFileBuffer(&NtLdr->FullDllName))

  {

    PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)lpFileBuffer;

    PIMAGE_NT_HEADERS lpNtHeader = (PIMAGE_NT_HEADERS)((PCHAR)lpDosHeader + lpDosHeader->e_lfanew);

    if (lpImageAddress = ExAllocatePool(NonPagedPool, lpNtHeader->OptionalHeader.SizeOfImage))

    {

      PUCHAR lpImageBytes = (PUCHAR)lpImageAddress;

      IMAGE_SECTION_HEADER *lpSection = IMAGE_FIRST_SECTION(lpNtHeader);

      ULONG i;

      RtlZeroMemory(lpImageAddress, lpNtHeader->OptionalHeader.SizeOfImage);

      RtlCopyMemory(lpImageBytes, lpFileBuffer, lpNtHeader->OptionalHeader.SizeOfHeaders);

      for (i = 0; i < lpNtHeader->FileHeader.NumberOfSections; i++)

      {

        RtlCopyMemory(lpImageBytes + lpSection[i].VirtualAddress, (PCHAR)lpFileBuffer + lpSection[i].PointerToRawData, lpSection[i].SizeOfRawData);

      }

      if (KeFixIAT(PsLoadedModuleList, lpImageAddress))

      {

        KeFixReloc1(lpImageAddress, NtLdr->DllBase);

      }

      else

      {

        ExFreePool(lpImageAddress);

        lpImageAddress = NULL;

      }

    }

    ExFreePool(lpFileBuffer);

  }

  if (lpImageAddress) DbgPrint("ImageAddress:0x%p\n", lpImageAddress);

  return lpImageAddress;

}

VOID KeFixReloc1(PVOID ImageBaseAddress)

{

  IMAGE_DOS_HEADER *lpDosHeader = (IMAGE_DOS_HEADER*)ImageBaseAddress;

  IMAGE_NT_HEADERS *lpNtHeader = (IMAGE_NT_HEADERS *)((PCHAR)lpDosHeader + lpDosHeader->e_lfanew);

  IMAGE_BASE_RELOCATION *lpRelocateTable = (IMAGE_BASE_RELOCATION*)((PCHAR)ImageBaseAddress + lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

  ULONG_PTR DifferOffset = (ULONG_PTR)ImageBaseAddress - lpNtHeader->OptionalHeader.ImageBase;

  while (lpRelocateTable->SizeOfBlock)

  {

    ULONG NumberOfItems = (lpRelocateTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);

    USHORT  *lpItem = (USHORT*)((PCHAR)lpRelocateTable + sizeof(IMAGE_BASE_RELOCATION));

    ULONG i;

    for (i = 0; i < NumberOfItems; i++)

    {

      switch (lpItem[i] >> 12)

      {

      case IMAGE_REL_BASED_HIGHLOW:

        {

          ULONG_PTR *lpFixAddress = (ULONG_PTR *)((PCHAR)ImageBaseAddress + lpRelocateTable->VirtualAddress + (lpItem[i] & 0x0FFF));

          *lpFixAddress += DifferOffset;

        }

        break;

      case IMAGE_REL_BASED_ABSOLUTE://do nothing

        break;

      default:

        DbgPrint("KeFixReloc1:Found unknown type(%X).\n", (lpItem[i] >> 12));

        break;

      }

    }

    lpRelocateTable = (IMAGE_BASE_RELOCATION *)((PCHAR)lpRelocateTable + lpRelocateTable->SizeOfBlock);

  }

  lpNtHeader->OptionalHeader.ImageBase = (ULONG)ImageBaseAddress;

  return;

}

VOID KeFixReloc2(PVOID New, PVOID Old)

{

  IMAGE_DOS_HEADER *lpDosHeader = (IMAGE_DOS_HEADER*)New;

  IMAGE_NT_HEADERS *lpNtHeader = (IMAGE_NT_HEADERS *)((PCHAR)lpDosHeader + lpDosHeader->e_lfanew);

  IMAGE_BASE_RELOCATION *lpRelocateTable = (IMAGE_BASE_RELOCATION*)((PCHAR)New + lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

  while (lpRelocateTable->SizeOfBlock)

  {

    ULONG NumberOfItems = (lpRelocateTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);

    USHORT  *lpItem = (USHORT*)((PCHAR)lpRelocateTable + sizeof(IMAGE_BASE_RELOCATION));

    ULONG i;

    for (i = 0; i < NumberOfItems; i++)

    {

      switch (lpItem[i] >> 12)

      {

      case IMAGE_REL_BASED_HIGHLOW:

        {

          PVOID lpFixAddress = (PCHAR)New + lpRelocateTable->VirtualAddress + (lpItem[i] & 0x0FFF);

          KeFixRelocEx(New, Old, lpFixAddress);

        }

        break;

      case IMAGE_REL_BASED_ABSOLUTE://do nothing

        break;

      default:

        break;

      }

    }

    lpRelocateTable = (IMAGE_BASE_RELOCATION *)((PCHAR)lpRelocateTable + lpRelocateTable->SizeOfBlock);

  }

  return;

}


  重点还是KeFixRelocEx,重定位修正的是“地址的值”,希望大家能看懂,那么我就在这里做了相对比较多的判断。
  “地址”可执行,“值”可执行。
  “地址”可执行,“值”不可执行。指向原模块。如访问全局变量。
  “地址”不可执行,“值”可执行。不修改。如函数表。
  “地址”不可执行,“值”不可执行。想不出来是什么,指向原模块吧。
对于第一点,我原本是指向新模块,但测试了好半天,最后还是没有解决重载后某些程序打不开的问题。我曾尝试解析汇编,但字节组合方式太多了,我无法做出最正确的判断,既然我找不到是哪一类重定位存在问题,那么我就做没有问题的地方,如IAT,这种处理很像权限访问中的解决方式(1.我能干什么。2.我不能干什么)。最后代码变成这个样子:

BOOLEAN KeIsExecutable(PVOID ImageBase, PVOID Address)

{

  IMAGE_DOS_HEADER *lpDosHeader = (IMAGE_DOS_HEADER*)ImageBase;

  IMAGE_NT_HEADERS *lpNtHeader = (IMAGE_NT_HEADERS *)((PCHAR)lpDosHeader + lpDosHeader->e_lfanew);

  IMAGE_SECTION_HEADER *lpSecHdr = IMAGE_FIRST_SECTION(lpNtHeader);

  ULONG_PTR Rva = (ULONG_PTR)Address - (ULONG_PTR)ImageBase;

  USHORT i;

  for (i = 0; i < lpNtHeader->FileHeader.NumberOfSections; i++)

  {

    if (Rva >= lpSecHdr[i].VirtualAddress && Rva < lpSecHdr[i].VirtualAddress + lpSecHdr[i].SizeOfRawData)

    {

      return ((lpSecHdr[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0);

    }

  }

  return FALSE;

}

VOID KeFixRelocEx(PVOID New, PVOID Old, PVOID FixAddress)

{

  if (KeIsExecutable(New, FixAddress))

  {

    if (KeIsExecutable(New, *(PVOID*)FixAddress))

    {

      if (KeIsIAT(New, *(PVOID*)FixAddress))

      {

        NOTHING;

      }

      else

      {

        *(ULONG_PTR*)FixAddress = *(ULONG_PTR*)FixAddress - (ULONG_PTR)New + (ULONG_PTR)Old;

      }

    }

    else

    {

      *(ULONG_PTR*)FixAddress = *(ULONG_PTR*)FixAddress - (ULONG_PTR)New + (ULONG_PTR)Old;

    }

  }

  else

  {

    if (KeIsExecutable(New, *(PVOID*)FixAddress))

    {

      NOTHING;

    }

    else

    {

      *(ULONG_PTR*)FixAddress = *(ULONG_PTR*)FixAddress - (ULONG_PTR)New + (ULONG_PTR)Old;

    }

  }

  return;

}


  啰嗦了那么多,非常不完美的解决了某些程序打不开的问题,如果有兴趣的朋友可以继续深入。但回头想想重载仅仅是为了防止HOOK而带来的麻烦,不妨假设HOOK没有那么猥琐,如果你发现重载内核不能绕过某些HOOK,再对症下药吧。
第三部分:获取原始地址
  重头戏来啦!一开始说过原始SSDT函数都存储在一个叫做KiServiceTable的变量里,这个变量同样没有导出,利用字节码搜索的方式效率很低且未必找的准确,所以另求出路。网上已经有大牛研究出了一种方式,那就是利用重定位来查找。
  首先来看WRK是如何做的:(KiInitSystem)

   KeServiceDescriptorTable[0].Base = &KiServiceTable[0];

    KeServiceDescriptorTable[0].Count = NULL;

    KeServiceDescriptorTable[0].Limit = KiServiceLimit;

    KeServiceDescriptorTable[0].Number = KiArgumentTable;

    for (Index = 1; Index < NUMBER_SERVICE_TABLES; Index += 1) {

        KeServiceDescriptorTable[Index].Limit = 0;

    }

    //

    // Copy the system service descriptor table to the shadow table

    // which is used to record the Win32 system services.

    //

    RtlCopyMemory(KeServiceDescriptorTableShadow,

                  KeServiceDescriptorTable,

                  sizeof(KeServiceDescriptorTable));


  再来对比Windows 7 x86中是如何做的,IDA分析如下:

  Ntoskrnl.exe的期望基址是00400000,所以第一句的RVA就是00395C12,来看一下重定位信息:

  可以发现一个特点,有两个连续的重定位00395C14与00396C18,而第一个RVA就是KeServiceDescriptorTable,第二个就是KiServiceTable了,结合IDA对附近代码的综合判断,最后的代码就是这样:

PVOID FindKiServiceTable(PVOID lpNtoskrnlAddress)

{

  PVOID lpKeServiceDescriptorTable = KeGetProcAddress(lpNtoskrnlAddress, "KeServiceDescriptorTable");

  IMAGE_DOS_HEADER *lpDosHeader = (IMAGE_DOS_HEADER*)lpNtoskrnlAddress;

  IMAGE_NT_HEADERS *lpNtHeader = (IMAGE_NT_HEADERS *)((PCHAR)lpDosHeader + lpDosHeader->e_lfanew);

  IMAGE_BASE_RELOCATION *lpRelocateTable = (IMAGE_BASE_RELOCATION*)((PCHAR)lpNtoskrnlAddress + lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

  while (lpRelocateTable->SizeOfBlock)

  {

    ULONG NumberOfItems = (lpRelocateTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);

    USHORT *lpItem = (USHORT*)((PCHAR)lpRelocateTable + sizeof(IMAGE_BASE_RELOCATION));

    ULONG j;

    for (j = 0; j < NumberOfItems - 1; j++)

    {

      if ((lpItem[j] >> 12) == IMAGE_REL_BASED_HIGHLOW && (lpItem[j + 1] >> 12) == IMAGE_REL_BASED_HIGHLOW)

      {

        ULONG *lpFixAddress1 = (ULONG*)((PCHAR)lpNtoskrnlAddress + lpRelocateTable->VirtualAddress + (lpItem[j] & 0x0FFF));

        ULONG *lpFixAddress2 = (ULONG*)((PCHAR)lpNtoskrnlAddress + lpRelocateTable->VirtualAddress + (lpItem[j + 1] & 0x0FFF));

        //两个连续的重定位

        if ((ULONG)lpFixAddress2 - (ULONG)lpFixAddress1 == sizeof(ULONG))

        {

          //MOV DWORD PTR DS:[KeServiceDescriptorTable], XXX

          if (*(USHORT*)((PCHAR)lpFixAddress1 - sizeof(USHORT)) == 0x05C7)

          {

            //DbgPrint("lpFixAddress1:%08X\n", (ULONG)lpFixAddress1 - 2);

            if (*lpFixAddress1 == (ULONG)lpKeServiceDescriptorTable)

            {

              return (PVOID)*lpFixAddress2;

            }

          }

        }

      }

    }

    lpRelocateTable = (IMAGE_BASE_RELOCATION *)((PCHAR)lpRelocateTable + lpRelocateTable->SizeOfBlock);

  }

  return NULL;

}

PVOID BuildKeServiceTable(PVOID lpKernelAddress, PVOID lpOrgKernelAddress)

{

  PVOID lpKiServiceTable = NULL;

  PVOID lpKeServiceTable = NULL;

  if (lpKiServiceTable = FindKiServiceTable(lpKernelAddress))

  {

    lpKeServiceTable = ExAllocatePool(NonPagedPool, KeServiceDescriptorTable->NumberOfService * sizeof(PVOID));

    

    if (lpKeServiceTable)

    {

      RtlCopyMemory(lpKeServiceTable, lpKiServiceTable, KeServiceDescriptorTable->NumberOfService * sizeof(PVOID));

      DbgPrint("BuildSSDT:0x%p\n", lpKeServiceTable);

    }    

  }

  return lpKeServiceTable;

}


  而最后的DriverEntry与ServiceCallFilter则是:

PVOID ServiceCallFilter(PVOID *lppServiceTableBase, ULONG_PTR ServiceIndex)

{

  if (lppServiceTableBase == KeServiceDescriptorTable->ServiceTableBase)

  {

    return g_KeServiceTable[ServiceIndex];

  }

  return lppServiceTableBase[ServiceIndex];

}

VOID __declspec(naked) _MyKiFastCallEntryFrame()

{

  __asm

  {

    push ecx;

    push eax;

    push edi;

    call ServiceCallFilter;

    mov  edx, eax;

    pop  ecx;

    pop  eax;

    sub  esp, ecx;

    shr  ecx, 2;

    mov  edi, esp;

    jmp  eax;

  }

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING  RegistryPath)

{

  PVOID lpHookKiFastCallEntryAddress;

  UCHAR HookCode[7];

  DbgPrint("Driver Load.\n");

  InitializePsLoadedModuleList(DriverObject);

  g_lpNtoskrnlAddress = KeGetModuleHandle(PsLoadedModuleList, "ntoskrnl.exe");

  g_lpNewNtoskrnlAddress = ReloadNtModule(PsLoadedModuleList);

  g_KeServiceTable = (PVOID*)BuildKeServiceTable(g_lpNewNtoskrnlAddress, g_lpNtoskrnlAddress);

  KeFixReloc2(g_lpNewNtoskrnlAddress, g_lpNtoskrnlAddress);

  lpHookKiFastCallEntryAddress = FindHookKiFastCallEntryAddress(GetKiFastCallEntryAddress());

  HookCode[0] = 0xE8;

  *(ULONG*)&HookCode[1] = (ULONG_PTR)_MyKiFastCallEntryFrame - (ULONG_PTR)lpHookKiFastCallEntryAddress - 5;

  HookCode[5] = 0x90;

  HookCode[6] = 0x90;

  RtlCopyMemoryEx(lpHookKiFastCallEntryAddress, HookCode, sizeof(HookCode));

  DriverObject->DriverUnload = DriverUnload;

  return STATUS_SUCCESS;

}


  来来来测试一下,就用HideToolz来看效果。


  加载驱动咯。

  好了,无视SSDT HOOK,同样无视INLINE HOOK,终于算是完成了一点像样的东西了,但这并不是全部,也不是此专题的完结,后面还有哦,我想后面应该是告诉大家如何再把SHADOW SSDT搞定。
  最后,此代码仍然不能拿到本机测试,如果你想蓝的话。对于新模块最少还有两个问题没有搞定(别喷我),后续我同样会告诉大家。嗯,先这样吧。

上传的附件 01.jpg
02.jpg
03.jpg
04.jpg
05.jpg
3.重载内核之获取原始地址.doc

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功