APC注入

原文转自:http://blog.csdn.net/swanabin/article/details/21459243
APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下:
1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断。
2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。

1.编写测试文件
新建MFC工程,添加按钮控件,双击写代码如下所示:
<code>
void CMfcTextApcInjectDlg::OnBnClickedSleepex()
{
// TODO: 在此添加控件通知处理程序代码
SleepEx(5000,TRUE);
}
</code>
这里我们需要注意一下SleepEx中第二个参数为TRUE,查下msdn,上面写到:
<code>
bAlertable [in]
If this parameter is FALSE, the function does not return until the time-out period has elapsed. If an I/O completion callback occurs, the function does not return and the I/O completion function is not executed. If an APC is queued tothe thread, the function does not return and the APC function is not executed.
</code>
大概意思是说当第二个参数为FALSE,APC是不被执行的,从此可以认为APC注入的使用条件还是有很大约束的。
.编写APC注入程序
由于我们需要时使用LoadLibrary()函数完成注入,因此需要为其先准备好必要的参数,需要我们可以通过在远程进程中申请空间的方式写入LoadLibrary()函数所需要的参数(也就是DLL的路径)。关键代码如下所示:
<code>
//打开远程进程
handle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);
if (handle)
{
//在远程进程申请空间
lpData = VirtualAllocEx(handle,
NULL,
1024,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);

    if (lpData)  
    {  
        //在远程进程申请空间中写入待注入DLL的路径  
        bRet = WriteProcessMemory(handle,  
            lpData,  
            (LPVOID)sDllName,  
            1024,&dwRet);  
    }  
    //关闭句柄  
    CloseHandle(handle);  

}
</code>
当我们准备好用于注入DLL的LoadLibrary()函数后,接下来需要使用QueueUserAPC()函数将此函数插入到软中断线程的APC队列中。但是由于QueueUserAPC()函数的第三个参数是线程ID,因此我们需要根据现有进程ID,并通过遍历对比得到线程ID,具体API如下表所示:
<code>
CreateToolhelp32Snapshot 创建线程快照
Thread32First 得到第一个线程快照
Thread32Next 循环下一个线程快照
关键代码如下所示:
THREADENTRY32 te = {0};
te.dwSize = sizeof(THREADENTRY32);
//得到线程快照
HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
if (INVALID_HANDLE_VALUE == handleSnap)
{
return FALSE;
}

BOOL bStat = FALSE;  
//得到第一个线程  
if (Thread32First(handleSnap,&te))  
{  
    do   
    {  
        //进行进程ID对比  
        if (te.th32OwnerProcessID == dwProcessId)  
        {  
            //得到线程句柄  
            HANDLE handleThread = OpenThread(  
                THREAD_ALL_ACCESS,  
                FALSE,  
                te.th32ThreadID);  

            if (handleThread)  
            {  
                //向线程插入APC  
                dwRet = QueueUserAPC(  
                    (PAPCFUNC)LoadLibrary,  
                    handleThread,  
                    (ULONG_PTR)lpData);  
                if (dwRet > 0)  
                {  
                    bStat = TRUE;  
                }  
                //关闭句柄  
                CloseHandle(handleThread);  
            }  
        }  
        //循环下一个线程  
    } while (Thread32Next(handleSnap,&te));  
}  

CloseHandle(handleSnap);
</code>
3.MFC工程设置和提升权限
经过以上两步的操作,我们已经准备好APC注入的关键代码,现在我们需要将自己的程序提升权限以方便注入操作(另,动态MFC库编译有可能造成注入失败)。主要代码如下:
<code>
int CApcInjectDll::EnablePrivilege(bool isStart)
{
//1. 得到令牌句柄
HANDLE hToken = NULL; //令牌句柄
if (!::OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ,
&hToken))
{
return FALSE;
}

//2. 得到特权值  
LUID    luid = {0};         //特权值  
if (!::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))  
{  
    return FALSE;  
}  

//3. 提升令牌句柄权限  
TOKEN_PRIVILEGES tp = {0};  //令牌新权限  
tp.PrivilegeCount = 1;                                                        
tp.Privileges[0].Luid = luid;  
tp.Privileges[0].Attributes = isStart ? SE_PRIVILEGE_ENABLED : 0;  

if (!::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL))  
{  
    return FALSE;  
}  

//4. 关闭令牌句柄  
::CloseHandle(hToken);  
return 0;  

}
</code>
4.测试注入效果
点击待注入的EXE进行SleepEx,这时EXE的窗口是不可以移动的,因为只有一个线程,处于SleepEx的挂起状态,然后进行注入,我们此时会发现处于挂起状态的进程窗口突然可以移动了,这是因为进程在挂起状态等待时,如果有APC队列就会退出等待并执行APC队列中的函数,然后程序继续运行。
APC注入因为受目标进程使用API的条件而受限,并且处于等待的线程被注入后会立即返回,也有可能造成线程的运行错误,所以应用起来不是很通用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容