驱动开发:内核枚举IoTimer定时器

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

驱动开发:内核枚举IoTimer定时器

枚举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            :
   +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

驱动开发:内核枚举IoTimer定时器

这里的这个 +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

驱动开发:内核枚举IoTimer定时器

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

#include

// 得到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;
}

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

驱动开发:内核枚举IoTimer定时器

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

lyshark.com: kd> uf IoInitializeTimer

nt!IoInitializeTimer+0x5d:
fffff80574b85bed 488d5008        lea     rdx,[rax+8]
fffff80574b85bf1 48897018        mov     qword ptr [rax+18h],rsi
fffff80574b85bf5 4c8d054475e0ff  lea     r8,[nt!IopTimerLock (fffff8057498d140)]
fffff80574b85bfc 48897820        mov     qword ptr [rax+20h],rdi
fffff80574b85c00 488d0dd9ddcdff  lea     rcx,[nt!IopTimerQueueHead (fffff805748639e0)]
fffff80574b85c07 e8141e98ff      call    nt!ExInterlockedInsertTailList (fffff80574507a20)
fffff80574b85c0c 33c0            xor     eax,eax

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

驱动开发:内核枚举IoTimer定时器

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

  • StartSearchAddress 代表开始位置
  • EndSearchAddress 代表结束位置,粗略计算0xff就可以定位到了。
#include

// 得到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指令的内存地址。

驱动开发:内核枚举IoTimer定时器

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

  • PIO_TIMER Timer = CONTAINING_RECORD(NextEntry, IO_TIMER, TimerList) 得到结构体,循环输出即可。
// By: LyShark 内核开发系列教程
// https://www.cnblogs.com/LyShark/articles/16784393.html
#include
#include

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:
    fffff806349963cd 488d5008        lea     rdx,[rax+8]
    fffff806349963d1 48897018        mov     qword ptr [rax+18h],rsi
    fffff806349963d5 4c8d05648de0ff  lea     r8,[nt!IopTimerLock (fffff8063479f140)]
    fffff806349963dc 48897820        mov     qword ptr [rax+20h],rdi
    fffff806349963e0 488d0d99f6cdff  lea     rcx,[nt!IopTimerQueueHead (fffff80634675a80)]
    fffff806349963e7 e8c43598ff      call    nt!ExInterlockedInsertTailList (fffff806343199b0)
    fffff806349963ec 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);

      // fffff806349963e0 48 8d 0d 99 f6 cd ff  lea rcx,[nt!IopTimerQueueHead (fffff80634675a80)]
      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定时器所以输出结果是空的:

驱动开发:内核枚举IoTimer定时器

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

Original: https://www.cnblogs.com/LyShark/p/16790834.html
Author: lyshark
Title: 驱动开发:内核枚举IoTimer定时器



相关阅读

Title: python断点调试不起作用flask_pycharm和flask autoreload和断点不起作用

6 个答案:

答案 0 :(得分:19)

我将从简短的回答开始:不,你想要的任何版本的PyCharm都不能用到4.0.1。

问题在于,当您使用重新加载器时,Flask应用程序在子进程中运行,因此PyCharm调试器附加到主进程并且无法控制子进程。

在我看来,解决这个问题的最佳方法是让Jetbrains在他们的IDE中构建一个”重启更新”功能。然后你根本不需要使用Werkzeug的重新加载器,你可以直接从PyCharm获得相同的功能。

在Jetbrains决定实施此功能之前,我可以分享我的解决方法,这并不是非常糟糕。

在”编辑配置”中,将要使用的配置设置为”仅限单一实例”(对话框右上角的复选框)

确保配置是活动配置。

将Flask应用配置为不使用Werkzeug重新加载器。

按Ctrl-D开始调试(在Mac上,其他人可能有不同的快捷方式)

断点应该可以正常工作,因为重新加载程序处于非活动状态。

[En]

The breakpoint should work properly because the reloader is not active.

进行所需的任何代码更改。

[En]

Make any code changes you need.

准备好重新启动时,再次按Ctrl-D。第一次执行此操作时,您将收到确认提示,例如”停止并重新启动?”。说是,并选中”不再显示”复选框。

现在,您可以在需要时按Ctrl-D快速重启调试器。

我同意这不完美,但一旦Ctrl-D进入你的肌肉记忆,你甚至都不会想到它。

祝你好运!

答案 1 :(得分:1)

问题是因为使用use_reloader=True werkzeug应用程序是在主应用程序的单独(子)线程中启动而PyCharm无法正确处理断点,因为它们在线程启动时丢失。

我建议使用像pdb这样的Python-ish,即:

@app.route(‘/’)

def main(page):

import pdb; pdb.set_trace() # This line actually stops application execution

and starts Python debug shell in the console

where you can examine current scope and continue

normal code execution at any time.

You can inject any code here.

For example, if you type print page during pause,

it will output content of “page” variable.

return render_template(‘index.html’)

答案 2 :(得分:1)

我发现在PyCharm 2018.1.2中运行配置中有FLASK_DEBUG checbox:

驱动开发:内核枚举IoTimer定时器

进行一些更改后,保存​​文件会触发重新加载操作。

[En]

After making some changes, saving the ​​ file triggers a reload operation.

答案 3 :(得分:0)

在我的设置中,我正在通过运行main.py文件来调试flask应用程序,该文件设置了一些配置并调用app.run()。我的python解释器是在Docker容器中设置的。

我的问题是我需要检查Run with Python console。

驱动开发:内核枚举IoTimer定时器

答案 4 :(得分:0)

尝试在”编辑配置”中配置此python运行配置。之后,以调试模式运行。

驱动开发:内核枚举IoTimer定时器

答案 5 :(得分:-1)

来自pycharm 2017的

使用python 2.7(在我的情况下使用虚拟环境,但我认为没有必要)我这样做:

运行…

将脚本和脚本参数留空

我输入了解释器选项:-m flask run

设置env变量FLASK_APP

最后选择正在运行的进程,而不是运行附加到本地进程

[En]

Rather than run attaching to a local process, finally select the running process

我的用例是从邮递员连接到FASK REST服务端点,并在断点处中断

[En]

My use case is to connect from the postman to the flask rest service endpoint and interrupt at my breakpoint

Original: https://blog.csdn.net/weixin_29001419/article/details/113671227
Author: 知安局
Title: python断点调试不起作用flask_pycharm和flask autoreload和断点不起作用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/363523/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

最近整理资源【免费获取】:   👉 程序员最新必读书单  | 👏 互联网各方向面试题下载 | ✌️计算机核心资源汇总