再谈进程与端口的映射

Author : ilsy
Email : ilsy@whitecell.org
HomePage: http://www.whitecell.org

日 期:2002-02-06
类 别:安全
关键字:进程 PDE PTE 分页 内核对象 线性地址 物理地址


关于进程与端口映射的文章已经有很多了,我把我对fport的分析也写出来,让大家知道fport是如何工作的.
fport.exe是由foundstone team出品的免费软件,可以列出系统中所有开放的端口都是由那些进程打开的.而下
面所描述的方法是基于fport v1.33的,如果和你机器上的fport有出入,请检查fport版本.

首先,它检测当前用户是否拥有管理员权限(通过读取当前进程的令牌可知当前用户是否具有管理权限,请参考
相关历程),如果没有,打印一句提示后退出,然后设置当前进程的令牌,接着,用ZwOpenSection函数打开内核对象
/Device/PhysicalMemory,这个对象用于对系统物理内存的访问.ZwOpenSection函数的原型如下:

NTSYSAPI
NTSTSTUS
NTAPI
ZwOpenSection(
    Out PHANDLE sectionHandle;
    IN ACCESS_MASK DesiredAccess;
    IN POBJECT_ATTRIBUTES ObjectAttributes
    };
(见ntddk.h)

第一个参数得到函数执行成功后的句柄
第二个参数DesiredAccess为一个常数,可以是下列值:
    #define SECTION_QUERY 0x0001
    #define SECTION_MAP_WRITE 0x0002
    #define SECTION_MAP_READ 0x0004
    #define SECTION_MAP_EXECUTE 0x0008
    #define SECTION_EXTEND_SIZE 0x0010

    #define SECTION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SECTION_QUERY|/
SECTION_MAP_WRITE | /
SECTION_MAP_READ | /
SECTION_MAP_EXECUTE | /
SECTION_EXTEND_SIZE)
    (见ntddk.h)
第三个参数是一个结构,包含要打开的对象类型等信息,结构定义如下:
    typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
    PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
    } OBJECT_ATTRIBUTES;
    typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
    (见ntdef.h)
对于这个结构的初始化用一个宏完成:
    #define InitializeObjectAttributes( p, n, a, r, s ) { /
    (p)->Length = sizeof( OBJECT_ATTRIBUTES ); /
    (p)->RootDirectory = r; /
    (p)->Attributes = a; /
    (p)->ObjectName = n; /
    (p)->SecurityDescriptor = s; /
    (p)->SecurityQualityOfService = NULL; /
    }
    (见ntdef.h)
那么,打开内核对象/Device/PhysicalMemory的语句如下:
WCHAR    PhysmemName[] =         L"//Device//PhysicalMemory";
void * pMapPhysicalMemory;
HANDLE pHandle;

bool    OpenPhysicalMemory()
{
    NTSTATUS    status;
    UNICODE_STRING    physmemString;
    OBJECT_ATTRIBUTES attributes;
    RtlInitUnicodeString( &physmemString, PhysmemName ); //初始化Unicode字符串,函数原型见ntddk.h    
    InitializeObjectAttributes( &attributes, &physmemString,
                OBJ_CASE_INSENSITIVE, NULL, NULL ); //初始化OBJECT_ATTRIBUTES结构
    status = ZwOpenSection(pHandle, SECTION_MAP_READ, &attributes ); //打开内核对象/Device/PhysicalMemory,获得句柄
    if( !NT_SUCCESS( status ))
        return false;
    pMapPhysicalMemory=MapViewOfFile(pHandle,FILE_MAP_READ,
                        0,0x30000,0x1000);
    //从内存地址0x30000开始映射0x1000个字节
    if( GetLastError()!=0)
        return false;                     
    return true;
}

