1. 程式人生 > >DLL注入技術之APC注入

DLL注入技術之APC注入

 APC注入的原理是利用當執行緒被喚醒時APC中的註冊函式會被執行的機制,並以此去執行我們的DLL載入程式碼,進而完成DLL注入的目的,其具體流程如下:
    1)當EXE裡某個執行緒執行到SleepEx()或者WaitForSingleObjectEx()時,系統就會產生一個軟中斷。
    2)當執行緒再次被喚醒時,此執行緒會首先執行APC佇列中的被註冊的函式。
    3)利用QueueUserAPC()這個API可以在軟中斷時向執行緒的APC佇列插入一個函式指標,如果我們插入的是Loadlibrary()執行函式的話,就能達到注入DLL的目的。

1.編寫測試檔案
    新建MFC工程,新增按鈕控制元件,雙擊寫程式碼如下所示:
  1. void CMfcTextApcInjectDlg::OnBnClickedSleepex()
  2. {
  3.     // TODO: 在此新增控制元件通知處理程式程式碼
  4.     SleepEx(5000,TRUE);
  5. }
複製程式碼 這裡我們需要注意一下SleepEx中第二個引數為TRUE,查下msdn,上面寫到:
  1. bAlertable [in] 
  2. 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.
複製程式碼 大概意思是說當第二個引數為FALSE,APC是不被執行的,從此可以認為APC注入的使用條件還是有很大約束的。

2.編寫APC注入程式
    由於我們需要時使用LoadLibrary()函式完成注入,因此需要為其先準備好必要的引數,需要我們可以通過在遠端程序中申請空間的方式寫入LoadLibrary()函式所需要的引數(也就是DLL的路徑)。關鍵程式碼如下所示:
  1.     //開啟遠端程序
  2.     handle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);
  3.     if (handle)
  4.     {
  5.         //在遠端程序申請空間
  6.         lpData = VirtualAllocEx(handle,
  7.             NULL,
  8.             1024,
  9.             MEM_COMMIT,
  10.             PAGE_EXECUTE_READWRITE);
  11.         if (lpData)
  12.         {
  13.             //在遠端程序申請空間中寫入待注入DLL的路徑
  14.             bRet = WriteProcessMemory(handle,
  15.                 lpData,
  16.                 (LPVOID)sDllName,
  17.                 1024,&dwRet);
  18.         }
  19.         //關閉控制代碼
  20.         CloseHandle(handle);
  21. }
複製程式碼 當我們準備好用於注入DLL的LoadLibrary()函式後,接下來需要使用QueueUserAPC()函式將此函式插入到軟中斷執行緒的APC佇列中。但是由於QueueUserAPC()函式的第三個引數是執行緒ID,因此我們需要根據現有程序ID,並通過遍歷對比得到執行緒ID,具體API如下表所示:
CreateToolhelp32Snapshot   建立執行緒快照  
Thread32First   得到第一個執行緒快照  
Thread32Next   迴圈下一個執行緒快照  

    關鍵程式碼如下所示:
  1.     THREADENTRY32 te = {0};
  2.     te.dwSize = sizeof(THREADENTRY32);
  3.     //得到執行緒快照
  4.     HANDLE handleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
  5.     if (INVALID_HANDLE_VALUE == handleSnap)
  6.     {
  7.         return FALSE;
  8.     }
  9.     BOOL bStat = FALSE;
  10.     //得到第一個執行緒
  11.     if (Thread32First(handleSnap,&te))
  12.     {
  13.         do 
  14.         {
  15.             //進行程序ID對比
  16.             if (te.th32OwnerProcessID == dwProcessId)
  17.             {
  18.                 //得到執行緒控制代碼
  19.                 HANDLE handleThread = OpenThread(
  20.                     THREAD_ALL_ACCESS,
  21.                     FALSE,
  22.                     te.th32ThreadID);
  23.                 if (handleThread)
  24.                 {
  25.                     //向執行緒插入APC
  26.                     dwRet = QueueUserAPC(
  27.                         (PAPCFUNC)LoadLibrary,
  28.                         handleThread,
  29.                         (ULONG_PTR)lpData);
  30.                     if (dwRet > 0)
  31.                     {
  32.                         bStat = TRUE;
  33.                     }
  34.                     //關閉控制代碼
  35.                     CloseHandle(handleThread);
  36.                 }
  37.             }
  38.             //迴圈下一個執行緒
  39.         } while (Thread32Next(handleSnap,&te));
  40.     }
  41. CloseHandle(handleSnap);
複製程式碼 3.MFC工程設定和提升許可權
    經過以上兩步的操作,我們已經準備好APC注入的關鍵程式碼,現在我們需要將自己的程式提升許可權以方便注入操作(另,動態MFC庫編譯有可能造成注入失敗)。主要程式碼如下:
  1. int CApcInjectDll::EnablePrivilege(bool isStart)
  2. {        
  3.     //1. 得到令牌控制代碼
  4.     HANDLE  hToken = NULL;      //令牌控制代碼  
  5.     if (!::OpenProcessToken( GetCurrentProcess(), 
  6.         TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ, 
  7.         &hToken))
  8.     {   
  9.         return FALSE;
  10.     }
  11.     //2. 得到特權值
  12.     LUID    luid = {0};         //特權值
  13.     if (!::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
  14.     {
  15.         return FALSE;
  16.     }
  17.     //3. 提升令牌控制代碼許可權
  18.     TOKEN_PRIVILEGES tp = {0};  //令牌新許可權
  19.     tp.PrivilegeCount = 1;                                                      
  20.     tp.Privileges[0].Luid = luid;
  21.     tp.Privileges[0].Attributes = isStart ? SE_PRIVILEGE_ENABLED : 0;
  22.     if (!::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL))
  23.     {
  24.         return FALSE;
  25.     }
  26.     //4. 關閉令牌控制代碼
  27.     ::CloseHandle(hToken);
  28.     return 0;
  29. }
複製程式碼 4.測試注入效果
    點選待注入的EXE進行SleepEx,這時EXE的視窗是不可以移動的,因為只有一個執行緒,處於SleepEx的掛起狀態,然後進行注入,我們此時會發現處於掛起狀態的程序視窗突然可以移動了,這是因為程序在掛起狀態等待時,如果有APC佇列就會退出等待並執行APC佇列中的函式,然後程式繼續執行。執行效果如下圖所示:

     APC注入因為受目標程序使用API的條件而受限,並且處於等待的執行緒被注入後會立即返回,也有可能造成執行緒的執行錯誤,所以應用起來不是很通用。