一次开发的意外逆向之旅

在从事windows内核开发时,我最近因功能需求需要遍历pspsetcreateprocessnotifyroutine回调函数数组。在获取pspcreateprocessnotifyroutine的过程中,我发现了一些有趣的现象,决定在此与大家分享。由于这是我第一次撰写文章,难免有不足之处,还请各位读者多多包涵。

01

1

首先,让我们讨论一下获取这个数组的思路,以Windows 7 x64为例,其他版本大同小异。PspCreateProcessNotifyRoutine是一个PVOID类型的指针,它存储通过PsSetCreateProcessNotifyRoutine函数设置的各种回调函数。我们可以通过以下步骤获取这个数组:

使用MmGetSystemRoutineAddress获取PsCreateProcessNotifyRoutine的地址。在这个函数中,可以发现它调用了PspSetCreateProcessNotifyRoutine。

一次开发的意外逆向之旅2. 在PspSetCreateProcessNotifyRoutine函数中,偏移0x33处有一次对PspCreateProcessNotifyRoutine的操作。

一次开发的意外逆向之旅3. 跟入这个函数地址后,可以看到这是一个函数地址表。然而,通过uf这些地址时,发现这些地址是错误的。这是为什么呢?

一次开发的意外逆向之旅4. 实际上,微软对这些函数进行了加密。要获取真实的函数地址,需要先对其进行解密。解密方法非常简单,只需将这些地址取出并进行 &0xfffffffffffffff8 操作即可。

一次开发的意外逆向之旅5. 这样就可以获得真实的地址指针,通过访问这个指针,可以跟到真实的挂钩函数地址。按照这个思路编写的遍历代码是可行的,此处就不再展示。

一次开发的意外逆向之旅6. 我们可以通过跟入这个函数并对比Pchunter的结果来验证我们的方法是正确的。

一次开发的意外逆向之旅7. 在获取这张函数表后,通过对比模块的基地址和模块大小,可以确定函数所属的模块。同样,可以根据模块名在PsSetCreateProcessNotifyRoutine的函数地址传入有目的的地址和True来取消该模块的挂钩。也可以直接对函数头部进行Ret0操作,但由于可能出现问题,不推荐这样做。

02

2

那么我在Win7 x86版本上遇到了什么问题呢?同样使用MmGetSystemRoutineAddress获取PsCreateProcessNotifyRoutine的函数地址时,发现这个地址虽然是一个函数地址,但实际上是错误的。因为在Windbg中uf获取的PsCreateProcessNotifyRoutine与通过MmGetSystemRoutineAddress获取的地址居然不一样!起初我百思不得其解,但冷静下来后,我开始逐步分析这个过程,有趣的旅程就此展开!

首先,Windbg的u命令一定是准确的。微软自家的调试器在有符号表的情况下,如果连函数位置都不知道,那真是让人无语了!而且我确实进入过u到的函数,确认是正确的。因此,我猜想问题一定出在MmGetSystemRoutineAddress这个关键函数上。

那么MmGetSystemRoutineAddress是如何工作的呢?经过查阅现有资料,我解开了我的疑惑。MmGetSystemRoutineAddress内部实际上是解析模块文件的EAT(导出表)来获取函数调用者所需的函数。通过解析EAT和模块基地址的运算结合ImageLoad的对齐方式,返回对应的函数位置。因此我们的思路有了,因为是x86操作系统,在没有KPP保护的情况下,我的内核的EAT很可能被一些第三方软件挂钩,导致我获取的函数不正确。于是我在Windbg中使用.reload命令重新加载所有模块信息,然后使用lm命令查看所有模块的地址,通过对比各个模块的基地址和模块大小,大致确定了是哪个模块。PCHunter的内核挂钩也验证了我的猜想。

03

模块分析

既然确认内核是被EAT挂钩的,我们不妨分析一下这个模块。

