龙马谷

 找回密码
 立即注册

QQ登录

只需一步,快速开始

龙马谷VIP会员办理客服QQ:82926983(如果临时会话没有收到回复,请先加QQ好友再发。)
1 [已完结] GG修改器新手入门与实战教程 31课 2 [已完结] GG修改器美化修改教程 6课 3 [已完结] GG修改器Lua脚本新手入门教程 12课
4 [已完结] 触动精灵脚本新手入门必学教程 22课 5 [已完结] 手游自动化脚本入门实战教程 9课 6 [已完结] C++射击游戏方框骨骼透视与自瞄教程 27课
7 [已完结] C++零基础UE4逆向开发FPS透视自瞄教程 29课 8 [已完结] C++零基础大漠模拟器手游自动化辅助教程 22课
以下是天马阁VIP教程,本站与天马阁合作,赞助VIP可以获得天马阁对应VIP会员,名额有限! 点击进入天马阁论坛
1 [已完结] x64CE与x64dbg入门基础教程 7课 2 [已完结] x64汇编语言基础教程 16课 3 [已完结] x64辅助入门基础教程 9课
4 [已完结] C++x64内存辅助实战技术教程 149课 5 [已完结] C++x64内存检测与过检测技术教程 10课 6 [已完结] C+x64二叉树分析遍历与LUA自动登陆教程 19课
7 [已完结] C++BT功能原理与x64实战教程 29课 8 [已完结] C+FPS框透视与自瞄x64实现原理及防护思路
查看: 2421|回复: 0

Windows内核驱动创建线程PsCreateSystemThread

[复制链接]

11

主题

5

回帖

19

积分

编程入门

Rank: 1

龙马币
32

有时候需要使用线程来完成一个或者一组任务。这些任务可能耗时过长,而开发者又不想让当前系统停止下来等待。在驱动中停止等待很容易使整个系统陷入“停顿”,最后可能只能重启电脑。但一个单独的线程长期等待,还不至于对系统造成致命的影响。另一些任务是希望长期、不断的执行,比如不断写入日志。为此启动一个特殊的线程来执行它们是最好的方法。

在驱动中生成的线程一般是系统线程。系统线程所在的进程名为“System”。用到的内核API函数原型如下:

  1. NTSTATUS PsCreateSystemThread(
  2.     OUT PHANDLE  ThreadHandle,
  3.     IN ULONG  DesiredAccess,
  4.     IN POBJECT_ATTRIBUTES  ObjectAttributes  OPTIONAL,
  5.     IN HANDLE  ProcessHandle  OPTIONAL,
  6.     OUT PCLIENT_ID  ClientId  OPTIONAL,
  7.     IN PKSTART_ROUTINE  StartRoutine,
  8.     IN PVOID  StartContext
  9. );
复制代码


这个函数的参数也很多。不过作者本人的使用经验如下:ThreadHandle用来返回句柄。放入一个句柄指针即可。DesiredAccess总是填写0。后面三个参数都填写NULL。最后的两个参数一个用于改线程启动的时候执行的函数。一个用于传入该函数的参数。

下面要关心的就是那个启动函数的原型。这个原型比起定时器回调函数倒是异常的简单,没有任何多余的东西:

  1. VOID CustomThreadProc(IN PVOID context)
复制代码


可以传入一个参数,就是那个context。context就是PsCreateSystemThread中的StartContext。值得注意的是,线程的结束应该在线程中自己调用PsTerminateSystemThread来完成。此外得到的句柄也必须要用ZwClose来关闭。但是请注意:关闭句柄并不结束线程。

下面举一个例子。这个例子传递一个字符串指针到一个线程中打印一下。然后结束该线程。当然打印字符串这种事情没有必要单独开一个线程来做。这里只是一个简单的示例。请注意,这个代码中有一个隐藏的错误,请读者指出这个错误是什么:

  1. // 我的线程函数。传入一个参数,这个参数是一个字符串。
  2. VOID MyThreadProc(PVOID context)
  3. {
  4.     PUNICODE_STRING str = (PUNICODE_STRING)context;
  5.     // 打印字符串
  6.     KdPrint((“PrintInMyThread:%wZ\r\n”,str));
  7.     // 结束自己。
  8.     PsTerminateSystemThread(STATUS_SUCCESS);
  9. }
  10. VOID MyFunction()
  11. {
  12.     UNICODE_STRING str = RTL_CONSTANT_STRING(L“Hello!”);
  13.     HANDLE thread = NULL;
  14.     NTSTATUS status;
  15.     status = PsCreateSystemThread(
  16.         &thread,0L,NULL,NULL,NULL,MyThreadProc,(PVOID)&str);
  17.     if(!NT_SUCCESS(status))
  18.     {
  19.         // 错误处理。
  20.         …
  21.     }
  22.     // 如果成功了,可以继续做自己的事。之后得到的句柄要关闭
  23.     ZwClose(thread);
  24. }
复制代码


以上错误之处在于:MyThreadProc执行的时候,MyFunction可能已经执行完毕了。
执行完毕之后,堆栈中的str已经无效。此时再执行KdPrint去打印str一定会蓝屏。这也是一个非常隐蔽,但是非常容易犯下的错误。

合理的方法是是在堆中分配str的空间。或者str必须在全局空间中。请读者自己写出正确的方法。

但是读者会发现,以上的写法在正确的代码中也是常见的。原因是这样做的时候,在PsCreateSystemThread结束之后,开发者会在后面加上一个等待线程结束的语句。

这样就没有任何问题了,因为在这个线程结束之前,这个函数都不会执行完毕,所以栈内存空间不会失效。

这样做的目的一般不是为了让任务并发。而是为了利用线程上下文环境而做的特殊处理。比如防止重入等等。在后面的章节读者会学到这方面的技巧。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

龙马谷| C/C++辅助教程| 安卓逆向安全| 论坛导航| 免责申明|Archiver|
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表龙马谷立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2018-2021 All Right Reserved.
在线客服
快速回复 返回顶部 返回列表