DLL注入技術之APC注入
阿新 • • 發佈:2019-02-19
APC注入的原理是利用當執行緒被喚醒時APC中的註冊函式會被執行的機制,並以此去執行我們的DLL載入程式碼,進而完成DLL注入的目的,其具體流程如下:
1)當EXE裡某個執行緒執行到SleepEx()或者WaitForSingleObjectEx()時,系統就會產生一個軟中斷。
2)當執行緒再次被喚醒時,此執行緒會首先執行APC佇列中的被註冊的函式。
3)利用QueueUserAPC()這個API可以在軟中斷時向執行緒的APC佇列插入一個函式指標,如果我們插入的是Loadlibrary()執行函式的話,就能達到注入DLL的目的。
1.編寫測試檔案
新建MFC工程,新增按鈕控制元件,雙擊寫程式碼如下所示:
2.編寫APC注入程式
由於我們需要時使用LoadLibrary()函式完成注入,因此需要為其先準備好必要的引數,需要我們可以通過在遠端程序中申請空間的方式寫入LoadLibrary()函式所需要的引數(也就是DLL的路徑)。關鍵程式碼如下所示:
關鍵程式碼如下所示:
經過以上兩步的操作,我們已經準備好APC注入的關鍵程式碼,現在我們需要將自己的程式提升許可權以方便注入操作(另,動態MFC庫編譯有可能造成注入失敗)。主要程式碼如下:
點選待注入的EXE進行SleepEx,這時EXE的視窗是不可以移動的,因為只有一個執行緒,處於SleepEx的掛起狀態,然後進行注入,我們此時會發現處於掛起狀態的程序視窗突然可以移動了,這是因為程序在掛起狀態等待時,如果有APC佇列就會退出等待並執行APC佇列中的函式,然後程式繼續執行。執行效果如下圖所示:
APC注入因為受目標程序使用API的條件而受限,並且處於等待的執行緒被注入後會立即返回,也有可能造成執行緒的執行錯誤,所以應用起來不是很通用。
1)當EXE裡某個執行緒執行到SleepEx()或者WaitForSingleObjectEx()時,系統就會產生一個軟中斷。
2)當執行緒再次被喚醒時,此執行緒會首先執行APC佇列中的被註冊的函式。
3)利用QueueUserAPC()這個API可以在軟中斷時向執行緒的APC佇列插入一個函式指標,如果我們插入的是Loadlibrary()執行函式的話,就能達到注入DLL的目的。
1.編寫測試檔案
新建MFC工程,新增按鈕控制元件,雙擊寫程式碼如下所示:
-
void CMfcTextApcInjectDlg::OnBnClickedSleepex()
-
{
-
// TODO: 在此新增控制元件通知處理程式程式碼
-
SleepEx(5000,TRUE);
- }
-
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.
2.編寫APC注入程式
由於我們需要時使用LoadLibrary()函式完成注入,因此需要為其先準備好必要的引數,需要我們可以通過在遠端程序中申請空間的方式寫入LoadLibrary()函式所需要的引數(也就是DLL的路徑)。關鍵程式碼如下所示:
-
//開啟遠端程序
-
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);
- }
關鍵程式碼如下所示:
-
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);
經過以上兩步的操作,我們已經準備好APC注入的關鍵程式碼,現在我們需要將自己的程式提升許可權以方便注入操作(另,動態MFC庫編譯有可能造成注入失敗)。主要程式碼如下:
-
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;
-
}
點選待注入的EXE進行SleepEx,這時EXE的視窗是不可以移動的,因為只有一個執行緒,處於SleepEx的掛起狀態,然後進行注入,我們此時會發現處於掛起狀態的程序視窗突然可以移動了,這是因為程序在掛起狀態等待時,如果有APC佇列就會退出等待並執行APC佇列中的函式,然後程式繼續執行。執行效果如下圖所示:
APC注入因為受目標程序使用API的條件而受限,並且處於等待的執行緒被注入後會立即返回,也有可能造成執行緒的執行錯誤,所以應用起來不是很通用。