通过数字签名,我们不难看出这个模块是一个安全软件的模块,到这里困惑就解开了。我们猜测是因为在x86系统上没有KPP保护,为了保护自身的钩子不被卸载或者为了监控其他软件的钩子,安全软件选择在EAT挂钩以保护自身的钩子不被其他软件取消,这是一个很好的思路。那么到底是不是这样呢,我们接着分析。

一次开发的意外逆向之旅2. 既然我们已经知道被替换函数的地址和被EAT挂钩的名称,那么我们接下来从这两点开始进行逆向。首先,我们先寻找字符串信息,根据模块名称。

一次开发的意外逆向之旅3. 我们先根据字符串找到对这个字符串引用的地址,很明显只有这一处。我们跟进去,结合上下文看到了一个关键函数ZwQuerySystemInformation。到这里,有过内核开发经验的小伙伴们肯定已经猜到这个函数是在获取模块基地址。看他对v4这个参数的引用,应该就是需要返回的模块地址。我们将这个函数命名为GetKeyModuleAddress,同时参数返回的就是模块大小。

影像之匠PixPretty 影像之匠PixPretty

商业级AI人像后期软件,专注于人像精修,色彩调节及批量图片编辑,支持Windows、Mac多平台使用。适用于写真、婚纱、旅拍、外景等批量修图场景。

影像之匠PixPretty 206 查看详情 影像之匠PixPretty

一次开发的意外逆向之旅一次开发的意外逆向之旅4. 接着我们对其x进行调用分析,可以看到有两处。我们跟入第一处,很幸运地找到了所需的函数。在这个函数中,我们可以看到大量关键函数的字符串,而我们的PsSetCreateProcessNotifyRoutine也在其中。这时我们的第二条主线就排上用场了。我们可以看到下图中使用PsSetCreateProcessNotifyRoutine这个字符串的函数也引用了sub_4A504这个函数,而这个函数正是我们内核被挂钩EAT后跳转过去的函数。因此我们猜测sub_49F84这个函数应该是GetProcAddressAndSetHook这两个功能,于是我们对其命名GetProcAddressAndSetHook,同时对sub_49F84这个函数命名为Hook_PsSetCreateProcessNotifyRoutine。

一次开发的意外逆向之旅一次开发的意外逆向之旅5. 接着我们对GetProcAddressAndSetHook(sub_4A504)的流程进行分析。首先进入该函数后,根据传入参数我们不难发现有一个函数名称和一个hook的函数地址。根据使用这两项的函数,我们猜测sub_49DDE这个函数的功能是获取函数地址。在他调用的函数sub_49D60中可以明显看到存在PE特征0x5A 0x4D以及0x45 0x50这几个字节码,所以这个函数肯定是根据函数名称获取函数地址无疑,我们将其命名为GetFunAddress(sub_49D60),其首参数为返回需求函数的地址。

一次开发的意外逆向之旅一次开发的意外逆向之旅6. 接着跟入sub_49B6E这个函数进行分析,我们可以看到明显的内存页操作,调用了IoAllocateMdl,MmProbeAndLockPages,MmMapLockedPagesSpecifyCache这几个关键函数。这很明显是申请MDL对内存页进行锁定,防止换页造成缺页异常等问题,这一般是hook的必要操作,所以我们对其命名为LockPage。

一次开发的意外逆向之旅7. 在完成页锁定之后,必然就是进行hook操作。这里采用了一个很好的方法,并没有使用memcpy或者其他内存填充写入的方法,而是采用了原子操作。这种一箭双雕的方法,首先使用_InterlockedExchange这个原子操作交换函数可以很方便地解决了同步问题,其次在_InterlockedExchange调用的时候返回值是上一次的状态,也很方便地保存了上一次的地址,以便于恢复。所以说是一种一箭双雕的方法,InterlockedExchange的第二个参数也使用到了我们的传入地址,以及刚才的LockPage。

