NDIS HOOK實現方法

來源:互聯網
上載者:User

轉載

探索NDIS HOOK新的實現方法(1)

NDIS HOOK是專業級防火牆使用的一種攔截技術,NDIS HOOK的重點是如何獲得特定協議對應NDIS_PROTOCOL_BLOCK指標,獲得了該指標,接下來就可以替換該協議所註冊的收發函數,而達到攔截網路資料的目的。
     獲 得NDIS_PROTOCOL_BLOCK指標的方法一般是用NdisRegisterProtocol註冊一個新的協議,所獲得的協議控制代碼實際上就是一 個NDIS_PROTOCOL_BLOCK指標,順著該指標遍曆NDIS_PROTOCOL_BLOCK鏈表,就可以找到你所要掛鈎的協議所對應的 NDIS_PROTOCOL_BLOCK.之所以可以這樣做,是因為每註冊一個協議,系統都會把該協議對應的NDIS_PROTOCOL_BLOCK放置 在協議鏈表的開頭,該協議鏈表每個元素都是NDIS_PROTOCOL_BLOCK類型,代表一個已經註冊的協議。
     事 實上我們需要的只是TCPIP協議族的NDIS_PROTOCOL_BLOCK指標,畢竟TCP,IP,ARP,ICMP等等幾乎所有我們感興趣的協議, 都是在tcpip.sys協議驅動裡面實現的。如果我們只需要TCPIP協議所對應的NDIS_PROTOCOL_BLOCK,那麼上面的方法就有點繁瑣 了。我們可以試著尋找更簡便的方法來獲得TCPIP協議的NDIS_PROTOCOL_BLOCK.
於 是我對tcpip.sys驅動進行了反組譯碼,發現NDIS_PROTOCOL_BLOCK指標存放在一個名為_ARPHandle的全域變數裡面,所以如 果能找到_ARPHandle的地址,我們就成功了,我們完全可以把該全域變數的位移量作為一個常量來使用,但這裡純粹為了拓寬思路,我介紹另一種找到該 全域變數的方法。
     Tcpip.sys有個匯出函數叫IPDelayedNdisReEnumerateBindings,該函數內部曾經出現過_ARPHandle 的地址,為什麼會出現它的地址呢,因為該函數內部調用過NdisReEnumerateProtocolBindings函數,懂得反組譯碼的應該知道,在 用call指令調用函數之前,必然會用到push指令將函數的參數壓到棧裡面去,不巧的是, NdisReEnumerateProtocolBindings函數只有一個參數,而該參數恰恰是一個NDIS_PROTOCOL_BLOCK指標類 型,在這裡,實際上就是把_ARPHandle當作參數傳給了
NdisReEnumerateProtocolBindings,所以_ARPHandle的地址必然會出現在push指令的後面,說具體一點,緊跟push指令的四個位元組就是_ARPHandle的地址。
      所以具體的思路就是這樣,先找到IPDelayedNdisReEnumerateBindings函數的地址,然後從該函數的地址開始搜尋push指令 的特徵碼,搜到了以後,把緊跟push指令的四個位元組作為指向NDIS_PROTOCOL_BLOCK指標的指標返回。
     也許有的人會問,如果IPDelayedNdisReEnumerateBindings函數體內部出現過多次push指令,豈不是會搜出不正確的地址, 事實上,雖然都叫push指令,然而在機器碼層級是不同的,push指令的機器碼錶示有十幾種之多,用來區別不同的定址方式,調用 NdisReEnumerateProtocolBindings 時用的push指令位元組序列是0xff35,這個push指令表示後面緊跟的四個位元組是一個記憶體位址,而不是一個立即數或者寄存器之類的。知道了這些,我 們就可以清楚,在一個有限的位址範圍,0xff35的唯一性是可以得到滿足的。根據我的觀察,在win2000,winxp,win2003上 面,IPDelayedNdisReEnumerateBindings本身是一個很短的函數,0xff35指令確實只出現過一次,所以該方法是很可靠 的。
