• APC注入


    前言:

      "APC"是"Asynchronous Procedure Call"(异步过程调用)的缩写,它是一种软中断机制,当一个线程从等待状态中苏醒时(线程调用SignalObjectAndWait 、SleepEx、WaitForSingleObjectEx、WaitForMultipleObjectsEx、MsgWaitForMultipleObjectsEx函数时会进入可唤醒状态),它会检测有没有APC交付给自己。如果有,就会执行这些APC过程。APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC称为用户模式APC。APC调用的顺序为先入先出(FIFO)。我们可以使用 QueueUserAPC 函数把一个APC函数压入APC队列中

    函数介绍:  

    DWORD
    WINAPI
    QueueUserAPC(
        _In_ PAPCFUNC pfnAPC,        //APC函数的地址
        _In_ HANDLE hThread,            //线程句柄
        _In_ ULONG_PTR dwData        //APC函数的参数
        );
    
    //APC函数原型
    VOID NTAPI PAPCFUNC(
        _In_ ULONG_PTR Parameter
        );

    实现原理:

      将 QueueUserAPC 函数的第一个参数(函数地址)设置的是LoadLibraryA函数地址;第三个参数(传递参数)设置的是DLL路径,第二个参数要注入的进程的线程句柄,那么执行APC时便会调用LoadLibraryA函数加载指定路径的DLL,完成DLL注入操作。

      注意:一个进程可能包含多个线程,为了确保能够执行插入的APC,应向目标进程的所有线程都插入相同的APC,实现加载DLL的操作

    实现流程:

      (1).通过OpenProcess打开目标进程,获取目标进程的句柄

      (2).使用VirtualAllocEx在目标进程中申请空间

      (3).使用WriteProcessMemory函数在刚申请的空间中写入要注入的DLL路径

      (4).获取LoadLibraryA函数地址

      (5).通过CreateToolhelp32Snapshot、Thread32First以及Thread32Next遍历线程快照,获取目标进程的所有线程ID

      (6).遍历获取的线程ID,通过OpenThread函数以THREAD_ALL_ACCESS访问权限打开线程,获取线程句柄

      (7).使用QueueUserAPC向所有线程插入APC函数,参数1是(4)步获取的地址,参数2是(6)步获取的句柄,参数3是(2)步申请空间的首地址

    实现代码:

    //APC注入
    BOOL CInjectDlg::ApcInjectDll(char* pszProcessName, char* pszDllFileName)
    {
        DWORD* pThreadId = NULL;
        DWORD dwThreadIdLength = 0;
        //根据进程名获取PID
        DWORD dwProcessId = GetProcessIdByProcessName(pszProcessName);
        if (dwProcessId <= 0)
        {    
            return FALSE;
        }
        //根据PID获取所有相应的线程ID
        DWORD bRet = GetAllThreadIdByProcessId(dwProcessId, &pThreadId, &dwThreadIdLength);
        if (FALSE == bRet)
        {
            return FALSE;
        }
        //打开目标进程
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
        if (NULL == hProcess)
        {
            MessageBox(L"打开目标进程失败!");
            return FALSE;
        }
        // 在目标进程中申请空间
        LPVOID lpPathAddr = VirtualAllocEx(
            hProcess,                    // 目标进程句柄
            0,                            // 指定申请地址
            strlen(pszDllFileName) + 1,    // 申请空间大小
            MEM_RESERVE | MEM_COMMIT,    // 内存的状态
            PAGE_READWRITE);            // 内存属性
        if (NULL == lpPathAddr)
        {
            MessageBox(L"在目标进程中申请空间失败!");
            CloseHandle(hProcess);
            return FALSE;
        }
        // 3.在目标进程中写入Dll路径
        if (FALSE == WriteProcessMemory(
            hProcess,                    // 目标进程句柄
            lpPathAddr,                    // 目标进程地址
            pszDllFileName,                    // 写入的缓冲区
            strlen(pszDllFileName) + 1,    // 缓冲区大小
            NULL))                // 实际写入大小
        {
            MessageBox(L"目标进程中写入Dll路径失败!");
            CloseHandle(hProcess);
            return FALSE;
        }
        //5.获取LoadLibraryA的函数地址
        //FARPROC可以自适应32位与64位
        FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle((LPCWSTR)L"kernel32.dll"), "LoadLibraryA");
        if (NULL == pFuncProcAddr)
        {
            MessageBox(L"获取LoadLibrary函数地址失败!");
            CloseHandle(hProcess);
            return FALSE;
        }
        // 遍历线程, 插入APC
        for (int i = 0; i < dwThreadIdLength; i++)
        {
            // 打开线程
            HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
            if (hThread)
            {
                // 插入APC
                QueueUserAPC((PAPCFUNC)pFuncProcAddr, hThread, (ULONG_PTR)lpPathAddr);
                // 关闭线程句柄
                CloseHandle(hThread);
                hThread = NULL;
            }
        }
    
        return TRUE;
    }
    
    //根据进程名获取PID
    DWORD CInjectDlg::GetProcessIdByProcessName(char* pszProcessName)
    {
        //1.创建进程快照
        HANDLE hSnap = CreateToolhelp32Snapshot(
            TH32CS_SNAPPROCESS,            //遍历进程快照1
            0);                            //进程PID
        if (NULL == hSnap)
        {
            MessageBox(L"创建进程快照失败!");
            return 0;
        }
    
        //2.获取第一条进程快照信息
        PROCESSENTRY32  stcPe = { sizeof(stcPe) };
        if (Process32First(hSnap, &stcPe))
        {
    
            //3.循环遍历进程Next
            do {
    
                //获取快照信息
                USES_CONVERSION;
                CString ProcessName = A2T(pszProcessName);
                if (!lstrcmp(stcPe.szExeFile, ProcessName))
                {
                    //4.关闭句柄
                    CloseHandle(hSnap);
                    return stcPe.th32ProcessID;
                }
    
            } while (Process32Next(hSnap, &stcPe));
    
        }
        
        //4.关闭句柄
        CloseHandle(hSnap);
        return 0;
    }
    
    //根据PID获取所有相应的线程ID
    BOOL CInjectDlg::GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD** ppThreadId, DWORD* pdwThreadIdLength)
    {
        DWORD* pThreadId = NULL;
        //统计线程个数
        DWORD dwThreadIdLength = 0;
        //默认情况下,一个线程的栈要预留1M的内存空间, 
        //而一个进程中可用的内存空间只有2G,所以理论
        //上一个进程中最多可以开2048个线程
        DWORD dwBuffLength = 2048;
    
        //申请内存
        pThreadId = new DWORD[dwBuffLength];
        if (pThreadId == NULL)
        {
            MessageBox(L"申请内存失败!");
            return FALSE;
        }
        //将申请的控件初始化为0
        RtlZeroMemory(pThreadId, (dwBuffLength * sizeof(DWORD)));
        //1.创建线程快照
        HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
        if (NULL == hSnap)
        {
            MessageBox(L"创建线程快照失败!");
            delete[] pThreadId;
            pThreadId = NULL;
            return FALSE;
        }
    
        //2.第一次遍历线程
        THREADENTRY32 th32 = { sizeof(th32) };
        if (Thread32First(hSnap, &th32))
        {
        //3.循环遍历线程
            do
            {
                //判断该线程是否属于这个进程
                if (th32.th32OwnerProcessID == dwProcessId)
                {
                    pThreadId[dwThreadIdLength] = th32.th32ThreadID;
                    dwThreadIdLength++;
                }
    
            } while (Thread32Next(hSnap, &th32));
    
            CloseHandle(hSnap);
            *ppThreadId = pThreadId;
            *pdwThreadIdLength = dwThreadIdLength;
            return TRUE;
        }
        else
        {
            MessageBox(L"创建线程快照失败!");
            delete[] pThreadId;
            pThreadId = NULL;
            CloseHandle(hSnap);
            return FALSE;
        }
    
    }
  • 相关阅读:
    springMVC controller输出前台提示信息
    使用ionic3开始自己的App开发之路
    async await
    mongodb安装和启动
    express框架的安装和使用
    js继承的实现方式
    node版本管理
    git提交时冲突问题解决
    css推荐
    js工具推荐
  • 原文地址:https://www.cnblogs.com/ndyxb/p/12890800.html
Copyright © 2020-2023  润新知