win32api/iphlpapi.go
starainrt 0f82ba044b
修正 Win32 封装语义并补齐关键结构体/进程测试覆盖
- 修正 WTS 会话相关类型、枚举与活动会话选择逻辑
- 对齐 FILE_ID_DESCRIPTOR 布局与 FILE_ID_TYPE 语义,修复 OpenFileById 调用前提
- 修正 user32/shell32/kernel32 部分 API 的返回值、参数个数与错误处理
- 完善剪贴板更新格式读取的缓冲区重试逻辑
- 补充常用进程、线程、调试、桌面与会话 helper
- 增加结构体布局、会话查询、剪贴板、CreateProcess 等回归测试
- 将默认 CreateProcess 相关测试切到 helper 进程,并保留显式开启的 cmd.exe 集成覆盖
2026-06-06 17:46:02 +08:00

351 lines
9.3 KiB
Go

package win32api
import (
"fmt"
"strings"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
func GetAdaptersAddresses(family, flags uint32) ([]AdapterAddressInfo, error) {
size := uint32(15 * 1024)
for i := 0; i < 4; i++ {
buf := make([]byte, size)
head := (*windows.IpAdapterAddresses)(unsafe.Pointer(&buf[0]))
err := windows.GetAdaptersAddresses(family, flags, 0, head, &size)
if err == nil {
return collectAdapterAddressInfo(head), nil
}
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_BUFFER_OVERFLOW {
continue
}
return nil, err
}
return nil, fmt.Errorf("GetAdaptersAddresses exceeded retry limit")
}
func GetIfTable2() ([]MIB_IF_ROW2, error) {
proc, err := getProcAddr("iphlpapi.dll", "GetIfTable2")
if err != nil {
return nil, err
}
var table *MIB_IF_TABLE2
r, _, _ := syscall.Syscall(proc, 1, uintptr(unsafe.Pointer(&table)), 0, 0)
if r != 0 {
return nil, syscall.Errno(r)
}
if table == nil {
return nil, fmt.Errorf("GetIfTable2 returned nil table")
}
defer FreeMibTable(unsafe.Pointer(table))
count := int(table.NumEntries)
rows := make([]MIB_IF_ROW2, 0, count)
rowSize := unsafe.Sizeof(table.Table[0])
base := uintptr(unsafe.Pointer(&table.Table[0]))
for i := 0; i < count; i++ {
row := (*MIB_IF_ROW2)(unsafe.Pointer(base + uintptr(i)*rowSize))
rows = append(rows, *row)
}
return rows, nil
}
func GetIfEntry2(row *MIB_IF_ROW2) error {
if row == nil {
return syscall.EINVAL
}
proc, err := getProcAddr("iphlpapi.dll", "GetIfEntry2")
if err != nil {
return err
}
r, _, _ := syscall.Syscall(proc, 1, uintptr(unsafe.Pointer(row)), 0, 0)
if r != 0 {
return syscall.Errno(r)
}
return nil
}
func FreeMibTable(memory unsafe.Pointer) {
if memory == nil {
return
}
proc, err := getProcAddr("iphlpapi.dll", "FreeMibTable")
if err != nil {
return
}
syscall.Syscall(proc, 1, uintptr(memory), 0, 0)
}
func GetExtendedTcp4Table(order bool, tableClass TCP_TABLE_CLASS) ([]MIB_TCPROW_OWNER_PID, error) {
if err := validateTCPOwnerPIDTableClass(tableClass); err != nil {
return nil, err
}
buf, err := getExtendedTCPTable(order, AF_INET, tableClass)
if err != nil {
return nil, err
}
if len(buf) == 0 {
return nil, nil
}
table := (*MIB_TCPTABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
count := int(table.NumEntries)
rows := make([]MIB_TCPROW_OWNER_PID, 0, count)
rowSize := unsafe.Sizeof(table.Table[0])
base := uintptr(unsafe.Pointer(&table.Table[0]))
for i := 0; i < count; i++ {
row := (*MIB_TCPROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
rows = append(rows, *row)
}
return rows, nil
}
func GetExtendedTcp6Table(order bool, tableClass TCP_TABLE_CLASS) ([]MIB_TCP6ROW_OWNER_PID, error) {
if err := validateTCPOwnerPIDTableClass(tableClass); err != nil {
return nil, err
}
buf, err := getExtendedTCPTable(order, AF_INET6, tableClass)
if err != nil {
return nil, err
}
if len(buf) == 0 {
return nil, nil
}
table := (*MIB_TCP6TABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
count := int(table.NumEntries)
rows := make([]MIB_TCP6ROW_OWNER_PID, 0, count)
rowSize := unsafe.Sizeof(table.Table[0])
base := uintptr(unsafe.Pointer(&table.Table[0]))
for i := 0; i < count; i++ {
row := (*MIB_TCP6ROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
rows = append(rows, *row)
}
return rows, nil
}
func GetExtendedUdp4Table(order bool, tableClass UDP_TABLE_CLASS) ([]MIB_UDPROW_OWNER_PID, error) {
if err := validateUDPOwnerPIDTableClass(tableClass); err != nil {
return nil, err
}
buf, err := getExtendedUDPTable(order, AF_INET, tableClass)
if err != nil {
return nil, err
}
if len(buf) == 0 {
return nil, nil
}
table := (*MIB_UDPTABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
count := int(table.NumEntries)
rows := make([]MIB_UDPROW_OWNER_PID, 0, count)
rowSize := unsafe.Sizeof(table.Table[0])
base := uintptr(unsafe.Pointer(&table.Table[0]))
for i := 0; i < count; i++ {
row := (*MIB_UDPROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
rows = append(rows, *row)
}
return rows, nil
}
func GetExtendedUdp6Table(order bool, tableClass UDP_TABLE_CLASS) ([]MIB_UDP6ROW_OWNER_PID, error) {
if err := validateUDPOwnerPIDTableClass(tableClass); err != nil {
return nil, err
}
buf, err := getExtendedUDPTable(order, AF_INET6, tableClass)
if err != nil {
return nil, err
}
if len(buf) == 0 {
return nil, nil
}
table := (*MIB_UDP6TABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
count := int(table.NumEntries)
rows := make([]MIB_UDP6ROW_OWNER_PID, 0, count)
rowSize := unsafe.Sizeof(table.Table[0])
base := uintptr(unsafe.Pointer(&table.Table[0]))
for i := 0; i < count; i++ {
row := (*MIB_UDP6ROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
rows = append(rows, *row)
}
return rows, nil
}
func (row MIB_TCPROW_OWNER_PID) LocalPortHost() uint16 {
return Ntohs(uint16(row.LocalPort))
}
func (row MIB_TCPROW_OWNER_PID) RemotePortHost() uint16 {
return Ntohs(uint16(row.RemotePort))
}
func (row MIB_TCP6ROW_OWNER_PID) LocalPortHost() uint16 {
return Ntohs(uint16(row.LocalPort))
}
func (row MIB_TCP6ROW_OWNER_PID) RemotePortHost() uint16 {
return Ntohs(uint16(row.RemotePort))
}
func (row MIB_UDPROW_OWNER_PID) LocalPortHost() uint16 {
return Ntohs(uint16(row.LocalPort))
}
func (row MIB_UDP6ROW_OWNER_PID) LocalPortHost() uint16 {
return Ntohs(uint16(row.LocalPort))
}
func validateTCPOwnerPIDTableClass(tableClass TCP_TABLE_CLASS) error {
switch tableClass {
case TCP_TABLE_OWNER_PID_LISTENER, TCP_TABLE_OWNER_PID_CONNECTIONS, TCP_TABLE_OWNER_PID_ALL:
return nil
default:
return fmt.Errorf("unsupported TCP owner-pid table class: %d", tableClass)
}
}
func validateUDPOwnerPIDTableClass(tableClass UDP_TABLE_CLASS) error {
if tableClass == UDP_TABLE_OWNER_PID {
return nil
}
return fmt.Errorf("unsupported UDP owner-pid table class: %d", tableClass)
}
func getExtendedTCPTable(order bool, family int, tableClass TCP_TABLE_CLASS) ([]byte, error) {
proc, err := getProcAddr("iphlpapi.dll", "GetExtendedTcpTable")
if err != nil {
return nil, err
}
return getExtendedIPTable(proc, order, family, uint32(tableClass))
}
func getExtendedUDPTable(order bool, family int, tableClass UDP_TABLE_CLASS) ([]byte, error) {
proc, err := getProcAddr("iphlpapi.dll", "GetExtendedUdpTable")
if err != nil {
return nil, err
}
return getExtendedIPTable(proc, order, family, uint32(tableClass))
}
func getExtendedIPTable(proc uintptr, order bool, family int, tableClass uint32) ([]byte, error) {
size := uint32(0)
r, _, _ := syscall.Syscall6(proc, 6,
0,
uintptr(unsafe.Pointer(&size)),
boolToUintptr(order),
uintptr(family),
uintptr(tableClass),
0,
)
if r != 0 && syscall.Errno(r) != syscall.ERROR_INSUFFICIENT_BUFFER {
return nil, syscall.Errno(r)
}
if size == 0 {
return nil, nil
}
for i := 0; i < 4; i++ {
buf := make([]byte, size)
r, _, _ = syscall.Syscall6(proc, 6,
uintptr(unsafe.Pointer(&buf[0])),
uintptr(unsafe.Pointer(&size)),
boolToUintptr(order),
uintptr(family),
uintptr(tableClass),
0,
)
if r == 0 {
return buf, nil
}
if syscall.Errno(r) != syscall.ERROR_INSUFFICIENT_BUFFER {
return nil, syscall.Errno(r)
}
}
return nil, fmt.Errorf("GetExtended IP table exceeded retry limit")
}
func boolToUintptr(v bool) uintptr {
if v {
return 1
}
return 0
}
func collectAdapterAddressInfo(head *windows.IpAdapterAddresses) []AdapterAddressInfo {
out := make([]AdapterAddressInfo, 0, 8)
for a := head; a != nil; a = a.Next {
item := AdapterAddressInfo{
IfIndex: a.IfIndex,
AdapterName: windows.BytePtrToString(a.AdapterName),
FriendlyName: windows.UTF16PtrToString(a.FriendlyName),
Description: windows.UTF16PtrToString(a.Description),
DNSSuffix: windows.UTF16PtrToString(a.DnsSuffix),
OperStatus: a.OperStatus,
Mtu: a.Mtu,
MACAddress: formatMACAddress(a.PhysicalAddress[:], a.PhysicalAddressLength),
PhysicalAddressLength: a.PhysicalAddressLength,
TransmitLinkSpeed: a.TransmitLinkSpeed,
ReceiveLinkSpeed: a.ReceiveLinkSpeed,
UnicastIPs: collectUnicastIPs(a.FirstUnicastAddress),
DNSServers: collectDNSServerIPs(a.FirstDnsServerAddress),
Gateways: collectGatewayIPs(a.FirstGatewayAddress),
}
out = append(out, item)
}
return out
}
func collectUnicastIPs(addr *windows.IpAdapterUnicastAddress) []string {
out := make([]string, 0, 4)
for ua := addr; ua != nil; ua = ua.Next {
ip := ua.Address.IP()
if ip == nil {
continue
}
out = append(out, ip.String())
}
return out
}
func collectDNSServerIPs(addr *windows.IpAdapterDnsServerAdapter) []string {
out := make([]string, 0, 2)
for cur := addr; cur != nil; cur = cur.Next {
ip := cur.Address.IP()
if ip == nil {
continue
}
out = append(out, ip.String())
}
return out
}
func collectGatewayIPs(addr *windows.IpAdapterGatewayAddress) []string {
out := make([]string, 0, 2)
for cur := addr; cur != nil; cur = cur.Next {
ip := cur.Address.IP()
if ip == nil {
continue
}
out = append(out, ip.String())
}
return out
}
func formatMACAddress(raw []byte, n uint32) string {
if n == 0 || len(raw) == 0 {
return ""
}
if int(n) > len(raw) {
n = uint32(len(raw))
}
var b strings.Builder
for i := 0; i < int(n); i++ {
if i > 0 {
b.WriteByte(':')
}
_, _ = fmt.Fprintf(&b, "%02X", raw[i])
}
return b.String()
}