在 Windows 的“netstat”实用程序的较新版本中,有一个命令行选项“-q”,可显示已绑定但未连接的套接字(即**“bound state”**)。
“bound state” 表示套接字已经绑定到了本地地址和端口但未连接,“bound state” 是套接字的一种暂时状态,目前只在Win 10系统中发现
有关此类套接字的输出如下所示
TCP 0.0.0.0:4294 0.0.0.0:0 BOUND
TCP 0.0.0.0:8054 0.0.0.0:0 BOUND
TCP 0.0.0.0:8840 0.0.0.0:0 BOUND
Windows 目前没有任何记录或支持的用于查询绑定连接的 API。唯一的选择是使用这两个未记录的函数:
ULONG WINAPI InternalGetBoundTcpEndpointTable(
_Out_ PMIB_TCPTABLE2* BoundTcpTable,
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
ULONG WINAPI InternalGetBoundTcp6EndpointTable(
_Out_ PMIB_TCP6TABLE2* BoundTcp6Table,
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
下面代码能够查询到处于bound状态的网络连接信息,包括本地ip、本地端口、远程ip、远程端口、进程id和套接字的状态。
静态加载:
#pragma comment(lib, "iphlpapi.lib")
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <windows.h>
#include <stdio.h>
ULONG WINAPI InternalGetBoundTcpEndpointTable(
_Out_ PMIB_TCPTABLE2* BoundTcpTable,
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
ULONG WINAPI InternalGetBoundTcp6EndpointTable(
_Out_ PMIB_TCP6TABLE2* BoundTcp6Table,
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
void main()
{
PMIB_TCPTABLE2 boundTcpTable;
PMIB_TCP6TABLE2 boundTcp6Table;
if (InternalGetBoundTcpEndpointTable(&boundTcpTable, GetProcessHeap(), 0) == ERROR_SUCCESS)
{
for (ULONG i = 0; i < boundTcpTable->dwNumEntries; i++)
{
printf("%lu: %lu\n",
boundTcpTable->table[i].dwOwningPid,
ntohs((u_short)boundTcpTable->table[i].dwLocalPort));
}
HeapFree(GetProcessHeap(), 0, boundTcpTable);
}
if (InternalGetBoundTcp6EndpointTable(&boundTcp6Table, GetProcessHeap(), 0) == ERROR_SUCCESS)
{
for (ULONG i = 0; i < boundTcp6Table->dwNumEntries; i++)
{
printf("%lu: %lu\n",
boundTcp6Table->table[i].dwOwningPid,
ntohs((u_short)boundTcp6Table->table[i].dwLocalPort));
}
HeapFree(GetProcessHeap(), 0, boundTcp6Table);
}
}
动态加载:
#include <windows.h>
#include <stdio.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
// 定义函数指针类型,用于存储加载的函数地址
typedef ULONG (WINAPI *LPFN_InternalGetBoundTcpEndpointTable)(
_Out_ PMIB_TCPTABLE2* BoundTcpTable,
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
typedef ULONG (WINAPI *LPFN_InternalGetBoundTcp6EndpointTable)(
_Out_ PMIB_TCP6TABLE2* BoundTcp6Table,
_In_ PVOID HeapHandle,
_In_opt_ ULONG HeapFlags
);
int main() {
// 加载 IPHLPAPI.DLL 库
HMODULE hIPHLPAPIDLL = LoadLibrary(TEXT("IPHLPAPI.DLL"));
if (hIPHLPAPIDLL == NULL) {
printf("Failed to load IPHLPAPI.DLL\n");
return 1;
}
// 获取函数地址
LPFN_InternalGetBoundTcpEndpointTable pfnInternalGetBoundTcpEndpointTable =
(LPFN_InternalGetBoundTcpEndpointTable)GetProcAddress(hIPHLPAPIDLL, "InternalGetBoundTcpEndpointTable");
LPFN_InternalGetBoundTcp6EndpointTable pfnInternalGetBoundTcp6EndpointTable =
(LPFN_InternalGetBoundTcp6EndpointTable)GetProcAddress(hIPHLPAPIDLL, "InternalGetBoundTcp6EndpointTable");
if (pfnInternalGetBoundTcpEndpointTable == NULL || pfnInternalGetBoundTcp6EndpointTable == NULL) {
printf("Failed to get function addresses\n");
FreeLibrary(hIPHLPAPIDLL); // 释放库
return 1;
}
PMIB_TCPTABLE2 boundTcpTable;
PMIB_TCP6TABLE2 boundTcp6Table;
// 使用加载的函数
if (pfnInternalGetBoundTcpEndpointTable(&boundTcpTable, GetProcessHeap(), 0) == ERROR_SUCCESS) {
for (ULONG i = 0; i < boundTcpTable->dwNumEntries; i++) {
printf("%lu: %lu\n",
boundTcpTable->table[i].dwOwningPid,
boundTcpTable->table[i].dwLocalPort);
}
HeapFree(GetProcessHeap(), 0, boundTcpTable);
}
if (pfnInternalGetBoundTcp6EndpointTable(&boundTcp6Table, GetProcessHeap(), 0) == ERROR_SUCCESS) {
for (ULONG i = 0; i < boundTcp6Table->dwNumEntries; i++) {
printf("%lu: %lu\n",
boundTcp6Table->table[i].dwOwningPid,
boundTcp6Table->table[i].dwLocalPort);
}
HeapFree(GetProcessHeap(), 0, boundTcp6Table);
}
FreeLibrary(hIPHLPAPIDLL); // 释放库
return 0;
}