Как создать структуры Python ctypes для структур PACKAGE_ID и PACKAGE_INFO в MS Windows? - PullRequest
1 голос
/ 03 июля 2019

Я пытаюсь перевести этот SO-код ответа на Python (и немного от другого ответа там) с помощью официальной документации для PACKAGE_ID и PACKAGE_INFO .

Я получаю полные имена пакетов по GetPackageFullName, но некоторые из моих структур сформированы не так хорошо, как OpenPackageInfoByFullName дает мне символы Юникода, которые являются для меня бредом. Также PackageIdFromFullName возвращает ERROR_INSUFFICIENT_BUFFER при вызове.

РЕДАКТИРОВАТЬ : Как заметил Пол Корнелиус , в моем коде были проблемы с PACKAGE_INFO_REFERENCE. Теперь у меня проблема с преобразованием буфера, который я получил, или я что-то испортил до этого. Я предоставил код для запуска в Windows 10, приложение Edge или Store должно быть открыто до запуска (или открыть другое приложение и изменить жестко закодированный текст в коде). Вывод показывает, что packageName (в качестве поля примера) имеет значение None:

import ctypes
import ctypes.wintypes

from win32api import OpenProcess, CloseHandle
from win32con import PROCESS_QUERY_INFORMATION, PROCESS_VM_READ
from win32gui import EnumChildWindows, EnumWindows
from win32process import GetWindowThreadProcessId

ERROR_SUCCESS = 0x0
ERROR_INSUFFICIENT_BUFFER = 0x7A
PACKAGE_FILTER_ALL_LOADED = 0x00000000
PACKAGE_FILTER_HEAD = 0x00000010
PACKAGE_INFORMATION_FULL = 0x00000100
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000


class PACKAGE_INFO_REFERENCE(ctypes.Structure):

    _fields_ = [("reserved", ctypes.c_void_p)]


class PACKAGE_SUBVERSION(ctypes.Structure):

    _fields_ = [
        ("Revision", ctypes.wintypes.ATOM),
        ("Build", ctypes.wintypes.ATOM),
        ("Minor", ctypes.wintypes.ATOM),
        ("Major", ctypes.wintypes.ATOM),
    ]


class PACKAGE_VERSION(ctypes.Union):

    _fields_ = [("Version", ctypes.c_uint64), ("DUMMYSTRUCTNAME", PACKAGE_SUBVERSION)]


class PACKAGE_ID(ctypes.Union):

    _fields_ = [
        ("reserved", ctypes.c_uint32),
        ("processorArchitecture", ctypes.c_uint32),
        ("version", PACKAGE_VERSION),
        # ("VersionRevision", ctypes.wintypes.ATOM),
        # ("VersionBuild", ctypes.wintypes.ATOM),
        # ("VersionMinor", ctypes.wintypes.ATOM),
        # ("VersionMajor", ctypes.wintypes.ATOM),
        ("name", ctypes.c_wchar_p),
        ("publisher", ctypes.c_wchar_p),
        ("resourceId", ctypes.c_wchar_p),
        ("publisherId", ctypes.c_wchar_p),
    ]


class PACKAGE_INFO(ctypes.Union):

    _fields_ = [
        ("reserved", ctypes.c_uint32),
        ("flags", ctypes.c_uint32),
        ("path", ctypes.c_wchar_p),
        ("packageFullName", ctypes.c_wchar_p),
        ("packageFamilyName", ctypes.c_wchar_p),
        ("packageId", PACKAGE_ID),
    ]


def append_to_collection(element, collection):
    collection.append(element)
    return True


def get_children(hwnd):
    children = []
    try:
        EnumChildWindows(hwnd, append_to_collection, children)
    except:
        pass
    return children


def package_full_name_from_handle(handle):
    length = ctypes.c_uint()
    retval = ctypes.windll.kernel32.GetPackageFullName(
        handle, ctypes.byref(length), None
    )
    assert retval == ERROR_INSUFFICIENT_BUFFER

    full_name = ctypes.create_unicode_buffer(length.value + 1)
    retval = ctypes.windll.kernel32.GetPackageFullName(
        handle, ctypes.byref(length), full_name
    )
    assert retval == ERROR_SUCCESS

    return full_name


