Allow untested Desktop versions with compatibility warnings
This commit is contained in:
1 parent
d2b621bce7
commit
ab69f287b6
16 files changed
+1571
-1495
No files matched your search
@@ -1,7 +1,8 @@
|
||||
// REA-backed WinRT ABI for WhatsAppNativeProjection.dll build 2.2639.100.0.
|
||||
// Native calls only; no CDP, UI automation or restart.
|
||||
const native=Process.getModuleByName('WhatsAppNative.dll');
|
||||
if(!native.path.includes('WhatsAppDesktop_2.2639.100.0_'))throw Error('Unsupported Desktop build; ABI must be revalidated with REA');
|
||||
const compatibility={tested_build:'2.2639.100.0',matches_tested_configuration:native.path.includes('WhatsAppDesktop_2.2639.100.0_')};
|
||||
compatibility.warning=compatibility.matches_tested_configuration?null:'Untested Desktop build; attempting native SQLite ABI discovery. Compatibility is unverified.';
|
||||
const combase=Process.getModuleByName('combase.dll');
|
||||
function api(name,ret,args){return new NativeFunction(combase.getExportByName(name),ret,args);}
|
||||
const createString=api('WindowsCreateString','int',['pointer','uint','pointer']);
|
||||
@@ -60,7 +61,7 @@ rpc.exports={
|
||||
found.push({id,address:object.toString(),tables:tables.map(r=>r[0])});object=ptr(0);
|
||||
}catch(error){if(failures.length<20)failures.push({address:hit.address.toString(),error:String(error)});if(!object.isNull())release(object);}
|
||||
}
|
||||
}return {databases:found,bytesScanned:scanned,vtable:vtable.toString(),vtableHits:hitCount,failures};
|
||||
}return {compatibility,databases:found,bytesScanned:scanned,vtable:vtable.toString(),vtableHits:hitCount,failures};
|
||||
}finally{release(iface);release(sample);release(factory);deleteString(name);}
|
||||
},
|
||||
search(id,term,limit){if(!Number.isInteger(id)||id<0||id>=references.length)throw Error('Unknown database');
|
||||
|
||||
+33
-13
@@ -1,16 +1,31 @@
|
||||
"""Shared verified Desktop process and window discovery."""
|
||||
import ctypes
|
||||
import hashlib
|
||||
import re
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
SUPPORTED_BUILD = "2.2639.100.0"
|
||||
TESTED_BUILD = "2.2639.100.0"
|
||||
SUPPORTED_BUILD = TESTED_BUILD # Backward-compatible constant; not an allowlist.
|
||||
CORE_SHA256 = "c6da3c67eda66a8d48ff2ae11f68085f1f8250111138a6ae384d38eefb4da536"
|
||||
|
||||
def check_core_build(path):
|
||||
if 'WhatsAppDesktop_' + SUPPORTED_BUILD + '_' not in str(path):
|
||||
raise ValueError('Unsupported WhatsApp build: ' + str(path) +
|
||||
'; supported build is ' + SUPPORTED_BUILD + '. No hook installed.')
|
||||
if hashlib.sha256(Path(path).read_bytes()).hexdigest() != CORE_SHA256:
|
||||
raise ValueError('Unknown WebView2 Core DLL hash. No hook installed.')
|
||||
"""Describe tested-build compatibility without rejecting other versions.
|
||||
|
||||
Native object/method checks still determine whether the bridge can operate.
|
||||
File access failures remain errors; a hash mismatch alone is advisory.
|
||||
"""
|
||||
path = Path(path)
|
||||
match = re.search(r'WhatsAppDesktop_([0-9.]+)_', str(path), re.IGNORECASE)
|
||||
actual_build = match.group(1) if match else None
|
||||
actual_hash = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
matched = actual_build == TESTED_BUILD and actual_hash == CORE_SHA256
|
||||
return {'tested_build': TESTED_BUILD, 'detected_build': actual_build,
|
||||
'tested_core_sha256': CORE_SHA256, 'detected_core_sha256': actual_hash,
|
||||
'matches_tested_configuration': matched,
|
||||
'warning': None if matched else
|
||||
'Untested WhatsApp/WebView configuration; attempting the bridge. '
|
||||
'Native object checks remain enabled. Compatibility is not verified; '
|
||||
'changed native layouts may cause failure or process instability.'}
|
||||
|
||||
def visible_ui_threads(pid):
|
||||
"""Inspect window ownership; no clicks, navigation or window messages."""
|
||||
@@ -36,8 +51,8 @@ def visible_ui_threads(pid):
|
||||
raise ctypes.WinError(ctypes.get_last_error())
|
||||
return threads
|
||||
|
||||
def detect_desktop_pid(requested_pid=None):
|
||||
"""Find the running supported Desktop host using Python and Windows APIs."""
|
||||
def detect_desktop_pid(requested_pid=None, compatibility=None):
|
||||
"""Find the running Desktop host using Python and Windows APIs."""
|
||||
import ctypes
|
||||
from ctypes import wintypes as w
|
||||
import hashlib
|
||||
@@ -91,8 +106,8 @@ def detect_desktop_pid(requested_pid=None):
|
||||
path = Path(entry.szExePath)
|
||||
if len(visible_ui_threads(pid)) == 1:
|
||||
try:
|
||||
check_core_build(path)
|
||||
candidates.append(pid)
|
||||
details = check_core_build(path)
|
||||
candidates.append((pid, details))
|
||||
except (ValueError, OSError) as error:
|
||||
rejected.append(str(error))
|
||||
break
|
||||
@@ -102,8 +117,13 @@ def detect_desktop_pid(requested_pid=None):
|
||||
finally:
|
||||
kernel.CloseHandle(snapshot)
|
||||
if not candidates:
|
||||
raise ValueError('; '.join(rejected) if rejected else 'No supported running WhatsApp Desktop host with one identifiable UI thread found; start WhatsApp normally')
|
||||
raise ValueError('; '.join(rejected) if rejected else 'No running WhatsApp Desktop host with one identifiable UI thread found; start WhatsApp normally')
|
||||
if len(candidates) != 1:
|
||||
raise ValueError('Multiple active Desktop hosts found; specify --pid: '+', '.join(map(str,candidates)))
|
||||
return candidates[0]
|
||||
raise ValueError('Multiple active Desktop hosts found; specify --pid: '+', '.join(str(item[0]) for item in candidates))
|
||||
selected, details = candidates[0]
|
||||
if compatibility is not None:
|
||||
compatibility.update(details)
|
||||
if details['warning']:
|
||||
warnings.warn(details['warning'], RuntimeWarning, stacklevel=2)
|
||||
return selected
|
||||
|
||||
@@ -39,6 +39,9 @@ def main(argv=None):
|
||||
try:
|
||||
session=frida.attach(pid);script=session.create_script(source);script.load()
|
||||
discovered=script.exports_sync.discover(args.max_mb)
|
||||
if discovered.get('compatibility',{}).get('warning'):
|
||||
import warnings
|
||||
warnings.warn(discovered['compatibility']['warning'], RuntimeWarning)
|
||||
result={'pid':pid,'native_sqlite':discovered,'matches':[]}
|
||||
if args.export_index:
|
||||
result['index_exports']=[]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Standalone fixed arithmetic and capability tests in the supported WhatsApp WebView.
|
||||
"""Standalone fixed arithmetic and capability tests in the WhatsApp WebView.
|
||||
|
||||
Windows x64 and 64-bit Python; standard library only. The bundled native DLL is
|
||||
hash-verified and cached in the workspace. Capability tests use fixed queries,
|
||||
@@ -21,7 +21,7 @@ import uuid
|
||||
import sys
|
||||
|
||||
from .common import BASE, verified_helper
|
||||
from .desktop import visible_ui_threads, detect_desktop_pid, check_core_build, SUPPORTED_BUILD, CORE_SHA256
|
||||
from .desktop import visible_ui_threads, detect_desktop_pid, TESTED_BUILD
|
||||
WAKE = 0x8000 + 0x498
|
||||
PROTOCOL = 0x4d415432
|
||||
# SHA-256 of assets/math_probe.dll.
|
||||
@@ -360,7 +360,7 @@ def run_math(pid=None, suite='math', _operation=None):
|
||||
'suite': suite, 'frida_used': False,
|
||||
'startup_acknowledged': False, 'javascript_executed': False,
|
||||
'cleanup': {'acknowledged': False, 'hook_removed': False},
|
||||
'supported_build': SUPPORTED_BUILD}
|
||||
'tested_build': TESTED_BUILD}
|
||||
kernel = user = None
|
||||
module = hook = mapping = view = process = None
|
||||
packet = None
|
||||
@@ -371,7 +371,9 @@ def run_math(pid=None, suite='math', _operation=None):
|
||||
raise ValueError('Unknown suite; choose math, capabilities, all, message-json or contacts')
|
||||
if os.name != 'nt' or ctypes.sizeof(ctypes.c_void_p) != 8:
|
||||
raise ValueError('Windows and 64-bit Python are required')
|
||||
selected = detect_desktop_pid(pid)
|
||||
compatibility = {}
|
||||
selected = detect_desktop_pid(pid, compatibility=compatibility)
|
||||
report['compatibility'] = compatibility
|
||||
thread = next(iter(visible_ui_threads(selected)))
|
||||
report.update(pid=selected, ui_thread_id=thread, pid_auto_detected=pid is None)
|
||||
kernel, user = windows_api()
|
||||
@@ -573,7 +575,7 @@ def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--suite', choices=('math', 'capabilities', 'all', 'message-json', 'contacts', 'history-read'), default='math',
|
||||
help='Fixed tests (default: math); message-json prints meow matches, contacts prints a five-contact sample')
|
||||
parser.add_argument('--pid', type=int, help='Optional supported Desktop host PID')
|
||||
parser.add_argument('--pid', type=int, help='Optional Desktop host PID')
|
||||
parser.add_argument('--output', type=Path)
|
||||
args = parser.parse_args(argv)
|
||||
if args.output is None:
|
||||
|
||||
@@ -48,7 +48,7 @@ def native_webview_test(pid, output, config, search_query=None, limit=50):
|
||||
user.UnhookWindowsHookEx.argtypes=[w.HANDLE];user.UnhookWindowsHookEx.restype=w.BOOL
|
||||
user.PostThreadMessageW.argtypes=[w.DWORD,w.UINT,w.WPARAM,w.LPARAM]
|
||||
user.PostThreadMessageW.restype=w.BOOL
|
||||
# PID discovery checks the shipped native library hash before any target injection.
|
||||
# PID discovery records compatibility and requires a unique active UI host.
|
||||
if pid != detect_desktop_pid():
|
||||
raise ValueError('Explicit PID is not the validated active Desktop instance')
|
||||
dll=build_native_probe()
|
||||
|
||||
Reference in new issue
Block a user