APC注入

APC注入的原理:

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


局限:
  这种注入方式局限性很明显,一是必须要等待时机,而是当注入成功后,SleepEx或者其他等待函数直接就会跳过当前等待继续往下走,这样可能造成被注入程序的不稳定行,经常导致被注入程序崩溃。


测试:双击测试.exe,先会弹窗提示开始测试,然后进入SleepEx,再双击APC注入.exe,因为要注入的程序是双击测试.exe的主线程,且它此时正在执行到SleepEx(),直接注入完成,弹出注入成功的对话框。

// APC注入.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

// LoadExeWin32.cpp : 定义控制台应用程序的入口点。
//
#include <iostream>
#include <string>
#include <windows.h>
#include <shlwapi.h>
#include <tlhelp32.h>

#include <tchar.h>
#include "APC.h"
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib,"ntdll.lib")

using namespace std;

BOOL __EnableDebugPrivilege = TRUE;



//根据进程名字获取pid
DWORD GetPidFromName(TCHAR* ProcessName)
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    PROCESSENTRY32W pe = { sizeof(pe) };
    BOOL bOk;
    for (bOk = Process32FirstW(hSnapshot, &pe); bOk; bOk = Process32NextW(hSnapshot, &pe)) 
    {
        wstring  wsNowProcName = pe.szExeFile;
        if (_tcsicmp(pe.szExeFile, ProcessName) == 0)
        {
            CloseHandle(hSnapshot);
            return pe.th32ProcessID;
        }
    }
    CloseHandle(hSnapshot);
    return 0;
}

/*提权代码*/
DWORD SunEnableSunDebugPrivilege(HANDLE ProcessHandle, BOOL IsEnable)
{
    DWORD  LastError;
    HANDLE TokenHandle = 0;

    if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
    {
        LastError = GetLastError();
        if (TokenHandle)
            CloseHandle(TokenHandle);
        return LastError;
    }
    TOKEN_PRIVILEGES TokenPrivileges;
    memset(&TokenPrivileges, 0, sizeof(TOKEN_PRIVILEGES));
    LUID v1;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &v1))
    {
        LastError = GetLastError();
        CloseHandle(TokenHandle);
        return LastError;
    }
    TokenPrivileges.PrivilegeCount = 1;
    TokenPrivileges.Privileges[0].Luid = v1;
    if (IsEnable)
        TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        TokenPrivileges.Privileges[0].Attributes = 0;
    AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
    LastError = GetLastError();
    CloseHandle(TokenHandle);
    return LastError;
}

HANDLE SunOpenProcess(DWORD DesiredAccess, BOOL IsInheritHandle, HANDLE ProcessIdentify)
{

    //提权
    if (__EnableDebugPrivilege)        //全局变量__EnableDebugPrivilege
    {
        SunEnableSunDebugPrivilege(GetCurrentProcess(), TRUE);
    }

    //打开进程,根据目标进程的进程ID得到进程句柄
    HANDLE ProcessHandle = OpenProcess(DesiredAccess, IsInheritHandle, (DWORD)ProcessIdentify);

    DWORD LastError = GetLastError();

    //关闭权限
    if (__EnableDebugPrivilege)
    {
        SunEnableSunDebugPrivilege(GetCurrentProcess(), FALSE);
    }
    SetLastError(LastError);
    return ProcessHandle;
}


BOOL SunEnumThreadsInProcess(HANDLE ProcessIdentify,DWORD& MainThreadID)
{
    BOOL                        IsOk = FALSE;
    NTSTATUS                    Status = 0;
    PSYSTEM_PROCESS_INFORMATION        SystemProcessInfo = NULL;
    HMODULE ModuleBase = NULL;    //模块句柄

    //获取当前进程中的Ntdll模块的基地址
    ModuleBase = GetModuleHandle(_T("Ntdll.dll"));
    if (ModuleBase == NULL)
    {
        return FALSE;
    }
    //从Ntdll模块的导出表中获取函数地址
    __NtQuerySystemInformation = (LPFN_NTQUERYSYSTEMINFORMATION)GetProcAddress(ModuleBase,"NtQuerySystemInformation");
    if (__NtQuerySystemInformation == NULL)
    {
        return FALSE;
    }
    //申请内存
    PVOID    SystemInfoBuffer = NULL;
    SystemInfoBuffer = malloc(1024 * 1024);
    if (!SystemInfoBuffer)
    {
        return FALSE;
    }
    // 在QuerySystemInformation系列函数中,查询SystemProcessInformation时,必须提前申请好内存,不能先查询得到长度再重新调用
    Status = __NtQuerySystemInformation(SystemProcessInformation, SystemInfoBuffer, 1024 * 1024, NULL);


    if (!NT_SUCCESS(Status))
    {
        free(SystemInfoBuffer);
        return FALSE;
    }
    SystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)SystemInfoBuffer;
    // 遍历进程
    while (TRUE)
    {
        IsOk = FALSE;
        if (SystemProcessInfo->UniqueProcessId == (HANDLE)ProcessIdentify)
        {
            IsOk = TRUE;
            break;
        }
        else if (SystemProcessInfo->NextEntryOffset)
        {
            SystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)((PUINT8)SystemProcessInfo + SystemProcessInfo->NextEntryOffset);
        }
        else
        {
            break;
        }
    }


    if (IsOk)
    {
        //查询主线程
        for (INT i = 0; i < SystemProcessInfo->NumberOfThreads; i++)
        {
            // 执行的不能是当前线程
            MainThreadID = (DWORD)SystemProcessInfo->Threads[0].ClientId.UniqueThread;  //主线程ID
            break;
        }
    }
    if (SystemInfoBuffer != NULL)
    {
        free(SystemInfoBuffer);
    }

    return IsOk;
}

