APC注入(Ring3)

  首先简单介绍一下APC队列和Alertable.

  看看MSDN上的一段介绍(https://msdn.microsoft.com/en-us/library/ms810047.aspx):

  The system delivers most user-mode APCs when a thread unwinds from kernel mode back to user mode after an alertable wait. User-mode APCs do not interrupt user-mode code. After an application queues a user-mode APC to a thread, the application can cause the system to deliver the APCs by calling a wait function with the Alertable parameter set to TRUE. (The user-mode terminate APC is an exception. This APC is queued to terminate a thread, and the system delivers it whenever the thread unwinds back to user mode from kernel mode, not only after an alertable wait.)

  上述写道,正常情况下,用户模式的APC是不会打断用户态程序的执行流的。除非,线程是Alertable——可唤醒的。

  

  所以存在的一个麻烦就是,我想要注入的对方进程,哪个线程是Alertable的状态,所以采取了一种暴力的方式——向目标进程的所有线程的UserMode Apc队列(线程有两个Apc队列:Kernel和User)上插入Apc对象。

  代码流程:

  

  1.提权(EnableSeDebugPrivilege) 
  (1)获得令牌token,OpenThreadToken(),OpenProcessToken ()
  WINADVAPI
  BOOL
  WINAPI
  OpenThreadToken(
    _In_ HANDLE ThreadHandle,
    _In_ DWORD DesiredAccess,
    _In_ BOOL OpenAsSelf,
    _Outptr_ PHANDLE TokenHandle
    );

  OpenAsSelf参数
  [in]  true 指定应使用调用线程的进程安全上下文执行访问检查;
    false 指定应使用调用线程本身的安全上下文执行访问检查。 
  如果线程正在模拟客户端,则此安全上下文可以是一个客户端进程的安全上下文。

  WINADVAPI
  BOOL
  WINAPI
  OpenProcessToken(
    _In_ HANDLE ProcessHandle,
    _In_ DWORD DesiredAccess,
    _Outptr_ PHANDLE TokenHandle
  );

  (2)将提升的权限放到TokenPrivileges结构中

  WINADVAPI
  BOOL
  WINAPI
  AdjustTokenPrivileges(
    _In_ HANDLE TokenHandle,
    _In_ BOOL DisableAllPrivileges, 标志这个函数是否禁用该令牌的所有特权.如果为TRUE,这个函数禁用所有特权,NewState参数无效.
    如果为假,以NewState参数指针的信息为基础来修改特权.
    _In_opt_ PTOKEN_PRIVILEGES NewState,
    _In_ DWORD BufferLength,
    _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength) PTOKEN_PRIVILEGES PreviousState,
    _Out_opt_ PDWORD ReturnLength
    );

  (3)将获取的令牌TokenHandle与TokenPrivileges结构关联

2.通过进程ImageName获取进程ID(GetProcessIDByProcessImageName) 
  (1)给系统的所有进程快照ProcessSnapshotHandle
  函数原型:
  HANDLE WINAPI CreateToolhelp32Snapshot(
    DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等
    DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0
    );

指定快照中包含的系统内容,dwFlags这个参数能够使用下列数值(常量)中的一个或多个。
TH32CS_INHERIT - 声明快照句柄是可继承的。
TH32CS_SNAPALL - 在快照中包含系统中所有的进程和线程。
TH32CS_SNAPHEAPLIST - 在快照中包含在th32ProcessID中指定的进程的所有的堆。
TH32CS_SNAPMODULE - 在快照中包含在th32ProcessID中指定的进程的所有的模块。
TH32CS_SNAPPROCESS - 在快照中包含系统中所有的进程。
TH32CS_SNAPTHREAD - 在快照中包含系统中所有的线程。
#define TH32CS_SNAPHEAPLIST 0x00000001
#define TH32CS_SNAPPROCESS 0x00000002
#define TH32CS_SNAPTHREAD 0x00000004
#define TH32CS_SNAPMODULE 0x00000008
#define TH32CS_SNAPMODULE32 0x00000010
#define TH32CS_SNAPALL (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
#define TH32CS_INHERIT 0x80000000
(2)通过PROCESSENTRY32结构得到ProcessID
PROCESSENTRY32,用来存放快照进程信息的一个结构体。(存放进程信息和调用成员输出进程信息)
用来 Process32First指向第一个进程信息,并将进程信息抽取到PROCESSENTRY32中。
用Process32Next指向下一条进程信息。
typedef struct tagPROCESSENTRY32
{
  DWORD dwSize;
  DWORD cntUsage;
  DWORD th32ProcessID; // this process
  ULONG_PTR th32DefaultHeapID;
  DWORD th32ModuleID; // associated exe
  DWORD cntThreads;
  DWORD th32ParentProcessID; // this process‘s parent process
  LONG pcPriClassBase; // Base priority of process‘s threads
  DWORD dwFlags;
  CHAR szExeFile[MAX_PATH]; // Path
} PROCESSENTRY32;

process32First,Process32Next是两个个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,
我们可以利用process32First函数来获得第一个进程的句柄,利用Process32Next函数来获得下一个进程的句柄。
 
