mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-26 07:29:00 -07:00
92 lines
4.4 KiB
Python
92 lines
4.4 KiB
Python
"""Experimental Quest 20230907 independent login; requires server platform-7 guard.
|
|
|
|
Preserves Quest VR setup before switching account initialization to standalone.
|
|
Redirects auth parameters, platform enum, and title-screen error-page navigation.
|
|
Produces an UNSIGNED APK. Does not assert that login/gameplay is supported.
|
|
The v1 full-initializer replacement failed with error 3 and no usable hands.
|
|
This v3 experiment restores SetVRDisplayMode, the password page, and Quest input.
|
|
Error navigation currently applies to all title-screen errors; not release-ready.
|
|
"""
|
|
import argparse
|
|
import hashlib
|
|
import struct
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
EXPECTED = 'd9a72f2906ecee6cd292fa094fa57df8904b125dee161a04887603a3d0d3449e'
|
|
LIB = 'lib/arm64-v8a/libil2cpp.so'
|
|
REDIRECTS = (
|
|
('GetAuthParams', 0x3d0a930, 0x396ad48),
|
|
('get_CurrentPlatform', 0x3d096fc, 0x43d6eb8),
|
|
('Title-screen error to password page', 0x39c0f40, 0x39bbd80),
|
|
('Saved-account auto-login error to password page', 0x39c67e0, 0x39bbd80),
|
|
)
|
|
|
|
def patch(data):
|
|
if hashlib.sha256(data).hexdigest() != EXPECTED:
|
|
raise ValueError('Unknown IL2CPP binary; refusing to patch')
|
|
result = bytearray(data)
|
|
phoff = struct.unpack_from('<Q', data, 32)[0]
|
|
entsize, count = struct.unpack_from('<HH', data, 54)
|
|
segments = [struct.unpack_from('<IIQQQQQQ', data, phoff + i * entsize) for i in range(count)]
|
|
def offset(rva):
|
|
for typ, flags, off, va, _, size, _, _ in segments:
|
|
if typ == 1 and flags & 1 and va <= rva < va + size:
|
|
return off + rva - va
|
|
raise ValueError('Address outside file-backed executable segment')
|
|
for name, src, dst in REDIRECTS:
|
|
delta = dst - src
|
|
if delta % 4 or not -(1 << 27) <= delta < (1 << 27):
|
|
raise ValueError('Branch target out of range')
|
|
offset(dst)
|
|
struct.pack_into('<I', result, offset(src), 0x14000000 | ((delta // 4) & 0x3ffffff))
|
|
print(f'{name}: 0x{src:x} -> 0x{dst:x}')
|
|
# Preserve the original prologue and Quest-specific VR display setup through
|
|
# 0x3d09f14. Then run independent initialization and restore the original frame.
|
|
start = 0x3d09f18
|
|
words = [0xaa1303e0, 0xaa1f03e1,
|
|
0x94000000 | (((0x43d6f0c - (start + 8)) // 4) & 0x3ffffff)]
|
|
for addr in (0x3d0a124, 0x3d0a12c, 0x3d0a130, 0x3d0a138):
|
|
words.append(struct.unpack_from('<I', data, offset(addr))[0])
|
|
words.append(0xd65f03c0)
|
|
struct.pack_into('<' + 'I' * len(words), result, offset(start), *words)
|
|
print('Preserved Quest VR initialization; redirected account initialization after it')
|
|
# InputManager.FMJDLCGGGKP switches on CurrentPlatform. Standalone (7)
|
|
# originally returns no VR input; map it to the Oculus hardware-aware path.
|
|
table = 0xc35a550
|
|
for typ, flags, off, va, _, size, _, _ in segments:
|
|
if typ == 1 and va <= table and table + 36 <= va + size:
|
|
table_offset = off + table - va
|
|
assert table + struct.unpack_from('<i', data, table_offset + 28)[0] == 0x460a2b0
|
|
assert table + struct.unpack_from('<i', data, table_offset + 4)[0] == 0x460a2c0
|
|
result[table_offset + 28:table_offset + 32] = data[table_offset + 4:table_offset + 8]
|
|
break
|
|
else:
|
|
raise ValueError('Input dispatch table not found')
|
|
print('Standalone input dispatch uses existing Oculus hardware-specific handler')
|
|
return bytes(result)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('input', type=Path)
|
|
parser.add_argument('output', type=Path)
|
|
parser.add_argument('--apply', action='store_true')
|
|
args = parser.parse_args()
|
|
if args.output.exists():
|
|
parser.error('Output already exists; choose a new path')
|
|
with zipfile.ZipFile(args.input) as original:
|
|
patched = patch(original.read(LIB))
|
|
print('Patched library SHA256:', hashlib.sha256(patched).hexdigest())
|
|
if not args.apply:
|
|
print('Dry run; pass --apply to write unsigned APK')
|
|
return
|
|
with zipfile.ZipFile(args.output, 'w') as output:
|
|
for info in original.infolist():
|
|
if info.filename.upper().startswith('META-INF/'):
|
|
continue
|
|
output.writestr(info, patched if info.filename == LIB else original.read(info))
|
|
print('Wrote unsigned experimental APK:', args.output)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|