模块是程序加载时被动态装载的,模块在装载后其存在于内存中同样存在一个内存基址,当我们需要操作这个模块时,通常第一步就是要得到该模块的内存基址,模块分为用户模块和内核模块,这里的用户模块指的是应用层进程运行后加载的模块,内核模块指的是内核中特定模块地址,本篇文章将实现一个获取驱动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的最大和最小范围。

#include <ntifs.h>

static PVOID g_KernelBase = 0;
static ULONG g_KernelSize = 0; #pragma pack(4)
typedef struct _PEB32
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR BitField;
ULONG Mutant;
ULONG ImageBaseAddress;
ULONG Ldr;
ULONG ProcessParameters;
ULONG SubSystemData;
ULONG ProcessHeap;
ULONG FastPebLock;
ULONG AtlThunkSListPtr;
ULONG IFEOKey;
ULONG CrossProcessFlags;
ULONG UserSharedInfoPtr;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
ULONG ApiSetMap;
} PEB32, *PPEB32; typedef struct _PEB_LDR_DATA32
{
ULONG Length;
UCHAR Initialized;
ULONG SsHandle;
LIST_ENTRY32 InLoadOrderModuleList;
LIST_ENTRY32 InMemoryOrderModuleList;
LIST_ENTRY32 InInitializationOrderModuleList;
} PEB_LDR_DATA32, *PPEB_LDR_DATA32; typedef struct _LDR_DATA_TABLE_ENTRY32
{
LIST_ENTRY32 InLoadOrderLinks;
LIST_ENTRY32 InMemoryOrderLinks;
LIST_ENTRY32 InInitializationOrderLinks;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING32 FullDllName;
UNICODE_STRING32 BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY32 HashLinks;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
#pragma pack() typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; typedef struct _RTL_PROCESS_MODULES
{
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemModuleInformation = 0xb,
} SYSTEM_INFORMATION_CLASS; // 取出KernelBase基地址
// By: lyshark.com
PVOID UtilKernelBase(OUT PULONG pSize)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG bytes = 0;
PRTL_PROCESS_MODULES pMods = 0;
PVOID checkPtr = 0;
UNICODE_STRING routineName; if (g_KernelBase != 0)
{
if (pSize)
*pSize = g_KernelSize;
return g_KernelBase;
} RtlInitUnicodeString(&routineName, L"NtOpenFile"); checkPtr = MmGetSystemRoutineAddress(&routineName);
if (checkPtr == 0)
return 0; __try
{
status = ZwQuerySystemInformation(SystemModuleInformation, 0, bytes, &bytes);
if (bytes == 0)
{
DbgPrint("Invalid SystemModuleInformation size\n");
return 0;
} pMods = (PRTL_PROCESS_MODULES)ExAllocatePoolWithTag(NonPagedPoolNx, bytes, "lyshark");
RtlZeroMemory(pMods, bytes); status = ZwQuerySystemInformation(SystemModuleInformation, pMods, bytes, &bytes); if (NT_SUCCESS(status))
{
PRTL_PROCESS_MODULE_INFORMATION pMod = pMods->Modules; for (ULONG i = 0; i < pMods->NumberOfModules; i++)
{
if (checkPtr >= pMod[i].ImageBase &&
checkPtr < (PVOID)((PUCHAR)pMod[i].ImageBase + pMod[i].ImageSize))
{
g_KernelBase = pMod[i].ImageBase;
g_KernelSize = pMod[i].ImageSize;
if (pSize)
*pSize = g_KernelSize;
break;
}
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
} if (pMods)
ExFreePoolWithTag(pMods, "lyshark");
return g_KernelBase;
} VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
} NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint(("hello lyshark \n")); PULONG ulong = 0;
UtilKernelBase(ulong);
DbgPrint("ntoskrnl.exe 模块基址: 0x%p \n", g_KernelBase);
DbgPrint("模块大小: 0x%p \n", g_KernelSize); Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}

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

参考文献:

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. 乘风破浪,遇见未来新能源汽车(Electric Vehicle)之特斯拉提车必须知道的十个流程

    订车 线下门店或者官网可以咨询和下单,一般来说,订金就是1000,还算可以接受. 订单账号 特斯拉账号是以邮箱为区分的,而不是手机号,我们下单的时候需要提供一个邮箱用来注册特斯拉账号. 注意了,敲黑板 ...

  2. maven项目(引入依赖失败, pom.xml 报错\爆红)

    引入项目过程中,idea引入磁盘的某个的maven项目 这里以springboot项目以例子,发现pom.xml 的依赖大面积爆红,springboot的版本依赖也报错了,然后发现下面有进度条在下载, ...

  3. mysql开发实战8问

    mysql读写性能是多少,有哪些性能相关的配置参数? Mysql负载高时,如何找到是由哪些SQL引起的? 如何针对具体的SQL做优化? SQL层面已难以优化,请求量继续增大时的应对策略? Mysql如 ...

  4. (转)git使用收集

    由于最近项目开始弃SVN用git,特意整理下git命令.原文链接为http://www.jb51.net/article/55442.htm git branch 查看本地所有分支git status ...

  5. 转:mysql保留关键字

    原文链接:http://www.tuicool.com/articles/Brauq2e 从网上找了一个mysql的保留字列表,仅供参考. ADD ALL ALTER ANALYZE AND AS A ...

  6. cad工具快速选择特性里面是空的解决方法

    工具-选项-文件中,支持文件搜索路径中 添加,再浏览,找到"C:\Program Files\Common Files\Autodesk Shared"确定就OK了.

  7. 黑马2022最新redis课程笔记知识点(面试用)持续更新

    redis入门 redis的常见五种数据类型 String类型 String类型,类似于java中的String类型,常见使用get,set方法. String类型还可以存储json字符串格式. Ha ...

  8. linux 磁盘io利用率高,分析的正确姿势

    一.背景简介 作为一个DBA难免不了会遇到性能问题,那么我们遇到性能问题该如何进行排查呢?例如我们在高并发的业务下,出现业务响应慢,处理时间长我们又该如何入手进行排查,本片文章将分析io高的情况下如何 ...

  9. 如何开发一款基于 vite+vue3 的在线表格系统(下)

    在上篇内容中我们为大家分享了详细介绍Vue3和Vite的相关内容.在本篇中我们将从项目实战出发带大家了解Vite+Vue3 的在线表格系统的构建. 使用Vite初始化Vue3项目 在这里需要注意:根据 ...

  10. 第四十四篇:Git分支(关键知识点)

    好家伙, GIT分支 分支就像是平行宇宙,两个平行宇宙自己平行,不相干扰,平安无事, 某一天它想不开,合并了.然后就变成了我写这篇博客的动机了. 1.关于Git分支中常用的指令 列出所有分支 git ...