Skip to content Skip to sidebar Skip to footer

How To Create Python Ctypes Structures For Ms Windows Package_id And Package_info Structures?

I'm trying to translate this SO answer code into Python (and a bit from the other answer there) with the help of official documentation for PACKAGE_ID and PACKAGE_INFO. I'm getting

Solution 1:

I "translated" your code to full ctypes (without the win32 package).

I patched alongside debugging it, but basically:

  • Some structures were declared as Union.
  • Lot of mismatch between pointer to types and types.
  • The ctypes.memove seemed ok, but I prefer <ctypes_struct>.from_buffer instead (shorter).

Tested on Windows 10 1903 (x64) and 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 =  0x0010classPACKAGE_INFO_REFERENCE(ctypes.Structure):
    _fields_ = [
        ("reserved", ctypes.c_void_p)
    ]


classPACKAGE_SUBVERSION(ctypes.Structure):

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


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


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


classPACKAGE_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),
    ]


classPACKAGE_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



defget_children(hwnd):
    children = []
    defappend_to_collection(element, param):
        children.append(element)
        returnTrue

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

    return children


defpackage_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.")
        returnNone

    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)}")
        returnNonereturn full_name


defpackage_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


defpackage_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


defpackage_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


defpackage_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


defget_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)
    ifnot full_name:
        returnelse:
        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)

defget_windows():
    hwnds = []
    defappend_to_collection(element, param):
        hwnds.append(element)
        returnTrue
    func = WNDENUMPROC(append_to_collection)
    _enum_windows(func, 0)
    return hwnds


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

Sample output:

===============================================================================full name:Microsoft.Windows.Cortana_1.12.3.18362_neutral_neutral_cw5n1h2txyewypackage path:C:\Windows\SystemApps\Microsoft.Windows.Cortana_cw5n1h2txyewyfamily name:Microsoft.Windows.Cortana_cw5n1h2txyewyinfo reference:2882563979504packageFullName:Microsoft.Windows.Cortana_1.12.3.18362_neutral_neutral_cw5n1h2txyewy===============================================================================

Post a Comment for "How To Create Python Ctypes Structures For Ms Windows Package_id And Package_info Structures?"