Posted on 2008-08-30 23:42
Condor 閱讀(1695)
評(píng)論(0) 編輯 收藏 引用
NDIS HOOK是專業(yè)級(jí)防火墻使用的一種攔截技術(shù),NDIS HOOK的重點(diǎn)是如何獲得特定協(xié)議對(duì)應(yīng)NDIS_PROTOCOL_BLOCK指針,獲得了該指針,接下來(lái)就可以替換該協(xié)議所注冊(cè)的收發(fā)函數(shù),而達(dá)到攔截網(wǎng)絡(luò)數(shù)據(jù)的目的。
獲 得NDIS_PROTOCOL_BLOCK指針的方法一般是用NdisRegisterProtocol注冊(cè)一個(gè)新的協(xié)議,所獲得的協(xié)議句柄實(shí)際上就是一 個(gè)NDIS_PROTOCOL_BLOCK指針,順著該指針遍歷NDIS_PROTOCOL_BLOCK鏈表,就可以找到你所要掛鉤的協(xié)議所對(duì)應(yīng)的 NDIS_PROTOCOL_BLOCK.之所以可以這樣做,是因?yàn)槊孔?cè)一個(gè)協(xié)議,系統(tǒng)都會(huì)把該協(xié)議對(duì)應(yīng)的NDIS_PROTOCOL_BLOCK放置 在協(xié)議鏈表的開(kāi)頭,該協(xié)議鏈表每個(gè)元素都是NDIS_PROTOCOL_BLOCK類型,代表一個(gè)已經(jīng)注冊(cè)的協(xié)議。
事 實(shí)上我們需要的只是TCPIP協(xié)議族的NDIS_PROTOCOL_BLOCK指針,畢竟TCP,IP,ARP,ICMP等等幾乎所有我們感興趣的協(xié)議, 都是在tcpip.sys協(xié)議驅(qū)動(dòng)里面實(shí)現(xiàn)的。如果我們只需要TCPIP協(xié)議所對(duì)應(yīng)的NDIS_PROTOCOL_BLOCK,那么上面的方法就有點(diǎn)繁瑣 了。我們可以試著尋找更簡(jiǎn)便的方法來(lái)獲得TCPIP協(xié)議的NDIS_PROTOCOL_BLOCK.
于 是我對(duì)tcpip.sys驅(qū)動(dòng)進(jìn)行了反匯編,發(fā)現(xiàn)NDIS_PROTOCOL_BLOCK指針存放在一個(gè)名為_(kāi)ARPHandle的全局變量里面,所以如 果能找到_ARPHandle的地址,我們就成功了,我們完全可以把該全局變量的偏移量作為一個(gè)常量來(lái)使用,但這里純粹為了拓寬思路,我介紹另一種找到該 全局變量的方法。
Tcpip.sys有個(gè)導(dǎo)出函數(shù)叫IPDelayedNdisReEnumerateBindings,該函數(shù)內(nèi)部曾經(jīng)出現(xiàn)過(guò)_ARPHandle 的地址,為什么會(huì)出現(xiàn)它的地址呢,因?yàn)樵摵瘮?shù)內(nèi)部調(diào)用過(guò)NdisReEnumerateProtocolBindings函數(shù),懂得反匯編的應(yīng)該知道,在 用call指令調(diào)用函數(shù)之前,必然會(huì)用到push指令將函數(shù)的參數(shù)壓到棧里面去,不巧的是, NdisReEnumerateProtocolBindings函數(shù)只有一個(gè)參數(shù),而該參數(shù)恰恰是一個(gè)NDIS_PROTOCOL_BLOCK指針類 型,在這里,實(shí)際上就是把_ARPHandle當(dāng)作參數(shù)傳給了
NdisReEnumerateProtocolBindings,所以_ARPHandle的地址必然會(huì)出現(xiàn)在push指令的后面,說(shuō)具體一點(diǎn),緊跟push指令的四個(gè)字節(jié)就是_ARPHandle的地址。
所以具體的思路就是這樣,先找到IPDelayedNdisReEnumerateBindings函數(shù)的地址,然后從該函數(shù)的地址開(kāi)始搜索push指令的特征碼,搜到了以后,把緊跟push指令的四個(gè)字節(jié)作為指向NDIS_PROTOCOL_BLOCK指針的指針?lè)祷亍?br> 也許有的人會(huì)問(wèn),如果IPDelayedNdisReEnumerateBindings函數(shù)體內(nèi)部出現(xiàn)過(guò)多次push指令,豈不是會(huì)搜出不正確的地址,事實(shí)上,雖然都叫push指令,然而在機(jī)器碼級(jí)別是不同的,push指令的機(jī)器碼表示有十幾種之多,用來(lái)區(qū)別不同的尋址方式,調(diào)用NdisReEnumerateProtocolBindings 時(shí)用的push指令字節(jié)序列是0xff35,這個(gè)push指令表示后面緊跟的四個(gè)字節(jié)是一個(gè)內(nèi)存地址,而不是一個(gè)立即數(shù)或者寄存器之類的。知道了這些,我 們就可以清楚,在一個(gè)有限的地址范圍,0xff35的唯一性是可以得到滿足的。根據(jù)我的觀察,在win2000,winxp,win2003上面,IPDelayedNdisReEnumerateBindings本身是一個(gè)很短的函數(shù),0xff35指令確實(shí)只出現(xiàn)過(guò)一次,所以該方法是很可靠的。
思路已經(jīng)出來(lái)了,下面我把詳細(xì)的代碼給大家貼出來(lái),理解這些代碼需要對(duì)windows Pe格式有所了解,如果你不想理解也行,代碼可以直接拿來(lái)用。
以下是我寫的一個(gè) 獲取內(nèi)核模塊某個(gè)導(dǎo)出函數(shù)地址的 通用例程。這里主要是為了獲取tcpip.sys模塊的導(dǎo)出函數(shù)IPDelayedNdisReEnumerateBindings
void* GetRoutineAddress(char* ModuleName,char* RoutineName)
{
PIMAGE_DOS_HEADER dos_hdr;
PIMAGE_NT_HEADERS nt_hdr;
PIMAGE_EXPORT_DIRECTORY export_dir;
ULONG *fn_name, *fn_addr, i;
char* base;
base=(char*)FindModule(ModuleName);//該函數(shù)用來(lái)獲得內(nèi)核模塊的基地址
if(!base)
return NULL;
DbgPrint("tcpip address:%p",base);
dos_hdr = (PIMAGE_DOS_HEADER)base;
if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
nt_hdr = (PIMAGE_NT_HEADERS)(base + dos_hdr->e_lfanew);
export_dir = (PIMAGE_EXPORT_DIRECTORY)(base + nt_hdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
fn_name = (ULONG *)(base + export_dir->AddressOfNames);
fn_addr = (ULONG *)(base + export_dir->AddressOfFunctions);
for (i = 0; i < export_dir->NumberOfNames; i++, fn_name++, fn_addr++)
{
if (strcmp(RoutineName, base + *fn_name) == 0)
{
return base + *fn_addr;
}
}
return NULL;
}
以下是FindModule函數(shù)的實(shí)現(xiàn):
void *
FindModule(char *name)
{
ULONG i, n, *q;
PSYSTEM_MODULE_INFORMATION p;
void *base;
ZwQuerySystemInformation(SystemModuleInformation, &n, 0, &n);
q = (ULONG *)ExAllocatePool(PagedPool, n);
ZwQuerySystemInformation(SystemModuleInformation, q, n * sizeof (*q), 0);
p = (PSYSTEM_MODULE_INFORMATION)(q + 1);
base = NULL;
for (i = 0; i < *q; i++) {
if (_stricmp(p.ImageName + p.ModuleNameOffset, name) == 0) {
base = p.Base;
break;
}
}
ExFreePool(q);
return base;
}
以下是獲取tcpip協(xié)議的NDIS_PROTOCOL_BLOCK指針的函數(shù)
void* GetProtocolBlock()
{
char* base;
char bytes[]={0xff,0x35};
base=GetRoutineAddress("tcpip.sys","IPDelayedNdisReEnumerateBindings");
while(RtlCompareMemory(base,bytes,2)!=2)
{
base++;
}
return **((void***)(base+2));
}