在筆者之前的文章《驅動開發:內核特征碼搜索函式封裝》中我們封裝實作了特征碼定位功能,本章將繼續使用該功能,本次我們需要列舉內核LoadImage映像回呼,在Win64環境下我們可以設定一個LoadImage映像加載通告回呼,當有新驅動或者DLL被加載時,回呼函式就會被呼叫從而執行我們自己的回呼例程,映像回呼也存盤在陣列里,列舉時從陣列中讀取值之后,需要進行位運算解密得到地址,
我們來看一款閉源ARK工具是如何實作的:

如上所述,如果我們需要拿到回呼陣列那么首先要得到該陣列,陣列的符號名是PspLoadImageNotifyRoutine我們可以在PsSetLoadImageNotifyRoutineEx中找到,
第一步使用WinDBG輸入uf PsSetLoadImageNotifyRoutineEx首先定位到,能夠找到PsSetLoadImageNotifyRoutineEx這里的兩個位置都可以被參考,當然了這個函式可以直接通過PsSetLoadImageNotifyRoutineEx函式動態拿到此處不需要我們動態定位,

我們通過獲取到PsSetLoadImageNotifyRoutineEx函式的記憶體首地址,然后向下匹配特征碼搜索找到488d0d88e8dbff并取出PspLoadImageNotifyRoutine記憶體地址,該記憶體地址就是LoadImage映像模塊的基址,

