在前面的文章《驅動開發:內核決議PE結構匯出表》
中我們封裝了兩個函式KernelMapFile()
函式可用來讀取內核檔案,GetAddressFromFunction()
函式可用來在匯出表中尋找指定函式的匯出地址,本章將以此為基礎實作對特定SSDT
函式的Hook
掛鉤操作,與《驅動開發:內核層InlineHook掛鉤函式》
所使用的掛鉤技識訓本一致,不同點是前者使用了CR3
的方式改寫記憶體,而今天所講的是通過MDL映射
實作,此外前者掛鉤中所取到的地址是通過GetProcessAddress()
取到的動態地址,而今天所使用的方式是通過讀取匯出表尋找,
掛鉤的目的就是要為特定函式增加功能,掛鉤的實作方式無非就是替換原函式地址,我們以內核函式ZwQueryDirectoryFile()
為例,ZwQueryDirectoryFile
例程回傳給定檔案句柄指定的目錄中檔案的各種資訊,其微軟定義如下;
NTSYSAPI NTSTATUS ZwQueryDirectoryFile(
[in] HANDLE FileHandle,
[in, optional] HANDLE Event,
[in, optional] PIO_APC_ROUTINE ApcRoutine,
[in, optional] PVOID ApcContext,
[out] PIO_STATUS_BLOCK IoStatusBlock,
[out] PVOID FileInformation,
[in] ULONG Length,
[in] FILE_INFORMATION_CLASS FileInformationClass,
[in] BOOLEAN ReturnSingleEntry,
[in, optional] PUNICODE_STRING FileName,
[in] BOOLEAN RestartScan
);
如果需要Hook
一個函式則你需要去微軟官方得到該函式的具體宣告部分包括其回傳值,而Hook
的目的只是為函式增加或處理新功能,則在執行完自定義函式后一定要跳回到原始函式上,此時定義一個typedef_ZwQueryDirectoryFile
函式指標在呼叫結束后即可很容易的跳轉回原函式上,保證流程被正確執行,如果需要Hook其他函式其撰寫模板也是如下所示;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
// 保存原函式地址
PVOID gOldFunctionAddress = NULL;
// Hook后被替換的新函式
NTSTATUS MyZwQueryDirectoryFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileMask OPTIONAL,
IN BOOLEAN RestartScan
)
{
NTSTATUS status = STATUS_SUCCESS;
// 定義函式指標
typedef NTSTATUS(*typedef_ZwQueryDirectoryFile)(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileMask OPTIONAL,
IN BOOLEAN RestartScan
);
DbgPrint("MyZwQueryDirectoryFile 自定義功能 \n");
// 執行原函式
status = ((typedef_ZwQueryDirectoryFile)gOldFunctionAddress)(FileHandle,
Event,
ApcRoutine,
ApcContext,
IoStatusBlock,
FileInformation,
Length,
FileInformationClass,
ReturnSingleEntry,
FileMask,
RestartScan);
return status;
}
接著就是如何掛鉤并讓其中轉到我們自己的代碼流程中的問題,由于掛鉤與恢復代碼是一樣的此處就以掛鉤為例,首先呼叫MmCreateMdl()
創建MDL,接著呼叫MmBuildMdlForNonPagedPool()
接收一個 MDL,該MDL指定非分頁虛擬記憶體緩沖區,并對其進行更新以描述基礎物理頁,呼叫MmMapLockedPages()
將此段記憶體提交為鎖定狀態,最后就是呼叫RtlCopyMemory()
將新函式地址寫出到記憶體中實作替換,最后釋放MDL句柄即可,這段代碼如下所示,看過驅動讀寫篇
的你一定很容易就能理解,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
// 掛鉤SSDT函式
BOOLEAN SSDTFunctionHook(ULONG64 FunctionAddress)
{
PMDL pMdl = NULL;
PVOID pNewAddress = NULL;
ULONG ulNewFuncAddr = 0;
gOldFunctionAddress = FunctionAddress;
// 使用MDL修改SSDT
pMdl = MmCreateMdl(NULL, &FunctionAddress, sizeof(ULONG));
if (NULL == pMdl)
{
return FALSE;
}
MmBuildMdlForNonPagedPool(pMdl);
// 鎖定記憶體
pNewAddress = MmMapLockedPages(pMdl, KernelMode);
if (NULL == pNewAddress)
{
IoFreeMdl(pMdl);
return FALSE;
}
// 寫入新函式地址
ulNewFuncAddr = (ULONG)MyZwQueryDirectoryFile;
RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG));
// 釋放
MmUnmapLockedPages(pNewAddress, pMdl);
IoFreeMdl(pMdl);
return TRUE;
}
Hook核心代碼如下所示,為了節約篇幅,如果您找不到程式中的核心功能,請看前面的幾篇文章,這里就不在贅述了,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
// 保存原函式地址
PVOID gOldFunctionAddress = NULL;
// Hook后被替換的新函式
NTSTATUS MyZwQueryDirectoryFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileMask OPTIONAL,
IN BOOLEAN RestartScan
)
{
NTSTATUS status = STATUS_SUCCESS;
// 定義函式指標
typedef NTSTATUS(*typedef_ZwQueryDirectoryFile)(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileMask OPTIONAL,
IN BOOLEAN RestartScan
);
DbgPrint("MyZwQueryDirectoryFile 自定義功能 \n");
// 執行原函式
status = ((typedef_ZwQueryDirectoryFile)gOldFunctionAddress)(FileHandle,
Event,
ApcRoutine,
ApcContext,
IoStatusBlock,
FileInformation,
Length,
FileInformationClass,
ReturnSingleEntry,
FileMask,
RestartScan);
return status;
}
// 掛鉤SSDT函式
BOOLEAN SSDTFunctionHook(ULONG64 FunctionAddress)
{
PMDL pMdl = NULL;
PVOID pNewAddress = NULL;
ULONG ulNewFuncAddr = 0;
gOldFunctionAddress = FunctionAddress;
// 使用MDL修改SSDT
pMdl = MmCreateMdl(NULL, &FunctionAddress, sizeof(ULONG));
if (NULL == pMdl)
{
return FALSE;
}
MmBuildMdlForNonPagedPool(pMdl);
// 鎖定記憶體
pNewAddress = MmMapLockedPages(pMdl, KernelMode);
if (NULL == pNewAddress)
{
IoFreeMdl(pMdl);
return FALSE;
}
// 寫入新函式地址
ulNewFuncAddr = (ULONG)MyZwQueryDirectoryFile;
RtlCopyMemory(pNewAddress, &ulNewFuncAddr, sizeof(ULONG));
// 釋放
MmUnmapLockedPages(pNewAddress, pMdl);
IoFreeMdl(pMdl);
return TRUE;
}
// 恢復SSDT函式
BOOLEAN SSDTFunctionUnHook(ULONG64 FunctionAddress)
{
PMDL pMdl = NULL;
PVOID pNewAddress = NULL;
ULONG ulOldFuncAddr = 0;
gOldFunctionAddress = FunctionAddress;
// 使用MDL修改SSDT
pMdl = MmCreateMdl(NULL, &FunctionAddress, sizeof(ULONG));
if (NULL == pMdl)
{
return FALSE;
}
MmBuildMdlForNonPagedPool(pMdl);
// 鎖定記憶體
pNewAddress = MmMapLockedPages(pMdl, KernelMode);
if (NULL == pNewAddress)
{
IoFreeMdl(pMdl);
return FALSE;
}
// 寫入新函式地址
ulOldFuncAddr = (ULONG)gOldFunctionAddress;
RtlCopyMemory(pNewAddress, &ulOldFuncAddr, sizeof(ULONG));
// 釋放
MmUnmapLockedPages(pNewAddress, pMdl);
IoFreeMdl(pMdl);
return TRUE;
}
// 關閉驅動
VOID UnDriver(PDRIVER_OBJECT driver)
{
SSDTFunctionUnHook(gOldFunctionAddress);
DbgPrint("驅動卸載 \n");
}
// 驅動入口
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
NTSTATUS status = STATUS_SUCCESS;
HANDLE hFile = NULL;
HANDLE hSection = NULL;
PVOID pBaseAddress = NULL;
UNICODE_STRING FileName = { 0 };
ULONG64 FunctionAddress = 0;
// 初始化字串
RtlInitUnicodeString(&FileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll");
// 記憶體映射檔案
status = KernelMapFile(FileName, &hFile, &hSection, &pBaseAddress);
if (NT_SUCCESS(status))
{
DbgPrint("讀取記憶體地址 = %p \n", pBaseAddress);
}
// 獲取指定模塊匯出函式地址
FunctionAddress = GetAddressFromFunction(FileName, "ZwQueryDirectoryFile");
DbgPrint("ZwQueryVirtualMemory記憶體地址 = %p \n", FunctionAddress);
// 開始Hook掛鉤
if (FunctionAddress != 0)
{
BOOLEAN ref = SSDTFunctionHook(FunctionAddress);
if (ref == TRUE)
{
DbgPrint("[+] Hook已掛鉤 \n");
}
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行這段驅動程式,則你會看到掛鉤成功的提示資訊;
文章出處:https://www.cnblogs.com/LyShark/p/17144310.html
本博客中的文章,轉載請注明出處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/554316.html
標籤:C
上一篇:C++面試八股文:如何在堆上和堆疊上分配一塊記憶體?
下一篇:返回列表