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. }
  30.       
複製代碼
4.測試注入效果
    點擊待注入的EXE進行SleepEx,這時EXE的窗口是不可以移動的,因爲只有一個線程,處於SleepEx的掛起狀態,然後進行注入,我們此時會發現處於掛起狀態的進程窗口突然可以移動了,這是因爲進程在掛起狀態等待時,如果有APC隊列就會退出等待並執行APC隊列中的函數,然後程序繼續運行。執行效果如下圖所示
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章