Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit 7953ee8

Browse files
committed
refactor: Split framework_modifier.py into modular package
Break down 628-line framework_modifier.py into focused modules: - src/core/modifiers/framework/patches.py: Smali patch constants (48 lines) - src/core/modifiers/framework/base.py: FrameworkModifierBase with utility methods (153 lines) - src/core/modifiers/framework/tasks.py: Specific modification tasks (690 lines) - src/core/modifiers/framework/modifier.py: Main FrameworkModifier class (59 lines) - src/core/modifiers/framework/__init__.py: Package exports Update src/core/modifiers/framework_modifier.py to be a compatibility shim Maintain backward compatibility for existing imports
1 parent be825d6 commit 7953ee8

5 files changed

Lines changed: 987 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Framework modifier package for smali patching operations.
2+
3+
This package provides the FrameworkModifier class and supporting components
4+
for modifying Android framework JARs including signature bypasses, PIF injection,
5+
and EU ROM compatibility patches.
6+
7+
Example:
8+
>>> from src.core.modifiers.framework import FrameworkModifier
9+
>>> modifier = FrameworkModifier(context)
10+
>>> modifier.run()
11+
"""
12+
13+
from __future__ import annotations
14+
15+
from src.core.modifiers.framework.modifier import FrameworkModifier
16+
from src.core.modifiers.framework.base import FrameworkModifierBase
17+
from src.core.modifiers.framework.tasks import FrameworkTasks
18+
from src.core.modifiers.framework.patches import (
19+
RETRUN_TRUE,
20+
RETRUN_FALSE,
21+
REMAKE_VOID,
22+
INVOKE_TRUE,
23+
PRELOADS_SHAREDUIDS,
24+
MY_PLATFORM_KEY,
25+
)
26+
27+
__all__ = [
28+
"FrameworkModifier",
29+
"FrameworkModifierBase",
30+
"FrameworkTasks",
31+
"RETRUN_TRUE",
32+
"RETRUN_FALSE",
33+
"REMAKE_VOID",
34+
"INVOKE_TRUE",
35+
"PRELOADS_SHAREDUIDS",
36+
"MY_PLATFORM_KEY",
37+
]
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"""Base class for framework modifier with utility methods."""
2+
3+
from __future__ import annotations
4+
5+
import re
6+
import shutil
7+
from pathlib import Path
8+
from typing import TYPE_CHECKING
9+
10+
from src.utils.shell import ShellRunner
11+
from src.core.modifiers.base_modifier import BaseModifier
12+
from src.core.modifiers.smali_args import SmaliArgs
13+
from src.utils.smalikit import SmaliKit
14+
15+
if TYPE_CHECKING:
16+
from src.core.context import PortingContext
17+
18+
19+
class FrameworkModifierBase(BaseModifier):
20+
"""Base class for framework-level modifications with utility methods."""
21+
22+
def __init__(self, context: PortingContext) -> None:
23+
super().__init__(context, "FrameworkModifier")
24+
self.shell = ShellRunner()
25+
self.bin_dir = Path("bin").resolve()
26+
27+
self.apktool_path = self.bin_dir / "apktool" / "apktool"
28+
self.apkeditor_path = self.bin_dir / "APKEditor.jar"
29+
self.baksmali_path = self.bin_dir / "baksmali.jar"
30+
31+
self.temp_dir = self.ctx.target_dir.parent / "temp_modifier"
32+
33+
def _run_smalikit(self, **kwargs) -> None:
34+
"""Run SmaliKit with given arguments."""
35+
args = SmaliArgs(**kwargs)
36+
patcher = SmaliKit(args, logger=self.logger)
37+
target = args.file_path if args.file_path else args.path
38+
if target:
39+
patcher.walk_and_patch(target)
40+
41+
def _apkeditor_decode(self, jar_path: Path, out_dir: Path) -> None:
42+
"""Decode JAR/APK using APKEditor."""
43+
self.shell.run_java_jar(
44+
self.apkeditor_path, ["d", "-f", "-i", str(jar_path), "-o", str(out_dir)]
45+
)
46+
47+
def _apkeditor_build(self, src_dir: Path, out_jar: Path) -> None:
48+
"""Build JAR/APK using APKEditor."""
49+
self.shell.run_java_jar(
50+
self.apkeditor_path, ["b", "-f", "-i", str(src_dir), "-o", str(out_jar)]
51+
)
52+
53+
def _find_file(self, root: Path, name_pattern: str) -> Path | None:
54+
"""Find file by name pattern recursively."""
55+
for p in Path(root).rglob(name_pattern):
56+
if p.is_file():
57+
return p
58+
return None
59+
60+
def _find_file_recursive(self, root: Path, name_pattern: str) -> Path | None:
61+
"""Alias for _find_file for backward compatibility."""
62+
return self._find_file(root, name_pattern)
63+
64+
def _replace_text_in_file(self, file_path: Path | None, old: str, new: str) -> None:
65+
"""Replace text in file if it exists."""
66+
if not file_path or not file_path.exists():
67+
return
68+
content = file_path.read_text(encoding="utf-8", errors="ignore")
69+
if old in content:
70+
new_content = content.replace(old, new)
71+
file_path.write_text(new_content, encoding="utf-8")
72+
self.logger.info(f"Patched {file_path.name}: {old[:20]}... -> {new[:20]}...")
73+
74+
def _copy_to_next_classes(self, work_dir: Path, source_dir: Path) -> None:
75+
"""Copy smali classes to next available classes directory."""
76+
max_num = 1
77+
for d in work_dir.glob("smali/classes*"):
78+
name = d.name
79+
if name == "classes":
80+
num = 1
81+
else:
82+
try:
83+
num = int(name.replace("classes", ""))
84+
except ValueError:
85+
num = 1
86+
if num > max_num:
87+
max_num = num
88+
89+
target = work_dir / "smali" / f"classes{max_num + 1}"
90+
shutil.copytree(source_dir, target, dirs_exist_ok=True)
91+
self.logger.info(f"Copied classes to {target.name}")
92+
93+
def _extract_register_from_invoke(
94+
self, content: str, method_signature: str, invoke_signature: str, arg_index: int = 1
95+
) -> str | None:
96+
"""Extract register name from invoke instruction in method."""
97+
method_pattern = re.compile(
98+
rf"\.method[^\n]*?{re.escape(method_signature)}(.*?)\.end method", re.DOTALL
99+
)
100+
method_match = method_pattern.search(content)
101+
102+
if not method_match:
103+
self.logger.warning(f"Target method not found: {method_signature}")
104+
return None
105+
106+
method_body = method_match.group(1)
107+
108+
invoke_pattern = re.compile(rf"invoke-\w+\s+{{(.*?)}}\s*,\s+{re.escape(invoke_signature)}")
109+
invoke_match = invoke_pattern.search(method_body)
110+
111+
if not invoke_match:
112+
self.logger.warning(f"Invoke signature not found in method body: {invoke_signature}")
113+
return None
114+
115+
matched_regs_str = invoke_match.group(1)
116+
reg_list = [r.strip() for r in matched_regs_str.split(",") if r.strip()]
117+
118+
if arg_index < len(reg_list):
119+
extracted_reg = reg_list[arg_index]
120+
self.logger.debug(f"Extracted register {extracted_reg} from {method_signature}")
121+
return extracted_reg
122+
else:
123+
self.logger.warning(f"arg_index {arg_index} out of bounds for registers: {reg_list}")
124+
return None
125+
126+
def _extract_register_from_local(
127+
self, content: str, method_signature: str, local_name: str
128+
) -> str | None:
129+
"""Extract register name from .local declaration or move-object instructions."""
130+
method_pattern = re.compile(
131+
rf"\.method[^\n]*?{re.escape(method_signature)}(.*?)\.end method", re.DOTALL
132+
)
133+
method_match = method_pattern.search(content)
134+
if not method_match:
135+
return None
136+
137+
body = method_match.group(1)
138+
139+
local_pattern = re.compile(rf"\.local\s+([vp]\d+),\s+{re.escape(local_name)}[;:,]")
140+
match = local_pattern.search(body)
141+
if match:
142+
return match.group(1)
143+
144+
if local_name == '"descriptor"':
145+
move_match = re.search(r"move-object(?:\/from16)?\s+([vp]\d+),\s+p1", body)
146+
if move_match:
147+
return move_match.group(1)
148+
elif local_name == '"args"':
149+
move_match = re.search(r"move-object(?:\/from16)?\s+([vp]\d+),\s+p3", body)
150+
if move_match:
151+
return move_match.group(1)
152+
153+
return None
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Framework-level modifications (smali patching)."""
2+
3+
from __future__ import annotations
4+
5+
import concurrent.futures
6+
from pathlib import Path
7+
from typing import TYPE_CHECKING
8+
9+
from src.core.modifiers.framework.tasks import FrameworkTasks
10+
from src.core.modifiers.framework.patches import (
11+
RETRUN_TRUE,
12+
RETRUN_FALSE,
13+
REMAKE_VOID,
14+
INVOKE_TRUE,
15+
PRELOADS_SHAREDUIDS,
16+
)
17+
18+
if TYPE_CHECKING:
19+
from src.core.context import PortingContext
20+
21+
22+
class FrameworkModifier(FrameworkTasks):
23+
"""Handles framework-level modifications (smali patching).
24+
25+
This class orchestrates the modification of framework JARs including:
26+
- miui-services.jar modifications for EU ROM compatibility
27+
- services.jar modifications for signature verification bypass
28+
- framework.jar modifications for PropsHook, PIF injection, and signature bypass
29+
- Xiaomi.eu Toolbox injection
30+
"""
31+
32+
def __init__(self, context: PortingContext) -> None:
33+
super().__init__(context)
34+
# Re-export patches as instance attributes for backward compatibility
35+
self.RETRUN_TRUE = RETRUN_TRUE
36+
self.RETRUN_FALSE = RETRUN_FALSE
37+
self.REMAKE_VOID = REMAKE_VOID
38+
self.INVOKE_TRUE = INVOKE_TRUE
39+
self.PRELOADS_SHAREDUIDS = PRELOADS_SHAREDUIDS
40+
41+
def run(self) -> None:
42+
"""Execute all framework modifications."""
43+
self.logger.info("Starting Framework Modification...")
44+
self.temp_dir.mkdir(parents=True, exist_ok=True)
45+
46+
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
47+
futures = []
48+
futures.append(executor.submit(self._mod_miui_services))
49+
futures.append(executor.submit(self._mod_services))
50+
futures.append(executor.submit(self._mod_framework))
51+
52+
for future in concurrent.futures.as_completed(futures):
53+
try:
54+
future.result()
55+
except Exception as e:
56+
self.logger.error(f"Framework modification failed: {e}")
57+
58+
self._inject_xeu_toolbox()
59+
self.logger.info("Framework Modification Completed.")
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Smali patch constants for framework modifications."""
2+
3+
from __future__ import annotations
4+
5+
# Return value shortcuts
6+
RETRUN_TRUE = ".locals 1\n const/4 v0, 0x1\n return v0"
7+
RETRUN_FALSE = ".locals 1\n const/4 v0, 0x0\n return v0"
8+
REMAKE_VOID = ".locals 0\n return-void"
9+
10+
# Hook helper invocations
11+
INVOKE_TRUE = "invoke-static {}, Lcom/android/internal/util/HookHelper;->RETURN_TRUE()Z"
12+
13+
# Special patches
14+
PRELOADS_SHAREDUIDS = """.locals 1
15+
invoke-static {}, Lcom/android/internal/util/HookHelper;->RETURN_TRUE()Z
16+
move-result v0
17+
sput-boolean v0, Lcom/android/server/pm/ReconcilePackageUtils;->ALLOW_NON_PRELOADS_SYSTEM_SHAREDUIDS:Z
18+
return-void"""
19+
20+
# Common method body replacements
21+
REMAKE_VOID = ".locals 0\n return-void"
22+
REMAKE_FALSE = ".locals 1\n const/4 v0, 0x0\n return v0"
23+
REMAKE_TRUE = ".locals 1\n const/4 v0, 0x1\n return v0"
24+
25+
# Custom platform key for ExtraPackageManager
26+
MY_PLATFORM_KEY = (
27+
"308203bb308202a3a00302010202146a0b4f6a1a8f61a32d8450ead92d479dea486573300d06092a864886f70d01"
28+
"010b0500306c310b300906035504061302434e3110300e06035504080c075369436875616e3110300e0603550407"
29+
"0c074368656e6744753110300e060355040a0c07504f5254524f4d31133011060355040b0c0a4d61696e7461696e"
30+
"65723112301006035504030c09427275636554656e673020170d3236303230323031333632385a180f323035333036"
31+
"32303031333632385a306c310b300906035504061302434e3110300e06035504080c075369436875616e3110300e"
32+
"06035504070c074368656e6744753110300e060355040a0c07504f5254524f4d31133011060355040b0c0a4d6169"
33+
"6e7461696e65723112301006035504030c09427275636554656e6730820122300d06092a864886f70d0101010500"
34+
"0382010f003082010a0282010100cb68bcf8927a175624a0a7428f1bbd67b4cf18c8ba42b73de9649fd2aa42935b"
35+
"9195b27ccd611971056654db51499ffa01783a1dbc95e03f9c557d4930193c3d04f9016a84411b502ea844fac9d46"
36+
"3b4c9eed2d73ca3267b8a399f5da254941c7413d2a7534fd30a4ed10567933bfda249e2027ce74da667de3b62788"
37+
"44d232e038c2c98deb7d172a44b2fd9ec90ea74cb1c96b647044c60ce18cec93b60b84065ddd8800e10bcf465e4f"
38+
"3ace6d423ef2b235d75081e36b5d0f1ca858090d3dd8d74437ebb504490a8e7e9e3e2b696c3ac8e2ec856bedf4ef"
39+
"e4e05e14f2437f81fbc8428aa330cdde0816450b4416e10f743204c17ee65b92ebc61799b4cf42b0203010001a35"
40+
"33051301d0603551d0e041604140a318d86cc0040341341b6dc716094da06cd4dd6301f0603551d2304183016801"
41+
"40a318d86cc0040341341b6dc716094da06cd4dd6300f0603551d130101ff040530030101ff300d06092a864886f"
42+
"70d01010b0500038201010023e7aeda5403f40c794504e3edf99182a5eb53c9ddec0d93fd9fe6539e1520ea6ad08"
43+
"ac3215555f3fe366fa6ab01e0f45d6ce1512416c572f387a72408dde6442b76e405296cc8c128844fe68a29f6a11"
44+
"4eb6f303e3545ea0b32d85e9c7d45cfa3c860b03d00171bb2aa4434892bf484dd390643f324a2e38a5e6ce7f26e9"
45+
"2b3d02ac8605514b9c75a8aab9ab990c01951213f7214a36389c0759cfb68737bb3bb85dff4b1b40377279e2c82"
46+
"298351c276ab266869d6494b838bd6cc175185f705b8806eb1950becec57fb4f9b50240bb92d1d30bbb5764d311d"
47+
"18446588e5fd2b9785c635f2bb690df1e4fb595305371350c6d306d3f6cae3bc4974e9d8609c"
48+
)

0 commit comments

Comments
 (0)