def package_path_from_full_name(full_name):
    length = ctypes.c_uint()
    retval = ctypes.windll.kernel32.GetPackagePathByFullName(
        ctypes.byref(full_name), ctypes.byref(length), None
    )
    assert retval == ERROR_INSUFFICIENT_BUFFER

    package_path = ctypes.create_unicode_buffer(length.value)
    retval = ctypes.windll.kernel32.GetPackagePathByFullName(
        ctypes.byref(full_name), ctypes.byref(length), ctypes.byref(package_path)
    )
    assert retval == ERROR_SUCCESS

    return package_path


def package_family_name_from_full_name(full_name):
    length = ctypes.c_uint()
    retval = ctypes.windll.kernel32.PackageFamilyNameFromFullName(
        ctypes.byref(full_name), ctypes.byref(length), None
    )
    assert retval == ERROR_INSUFFICIENT_BUFFER

    family_name = ctypes.create_unicode_buffer(length.value)
    retval = ctypes.windll.kernel32.PackageFamilyNameFromFullName(
        ctypes.byref(full_name), ctypes.byref(length), ctypes.byref(family_name)
    )
    assert retval == ERROR_SUCCESS

    return family_name


def package_info_reference_from_full_name(full_name):
    package_info_reference = ctypes.pointer(PACKAGE_INFO_REFERENCE())
    retval = ctypes.windll.kernel32.OpenPackageInfoByFullName(
        ctypes.byref(full_name), 0, ctypes.byref(package_info_reference)
    )
    assert retval == ERROR_SUCCESS

    return package_info_reference


def package_info_buffer_from_reference(package_info_reference):
    length = ctypes.c_uint(0)
    count = ctypes.c_uint()

    retval = ctypes.windll.kernel32.GetPackageInfo(
        package_info_reference,
        PACKAGE_FILTER_HEAD,
        ctypes.byref(length),
        None,
        ctypes.byref(count),
    )
    assert retval == ERROR_INSUFFICIENT_BUFFER

    buffer = ctypes.create_string_buffer(length.value)
    retval = ctypes.windll.kernel32.GetPackageInfo(
        package_info_reference,
        PACKAGE_FILTER_HEAD,
        ctypes.byref(length),
        ctypes.byref(buffer),
        ctypes.byref(count),
    )
    assert retval == ERROR_SUCCESS

    return buffer


def get_package(hwnd):
    hprocess = None
    _, pid = GetWindowThreadProcessId(hwnd)
    children = get_children(hwnd)
    for child in children:
        _, child_pid = GetWindowThreadProcessId(child)
        if child_pid != pid:
            # hprocess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, child_pid)
            hprocess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, child_pid)
            break

    if hprocess is None:
        return

    full_name = package_full_name_from_handle(hprocess.handle)
    if not (
        "Microsoft.MicrosoftEdge" in full_name.value
        or "Microsoft.WindowsStore" in full_name.value
    ):
        return None

    print("\n      full name:", full_name.value)
    package_path = package_path_from_full_name(full_name)
    print("   package path:", package_path.value)
    family_name = package_family_name_from_full_name(full_name)
    print("    family name:", family_name.value)
    package_info_reference = package_info_reference_from_full_name(full_name)
    print(" info reference:", package_info_reference.contents.reserved)

    package_info_buffer = package_info_buffer_from_reference(package_info_reference)
    package_info = PACKAGE_INFO()
    ctypes.memmove(
        ctypes.addressof(package_info), package_info_buffer, ctypes.sizeof(package_info)
    )
    print("packageFullName:", package_info.packageFullName)

    CloseHandle(hprocess)
    ctypes.windll.kernel32.ClosePackageInfo(package_info_reference)

def get_windows():
    hwnds = []
    EnumWindows(append_to_collection, hwnds)
    return hwnds


if __name__ == "__main__":
    for hwnd in get_windows():
        get_package(hwnd)

И вывод:

(venv) C:\dev\examples>python uwp.py

      full name: Microsoft.MicrosoftEdge_44.17763.1.0_neutral__8wekyb3d8bbwe
   package path: C:\Windows\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe
    family name: Microsoft.MicrosoftEdge_8wekyb3d8bbwe
 info reference: 4128769
