1. 程式人生 > WINDOWS開發 >windows:驅動模組隱藏

windows:驅動模組隱藏

  windwos下想要搞點事,許可權當然是越大越好;驅動模組天生在0環,和操作提供平級,大家互相是兄弟,所以很多外掛、木馬、病毒都會使用驅動達到自己的目的。那麼問題來了:PCHUNTER這種工具能查到系統裡面所有驅動模組,外掛\木馬\病毒該怎麼隱藏了?

技術分享圖片

  常見隱藏驅動的方式:

  •  驅動模組斷鏈
  • 呼叫MiProcessLoadEntry刪除驅動物件(據說不會觸發PG)
  • 清理MmUnloadDriver List 和 PiDDBCacheTable兩處
  • driveEntry返回失敗
  • 驅動模組載入後立即解除安裝

  今天介紹一種driveEntry返回失敗隱藏驅動的方法 —— DriverEntry返回失敗;

  windows會根據DriverEntry的返回值判斷驅動是否載入成功。如果返回成功,會在登錄檔詳細記錄,並將sys檔案複製到System32/drivers目錄下;如果失敗,會回收執行程式碼時分配的棧空間,但此時程式碼已經執行,該方法隱藏驅動最核心的點就在這了:

  •  driverEntry執行的時候作業系統分配的棧記憶體,執行完畢會被回收,資料和程式碼都不會留下,只能額外在堆空間分配一塊記憶體,把需要繼續執行的程式碼和資料(本實驗是回撥函式)都留在堆上
  • 本例中,回撥函式需要列印。但call函式後面的運算元都是相對偏移,不是絕對地址。回撥函式被複制到堆上,自身位置改變,列印函式的偏移肯定也變了,這裡需要重定位,怎麼辦?

    先自定義一個函式指標,指向列印函式,但地址隨便用個Magic Number糊弄; 等回撥函式的程式碼拷貝到堆上後,再呼叫MmGetSystemRoutineAddress得到列印函式的地址,再寫回函式指標;

  完整程式碼如下(其實不多,也就100行左右):

#include <fltKernel.h>

typedef ULONG (__cdecl * DbgPrintType)(
    _In_z_ _Printf_format_string_ PCSTR Format,...
);

#define DBG_PTR_TAG_MAGICNO 0xbabababababababa

void
MyLoadImageNotifyRoutine( PUNICODE_STRING FullImageName,HANDLE ProcessId,PIMAGE_INFO ImageInfo ) { ProcessId = ProcessId; DbgPrintType MyDbgPrint = (DbgPrintType)DBG_PTR_TAG_MAGICNO; if (FullImageName != NULL && ImageInfo != NULL) { if ((ULONG_PTR)ImageInfo->ImageBase > (ULONG_PTR)0xf000000000000000) { MyDbgPrint("MyLoadImageNotifyRoutine: loading a kernel module: %wZ.\r\n",FullImageName); } } } void DriverUnload(PDRIVER_OBJECT DriverObject) { DriverObject = DriverObject; KdPrint(("Hello,unloaded.\r\n")); PsRemoveLoadImageNotifyRoutine(MyLoadImageNotifyRoutine); } #define FUNC_LEN 0x100 NTSTATUS DriverEntry( _In_ PDRIVER_OBJECT DriverObject,_In_ PUNICODE_STRING RegistryPath ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING dbgprint_str = RTL_CONSTANT_STRING(L"DbgPrint"); //分配堆空間,後續把自己的回撥函式(這部分程式碼還要執行) PVOID my_func_body = ExAllocatePoolWithTag(NonPagedPool,FUNC_LEN,Disp); DbgPrintType dbgprint_ptr = NULL; PUCHAR func_body_ptr = NULL; int i; // 防止警告。 DriverObject = DriverObject; RegistryPath = RegistryPath; DbgBreakPoint(); do { if (my_func_body == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; } // 動態獲取DbgPrint函式的地址。 dbgprint_ptr = (DbgPrintType)MmGetSystemRoutineAddress(&dbgprint_str); if (dbgprint_ptr == NULL) { status = STATUS_UNSUCCESSFUL; break; } // 拷貝函式體。 memcpy(my_func_body,(PVOID)MyLoadImageNotifyRoutine,FUNC_LEN); // 替換函式體中的立即數0xbabababababababa,使之變成DbgPrint函式的地址 for (i = 0; i < FUNC_LEN; ++i) { func_body_ptr = (PUCHAR)my_func_body + i; if (*(ULONG_PTR*)func_body_ptr == (ULONG_PTR)DBG_PTR_TAG_MAGICNO) { *(ULONG_PTR*)func_body_ptr = (ULONG_PTR)dbgprint_ptr; break; } } if (i == FUNC_LEN) { status = STATUS_UNSUCCESSFUL; break; } // 將分配的堆函式註冊成回撥函式。 status = PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)my_func_body); } while (0); if (status != STATUS_SUCCESS && my_func_body != NULL) { ExFreePool(my_func_body); } // 返回失敗,確保驅動"不載入"。 status = STATUS_UNSUCCESSFUL; return status;

  效果如下:

  (1)反正在PCHUNTER的驅動模組頁面是找不到了

  (2)載入驅動時看到的提示,給人感覺好像失敗了:

  技術分享圖片

  但其實驅動的程式碼已經運行了,這裡能正常檢測和列印被載入的模組:

  技術分享圖片

  (3)核心模組還是能看到,但沒有路徑,不好找模組在哪,給分析增加難度:

  技術分享圖片

  

  最後總結一下: 目前流行的隱藏驅動方式,都逃不多微軟VBS的法眼;一旦開啟VT,處於-1環,相當於擁有了上帝視角,通過EPT把作業系統核心的所有操作都能監控到,所以現在的對抗進一步演變成了對系統更底層許可權的爭奪;誰先獲得了更底層的許可權,誰就能監控上一層的一舉一動。

  技術分享圖片