Files
lippert-onecontrol/scripts/extract_xaba_v2_new.py
T
wes 718a13c02f Reorganize repository structure into logical folders
Structure:
- src/ - Python implementation (cobs_protocol.py, onecontrol_client.py)
- docs/ - All documentation markdown files
- scripts/ - Extraction scripts (for reference only)

Changes:
- Moved Python files to src/
- Moved all .md docs to docs/
- Moved extraction scripts to scripts/
- Updated README.md with new structure
- Updated import paths in README examples
- Added placeholder for future Quartz documentation URL

Benefits:
- Cleaner repository organization
- Easier to navigate
- Separates code from documentation
- Follows standard project conventions
2025-12-29 09:33:20 -05:00

111 lines
3.7 KiB
Python

import struct
import os
import lz4.block
import re
def extract():
print("Reading payload.bin...")
with open('payload.bin', 'rb') as f:
data = f.read()
# Find all XALZ offsets
print("Scanning for XALZ blocks...")
offsets = []
pos = 0
while True:
pos = data.find(b'XALZ', pos)
if pos == -1:
break
offsets.append(pos)
pos += 4
print(f"Found {len(offsets)} XALZ blocks.")
os.makedirs('extracted_final', exist_ok=True)
success_count = 0
for i, offset in enumerate(offsets):
# Determine end of compressed data
if i < len(offsets) - 1:
end = offsets[i+1]
else:
end = len(data)
# Header parsing
# XALZ (4) + ID (4) + UncompressedSize (4)
header = data[offset:offset+12]
magic, blob_id, uncomp_size = struct.unpack('<4sII', header)
# print(f"Block {i}: Offset {offset}, Size {uncomp_size}")
compressed_data = data[offset+12:end]
try:
# lz4.block.decompress expects the compressed data.
# If uncompressed_size is provided, it helps allocation.
decompressed = lz4.block.decompress(compressed_data, uncompressed_size=uncomp_size)
except Exception as e:
print(f"Error extracting block {i} at {offset}: {e}")
# Try extracting just uncompressed if size matches?
# If failed, maybe it wasn't compressed? But we saw MZ in literals.
continue
# Determine name
name = f"assembly_{i:03d}.dll"
# Scan for internal name
# Search first 20KB for .dll names
search_area = decompressed[:min(20000, len(decompressed))]
# Regex: alphanumeric + . _ -
matches = re.finditer(b'([a-zA-Z0-9_.-]+\.dll)', search_area)
best_name = None
for match in matches:
try:
cand = match.group(1).decode('utf-8')
# Filter
if cand.lower() != 'mscoree.dll' and len(cand) > 4:
# Heuristic: usually starts with capital or specific words
if 'System' in cand or 'Microsoft' in cand or 'OneControl' in cand or 'Lippert' in cand or 'Plugin' in cand or 'Xamarin' in cand or 'Android' in cand:
best_name = cand
break
# Fallback
if not best_name:
best_name = cand
except:
continue
if best_name:
name = best_name
# Handle duplicates
output_path = os.path.join('extracted_final', name)
if os.path.exists(output_path):
counter = 2
base_name = name
while True:
name = f"{base_name[:-4]}_{counter}.dll"
output_path = os.path.join('extracted_final', name)
if not os.path.exists(output_path):
break
counter += 1
with open(output_path, 'wb') as out:
out.write(decompressed)
success_count += 1
if i % 50 == 0:
print(f"Processed {i}/{len(offsets)}...")
print(f"Successfully extracted {success_count} assemblies to extracted_final/")
# List Key Assemblies
print("\nKey Assemblies Found:")
for fname in sorted(os.listdir('extracted_final')):
if any(x in fname for x in ['OneControl', 'Plugin.BLE', 'IdsCan', 'MyRvLink']):
sz = os.path.getsize(os.path.join('extracted_final', fname))
print(f" {fname} ({sz} bytes)")
if __name__ == '__main__':
extract()