模块是程序加载时被动态装载的,模块在装载后其存在于内存中同样存在一个内存基址,当我们需要操作这个模块时,通常第一步就是要得到该模块的内存基址,模块分为用户模块和内核模块,这里的用户模块指的是应用层进程运行后加载的模块,内核模块指的是内核中特定模块地址,本篇文章将实现一个获取驱动ntoskrnl.exe的基地址以及长度,此功能是驱动开发中尤其是安全软件开发中必不可少的一个功能。

关于该程序的解释,官方的解析是这样的ntoskrnl.exeWindows操作系统的一个重要内核程序,里面存储了大量的二进制内核代码,用于调度系统时使用,也是操作系统启动后第一个被加载的程序,通常该进程在任务管理器中显示为System

使用ARK工具也可看出其代表的是第一个驱动模块。

那么如何使用代码得到如上图中所展示的基地址以及大小呢,实现此功能我们需要调用ZwQuerySystemInformation这个API函数,这与上一篇文章《驱动开发:判断自身是否加载成功》所使用的NtQuerySystemInformation只是开头部分不同,但其本质上是不同的,如下是一些参考资料;

  • 从内核模式调用NtZw系列API,其最终都会连接到nooskrnl.lib导出库:

    • Nt系列API将直接调用对应的函数代码,而Zw系列API则通过调用KiSystemService最终跳转到对应的函数代码。
    • 重要的是两种不同的调用对内核中previous mode的改变,如果是从用户模式调用Native APIprevious mode是用户态,如果从内核模式调用Native APIprevious mode是内核态。
    • 如果previous为用户态时Native API将对传递的参数进行严格的检查,而为内核态时则不会检查。

调用Nt API时不会改变previous mode的状态,调用Zw API时会将previous mode改为内核态,因此在进行Kernel Mode Driver开发时可以使用Zw系列API可以避免额外的参数列表检查,提高效率。Zw*会设置KernelMode已避免检查,Nt*不会自动设置,如果是KernelMode当然没问题,如果就UserMode就挂了。

