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,3 +1,3 @@
|
|||||||
# Project instructions
|
# Project instructions
|
||||||
|
|
||||||
Use `whatsapp_suite/` as canonical source; regenerate `whatsapp.py` after source changes. Run source and portable offline tests. Keep default tests free of live side effects. Do not commit exports, profiles, real message/contact fixtures, credentials or research captures. Preserve explicit self-message recipient guards and supported-build checks. Read `CONTRIBUTING.md` and `docs/VALIDATION.md` before changing native integration.
|
Use `whatsapp_suite/` as canonical source; regenerate `whatsapp.py` after source changes. Run source and portable offline tests. Keep default tests free of live side effects. Do not commit exports, profiles, real message/contact fixtures, credentials or research captures. Preserve explicit self-message recipient guards and native object checks and advisory compatibility reporting. Read `CONTRIBUTING.md` and `docs/VALIDATION.md` before changing native integration.
|
||||||
+8
-1
@@ -1,6 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Initial prepared project — 2026-10-11
|
## Compatibility policy update — 2026-10-11
|
||||||
|
|
||||||
|
- Treat the recorded Desktop version and Core DLL hash as tested evidence, rather than an allowlist.
|
||||||
|
- Attempt other configurations with a warning and report detected/tested metadata.
|
||||||
|
- Keep native object checks, helper integrity verification and self-message guards.
|
||||||
|
- Document process instrumentation separately from a demonstrated vulnerability exploit.
|
||||||
|
|
||||||
|
## Initial prepared project — 2026-10-11
|
||||||
|
|
||||||
- Consolidated toolkit with one CLI and a portable Python bundle.
|
- Consolidated toolkit with one CLI and a portable Python bundle.
|
||||||
- Native WebView message/contact export, old-message reading and fixed self-send test.
|
- Native WebView message/contact export, old-message reading and fixed self-send test.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ python whatsapp.py export-zip --output messages.zip
|
|||||||
python whatsapp.py live --suite history-read
|
python whatsapp.py live --suite history-read
|
||||||
```
|
```
|
||||||
|
|
||||||
Live bridge commands require **Windows x64, 64-bit Python and WhatsApp Desktop 2.2639.100.0** with the verified WebView component. This is a version-specific integration, not a general promise of compatibility with future WhatsApp releases. The core commands need only Python's standard library. Optional tools have [additional dependencies](docs/CLI.md#optional-dependencies); Node.js enables the JavaScript fixture tests.
|
Live bridge commands require **Windows x64 and 64-bit Python**. They were tested with WhatsApp Desktop **2.2639.100.0** and the recorded WebView component. Other versions are attempted with a compatibility warning rather than blocked. The helper still uses native layout offsets from the tested component, so success on other versions is unverified and changed layouts can cause failure or process instability. The core commands need only Python's standard library. Optional tools have [additional dependencies](docs/CLI.md#optional-dependencies); Node.js enables the JavaScript fixture tests.
|
||||||
|
|
||||||
`send-self` actually sends a message. It is never part of the default tests and does not retry automatically. ZIP exports cover the local database, not necessarily every message ever received. Contacts are local table rows, not a deduplicated phone address book.
|
`send-self` actually sends a message. It is never part of the default tests and does not retry automatically. ZIP exports cover the local database, not necessarily every message ever received. Contacts are local table rows, not a deduplicated phone address book.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
Use the toolkit only with accounts and installations you are authorized to inspect. Live reports, contacts, messages, browser profiles, pairing QR payloads and process dumps can contain private data. Keep them outside Git. The ignore rules are a guardrail; review staged files before upload.
|
Use the toolkit only with accounts and installations you are authorized to inspect. Live reports, contacts, messages, browser profiles, pairing QR payloads and process dumps can contain private data. Keep them outside Git. The ignore rules are a guardrail; review staged files before upload.
|
||||||
|
|
||||||
The native bridge executes inside the Desktop process and depends on a specific build. Use a trusted bundle. An embedded hash detects accidental modification but is not a digital signature. Do not make a CDP endpoint publicly reachable.
|
The native bridge executes inside the Desktop process and uses native layout offsets from a tested build; other versions are attempted with a warning. Use a trusted bundle. An embedded hash detects accidental modification but is not a digital signature. Do not make a CDP endpoint publicly reachable.
|
||||||
|
|
||||||
For a sensitive issue, contact the repository owner privately through an established channel. Do not attach live exports, session material or credentials to issues. For ordinary bug reports, include the command, Python/app versions and a redacted diagnostic summary.
|
For a sensitive issue, contact the repository owner privately through an established channel. Do not attach live exports, session material or credentials to issues. For ordinary bug reports, include the command, Python/app versions and a redacted diagnostic summary.
|
||||||
+10
-2
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
The portable launcher verifies and decompresses its embedded payload, then imports the Python package directly from the in-memory ZIP. It uses the same CLI and modules as `python -m whatsapp_suite`.
|
The portable launcher verifies and decompresses its embedded payload, then imports the Python package directly from the in-memory ZIP. It uses the same CLI and modules as `python -m whatsapp_suite`.
|
||||||
|
|
||||||
For a core live operation, Python uses Windows APIs through `ctypes`. It verifies the helper DLL and supported application build, installs a temporary `WH_GETMESSAGE` hook on the Desktop UI thread, and passes a bounded JavaScript request through shared memory. The native helper locates the existing WebView object using build-specific signatures and calls its `ExecuteScriptAsync` interface. A thread message wakes the hook. Results return through the shared mapping; the hook, mapping and process handles are then released. Reports record cleanup outcomes.
|
For a core live operation, Python uses Windows APIs through `ctypes`. It verifies the helper DLL, records application/WebView compatibility, installs a temporary `WH_GETMESSAGE` hook on the Desktop UI thread, and passes a bounded JavaScript request through shared memory. The native helper locates the existing WebView object using build-specific signatures and calls its `ExecuteScriptAsync` interface. A thread message wakes the hook. Results return through the shared mapping; the hook, mapping and process handles are then released. Reports record cleanup outcomes.
|
||||||
|
|
||||||
This still uses native C++ DLLs. Python packages their bytes; it does not replace their implementation with pure Python. The C++ source is for maintenance and is unnecessary on a machine running the portable file. SHA-256 checks verify embedded asset integrity, not publisher identity or authenticity of a downloaded file.
|
This still uses native C++ DLLs. Python packages their bytes; it does not replace their implementation with pure Python. The C++ source is for maintenance and is unnecessary on a machine running the portable file. SHA-256 checks verify embedded asset integrity, not publisher identity or authenticity of a downloaded file.
|
||||||
|
|
||||||
@@ -14,6 +14,14 @@ Contact export reads selected public fields from the local contact table. Sendin
|
|||||||
|
|
||||||
## Boundaries
|
## Boundaries
|
||||||
|
|
||||||
Live native operations support Desktop build `2.2639.100.0`. Updates may invalidate signatures and require new evidence and validation. Core bridge commands do not use CDP or UI automation; optional legacy commands have their own transports and dependencies. RAM readers only see loaded models. Exported message text does not include media file downloads. Password or passkey private-key extraction is not an implemented feature.
|
Live native operations were tested with Desktop build `2.2639.100.0`. Other versions are attempted with a warning. The helper still uses fixed offsets and native object/method checks derived from the tested WebView DLL. A changed layout may fail discovery or cause process instability; removing the version gate does not make those offsets version-independent. Updates require new evidence and validation to be considered tested. Core bridge commands do not use CDP or UI automation; optional legacy commands have their own transports and dependencies. RAM readers only see loaded models. Exported message text does not include media file downloads. Password or passkey private-key extraction is not an implemented feature.
|
||||||
|
|
||||||
The repository includes only maintained source, synthetic fixtures and required runtime assets. Local profiles, extracted application code, credentials, messages, contacts and research dumps are excluded.
|
The repository includes only maintained source, synthetic fixtures and required runtime assets. Local profiles, extracted application code, credentials, messages, contacts and research dumps are excluded.
|
||||||
|
|
||||||
|
## Security mechanism classification
|
||||||
|
|
||||||
|
The observed implementation uses documented Windows hook loading and WebView script execution APIs. It does not demonstrate a memory-corruption exploit, privilege escalation or a cryptographic break. It obtains the running application's existing decrypted data through that application's internal getters under the current signed-in session.
|
||||||
|
|
||||||
|
However, loading a helper into another process and locating internal objects by memory layout is reverse-engineered process instrumentation. WhatsApp does not expose the internal module calls used here as a documented external automation API. Calling documented OS/WebView APIs does not establish that this overall integration is supported or intended by WhatsApp, nor is this observation a complete security audit.
|
||||||
|
|
||||||
|
References: [Microsoft SetWindowsHookExW](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw), [Microsoft WebView2 JavaScript execution](https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/javascript).
|
||||||
+10
-9
@@ -19,11 +19,11 @@ The bundle compresses the original modules and resources together with LZMA.
|
|||||||
It verifies its embedded payload and imports Python modules directly from memory.
|
It verifies its embedded payload and imports Python modules directly from memory.
|
||||||
Only a native helper needed for a live command is materialized in a hash-verified
|
Only a native helper needed for a live command is materialized in a hash-verified
|
||||||
cache beside the bundle. Keep the containing folder writable. Native helper
|
cache beside the bundle. Keep the containing folder writable. Native helper
|
||||||
bytes, build checks, batch sizes and message read/send paths are unchanged.
|
bytes, native object checks, batch sizes and message read/send paths are unchanged.
|
||||||
|
|
||||||
Core WebView commands use the standard library. Python 3.11+, Windows x64,
|
Core WebView commands use the standard library. Python 3.11+, Windows x64,
|
||||||
64-bit Python and the supported WhatsApp build are still required for live
|
64-bit Python are required for live Desktop operations. Other Desktop versions
|
||||||
Desktop operations. Optional legacy CDP, Frida and QR tools retain the packages
|
are attempted with a compatibility warning; only the recorded version was tested. Optional legacy CDP, Frida and QR tools retain the packages
|
||||||
listed below. Node.js remains optional for JavaScript fixture tests.
|
listed below. Node.js remains optional for JavaScript fixture tests.
|
||||||
|
|
||||||
The development package below remains the editable source. Rebuild the generated
|
The development package below remains the editable source. Rebuild the generated
|
||||||
@@ -53,7 +53,7 @@ probe responses, command dispatch, and bundled DLL hashes. Node.js runs the
|
|||||||
JavaScript history fixtures; that test is reported as skipped if Node is absent.
|
JavaScript history fixtures; that test is reported as skipped if Node is absent.
|
||||||
Python 3.11 or newer is required.
|
Python 3.11 or newer is required.
|
||||||
|
|
||||||
To test an already running supported WhatsApp Desktop installation:
|
To test an already running WhatsApp Desktop installation:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
python -m whatsapp_suite test --live
|
python -m whatsapp_suite test --live
|
||||||
@@ -66,11 +66,12 @@ python -m whatsapp_suite contacts-export --output exports/contacts.json
|
|||||||
python -m whatsapp_suite send-self
|
python -m whatsapp_suite send-self
|
||||||
```
|
```
|
||||||
|
|
||||||
Live WebView probes require Windows x64, 64-bit Python, Desktop build
|
Live WebView probes require Windows x64 and 64-bit Python. Desktop build
|
||||||
`2.2639.100.0`, and the verified WebView2 Core DLL. They use bundled native
|
`2.2639.100.0` and its recorded WebView2 Core DLL were tested. Other versions or
|
||||||
helpers and check the build before installing a temporary hook. A missing or
|
DLL hashes produce a warning and are attempted. The helper still checks native
|
||||||
unsupported app produces a failed live result. The JSON report includes cleanup
|
object signatures and methods before executing JavaScript; changed layouts can
|
||||||
status. `--pid` selects a particular Desktop host.
|
cause failure or process instability. The JSON report includes detected/tested
|
||||||
|
compatibility metadata and cleanup status. `--pid` selects a Desktop host.
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -14,4 +14,4 @@ git push -u origin main
|
|||||||
|
|
||||||
The owner and remote URL were supplied by Karl. Repository visibility is controlled on the server. Never push the surrounding research workspace.
|
The owner and remote URL were supplied by Karl. Repository visibility is controlled on the server. Never push the surrounding research workspace.
|
||||||
|
|
||||||
For a release, rebuild and test the bundle, then attach only `whatsapp.py` and a SHA-256 checksum to a server release. State the supported Desktop build and optional dependencies. Do not attach test exports or research reports. No server-specific CI workflow is included because runner availability and configuration have not been verified.
|
For a release, rebuild and test the bundle, then attach only `whatsapp.py` and a SHA-256 checksum to a server release. State the tested Desktop build and optional dependencies. Do not attach test exports or research reports. No server-specific CI workflow is included because runner availability and configuration have not been verified.
|
||||||
@@ -15,3 +15,7 @@ Observed on 2026-10-11 in the original Windows development workspace. These obse
|
|||||||
Every CLI help path is covered by regression tests. Synthetic fixtures cover parsing, normalization/search, export contracts, recipient guarding and helper cache integrity. Not every optional or live feature was rerun from the final bundle. Default tests do not send messages, link accounts or run live probes. JavaScript fixture checks can be skipped when Node.js is absent; inspect the report.
|
Every CLI help path is covered by regression tests. Synthetic fixtures cover parsing, normalization/search, export contracts, recipient guarding and helper cache integrity. Not every optional or live feature was rerun from the final bundle. Default tests do not send messages, link accounts or run live probes. JavaScript fixture checks can be skipped when Node.js is absent; inspect the report.
|
||||||
|
|
||||||
Run `python whatsapp.py test` for portable checks, or `python -m whatsapp_suite test` for source checks. Use `--help` before explicit live operations. Test reports and export artifacts are generated locally and ignored by Git. Do not commit live test output.
|
Run `python whatsapp.py test` for portable checks, or `python -m whatsapp_suite test` for source checks. Use `--help` before explicit live operations. Test reports and export artifacts are generated locally and ignored by Git. Do not commit live test output.
|
||||||
|
|
||||||
|
## Advisory compatibility update — 2026-10-11
|
||||||
|
|
||||||
|
Source and rebuilt portable bundle each passed 32 offline tests, including acceptance of untested version/hash combinations and recognition of the tested configuration. A portable live arithmetic probe passed on the original tested build, with successful native cleanup. No other installed Desktop version was available for a live check. The optional Frida SQLite agent's version guard is also advisory; its changed metadata/warning path has not been retested live.
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
"sha256": {
|
"sha256": {
|
||||||
"math_probe.dll": "136620d96f4745a4c41edfe1f4a9272ef25fc060ddace1c0eec1027bf252e02a",
|
"math_probe.dll": "136620d96f4745a4c41edfe1f4a9272ef25fc060ddace1c0eec1027bf252e02a",
|
||||||
"search_probe.dll": "911beb9f6bb52390cde8a75562ca1d6c46f4629f641426a5250286903de60895",
|
"search_probe.dll": "911beb9f6bb52390cde8a75562ca1d6c46f4629f641426a5250286903de60895",
|
||||||
"sqlite_inject.js": "f31f31e170d30c0c845a3759db631d084b0bb096f1b654a9d9168a1078daed3f"
|
"sqlite_inject.js": "61a5d8a9365353ee19ba82d95ded364331e7e69f6705b470c910d6560a22397a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,3 +289,25 @@ class NativeCacheTests(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|
||||||
|
class DesktopCompatibilityTests(unittest.TestCase):
|
||||||
|
def test_untested_version_and_core_are_advisory(self):
|
||||||
|
from whatsapp_suite import desktop
|
||||||
|
with tempfile.TemporaryDirectory() as folder:
|
||||||
|
path=Path(folder)/'WhatsAppDesktop_9.9.9.0_x64'/'Core.dll'
|
||||||
|
path.parent.mkdir();path.write_bytes(b'other core fixture')
|
||||||
|
result=desktop.check_core_build(path)
|
||||||
|
self.assertEqual(result['detected_build'],'9.9.9.0')
|
||||||
|
self.assertFalse(result['matches_tested_configuration'])
|
||||||
|
self.assertIn('Untested',result['warning'])
|
||||||
|
|
||||||
|
def test_tested_configuration_has_no_warning(self):
|
||||||
|
from whatsapp_suite import desktop
|
||||||
|
payload=b'tested core fixture'
|
||||||
|
with tempfile.TemporaryDirectory() as folder, patch.object(desktop,'CORE_SHA256',hashlib.sha256(payload).hexdigest()):
|
||||||
|
path=Path(folder)/('WhatsAppDesktop_'+desktop.TESTED_BUILD+'_x64')/'Core.dll'
|
||||||
|
path.parent.mkdir();path.write_bytes(payload)
|
||||||
|
result=desktop.check_core_build(path)
|
||||||
|
self.assertTrue(result['matches_tested_configuration'])
|
||||||
|
self.assertIsNone(result['warning'])
|
||||||
+1465
-1457
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,8 @@
|
|||||||
// REA-backed WinRT ABI for WhatsAppNativeProjection.dll build 2.2639.100.0.
|
// REA-backed WinRT ABI for WhatsAppNativeProjection.dll build 2.2639.100.0.
|
||||||
// Native calls only; no CDP, UI automation or restart.
|
// Native calls only; no CDP, UI automation or restart.
|
||||||
const native=Process.getModuleByName('WhatsAppNative.dll');
|
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');
|
const combase=Process.getModuleByName('combase.dll');
|
||||||
function api(name,ret,args){return new NativeFunction(combase.getExportByName(name),ret,args);}
|
function api(name,ret,args){return new NativeFunction(combase.getExportByName(name),ret,args);}
|
||||||
const createString=api('WindowsCreateString','int',['pointer','uint','pointer']);
|
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);
|
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);}
|
}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);}
|
}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');
|
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."""
|
"""Shared verified Desktop process and window discovery."""
|
||||||
import ctypes
|
import ctypes
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import re
|
||||||
|
import warnings
|
||||||
from pathlib import Path
|
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"
|
CORE_SHA256 = "c6da3c67eda66a8d48ff2ae11f68085f1f8250111138a6ae384d38eefb4da536"
|
||||||
|
|
||||||
def check_core_build(path):
|
def check_core_build(path):
|
||||||
if 'WhatsAppDesktop_' + SUPPORTED_BUILD + '_' not in str(path):
|
"""Describe tested-build compatibility without rejecting other versions.
|
||||||
raise ValueError('Unsupported WhatsApp build: ' + str(path) +
|
|
||||||
'; supported build is ' + SUPPORTED_BUILD + '. No hook installed.')
|
Native object/method checks still determine whether the bridge can operate.
|
||||||
if hashlib.sha256(Path(path).read_bytes()).hexdigest() != CORE_SHA256:
|
File access failures remain errors; a hash mismatch alone is advisory.
|
||||||
raise ValueError('Unknown WebView2 Core DLL hash. No hook installed.')
|
"""
|
||||||
|
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):
|
def visible_ui_threads(pid):
|
||||||
"""Inspect window ownership; no clicks, navigation or window messages."""
|
"""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())
|
raise ctypes.WinError(ctypes.get_last_error())
|
||||||
return threads
|
return threads
|
||||||
|
|
||||||
def detect_desktop_pid(requested_pid=None):
|
def detect_desktop_pid(requested_pid=None, compatibility=None):
|
||||||
"""Find the running supported Desktop host using Python and Windows APIs."""
|
"""Find the running Desktop host using Python and Windows APIs."""
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes import wintypes as w
|
from ctypes import wintypes as w
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -91,8 +106,8 @@ def detect_desktop_pid(requested_pid=None):
|
|||||||
path = Path(entry.szExePath)
|
path = Path(entry.szExePath)
|
||||||
if len(visible_ui_threads(pid)) == 1:
|
if len(visible_ui_threads(pid)) == 1:
|
||||||
try:
|
try:
|
||||||
check_core_build(path)
|
details = check_core_build(path)
|
||||||
candidates.append(pid)
|
candidates.append((pid, details))
|
||||||
except (ValueError, OSError) as error:
|
except (ValueError, OSError) as error:
|
||||||
rejected.append(str(error))
|
rejected.append(str(error))
|
||||||
break
|
break
|
||||||
@@ -102,8 +117,13 @@ def detect_desktop_pid(requested_pid=None):
|
|||||||
finally:
|
finally:
|
||||||
kernel.CloseHandle(snapshot)
|
kernel.CloseHandle(snapshot)
|
||||||
if not candidates:
|
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:
|
if len(candidates) != 1:
|
||||||
raise ValueError('Multiple active Desktop hosts found; specify --pid: '+', '.join(map(str,candidates)))
|
raise ValueError('Multiple active Desktop hosts found; specify --pid: '+', '.join(str(item[0]) for item in candidates))
|
||||||
return candidates[0]
|
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:
|
try:
|
||||||
session=frida.attach(pid);script=session.create_script(source);script.load()
|
session=frida.attach(pid);script=session.create_script(source);script.load()
|
||||||
discovered=script.exports_sync.discover(args.max_mb)
|
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':[]}
|
result={'pid':pid,'native_sqlite':discovered,'matches':[]}
|
||||||
if args.export_index:
|
if args.export_index:
|
||||||
result['index_exports']=[]
|
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
|
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,
|
hash-verified and cached in the workspace. Capability tests use fixed queries,
|
||||||
@@ -21,7 +21,7 @@ import uuid
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .common import BASE, verified_helper
|
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
|
WAKE = 0x8000 + 0x498
|
||||||
PROTOCOL = 0x4d415432
|
PROTOCOL = 0x4d415432
|
||||||
# SHA-256 of assets/math_probe.dll.
|
# 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,
|
'suite': suite, 'frida_used': False,
|
||||||
'startup_acknowledged': False, 'javascript_executed': False,
|
'startup_acknowledged': False, 'javascript_executed': False,
|
||||||
'cleanup': {'acknowledged': False, 'hook_removed': False},
|
'cleanup': {'acknowledged': False, 'hook_removed': False},
|
||||||
'supported_build': SUPPORTED_BUILD}
|
'tested_build': TESTED_BUILD}
|
||||||
kernel = user = None
|
kernel = user = None
|
||||||
module = hook = mapping = view = process = None
|
module = hook = mapping = view = process = None
|
||||||
packet = 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')
|
raise ValueError('Unknown suite; choose math, capabilities, all, message-json or contacts')
|
||||||
if os.name != 'nt' or ctypes.sizeof(ctypes.c_void_p) != 8:
|
if os.name != 'nt' or ctypes.sizeof(ctypes.c_void_p) != 8:
|
||||||
raise ValueError('Windows and 64-bit Python are required')
|
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)))
|
thread = next(iter(visible_ui_threads(selected)))
|
||||||
report.update(pid=selected, ui_thread_id=thread, pid_auto_detected=pid is None)
|
report.update(pid=selected, ui_thread_id=thread, pid_auto_detected=pid is None)
|
||||||
kernel, user = windows_api()
|
kernel, user = windows_api()
|
||||||
@@ -573,7 +575,7 @@ def main(argv=None):
|
|||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument('--suite', choices=('math', 'capabilities', 'all', 'message-json', 'contacts', 'history-read'), default='math',
|
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')
|
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)
|
parser.add_argument('--output', type=Path)
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
if args.output is None:
|
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.UnhookWindowsHookEx.argtypes=[w.HANDLE];user.UnhookWindowsHookEx.restype=w.BOOL
|
||||||
user.PostThreadMessageW.argtypes=[w.DWORD,w.UINT,w.WPARAM,w.LPARAM]
|
user.PostThreadMessageW.argtypes=[w.DWORD,w.UINT,w.WPARAM,w.LPARAM]
|
||||||
user.PostThreadMessageW.restype=w.BOOL
|
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():
|
if pid != detect_desktop_pid():
|
||||||
raise ValueError('Explicit PID is not the validated active Desktop instance')
|
raise ValueError('Explicit PID is not the validated active Desktop instance')
|
||||||
dll=build_native_probe()
|
dll=build_native_probe()
|
||||||
|
|||||||
Reference in new issue
Block a user