3.通过进程ID获取线程ID

4.注入
(1)在对方进程空间申请内存,

LPVOID
WINAPI
VirtualAllocEx(
  _In_ HANDLE hProcess,
  _In_opt_ LPVOID lpAddress,
  _In_ SIZE_T dwSize,
  _In_ DWORD flAllocationType,
  _In_ DWORD flProtect
);
flAllocationType参数设置:MEM_COMMIT | MEM_RESERVE
(如果物理内存申请失败,将保留进程的虚拟地址空间,到真正执行时,会触发异常,进行物理内存分配)
MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用

(2)将DllFullPath写入内存当中
WriteProcessMemory()函数能写入某一进程的内存区域。入口区必须可以访问,否则操作将失败。
BOOL
WINAPI
WriteProcessMemory(
  _In_ HANDLE hProcess,
  _In_ LPVOID lpBaseAddress,
  _In_reads_bytes_(nSize) LPCVOID lpBuffer,
  _In_ SIZE_T nSize,
  _Out_opt_ SIZE_T * lpNumberOfBytesWritten
);
(3)加载dll文件
LoadLibraryWAddress = (UINT_PTR)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");

// QueryUserAPC.cpp : 定义控制台应用程序的入口点。
//

#include "QueryUserAPC.h"

int main()
{
	if (EnableSeDebugPrivilege(L"SeDebugPrivilege", TRUE) == FALSE)
	{
		return 0;
	}
	SeQueueUserAPC(L"explorer.exe", L"Dll.dll");
    return 0;
}
BOOL EnableSeDebugPrivilege(IN const WCHAR*  PriviledgeName, BOOL IsEnable)
{
	HANDLE  ProcessHandle = GetCurrentProcess();
	HANDLE  TokenHandle = NULL;
	TOKEN_PRIVILEGES TokenPrivileges = { 0 };
	if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
	{
		return FALSE;
	}
	LUID			 v1;
	if (!LookupPrivilegeValue(NULL, PriviledgeName, &v1))
	{
		CloseHandle(TokenHandle);
		TokenHandle = NULL;
		return FALSE;
	}

	TokenPrivileges.PrivilegeCount = 1;
	TokenPrivileges.Privileges[0].Attributes = IsEnable == TRUE ? SE_PRIVILEGE_ENABLED : 0;
	TokenPrivileges.Privileges[0].Luid = v1;
	if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges,
		sizeof(TOKEN_PRIVILEGES), NULL, NULL))
	{
		printf("%d\r\n", GetLastError());
		CloseHandle(TokenHandle);
		TokenHandle = NULL;
		return FALSE;
	}
	CloseHandle(TokenHandle);
	TokenHandle = NULL;
	return TRUE;
}
BOOL SeQueueUserAPC(WCHAR* ProcessImageName, WCHAR* DllNameData)
{
	TCHAR DllFullPathData[MAX_PATH];
	GetFullPathName(DllNameData, MAX_PATH, DllFullPathData, NULL);

	DWORD ProcessID{};
	vector<DWORD> ThreadID{};

	if (!SeEnumProcessThreadIDByToolhelp32(ProcessImageName, ProcessID, ThreadID))
	{
		return FALSE;
	}

	auto ProcessHandle = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, ProcessID);
	if (!ProcessHandle)
	{
		printf("OpenProcess() Error\r\n");
		return FALSE;
	}

	auto RemoteBufferLength = 0x2000;
	auto RemoteBufferData = VirtualAllocEx(ProcessHandle,
		NULL,
		RemoteBufferLength,
		MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!WriteProcessMemory(ProcessHandle, RemoteBufferData, DllFullPathData, sizeof(DllFullPathData), NULL))
	{

		printf("WriteProcessMemory() Error\r\n");
		VirtualFreeEx(ProcessHandle, RemoteBufferData, RemoteBufferLength, MEM_RELEASE);
		CloseHandle(ProcessHandle);
		ProcessHandle = NULL;
		return FALSE;
	}