一次开发的意外逆向之旅8. 在完成原子交换之后,GetProcAddressAndSetHook的第五个参数被使用。这里可以看到使用结束之后,之前的地址被保存下来,所以可以论证这里是用于恢复使用的。而且结合外面的函数传入值来看,这里是一个全局对象,而且这里这个值在hook的函数之中仍然需要去调用,所以也论证了这一点。

在之后紧接着调用了sub_4B340这个函数,在这个函数中就是一些基本的解除页面锁定的函数,我们将其命名为UnlockPage。

至此,Hook的全套流程就已经分析完毕了。接下来我们来看一看hook掉的代理函数做了一些什么。

04

代理函数分析

一次开发的意外逆向之旅1. 首先,第一个函数sub_4A3F2的操作非常奇怪,该函数作为替换函数应该是一个两参函数,但很不幸IDA分析失败了。最开始因为经验欠缺我没有明白这个函数的意义,但随着之后的分析我茅塞顿开。这个函数是通过栈寄存器来获取调用地址的,因为在栈上有函数的调用地址,所以在之后的LogAboutInformation中会有使用。

一次开发的意外逆向之旅2. 根据Hook_PsSetCreateProcessNotifyRoutine的第二个参数True或False来确定具体流程,无论是在取消设置还是在设置函数中都会调用sub_49CE0这个函数,这个函数的唯一作用就是调用之前保存下来给全局变量的原始的PsSetCreateProcessNotifyRoutine,我们将其命名为CallRightOldPsSetCreaetProcess。

一次开发的意外逆向之旅3. 接着会根据对于PsSetCreateProcessNotifyRoutine调用和失败会进入到LogAboutInformation(sub_4A2C8)这个函数中,跟入该函数结合传入参数分析该函数的唯一意义就是获取设置的函数地址模块名称以及调用者的模块名称,对其进行格式化之后结合特定标志位上传到r3上。

一次开发的意外逆向之旅4. 有意思的是在设置回调的代理函数Hook_PsSetCreateProcessNotifyRoutine中在设置行为下是存在拦截操作的,拦截操作的行为依据来源于LogAboutInformation的返回值并且返回0xC0000022,但是在LogAboutInformation的第三个参数为0的情况下LogAboutInformation直接返回0,所以也就是说在该版本下拦截其实并不生效。

一次开发的意外逆向之旅05

恢复与绕过思路

通用思路,首先同样获取系统内核模块的相关函数地址,模拟MmGetSystemRoutineAddtess的流程,但是我们这里需要解析文件并且解些EAT载入内存,如果采用读文件的方式的话需要注意内存对齐问题。

拿到真正的函数地址时,如果需要恢复的话可以直接学习刚才那一套mdl锁+原子交换的理念,这样就可以解决了。

但这里其实并不提倡这种方法,因为在一些软件中会对于代码有crc校验等功能,如果强行解除hook的话很有可能导致crc校验失败导致不可预料的结果,所以直接可以将获取到的函数进行指针强转直接调用即可。

以上就是一次开发的意外逆向之旅的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/594053.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
消息称明年小米 REDMI、一加、iQOO 等品牌将推出 7000mAh 新机
上一篇 2025年11月10日 17:19:12
如何恢复删除分区文件?详细数据恢复攻略
下一篇 2025年11月10日 17:19:17

