Files
Kiwy-Signage/windows/verify_sendinput_fix.py
T
ske087 5d9aa02c07 Add Windows card reader, code-signing helpers and playlist diagnostics
- windows/win_card_reader.py: Windows-native card reader via the Raw Input API
  with a low-level keyboard-hook fallback.
- windows/sign_exe.ps1: sign the built executable with a .pfx certificate.
- windows/create_self_signed_cert.ps1: generate a self-signed cert for local
  testing (not trusted by Smart App Control).
- windows/verify_sendinput_fix.py: verification helper for the SendInput
  foreground-unlock fix in run_win.py.
- documentation/CODE_SIGNING_SMART_APP_CONTROL.md: signing guidance.
- working_files/execute_playlist_retrieve.py,
  working_files/raw_server_playlist.json: playlist retrieval diagnostics.

Note: windows/archive_list.txt and windows/build_last.txt are build output and
were committed by request rather than by convention.
2026-09-10 16:44:12 +03:00

90 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""Rigorous verification: is the fixed SendInput code in the compiled run_win?"""
import marshal
import types
import dis
from PyInstaller.archive.readers import CArchiveReader
EXE = r'dist\KiwySignagePlayer\KiwySignagePlayer.exe'
def collect_strings(code, acc):
for c in code.co_consts:
if isinstance(c, str):
acc.append(c)
elif isinstance(c, types.CodeType):
collect_strings(c, acc)
def collect_codes(code, acc):
acc.append(code)
for c in code.co_consts:
if isinstance(c, types.CodeType):
collect_codes(c, acc)
def main():
arc = CArchiveReader(EXE)
data = arc.extract('run_win')
code = marshal.loads(data)
all_codes = []
collect_codes(code, all_codes)
strings = []
collect_strings(code, strings)
# 1) c_ulonglong can ONLY come from the fixed code (old used c_ulong + POINTER)
has_c_ulonglong = any('c_ulonglong' in c.co_names for c in all_codes)
print(f'c_ulonglong in co_names of any code object: {has_c_ulonglong}')
# 2) INPUTUNION should be STORE_DEREF/STORE_NAME'd inside
# _force_foreground_sendinput (classes are defined in a closure,
# so they're stored via STORE_DEREF).
store_names = set()
for c in all_codes:
for instr in dis.get_instructions(c):
if instr.opname in ('STORE_NAME', 'STORE_FAST', 'STORE_DEREF'):
store_names.add(instr.argval)
print(f'INPUTUNION stored: {"INPUTUNION" in store_names}')
print(f'KEYBDINPUT stored: {"KEYBDINPUT" in store_names}')
print(f'MOUSEINPUT stored: {"MOUSEINPUT" in store_names}')
print(f'HARDWAREINPUT stored: {"HARDWAREINPUT" in store_names}')
# 3) The new code uses INPUT() + field assignment (type, u.ki.wVk, u.ki.dwFlags)
names = set()
for c in all_codes:
names.update(c.co_names)
print(f'Has "u" attribute usage: {"u" in names}')
# 4) string markers
print(f'string "undersized buffer": {any("undersized buffer" in s for s in strings)}')
print(f'string "real Win32 x64": {any("real Win32 x64" in s for s in strings)}')
print(f'string "ULONG_PTR": {any("ULONG_PTR" in s for s in strings)}')
# 5) Console-window fix in _find_kivy_hwnd: cellvars/freevars + tuple consts
find_kv = [c for c in all_codes if c.co_name == '_find_kivy_hwnd']
cell_vars = set()
for c in find_kv:
cell_vars.update(c.co_cellvars)
cell_vars.update(c.co_freevars)
enum_cb = [c for c in all_codes
if c.co_name == '_enum_cb' and 'SDL_CLASSES' in c.co_freevars]
tuple_strs = set()
for c in enum_cb:
for x in c.co_consts:
if isinstance(x, tuple):
tuple_strs.update(str(v) for v in x)
print(f'_find_kivy_hwnd cell/free vars (SDL_CLASSES/sdl_windows/our_pid): '
f'{"SDL_CLASSES" in cell_vars and "sdl_windows" in cell_vars and "our_pid" in cell_vars}')
print(f'console class excluded (ConsoleWindowClass in _enum_cb tuple): '
f'{"ConsoleWindowClass" in tuple_strs}')
verdict = has_c_ulonglong and 'INPUTUNION' in store_names
console_fix = ('SDL_CLASSES' in cell_vars and 'ConsoleWindowClass' in tuple_strs)
print(f'\nVerdict SendInput: {"FIX PRESENT" if verdict else "FIX ABSENT - rebuild needed"}')
print(f'Verdict console-hwnd: {"FIX PRESENT" if console_fix else "FIX ABSENT - rebuild needed"}')
if __name__ == '__main__':
main()