Определяемый ctypes PROCESSENTRY32 выдает неправильный размер при использовании ctypes.sizeof - PullRequest
0 голосов
/ 02 сентября 2018
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)
            ]

Вот мое определение структуры; некоторые начальные замечания заключаются в том, что с / без _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, если оно не истинно, либо принимает пользовательский ввод. До этого в import_winapi_call аргументы и возвращаемые типы задаются как ctypes.c_voidp и ctypes.POINTER(PROCESSENTRY32) соответственно для определения WinAPI. Возвращаемый тип - 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, ...) дает код ошибки после вызова 24 / ERROR_BAD_LENGTH, и я пытаюсь объяснить, что типы не соответствуют типу PROCESSENTRY32 / pe.

Как я могу это исправить?

EDIT:

Вот 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]

Определение WinAPIFunction:

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 в моей среде.

1 Ответ

0 голосов
/ 02 сентября 2018

Элемент szExeFile структуры должен быть TCHAR (или, предпочтительно, WCHAR, как мне сказали в другом месте), а не CHAR, как определено в PROCESSENTRY * 1006. * определение и типы данных (см. CHAR) здесь.

...