思路已經出來了,下面我把詳細的代碼給大家貼出來,理解這些代碼需要對windows Pe格式有所瞭解,如果你不想理解也行,代碼可以直接拿來用。
       以下是我寫的一個 擷取核心模組某個匯出函數地址的 通用常式。這裡主要是為了擷取tcpip.sys模組的匯出函數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);//該函數用來獲得核心模組的基地址
    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函數的實現:
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協議的NDIS_PROTOCOL_BLOCK指標的函數
   void* GetProtocolBlock()
{
    char* base;
    char bytes[]={0xff,0x35};
    base=GetRoutineAddress("tcpip.sys","IPDelayedNdisReEnumerateBindings");
     while(RtlCompareMemory(base,bytes,2)!=2)
    {
       base++;
    }
    return **((void***)(base+2));

}

 

探索NDIS HOOK新的實現方法(2)

---INLINE HOOK實現NDIS HOOK
前 面講述了如何通過擷取NDIS_PROTOCOL_BLOCK來實現NDIS HOOK,這裡講述第二種方法,那就是inline hook方法。說起inline hook,也不是什麼新鮮玩意,無非是在一個函數的首部嵌入一個jmp機器指令,在該函數執行有效代碼前就跳到我們的代理函數,在我們的代理函數裡做了必 要的處理以後,再跳回原來的函數,接著執行原函數的指令。
既然tcpip.sys是標準的NDIS協議驅動,那麼收包函數顯然應該是在 tcpip.sys內部實現的,我們直接找到這兩個收包函數,然後對其inline hook不就可以了嗎?經過逆向分析,我找到了這兩個函數,本人安裝了兩個XP系統,其中一個匯出了這兩個函數,另一個系統卻沒匯出,所以我們仍然需要用 特徵碼搜尋這兩個函數,這兩個函式宣告如下:
NDIS_STATUS
ARPRcv (NDIS_HANDLE BindContext,
NDIS_HANDLE MacContext,
UCHAR* HeadBuffer,
ULONG HeadSize,
UCHAR* Buffer,
ULONG BufferSize,
ULONG PacketSize);
INT
ARPRcvPacket (NDIS_HANDLE BindContext,
PNDIS_PACKET Packet);
搜尋這兩個函數地址的代碼如下:
//以下全域變數儲存兩個函數的地址
void* ARPRcv=NULL;
void* ARPRcvPacket=NULL;
void SearchProtocolRoutine()
{
//以下分別為兩個收包函數的特徵碼
UCHAR ARPRcvBytes[] ={0x8b,0xff,0x55,0x8b,0xec,0x56,0x8b,0x75,0x08,0x33};
UCHAR ARPRcvPacketBytes[]={0x8b,0xff,0x55,0x8b,0xec,0x51,0x53,0x56,0x57,0x8b};
//擷取tcpip.sys模組的基地址,該函數在前一節已經提供給大家
char* base=FindModule("tcpip.sys");
while(ARPRcv==NULL||ARPRcvPacket==NULL)
{
if(ARPRcv==NULL&&
RtlCompareMemory(ARPRcvBytes,base,10)==10)
{
ARPRcv=base;
}
else if(ARPRcvPacket==NULL&&
RtlCompareMemory(ARPRcvPacketBytes,base,10)==10)
{
ARPRcvPacket=base;
}
base++;
}
}
各種編譯器所編譯的函數,前幾個指令都是幾乎一樣的,用來建立堆疊框架,這些指令叫函數的序言。
在win2000上是三位元組
push ebp
mov ebp, esp
到了winxp以及後續系統上,則變成了五位元組
mov edi, edi
push ebp
mov ebp, esp
而一個近跳轉指令剛好是五位元組,在xp上剛好覆蓋了函數的序言,所以在XP上掛鈎也相對容易一點,這裡著重說明如何對ARPRcv進行掛鈎,我們在ARPRcv內部插入一個jmp指令,將跳到ARPRcvProx函數,該函數是個裸函數,函數實現如下:
_declspec(naked) ARPRcvProx()//跳板函數
{
_asm
{
mov edi, edi
push ebp
mov ebp ,esp
//七個參數開始壓棧
push [ebp+20h]
push [ebp+1ch]
push [ebp+18h]
push [ebp+14h]
push [ebp+10h]
push [ebp+0ch]
push [ebp+8]
call NewARPRcv //調用NewARPRcv函數
cmp eax,0x10003 //判斷函數傳回值是否NDIS_STATUS_NOT_ACCEPTED
jz end //如果是NDIS_STATUS_NOT_ACCEPTED,直接結束本函數
//而不跳回到ARPRcv函數
mov eax,ARPRcv //如果返回的不是NDIS_STATUS_NOT_ACCEPTED,將會
//執行到這條指令,該指令將 ARPRcv函數的地址裝入eax
add eax,5 //將ARPRcv地址值加上5,存入eax,表示即將跳轉的//地址
jmp eax //開始跳回ARPRcv體內
end:
pop ebp
retn 1ch
}
}
在該函數內部,又調用了NewARPRcv函數,原型和ARPRcv保持一致,也必須由我們自己實現:
NDIS_STATUS
NewARPRcv(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
)
{
/*
在這裡加入你的判斷邏輯代碼,是否攔截該資料
如果要攔截,則返回 NDIS_STATUS_NOT_ACCEPTED
否則返回NDIS_STATUS_SUCCESS,把資料交給ARPRcv處理
*/
return NDIS_STATUS_SUCCESS;
}
同樣的原理,我們在ARPRcvPacket裡面插入jmp指令,將跳轉到ARPRcvPacketProx裸函數,該函數實現如下:
_declspec(naked) ARPRcvPacketProx()
{
_asm
{
mov edi, edi
push ebp
mov ebp ,esp
//兩個參數開始壓棧
push [ebp+0ch]
push [ebp+8]
call NewARPRcvPacket//調用NewARPRcvPacket
cmp eax,0 //如果返回0則表示拒絕該資料包
jz end //直接返回本函數
mov eax ,ARPRcvPacket
add eax ,5
jmp eax //跳回ARPRcvPacket函數第六個位元組
end: pop ebp
retn 8
}
}
在該函數內部,將會調用NewARPRcvPacket,函數實現如下:
INT
NewARPRcvPacket(NDIS_HANDLE BindContext,
PNDIS_PACKET ndisPacket)
{
/*
在這裡加入你的判斷邏輯,是否攔截該資料,如果要攔截,則返回0,
否則返回非0
*/
DbgPrint("RcvPacket");
return 1;
}
請仔細閱讀以上代碼的注釋,接下來,我們還必須提供一個函數實現安裝和卸載掛鈎功能
void PatchARPRcv(BOOLEAN isPatch)//isPatch為TRUE表示安裝掛鈎,為FALSE表示卸載掛鈎。
{
/*即將用以下五個位元組覆蓋ARPRcv函數前五個位元組
這5個位元組就是jmp XXXX指令的機器碼,因為跳轉的相對位址還需要
進一步計算,所以暫時用零填充
*/
UCHAR patchBytes[5]={0xe9,0x00,0x00,0x00,0x00};
//即將用以下五個位元組覆蓋ARPRcvPacket函數前五個位元組
UCHAR patchBytes2[5]={0xe9,0x00,0x00,0x00,0x00};
//儲存原始函數的前五個位元組,方便以後恢複掛鈎
UCHAR restoreBytes[5]={0x8b,0xff,0x55,0x8b,0xec};
/*
以下兩行代碼計算跳轉的位移量
*/
int offset=(char*)ARPRcvProx-(char*)ARPRcv-5;
int offset2=(char*)ARPRcvPacketProx-(char*)ARPRcvPacket-5;
//修正patchBytes和patchBytes2中的相對位址
memcpy(patchBytes+1,&offset,4);
memcpy(patchBytes2+1,&offset2,4);
if(isPatch)
{
DisableWriteProtect();//禁止防寫保護
memcpy(ARPRcv,patchBytes,5);
memcpy(ARPRcvPacket,patchBytes2,5);
EnableWriteProtect(); //開啟防寫保護
}
else
{
DisableWriteProtect();
memcpy(ARPRcv,restoreBytes,5);
memcpy(ARPRcvPacket,restoreBytes,5);
EnableWriteProtect();
}
}
因為ARPRcv和ARPRcvPacket函數處於唯讀頁,所以必須先禁用防寫保護才能向其中插入代碼,禁用防寫保護和開啟防寫保護代碼如下:
void
DisableWriteProtect()
{
_asm{
cli
mov eax, cr0
and eax, 0FFFEFFFFh
mov cr0, eax
}
}
void
EnableWriteProtect()
{
_asm{
mov eax, cr0
or eax, not 0FFFEFFFFh
mov cr0, eax
sti
}
}
注意這些代碼暫時只適用XP系統,在win2000和win2003上都需要少許改動。

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.