为什么要从0x30000开始映射呢,是这样,我们知道,在Windows NT/2000下,系统分为内核模式和用户模式,也就是我们
所说的Ring0和Ring3,在Windows NT/2000下,我们所能够看到的进程都运行在Ring3下,一般情况下,系统进程(也就是System
进程)的页目录(PDE)所在物理地址地址为0x30000,或者说,系统中最小的页目录所在的物理地址为0x30000.而页目录(PDE)由
1024项组成,每项均指向一页表(PTE),每一页表也由1024个页组成,而每页的大小为4K,1024*4=4096(0x1000),所以,上面从物
理地址0x30000开始映射了0x1000个字节.(具体描述见WebCrazy的文章<<小议Windows NT/2000的分页机制>>)

程序打开打开内核对象/Device/PhysicalMemory后,继续用函数ZwOpenFile打开内核对象/Device/Tcp和Device/Udp,ZwOpenFile
函数的原型如下:
NTSYSAPI
NTSTATUS
NTAPI
ZwOpenFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG ShareAccess,
IN ULONG OpenOptions
);
(见ntddk.h)

第一个参数返回打开对象的句柄
第二个参数DesiredAccess为一个常数,可以是下列值:
    #define FILE_READ_DATA ( 0x0001 ) // file & pipe
    #define FILE_LIST_DIRECTORY ( 0x0001 ) // directory
    #define FILE_WRITE_DATA ( 0x0002 ) // file & pipe
    #define FILE_ADD_FILE ( 0x0002 ) // directory
    #define FILE_APPEND_DATA ( 0x0004 ) // file
    #define FILE_ADD_SUBDIRECTORY ( 0x0004 ) // directory
    #define FILE_CREATE_PIPE_INSTANCE ( 0x0004 ) // named pipe
    #define FILE_READ_EA ( 0x0008 ) // file & directory
    #define FILE_WRITE_EA ( 0x0010 ) // file & directory
    #define FILE_EXECUTE ( 0x0020 ) // file
    #define FILE_TRAVERSE ( 0x0020 ) // directory
    #define FILE_DELETE_CHILD ( 0x0040 ) // directory
    #define FILE_READ_ATTRIBUTES ( 0x0080 ) // all
    #define FILE_WRITE_ATTRIBUTES ( 0x0100 ) // all
    #define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF)
    #define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |/
    FILE_READ_DATA |/
    FILE_READ_ATTRIBUTES |/
    FILE_READ_EA |/
    SYNCHRONIZE)
    #define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |/
    FILE_WRITE_DATA |/
    FILE_WRITE_ATTRIBUTES |/
    FILE_WRITE_EA |/
    FILE_APPEND_DATA |/
    SYNCHRONIZE)
    #define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |/
    FILE_READ_ATTRIBUTES |/
    FILE_EXECUTE |/
    SYNCHRONIZE)
(见ntdef.h)
第三个参数是一个结构,包含要打开的对象类型等信息,结构定义见上面所述
第四个参数返回打开对象的属性,是一个结构,定义如下:
    typedef struct _IO_STATUS_BLOCK {
    union {
    NTSTATUS Status;
    PVOID Pointer;
    };

    ULONG_PTR Information;
    } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

    #if defined(_WIN64)
    typedef struct _IO_STATUS_BLOCK32 {
    NTSTATUS Status;
    ULONG Information;
    } IO_STATUS_BLOCK32, *PIO_STATUS_BLOCK32;
    #endif
    (见ntddk.h)
第五个参数ShareAccess是一个常数,可以是下列值:
    #define FILE_SHARE_READ 0x00000001 // winnt
    #define FILE_SHARE_WRITE 0x00000002 // winnt
    #define FILE_SHARE_DELETE 0x00000004 // winnt
    (见ntddk.h)