//把wcCacheInDllPath DLL文件注入进程wsProcessName
BOOL Injection_APC(TCHAR* wsProcessName, const TCHAR wcCacheInDllPath[])
{
    //初始化
    DWORD dwProcessId = GetPidFromName(wsProcessName);
    HANDLE hProcess = SunOpenProcess(PROCESS_ALL_ACCESS, FALSE, (HANDLE)dwProcessId);
    if (!hProcess) 
    {
        return FALSE;
    }
    PVOID  lpData = VirtualAllocEx(hProcess,
        NULL,
        1024,
        MEM_COMMIT,
        PAGE_EXECUTE_READWRITE);
    SIZE_T dwRet;
    if (lpData) 
    {
        //在远程进程申请空间中写入待注入DLL的路径  
        WriteProcessMemory(hProcess,
            lpData,
            (LPVOID)wcCacheInDllPath,
            MAX_PATH, &dwRet);
    }
    CloseHandle(hProcess);
    //开始注入
    DWORD MainThreadID = NULL;
    SunEnumThreadsInProcess((HANDLE)dwProcessId, MainThreadID);
    BOOL bStat = FALSE;

    //得到线程句柄  
    HANDLE handleThread = OpenThread(THREAD_ALL_ACCESS, FALSE, MainThreadID);
    if (handleThread)
    {  //向主线程插入APC  
        DWORD dwRet = QueueUserAPC(
        (PAPCFUNC)LoadLibraryW,
        handleThread,
        (ULONG_PTR)lpData);

        if (dwRet > 0)
        {
            bStat = TRUE;
        }
                    //关闭句柄  
        CloseHandle(handleThread);
    }
    return bStat;
}



//Adds a user-mode asynchronous procedure call (APC)
int main(int argc, char* argv[])
{
    //Sleep(1000*100);
    TCHAR* V1 = NULL;
    V1 = (TCHAR*)(_T("测试.exe"));
    Injection_APC(V1, _T("Dll.dll"));
    return 0;
}

 

二、DLL部分

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <tchar.h>
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:

        MessageBox(0, _T("APC注入实现啦"), _T("APC注入"),0 );
    case DLL_THREAD_ATTACH:
        //MessageBox(0, _T("APC注入实现啦"), _T("APC注入"),0 );
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

 

三、测试主程序

// 测试.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
#include <tchar.h>
using namespace std;

int main()
{
    MessageBox(0, _T("开始测试"), _T("测试"), MB_OK);
    SleepEx(1000 * 60 * 5, TRUE);
    MessageBox(0, _T("slee之后弹窗"), _T("测试"), MB_OK);

}

 

四、相关头文件和结构定义

#include <iostream>
#include <windows.h>
#include <tchar.h>
using namespace std;

#define STATUS_SUCCESS                  (NTSTATUS)0x00000000
#define NT_SUCCESS(x) ((x) >= 0)
#define STATUS_UNSUCCESSFUL              ((NTSTATUS)0xC0000001L)

typedef enum _SYSTEM_INFORMATION_CLASS {

    SystemProcessInformation = 5
} SYSTEM_INFORMATION_CLASS;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;

typedef struct _ANSI_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PSTR   Buffer;
} ANSI_STRING;
typedef ANSI_STRING *PANSI_STRING;


typedef struct _CLIENT_ID
{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;

typedef LONG KPRIORITY;

typedef struct _SYSTEM_THREAD_INFORMATION
{
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER CreateTime;
    ULONG WaitTime;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    KPRIORITY Priority;
    LONG BasePriority;
    ULONG ContextSwitches;
    ULONG ThreadState;
    ULONG WaitReason;
    ULONG PadPadAlignment;
} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;

typedef struct _SYSTEM_PROCESS_INFORMATION
{
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER WorkingSetPrivateSize;
    ULONG HardFaultCount;
    ULONG NumberOfThreadsHighWatermark;
    ULONGLONG CycleTime;
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    HANDLE InheritedFromUniqueProcessId;
    ULONG HandleCount;
    ULONG SessionId;
    ULONG_PTR UniqueProcessKey;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG PageFaultCount;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    SIZE_T QuotaPeakPagedPoolUsage;
    SIZE_T QuotaPagedPoolUsage;
    SIZE_T QuotaPeakNonPagedPoolUsage;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER ReadOperationCount;
    LARGE_INTEGER WriteOperationCount;
    LARGE_INTEGER OtherOperationCount;
    LARGE_INTEGER ReadTransferCount;
    LARGE_INTEGER WriteTransferCount;
    LARGE_INTEGER OtherTransferCount;
    SYSTEM_THREAD_INFORMATION Threads[1];
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; 

typedef NTSTATUS(NTAPI *LPFN_NTQUERYSYSTEMINFORMATION)(
    IN SYSTEM_INFORMATION_CLASS SystemInfoClass,
    OUT PVOID SystemInfoBuffer,
    IN ULONG SystemInfoBufferSize,
    OUT PULONG BytesReturned OPTIONAL);

LPFN_NTQUERYSYSTEMINFORMATION __NtQuerySystemInformation = NULL;

extern LPFN_NTQUERYSYSTEMINFORMATION __NtQuerySystemInformation;

 

posted @ 2022-04-05 21:57  人类观察者  阅读(214)  评论(0编辑  收藏  举报