ctypes 定义的 PROCESSENTRY32 在使用 ctypes.sizeof 时产生不正确的大小
ctypes-defined PROCESSENTRY32 yields incorrect size when using ctypes.sizeof
class PROCESSENTRY32(ctypes.Structure):
_pack_ = 8
_fields_ = [
("dwSize", ctypes.c_ulong),
("cntUsage", ctypes.c_ulong),
("th32ProcessID", ctypes.c_ulong),
("th32DefaultHeapID", ctypes.POINTER(ctypes.c_uint64)),
("th32ModuleID", ctypes.c_ulong),
("cntThreads", ctypes.c_ulong),
("th32ParentProcessID", ctypes.c_ulong),
("pcPriClassBase", ctypes.c_long),
("dwFlags", ctypes.c_ulong),
("szExeFile", ctypes.c_char*MAX_PATH)
]
这是我的结构定义;一些最初的评论是 with/without _pack_=8
结构没有显示正确的大小,_pack_=1
也没有显示正确的大小。我基于 Process32First
在调用后设置 ERROR_BAD_LENGTH
错误代码这一事实来解决这个大小问题;而且我看不出为什么它会是快照本身。
def process32_first(snapshot_handle, process_entry_pointer, _ctypes_configuration=(
("hSnapshot", (ctypes.c_voidp, True)),
("lppe", (ctypes.POINTER(PROCESSENTRY32), True))
)):
process32_first = import_winapi_function(
"kernel32",
"Process32First",
argtypes_from_ctypes_configuration(_ctypes_configuration),
ctypes.c_int
)
return process32_first(
ctypes_configuration_param_select(_ctypes_configuration, 0) or snapshot_handle,
ctypes_configuration_param_select(_ctypes_configuration, 1) or process_entry_pointer
)
ctypes_configuration_param_select
只需选择 _ctypes_configuration
中的预设值(如果它不是 True)或采用用户给定的输入。在此之前,在 import_winapi_call
中,参数和 return 类型分别设置为 ctypes.c_voidp
和 ctypes.POINTER(PROCESSENTRY32)
到 WinAPI 定义。 return 类型是 ctypes.c_int
表示 BOOL
.
if __name__ == "__main__":
snapshot = debug_fn(create_toolhelp32_snapshot, 0x2, 0)
pe = PROCESSENTRY32()
pe.dwSize = ctypes.sizeof(PROCESSENTRY32)
debug_fn(process32_first, snapshot, ctypes.pointer(pe))
下面是函数的实际用法和结构体的定义; debug_fn
只是一个包装器,它将实际函数调用夹在两个 GetLastError 调用之间,代表错误代码之前和之后。
总而言之,debug_fn(process32_first, ...)
调用产生了一个 post 调用错误代码 24/ERROR_BAD_LENGTH,我将其归因于 ctypes 错误调整 PROCESSENTRY32
/pe
结构.
我该如何解决这个问题?
编辑:
这里是 ctypes_configuration_param_select
、argtypes_from_ctypes_configuration
、import_winapi_function
、WinAPIFunction
和 debug_fn
:
def argtypes_from_ctypes_configuration(ctypes_configuration):
return tuple(v[0] for _, v in ctypes_configuration)
def ctypes_configuration_param_select(ctypes_configuration, idx):
return ctypes_configuration[idx][1][1] if ctypes_configuration[idx][1][1] is not True else False
import_winapi_function
定义:
def import_winapi_function(namespace, name, argtypes, restype, is_unicode=UNICODE):
gle = function_cache['kernel32.GetLastError']
sle = function_cache['kernel32.SetLastError']
gpa = function_cache['kernel32.GetProcAddress']
gmh = function_cache['kernel32.GetModuleHandleA']
name += "W" if is_unicode else "A"
qual_fn_name = f"{namespace}.{name}"
if qual_fn_name in function_cache:
return function_cache[qual_fn_name]
namespace_handle = gmh(create_string(namespace, False))
if gle() == 127:
sle(0)
raise LookupError(f"Module: {namespace} doesn't exist.")
function_handle = gpa(namespace_handle, create_string(name, False))
if gle() != 127:
function_cache[qual_fn_name] = WinAPIFunction(namespace, name, function_handle, restype, argtypes)
return function_cache[qual_fn_name]
sle(0)
name = name[:-1]
qual_fn_name = qual_fn_name[:-1]
if qual_fn_name in function_cache:
return function_cache[qual_fn_name]
function_handle = gpa(namespace_handle, create_string(name, False))
if gle() == 127:
sle(0)
raise LookupError(f"Function: {namespace}.{name} doesn't exist.")
function_cache[qual_fn_name] = WinAPIFunction(namespace, name, function_handle, restype, argtypes)
return function_cache[qual_fn_name]
WinAPI函数定义:
class WinAPIFunction(object):
def __init__(self, module, name, handle, restype, argtypes):
self.module = module
self.name = name
self.handle = handle
self.argtypes = argtypes
self.restype = restype
def __repr__(self):
return f"<{self.module}.{self.name} @ {hex(self.handle)}>"
__str__ = __repr__
def __call__(self, *args):
return ctypes.WINFUNCTYPE(self.restype, *self.argtypes)(self.handle)(*args)
和debug_fn
:
def debug_fn(fn, *args, **kwargs):
gle = get_last_error # changeable
print(f"\n{fn}:")
print(f"\tget_last_error: {gle()}")
res = fn(*args, **kwargs)
print(f"\tret. code: {res}")
print(f"\tget_last_error: {gle()}\n")
return res
如果你想复制我的代码;那么您只需要 function_cache
以及上述代码片段:
MAX_PATH = 260
function_cache = {
"kernel32.GetProcAddress": ctypes.windll.kernel32.GetProcAddress,
"kernel32.SetLastError": ctypes.windll.kernel32.SetLastError,
"kernel32.GetModuleHandleA": ctypes.windll.kernel32.GetModuleHandleA,
"kernel32.GetLastError": ctypes.windll.kernel32.GetLastError
} # primitive definitions, not WinAPIFunction instances
function_cache['kernel32.GetProcAddress'].argtypes = (
ctypes.c_voidp,
ctypes.POINTER(ctypes.c_char)
)
function_cache['kernel32.GetProcAddress'].restype = ctypes.c_voidp
function_cache['kernel32.SetLastError'].argtypes = (
ctypes.c_ulong,
)
function_cache['kernel32.SetLastError'].restype = None
function_cache['kernel32.GetModuleHandleA'].argtypes = (
ctypes.POINTER(ctypes.c_char),
)
function_cache['kernel32.GetModuleHandleA'].restype = ctypes.c_voidp
function_cache['kernel32.GetLastError'].argtypes = ()
function_cache['kernel32.GetLastError'].restype = ctypes.c_ulong
UNICODE
在我的环境中设置为 True。
结构的 szExeFile
成员应该是 TCHAR
(或者最好是 WCHAR
,正如我在其他地方被告知的那样)而不是 CHAR
此处由 PROCESSENTRY definition and the data types(参考 CHAR
)定义。
class PROCESSENTRY32(ctypes.Structure):
_pack_ = 8
_fields_ = [
("dwSize", ctypes.c_ulong),
("cntUsage", ctypes.c_ulong),
("th32ProcessID", ctypes.c_ulong),
("th32DefaultHeapID", ctypes.POINTER(ctypes.c_uint64)),
("th32ModuleID", ctypes.c_ulong),
("cntThreads", ctypes.c_ulong),
("th32ParentProcessID", ctypes.c_ulong),
("pcPriClassBase", ctypes.c_long),
("dwFlags", ctypes.c_ulong),
("szExeFile", ctypes.c_char*MAX_PATH)
]
这是我的结构定义;一些最初的评论是 with/without _pack_=8
结构没有显示正确的大小,_pack_=1
也没有显示正确的大小。我基于 Process32First
在调用后设置 ERROR_BAD_LENGTH
错误代码这一事实来解决这个大小问题;而且我看不出为什么它会是快照本身。
def process32_first(snapshot_handle, process_entry_pointer, _ctypes_configuration=(
("hSnapshot", (ctypes.c_voidp, True)),
("lppe", (ctypes.POINTER(PROCESSENTRY32), True))
)):
process32_first = import_winapi_function(
"kernel32",
"Process32First",
argtypes_from_ctypes_configuration(_ctypes_configuration),
ctypes.c_int
)
return process32_first(
ctypes_configuration_param_select(_ctypes_configuration, 0) or snapshot_handle,
ctypes_configuration_param_select(_ctypes_configuration, 1) or process_entry_pointer
)
ctypes_configuration_param_select
只需选择 _ctypes_configuration
中的预设值(如果它不是 True)或采用用户给定的输入。在此之前,在 import_winapi_call
中,参数和 return 类型分别设置为 ctypes.c_voidp
和 ctypes.POINTER(PROCESSENTRY32)
到 WinAPI 定义。 return 类型是 ctypes.c_int
表示 BOOL
.
if __name__ == "__main__":
snapshot = debug_fn(create_toolhelp32_snapshot, 0x2, 0)
pe = PROCESSENTRY32()
pe.dwSize = ctypes.sizeof(PROCESSENTRY32)
debug_fn(process32_first, snapshot, ctypes.pointer(pe))
下面是函数的实际用法和结构体的定义; debug_fn
只是一个包装器,它将实际函数调用夹在两个 GetLastError 调用之间,代表错误代码之前和之后。
总而言之,debug_fn(process32_first, ...)
调用产生了一个 post 调用错误代码 24/ERROR_BAD_LENGTH,我将其归因于 ctypes 错误调整 PROCESSENTRY32
/pe
结构.
我该如何解决这个问题?
编辑:
这里是 ctypes_configuration_param_select
、argtypes_from_ctypes_configuration
、import_winapi_function
、WinAPIFunction
和 debug_fn
:
def argtypes_from_ctypes_configuration(ctypes_configuration):
return tuple(v[0] for _, v in ctypes_configuration)
def ctypes_configuration_param_select(ctypes_configuration, idx):
return ctypes_configuration[idx][1][1] if ctypes_configuration[idx][1][1] is not True else False
import_winapi_function
定义:
def import_winapi_function(namespace, name, argtypes, restype, is_unicode=UNICODE):
gle = function_cache['kernel32.GetLastError']
sle = function_cache['kernel32.SetLastError']
gpa = function_cache['kernel32.GetProcAddress']
gmh = function_cache['kernel32.GetModuleHandleA']
name += "W" if is_unicode else "A"
qual_fn_name = f"{namespace}.{name}"
if qual_fn_name in function_cache:
return function_cache[qual_fn_name]
namespace_handle = gmh(create_string(namespace, False))
if gle() == 127:
sle(0)
raise LookupError(f"Module: {namespace} doesn't exist.")
function_handle = gpa(namespace_handle, create_string(name, False))
if gle() != 127:
function_cache[qual_fn_name] = WinAPIFunction(namespace, name, function_handle, restype, argtypes)
return function_cache[qual_fn_name]
sle(0)
name = name[:-1]
qual_fn_name = qual_fn_name[:-1]
if qual_fn_name in function_cache:
return function_cache[qual_fn_name]
function_handle = gpa(namespace_handle, create_string(name, False))
if gle() == 127:
sle(0)
raise LookupError(f"Function: {namespace}.{name} doesn't exist.")
function_cache[qual_fn_name] = WinAPIFunction(namespace, name, function_handle, restype, argtypes)
return function_cache[qual_fn_name]
WinAPI函数定义:
class WinAPIFunction(object):
def __init__(self, module, name, handle, restype, argtypes):
self.module = module
self.name = name
self.handle = handle
self.argtypes = argtypes
self.restype = restype
def __repr__(self):
return f"<{self.module}.{self.name} @ {hex(self.handle)}>"
__str__ = __repr__
def __call__(self, *args):
return ctypes.WINFUNCTYPE(self.restype, *self.argtypes)(self.handle)(*args)
和debug_fn
:
def debug_fn(fn, *args, **kwargs):
gle = get_last_error # changeable
print(f"\n{fn}:")
print(f"\tget_last_error: {gle()}")
res = fn(*args, **kwargs)
print(f"\tret. code: {res}")
print(f"\tget_last_error: {gle()}\n")
return res
如果你想复制我的代码;那么您只需要 function_cache
以及上述代码片段:
MAX_PATH = 260
function_cache = {
"kernel32.GetProcAddress": ctypes.windll.kernel32.GetProcAddress,
"kernel32.SetLastError": ctypes.windll.kernel32.SetLastError,
"kernel32.GetModuleHandleA": ctypes.windll.kernel32.GetModuleHandleA,
"kernel32.GetLastError": ctypes.windll.kernel32.GetLastError
} # primitive definitions, not WinAPIFunction instances
function_cache['kernel32.GetProcAddress'].argtypes = (
ctypes.c_voidp,
ctypes.POINTER(ctypes.c_char)
)
function_cache['kernel32.GetProcAddress'].restype = ctypes.c_voidp
function_cache['kernel32.SetLastError'].argtypes = (
ctypes.c_ulong,
)
function_cache['kernel32.SetLastError'].restype = None
function_cache['kernel32.GetModuleHandleA'].argtypes = (
ctypes.POINTER(ctypes.c_char),
)
function_cache['kernel32.GetModuleHandleA'].restype = ctypes.c_voidp
function_cache['kernel32.GetLastError'].argtypes = ()
function_cache['kernel32.GetLastError'].restype = ctypes.c_ulong
UNICODE
在我的环境中设置为 True。
结构的 szExeFile
成员应该是 TCHAR
(或者最好是 WCHAR
,正如我在其他地方被告知的那样)而不是 CHAR
此处由 PROCESSENTRY definition and the data types(参考 CHAR
)定义。