回到代码上来,下方代码就是获取ntoskrnl.exe基地址以及长度的具体实现,核心代码就是调用ZwQuerySystemInformation得到SystemModuleInformation,里面的对比部分是在比较当前获取的地址是否超出了ntoskrnl的最大和最小范围。

  1. #include <ntifs.h>
  2. static PVOID g_KernelBase = 0;
  3. static ULONG g_KernelSize = 0;
  4. #pragma pack(4)
  5. typedef struct _PEB32
  6. {
  7. UCHAR InheritedAddressSpace;
  8. UCHAR ReadImageFileExecOptions;
  9. UCHAR BeingDebugged;
  10. UCHAR BitField;
  11. ULONG Mutant;
  12. ULONG ImageBaseAddress;
  13. ULONG Ldr;
  14. ULONG ProcessParameters;
  15. ULONG SubSystemData;
  16. ULONG ProcessHeap;
  17. ULONG FastPebLock;
  18. ULONG AtlThunkSListPtr;
  19. ULONG IFEOKey;
  20. ULONG CrossProcessFlags;
  21. ULONG UserSharedInfoPtr;
  22. ULONG SystemReserved;
  23. ULONG AtlThunkSListPtr32;
  24. ULONG ApiSetMap;
  25. } PEB32, *PPEB32;
  26. typedef struct _PEB_LDR_DATA32
  27. {
  28. ULONG Length;
  29. UCHAR Initialized;
  30. ULONG SsHandle;
  31. LIST_ENTRY32 InLoadOrderModuleList;
  32. LIST_ENTRY32 InMemoryOrderModuleList;
  33. LIST_ENTRY32 InInitializationOrderModuleList;
  34. } PEB_LDR_DATA32, *PPEB_LDR_DATA32;
  35. typedef struct _LDR_DATA_TABLE_ENTRY32
  36. {
  37. LIST_ENTRY32 InLoadOrderLinks;
  38. LIST_ENTRY32 InMemoryOrderLinks;
  39. LIST_ENTRY32 InInitializationOrderLinks;
  40. ULONG DllBase;
  41. ULONG EntryPoint;
  42. ULONG SizeOfImage;
  43. UNICODE_STRING32 FullDllName;
  44. UNICODE_STRING32 BaseDllName;
  45. ULONG Flags;
  46. USHORT LoadCount;
  47. USHORT TlsIndex;
  48. LIST_ENTRY32 HashLinks;
  49. ULONG TimeDateStamp;
  50. } LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
  51. #pragma pack()
  52. typedef struct _RTL_PROCESS_MODULE_INFORMATION
  53. {
  54. HANDLE Section;
  55. PVOID MappedBase;
  56. PVOID ImageBase;
  57. ULONG ImageSize;
  58. ULONG Flags;
  59. USHORT LoadOrderIndex;
  60. USHORT InitOrderIndex;
  61. USHORT LoadCount;
  62. USHORT OffsetToFileName;
  63. UCHAR FullPathName[256];
  64. } RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
  65. typedef struct _RTL_PROCESS_MODULES
  66. {
  67. ULONG NumberOfModules;
  68. RTL_PROCESS_MODULE_INFORMATION Modules[1];
  69. } RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
  70. typedef enum _SYSTEM_INFORMATION_CLASS
  71. {
  72. SystemModuleInformation = 0xb,
  73. } SYSTEM_INFORMATION_CLASS;
  74. // 取出KernelBase基地址
  75. // By: lyshark.com
  76. PVOID UtilKernelBase(OUT PULONG pSize)
  77. {
  78. NTSTATUS status = STATUS_SUCCESS;
  79. ULONG bytes = 0;
  80. PRTL_PROCESS_MODULES pMods = 0;
  81. PVOID checkPtr = 0;
  82. UNICODE_STRING routineName;
  83. if (g_KernelBase != 0)
  84. {
  85. if (pSize)
  86. *pSize = g_KernelSize;
  87. return g_KernelBase;
  88. }
  89. RtlInitUnicodeString(&routineName, L"NtOpenFile");
  90. checkPtr = MmGetSystemRoutineAddress(&routineName);
  91. if (checkPtr == 0)
  92. return 0;
  93. __try
  94. {
  95. status = ZwQuerySystemInformation(SystemModuleInformation, 0, bytes, &bytes);
  96. if (bytes == 0)
  97. {
  98. DbgPrint("Invalid SystemModuleInformation size\n");
  99. return 0;
  100. }
  101. pMods = (PRTL_PROCESS_MODULES)ExAllocatePoolWithTag(NonPagedPoolNx, bytes, "lyshark");
  102. RtlZeroMemory(pMods, bytes);
  103. status = ZwQuerySystemInformation(SystemModuleInformation, pMods, bytes, &bytes);
  104. if (NT_SUCCESS(status))
  105. {
  106. PRTL_PROCESS_MODULE_INFORMATION pMod = pMods->Modules;
  107. for (ULONG i = 0; i < pMods->NumberOfModules; i++)
  108. {
  109. if (checkPtr >= pMod[i].ImageBase &&
  110. checkPtr < (PVOID)((PUCHAR)pMod[i].ImageBase + pMod[i].ImageSize))
  111. {
  112. g_KernelBase = pMod[i].ImageBase;
  113. g_KernelSize = pMod[i].ImageSize;
  114. if (pSize)
  115. *pSize = g_KernelSize;
  116. break;
  117. }
  118. }
  119. }
  120. }
  121. __except (EXCEPTION_EXECUTE_HANDLER)
  122. {
  123. return 0;
  124. }
  125. if (pMods)
  126. ExFreePoolWithTag(pMods, "lyshark");
  127. return g_KernelBase;
  128. }
  129. VOID UnDriver(PDRIVER_OBJECT driver)
  130. {
  131. DbgPrint(("Uninstall Driver Is OK \n"));
  132. }
  133. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  134. {
  135. DbgPrint(("hello lyshark \n"));
  136. PULONG ulong = 0;
  137. UtilKernelBase(ulong);
  138. DbgPrint("ntoskrnl.exe 模块基址: 0x%p \n", g_KernelBase);
  139. DbgPrint("模块大小: 0x%p \n", g_KernelSize);
  140. Driver->DriverUnload = UnDriver;
  141. return STATUS_SUCCESS;
  142. }

我们编译并运行上方代码,效果如下:

参考文献:

https://blog.csdn.net/u012410612/article/details/17096597