/*	for (const auto &v1 : ThreadID)    //Win7 崩溃
	{
		auto ThreadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, v1);
		if (ThreadHandle)
		{

			//向目标进程中的各个线程的APC队列插入执行体
			QueueUserAPC(
				(PAPCFUNC)GetProcAddress(
					GetModuleHandle(L"kernel32"),
					"LoadLibraryW"),
				ThreadHandle,
				(ULONG_PTR)RemoteBufferData);

			CloseHandle(ThreadHandle);
		}
	}*/

	int i = 0;
	for (int i=ThreadID.size()-1;i>=0;i--)   //Win10 Win7 通用
	{
		auto ThreadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, ThreadID[i]);
		if (ThreadHandle)
		{

			//向目标进程中的各个线程的APC队列插入执行体
			QueueUserAPC(
				(PAPCFUNC)GetProcAddress(
					GetModuleHandle(L"kernel32"),
					"LoadLibraryW"),
				ThreadHandle,
				(ULONG_PTR)RemoteBufferData);

			CloseHandle(ThreadHandle);
		}
	}
	CloseHandle(ProcessHandle);
	return TRUE;
}
BOOL SeEnumProcessThreadIDByToolhelp32(PCWSTR ProcessImageName, DWORD& ProcessID, vector<DWORD>& ThreadID)
{
	auto SnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
	if (SnapshotHandle == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	ProcessID = 0;

	PROCESSENTRY32 ProcessEntry32 = { sizeof(ProcessEntry32) };
	if (::Process32First(SnapshotHandle, &ProcessEntry32))
	{
		do {
			if (_wcsicmp(ProcessEntry32.szExeFile, ProcessImageName) == 0)
			{
				ProcessID = ProcessEntry32.th32ProcessID;
				THREADENTRY32 ThreadEntry32 = { sizeof(ThreadEntry32) };
				if (Thread32First(SnapshotHandle, &ThreadEntry32)) {
					do {
						if (ThreadEntry32.th32OwnerProcessID == ProcessID) {
							ThreadID.push_back(ThreadEntry32.th32ThreadID);
						}
					} while (Thread32Next(SnapshotHandle, &ThreadEntry32));
				}
				break;
			}
		} while (Process32Next(SnapshotHandle, &ProcessEntry32));
	}

	CloseHandle(SnapshotHandle);
	return ProcessID > 0 && !ThreadID.empty();
}

  

时间: 2024-10-10 07:17:25

APC注入(Ring3)的相关文章

QueryUserAPC Ring3下 APC注入

DLL.dll可以自己建,实测在win7 X86 X64, win10 X64下可用 #pragma once /****************************************************************************************************/ /*Ring3下 APC注入提权 TLHelp32枚举线程 vector*/ /***************************************************

[转载]Dll注入技术之APC注入

转自:黑客反病毒 APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下:     1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断.     2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数.     3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是L

注入理解之APC注入

近期学习做了一个各种注入的MFC程序,把一些心得和体会每天分享一些 APC(Asynchronous procedure call)异步程序调用,在NT中,有两种类型的APCs:用户模式和内核模式.用户APCs运行在用户模式下目标线程当前上下文中,并且需要从目标线程得到许可来运行.特别是,用户模式的APCs需要目标线程处在alertable等待状态才能被成功的调度执行.通过调用下面任意一个函数,都可以让线程进入这种状态.这些函数是:KeWaitForSingleObject, KeWaitFor

APC注入APCInject(DLL)

APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下:1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断(或者是Messagebox弹窗的时候不点OK的时候也能注入).2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数.3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果

Ring3 APC注入

系统产生一个软中断,当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数,利用QueueUserAPC()这个API,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的, 1.根据进程名称得进程ID2.枚举该进程中的线程3.将自己的函数插入到每个线程的APC队列中 1 // APCInject(Ring3).cpp : 定义控制台应用程序的入口点. 2 // 3 4 #include "stdafx.h" 5 #include <windows.h> 6

APC注入(Ring3层)

/* 步骤: 1.提权(GrantDebugPrivileges) (1)获得令牌token,OpenThreadToken(),OpenProcessToken () WINADVAPI BOOL WINAPI OpenThreadToken( _In_ HANDLE ThreadHandle, _In_ DWORD DesiredAccess, _In_ BOOL OpenAsSelf, _Outptr_ PHANDLE TokenHandle ); OpenAsSelf参数 [in]  t

Windows x86/ x64 Ring3层注入Dll总结

0x01.前言 提到Dll的注入,立马能够想到的方法就有很多,比如利用远程线程.Apc等等,这里我对Ring3层的Dll注入学习做一个总结吧. 我把注入的方法分成六类,分别是:1.创建新线程.2.设置线程上下背景文,修改寄存器.3.插入Apc队列.4.修改注册表.5.挂钩窗口消息.6.远程手动实现LoadLibrary. 那么下面就开始学习之旅吧! 0x02.预备工作 在涉及到注入的程序中,提升程序的权限自然是必不可少的,这里我提供了两个封装的函数,都可以用于提权.第一个是通过权限令牌来调整权限

Windows 注入

平常用的最多的dll注入技术就是远程线程,刚刚逛看雪,看到有人写的面试的时候被问到的问题,其中就有dll注入的方法,我突然想到我开始面试的时候也被问了dll注入的方法,当时也是就只知道一个远程线程,答的也不好,然后就想把一些注入技术写个总结.接下来讲的注入技术,有ring3层的lld的远程线程和apc注入,还有ring0的apc注入,此外还有更为隐蔽的代码注入. 先写最广泛的,也是相对简单的注入方式,远程线程. 一 ring3 dll的远程线程 我写的涉及到x86和x64的注入,因为x64的系统

注入DLL之主线程方式

目前对于APC注入方式依然还没有了解内幕, QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)param); 但看其调用方式可以猜出一二.,基本也是插入到线程再装载DLL. 以前在黑客防线里看到过主线程注入方式装载DLL,研究了一下,发现很像是手工实现了QueueUserApc的功能...不过这种比直接调用API更自由灵活. 另不知怎么回事,WIN7下与黑客防线所载文章实例中的细节有些出入. 首先黑客防线里的找主线程的方式有些绕,他