packageFullName: None

      full name: Microsoft.WindowsStore_11805.1001.49.0_x64__8wekyb3d8bbwe
   package path: C:\Program Files\WindowsApps\Microsoft.WindowsStore_11805.1001.49.0_x64__8wekyb3d8bbwe
    family name: Microsoft.WindowsStore_8wekyb3d8bbwe
 info reference: 6619137
packageFullName: None

Буду признателен, если кто-нибудь посмотрит на мой код и обнаружит мои ошибки.

1 Ответ

2 голосов
/ 04 июля 2019

Я "перевел" ваш код в полные типы (без пакета win32).

Я пропатчил вместе с отладкой, но в основном:

  • Некоторые сооружения были объявлены Союзом.
  • Много несоответствий между указателем на типы и типы.
  • Казалось бы, ctypes.memove нормально, но я предпочитаю <ctypes_struct>.from_buffer (короче).

Протестировано на Windows 10 1903 (x64) и Python 3.7.0 (x64).

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ctypes
import ctypes.wintypes


ERROR_SUCCESS = 0x0
ERROR_INSUFFICIENT_BUFFER = 0x7A
APPMODEL_ERROR_NO_PACKAGE = 15700

PACKAGE_FILTER_ALL_LOADED = 0x00000000
PACKAGE_FILTER_HEAD = 0x00000010
PACKAGE_INFORMATION_FULL = 0x00000100
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000

PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ =  0x0010


class PACKAGE_INFO_REFERENCE(ctypes.Structure):
    _fields_ = [
        ("reserved", ctypes.c_void_p)
    ]


class PACKAGE_SUBVERSION(ctypes.Structure):

    _fields_ = [
        ("Revision", ctypes.wintypes.USHORT),
        ("Build", ctypes.wintypes.USHORT),
        ("Minor", ctypes.wintypes.USHORT),
        ("Major", ctypes.wintypes.USHORT),
    ]


class PACKAGE_VERSION_U(ctypes.Union):
    _fields_ = [
        ("Version", ctypes.c_uint64),
        ("DUMMYSTRUCTNAME", PACKAGE_SUBVERSION),
    ]


class PACKAGE_VERSION(ctypes.Structure):
    _anonymous_ = ("u", )
    _fields_ = [
        ("u", PACKAGE_VERSION_U),
    ]


class PACKAGE_ID(ctypes.Structure):

    _fields_ = [
        ("reserved", ctypes.c_uint32),
        ("processorArchitecture", ctypes.c_uint32),
        ("version", PACKAGE_VERSION),
        ("name", ctypes.c_wchar_p),
        ("publisher", ctypes.c_wchar_p),
        ("resourceId", ctypes.c_wchar_p),
        ("publisherId", ctypes.c_wchar_p),
    ]


class PACKAGE_INFO(ctypes.Structure):

    _fields_ = [
        ("reserved", ctypes.c_uint32),
        ("flags", ctypes.c_uint32),
        ("path", ctypes.c_wchar_p),
        ("packageFullName", ctypes.c_wchar_p),
        ("packageFamilyName", ctypes.c_wchar_p),
        ("packageId", PACKAGE_ID),
    ]


_user32 = ctypes.WinDLL("user32", use_last_error=True)
_get_windows_thread_process_id = _user32.GetWindowThreadProcessId
_get_windows_thread_process_id.argtypes = (ctypes.wintypes.HWND, ctypes.POINTER(ctypes.wintypes.DWORD))
_get_windows_thread_process_id.restype = ctypes.wintypes.DWORD

WNDENUMPROC  = ctypes.WINFUNCTYPE(ctypes.wintypes.BOOL, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)

_enum_child_windows = _user32.EnumChildWindows
_enum_child_windows.argtypes = (ctypes.wintypes.HWND, WNDENUMPROC, ctypes.wintypes.LPARAM)
_enum_child_windows.restype = ctypes.wintypes.BOOL

_enum_windows = _user32.EnumWindows
_enum_windows.argtypes = (WNDENUMPROC, ctypes.wintypes.LPARAM)
_enum_windows.restype = ctypes.wintypes.BOOL

_kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
_open_process = _kernel32.OpenProcess
_open_process.argtypes = (ctypes.wintypes.DWORD, ctypes.wintypes.BOOL, ctypes.wintypes.DWORD)
_open_process.restype = ctypes.wintypes.HANDLE

_close_handle = _kernel32.CloseHandle
_close_handle.argtypes = (ctypes.wintypes.HANDLE, )
_close_handle.restype = ctypes.wintypes.BOOL

_get_package_info = _kernel32.GetPackageInfo
_get_package_info.argtypes = (
    PACKAGE_INFO_REFERENCE,
    ctypes.c_uint32,
    ctypes.POINTER(ctypes.c_uint32),
    ctypes.POINTER(ctypes.c_uint8),
    ctypes.POINTER(ctypes.c_uint32)
)
_get_package_info.restype = ctypes.wintypes.LONG

_get_package_full_name = _kernel32.GetPackageFullName
_get_package_full_name.argtypes = (ctypes.wintypes.HANDLE, ctypes.POINTER(ctypes.c_uint32), ctypes.wintypes.LPCWSTR)
_get_package_full_name.restype = ctypes.wintypes.LONG

_get_package_path_by_full_name = _kernel32.GetPackagePathByFullName
_get_package_path_by_full_name.argtypes = (ctypes.wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_uint32), ctypes.wintypes.LPCWSTR)
_get_package_path_by_full_name.restype = ctypes.wintypes.LONG

_package_family_name_from_full_name = _kernel32.PackageFamilyNameFromFullName
_package_family_name_from_full_name.argtypes = (
    ctypes.wintypes.LPCWSTR,
    ctypes.POINTER(ctypes.c_uint32),
    ctypes.wintypes.LPCWSTR)
_package_family_name_from_full_name.restype = ctypes.wintypes.LONG

_open_package_info_by_full_name = _kernel32.OpenPackageInfoByFullName
_open_package_info_by_full_name.argtypes = (
    ctypes.wintypes.LPCWSTR,
    ctypes.c_uint32,
    ctypes.POINTER(PACKAGE_INFO_REFERENCE)
)
_open_package_info_by_full_name.restype = ctypes.wintypes.LONG

_close_package_info = _kernel32.ClosePackageInfo
_close_package_info.argtypes = (
    PACKAGE_INFO_REFERENCE,
)
_close_package_info.restype = ctypes.wintypes.LONG



def get_children(hwnd):
    children = []
    def append_to_collection(element, param):
        children.append(element)
        return True

    func = WNDENUMPROC(append_to_collection)
    _enum_child_windows(hwnd, func, 0)

    return children


def package_full_name_from_handle(handle):
    length = ctypes.c_uint()
    ret_val = _get_package_full_name(handle, ctypes.byref(length), None)
    if ret_val == APPMODEL_ERROR_NO_PACKAGE:
        print(f"package_full_name_from_handle: handle {handle:#x} has no package.")
        return None

    full_name = ctypes.create_unicode_buffer(length.value + 1)
    ret_val = _get_package_full_name(handle, ctypes.byref(length), full_name)
    if ret_val != ERROR_SUCCESS:
        err =  ctypes.WinError(ctypes.get_last_error())
        print(f"package_full_name_from_handle: error -> {str(err)}")
        return None

    return full_name


def package_path_from_full_name(full_name):
    length = ctypes.c_uint()
    retval = _get_package_path_by_full_name(full_name, ctypes.byref(length), None)
    if retval != ERROR_INSUFFICIENT_BUFFER:
        raise ctypes.WinError(ctypes.get_last_error())

    package_path = ctypes.create_unicode_buffer(length.value)
    retval = _get_package_path_by_full_name(full_name, ctypes.byref(length), package_path)
    if retval != ERROR_SUCCESS:
        raise ctypes.WinError(ctypes.get_last_error())

    return package_path


def package_family_name_from_full_name(full_name):
    length = ctypes.c_uint()
    retval = _package_family_name_from_full_name(full_name, ctypes.byref(length), None)
    if retval != ERROR_INSUFFICIENT_BUFFER:
        raise ctypes.WinError(ctypes.get_last_error())

    family_name = ctypes.create_unicode_buffer(length.value)
    retval = _package_family_name_from_full_name(full_name, ctypes.byref(length), family_name)
    if retval != ERROR_SUCCESS:
        raise ctypes.WinError(ctypes.get_last_error())

    return family_name


