在前面的系列教程如《驅動開發:內核列舉DpcTimer定時器》或者《驅動開發:內核列舉IoTimer定時器》里面LyShark大量使用了特征碼定位這一方法來尋找符合條件的匯編指令集,總體來說這種方式只能定位特征較小的指令如果特征值擴展到5位以上那么就需要寫很多無用的代碼,本章內容中將重點分析,并實作一個通用特征定位函式,
如下是一段特征碼搜索片段,可以看到其實僅僅只是將上章中的搜索方式變成了一個SearchSpecialCode函式,如下函式,用戶傳入一個掃描起始地址以及搜索特征碼的位元組陣列,即可完成搜索作業,具體的引數定義如下,
- pSearchBeginAddr 掃描的記憶體(內核)起始地址
- ulSearchLength 需要掃描的長度
- pSpecialCode 掃描特征碼,傳入一個UCHAR型別的位元組陣列
- ulSpecialCodeLength 特征碼長度,傳入位元組陣列長度
// By: LyShark.com
PVOID SearchSpecialCode(PVOID pSearchBeginAddr, ULONG ulSearchLength, PUCHAR pSpecialCode, ULONG ulSpecialCodeLength)
{
PVOID pDestAddr = NULL;
PUCHAR pBeginAddr = (PUCHAR)pSearchBeginAddr;
PUCHAR pEndAddr = pBeginAddr + ulSearchLength;
PUCHAR i = NULL;
ULONG j = 0;
for (i = pBeginAddr; i <= pEndAddr; i++)
{
// 遍歷特征碼
for (j = 0; j < ulSpecialCodeLength; j++)
{
// 判斷地址是否有效
if (FALSE == MmIsAddressValid((PVOID)(i + j)))
{
break;
}
// 匹配特征碼
if (*(PUCHAR)(i + j) != pSpecialCode[j])
{
break;
}
}
// 匹配成功
if (j >= ulSpecialCodeLength)
{
pDestAddr = (PVOID)i;
break;
}
}
return pDestAddr;
}
那么這個簡單的特征碼掃描函式該如何使用,這里我們就用《驅動開發:內核列舉IoTimer定時器》中列舉IopTimerQueueHead鏈表頭部地址為案例進行講解,如果你忘記了如何尋找鏈表頭部可以去前面的文章中學習,這里只給出實作流程,

我們首先通過MmGetSystemRoutineAddress得到IoInitializeTimer首地址,然后在偏移長度為0x7e范圍內搜索特征碼48 8d 0d特征,其代碼可以總結為如下樣子,
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint(("hello lyshark.com \n"));
// 得到基址
PUCHAR IoInitializeTimer = GetIoInitializeTimerAddress();
DbgPrint("IoInitializeTimer Address = %p \n", IoInitializeTimer);
// ---------------------------------------------------
// LyShark 開始定位特征
// 設定起始位置
PUCHAR StartSearchAddress = (PUCHAR)IoInitializeTimer;
// 設定結束位置
PUCHAR EndSearchAddress = StartSearchAddress + 0x7e;
DbgPrint("[LyShark 搜索區間] 起始地址: 0x%X --> 結束地址: 0x%X \n", StartSearchAddress, EndSearchAddress);
// 設定搜索長度
LONGLONG size = EndSearchAddress - StartSearchAddress;
DbgPrint("[LyShark 搜索長度] 長度: %d \n", size);
PVOID ptr;
// 指定特征碼
UCHAR pSpecialCode[256] = { 0 };
// 指定特征碼長度
ULONG ulSpecialCodeLength = 3;
pSpecialCode[0] = 0x48;
pSpecialCode[1] = 0x8d;
pSpecialCode[2] = 0x0d;
// 開始搜索,找到后回傳首地址
ptr = SearchSpecialCode(StartSearchAddress, size, pSpecialCode, ulSpecialCodeLength);
DbgPrint("搜索特征碼首地址: 0x%p \n", ptr);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
代碼運行后你會發現可以直接定位到我們所需要的位置上,如下圖所示:

如上圖可以看到,這個特征碼定位函式回傳的是記憶體地址,而我們需要得到地址內的資料,此時就需要提取以下,
例如當指令是:
fffff80206185c00 488d0dd9ddcdff lea rcx,[nt!IopTimerQueueHead (fffff80205e639e0)]
那么就需要RtlCopyMemory跳過前三個位元組,并在第四個位元組開始取資料,并將讀入的資料放入到IopTimerQueueHead_LyShark_Code變數內,
// 開始搜索,找到后回傳首地址
ptr = SearchSpecialCode(StartSearchAddress, size, pSpecialCode, ulSpecialCodeLength);
DbgPrint("搜索特征碼首地址: 0x%p \n", ptr);
// 提取特征
// fffff802`06185c00 488d0dd9ddcdff lea rcx,[nt!IopTimerQueueHead (fffff802`05e639e0)]
ULONG64 iOffset = 0;
ULONG64 IopTimerQueueHead_LyShark_Code = 0;
__try
{
// 拷貝記憶體跳過lea,向后四位元組
RtlCopyMemory(&iOffset, (ULONG64)ptr + 3, 4);
// 取出后面的IopTimerQueueHead記憶體地址 LyShark.com
IopTimerQueueHead_LyShark_Code = iOffset + (ULONG64)ptr + 7;
DbgPrint("提取資料: 0x%p \n", IopTimerQueueHead_LyShark_Code);
}
__except (1)
{
DbgPrint("[LySHark] 拷貝記憶體例外 \n");
}
這樣即可得到我們所需要的地址,如下結果所示:

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