我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C语言驱动开发内核枚举IoTimer定时器解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C语言驱动开发内核枚举IoTimer定时器解析

正文

今天继续分享内核枚举系列知识,这次我们来学习如何通过代码的方式枚举内核IoTimer定时器,内核定时器其实就是在内核中实现的时钟,该定时器的枚举非常简单,因为在IoInitializeTimer初始化部分就可以找到IopTimerQueueHead地址,该变量内存储的就是定时器的链表头部。枚举IO定时器的案例并不多见,即便有也是无法使用过时的,此教程学到肯定就是赚到了。

枚举Io定时器过程

  • 1.找到IoInitializeTimer函数,该函数可以通过MmGetSystemRoutineAddress得到。
  • 2.找到地址以后,我们向下增加0xFF偏移量,并搜索特征定位到IopTimerQueueHead链表头。
  • 3.将链表头转换为IO_TIMER结构体,并循环链表头输出。

这里解释一下为什么要找IoInitializeTimer这个函数他是一个初始化函数,既然是初始化里面一定会涉及到链表的存储问题,找到他就能找到定时器链表基址,该函数的定义如下。

NTSTATUS 
  IoInitializeTimer(
    IN PDEVICE_OBJECT  DeviceObject,     // 设备对象指针
    IN PIO_TIMER_ROUTINE  TimerRoutine,  // 定时器例程
    IN PVOID  Context                    // 传给定时器例程的函数
    );

接着我们需要得到IO定时器的结构定义,在DEVICE_OBJECT设备对象指针中存在一个Timer属性。

lyshark.com: kd> dt _DEVICE_OBJECT
ntdll!_DEVICE_OBJECT
   +0x000 Type             : Int2B
   +0x002 Size             : Uint2B
   +0x004 ReferenceCount   : Int4B
   +0x008 DriverObject     : Ptr64 _DRIVER_OBJECT
   +0x010 NextDevice       : Ptr64 _DEVICE_OBJECT
   +0x018 AttachedDevice   : Ptr64 _DEVICE_OBJECT
   +0x020 CurrentIrp       : Ptr64 _IRP
   +0x028 Timer            : Ptr64 _IO_TIMER
   +0x030 Flags            : Uint4B
   +0x034 Characteristics  : Uint4B
   +0x038 Vpb              : Ptr64 _VPB
   +0x040 DeviceExtension  : Ptr64 Void
   +0x048 DeviceType       : Uint4B
   +0x04c StackSize        : Char
   +0x050 Queue            : <anonymous-tag>
   +0x098 AlignmentRequirement : Uint4B
   +0x0a0 DeviceQueue      : _KDEVICE_QUEUE
   +0x0c8 Dpc              : _KDPC
   +0x108 ActiveThreadCount : Uint4B
   +0x110 SecurityDescriptor : Ptr64 Void
   +0x118 DeviceLock       : _KEVENT
   +0x130 SectorSize       : Uint2B
   +0x132 Spare1           : Uint2B
   +0x138 DeviceObjectExtension : Ptr64 _DEVOBJ_EXTENSION
   +0x140 Reserved         : Ptr64 Void

这里的这个+0x028 Timer定时器是一个结构体_IO_TIMER其就是IO定时器的所需结构体。

lyshark.com: kd> dt _IO_TIMER
ntdll!_IO_TIMER
   +0x000 Type             : Int2B
   +0x002 TimerFlag        : Int2B
   +0x008 TimerList        : _LIST_ENTRY
   +0x018 TimerRoutine     : Ptr64     void 
   +0x020 Context          : Ptr64 Void
   +0x028 DeviceObject     : Ptr64 _DEVICE_OBJECT

如上方的基础知识有了也就够了,接着就是实际开发部分,首先我们需要编写一个GetIoInitializeTimerAddress()函数,让该函数可以定位到IoInitializeTimer所在内核中的基地址上面,具体实现调用代码如下所示。

GetIoInitializeTimerAddress()函数