def package_info_reference_from_full_name(full_name):
    package_info_reference = ctypes.pointer(PACKAGE_INFO_REFERENCE())
    retval = _open_package_info_by_full_name(full_name, 0, package_info_reference)
    if retval != ERROR_SUCCESS:
        raise ctypes.WinError(ctypes.get_last_error())

    return package_info_reference


def package_info_buffer_from_reference(package_info_reference):
    length = ctypes.c_uint(0)
    count = ctypes.c_uint()

    retval = _get_package_info(
        package_info_reference.contents,  # package_info_reference is already a pointer. We want its content.
        PACKAGE_FILTER_HEAD,
        ctypes.byref(length),
        None,
        ctypes.byref(count),
    )
    if retval != ERROR_INSUFFICIENT_BUFFER:
        raise ctypes.WinError(ctypes.get_last_error())

    buffer = ctypes.create_string_buffer(length.value)
    buffer_bytes = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_uint8))
    retval = _get_package_info(
        package_info_reference.contents,
        PACKAGE_FILTER_HEAD,
        ctypes.byref(length),
        buffer_bytes,
        ctypes.byref(count),
    )
    if retval != ERROR_SUCCESS:
        raise ctypes.WinError(ctypes.get_last_error())

    return buffer, length


def get_package(hwnd):
    pid = ctypes.wintypes.DWORD()
    _get_windows_thread_process_id(
        hwnd,
        ctypes.byref(pid)
    )

    hprocess = _open_process(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
    full_name = package_full_name_from_handle(hprocess)
    if not full_name:
        return
    else:
        print(full_name.value)

    '''
    children = get_children(hwnd)
    for child in children:
        child_pid = ctypes.wintypes.DWORD(0)
        _get_windows_thread_process_id(child, ctypes.byref(child_pid))
        if child_pid != pid:
            hprocess = _open_process(PROCESS_QUERY_LIMITED_INFORMATION, False, child_pid)
            break

    if hprocess is None:
        return

    full_name = package_full_name_from_handle(hprocess)
    if full_name is None:
        return

    if not ("Microsoft.MicrosoftEdge" in full_name.value or "Microsoft.WindowsStore" in full_name.value):
    return None
    '''

    print("=" * 79)
    print("full name: ", full_name.value)
    package_path = package_path_from_full_name(full_name)
    print("package path: ", package_path.value)
    family_name = package_family_name_from_full_name(full_name)
    print("family name:", family_name.value)
    package_info_reference = package_info_reference_from_full_name(full_name)
    print("info reference:", package_info_reference.contents.reserved)

    package_info_buffer, length = package_info_buffer_from_reference(package_info_reference)
    # size_package_info = ctypes.sizeof(PACKAGE_INFO)
    # print(f"PACKAGE_INFO size: {size_package_info:#x}")
    # print(f"num package info: {length.value / size_package_info}")
    package_info = PACKAGE_INFO.from_buffer(package_info_buffer)
    print("packageFullName:", package_info.packageFullName)
    print("=" * 79)

    _close_handle(hprocess)
    _close_package_info(package_info_reference.contents)

def get_windows():
    hwnds = []
    def append_to_collection(element, param):
        hwnds.append(element)
        return True
    func = WNDENUMPROC(append_to_collection)
    _enum_windows(func, 0)
    return hwnds


if __name__ == "__main__":
    for hwnd in get_windows():
        get_package(hwnd)

Пример вывода:

===============================================================================
full name:  Microsoft.Windows.Cortana_1.12.3.18362_neutral_neutral_cw5n1h2txyewy
package path:  C:\Windows\SystemApps\Microsoft.Windows.Cortana_cw5n1h2txyewy
family name: Microsoft.Windows.Cortana_cw5n1h2txyewy
info reference: 2882563979504
packageFullName: Microsoft.Windows.Cortana_1.12.3.18362_neutral_neutral_cw5n1h2txyewy
===============================================================================
...