第六个参数OpenOptions也是一个常数,可以是下列的值:
    #define FILE_DIRECTORY_FILE 0x00000001
    #define FILE_WRITE_THROUGH 0x00000002
    #define FILE_SEQUENTIAL_ONLY 0x00000004
    #define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
    #define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
    #define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
    #define FILE_NON_DIRECTORY_FILE 0x00000040
    #define FILE_CREATE_TREE_CONNECTION 0x00000080
    #define FILE_COMPLETE_IF_OPLOCKED 0x00000100
    #define FILE_NO_EA_KNOWLEDGE 0x00000200
    #define FILE_OPEN_FOR_RECOVERY 0x00000400
    #define FILE_RANDOM_ACCESS 0x00000800
    #define FILE_DELETE_ON_CLOSE 0x00001000
    #define FILE_OPEN_BY_FILE_ID 0x00002000
    #define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
    #define FILE_NO_COMPRESSION 0x00008000
    #define FILE_RESERVE_OPFILTER 0x00100000
    #define FILE_OPEN_REPARSE_POINT 0x00200000
    #define FILE_OPEN_NO_RECALL 0x00400000
    #define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
    #define FILE_COPY_STRUCTURED_STORAGE 0x00000041
    #define FILE_STRUCTURED_STORAGE 0x00000441
    #define FILE_VALID_OPTION_FLAGS 0x00ffffff
    #define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032
    #define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032
    #define FILE_VALID_SET_FLAGS 0x00000036
    (见ntddk.h)
       
那么,打开内核对象/Device/Tcp和/Device/Udp的语句如下:   
WCHAR physmemNameTcp[]=L"//Device//TCP";
WCHAR physmemNameUdp[]=L"//Device//UDP";
HANDLE pTcpHandle;
HANDLE pUdpHandle;

HANDLE OpenDeviceTcpUdp(WCHAR * deviceName)
{
    NTSTATUS    status;
    UNICODE_STRING    physmemString;
    OBJECT_ATTRIBUTES attributes;
    IO_STATUS_BLOCK iosb;
    HANDLE pDeviceHandle;

    RtlInitUnicodeString(&physmemString, deviceName);    
    if(GetLastError()!=0)
        return NULL;
    InitializeObjectAttributes( &attributes,&physmemString,
                            OBJ_CASE_INSENSITIVE,0, NULL );
    status = ZwOpenFile ( &pDeviceHandle,0x100000, &attributes, &iosb, 3,0);
    if( !NT_SUCCESS( status ))
        return NULL;
}

接着,程序用ZwQuerySystemInformation函数获得系统当前所以进程的所建立的句柄及其相关信息,函数的原型如下:
NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySystemInformation(
    IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
    IN OUT PVOID SystemInformation,
    IN ULONG SystemInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    };
(这个函数结构Microsoft没有公开,参见Gary Nebbett<<Windows NT/2000 Native API Reference>>)

第一个参数是一个枚举常数,设置要查询的系统信息类型,ZwQuerySystemInformation支持54个系统信息的查询,我们要用到的
是它的第16号功能,进行SystemHandleInformation查询.
SYSTEM_HANDLE_INFORMATION结构定义如下:
    typedef struct _SYSTEM_HANDLE_INFORMATION{
        ULONG ProcessID;         //进程的标识ID
        UCHAR ObjectTypeNumber;         //对象类型
        UCHAR Flags;              //0x01 = PROTECT_FROM_CLOSE,0x02 = INHERIT
        USHORT Handle;              //对象句柄的数值
        PVOID Object;             //对象句柄所指的内核对象地址
        ACCESS_MASK GrantedAccess;      //创建句柄时所准许的对象的访问权
    }SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
    (这个函数结构Microsoft没有公开,参见Gary Nebbett<<Windows NT/2000 Native API Reference>>)
第二个参数输出查询的结果
第三个参数设置缓冲区的长度
第四个参数返回函数正确执行需要的缓冲区的大小
代码如下:
#define SystemHandleInformation 16
PULONG GetHandleList()
{
    ULONG cbBuffer = 0x1000;    //先设定一个较小的缓冲空间
    PULONG pBuffer = new ULONG[cbBuffer]; //分配内存
    NTSTATUS Status;

    do
    {
        Status = ZwQuerySystemInformation(
                    SystemHandleInformation,
                    pBuffer, cbBuffer * sizeof * pBuffer, NULL);

        if (Status == STATUS_INFO_LENGTH_MISMATCH)
        {
            //如果返回的错误信息为缓冲区长度不够,那么重新分配内存
            delete [] pBuffer;
            pBuffer = new ULONG[cbBuffer *= 2];
        }
        else if (!NT_SUCCESS(Status))
        {
            //如果是其他错误信息,返回
            delete [] pBuffer;
            return false;
        }
    }
    while (Status == STATUS_INFO_LENGTH_MISMATCH);
    return pBuffer;
}