#include <ntifs.h>
// 得到IoInitializeTimer基址
// By: LyShark 内核开发系列教程
PVOID GetIoInitializeTimerAddress()
{
	PVOID VariableAddress = 0;
	UNICODE_STRING uioiTime = { 0 };
	RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer");
	VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime);
	if (VariableAddress != 0)
	{
		return VariableAddress;
	}
	return 0;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint(("Uninstall Driver Is OK \n"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint(("hello lyshark.com \n"));
	// 得到基址
	PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress();
	DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer);
	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

运行这个驱动程序,然后对比下是否一致:

接着我们在反汇编代码中寻找IoTimerQueueHead,此处在LyShark系统内这个偏移位置是nt!IoInitializeTimer+0x5d 具体输出位置如下。

lyshark.com: kd> uf IoInitializeTimer
nt!IoInitializeTimer+0x5d:
fffff805`74b85bed 488d5008        lea     rdx,[rax+8]
fffff805`74b85bf1 48897018        mov     qword ptr [rax+18h],rsi
fffff805`74b85bf5 4c8d054475e0ff  lea     r8,[nt!IopTimerLock (fffff805`7498d140)]
fffff805`74b85bfc 48897820        mov     qword ptr [rax+20h],rdi
fffff805`74b85c00 488d0dd9ddcdff  lea     rcx,[nt!IopTimerQueueHead (fffff805`748639e0)]
fffff805`74b85c07 e8141e98ff      call    nt!ExInterlockedInsertTailList (fffff805`74507a20)
fffff805`74b85c0c 33c0            xor     eax,eax

在WinDBG中标注出颜色lea rcx,[nt!IopTimerQueueHead (fffff805748639e0)]更容易看到。

接着就是通过代码实现对此处的定位,定位我们就采用特征码搜索的方式,如下代码是特征搜索部分。

特征搜索部分

  • StartSearchAddress 代表开始位置
  • EndSearchAddress 代表结束位置,粗略计算0xff就可以定位到了。
#include <ntifs.h>
// 得到IoInitializeTimer基址
// By: LyShark 内核开发系列教程
PVOID GetIoInitializeTimerAddress()
{
	PVOID VariableAddress = 0;
	UNICODE_STRING uioiTime = { 0 };
	RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer");
	VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime);
	if (VariableAddress != 0)
	{
		return VariableAddress;
	}
	return 0;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint(("Uninstall Driver Is OK \n"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint(("hello lyshark.com \n"));
	// 得到基址
	PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress();
	DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer);
	INT32 iOffset = 0;
	PLIST_ENTRY IoTimerQueueHead = NULL;
	PUCHAR StartSearchAddress = IoInitializeTimer;
	PUCHAR EndSearchAddress = IoInitializeTimer + 0xFF;
	UCHAR v1 = 0, v2 = 0, v3 = 0;
	for (PUCHAR i = StartSearchAddress; i < EndSearchAddress; i++)
	{
		if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
		{
			v1 = *i;
			v2 = *(i + 1);
			v3 = *(i + 2);
			// 三个特征码
			if (v1 == 0x48 && v2 == 0x8d && v3 == 0x0d)
			{
				memcpy(&iOffset, i + 3, 4);
				IoTimerQueueHead = (PLIST_ENTRY)(iOffset + (ULONG64)i + 7);
				DbgPrint("IoTimerQueueHead = %p \n", IoTimerQueueHead);
				break;
			}
		}
	}
	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

搜索三个特征码v1 == 0x48 && v2 == 0x8d && v3 == 0x0d从而得到内存位置,运行驱动对比下。

  • 运行代码会取出lea指令后面的操作数,而不是取出lea指令的内存地址。

IO_TIMER结构体定义

最后一步就是枚举部分,我们需要前面提到的IO_TIMER结构体定义。

  • PIO_TIMER Timer = CONTAINING_RECORD(NextEntry, IO_TIMER, TimerList) 得到结构体,循环输出即可。
// By: LyShark 内核开发系列教程
// https://www.cnblogs.com/LyShark/articles/16784393.html
#include <ntddk.h>
#include <ntstrsafe.h>
typedef struct _IO_TIMER
{
  INT16        Type;
  INT16        TimerFlag;
  LONG32       Unknown;
  LIST_ENTRY   TimerList;
  PVOID        TimerRoutine;
  PVOID        Context;
  PVOID        DeviceObject;
}IO_TIMER, *PIO_TIMER;
// 得到IoInitializeTimer基址
PVOID GetIoInitializeTimerAddress()
{
  PVOID VariableAddress = 0;
  UNICODE_STRING uioiTime = { 0 };
  RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer");
  VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime);
  if (VariableAddress != 0)
  {
    return VariableAddress;
  }
  return 0;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
  DbgPrint("卸载完成... \n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
  DbgPrint(("hello lyshark.com \n"));
  // 得到基址
  PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress();
  DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer);
  // 搜索IoTimerQueueHead地址
  
  INT32 iOffset = 0;
  PLIST_ENTRY IoTimerQueueHead = NULL;
  PUCHAR StartSearchAddress = IoInitializeTimer;
  PUCHAR EndSearchAddress = IoInitializeTimer + 0xFF;
  UCHAR v1 = 0, v2 = 0, v3 = 0;
  for (PUCHAR i = StartSearchAddress; i < EndSearchAddress; i++)
  {
    if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
    {
      v1 = *i;
      v2 = *(i + 1);
      v3 = *(i + 2);
      // fffff806`349963e0 48 8d 0d 99 f6 cd ff  lea rcx,[nt!IopTimerQueueHead (fffff806`34675a80)]
      if (v1 == 0x48 && v2 == 0x8d && v3 == 0x0d)
      {
        memcpy(&iOffset, i + 3, 4);
        IoTimerQueueHead = (PLIST_ENTRY)(iOffset + (ULONG64)i + 7);
        DbgPrint("IoTimerQueueHead = %p \n", IoTimerQueueHead);
        break;
      }
    }
  }
  // 枚举列表
  KIRQL OldIrql;
  // 获得特权级
  OldIrql = KeRaiseIrqlToDpcLevel();
  if (IoTimerQueueHead && MmIsAddressValid((PVOID)IoTimerQueueHead))
  {
    PLIST_ENTRY NextEntry = IoTimerQueueHead->Flink;
    while (MmIsAddressValid(NextEntry) && NextEntry != (PLIST_ENTRY)IoTimerQueueHead)
    {
      PIO_TIMER Timer = CONTAINING_RECORD(NextEntry, IO_TIMER, TimerList);
      if (Timer && MmIsAddressValid(Timer))
      {
        DbgPrint("IO对象地址: %p \n", Timer);
      }
      NextEntry = NextEntry->Flink;
    }
  }
  // 恢复特权级
  KeLowerIrql(OldIrql);
  Driver->DriverUnload = UnDriver;
  return STATUS_SUCCESS;
}

运行这段源代码,并可得到以下输出,由于没有IO定时器所以输出结果是空的:

至此IO定时器的枚举就介绍完了,在教程中你已经学会了使用特征码定位这门技术,相信你完全可以输出内核中想要得到的任何结构体。

以上就是C语言驱动开发内核枚举IoTimer定时器解析的详细内容,更多关于C语言 内核枚举IoTimer定时器的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C语言驱动开发内核枚举IoTimer定时器解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C语言驱动开发内核枚举IoTimer定时器解析

这篇文章主要为大家介绍了C语言驱动开发内核枚举IoTimer定时器解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

C语言驱动开发内核枚举IoTimer定时器怎么实现

本篇内容主要讲解“C语言驱动开发内核枚举IoTimer定时器怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言驱动开发内核枚举IoTimer定时器怎么实现”吧!正文IoTimer内核定
2023-07-04

C语言驱动开发之内核使用IO/DPC定时器详解

本章将继续探索驱动开发中的基础部分,定时器在内核中同样很常用,在内核中定时器可以使用两种,即IO定时器,以及DPC定时器,感兴趣的可以了解一下
2023-05-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录