Python崩溃的字段访问ctypes

时间:2016-07-24 21:27:36

标签: winapi ctypes python-3.5

我正在使用ctypes模块来调用GetTcpTable2。 我一直在慢慢地将C ++中的示例here转换为Python;但是在现场访问期间我遇到了崩溃。

if __name__ == "__main__":
    ptcp_table = POINTER(MIB_TCPTABLE2)()
    ptcp_table = cast(create_string_buffer(sizeof(MIB_TCPTABLE2)),
                      POINTER(MIB_TCPTABLE2))
    ip_addr = in_addr()
    size = c_ulong(sizeof(MIB_TCPTABLE2))
    retval = GetTcpTable2(ptcp_table, byref(size), TRUE)
    if retval == ERROR_INSUFFICIENT_BUFFER:
        ptcp_table = cast(create_string_buffer(size.value),
                          POINTER(MIB_TCPTABLE2))
        if not ptcp_table:
            #throw error
            pass

    retval = GetTcpTable2(ptcp_table, byref(size), TRUE)
    if retval == NO_ERROR:
        print("Entries %d" % ptcp_table[0].dwNumEntries)
        for i in range(0, ptcp_table[0].dwNumEntries):
            print(ptcp_table[0].table[i])
            #ip_addr.S_un.S_addr = ptcp_table[0].table[i].dwLocalAddr
            #ip_addr_string = inet_nota(ip_addr)
            #print(ip_addr_string)
            #print(string_at(ip_addr_string))

尝试访问dwLocalAddr以外的table[i]时崩溃。

ptcp_table[0].table[i].dwLocalAddr

然而,只打印ptcp_table[0].table[i]时它不会崩溃。 我已经尝试过打印和访问其他领域;但是Python只是崩溃了。

以下是我的结构定义:

class MIB_TCPROW2(Structure):
    _fields_ = [
        ("dwState", c_ulong),
        ("dwLocalAddr", c_ulong),
        ("dwLocalPort", c_ulong),
        ("dwRemoteAddr", c_ulong),
        ("dwRemotePort", c_ulong),
        ("dwOwningPid", c_ulong),
        ("dwOffloadState", c_int)
    ]


class MIB_TCPTABLE2(Structure):
    _fields_ = [
        ("dwNumEntries", c_ulong),
        ("table", POINTER(MIB_TCPROW2))
    ]

GetTcpTable2的定义:

GetTcpTable2 = windll.iphlpapi.GetTcpTable2
GetTcpTable2.argtypes = [POINTER(MIB_TCPTABLE2), POINTER(c_ulong), c_char]
GetTcpTable2.restype = c_ulong

我在MIB_TCPTABLE2结构的定义中有一个小小的预感;文档说table是一个MIB_TCPROW2大小ANY_SIZE的数组;并且进一步检查ANY_SIZE是否为1来检查iphlpapi.h文件。我知道POINTER(MIB_TCPROW2)的大小不等于MIB_TCPROW2的大小。

2 个答案:

答案 0 :(得分:1)

我查看了围绕结构内部的变长字段的其他ctypes问题,并找到answer,建议使用工厂方法生成类定义。

def MIB_TCPTABLE2_FACTORY(size):
    class MIB_TCPTABLE2(Structure):
        _fields_ = [
            ("dwNumEntries", c_ulong),
            ("table", MIB_TCPROW2 * size)
        ]
    return MIB_TCPTABLE2

我知道从size返回的GetTcpTable2来创建新类型。然后,我要做的就是更改argtypes GetTcpTable2以接受void *

GetTcpTable2.argtypes = [c_void_p, POINTER(c_ulong), c_char]

答案 1 :(得分:0)

我就是这样解决的。我首先通过传入以下参数来获得所需的大小:

ret = windll.iphlpapi.GetTcpTable2(None, byref(tcp_table_size), True)

注意 None 是第一个参数,当传入 ctypes windows 函数时相当于 NULL。然后我定义了 MIB_TCPTABLE2 类,并在其中传递了第一次调用 GetTcpTable2 返回的大小:

class MIB_TCPTABLE2(Structure):
    _fields_ = [
        ("dwNumEntries", c_ulong),
        ("table", MIB_TCPROW2 * tcp_table_size.value),
    ]

接下来,我创建了该结构的一个实例并再次调用 GetTcpTable2 并传入新创建的结构:

tcp_table = MIB_TCPTABL2()

ret = windll.iphlpapi.GetTcpTable2(byref(tcp_table), byref(tcp_table_size), True)

示例代码如下:

from ctypes import *
import socket
import struct

NO_ERROR = 0
ERROR_INSUFFICIENT_BUFFER = 122

TcpConnectionOffloadStateInHost = 0
TcpConnectionOffloadStateOffloading = 1
TcpConnectionOffloadStateOffloaded = 2
TcpConnectionOffloadStateUploading = 3
TcpConnectionOffloadStateMax = 4

class MIB_TCPROW2(Structure):
    _fields_ = [
        ("dwState", c_ulong),
        ("dwLocalAddr", c_ulong),
        ("dwLocalPort", c_ulong),
        ("dwRemoteAddr", c_ulong),
        ("dwRemotePort", c_ulong),
        ("dwOwningPid", c_ulong),
        ("dwOffloadState", c_ulong),
    ]
    
def main():
    windll.iphlpapi.GetTcpTable2.argtypes = [c_void_p, POINTER(c_ulong), c_bool]
    tcp_table_size = c_ulong()
        
    ret = windll.iphlpapi.GetTcpTable2(None, byref(tcp_table_size), True)
    if ret == ERROR_INSUFFICIENT_BUFFER:
        class MIB_TCPTABLE2(Structure):
            _fields_ = [
                ("dwNumEntries", c_ulong),
                ("table", MIB_TCPROW2 * tcp_table_size.value),
            ]
        
        tcp_table = MIB_TCPTABLE2()
        
        ret = windll.iphlpapi.GetTcpTable2(byref(tcp_table), byref(tcp_table_size), True)
        if ret != NO_ERROR:
            print("ERROR: GetTcpTable2() failed, error = " + str(ret))
        else:
            for i in range(tcp_table.dwNumEntries):
                dest_ip = socket.inet_ntoa(struct.pack('<L', tcp_table.table[i].dwRemoteAddr))
                print("PID: " + str(tcp_table.table[i].dwOwningPid) + ", DEST IP: " + dest_ip)
    
    
if __name__ == "__main__":
    main()