如果使用代碼去定位這段空間,則你可以這樣寫,這樣即可得到具體特征地址,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntddk.h>
#include <windef.h>
// 指定記憶體區域的特征碼掃描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;
// 掃描記憶體
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判斷特征碼
for (m = 0; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判斷是否找到符合特征碼的地址
if (m >= ulMemoryDataSize)
{
// 找到特征碼位置, 獲取緊接著特征碼的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}
return pAddress;
}
// 根據特征碼獲取 PspLoadImageNotifyRoutine 陣列地址
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetLoadImageNotifyRoutine = NULL;
PVOID pPspLoadImageNotifyRoutine = NULL;
// 先獲取 PsSetLoadImageNotifyRoutineEx 函式地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetLoadImageNotifyRoutineEx");
pPsSetLoadImageNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetLoadImageNotifyRoutine)
{
return pPspLoadImageNotifyRoutine;
}
// 查找 PspLoadImageNotifyRoutine 函式地址
pAddress = SearchMemory(pPsSetLoadImageNotifyRoutine, (PVOID)((PUCHAR)pPsSetLoadImageNotifyRoutine + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
return pPspLoadImageNotifyRoutine;
}
// 先獲取偏移, 再計算地址
lOffset = *(PLONG)pAddress;
pPspLoadImageNotifyRoutine = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
return pPspLoadImageNotifyRoutine;
}
VOID UnDriver(PDRIVER_OBJECT Driver)
{
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
PVOID pPspLoadImageNotifyRoutineAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
// 獲取系統版本資訊, 判斷系統版本
RtlGetVersion(&osInfo);
if (10 == osInfo.dwMajorVersion)
{
// 48 8d 0d 88 e8 db ff
// 查找指令 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff804`44313ce0)]
/*
nt!PsSetLoadImageNotifyRoutineEx+0x41:
fffff801`80748a81 488d0dd8d3dbff lea rcx,[nt!PspLoadImageNotifyRoutine (fffff801`80505e60)]
fffff801`80748a88 4533c0 xor r8d,r8d
fffff801`80748a8b 488d0cd9 lea rcx,[rcx+rbx*8]
fffff801`80748a8f 488bd7 mov rdx,rdi
fffff801`80748a92 e80584a3ff call nt!ExCompareExchangeCallBack (fffff801`80180e9c)
fffff801`80748a97 84c0 test al,al
fffff801`80748a99 0f849f000000 je nt!PsSetLoadImageNotifyRoutineEx+0xfe (fffff801`80748b3e) Branch
*/
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
}
// 根據特征碼獲取地址 獲取 PspLoadImageNotifyRoutine 陣列地址
pPspLoadImageNotifyRoutineAddress = SearchPspLoadImageNotifyRoutine(pSpecialData, ulSpecialDataSize);
DbgPrint("[LyShark] PspLoadImageNotifyRoutine = 0x%p \n", pPspLoadImageNotifyRoutineAddress);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
將這個驅動拖入到虛擬機中并運行,輸出結果如下:

有了陣列地址接下來就是要對陣列進行解密,如何解密?
- 1.首先拿到陣列指標
pPspLoadImageNotifyRoutineAddress + sizeof(PVOID) * i此處的i也就是下標, - 2.得到的新地址在與
pNotifyRoutineAddress & 0xfffffffffffffff8進行與運算, - 3.最后
*(PVOID *)pNotifyRoutineAddress取出里面的引數,
增加解密代碼以后,這段程式的完整代碼也就可以被寫出來了,如下所示,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntddk.h>
#include <windef.h>
// 指定記憶體區域的特征碼掃描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;
// 掃描記憶體
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判斷特征碼
for (m = 0; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判斷是否找到符合特征碼的地址
if (m >= ulMemoryDataSize)
{
// 找到特征碼位置, 獲取緊接著特征碼的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}
return pAddress;
}
// 根據特征碼獲取 PspLoadImageNotifyRoutine 陣列地址
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetLoadImageNotifyRoutine = NULL;
PVOID pPspLoadImageNotifyRoutine = NULL;
// 先獲取 PsSetLoadImageNotifyRoutineEx 函式地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetLoadImageNotifyRoutineEx");
pPsSetLoadImageNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetLoadImageNotifyRoutine)
{
return pPspLoadImageNotifyRoutine;
}
// 查找 PspLoadImageNotifyRoutine 函式地址
pAddress = SearchMemory(pPsSetLoadImageNotifyRoutine, (PVOID)((PUCHAR)pPsSetLoadImageNotifyRoutine + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
return pPspLoadImageNotifyRoutine;
}
// 先獲取偏移, 再計算地址
lOffset = *(PLONG)pAddress;
pPspLoadImageNotifyRoutine = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
return pPspLoadImageNotifyRoutine;
}
VOID UnDriver(PDRIVER_OBJECT Driver)
{
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
PVOID pPspLoadImageNotifyRoutineAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
// 獲取系統版本資訊, 判斷系統版本
RtlGetVersion(&osInfo);
if (10 == osInfo.dwMajorVersion)
{
// 48 8d 0d 88 e8 db ff
// 查找指令 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff804`44313ce0)]
/*
nt!PsSetLoadImageNotifyRoutineEx+0x41:
fffff801`80748a81 488d0dd8d3dbff lea rcx,[nt!PspLoadImageNotifyRoutine (fffff801`80505e60)]
fffff801`80748a88 4533c0 xor r8d,r8d
fffff801`80748a8b 488d0cd9 lea rcx,[rcx+rbx*8]
fffff801`80748a8f 488bd7 mov rdx,rdi
fffff801`80748a92 e80584a3ff call nt!ExCompareExchangeCallBack (fffff801`80180e9c)
fffff801`80748a97 84c0 test al,al
fffff801`80748a99 0f849f000000 je nt!PsSetLoadImageNotifyRoutineEx+0xfe (fffff801`80748b3e) Branch
*/
pSpecialData[0] = 0x48;
pSpecialData[1] = 0x8D;
pSpecialData[2] = 0x0D;
ulSpecialDataSize = 3;
}
// 根據特征碼獲取地址 獲取 PspLoadImageNotifyRoutine 陣列地址
pPspLoadImageNotifyRoutineAddress = SearchPspLoadImageNotifyRoutine(pSpecialData, ulSpecialDataSize);
DbgPrint("[LyShark] PspLoadImageNotifyRoutine = 0x%p \n", pPspLoadImageNotifyRoutineAddress);
// 遍歷回呼
ULONG i = 0;
PVOID pNotifyRoutineAddress = NULL;
// 獲取 PspLoadImageNotifyRoutine 陣列地址
if (NULL == pPspLoadImageNotifyRoutineAddress)
{
return FALSE;
}
// 獲取回呼地址并解密
for (i = 0; i < 64; i++)
{
pNotifyRoutineAddress = *(PVOID *)((PUCHAR)pPspLoadImageNotifyRoutineAddress + sizeof(PVOID) * i);
pNotifyRoutineAddress = (PVOID)((ULONG64)pNotifyRoutineAddress & 0xfffffffffffffff8);
if (MmIsAddressValid(pNotifyRoutineAddress))
{
pNotifyRoutineAddress = *(PVOID *)pNotifyRoutineAddress;
DbgPrint("[LyShark] 序號: %d | 回呼地址: 0x%p \n", i, pNotifyRoutineAddress);
}
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行這段完整的程式代碼,輸出如下效果:

目前系統中只有兩個回呼,所以列舉出來的只有兩條,打開ARK驗證一下會發現完全正確,忽略pyark這是后期打開的,

文章出處:https://www.cnblogs.com/LyShark/p/16808853.html
著作權宣告:本博客文章與代碼均為學習時整理的筆記,文章 [均為原創] 作品,轉載請 [添加出處] ,您添加出處是我創作的動力!
轉載文章請遵守《中華人民共和國著作權法》相關法律規定或遵守《署名CC BY-ND 4.0國際》規范,合理合規攜帶原創出處轉載,如果不攜帶文章出處,并惡意轉載多篇原創文章被本人發現,本人保留起訴權!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/518728.html
標籤:其他
上一篇:Scala-集合