相关推荐

  • 怎么在PHP代码中实现图片上传功能_PHP图片上传功能实现与安全处理教程

    首先创建含enctype的HTML表单,再用PHP接收文件,检查目录、移动临时文件,验证类型与大小,生成唯一文件名,并调整php.ini限制以确保上传成功。 如果您尝试在PHP项目中添加图片上传功能,但服务器无法正确接收或保存文件,则可能是由于表单配置、文件处理逻辑或安全限制的问题。以下是实现该功能…

    2026年5月10日
    100
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    000
  • 理解编程指令:当结果正确,但实现方式不符要求时

    本文探讨了在编程实践中,即使程序输出了正确的结果,但若其实现方式未能严格遵循既定指令,仍可能被视为“不正确”的问题。我们将通过具体示例,对比直接求和与累加求和两种实现策略,强调理解和遵守编程规范的重要性,以确保代码的健壮性、可维护性及符合项目要求。 在软件开发过程中,我们经常会遇到这样的情况:编写的…

    2026年5月10日
    000
  • Discord.py 交互按钮超时与持久化解决方案

    本教程旨在解决Discord.py中交互按钮在一段时间后出现“This Interaction Failed”错误的问题。我们将深入探讨视图(View)的超时机制,并提供通过正确设置timeout参数以及利用bot.add_view()方法实现按钮持久化的具体方案,确保您的机器人交互功能稳定可靠,即…

    2026年5月10日
    000
  • c++如何实现UDP通信_c++基于UDP的网络通信示例

    UDP通信基于套接字实现,适用于实时性要求高的场景。1. 流程包括创建套接字、绑定地址(接收方)、发送(sendto)与接收(recvfrom)数据、关闭套接字;2. 服务端监听指定端口,接收客户端消息并回传;3. 客户端发送消息至服务端并接收响应;4. 跨平台需处理Winsock初始化与库链接,编…

    2026年5月10日
    000
  • 谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    使用谷歌浏览器的开发者工具截图步骤:1. 按ctrl+shift+i(windows/linux)或cmd+option+i(mac)打开开发者工具。2. 点击右上角三个点,选择”更多工具”,再选择”截图”。3. 选择截取整个页面。推荐的谷歌浏览器扩展…

    2026年5月10日 用户投稿
    100
  • JS如何实现迭代器?迭代器协议

    JavaScript中实现迭代器需遵循可迭代协议和迭代器协议,通过定义[Symbol.iterator]方法返回具备next()方法的迭代器对象,从而支持for…of和展开运算符;该机制统一了数据结构的遍历接口,实现惰性求值,适用于自定义对象、树、图及无限序列等复杂场景,提升代码通用性与…

    2026年5月10日
    000
  • 动态更新圆形进度条:JavaScript成绩计算器集成指南

    本文档旨在指导开发者如何将JavaScript成绩计算系统与动态圆形进度条集成,实现可视化展示平均成绩。我们将详细讲解如何修改现有的JavaScript代码,使其在计算出平均分后,能够动态更新圆形进度条的进度,从而提供更直观的用户体验。本文档包含详细的代码示例和注意事项,帮助开发者轻松实现这一功能。…

    2026年5月10日
    000
  • Golang使用Protobuf定义接口与消息格式

    Protobuf通过字段编号实现兼容性,新增字段可忽略、删除字段可保留编号,确保新旧版本互操作,支持服务独立演进。 在Golang项目中,利用Protobuf定义接口和消息格式,本质上是为服务间通信构建了一套高效、类型安全且跨语言的契约。它让数据结构清晰可见,RPC调用标准化,极大地简化了分布式系统…

    2026年5月10日
    000
  • 使用 Ajax 和 FormData 实现文件上传及文本数据提交的完整教程

    本文旨在解决在使用 Ajax 和 FormData 进行文件上传时,遇到的 $_POST 和 $_FILES 为空的问题。通过详细的代码示例和解释,我们将展示如何正确地构建 FormData 对象,并通过 Ajax 将文件和文本数据发送到服务器端,同时避免常见的错误配置,确保数据能够成功地被 PHP…

    2026年5月10日
    000
  • pycharm解析器怎么添加 解析器添加详细流程

    在pycharm中添加解析器的步骤包括:1) 打开pycharm并进入设置,2) 选择project interpreter,3) 点击齿轮图标并选择add,4) 选择解析器类型并配置路径,5) 点击ok完成添加。添加解析器后,选择合适的类型和版本,配置环境变量,并利用解析器的功能提高开发效率。 在…

    2026年5月10日
    000
  • 深入理解MQTT多级通配符#的用法限制与Paho-MQTT订阅实践

    本文旨在解析mqtt多级通配符`#`在订阅主题时的严格使用规则,尤其是在paho-mqtt库中遇到的`valueerror: ‘invalid subscription filter.’`问题。我们将详细阐述mqtt规范中关于`#`必须作为主题过滤器最后一个字符的规定,并通过…

    2026年5月10日
    000
  • 解决Persistent UTM代码导致链接意外添加问号的问题

    本文旨在解决在使用JavaScript持久化UTM参数时,链接在没有UTM参数的情况下被意外添加问号的问题。通过分析问题代码,找出错误原因,并提供修正后的代码示例,确保只有当存在UTM参数时,链接才会被添加相应的参数。同时,强调了代码的健壮性和可维护性,避免不必要的修改和潜在的错误。 在使用Java…

    2026年5月10日
    200
  • HTML文档如何工作?如何编辑HTML格式文件?

    HTML文档如何工作?如何编辑HTML格式文件?HTML文档如何工作?如何编辑HTML格式文件?HTML文档如何工作?如何编辑HTML格式文件?HTML文档如何工作?如何编辑HTML格式文件?

    浏览器解析和渲染html的过程包括:1. 解析html构建dom树;2. 结合css构建渲染树;3. 布局计算元素位置;4. 绘制像素到屏幕。编辑html可使用记事本、vs code、sublime text等文本或代码编辑器,其中vs code因语法高亮、自动补全和插件生态成为主流选择。标准htm…

    2026年5月10日 用户投稿
    000
  • JavaScript 中使用多个 querySelector 更新页面元素

    本文旨在讲解如何在 JavaScript 的 if 语句中使用多个 querySelector 来更新不同的页面元素,并提供示例代码和注意事项,帮助开发者理解并应用此技术。通过该方法,可以根据特定条件动态修改页面内容,提升用户体验。 使用 querySelector 在 if 语句中更新多个元素 在…

    2026年5月10日
    100
  • 硬盘数据被误删除怎么办?教你快速找回删除的文件!

    硬盘数据被误删除,别慌!恢复数据并非不可能,关键在于你接下来的操作。立刻停止对该硬盘的任何写入操作,然后尝试使用专业的数据恢复软件。 解决方案 首先,数据恢复的原理是,删除文件后,操作系统只是将文件占用的空间标记为“可覆盖”,但文件本身的数据可能还存在于硬盘上。所以,避免新的数据写入覆盖掉旧数据,是…

    2026年5月10日
    000
  • CodeIgniter在IIS环境下实现URL重写与index.php移除指南

    本教程详细指导如何在IIS服务器上部署的CodeIgniter应用中,移除URL中不必要的index.php。核心解决方案涉及修改CodeIgniter的config.php文件,将$config[‘index_page’]设置为空,并辅以正确的IIS web.config重…

    2026年5月10日
    100
  • HTML文档的基本结构是什么? 3分钟带你了解HTML文档基础框架

    html文档的基础结构由四部分组成:1. 声明,用于告知浏览器以html5标准模式解析页面,避免怪异模式导致的兼容性问题;2. 根元素,包裹整个文档内容,并可通过lang属性指定语言;3. 头部区域,包含元数据如设置字符编码、实现响应式布局、定义页面标题、引入css和favicon、加载脚本等;4.…

    2026年5月10日
    000
  • Android和iOS系统下,HTML+JS代码运行结果差异:为什么input宽度为0时,Android输入方向异常?

    Android和iOS系统HTML+JS代码运行差异分析:input宽度为0引发的Android输入方向异常 开发OTP输入组件时,我们发现一个有趣的现象:当input元素的宽度设置为0 (style=”width: 0;”)时,Android系统下的输入方向会异常,而iOS系统则正常工作。 移除w…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信