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

目录
  • 正文
  • 枚举Io定时器过程
    • GetIoInitializeTimerAddress()函数
    • nt!IoInitializeTimer+0x5d 输出位置
    • 特征搜索部分
    • IO_TIMER结构体定义

正文

今天继续分享内核枚举系列知识,这次我们来学习如何通过代码的方式枚举内核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;
}

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

nt!IoInitializeTimer+0x5d 输出位置

接着我们在反汇编代码中寻找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地址
  /*
    nt!IoInitializeTimer+0x5d:
    fffff806`349963cd 488d5008        lea     rdx,[rax+8]
    fffff806`349963d1 48897018        mov     qword ptr [rax+18h],rsi
    fffff806`349963d5 4c8d05648de0ff  lea     r8,[nt!IopTimerLock (fffff806`3479f140)]
    fffff806`349963dc 48897820        mov     qword ptr [rax+20h],rdi
    fffff806`349963e0 488d0d99f6cdff  lea     rcx,[nt!IopTimerQueueHead (fffff806`34675a80)]
    fffff806`349963e7 e8c43598ff      call    nt!ExInterlockedInsertTailList (fffff806`343199b0)
    fffff806`349963ec 33c0            xor     eax,eax
  */
  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定时器的资料请关注我们其它相关文章!

(0)

