161 lines
7.6 KiB
Python
161 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Patch Symphonium 14.1.0 — nuke all DRM, Firebase, license checks, and telemetry."""
|
|
|
|
import os, shutil
|
|
|
|
BASE = '/home/icetop/Music app fix/apktool_output_v4'
|
|
|
|
def replace_method(filepath, method_sig, replacement, backup=True):
|
|
"""Find and replace a method in a smali file. Returns lines removed."""
|
|
if backup:
|
|
shutil.copy2(filepath, filepath + '.bak')
|
|
with open(filepath) as f:
|
|
lines = f.readlines()
|
|
# Find method start
|
|
s = None
|
|
for i, line in enumerate(lines):
|
|
if method_sig in line:
|
|
s = i
|
|
break
|
|
assert s is not None, f'{method_sig} not found in {filepath}'
|
|
e = s
|
|
while e < len(lines) and '.end method' not in lines[e]:
|
|
e += 1
|
|
assert '.end method' in lines[e], f'.end method not found after line {s} in {filepath}'
|
|
removed = e - s + 1
|
|
lines = lines[:s] + [replacement + '\n'] + lines[e+1:]
|
|
with open(filepath, 'w') as f:
|
|
f.writelines(lines)
|
|
return removed
|
|
|
|
|
|
# ─── 1. Patch nh3.smali (FirebaseManager license class) ───
|
|
nh3_path = f'{BASE}/smali/nh3.smali'
|
|
shutil.copy2(nh3_path, nh3_path + '.bak')
|
|
|
|
with open(nh3_path) as f:
|
|
lines = f.readlines()
|
|
|
|
patches = [
|
|
('m', 4205, '.method public final m()Z\n .locals 2\n\n iget-object v0, p0, Lnh3;->x:Lhg5;\n\n const/4 v1, 0x1\n\n iput-boolean v1, v0, Lhg5;->y:Z\n\n const/4 v0, 0x1\n\n return v0\n.end method\n'),
|
|
('h', 3102, '.method public final h(Lxo1;)Ljava/lang/Object;\n .locals 1\n\n const/4 v0, 0x0\n\n invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;\n\n move-result-object v0\n\n return-object v0\n.end method\n'),
|
|
('k', 3753, '.method public final k(Lxo1;)Ljava/lang/Object;\n .locals 1\n\n sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;\n\n return-object v0\n.end method\n'),
|
|
('j', 3526, '.method public final j(Lxo1;)Ljava/lang/Object;\n .locals 1\n\n sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;\n\n return-object v0\n.end method\n'),
|
|
('d', 1860, '.method public final d(Ljava/lang/String;Ljava/lang/String;Lxo1;)Ljava/lang/Object;\n .locals 1\n\n const/4 v0, 0x0\n\n return-object v0\n.end method\n'),
|
|
('r', 5444, '.method public final r(Lxo1;)Ljava/io/Serializable;\n .locals 1\n\n const/4 v0, 0x0\n\n return-object v0\n.end method\n'),
|
|
('b', 1270, '.method public final b(Lxo1;)Ljava/lang/Object;\n .locals 1\n\n sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;\n\n return-object v0\n.end method\n'),
|
|
('c', 1463, '.method public final c(Ljava/lang/String;Ljava/lang/String;Lxo1;)Ljava/lang/Object;\n .locals 1\n\n const/4 v0, 0x0\n\n return-object v0\n.end method\n'),
|
|
]
|
|
|
|
for name, start_line, stub in patches:
|
|
s = start_line - 1
|
|
found = -1
|
|
for i in range(max(0, s - 5), min(len(lines), s + 5)):
|
|
if '.method public final ' + name in lines[i]:
|
|
found = i
|
|
break
|
|
if found == -1:
|
|
for i in range(len(lines)):
|
|
if '.method public final ' + name in lines[i]:
|
|
found = i
|
|
break
|
|
assert found >= 0, f'{name}() method not found!'
|
|
s = found
|
|
e = s
|
|
while e < len(lines) and '.end method' not in lines[e]:
|
|
e += 1
|
|
assert '.end method' in lines[e], f'{name}() .end method not found at offset {s}'
|
|
removed = e - s + 1
|
|
lines = lines[:s] + [stub] + lines[e+1:]
|
|
for j in range(len(patches)):
|
|
if patches[j][1] > start_line:
|
|
patches[j] = (patches[j][0], patches[j][1] - removed + stub.count('\n'), patches[j][2])
|
|
print(f' nh3.{name}() — removed {removed} lines')
|
|
|
|
with open(nh3_path, 'w') as f:
|
|
f.writelines(lines)
|
|
print(f'nh3.smali: {len(lines)} lines')
|
|
|
|
# ─── 2. Patch LibSymfonikWrapper.smali ───
|
|
lw_path = f'{BASE}/smali/app/symfonik/crypto/internal/LibSymfonikWrapper.smali'
|
|
shutil.copy2(lw_path, lw_path + '.bak')
|
|
|
|
with open(lw_path) as f:
|
|
lines = f.readlines()
|
|
|
|
s = 239 - 1
|
|
e = s
|
|
while e < len(lines) and '.end method' not in lines[e]:
|
|
e += 1
|
|
assert '.method public final b' in lines[s]
|
|
|
|
stub = (
|
|
'.method public final b(Lvz3;)V\n'
|
|
' .locals 2\n\n'
|
|
' sput-object p1, Lapp/symfonik/crypto/internal/LibSymfonikWrapper;->c:Lvz3;\n\n'
|
|
' :try_start_0\n'
|
|
' const-string v0, "symfonik"\n'
|
|
' invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\n'
|
|
' :try_end_0\n'
|
|
' .catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0\n\n'
|
|
' :catch_0\n'
|
|
' sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;\n\n'
|
|
' sput-object v0, Lapp/symfonik/crypto/internal/LibSymfonikWrapper;->b:Ljava/lang/Boolean;\n\n'
|
|
' check-cast p1, Lapp/symfonik/init/HandlerInitializable$handler$1;\n\n'
|
|
' sget-object v0, Lrc5;->a:Landroid/os/Handler;\n\n'
|
|
' iget-object p1, p1, Lapp/symfonik/init/HandlerInitializable$handler$1;->a:La04;\n\n'
|
|
' new-instance v1, Lql;\n\n'
|
|
' const/16 v2, 0x14\n\n'
|
|
' invoke-direct {v1, v2, p1}, Lql;-><init>(ILjava/lang/Object;)V\n\n'
|
|
' invoke-virtual {v0, v1}, Landroid/os/Handler;->post(Ljava/lang/Runnable;)Z\n\n'
|
|
' return-void\n'
|
|
'.end method\n'
|
|
)
|
|
removed = e - s + 1
|
|
lines = lines[:s] + [stub] + lines[e+1:]
|
|
with open(lw_path, 'w') as f:
|
|
f.writelines(lines)
|
|
print(f'LibSymfonikWrapper.smali: {len(lines)} lines (removed {removed})')
|
|
|
|
# ─── 3. TELEMETRY NUKE ───
|
|
print()
|
|
|
|
# 3a. FirebaseAnalytics.smali — kill logEvent() and setCurrentScreen()
|
|
fa_path = f'{BASE}/smali/com/google/firebase/analytics/FirebaseAnalytics.smali'
|
|
r = replace_method(fa_path, '.method public final a(Ljava/lang/String;Landroid/os/Bundle;)V',
|
|
'.method public final a(Ljava/lang/String;Landroid/os/Bundle;)V\n .locals 0\n\n return-void\n.end method')
|
|
print(f'FirebaseAnalytics.a(logEvent) — removed {r} lines')
|
|
|
|
r = replace_method(fa_path, '.method public setCurrentScreen(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V',
|
|
'.method public setCurrentScreen(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V\n .locals 0\n\n return-void\n.end method')
|
|
print(f'FirebaseAnalytics.setCurrentScreen() — removed {r} lines')
|
|
|
|
# 3b. oa2.smali (Matomo/Piwik tracker) — kill dispatch
|
|
oa2_path = f'{BASE}/smali/oa2.smali'
|
|
r = replace_method(oa2_path, '.method public final b(Llpa;)V',
|
|
'.method public final b(Llpa;)V\n .locals 0\n\n return-void\n.end method')
|
|
print(f'Matomo oa2.b(dispatch) — removed {r} lines')
|
|
|
|
# 3c. b70.smali (Crashlytics settings + report upload) — kill settings fetch
|
|
b70_path = f'{BASE}/smali/b70.smali'
|
|
r = replace_method(b70_path, '.method public j(Lu10;)Ljava/lang/Object;',
|
|
'.method public j(Lu10;)Ljava/lang/Object;\n .locals 1\n\n const/4 v0, 0x0\n\n return-object v0\n.end method')
|
|
print(f'Crashlytics b70.j(settings) — removed {r} lines')
|
|
|
|
# 3d. vg3.smali (Firebase Installations HTTP) — kill connection creator
|
|
vg3_path = f'{BASE}/smali/vg3.smali'
|
|
r = replace_method(vg3_path, '.method public final c(Ljava/net/URL;Ljava/lang/String;)Ljava/net/HttpURLConnection;',
|
|
'.method public final c(Ljava/net/URL;Ljava/lang/String;)Ljava/net/HttpURLConnection;\n .locals 1\n\n const/4 v0, 0x0\n\n return-object v0\n.end method')
|
|
print(f'FirebaseInst.vg3.c(connection) — removed {r} lines')
|
|
|
|
# ─── 4. Lower minSdkVersion ───
|
|
yml_path = f'{BASE}/apktool.yml'
|
|
with open(yml_path) as f:
|
|
yml = f.read()
|
|
yml = yml.replace('minSdkVersion: 32', 'minSdkVersion: 26')
|
|
with open(yml_path, 'w') as f:
|
|
f.write(yml)
|
|
print(f'\napktool.yml: minSdkVersion 32 → 26')
|
|
|
|
print('\nAll patches applied. Ready for build.')
|