因为如果一个进程打开了端口,那么它肯定会建立类型为/Device/Tcp和/Device/Udp的内核对象,所以,我们在当前进程中打开
上述的两个内核对象,在打开的同时保存了打开的句柄,这样,我们可以在上面获得的句柄列表中的当前进程中查找对象句柄的
数值和我们保存的两个打开的内核对象的句柄数值相同的句柄,并得到其句柄所指向的内核对象的地址.代码如下:
DWORD TcpHandle;
DWORD UdpHandle;
DWORD GetTcpUdpObject(PULONG pBuffer,HANDLE pHandle,DWORD ProcessId)
{
    DWORD objTYPE1,objTYPE2,HandleObject;

    PSYSTEM_HANDLE_INFORMATION pProcesses = (PSYSTEM_HANDLE_INFORMATION)(pBuffer+1);

    for (i=0;i< * pBuffer;i++)
    {
        if ((pProcesses[i].ProcessID) == ProcessId)
        {
            objTYPE1 = (DWORD)hDeviceTcpUdp;
            objTYPE2 = (DWORD)pProcesses[i].Handle;
            if(objTYPE1==objTYPE2)
            {
                HandleObject = (DWORD)pProcesses.Object;
                return HandleObject;
        }
    }
    return 0;
}

这个内核对象地址是一个线性地址,我们需要把这个地址转换为物理地址,并得到一些相关的数据.在fport中,换算是这样进行的:
(具体描述见WebCrazy的文章<<小议Windows NT/2000的分页机制>>)
void * NewmapPhy;

void GetPTE(DWORD objAddress)
{
    DWORD physmemBuff;
    DWORD newAddress1,newAddress2,newAddress3,newAddress4;
    DWORD * newAddress;

    physmemBuff = (DWORD)pMapPhysicalMemory;
    newAddress1 = physmemBuff+(objAddress>>0x16)*4;
    newAddress = (DWORD *)newAddress1;
    newAddress1 = * newAddress;
    newAddress2 = objAddress & 0x3FF000;
    newAddress3 = newAddress1 & 0x0FFFFF000;
    newAddress4 = newAddress2 + newAddress3;
    NewmapPhy = MapViewOfFile(ghPhysicalMemory,FILE_MAP_READ,0,newAddress4,0x1000);
    //重新映射物理内存,得到当前线性地址所指向的PTE的物理地址内容
}

然后在根据内核对象的线性地址得到这个地址所指向的物理页,得到体现当前内核对象内容的页,其结构如下:
typedef struct {
ULONG Present;
ULONG WriteTable;
ULONG User;
ULONG WriteThru;
ULONG NoCache;
ULONG Accessed;
ULONG Dirty;
ULONG PageSize;
ULONG Global;
ULONG Available;
ULONG Pfn;
} PTE, *PPTE;
(注:我不能保证这个结构的正确性,但我们只会用到其中的两个值,对程序来说,这个结构是可以工作的,^_^)
代码如下:
ULONG CurrWriteTable;
ULONG NoCache;

void GetMustPar(DWORD objAddress)
{
    DWORD CurrAddress;
    CurrAddress = objAddress & 0xFFF;
    PPTE pte = (PPTE)(VOID *)((DWORD)NewmapPhy+CurrAddress);
    CurrWriteTable = pte->WriteTable;
    CurrNoCache = Pte->NoCache;
}

好了,我们现在想要得到的都已经得到了,下面需要做的是遍历进程,用每一个进程中的每一个句柄(呵呵,不是每一个句柄,
在Windows NT下,/Device/Tcp和/Device/Udp的句柄类型值为0x16,在Windows 2000下这个值为0x1A)的核心地址用上面所描
述的办法得到其PTE内容,得到其WriteTable值,如果与内核对象/Device/Tcp和/Device/Udp相等,那么这个句柄就有可能打开
了一个端口,再对这个句柄进行确认,就可以了.确认的代码如下:
typedef struct _TDI_CONNECTION_INFO {
ULONG State;
ULONG Event;
ULONG TransmittedTsdus;
ULONG ReceivedTsdus;
ULONG TransmissionErrors;
ULONG ReceiveErrors;
LARGE_INTEGER Throughput;
LARGE_INTEGER Delay;
ULONG SendBufferSize;
ULONG ReceiveBufferSize;
BOOLEAN Unreliable;
} TDI_CONNECTION_INFO, *PTDI_CONNECTION_INFO;

typedef struct _TDI_CONNECTION_INFORMATION {
LONG UserDataLength;
PVOID UserData;
LONG OptionsLength;
PVOID Options;
LONG RemoteAddressLength;
PVOID RemoteAddress;
} TDI_CONNECTION_INFORMATION, *PTDI_CONNECTION_INFORMATION;
(以上结构见tdi.h)

void GetOpenPort(DWORD dwProcessesID,USHORT Handle,int NoCache)
//dwProcessesID为进程标识ID
//Handle为进程打开的句柄,并且经过比较为/Device/Tcp或/Device/Udp类型
//NoCache为PTE结构中的一个值
{
    HANDLE hProc,DupHandle=NULL;
    HANDLE hEven=NULL;
    OVERLAPPED overlap;
    u_short openport;
    int i=0;
    char procName[256]={0};
    int portflag=0;

    overlap.Internal = 0;
    overlap.InternalHigh = 0;
    overlap.Offset = 0;
    overlap.OffsetHigh = 0;
    hEven=CreateEvent(0,1,0,0);
    overlap.hEvent = hEven;

    hProc = OpenProcess(PROCESS_DUP_HANDLE,
                        0,
                        dwProcessesID);
    if(hProc)
    {
        DuplicateHandle(hProc,
                        (HANDLE)Handle,
                        GetCurrentProcess(),
                        &DupHandle,
                        0,
                        FALSE,
                        2);
        CloseHandle( hProc );
        if(DupHandle)
        {
            TDI_CONNECTION_INFO     TdiConnInfo={0};
            TDI_CONNECTION_INFORMATION TdiConnInformation={0};
            DWORD dwRetu=0;    

            if(NoCache==0x2)
            {
                TdiConnInformation.RemoteAddressLength= 4;
                if(DeviceIoControl(DupHandle,0x210012,
                        &TdiConnInformation,sizeof(TdiConnInformation),
                            &TdiConnInfo,sizeof(TdiConnInfo),
                            0,&overlap))
                //进行TDI查询,得到连接的相关信息
                {
                    openport = ntohs((u_short)TdiConnInfo.ReceivedTsdus);
                    procname = GetProcName(dwProcessesID); //得到进程标识ID的进程名称
                    printf("PID = %4d ProcessName = %15s PORT = %4d/n",dwProcessesID,procName,openport);
                }
            }
            if(NoCache==0x1)
            {
                TdiConnInformation.RemoteAddressLength= 3;
                if(DeviceIoControl(DupHandle,0x210012,
                        &TdiConnInformation,sizeof(TdiConnInformation),
                            &TdiConnInfo,sizeof(TdiConnInfo),
                            0,&overlap))
                //进行TDI查询,得到连接的相关信息
                {
                    openport = ntohs((u_short)TdiConnInfo.ReceivedTsdus);
                    procname = GetProcName(dwProcessesID); //得到进程标识ID的进程名称
                    printf("PID = %4d ProcessName = %15s PORT = %4d/n",dwProcessesID,procName,openport);
                }
            }
        }
    }
    CloseHandle(hEven);
    CloseHandle(DupHandle);
}

以上是我对fport.exe的分析及其实现代码,演示程序可以从whitecell.org下载,如果你发现有问题,请通知我,^_^

参考:

fport.exe
Gary Nebbett<<Windows NT/2000 Native API Reference>>
WebCrazy<<小议Windows NT/2000分页机制>>
NTDDK

关于我们:

WSS(Whitecell Security Systems),一个非营利性民间技术组织,致力于各种系统安全技术的研究。坚持传统的hacker精神,追求技术的精纯。

WSS 主页:http://www.whitecell.org/
WSS 论坛:http://www.whitecell.org/forum/


Hello,

I've written an example for Windows XP that enumerates all open TCP and UDP ports and map them to the owning application. It uses a couple of undocumented functions like NETSTAT.EXE does.

Unfortunately, I don't have a time to write an article about this technique for Godeguru so if anybody want to write it, please feel free to use my example.


// portsenumxp.cpp (Windows XP)
//
// This example will show how you can enumerate
// all open TCP and UDP ports and map them to
// the owning application. It uses undocumented
// functions from iphlpapi.dll which were found
// by reversing netstat.exe.
//
//
// (c) 2002 Ashot Oganesyan K (ashot@protect-me.com)
// (c) 2002 SmartLine, Inc. (http://www.protect-me.com)

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <Iphlpapi.h>

typedef struct _MIB_TCPROW_EX
{
DWORD dwState; // MIB_TCP_STATE_*
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwRemoteAddr;
DWORD dwRemotePort;
DWORD dwProcessId;
} MIB_TCPROW_EX, *PMIB_TCPROW_EX;

typedef struct _MIB_TCPTABLE_EX
{
DWORD dwNumEntries;
MIB_TCPROW_EX table[ANY_SIZE];
} MIB_TCPTABLE_EX, *PMIB_TCPTABLE_EX;

typedef struct _MIB_UDPROW_EX
{
DWORD dwLocalAddr;
DWORD dwLocalPort;
DWORD dwProcessId;
} MIB_UDPROW_EX, *PMIB_UDPROW_EX;

typedef struct _MIB_UDPTABLE_EX
{
DWORD dwNumEntries;
MIB_UDPROW_EX table[ANY_SIZE];
} MIB_UDPTABLE_EX, *PMIB_UDPTABLE_EX;


/*
DWORD
WINAPI
AllocateAndGetTcpExTableFromStack(
OUT PMIB_TCPTABLE_EX *pTcpTableEx,
IN BOOL bOrder,
IN HANDLE hAllocHeap,
IN DWORD dwAllocFlags,
IN DWORD dwProtocolVersion; // 2 - TCP, 23 - TCPv6 (size of *pTcpTableEx must be 56!)
);
*/
typedef DWORD (WINAPI *PROCALLOCATEANDGETTCPEXTABLEFROMSTACK)(PMIB_TCPTABLE_EX*,BOOL,HANDLE,DWORD,DWORD);
PROCALLOCATEANDGETTCPEXTABLEFROMSTACK lpfnAllocateAndGetTcpExTableFromStack = NULL;

/*
DWORD
WINAPI
AllocateAndGetUdpExTableFromStack(
OUT PMIB_UDPTABLE_EX *pUdpTable,
IN BOOL bOrder,
IN HANDLE hAllocHeap,
IN DWORD dwAllocFlags,
IN DWORD dwProtocolVersion; // 2 - UDP, 23 - UDPv6 (size of *pUdpTable must be 28!)
);
*/
typedef DWORD (WINAPI *PROCALLOCATEANDGETUDPEXTABLEFROMSTACK)(PMIB_UDPTABLE_EX*,BOOL,HANDLE,DWORD,DWORD);
PROCALLOCATEANDGETUDPEXTABLEFROMSTACK lpfnAllocateAndGetUdpExTableFromStack = NULL;


BOOL LoadExIpHelperProcedures(void)
{
HMODULE hModule;

hModule = LoadLibrary(_T("iphlpapi.dll"));
if (hModule == NULL)
return FALSE;

// XP and later
lpfnAllocateAndGetTcpExTableFromStack = (PROCALLOCATEANDGETTCPEXTABLEFROMSTACK)GetProcAddress(hModule,"AllocateAndGetTcpExTableFromStack");
if (lpfnAllocateAndGetTcpExTableFromStack == NULL)
return FALSE;

// XP and later
lpfnAllocateAndGetUdpExTableFromStack = (PROCALLOCATEANDGETUDPEXTABLEFROMSTACK)GetProcAddress(hModule,"AllocateAndGetUdpExTableFromStack");
if (lpfnAllocateAndGetUdpExTableFromStack == NULL)
return FALSE;

return TRUE;
}

void main(void)
{
DWORD dwLastError,
dwSize;
PMIB_TCPTABLE_EX lpBuffer = NULL;
PMIB_UDPTABLE_EX lpBuffer1 = NULL;

if (!LoadExIpHelperProcedures())
return;

dwLastError = lpfnAllocateAndGetTcpExTableFromStack(&lpBuffer,TRUE,GetProcessHeap(),0,2);
if (dwLastError == NO_ERROR)
{
printf(_T("Local IP/tLocal Port/tRemote Ip/tRemote Port/tPID/n/n"));

for (dwSize = 0; dwSize < lpBuffer->dwNumEntries; dwSize++)
{
// Local IP Address
printf(_T("%d.%d.%d.%d/t"),LOBYTE(LOWORD(lpBuffer->table[dwSize].dwLocalAddr)),HIBYTE(LOWORD(lpBuffer->table[dwSize].dwLocalAddr)),LOBYTE(HIWORD(lpBuffer->table[dwSize].dwLocalAddr)),HIBYTE(HIWORD(lpBuffer->table[dwSize].dwLocalAddr)));

// Local Port
printf(_T("%d/t"),((lpBuffer->table[dwSize].dwLocalPort & 0xFF00) >> 8) + ((lpBuffer->table[dwSize].dwLocalPort & 0x00FF) << 8));

// Remote IP Address
printf(_T("%d.%d.%d.%d/t"),LOBYTE(LOWORD(lpBuffer->table[dwSize].dwRemoteAddr)),HIBYTE(LOWORD(lpBuffer->table[dwSize].dwRemoteAddr)),LOBYTE(HIWORD(lpBuffer->table[dwSize].dwRemoteAddr)),HIBYTE(HIWORD(lpBuffer->table[dwSize].dwRemoteAddr)));

// Remote Port
if (lpBuffer->table[dwSize].dwRemoteAddr)
printf(_T("%d/t"),((lpBuffer->table[dwSize].dwRemotePort & 0xFF00) >> 8) + ((lpBuffer->table[dwSize].dwRemotePort & 0x00FF) << 8));
else
printf(_T("0/t"));

// PID
printf(_T("%lu/n"),lpBuffer->table[dwSize].dwProcessId);
}
}

dwLastError = lpfnAllocateAndGetUdpExTableFromStack(&lpBuffer1,TRUE,GetProcessHeap(),0,2);
if (dwLastError == NO_ERROR)
{
printf(_T("Local IP/tLocal Port/tPID/n/n"));

for (dwSize = 0; dwSize < lpBuffer1->dwNumEntries; dwSize++)
{
// Local IP Address
printf(_T("%d.%d.%d.%d/t"),LOBYTE(LOWORD(lpBuffer1->table[dwSize].dwLocalAddr)),HIBYTE(LOWORD(lpBuffer1->table[dwSize].dwLocalAddr)),LOBYTE(HIWORD(lpBuffer1->table[dwSize].dwLocalAddr)),HIBYTE(HIWORD(lpBuffer1->table[dwSize].dwLocalAddr)));

// Local Port
printf(_T("%d/t"),((lpBuffer1->table[dwSize].dwLocalPort & 0xFF00) >> 8) + ((lpBuffer1->table[dwSize].dwLocalPort & 0x00FF) << 8));

// PID
printf(_T("%lu/n"),lpBuffer1->table[dwSize].dwProcessId);
}
}


if (lpBuffer)
HeapFree(GetProcessHeap(),0,lpBuffer);

if (lpBuffer1)
HeapFree(GetProcessHeap(),0,lpBuffer1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值