相关推荐

  • vc6.0中c语言控制台程序中的定时技术(定时器)

    打开main.c编译运行,注意,打开main.c之后一定要将win32timer.c也加进工程中一起编译,下面有图.在开发单片机.ARM以及Linux系统的程序时,因为硬件定时中断的存在我们很方便构造出定时ISR,然而在VC6.0中,我们如何写一个定时程序呢?其实,就是timeSetEvent()这个函数的调用.这个函数的解释见MSDN.详细原理,请看我代码中的注释,我写得很详细了. main.c 复制代码 代码如下: //======================// main.c//===

  • C语言实现简单的定时器

    本文实例为大家分享了C语言实现简单的定时器的具体代码,供大家参考,具体内容如下 1.代码分析 2.代码 #include <stdio.h> #include <time.h> #include <conio.h> #ifndef CLOCKS_PER_SEC #define CLOCKS_PER_SEC 1000 #endif int main( void ) { clock_t start; long count = 1; start = clock(); whil

  • C语言实现定时器控制LED灯闪烁

    本文实例为大家分享了C语言实现定时器控制LED灯闪烁的具体代码,供大家参考,具体内容如下 实现效果如图: 周期:2s: LED引脚为P2口. #include<reg52.h> void Delay_1s(void) { unsigned char i; for(i=0;i<20;i++) { //2的6次方=655136 TH1=0x3C;//655136-50000=155146==>0x3CB0 TL1=0xB0; TR1=1;//启动定时器,开始计时 while(!TF1)

  • C语言实现多线程定时器实例讲解

    1. 大致功能介绍 实现任务列表,定时器会间隔一段时间遍历列表发现要执行的任务 任务列表中的所有任务并行执行 每个任务都可以有自己的定时器,并且可以选择是否要重复执行 定义方便的任务函数实现接口 定时器可以由用户自定义何时启动和停止 提供等待功能,保证任务列表中的所有任务执行完成 提供任务列表的传参功能 2. API库介绍 void setTick(int val); 设置定时间的间隔时间tick,若设置tick为1000,且任务的定时器时间为1000,则任务会在1秒后执行,默认tick为1秒,

  • 利用C语言实现经典多级时间轮定时器

    目录 1. 序言 2. 多级时间轮实现框架 2.1 多级时间轮对象 2.2 时间轮对象 2.3 定时任务对象 2.4 双向链表 2.5 联结方式 3. 多级时间轮C语言实现 3.1 双向链表头文件: list.h 3.2 调试信息头文件: log.h 3.3 时间轮代码: timewheel.c 3.4 编译运行 总结 1. 序言 最近一直在找时间轮的C语言实现代码,发现很多都是Java或者c++实现的.而我对其他语言不熟悉,看不太懂.关于C实现的,让我如沐春风的实现没找到,github上也只找

  • c语言定时器示例分享

    在linux下开发,使用的是C语言.适用于需要定时的软件开发,以系统真实的时间来计算,它送出SIGALRM信号.每隔一秒定时一次 c语言定时器 复制代码 代码如下: #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <termios.h>#include <errno.h>#include <ctype.h>#include <stdio.

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

    目录 正文 枚举Io定时器过程 GetIoInitializeTimerAddress()函数 nt!IoInitializeTimer+0x5d 输出位置 特征搜索部分 IO_TIMER结构体定义 正文 今天继续分享内核枚举系列知识,这次我们来学习如何通过代码的方式枚举内核IoTimer定时器,内核定时器其实就是在内核中实现的时钟,该定时器的枚举非常简单,因为在IoInitializeTimer初始化部分就可以找到IopTimerQueueHead地址,该变量内存储的就是定时器的链表头部.枚举

  • C语言驱动开发之通过ReadFile与内核层通信

    驱动与应用程序的通信是非常有必要的,内核中执行代码后需要将其动态显示给应用层,但驱动程序与应用层毕竟不在一个地址空间内,为了实现内核与应用层数据交互则必须有通信的方法,微软为我们提供了三种通信方式,如下先来介绍通过ReadFile系列函数实现的通信模式. 长话短说,不说没用的概念,首先系统中支持的通信模式可以总结为三种. 缓冲区方式读写(DO_BUFFERED_IO) 直接方式读写(DO_DIRECT_IO) 其他方式读写 而通过ReadFile,WriteFile系列函数实现的通信机制则属于缓

  • Go语言驱动低代码应用引擎工具Yao开发管理系统

    目录 前言 Yao简介 安裝 使用 基本使用 创建数据模型 编写接口 编写界面 总结 前言 之前写过一篇关于阿里的低代码工具LowCodeEngine的文章,发现大家还是挺感兴趣的.最近又发现了一款很有意思的低代码工具Yao,支持使用JSON的形式开发管理系统,不仅可以用来开发后端API,还能用来开发前端界面,简洁而且高效,推荐给大家! SpringBoot实战电商项目mall(50k+star)地址:https://github.com/macrozheng/mall Yao简介 Yao是一款

  • 弱类型语言javascript开发中的一些坑实例小结【变量、函数、数组、对象、作用域等】

    本文实例讲述了弱类型语言javascript开发中的一些坑.分享给大家供大家参考,具体如下: 测试1: (未声明变量自动提升为全局变量) test1(); function test1() { function setName() { name = '张三'; // 此处没有var声明,提升至全局 } setName(); console.log(name);// '张三' } 测试2: (函数内部局部变量的变量提升) test2(); function test2() { var a = 1;

  • SpringBoot开发实战系列之定时器

    前言 定时器功能在项目里面往往会用到,比如定时发送邮件.定时释放数据库资源:这里记录一下springboot对定时器的支持的简单实例 cron表达式 开始之前要先介绍一下cron表达式,这里当一下百度百科搬运工: Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: Seconds Minutes Hours DayofMonth Month DayofWeek Year或 Seconds Minutes Hours Dayof

  • Go语言提升开发效率的语法糖技巧分享

    目录 前言 可变长参数 声明不定长数组 init函数 忽略导包 忽略字段 json序列化忽略某个字段 json序列化忽略空值字段 短变量声明 类型断言 切片循环 判断map的key是否存在 select控制结构 前言 哈喽,大家好,我是asong. 每门语言都有自己的语法糖,像java​的语法糖就有方法变长参数.拆箱与装箱.枚举.for-each​等等,Go​语言也不例外,其也有自己的语法糖,掌握这些语法糖可以助我们提高开发的效率,所以本文就来介绍一些Go语言的语法糖,总结的可能不能全,欢迎评论

  • 用Python进行行为驱动开发的入门教程

    为驱动开发(Behavior-Driven Development,BDD)是一种卓越的开发模式.能帮助开发者养成日清日结的好习惯,从而避免甚至杜绝"最后一分钟"的情况出现,因此对提高代码质量是大有裨益的.其与Gherkin语法相结合的测试结构及设计形式,使得对团队的全部成员包括非技术人员都具有极好的易读性. 所有代码都必须进行测试,这意味着上线时把系统瑕疵降到最低甚至为零.这需要与完整的测试套件相配,从整体把控软件行为,使得检测与维护都能有序进行.这就是BDD的魅力所在,难道不心动吗

  • .Net语言Smobiler开发之如何在手机上实现表单设计

    最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 一.目标样式 我们要实现上图中的效果,需要如下的操作: 1.从工具栏上的"Smobiler Components"拖动一个一个TableView控件到窗体界面上 2.修改GridView控件的属性 a.load事件代码 VB: Private Sub TestTableView_Load(sender As Object, e As EventArgs)Handles My

  • .Net语言Smobiler开发利用Gridview控件设计较复杂的表单

    最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便. 一.目标样式 我们要实现上图中的效果,需要如下的操作: 1.从工具栏上的"Smobiler Components"拖动一个GridView控件到窗体界面上 2.修改GridView控件的属性 a.load事件代码  VB: Private Sub TestGridView3_Load(senderAs Object, e As EventArgs)Handles MyBa

随机推荐