驱动开发:内核取ntoskrnl模块基地址的更多相关文章

  1. Linux驱动之内核加载模块过程分析

    Linux内核支持动态的加载模块运行:比如insmod first_drv.ko,这样就可以将模块加载到内核所在空间供应用程序调用.现在简单描述下insmod first_drv.ko的过程 1.in ...

  2. Windows驱动开发-内核常用内存函数

    搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool

  3. 驱动开发:内核特征码扫描PE代码段

    在笔者上一篇文章<驱动开发:内核特征码搜索函数封装>中为了定位特征的方便我们封装实现了一个可以传入数组实现的SearchSpecialCode定位函数,该定位函数其实还不能算的上简单,本章 ...

  4. 驱动开发:内核中实现Dump进程转储

    多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导 ...

  5. 驱动开发:内核枚举ShadowSSDT基址

    在笔者上一篇文章<驱动开发:Win10枚举完整SSDT地址表>实现了针对SSDT表的枚举功能,本章继续实现对SSSDT表的枚举,ShadowSSDT中文名影子系统服务描述表,SSSDT其主 ...

  6. Windows x64位通过PEB获得Kernel32基地址

    在64位系统下 gs:[0x30] 指向TEB gs:[0x60] 指向PEB kd> dt _TEB nt!_TEB +0x000 NtTib : _NT_TIB +0x000 Excepti ...

  7. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入L ...

  8. 驱动开发:内核枚举PspCidTable句柄表

    在上一篇文章<驱动开发:内核枚举DpcTimer定时器>中我们通过枚举特征码的方式找到了DPC定时器基址并输出了内核中存在的定时器列表,本章将学习如何通过特征码定位的方式寻找Windows ...

  9. 驱动开发:内核枚举Registry注册表回调

    在笔者上一篇文章<驱动开发:内核枚举LoadImage映像回调>中LyShark教大家实现了枚举系统回调中的LoadImage通知消息,本章将实现对Registry注册表通知消息的枚举,与 ...

随机推荐

  1. jenkins+tomcat自动化部署

    一.配置tomcat tomcat版本:tomcat-9.0.39 vim /usr/local/tomcat-9.0.39/conf/tomcat-users.xml <tomcat-user ...

  2. angular变更检测相关文章

    你需要了解的关于Angular 变更检测的一切 If you think `ngDoCheck` means your component is being checked - read this a ...

  3. React Native环境配置、初始化项目、打包安装到手机,以及开发小知识

    1.前言 环境:Win10 + Android 已经在Windows电脑上安装好 Node(v14+).Git.Yarn. JDK(v11) javac -version javac 11.0.15. ...

  4. NodeJS & Dapr Javascript SDK 官方使用指南

    Dapr 是一个可移植的.事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的.无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架.Dapr 确保开发人员专注 ...

  5. 关于virtio_net网卡命名的小问题

    最近看了一个小问题,涉及到一致性网络设备命名(Consistent Network Device Naming),在此记录一下. 系统是 4.18.0-240.el8.x86_64,centos 8. ...

  6. 刷题记录:Codeforces Round #724 (Div. 2)

    Codeforces Round #724 (Div. 2) 20210713.网址:https://codeforces.com/contest/1536. div2明显比div3难多了啊-只做了前 ...

  7. tqdm和zip组合使用时无法显示进度条-解决办法

    问题 单独对于可迭代对象iterator使用tqdm时,结合循环就可以在终端显示进度条, 以直观展示程序进度,如下: from tqdm import tqdm textlist = [] for i ...

  8. HTML创建访问加密代码

    在</head>前面加入即可 普通方式 此方法屏蔽F12查看源码但是屏蔽不了Ctrl+U查看源码 解决方式加密html即可注意!解密比较繁琐切记要记住自己设置的密码 <SCRIPT ...

  9. 踩坑,发现一个ShardingJdbc读写分离的BUG

    ShardingJdbc 怎么处理写完数据立即读的情况的呢? 写在前面 我本地使用了两个库来做写库(ds_0_master)和读库(ds_0_salve),两个库并没有配置主从. 下面我就使用库里的 ...

  10. MAC上PKG打包

    pkg是Mac平台上非常常见的一种安装包格式,如果你想要快速将软件制作为pkg文件,就千万不要错过Packages Mac版,Packages Mac版是Mac平台上能够快速为您生成pkg程序包的一款 ...