当前位置: 首页 > news >正文

2507C++,APC可以干的活

原文

窗口中的异步过程调用(APC)是可附加进线程的对象.每个线程都有自己的存储要调用的函数和参数APC队列.

想法是,希望由特定线程而不是某个任意线程执行a函数.这是因为线程所属的进程有时很重要,因此APC(执行时)可完全访问该进程资源.

技术上,有用户模式,内核模式特殊内核模式APC,这里,讨论用户模式APC,即窗口接口直接支持的用户模式APC.
还有特殊用户模式APC,但它一般不可用.下面是线程及其APC队列的概念表示:

线程==用户模式APC队列 APC APC APC APC
线程==内核模式APC队列 APC APC 线程及其`APC`队列

当在线程中排队用户模式APC时,APC只是坐在队列中闲着.要实际运行当前附加进线程的APC,该线程必须进入可警告等待(也叫可警告状态).

在该状态时,线程队列中的任何和所有APC将按顺序执行.但是线程如何进入可警告等待?

有一些函数可完成.最简单的是扩展的休息函数SleepEx,这里:

DWORD SleepEx(DWORD msec, BOOL alertable);

如果可警告,则该函数休息相同.否则,线程将休息指定的时间(可为零),如果在其队列中任何APC退出(或在休息时出现),则现在将执行并结束休息,此时,SleepEx返回值WAIT_IO_COMPLETION而不是零.

典型的调用可能是SleepEx(0,TRUE),以强制运行所有排队的APC(如果有).可按APC"垃集"对待此调用.如果线程从未进入可警告等待,则不会执行任何附加的APC.

进入可警告等待的其他方法,涉及使用各种等待函数的扩展版本,如WaitForSingleObjectEx,WaitForMultipleObjectsEx,它们都接受与SleepEx一样的额外的布尔参数.
这里
这里
MsgWaitForMultipleObjectsEx也可这样,只是使用(MWMO_ALERTABLE)标志,而不是指定可警告状态的极.
这里

异步I/O完成

用户模式APC"经典"用法是,取异步I/O操作的通知.对此,窗口有多种机制,其中一个涉及APC.即,ReadFileExWriteFileEx,API接收一个(与其非Ex变量相比)异步I/O操作完成时要调用的回调额外的参数.
这里
这里

问题是,回调包装在请求线程中排队的APC中,即只能在该线程进入可警告等待时执行它.下面是一些概念代码:

HANDLE hFile = ::CreateFile(..., FILE_FLAG_OVERLAPPED, nullptr);
OVERLAPPED ov{};
ov.Offset = ...;
::ReadFileEx(hFile, buffer, size, &ov, OnIoCompleted);//其他工作......//不时执行 APC:
::SleepEx(0, TRUE);

完成例程有以下原型:

void OnIoCompletion(DWORD dwErrorCode,DWORD dwNumberOfBytesTransfered,LPOVERLAPPED lpOverlapped);

实际上,这种通知异步/O完成的机制不是很流行,因为使用同一线程来完成一般不方便.事实上,线程可能会在I/O完成之前退出.尽管如此,它仍是一个利用APC的选项.

进程中注入DLL

有时"强制"另一个进程加载你提供的DLL很有用.完成的经典方法是,使用按LoadLibrary,API的地址设置"线程函数"的CreateRemoteThread,API,因为从二进制角度来看,线程的函数LoadLibrary有相同原型,因为两者都接受指针.
这里
这里

LoadLibrary传递要加载的DLL路径.视频
完整源码在此.

该方法的问题是它非常明显,在创建线程时会通知反恶意软件内核驱动,如果由不同进程中的线程创建,在驱动看来,是可疑的.

顺便,CreateRemoteThread"合法"用法是调试器通过强制进程中的新线程调用DbgBreakPoint,来强制中断到初始附加中的目标进程.

使用APC,也许可"说服"现有线程加载我们的DLL.这要隐蔽得多,因为它是现有线程加载DLL,这非常常见.为此,可用通用的QueueUserAPC,API,这里:

DWORD QueueUserAPC(PAPCFUNC  pfnAPC,HANDLE    hThread,ULONG_PTR dwData
);

幸好,APC函数有与线程函数相同的二进制布局,再次,接收某种指针.主要问题目标线程可能不会进入可警告的等待.

为了增加成功概率,可在目标进程中排队所有线程的APC,只需要一个线程即可进入可警告的等待.这对像资管此类有很多线程的进程效果很好,几乎总是可工作,视频.

自然队列

最后,因为在队列中保存APC,因此只需使用APC就可非常自然地创建一个"工作队列".如果需要顺序调用函数队列,可借助C++中的std::queue<>自行管理它们.

但是该队列不是线安的,因此你必须正确保护它.如果使用的是.NET,则可用ConcurrentQueue<>来帮助同步,但仍要构建某种循环弹出项目,调用它们等.
有了APC,一切都自然而简单:

void WorkQueue(HANDLE hQuitEvent) {while(::WaitForSingleObjectEx(hQuitEvent, INFINITE, TRUE) != WAIT_OBJECT_0);
}

简单自身.可用事件对象来退出此无限循环(从某处调用的SetEvent).线程等待APC出现在其队列中,并在出现时运行它们,返回等待.
这里
此队列的客户调用QueueUserAPC在该线程中入队工作项(回调).就是这样,简单而优雅.

http://www.dtcms.com/a/301739.html

相关文章:

  • Leetcode 3628. Maximum Number of Subsequences After One Inserting
  • mybatis-plus逻辑删除配置
  • 高可用集群KEEPALIVED实战解析
  • Gradio全解8——ChatInterfaceChatbot:聊天界面类与聊天机器人(2)——ChatInterface的自定义函数和界面
  • 芯片库和标准库寻找的方法
  • print(“\033[31m红\033[32m绿\033[34m蓝\033[0m默认色“)
  • 随机密码生成
  • Spring IOC 容器 **默认注册 Bean** 的 8 条规则
  • 网络服务综合项目
  • 数据结构基础内容(第七篇:堆、哈夫曼树)
  • SABR-Net
  • Linux 系统文件夹结构及用途说明
  • 《频率之光:共振之战》
  • 题解:CF1010C Border
  • Python异常处理:金融风控系统中的救命盾牌
  • Web开发系列-第13章 Vue3 + ElementPlus
  • 第十二讲:C++继承
  • 每日算法刷题Day55:7.27:leetcode 复习完第K小/大+栈4道题,用时1h50min
  • Datawhale 科大讯飞AI大赛(模型蒸馏)
  • 个人笔记HTML5
  • 聊聊回归测试的应对策略
  • selenium完整版一览
  • Spring Boot音乐服务器项目-删除音乐模块
  • Telerik 2025 Q2 Crack,Telerik Report Serve完整的解决方案
  • 腾讯云AI代码助手CodeBuddy开发指导
  • java小白闯关记第一天(两个数相加)
  • 第七章 状态管理
  • (LeetCode 每日一题) 2210. 统计数组中峰和谷的数量 (数组)
  • 通过阿里云服务器使用NPS实现外网访问本机服务
  • vulkan从小白到专家——YUV处理