Compare commits

...

15 Commits

Author SHA1 Message Date
topjohnwu
ff4ca74cfe Ban all unwrap usage in codebase 2025-12-08 23:31:04 -08:00
topjohnwu
200665c48a Make jni_hooks.hpp a normal C++ header
[skip ci]
2025-12-07 02:44:19 -08:00
topjohnwu
dd42aa99ea Refactor gen_jni_hooks.py
[skip ci]
2025-12-07 02:25:50 -08:00
Wang Han
0936cdb192 Update nativeForkAndSpecialize signature for A16 QPR2
67a4b1b2fe
2025-12-07 00:18:50 -08:00
topjohnwu
871643dce2 Enable CI for API 36.1 2025-12-07 00:18:50 -08:00
topjohnwu
a510554b21 Disable Zygisk upon incomplete JNI hook
Do not enable Zygisk unless ALL replacements are hooked properly.
This allow Zygisk tests to fail when new signature is introduced.
2025-12-07 00:18:50 -08:00
Arbri çoçka
9cc830c565 Update strings.xml values_sq 2025-12-05 16:22:03 -08:00
hajs1664
ddbac50645 update korean translation 2025-12-05 11:15:21 -08:00
南宫雪珊
b5138a4af0 Update unpack boot image help message 2025-12-05 11:14:45 -08:00
topjohnwu
64752f38e8 Do not unwrap when getting decoder and encoder
Or else things will crash mysteriously when unexpected input occurs
2025-12-05 03:40:18 -08:00
topjohnwu
9ac4b5ce7d Add proper lzma format detection 2025-12-05 03:40:18 -08:00
topjohnwu
505053f9b4 Properly support AVD with minor SDK version 2025-12-04 20:55:46 -08:00
topjohnwu
ccb264f33a Release Magisk v30.6
[skip ci]
2025-12-01 15:46:33 -08:00
topjohnwu
84f7d75d30 Update release.sh
Strip out all canary build logic
2025-12-01 15:27:01 -08:00
南宫雪珊
9a776c22d9 Revert "Use rootfs for magisktmp if possible" 2025-12-01 11:45:34 -08:00
42 changed files with 1152 additions and 742 deletions

View File

@@ -82,7 +82,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, "CANARY"] version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36.1, "CANARY"]
type: [""] type: [""]
include: include:
- version: "CANARY" - version: "CANARY"

View File

@@ -15,18 +15,18 @@
<string name="app_changelog">앱 변경 사항</string> <string name="app_changelog">앱 변경 사항</string>
<string name="loading">로딩중…</string> <string name="loading">로딩중…</string>
<string name="update">업데이트</string> <string name="update">업데이트</string>
<string name="not_available">N/A</string> <string name="not_available">알 수 없음</string>
<string name="hide">숨기기</string> <string name="hide">숨기기</string>
<string name="home_package">패키지</string> <string name="home_package">패키지</string>
<string name="home_app_title"></string> <string name="home_app_title"></string>
<string name="home_notice_content">공식 Github 페이지에서 Magisk를 다운로드하십시오. 알 수 없는 소스의 파일이 악의적일 수 있습니다!</string> <string name="home_notice_content">공식 Github 페이지에서 Magisk를 다운로드하십시오. 알 수 없는 출처에서 받은 파일이 위험할 수 있습니다!</string>
<string name="home_support_title">후원하기</string> <string name="home_support_title">후원하기</string>
<string name="home_item_source">소스</string> <string name="home_item_source">소스</string>
<string name="home_support_content">Magisk는 항상 무료일 것이며, 오픈소스일 것입니다. 그러나 소액의 후원을 통해 관심을 표할 수 있습니다.</string> <string name="home_support_content">Magisk는 항상 무료일 것이며, 오픈소스일 것입니다. 그러나 소액의 후원을 통해 관심을 표할 수 있습니다.</string>
<string name="home_installed_version">설치됨</string> <string name="home_installed_version">설치됨</string>
<string name="home_latest_version">최신</string> <string name="home_latest_version">최신</string>
<string name="invalid_update_channel">올바르지 않은 업데이트 채널</string> <string name="invalid_update_channel">잘못된 업데이트 채널</string>
<string name="uninstall_magisk_title">Magisk 제거</string> <string name="uninstall_magisk_title">Magisk 제거</string>
<string name="uninstall_magisk_msg">모든 모듈이 비활성화/제거됩니다. 루트도 제거될 것이며, 데이터도 암호화 되어있지 않으면 암호화될 수도 있습니다.</string> <string name="uninstall_magisk_msg">모든 모듈이 비활성화/제거됩니다. 루트도 제거될 것이며, 데이터도 암호화 되어있지 않으면 암호화될 수도 있습니다.</string>
@@ -51,11 +51,11 @@
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">슈퍼유저 요청</string> <string name="su_request_title">슈퍼유저 요청</string>
<string name="touch_filtered_warning">앱이 슈퍼유저 요청을 가려, Magisk에서 응답을 확인할 수 없습니다.</string> <string name="touch_filtered_warning">앱이 슈퍼유저 요청을 가려, Magisk에서 응답을 확인할 수 없습니다.</string>
<string name="deny">일괄 거부</string> <string name="deny">모두 거부</string>
<string name="prompt">수동 허가</string> <string name="prompt">물어보기</string>
<string name="grant">일괄 허용</string> <string name="grant">모두 허용</string>
<string name="su_warning">기기에 대한 전체 액세스 권한을 부여합니다.\n확실하지 않은 경우 거부하세요!</string> <string name="su_warning">기기에 대한 슈퍼유저 권한을 부여합니다.\n확실하지 않은 경우 거부하세요!</string>
<string name="forever">영구적으로</string> <string name="forever">영구</string>
<string name="once">한 번만</string> <string name="once">한 번만</string>
<string name="tenmin">10분</string> <string name="tenmin">10분</string>
<string name="twentymin">20분</string> <string name="twentymin">20분</string>
@@ -69,20 +69,20 @@
<string name="su_snack_notif_off">%1$s의 알림이 비활성화됨</string> <string name="su_snack_notif_off">%1$s의 알림이 비활성화됨</string>
<string name="su_snack_log_on">%1$s의 로깅이 활성화됨</string> <string name="su_snack_log_on">%1$s의 로깅이 활성화됨</string>
<string name="su_snack_log_off">%1$s의 로깅이 비활성화됨</string> <string name="su_snack_log_off">%1$s의 로깅이 비활성화됨</string>
<string name="su_revoke_title">취소하시겠습니까?</string> <string name="su_revoke_title">제거하시겠습니까?</string>
<string name="su_revoke_msg">정말 %1$s의 권한을 취소하시겠습니까?</string> <string name="su_revoke_msg">정말 %1$s의 권한을 제거하시겠습니까?</string>
<string name="toast">토스트</string> <string name="toast">토스트</string>
<string name="none">없음</string> <string name="none">없음</string>
<string name="superuser_toggle_notification">알림</string> <string name="superuser_toggle_notification">알림</string>
<string name="superuser_toggle_revoke">권한삭제</string> <string name="superuser_toggle_revoke">권한 제거</string>
<string name="superuser_policy_none">슈퍼유저 권한을 요청한 앱이 없습니다.</string> <string name="superuser_policy_none">슈퍼유저 권한을 요청한 앱이 없습니다.</string>
<!--Logs--> <!--Logs-->
<string name="log_data_none">로그가 없습니다. 슈퍼유저 권한을 필요로 하는 앱을 사용하십시오.</string> <string name="log_data_none">로그가 없습니다. 슈퍼유저 권한을 필요로 하는 앱을 사용하십시오.</string>
<string name="log_data_magisk_none">Magisk 로그가 없습니다.</string> <string name="log_data_magisk_none">Magisk 로그가 없습니다.</string>
<string name="menuSaveLog">로그 저장</string> <string name="menuSaveLog">로그 저장</string>
<string name="menuClearLog">지금 로그 삭제</string> <string name="menuClearLog">로그 삭제</string>
<string name="logs_cleared">로그 삭제 완료.</string> <string name="logs_cleared">로그 삭제 완료.</string>
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
<string name="target_uid">Target UID: %1$d</string> <string name="target_uid">Target UID: %1$d</string>
@@ -96,9 +96,9 @@
<string name="hide_search">검색</string> <string name="hide_search">검색</string>
<!--Module--> <!--Module-->
<string name="no_info_provided">(제공된 정보 없음)</string> <string name="no_info_provided">(정보 없음)</string>
<string name="reboot_userspace">조용히 다시 시작</string> <string name="reboot_userspace">조용히 다시 시작</string>
<string name="reboot_recovery">리커버리로 다시 시작</string> <string name="reboot_recovery">복구 모드로 다시 시작</string>
<string name="reboot_bootloader">부트로더로 다시 시작</string> <string name="reboot_bootloader">부트로더로 다시 시작</string>
<string name="reboot_download">다운로드 모드로 다시 시작</string> <string name="reboot_download">다운로드 모드로 다시 시작</string>
<string name="reboot_edl">EDL로 다시 시작</string> <string name="reboot_edl">EDL로 다시 시작</string>
@@ -107,20 +107,20 @@
<string name="module_state_restore">복구</string> <string name="module_state_restore">복구</string>
<string name="module_action_install_external">저장소에서 설치</string> <string name="module_action_install_external">저장소에서 설치</string>
<string name="update_available">업데이트 가능</string> <string name="update_available">업데이트 가능</string>
<string name="suspend_text_riru">%1$s 가 활성화 되어있어 모듈 로드가 일시정지 되었습니다.</string> <string name="suspend_text_riru">%1$s 가 활성화 되어있어 모듈 로드되지 않았습니다.</string>
<string name="suspend_text_zygisk">%1$s 가 활성화 되어있어 모듈이 로드되지 않았습니다.</string> <string name="suspend_text_zygisk">%1$s 가 활성화되어 있지 않아 모듈이 로드되지 않았습니다.</string>
<string name="zygisk_module_unloaded">호환성 문제로 인해 Zygisk 모듈이 로드되지 않았습니다.</string> <string name="zygisk_module_unloaded">호환성 문제로 인해 Zygisk 모듈이 로드되지 않았습니다.</string>
<!--Settings--> <!--Settings-->
<string name="settings_dark_mode_title">테마 선택</string> <string name="settings_dark_mode_title">테마 선택</string>
<string name="settings_dark_mode_message">원하는 테마 모드를 선택하세요!</string> <string name="settings_dark_mode_message">원하는 테마 모드를 선택하세요!</string>
<string name="settings_dark_mode_light">기본</string> <string name="settings_dark_mode_light">기본</string>
<string name="settings_dark_mode_system">시스템 설정값</string> <string name="settings_dark_mode_system">자동</string>
<string name="settings_dark_mode_dark">다크 모드</string> <string name="settings_dark_mode_dark">다크 모드</string>
<string name="settings_download_path_title">다운로드 경로</string> <string name="settings_download_path_title">다운로드 위치</string>
<string name="settings_download_path_message">파일이 %1$s에 저장됩니다</string> <string name="settings_download_path_message">파일이 %1$s에 저장됩니다</string>
<string name="settings_hide_app_title">Magisk 앱 숨기기</string> <string name="settings_hide_app_title">Magisk 앱 숨기기</string>
<string name="settings_hide_app_summary">랜덤 패키지 ID와 커스텀 앱 이름으로 Magisk 프록시 앱을 설치합니다.</string> <string name="settings_hide_app_summary">무작위 패키지명과 사용자 지정 앱 이름으로 Magisk 프록시 앱을 설치합니다.</string>
<string name="settings_restore_app_title">Magisk 앱 복원</string> <string name="settings_restore_app_title">Magisk 앱 복원</string>
<string name="settings_restore_app_summary">앱 숨기기를 해제하고 원래 APK로 복원합니다.</string> <string name="settings_restore_app_summary">앱 숨기기를 해제하고 원래 APK로 복원합니다.</string>
<string name="language">언어</string> <string name="language">언어</string>
@@ -167,12 +167,12 @@
<string name="settings_doh_description">일부 국가에 존재하는 DNS 포이즈닝을 해결합니다.</string> <string name="settings_doh_description">일부 국가에 존재하는 DNS 포이즈닝을 해결합니다.</string>
<string name="multiuser_mode">다중 사용자 모드</string> <string name="multiuser_mode">다중 사용자 모드</string>
<string name="settings_owner_only">기기 소유자만</string> <string name="settings_owner_only">주인 사용자만</string>
<string name="settings_owner_manage">기기 소유자에 의해 관리됨</string> <string name="settings_owner_manage">주인 사용자에 의해 관리됨</string>
<string name="settings_user_independent">사용자별</string> <string name="settings_user_independent">사용자별 분리</string>
<string name="owner_only_summary">소유자만 루트 액세스를 갖습니다.</string> <string name="owner_only_summary">주인 사용자만 루트 액세스를 갖습니다.</string>
<string name="owner_manage_summary">소유자만 루트 액세스를 관리하고 요청을 받을 수 있습니다.</string> <string name="owner_manage_summary">주인 사용자가 다른 사용자들의 루트 액세스를 관리하고 요청을 받을 수 있습니다.</string>
<string name="user_independent_summary">각각의 사용자가 개별적인 권한을 갖습니다.</string> <string name="user_independent_summary">각각의 사용자가 권한을 관리합니다.</string>
<string name="mount_namespace_mode">네임스페이스 마운트 모드</string> <string name="mount_namespace_mode">네임스페이스 마운트 모드</string>
<string name="settings_ns_global">전역 네임스페이스</string> <string name="settings_ns_global">전역 네임스페이스</string>
@@ -187,7 +187,7 @@
<string name="progress_channel">진행 상황</string> <string name="progress_channel">진행 상황</string>
<string name="updated_channel">업데이트 완료</string> <string name="updated_channel">업데이트 완료</string>
<string name="download_complete">다운로드 완료</string> <string name="download_complete">다운로드 완료</string>
<string name="download_file_error">파일 다운로드 오류</string> <string name="download_file_error">파일 다운로드 실패</string>
<string name="magisk_update_title">새 버전의 Magisk를 사용할 수 있습니다!</string> <string name="magisk_update_title">새 버전의 Magisk를 사용할 수 있습니다!</string>
<string name="updated_title">Magisk가 업데이트 되었습니다!</string> <string name="updated_title">Magisk가 업데이트 되었습니다!</string>
<string name="updated_text">터치하여 앱 열기</string> <string name="updated_text">터치하여 앱 열기</string>
@@ -208,7 +208,7 @@
<string name="restore_img">이미지 복구</string> <string name="restore_img">이미지 복구</string>
<string name="restore_img_msg">복구하는 중…</string> <string name="restore_img_msg">복구하는 중…</string>
<string name="restore_done">복구 완료!</string> <string name="restore_done">복구 완료!</string>
<string name="restore_fail">백업이 존재하지 않습니다!</string> <string name="restore_fail">백업이 존재하지 않습니다!</string>
<string name="setup_fail">설치 실패</string> <string name="setup_fail">설치 실패</string>
<string name="env_fix_title">추가 설정 필요</string> <string name="env_fix_title">추가 설정 필요</string>
<string name="env_fix_msg">Magisk가 제대로 작동하려면 추가 설정이 필요합니다. 다시 시작 하시겠습니까?</string> <string name="env_fix_msg">Magisk가 제대로 작동하려면 추가 설정이 필요합니다. 다시 시작 하시겠습니까?</string>
@@ -219,10 +219,10 @@
<string name="unsupport_system_app_msg">해당 앱을 시스템 앱으로 실행하는 것은 지원되지 않습니다. 앱을 일반 사용자 앱으로 실행해 주세요.</string> <string name="unsupport_system_app_msg">해당 앱을 시스템 앱으로 실행하는 것은 지원되지 않습니다. 앱을 일반 사용자 앱으로 실행해 주세요.</string>
<string name="unsupport_other_su_msg">Magisk으로 부터 설치되지 않은 \"su\" 바이너리가 감지되었습니다. 다른 루팅 방법을 제거하거나, Magisk 를 다시 설치해주세요.</string> <string name="unsupport_other_su_msg">Magisk으로 부터 설치되지 않은 \"su\" 바이너리가 감지되었습니다. 다른 루팅 방법을 제거하거나, Magisk 를 다시 설치해주세요.</string>
<string name="unsupport_external_storage_msg">Magisk 가 외부 저장소에 설치되어 있습니다. Magisk 를 내부 저장소에 설치 해주세요.</string> <string name="unsupport_external_storage_msg">Magisk 가 외부 저장소에 설치되어 있습니다. Magisk 를 내부 저장소에 설치 해주세요.</string>
<string name="unsupport_nonroot_stub_msg">숨겨진 Magisk 앱은 루팅이 풀려 더이상 작동하지 못합니다. 래 APK 를 복원하거나 재설치 해주세요.</string> <string name="unsupport_nonroot_stub_msg">숨겨진 Magisk 앱은 루트 권한이 손실되어 사용할 수 없습니다. 래 APK 를 복원하거나 재설치 해주세요.</string>
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string> <string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
<string name="external_rw_permission_denied">해당 기능을 사용하려면 저장소 권한을 허용해 주십시오.</string> <string name="external_rw_permission_denied">해당 기능을 사용하려면 저장소 권한을 허용해 주십시오.</string>
<string name="install_unknown_denied">이 기능을 활성화 하려면 "알 수 없는 앱 설치"를 허용해주세요.</string> <string name="install_unknown_denied">이 기능을 활성화 하려면 "출처를 알 수 없는 앱 설치"를 허용해주세요.</string>
<string name="add_shortcut_title">홈 화면에 바로가기 추가</string> <string name="add_shortcut_title">홈 화면에 바로가기 추가</string>
<string name="add_shortcut_msg">앱을 숨긴 후 아이콘과 이름을 알아보기 힘들 경우를 위해 알아보기 쉬운 바로가기를 홈 화면에 추가합니다.</string> <string name="add_shortcut_msg">앱을 숨긴 후 아이콘과 이름을 알아보기 힘들 경우를 위해 알아보기 쉬운 바로가기를 홈 화면에 추가합니다.</string>
<string name="app_not_found">해당 작업을 처리할 어플리케이션이 없습니다.</string> <string name="app_not_found">해당 작업을 처리할 어플리케이션이 없습니다.</string>

View File

@@ -2,242 +2,255 @@
<!--Sections--> <!--Sections-->
<string name="modules">Modulet</string> <string name="modules">Modulet</string>
<string name="superuser">Super-përdoruesi</string> <string name="superuser">Superuser</string>
<string name="logs">Regjistrat</string> <string name="logs">Regjistrimet</string>
<string name="settings">Cilësimet</string> <string name="settings">Parametrat</string>
<string name="install">Instalo</string> <string name="install">Instalo</string>
<string name="section_home">Shtëpi</string> <string name="section_home">Shtëpia</string>
<string name="section_theme">Tema</string> <string name="section_theme">Temat</string>
<string name="denylist">Lista e mohimit</string> <string name="denylist">Lista e ndaluar</string>
<!--Home--> <!--Home-->
<string name="no_connection">Nuk ka lidhje interneti</string> <string name="no_connection">Nuk ka lidhje të disponueshme</string>
<string name="app_changelog">Ndryshimet</string> <string name="app_changelog">Shënimet e ndryshimeve</string>
<string name="loading">Po ngarkohet</string> <string name="loading">Duke u ngarkuar</string>
<string name="update">Përditëso</string> <string name="update">Përditëso</string>
<string name="not_available">N/A</string> <string name="not_available">N/A</string>
<string name="hide">Fshih</string> <string name="hide">Fshih</string>
<string name="home_package">Paketa</string> <string name="home_package">Paketa</string>
<string name="home_app_title">App</string> <string name="home_app_title">Aplikacioni</string>
<string name="home_notice_content">Shkarkoni Magisk VETËM nga faqja zyrtare në GitHub. Skedarët nga burime të panjohura mund të jenë të dëmshëm!</string>
<string name="home_notice_content">Shkarkoni Magisk VETEM nga faqja zyrtare e GitHub. Skedarët nga burime të panjohura mund të jenë me qëllim të keq! </string>
<string name="home_support_title">Na mbështetni</string> <string name="home_support_title">Na mbështetni</string>
<string name="home_follow_title">Na ndiqni</string> <string name="home_follow_title">Na ndiqni</string>
<string name="home_item_source">Burimi</string> <string name="home_item_source">Burimi</string>
<string name="home_support_content">Magisk është, dhe gjithmonë do të jetë, falas dhe me burim të hapur. Sidoqoftë, mund të na tregoni se kujdeseni duke dërguar një donacion të vogël.</string> <string name="home_support_content">Magisk është dhe do të mbetet gjithmonë falas dhe me burim të hapur. Megjithatë, mund të na mbështesni duke bërë një donacion.</string>
<string name="home_installed_version">Instaluar</string> <string name="home_installed_version">Instaluar</string>
<string name="home_latest_version">E fundit</string> <string name="home_latest_version">Më i fundit</string>
<string name="invalid_update_channel">Kanali i përditësimit i pavlefshëm</string> <string name="invalid_update_channel">Kanal i pavlefshëm për përditësime</string>
<string name="uninstall_magisk_title">Çinstalo Magisk</string> <string name="uninstall_magisk_title">Çinstalo Magisk</string>
<string name="uninstall_magisk_msg">Të gjitha modulet do të çaktivizohen/hiqen!\nRrënja do të hiqet!\nTë dhënat tuaja potencialisht të koduara nëse jo tashmë!</string> <string name="uninstall_magisk_msg">Të gjitha modulet do të çaktivizohen/hiqen!
Root-i do të hiqet!
Çdo memorie e brendshme që është çenkriptuar përmes Magisk do të rikriptohet!</string>
<!--Install--> <!--Install-->
<string name="keep_force_encryption">Ruaj kriptimin me forcë</string> <string name="keep_force_encryption">Ruaj enkriptimin e detyruar</string>
<string name="keep_dm_verity">Ruaj AVB 2.0/dm-verity</string> <string name="keep_dm_verity">Ruaj AVB 2.0/dm-verity</string>
<string name="recovery_mode">Recovery Mode</string> <string name="recovery_mode">Mënyra Recovery</string>
<string name="install_options_title">Opsionet</string> <string name="install_options_title">Opsionet</string>
<string name="install_method_title">Metoda</string> <string name="install_method_title">Metoda</string>
<string name="install_next">Tjetër</string> <string name="install_next">Vazhdoni</string>
<string name="install_start">Shkojme</string> <string name="install_start">Le të fillojmë</string>
<string name="manager_download_install">Shtypni për ta shkarkuar dhe instaluarl</string> <string name="manager_download_install">Shtypni për të shkarkuar dhe instaluar</string>
<string name="direct_install">Instalimi i direkt (Rekomandohet)</string> <string name="direct_install">Instalim i drejtpërdrejtë (Rekomandohet)</string>
<string name="install_inactive_slot">Instaloni në slotin joaktiv(Pas OTA)</string> <string name="install_inactive_slot">Instalo në slot-in joaktiv (Pas OTA)</string>
<string name="install_inactive_slot_msg">Pajisja juaj do të detyrohet të fillojë në folenë aktuale joaktive pas një rindezje!\nPërdoreni këtë opsion vetëm pasi të keni përfunduar OTA.\nVazhdo?</string> <string name="install_inactive_slot_msg">Pajisja juaj do të detyrohet të niset në slot-in joaktiv pas rinisjes!
<string name="setup_title">Konfigurimet shtesë</string> Përdorni këtë opsion vetëm pasi OTA të ketë përfunduar.
<string name="select_patch_file">Zgjidhni dhe Patch një skader</string> Të vazhdoj?</string>
<string name="patch_file_msg">Zgjidhni një imazh të papërpunuar (*.img) ose një skedar ODIN (*.tar) ose një payload.bin (*.bin)</string> <string name="setup_title">Konfigurim shtesë</string>
<string name="reboot_delay_toast">Rinisje pas 5 sekondash…</string> <string name="select_patch_file">Zgjidh dhe përpuno një skedar</string>
<string name="patch_file_msg">Zgjidh një imazh të papërpunuar (*.img) ose një skedar ODIN (*.tar) ose një payload.bin (*.bin)</string>
<string name="reboot_delay_toast">Rinisja pas 5 sekondash…</string>
<string name="flash_screen_title">Instalimi</string> <string name="flash_screen_title">Instalimi</string>
<!--Superuser--> <!--Superuser-->
<string name="su_request_title">Kërkesë nga superpërdoruesi</string> <string name="su_request_title">Kërkesë Superuser</string>
<string name="touch_filtered_warning">Për shkak se një aplikacion po errëson një kërkesë të superpërdoruesit, Magisk nuk mund të verifikojë përgjigjen tuaj</string> <string name="touch_filtered_warning">Për shkak se një aplikacion po mbivendos kërkesën Superuser, Magisk nuk mund të verifikojë përgjigjen tuaj.</string>
<string name="deny">Refuzo</string> <string name="deny">Refuzo</string>
<string name="prompt">Pyet</string> <string name="prompt">Pyete</string>
<string name="restrict">Kufizo</string>
<string name="grant">Lejo</string> <string name="grant">Lejo</string>
<string name="su_warning">Jep akses të plotë në pajisjen tuaj.\nRefuzo nëse nuk jeni të sigurt!</string> <string name="su_warning">Jep akses të plotë në pajisjen tuaj.
<string name="forever">Gjithmonë</string> Refuzoni nëse nuk jeni të sigurt!</string>
<string name="forever">Përgjithmonë</string>
<string name="once">Një herë</string> <string name="once">Një herë</string>
<string name="tenmin">10 minuta</string> <string name="tenmin">10 minuta</string>
<string name="twentymin">20 minuta</string> <string name="twentymin">20 minuta</string>
<string name="thirtymin">30 minuta</string> <string name="thirtymin">30 minuta</string>
<string name="sixtymin">60 minuta</string> <string name="sixtymin">60 minuta</string>
<string name="su_allow_toast">%1$s iu dha aksesi te Super-përdoruesi</string> <string name="su_allow_toast">%1$s mori të drejtat Superuser</string>
<string name="su_deny_toast">%1$s iu refuzua aksesi te Super -përdoruesi</string> <string name="su_deny_toast">%1$s u refuzua të drejtat Superuser</string>
<string name="su_snack_grant">Aksesi i super-përdoruesit te %1$s është lenuar</string> <string name="su_snack_grant">%1$s mori të drejtat Superuser</string>
<string name="su_snack_deny">Aksesi i super-përdoruesit te %1$s është refuzuar</string> <string name="su_snack_deny">%1$s u refuzua të drejtat Superuser</string>
<string name="su_snack_notif_on">Njoftimet e %1$s janë aktivizuar</string> <string name="su_snack_notif_on">Njoftimet për %1$s u aktivizuan</string>
<string name="su_snack_notif_off">Njoftimet e %1$s janë çaktivizuar</string> <string name="su_snack_notif_off">Njoftimet për %1$s u çaktivizuan</string>
<string name="su_snack_log_on">Regjistrat e %1$s janë aktivizuar</string> <string name="su_snack_log_on">Regjistrimi për %1$s u aktivizua</string>
<string name="su_snack_log_off">Regjistrat e %1$s janë çaktivizuar</string> <string name="su_snack_log_off">Regjistrimi për %1$s u çaktivizua</string>
<string name="su_revoke_title">drejtat?</string> <string name="su_revoke_title">hiqen?</string>
<string name="su_revoke_msg">Konfirmo për të hequr të drejtat e %1$s?</string> <string name="su_revoke_msg">Konfirmoni heqjen e të drejtave Superuser për %1$s</string>
<string name="toast">Dolli</string> <string name="toast">Njoftim</string>
<string name="none">Asnjë</string> <string name="none">Asnjë</string>
<string name="superuser_toggle_notification">Njoftimet</string> <string name="superuser_toggle_notification">Njoftimet</string>
<string name="superuser_toggle_revoke">Të drejtat</string> <string name="superuser_toggle_revoke">Hiq</string>
<string name="superuser_policy_none">Asnjë aplikacion nuk ka kërkuar akoma akses për super-përdoruesin.</string> <string name="superuser_policy_none">Asnjë aplikacion nuk ka kërkuar ende leje Superuser.</string>
<!--Logs--> <!--Logs-->
<string name="log_data_none">Nuk ka regjistra, provoni të përdorni më shumë aplikacionet tuaja me SU</string> <string name="log_data_none">Nuk keni regjistrime. Provojeni të përdorni më shumë aplikacionet me root.</string>
<string name="log_data_magisk_none">Regjistrat Magisk janë bosh, kjo është e çuditshme</string> <string name="log_data_magisk_none">Regjistrimet e Magisk janë bosh — çuditërisht.</string>
<string name="menuSaveLog">Ruaj regjistrar</string> <string name="menuSaveLog">Ruaj regjistrimin</string>
<string name="menuClearLog">Pastro regjistrat tani</string> <string name="menuClearLog">Pastro regjistrimin tani</string>
<string name="logs_cleared">Regjistrat u pastuan me sukses</string> <string name="logs_cleared">Regjistrimet u pastruan me sukses</string>
<string name="pid">PID: %1$d</string> <string name="pid">PID: %1$d</string>
<string name="target_uid">Target UID: %1$d</string> <string name="target_uid">UID i synuar: %1$d</string>
<string name="target_pid">Montoni PID synuar ns: %s</string> <string name="target_pid">PID i synuar: %s</string>
<string name="selinux_context">Konteksti SELinux: %s</string> <string name="selinux_context">Konteksti SELinux: %s</string>
<string name="supp_group">Grupi suplementar: %s</string> <string name="supp_group">Grupi shtesë: %s</string>
<!--MagiskHide--> <!--MagiskHide-->
<string name="show_system_app">Shfaq aplikacionet e sistemit</string> <string name="show_system_app">Shfaq aplikacionet e sistemit</string>
<string name="show_os_app">Shfaq aplikacionet e sistemit operativ</string> <string name="show_os_app">Shfaq aplikacionet e OS</string>
<string name="hide_filter_hint">Kërko sipas emrit</string> <string name="hide_filter_hint">Filtro sipas emrit</string>
<string name="hide_search">Kërko</string> <string name="hide_search">Kërko</string>
<!--Module--> <!--Module-->
<string name="no_info_provided">(Nuk ka asnjë informacion)</string> <string name="no_info_provided">(Nuk u dha informacion)</string>
<string name="reboot_userspace">Rinisje e shpejtë</string> <string name="reboot_userspace">Rinisje Normale</string>
<string name="reboot_recovery">Rinis te Recovery</string> <string name="reboot_recovery">Rinis Recovery</string>
<string name="reboot_bootloader">Rinis te Bootloader</string> <string name="reboot_bootloader">Rinis Bootloader</string>
<string name="reboot_download">Rinis te Download</string> <string name="reboot_download">Rinis Download</string>
<string name="reboot_edl">Rinis te EDL</string> <string name="reboot_edl">Rinis EDL</string>
<string name="reboot_safe_mode">Rinis në safe mode</string> <string name="reboot_safe_mode">Mënyra e sigurt</string>
<string name="module_version_author">%1$s nga %2$s</string> <string name="module_version_author">%1$s nga %2$s</string>
<string name="module_state_remove">Hiqe</string> <string name="module_state_remove">Hiqe</string>
<string name="module_action">Veprim</string> <string name="module_action">Veprimi</string>
<string name="module_state_restore">Rikëthe</string> <string name="module_state_restore">Rikthe</string>
<string name="module_action_install_external">Instaloni nga sdcard</string> <string name="module_action_install_external">Instalo nga memoria</string>
<string name="update_available">Përditësimi dispozicion</string> <string name="update_available">Përditësim i disponueshëm</string>
<string name="suspend_text_riru">Moduli u pezullua sepse %1$s është aktivizuar</string> <string name="suspend_text_riru">Moduli u pezullua sepse %1$s është aktiv</string>
<string name="suspend_text_zygisk">Moduli është pezulluar sepse %1$s nuk është i aktivizuar</string> <string name="suspend_text_zygisk">Moduli u pezullua sepse %1$s nuk është aktiv</string>
<string name="zygisk_module_unloaded">Moduli Zygisk nuk është ngarkuar për shkak të papajtueshmërisë</string> <string name="zygisk_module_unloaded">Moduli Zygisk nuk u ngarkua për shkak të mospërputhjes</string>
<string name="module_empty">Ska module të instaluar</string> <string name="module_empty">Nuk ka module të instaluara</string>
<string name="confirm_install">Të instalohet moduli %1$s?</string> <string name="confirm_install">Të instalohet moduli %1$s?</string>
<string name="confirm_install_title">Konfirmo instalimin</string> <string name="confirm_install_title">Konfirmim instalimi</string>
<!--Settings--> <!--Settings-->
<string name="settings_dark_mode_title">Mënyra e temës</string> <string name="settings_dark_mode_title">Mënyra e temës</string>
<string name="settings_dark_mode_message">Zgjidhni mënyrën që i përshtatet më shumë stilit tuaj!</string> <string name="settings_dark_mode_message">Zgjidh mënyrën që i përshtatet më shumë stilit tënd!</string>
<string name="settings_dark_mode_light">Gjithmonë e bardhë</string> <string name="settings_dark_mode_light">Gjithmonë e ndritshme</string>
<string name="settings_dark_mode_system">Sipas sistemit</string> <string name="settings_dark_mode_system">Ndiq sistemin</string>
<string name="settings_dark_mode_dark">Gjithmonë e zezë</string> <string name="settings_dark_mode_dark">Gjithmonë e errët</string>
<string name="settings_download_path_title">Vendodhje e shkarkimit</string> <string name="settings_download_path_title">Rruga e shkarkimit</string>
<string name="settings_download_path_message">Shkarkimet do të ruhen në %1$s</string> <string name="settings_download_path_message">Skedarët do të ruhen në %1$s</string>
<string name="settings_hide_app_title">Fsheh aplikacionin Magisk</string> <string name="settings_hide_app_title">Fshi aplikacionin Magisk</string>
<string name="settings_hide_app_summary">Instaloni një aplikacion përfaqësues me ID paketës të rastësishme dhe etiketë të personalizuar të aplikacionitl</string> <string name="settings_hide_app_summary">Instalo një aplikacion proxy me një ID pakete të rastësishme dhe emër të personalizuar</string>
<string name="settings_restore_app_title">Rivendosni aplikacionin Magisk</string> <string name="settings_restore_app_title">Rikthe aplikacionin Magisk</string>
<string name="settings_restore_app_summary">un-fsheh aplikacionin dhe riktheni atë në APK origjinale</string> <string name="settings_restore_app_summary">Zbulo aplikacionin dhe rikthe APK-në origjinale</string>
<string name="language">Gjuha</string> <string name="language">Gjuha</string>
<string name="system_default">(Parazgjedhja e sistemit)</string> <string name="system_default">(Parazgjedhja e sistemit)</string>
<string name="settings_check_update_title">Kontrollo për përditësime</string> <string name="settings_check_update_title">Kontrollo për përditësime</string>
<string name="settings_check_update_summary">Kontrolloni automatikisht për përditësime në sfond</string> <string name="settings_check_update_summary">Kontrollo periodikisht për përditësimet në sfond</string>
<string name="settings_update_channel_title">Perditeso kanalin</string> <string name="settings_update_channel_title">Kanal për përditësime</string>
<string name="settings_update_stable">E qëndrueshme</string> <string name="settings_update_stable">Stable</string>
<string name="settings_update_beta">Beta</string> <string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Kanal me porosi</string> <string name="settings_update_debug">Debug</string>
<string name="settings_update_custom_msg">Fut një URL të personalizuar</string> <string name="settings_update_custom">Custom</string>
<string name="settings_zygisk_summary">Drejtoni pjesë të Magisk në demonin zygote</string> <string name="settings_update_custom_msg">Fut një URL të personalizuar të kanalit</string>
<string name="settings_denylist_title">Zbato Listën e Mohimit</string> <string name="settings_zygisk_summary">Ekzekuto pjesë të Magisk në demonin Zygote</string>
<string name="settings_denylist_summary">Proceset në listën e mohimit do të kenë të gjitha modifikimet e Magisk</string> <string name="settings_denylist_title">Zbato listën e ndaluar</string>
<string name="settings_denylist_config_title">Konfiguro Listën e Mohimit</string> <string name="settings_denylist_summary">Proceset në listën e ndaluar do të rikthehen pa modifikimet e Magisk</string>
<string name="settings_denylist_config_summary">Zgjidhni proceset që do të përfshihen në listën e mohimit</string> <string name="settings_denylist_config_title">Konfiguro listën e ndaluar</string>
<string name="settings_hosts_title">Pritësit pa sistem</string> <string name="settings_denylist_config_summary">Zgjidh proceset që do të përfshihen në listën e ndaluar</string>
<string name="settings_hosts_summary">Pritësit pa sistem mbështesin aplikacionet Adblock</string> <string name="settings_hosts_title">Systemless hosts</string>
<string name="settings_hosts_toast">Moduli i hosteve pa sistem u shtua</string> <string name="settings_hosts_summary">Mbështetje për systemless hosts për aplikacionet që bllokojnë reklamat</string>
<string name="settings_app_name_hint">Emri i ri</string> <string name="settings_hosts_toast">U shtua moduli systemless hosts</string>
<string name="settings_app_name_helper">Aplikacioni do të ripaketohet me këtë emër</string> <string name="settings_app_name_hint">Emër i ri</string>
<string name="settings_app_name_helper">Aplikacioni do të ripaketizohet me këtë emër</string>
<string name="settings_app_name_error">Format i pavlefshëm</string> <string name="settings_app_name_error">Format i pavlefshëm</string>
<string name="settings_su_app_adb">Aplikacionet dhe ADB</string> <string name="settings_su_app_adb">Aplikacionet dhe ADB</string>
<string name="settings_su_app">Vetëm aplikacionet</string> <string name="settings_su_app">Vetëm aplikacionet</string>
<string name="settings_su_adb">Vetëm ADB</string> <string name="settings_su_adb">Vetëm ADB</string>
<string name="settings_su_disable">Çaktivizuar</string> <string name="settings_su_disable">Çaktivizuar</string>
<string name="settings_su_request_10">10 Sekonda</string> <string name="settings_su_request_10">10 sekonda</string>
<string name="settings_su_request_15">15 Sekonda</string> <string name="settings_su_request_15">15 sekonda</string>
<string name="settings_su_request_20">20 Sekonda</string> <string name="settings_su_request_20">20 sekonda</string>
<string name="settings_su_request_30">30 Sekonda</string> <string name="settings_su_request_30">30 sekonda</string>
<string name="settings_su_request_45">45 Sekonda</string> <string name="settings_su_request_45">45 sekonda</string>
<string name="settings_su_request_60">60 Sekonda</string> <string name="settings_su_request_60">60 sekonda</string>
<string name="superuser_access">Aksesi i Super-përdorues</string> <string name="superuser_access">Akses Superuser</string>
<string name="auto_response">Përgjigje automatike</string> <string name="auto_response">Përgjigje automatike</string>
<string name="request_timeout">Koha për mbarimit të Kërkesës</string> <string name="request_timeout">Koha e skadimit të kërkesës</string>
<string name="superuser_notification">Njoftimi i Super-përdoruesit</string> <string name="superuser_notification">Njoftimi Superuser</string>
<string name="settings_su_reauth_title">Ri-vërtetimi pas azhurnimit</string> <string name="settings_su_reauth_title">Riautentifikimi pas përditësimit</string>
<string name="settings_su_reauth_summary">Ri-vërtetoni lejet e super-përdoruesit pas azhurnimit të aplikacionit</string> <string name="settings_su_reauth_summary">Kërko sërish lejet Superuser pas përditësimit të aplikacioneve</string>
<string name="settings_su_tapjack_title">Aktivizo mbrojtjen tapjacking</string> <string name="settings_su_tapjack_title">Mbrojtje nga mbivendosja e klikimeve</string>
<string name="settings_su_tapjack_summary">Dialogu i menjëhershëm i super-përdoruesit nuk do ti përgjigjet hyrjes ndërsa është i errësuar nga ndonjë dritare ose mbivendosje tjetër</string> <string name="settings_su_tapjack_summary">Dritarja e kërkesës Superuser nuk do të pranojë input kur është e mbuluar nga ndonjë dritare tjetër</string>
<string name="settings_su_auth_title">Autentifikimi i përdoruesit</string>
<string name="settings_su_auth_summary">Kërko autentifikim të përdoruesit gjatë kërkesave Superuser</string>
<string name="settings_su_auth_insecure">Nuk ka asnjë metodë autentifikimi të konfiguruar në pajisje</string>
<string name="settings_su_restrict_title">Kufizo aftësitë e root</string>
<string name="settings_su_restrict_summary">Do të kufizojë aplikacionet e reja Superuser si parazgjedhje. Kujdes: kjo mund të prishë shumicën e aplikacioneve. Mos e aktivizoni nëse nuk dini çfarë bëni.</string>
<string name="settings_customization">Personalizimi</string> <string name="settings_customization">Personalizimi</string>
<string name="setting_add_shortcut_summary">Shtoni një shkurtore mjaft të mirë në ekranin fillestar në rast se emri dhe ikona janë të vështira për tu njohur pasi keni fshehur aplikacionin</string> <string name="setting_add_shortcut_summary">Shto një shkurtore në ekranin bazë nëse emri/ikona bëhen të vështira për tu dalluar pas fshehjes së aplikacionit</string>
<string name="settings_doh_title">DNS mbi HTTPS</string> <string name="settings_doh_title">DNS mbi HTTPS</string>
<string name="settings_doh_description">Helmimi i paqartë nga DNS në disa kombe</string> <string name="settings_doh_description">Zgjidhje për helmimin e DNS në disa shtete</string>
<string name="multiuser_mode">Mënyra Multi-përdoruesit</string> <string name="settings_random_name_title">Emër i rastësishëm</string>
<string name="settings_owner_only">Vetëm pronari i paisjes</string> <string name="settings_random_name_description">Rastësizo emrin e skedarit të daljes për imazhet e patch-uara dhe skedarët tar për të shmangur detektimin</string>
<string name="settings_owner_manage">Pronari i paisjes që e manaxhon</string> <string name="multiuser_mode">Mënyra multi-përdorues</string>
<string name="settings_user_independent">I pavarur nga përdoruesi</string> <string name="settings_owner_only">Vetëm pronari i pajisjes</string>
<string name="owner_only_summary">Vetëm pronari ka akses në rrënjë</string> <string name="settings_owner_manage">Menaxhuar nga pronari</string>
<string name="owner_manage_summary">Vetëm pronari mund të menaxhojë aksesin në rrënjë dhe të marrë kërkesat</string> <string name="settings_user_independent">I pavarur për përdoruesit</string>
<string name="user_independent_summary">Çdo përdorues ka rregullat e veta të veçanta rrënjësore</string> <string name="owner_only_summary">Vetëm pronari ka akses root</string>
<string name="mount_namespace_mode">Mënyra e Montimit të Hapësirës Emërore</string> <string name="owner_manage_summary">Vetëm pronari mund të menaxhojë aksesin root dhe të marrë kërkesat</string>
<string name="settings_ns_global">Hapësira globale e emrave</string> <string name="user_independent_summary">Çdo përdorues ka rregullat e veta të root</string>
<string name="settings_ns_requester">Trashëgoni hapësirën e emrave</string> <string name="mount_namespace_mode">Mënyra e mount namespace</string>
<string name="settings_ns_isolate">Hapësira e izoluar e emrave</string> <string name="settings_ns_global">Namespace global</string>
<string name="global_summary">Të gjitha sesionet rrënjë përdorin hapësirën globale të emrave të montimit</string> <string name="settings_ns_requester">Trashëgo namespace</string>
<string name="requester_summary">Seancat rrënjësore do të trashëgojnë hapësirën e emrave të kërkuesit të tyre</string> <string name="settings_ns_isolate">Namespace i izoluar</string>
<string name="isolate_summary">Çdo sesion rrënjë do të ketë hapësirën e vet të izoluar të emrave</string> <string name="global_summary">Të gjitha sesionet root përdorin namespace global</string>
<string name="settings_su_auth_title">Vërtetimi i përdoruesit</string> <string name="requester_summary">Sesioni root trashëgon namespace-in e kërkuesit</string>
<string name="settings_su_auth_summary">Kërkoni vërtetimin e përdoruesit gjatë kërkesave të Superpërdoruesit</string> <string name="isolate_summary">Çdo sesion root do të ketë namespace të izoluar</string>
<string name="settings_su_auth_insecure">Asnjë metodë vërtetimi nuk është konfiguruar në pajisje</string>
<!--Notifications--> <!--Notifications-->
<string name="update_channel">Përditësimet e magisk</string> <string name="update_channel">Përditësimet e Magisk</string>
<string name="updated_channel">Përditësimi përfundoi</string>
<string name="progress_channel">Njoftimet e progresit</string> <string name="progress_channel">Njoftimet e progresit</string>
<string name="updated_channel">Përditësimi përfundoi</string>
<string name="download_complete">Shkarkimi përfundoi</string> <string name="download_complete">Shkarkimi përfundoi</string>
<string name="download_file_error">Gabim në shkarkimin e skedarit</string> <string name="download_file_error">Gabim gjatë shkarkimit të skedarit</string>
<string name="magisk_update_title">Përditësimi Magisk i disponueshëm!</string> <string name="magisk_update_title">Përditësim i ri i Magisk!</string>
<string name="updated_title">Magisk u përditësua</string> <string name="updated_title">Magisk u përditësua</string>
<string name="updated_text">Prekni për të hapur aplikacionin</string> <string name="updated_text">Shtypni për të hapur aplikacionin</string>
<!--Toasts, Dialogs--> <!--Toasts, Dialogs-->
<string name="yes">Po</string> <string name="yes">Po</string>
<string name="no">Jo</string> <string name="no">Jo</string>
<string name="repo_install_title">Instalo %1$s %2$s(%3$d)</string> <string name="repo_install_title">Instalo %1$s %2$s(%3$d)</string>
<string name="download">Shkarko</string> <string name="download">Shkarko</string>
<string name="reboot">Rinis</string> <string name="reboot">Rinise</string>
<string name="close">Mbylle</string> <string name="close">Mbyll</string>
<string name="release_notes">Shënimet e lëshimit</string> <string name="release_notes">Shënimet e versionit</string>
<string name="flashing">Duke flashuar</string> <string name="flashing">Duke flashuar..</string>
<string name="running">Duke vepruar...</string> <string name="running">Duke u ekzekutuar..</string>
<string name="done">U krye!</string> <string name="done">U krye!</string>
<string name="done_action">Veprimi i ekzekutimit të %1$s u krye</string> <string name="done_action">Veprimi i %1$s u krye</string>
<string name="failure">Dështoi!</string> <string name="failure">Dështoi!</string>
<string name="hide_app_title">Fshehja e aplikacionit Magisk</string> <string name="hide_app_title">Duke fshehur aplikacionin Magisk..</string>
<string name="open_link_failed_toast">Nuk u gjet asnjë aplikacion për të hapur lidhjen</string> <string name="open_link_failed_toast">Nuk u gjet asnjë aplikacion për të hapur lidhjen</string>
<string name="complete_uninstall">Çinstalimi i plotë</string> <string name="complete_uninstall">Çinstalim i plotë</string>
<string name="restore_img">Rivendosni imazhet</string> <string name="restore_img">Rikthe imazhet</string>
<string name="restore_img_msg">Duke rivendosur…</string> <string name="restore_img_msg">Duke rikthyer..</string>
<string name="restore_done">Rivendosja u krye!</string> <string name="restore_done">Rikthimi u krye!</string>
<string name="restore_fail">Rezervimi i aksioneve nuk ekziston!</string> <string name="restore_fail">Backup-i origjinal nuk ekziston!</string>
<string name="setup_fail">Konfigurimi dështoi</string> <string name="setup_fail">Konfigurimi dështoi</string>
<string name="env_fix_title">Kërkon Konfigurim shtesë</string> <string name="env_fix_title">Kërkohet konfigurim shtesë</string>
<string name="env_fix_msg">Pajisja juaj ka nevojë për konfigurim shtesë që Magisk të funksionojë siç duhet. Dëshironi të vazhdoni dhe rindizni?</string> <string name="env_fix_msg">Pajisja ka nevojë për konfigurim shtesë që Magisk të funksionojë si duhet. Dëshironi të vazhdoni dhe të rinisni pajisjen?</string>
<string name="env_full_fix_msg">Pajisja juaj ka nevojë për re-flashuar Magisk të funksionojë siç duhet. Ju lutemi ri-instaloni Magisk brenda aplikacionit, modaliteti i rikuperimit nuk mund të marrë informacionin e saktë të pajisjes.</string> <string name="env_full_fix_msg">Pajisja ka nevojë për ri-flash Magisk për të funksionuar saktë. Ju lutemi riinstaloni Magisk brenda aplikacionit; Recovery nuk mund të marrë informacionet e sakta të pajisjes.</string>
<string name="setup_msg">Konfigurimi i mjedisit të funksionimit…</string> <string name="setup_msg">Duke ekzekutuar konfigurimin e mjedisit..</string>
<string name="unsupport_magisk_title">Version Magjik i Pambështetur</string> <string name="unsupport_magisk_title">Version i Magisk i pambështetur</string>
<string name="unsupport_magisk_msg">Ky version i aplikacionit nuk e mbështet versionin Magisk më të ulët se %1$s.\n\nAplikacioni do të sillet sikur të mos jetë i instaluar Magisk, ju lutemi azhurnoni Magisk sa më shpejt të jetë e mundur.</string> <string name="unsupport_magisk_msg">Ky version i aplikacionit nuk mbështet versione të Magisk më të ulëta se %1$s.
Aplikacioni do të sillet sikur Magisk nuk është i instaluar. Ju lutemi përditësoni Magisk sa më shpejt të jetë e mundur.</string>
<string name="unsupport_general_title">Gjendje jonormale</string> <string name="unsupport_general_title">Gjendje jonormale</string>
<string name="unsupport_system_app_msg">Drejtimi i këtij aplikacioni si një aplikacion sistemi nuk mbështetet. Ju lutemi kthejeni aplikacionin në një aplikacion përdoruesi.</string> <string name="unsupport_system_app_msg">Ekzekutimi i këtij aplikacioni si aplikacion sistemi nuk mbështetet. Ju lutemi kthejeni në aplikacion përdoruesi.</string>
<string name="unsupport_other_su_msg">Një komandë \"su"\ që nuk i përket Magisk është zbuluar. Ju lutemi hiqni SU-në tjetër të pambështetur.</string> <string name="unsupport_other_su_msg">Është zbuluar një binar "su" që nuk është nga Magisk. Ju lutemi hiqni çdo zgjidhje tjetër root dhe/ose riinstaloni Magisk.</string>
<string name="unsupport_external_storage_msg">Magisk është instaluar në ruajtjen e jashtme. Ju lutemi zhvendosni aplikacionin në ruajtjen e brendshme.</string> <string name="unsupport_external_storage_msg">Magisk është instaluar në memorien e jashtme. Lëvizni aplikacionin në memorien e brendshme.</string>
<string name="unsupport_nonroot_stub_msg">Aplikacioni nuk mund të vazhdojë të punojë në gjendjen e fshehur pasi rrënja ishte e humbur. Ju lutemi rivendoseni përsëri në APK-në origjinale.</string> <string name="unsupport_nonroot_stub_msg">Aplikacioni i fshehur i Magisk nuk mund të vazhdojë të funksionojë sepse root u humb. Ju lutemi riktheni APK-në origjinale.</string>
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string> <string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
<string name="external_rw_permission_denied">Jepni lejen e ruajtjes për të aktivizuar këtë funksion</string> <string name="external_rw_permission_denied">Jepni lejen e magazinimit për të aktivizuar këtë funksion</string>
<string name="post_notifications_denied">Jepni lejen e njoftimeve për të aktivizuar këtë funksion</string> <string name="post_notifications_denied">Jepni lejen e njoftimeve për të aktivizuar këtë funksion</string>
<string name="install_unknown_denied">Lejo "instaloni aplikacione të panjohura" për të aktivizuar këtë funksion</string> <string name="install_unknown_denied">Lejoni "Instalo aplikacione të panjohura" për të aktivizuar këtë funksion</string>
<string name="add_shortcut_title">Shto shkurtore në ekranin bazë</string> <string name="add_shortcut_title">Shto shkurtore në ekranin bazë</string>
<string name="add_shortcut_msg">Pas fshehjes së këtij aplikacioni, emri dhe ikona e tij mund të bëhen të vështira për tu njohur. Dëshironi të shtoni një shkurtore mjaft të bukur në ekranin bazë?</string> <string name="add_shortcut_msg">Pas fshehjes së aplikacionit, emri dhe ikona mund të jenë të vështira për tu njohur. Dëshironi të shtoni një shkurtore të bukur në ekranin bazë?</string>
<string name="app_not_found">Asnjë aplikacion nuk u gjet për të trajtuar këtë veprim</string> <string name="app_not_found">Nuk u gjet aplikacion për të kryer këtë veprim</string>
<string name="reboot_apply_change">Rinisni për të aplikuar ndryshimet</string> <string name="reboot_apply_change">Rinisni për të aplikuar ndryshimet</string>
<string name="restore_app_confirmation">Kjo do të rivendosë aplikacionin e fshehur në aplikacionin origjinal. A dëshironi vërtet ta bëni këtë?</string> <string name="restore_app_confirmation">Kjo do të rikthejë aplikacionin e fshehur në gjendjen origjinale. Jeni të sigurt që dëshironi ta bëni këtë?</string>
</resources> </resources>

View File

@@ -30,4 +30,4 @@ android.nonFinalResIds=false
# Magisk # Magisk
magisk.stubVersion=40 magisk.stubVersion=40
magisk.versionCode=30500 magisk.versionCode=30600

View File

@@ -1,5 +1,9 @@
# Magisk Changelog # Magisk Changelog
### v30.6 (2025.12.1)
- [MagiskInit] Revert a change that could result in bootloops
### v30.5 (2025.12.1) ### v30.5 (2025.12.1)
- [General] Improve commandline argument parsing logic - [General] Improve commandline argument parsing logic

View File

@@ -53,6 +53,9 @@ pb-rs = { git = "https://github.com/topjohnwu/quick-protobuf.git" }
quick-protobuf = { git = "https://github.com/topjohnwu/quick-protobuf.git" } quick-protobuf = { git = "https://github.com/topjohnwu/quick-protobuf.git" }
lz4-sys = { path = "external/lz4-sys" } lz4-sys = { path = "external/lz4-sys" }
[workspace.lints.clippy]
unwrap_used = "deny"
[profile.dev] [profile.dev]
opt-level = "z" opt-level = "z"
lto = "thin" lto = "thin"

View File

@@ -9,6 +9,9 @@ path = "lib.rs"
[features] [features]
selinux = [] selinux = []
[lints]
workspace = true
[build-dependencies] [build-dependencies]
cxx-gen = { workspace = true } cxx-gen = { workspace = true }

View File

@@ -132,13 +132,9 @@ impl PositionalArgParser<'_> {
} }
fn ensure_end(&mut self) -> Result<(), EarlyExit> { fn ensure_end(&mut self) -> Result<(), EarlyExit> {
if self.0.len() == 0 { match self.0.next() {
Ok(()) None => Ok(()),
} else { Some(s) => Err(EarlyExit::from(format!("Unrecognized argument: {s}\n"))),
Err(EarlyExit::from(format!(
"Unrecognized argument: {}\n",
self.0.next().unwrap()
)))
} }
} }
} }

View File

@@ -7,6 +7,9 @@ edition.workspace = true
crate-type = ["staticlib"] crate-type = ["staticlib"]
path = "lib.rs" path = "lib.rs"
[lints]
workspace = true
[build-dependencies] [build-dependencies]
cxx-gen = { workspace = true } cxx-gen = { workspace = true }
pb-rs = { workspace = true } pb-rs = { workspace = true }

View File

@@ -53,6 +53,19 @@ static bool check_env(const char *name) {
return val != nullptr && val == "true"sv; return val != nullptr && val == "true"sv;
} }
static bool guess_lzma(const uint8_t *buf, size_t len) {
// 0 : (pb * 5 + lp) * 9 + lc
// 1 - 4 : dict size, must be 2^n
// 5 - 12: all 0xFF
if (len <= 13) return false;
if (memcmp(buf, "\x5d", 1) != 0) return false;
uint32_t dict_sz = 0;
memcpy(&dict_sz, buf + 1, sizeof(dict_sz));
if (dict_sz == 0 || (dict_sz & (dict_sz - 1)) != 0) return false;
if (memcmp(buf + 5, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) != 0) return false;
return true;
}
FileFormat check_fmt(const void *buf, size_t len) { FileFormat check_fmt(const void *buf, size_t len) {
if (CHECKED_MATCH(CHROMEOS_MAGIC)) { if (CHECKED_MATCH(CHROMEOS_MAGIC)) {
return FileFormat::CHROMEOS; return FileFormat::CHROMEOS;
@@ -66,8 +79,7 @@ FileFormat check_fmt(const void *buf, size_t len) {
return FileFormat::LZOP; return FileFormat::LZOP;
} else if (CHECKED_MATCH(XZ_MAGIC)) { } else if (CHECKED_MATCH(XZ_MAGIC)) {
return FileFormat::XZ; return FileFormat::XZ;
} else if (len >= 13 && memcmp(buf, "\x5d\x00\x00", 3) == 0 } else if (guess_lzma(static_cast<const uint8_t *>(buf), len)) {
&& (((char *)buf)[12] == '\xff' || ((char *)buf)[12] == '\x00')) {
return FileFormat::LZMA; return FileFormat::LZMA;
} else if (CHECKED_MATCH(BZIP_MAGIC)) { } else if (CHECKED_MATCH(BZIP_MAGIC)) {
return FileFormat::BZIP2; return FileFormat::BZIP2;

View File

@@ -6,6 +6,7 @@ use crate::codegen::gen_cxx_binding;
#[path = "../include/codegen.rs"] #[path = "../include/codegen.rs"]
mod codegen; mod codegen;
#[allow(clippy::unwrap_used)]
fn main() { fn main() {
println!("cargo:rerun-if-changed=proto/update_metadata.proto"); println!("cargo:rerun-if-changed=proto/update_metadata.proto");

View File

@@ -205,7 +205,7 @@ Supported actions:
dumped to the file 'header', which can be used to modify header dumped to the file 'header', which can be used to modify header
configurations during repacking. configurations during repacking.
Return values: Return values:
0:valid 1:error 2:chromeos 0:valid 1:error 2:chromeos 3:vendor_boot
repack [-n] <origbootimg> [outbootimg] repack [-n] <origbootimg> [outbootimg]
Repack boot image components using files from the current directory Repack boot image components using files from the current directory

View File

@@ -1,8 +1,6 @@
use crate::ffi::{FileFormat, check_fmt}; use crate::ffi::{FileFormat, check_fmt};
use base::nix::fcntl::OFlag; use base::nix::fcntl::OFlag;
use base::{ use base::{Chunker, FileOrStd, LoggedResult, ReadExt, Utf8CStr, Utf8CString, WriteExt, log_err};
Chunker, FileOrStd, LoggedResult, ReadExt, ResultExt, Utf8CStr, Utf8CString, WriteExt, log_err,
};
use bzip2::Compression as BzCompression; use bzip2::Compression as BzCompression;
use bzip2::read::BzDecoder; use bzip2::read::BzDecoder;
use bzip2::write::BzEncoder; use bzip2::write::BzEncoder;
@@ -218,16 +216,21 @@ impl<R: Read> Read for LZ4BlockDecoder<R> {
// Top-level APIs // Top-level APIs
pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn WriteFinish<W> + 'a> { pub fn get_encoder<'a, W: Write + 'a>(
match format { format: FileFormat,
w: W,
) -> std::io::Result<Box<dyn WriteFinish<W> + 'a>> {
Ok(match format {
FileFormat::XZ => { FileFormat::XZ => {
let mut opt = XzOptions::with_preset(9); let mut opt = XzOptions::with_preset(9);
opt.set_check_sum_type(CheckType::Crc32); opt.set_check_sum_type(CheckType::Crc32);
Box::new(XzWriter::new(w, opt).unwrap()) Box::new(XzWriter::new(w, opt)?)
}
FileFormat::LZMA => {
Box::new(LzmaWriter::new_use_header(w, &LzmaOptions::with_preset(9), None).unwrap())
} }
FileFormat::LZMA => Box::new(LzmaWriter::new_use_header(
w,
&LzmaOptions::with_preset(9),
None,
)?),
FileFormat::BZIP2 => Box::new(BzEncoder::new(w, BzCompression::best())), FileFormat::BZIP2 => Box::new(BzEncoder::new(w, BzCompression::best())),
FileFormat::LZ4 => { FileFormat::LZ4 => {
let encoder = LZ4FrameEncoderBuilder::new() let encoder = LZ4FrameEncoderBuilder::new()
@@ -237,8 +240,7 @@ pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn Write
.block_checksum(BlockChecksum::BlockChecksumEnabled) .block_checksum(BlockChecksum::BlockChecksumEnabled)
.level(9) .level(9)
.auto_flush(true) .auto_flush(true)
.build(w) .build(w)?;
.unwrap();
Box::new(encoder) Box::new(encoder)
} }
FileFormat::LZ4_LEGACY => Box::new(LZ4BlockEncoder::new(w, false)), FileFormat::LZ4_LEGACY => Box::new(LZ4BlockEncoder::new(w, false)),
@@ -246,27 +248,30 @@ pub fn get_encoder<'a, W: Write + 'a>(format: FileFormat, w: W) -> Box<dyn Write
FileFormat::ZOPFLI => { FileFormat::ZOPFLI => {
// These options are already better than gzip -9 // These options are already better than gzip -9
let opt = ZopfliOptions { let opt = ZopfliOptions {
iteration_count: NonZeroU64::new(1).unwrap(), iteration_count: unsafe { NonZeroU64::new_unchecked(1) },
maximum_block_splits: 1, maximum_block_splits: 1,
..Default::default() ..Default::default()
}; };
Box::new(ZopFliEncoder::new_buffered(opt, BlockType::Dynamic, w).unwrap()) Box::new(ZopFliEncoder::new_buffered(opt, BlockType::Dynamic, w)?)
} }
FileFormat::GZIP => Box::new(GzEncoder::new(w, GzCompression::best())), FileFormat::GZIP => Box::new(GzEncoder::new(w, GzCompression::best())),
_ => unreachable!(), _ => unreachable!(),
} })
} }
pub fn get_decoder<'a, R: Read + 'a>(format: FileFormat, r: R) -> Box<dyn Read + 'a> { pub fn get_decoder<'a, R: Read + 'a>(
match format { format: FileFormat,
r: R,
) -> std::io::Result<Box<dyn Read + 'a>> {
Ok(match format {
FileFormat::XZ => Box::new(XzReader::new(r, true)), FileFormat::XZ => Box::new(XzReader::new(r, true)),
FileFormat::LZMA => Box::new(LzmaReader::new_mem_limit(r, u32::MAX, None).unwrap()), FileFormat::LZMA => Box::new(LzmaReader::new_mem_limit(r, u32::MAX, None)?),
FileFormat::BZIP2 => Box::new(BzDecoder::new(r)), FileFormat::BZIP2 => Box::new(BzDecoder::new(r)),
FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(r).unwrap()), FileFormat::LZ4 => Box::new(LZ4FrameDecoder::new(r)?),
FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(r)), FileFormat::LZ4_LG | FileFormat::LZ4_LEGACY => Box::new(LZ4BlockDecoder::new(r)),
FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(r)), FileFormat::ZOPFLI | FileFormat::GZIP => Box::new(MultiGzDecoder::new(r)),
_ => unreachable!(), _ => unreachable!(),
} })
} }
// C++ FFI // C++ FFI
@@ -274,9 +279,9 @@ pub fn get_decoder<'a, R: Read + 'a>(format: FileFormat, r: R) -> Box<dyn Read +
pub fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) { pub fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) }; let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
let mut encoder = get_encoder(format, out_file.deref_mut());
let _: LoggedResult<()> = try { let _: LoggedResult<()> = try {
encoder.write_all(in_bytes)?; let mut encoder = get_encoder(format, out_file.deref_mut())?;
std::io::copy(&mut Cursor::new(in_bytes), encoder.deref_mut())?;
encoder.finish()?; encoder.finish()?;
}; };
} }
@@ -284,8 +289,10 @@ pub fn compress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
pub fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) { pub fn decompress_bytes(format: FileFormat, in_bytes: &[u8], out_fd: RawFd) {
let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) }; let mut out_file = unsafe { ManuallyDrop::new(File::from_raw_fd(out_fd)) };
let mut decoder = get_decoder(format, in_bytes); let _: LoggedResult<()> = try {
std::io::copy(decoder.as_mut(), out_file.deref_mut()).log_ok(); let mut decoder = get_decoder(format, in_bytes)?;
std::io::copy(decoder.as_mut(), out_file.deref_mut())?;
};
} }
// Command-line entry points // Command-line entry points
@@ -341,7 +348,7 @@ pub(crate) fn decompress_cmd(infile: &Utf8CStr, outfile: Option<&Utf8CStr>) -> L
FileOrStd::File(outfile.create(OFlag::O_WRONLY | OFlag::O_TRUNC, 0o644)?) FileOrStd::File(outfile.create(OFlag::O_WRONLY | OFlag::O_TRUNC, 0o644)?)
}; };
let mut decoder = get_decoder(format, Cursor::new(buf).chain(input.as_file())); let mut decoder = get_decoder(format, Cursor::new(buf).chain(input.as_file()))?;
std::io::copy(decoder.as_mut(), &mut output.as_file())?; std::io::copy(decoder.as_mut(), &mut output.as_file())?;
if rm_in { if rm_in {
@@ -384,7 +391,7 @@ pub(crate) fn compress_cmd(
FileOrStd::File(outfile) FileOrStd::File(outfile)
}; };
let mut encoder = get_encoder(method, output.as_file()); let mut encoder = get_encoder(method, output.as_file())?;
std::io::copy(&mut input.as_file(), encoder.as_mut())?; std::io::copy(&mut input.as_file(), encoder.as_mut())?;
encoder.finish()?; encoder.finish()?;

View File

@@ -484,10 +484,9 @@ impl Cpio {
}; };
for (name, entry) in &self.entries { for (name, entry) in &self.entries {
let p = "/".to_string() + name.as_str(); let p = "/".to_string() + name.as_str();
if !p.starts_with(&path) { let Some(p) = p.strip_prefix(&path) else {
continue; continue;
} };
let p = p.strip_prefix(&path).unwrap();
if !p.is_empty() && !p.starts_with('/') { if !p.is_empty() && !p.starts_with('/') {
continue; continue;
} }
@@ -614,8 +613,11 @@ impl Cpio {
o.rm(".backup", true); o.rm(".backup", true);
self.rm(".backup", true); self.rm(".backup", true);
let mut lhs = o.entries.into_iter().peekable(); let mut left_iter = o.entries.into_iter();
let mut rhs = self.entries.iter().peekable(); let mut right_iter = self.entries.iter();
let mut lhs = left_iter.next();
let mut rhs = right_iter.next();
loop { loop {
enum Action<'a> { enum Action<'a> {
@@ -623,32 +625,38 @@ impl Cpio {
Record(&'a String), Record(&'a String),
Noop, Noop,
} }
let action = match (lhs.peek(), rhs.peek()) {
(Some((l, _)), Some((r, re))) => match l.as_str().cmp(r.as_str()) { // Move the iterator forward if needed
if lhs.is_none() {
lhs = left_iter.next();
}
if rhs.is_none() {
rhs = right_iter.next();
}
let action = match (lhs.take(), rhs.take()) {
(Some((ln, le)), Some((rn, re))) => match ln.as_str().cmp(rn.as_str()) {
Ordering::Less => { Ordering::Less => {
let (l, le) = lhs.next().unwrap(); // Put rhs back
Action::Backup(l, le) rhs = Some((rn, re));
Action::Backup(ln, le)
}
Ordering::Greater => {
// Put lhs back
lhs = Some((ln, le));
Action::Record(rn)
} }
Ordering::Greater => Action::Record(rhs.next().unwrap().0),
Ordering::Equal => { Ordering::Equal => {
let (l, le) = lhs.next().unwrap(); if re.data != le.data {
let action = if re.data != le.data { Action::Backup(ln, le)
Action::Backup(l, le)
} else { } else {
Action::Noop Action::Noop
}; }
rhs.next();
action
} }
}, },
(Some(_), None) => { (Some((ln, le)), None) => Action::Backup(ln, le),
let (l, le) = lhs.next().unwrap(); (None, Some((rn, _))) => Action::Record(rn),
Action::Backup(l, le) (None, None) => break,
}
(None, Some(_)) => Action::Record(rhs.next().unwrap().0),
(None, None) => {
break;
}
}; };
match action { match action {
Action::Backup(name, mut entry) => { Action::Backup(name, mut entry) => {
@@ -691,8 +699,8 @@ impl CpioEntry {
if self.mode & S_IFMT != S_IFREG { if self.mode & S_IFMT != S_IFREG {
return false; return false;
} }
let mut encoder = get_encoder(FileFormat::XZ, Vec::new());
let Ok(data): std::io::Result<Vec<u8>> = (try { let Ok(data): std::io::Result<Vec<u8>> = (try {
let mut encoder = get_encoder(FileFormat::XZ, Vec::new())?;
encoder.write_all(&self.data)?; encoder.write_all(&self.data)?;
encoder.finish()? encoder.finish()?
}) else { }) else {
@@ -710,7 +718,7 @@ impl CpioEntry {
} }
let Ok(data): std::io::Result<Vec<u8>> = (try { let Ok(data): std::io::Result<Vec<u8>> = (try {
let mut decoder = get_decoder(FileFormat::XZ, Cursor::new(&self.data)); let mut decoder = get_decoder(FileFormat::XZ, Cursor::new(&self.data))?;
let mut data = Vec::new(); let mut data = Vec::new();
std::io::copy(decoder.as_mut(), &mut data)?; std::io::copy(decoder.as_mut(), &mut data)?;
data data

View File

@@ -164,8 +164,8 @@ pub fn extract_boot_from_payload(
out_file.seek(SeekFrom::Start(out_offset))?; out_file.seek(SeekFrom::Start(out_offset))?;
let fmt = check_fmt(data); let fmt = check_fmt(data);
let mut decoder = get_decoder(fmt, Cursor::new(data));
let Ok(_): std::io::Result<()> = (try { let Ok(_): std::io::Result<()> = (try {
let mut decoder = get_decoder(fmt, Cursor::new(data))?;
std::io::copy(decoder.as_mut(), &mut out_file)?; std::io::copy(decoder.as_mut(), &mut out_file)?;
}) else { }) else {
return Err(bad_payload!("decompression failed")); return Err(bad_payload!("decompression failed"));

View File

@@ -15,6 +15,9 @@ check-signature = []
check-client = [] check-client = []
su-check-db = [] su-check-db = []
[lints]
workspace = true
[build-dependencies] [build-dependencies]
cxx-gen = { workspace = true } cxx-gen = { workspace = true }
pb-rs = { workspace = true } pb-rs = { workspace = true }

View File

@@ -82,7 +82,7 @@ impl MagiskD {
Command::new(&tmp_bb) Command::new(&tmp_bb)
.arg("--install") .arg("--install")
.arg("-s") .arg("-s")
.arg(tmp_bb.parent_dir().unwrap()) .arg(tmp_bb.parent_dir().unwrap_or_default())
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.status() .status()
@@ -186,13 +186,13 @@ impl MagiskD {
setup_preinit_dir(); setup_preinit_dir();
self.ensure_manager(); self.ensure_manager();
if self.zygisk_enabled.load(Ordering::Relaxed) { if self.zygisk_enabled.load(Ordering::Relaxed) {
self.zygisk.lock().unwrap().reset(true); self.zygisk.lock().reset(true);
} }
} }
pub fn boot_stage_handler(&self, client: UnixStream, code: RequestCode) { pub fn boot_stage_handler(&self, client: UnixStream, code: RequestCode) {
// Make sure boot stage execution is always serialized // Make sure boot stage execution is always serialized
let mut state = self.boot_stage_lock.lock().unwrap(); let mut state = self.boot_stage_lock.lock();
match code { match code {
RequestCode::POST_FS_DATA => { RequestCode::POST_FS_DATA => {

View File

@@ -6,6 +6,7 @@ use crate::codegen::gen_cxx_binding;
#[path = "../include/codegen.rs"] #[path = "../include/codegen.rs"]
mod codegen; mod codegen;
#[allow(clippy::unwrap_used)]
fn main() { fn main() {
println!("cargo:rerun-if-changed=resetprop/proto/persistent_properties.proto"); println!("cargo:rerun-if-changed=resetprop/proto/persistent_properties.proto");

View File

@@ -31,8 +31,9 @@ use std::io::{BufReader, Write};
use std::os::fd::{AsFd, AsRawFd, IntoRawFd, RawFd}; use std::os::fd::{AsFd, AsRawFd, IntoRawFd, RawFd};
use std::os::unix::net::{UCred, UnixListener, UnixStream}; use std::os::unix::net::{UCred, UnixListener, UnixStream};
use std::process::{Command, exit}; use std::process::{Command, exit};
use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, OnceLock}; use std::sync::nonpoison::Mutex;
use std::time::Duration; use std::time::Duration;
// Global magiskd singleton // Global magiskd singleton
@@ -105,7 +106,7 @@ impl MagiskD {
denylist_handler(-1); denylist_handler(-1);
// Restore native bridge property // Restore native bridge property
self.zygisk.lock().unwrap().restore_prop(); self.zygisk.lock().restore_prop();
client.write_pod(&0).log_ok(); client.write_pod(&0).log_ok();
@@ -129,7 +130,7 @@ impl MagiskD {
self.prune_su_access(); self.prune_su_access();
scan_deny_apps(); scan_deny_apps();
if self.zygisk_enabled.load(Ordering::Relaxed) { if self.zygisk_enabled.load(Ordering::Relaxed) {
self.zygisk.lock().unwrap().reset(false); self.zygisk.lock().reset(false);
} }
} }
RequestCode::SQLITE_CMD => { RequestCode::SQLITE_CMD => {

View File

@@ -187,7 +187,7 @@ unsafe extern "C" fn read_db_row<T: SqlTable>(
impl MagiskD { impl MagiskD {
fn with_db<F: FnOnce(*mut sqlite3) -> i32>(&self, f: F) -> i32 { fn with_db<F: FnOnce(*mut sqlite3) -> i32>(&self, f: F) -> i32 {
let mut db = self.sql_connection.lock().unwrap(); let mut db = self.sql_connection.lock();
if db.is_none() { if db.is_none() {
let raw_db = open_and_init_db(); let raw_db = open_and_init_db();
*db = NonNull::new(raw_db).map(Sqlite3); *db = NonNull::new(raw_db).map(Sqlite3);

View File

@@ -4,6 +4,9 @@
#![feature(unix_socket_peek)] #![feature(unix_socket_peek)]
#![feature(default_field_values)] #![feature(default_field_values)]
#![feature(peer_credentials_unix_socket)] #![feature(peer_credentials_unix_socket)]
#![feature(sync_nonpoison)]
#![feature(nonpoison_mutex)]
#![feature(nonpoison_condvar)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
use crate::ffi::SuRequest; use crate::ffi::SuRequest;

View File

@@ -20,9 +20,10 @@ use std::io::{IoSlice, Read, Write};
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::os::fd::{FromRawFd, IntoRawFd, RawFd}; use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::Arc;
use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::nonpoison::Mutex;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{fs, io}; use std::{fs, io};
#[allow(dead_code, non_camel_case_types)] #[allow(dead_code, non_camel_case_types)]
@@ -117,12 +118,12 @@ fn write_log_to_pipe(mut logd: &File, prio: i32, msg: &Utf8CStr) -> io::Result<u
static MAGISK_LOGD_FD: Mutex<Option<Arc<File>>> = Mutex::new(None); static MAGISK_LOGD_FD: Mutex<Option<Arc<File>>> = Mutex::new(None);
fn with_logd_fd<R, F: FnOnce(&File) -> io::Result<R>>(f: F) { fn with_logd_fd<R, F: FnOnce(&File) -> io::Result<R>>(f: F) {
let fd = MAGISK_LOGD_FD.lock().unwrap().clone(); let fd = MAGISK_LOGD_FD.lock().clone();
if let Some(logd) = fd if let Some(logd) = fd
&& f(&logd).is_err() && f(&logd).is_err()
{ {
// If any error occurs, shut down the logd pipe // If any error occurs, shut down the logd pipe
*MAGISK_LOGD_FD.lock().unwrap() = None; *MAGISK_LOGD_FD.lock() = None;
} }
} }
@@ -265,7 +266,9 @@ fn logfile_write_loop(mut pipe: File) -> io::Result<()> {
_ => continue, _ => continue,
}; };
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::ZERO);
// Note: the obvious better implementation is to use the rust chrono crate, however // Note: the obvious better implementation is to use the rust chrono crate, however
// the crate cannot fetch the proper local timezone without pulling in a bunch of // the crate cannot fetch the proper local timezone without pulling in a bunch of
@@ -322,7 +325,7 @@ pub fn start_log_daemon() {
let file = unsafe { File::from_raw_fd(arg as RawFd) }; let file = unsafe { File::from_raw_fd(arg as RawFd) };
logfile_write_loop(file).ok(); logfile_write_loop(file).ok();
// If any error occurs, shut down the logd pipe // If any error occurs, shut down the logd pipe
*MAGISK_LOGD_FD.lock().unwrap() = None; *MAGISK_LOGD_FD.lock() = None;
0 0
} }
@@ -331,7 +334,7 @@ pub fn start_log_daemon() {
chown(path.as_utf8_cstr(), Some(Uid::from(0)), Some(Gid::from(0)))?; chown(path.as_utf8_cstr(), Some(Uid::from(0)), Some(Gid::from(0)))?;
let read = path.open(OFlag::O_RDWR | OFlag::O_CLOEXEC)?; let read = path.open(OFlag::O_RDWR | OFlag::O_CLOEXEC)?;
let write = path.open(OFlag::O_WRONLY | OFlag::O_CLOEXEC)?; let write = path.open(OFlag::O_WRONLY | OFlag::O_CLOEXEC)?;
*MAGISK_LOGD_FD.lock().unwrap() = Some(Arc::new(write)); *MAGISK_LOGD_FD.lock() = Some(Arc::new(write));
unsafe { unsafe {
new_daemon_thread(logfile_writer_thread, read.into_raw_fd() as usize); new_daemon_thread(logfile_writer_thread, read.into_raw_fd() as usize);
} }

View File

@@ -889,7 +889,7 @@ impl MagiskD {
// Handle zygisk // Handle zygisk
if self.zygisk_enabled.load(Ordering::Acquire) { if self.zygisk_enabled.load(Ordering::Acquire) {
let mut zygisk = self.zygisk.lock().unwrap(); let mut zygisk = self.zygisk.lock();
zygisk.set_prop(); zygisk.set_prop();
inject_zygisk_bins(&zygisk.lib_name, &mut system); inject_zygisk_bins(&zygisk.lib_name, &mut system);
} }

View File

@@ -10,6 +10,7 @@ use nix::mount::MsFlags;
use nix::sys::stat::{Mode, SFlag, mknod}; use nix::sys::stat::{Mode, SFlag, mknod};
use num_traits::AsPrimitive; use num_traits::AsPrimitive;
use std::cmp::Ordering::{Greater, Less}; use std::cmp::Ordering::{Greater, Less};
use std::ffi::OsStr;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub fn setup_preinit_dir() { pub fn setup_preinit_dir() {
@@ -203,9 +204,8 @@ pub fn find_preinit_device() -> String {
} }
Path::new(&info.source) Path::new(&info.source)
.file_name() .file_name()
.unwrap() .and_then(OsStr::to_str)
.to_str() .unwrap_or_default()
.unwrap()
.to_string() .to_string()
} }
@@ -221,10 +221,7 @@ pub fn revert_unmount(pid: i32) {
// Unmount Magisk tmpfs and mounts from module files // Unmount Magisk tmpfs and mounts from module files
for info in parse_mount_info("self") { for info in parse_mount_info("self") {
if info.source == "magisk" if info.source == "magisk" || info.root.starts_with("/adb/modules") {
|| info.root.starts_with("/adb/modules")
|| (info.fs_type == "rootfs" && info.root.starts_with("/magisk"))
{
targets.push(info.target); targets.push(info.target);
} }
} }

View File

@@ -441,7 +441,7 @@ impl MagiskD {
} }
pub fn preserve_stub_apk(&self) { pub fn preserve_stub_apk(&self) {
let mut info = self.manager_info.lock().unwrap(); let mut info = self.manager_info.lock();
let apk = cstr::buf::default() let apk = cstr::buf::default()
.join_path(get_magisk_tmp()) .join_path(get_magisk_tmp())
@@ -458,19 +458,19 @@ impl MagiskD {
} }
pub fn get_manager_uid(&self, user: i32) -> i32 { pub fn get_manager_uid(&self, user: i32) -> i32 {
let mut info = self.manager_info.lock().unwrap(); let mut info = self.manager_info.lock();
let (uid, _) = info.get_manager(self, user, false); let (uid, _) = info.get_manager(self, user, false);
uid uid
} }
pub fn get_manager(&self, user: i32, install: bool) -> (i32, String) { pub fn get_manager(&self, user: i32, install: bool) -> (i32, String) {
let mut info = self.manager_info.lock().unwrap(); let mut info = self.manager_info.lock();
let (uid, pkg) = info.get_manager(self, user, install); let (uid, pkg) = info.get_manager(self, user, install);
(uid, pkg.to_string()) (uid, pkg.to_string())
} }
pub fn ensure_manager(&self) { pub fn ensure_manager(&self) {
let mut info = self.manager_info.lock().unwrap(); let mut info = self.manager_info.lock();
let _ = info.get_manager(self, 0, true); let _ = info.get_manager(self, 0, true);
} }

View File

@@ -44,7 +44,9 @@ impl Extra<'_> {
IntList(list) => { IntList(list) => {
cmd.args(["--es", self.key]); cmd.args(["--es", self.key]);
let mut tmp = String::new(); let mut tmp = String::new();
list.iter().for_each(|i| write!(&mut tmp, "{i},").unwrap()); list.iter().for_each(|i| {
write!(&mut tmp, "{i},").ok();
});
tmp.pop(); tmp.pop();
cmd.arg(&tmp); cmd.arg(&tmp);
} }
@@ -67,7 +69,9 @@ impl Extra<'_> {
IntList(list) => { IntList(list) => {
tmp = format!("{}:s:", self.key); tmp = format!("{}:s:", self.key);
if !list.is_empty() { if !list.is_empty() {
list.iter().for_each(|i| write!(&mut tmp, "{i},").unwrap()); list.iter().for_each(|i| {
write!(&mut tmp, "{i},").ok();
});
tmp.pop(); tmp.pop();
} }
} }
@@ -202,8 +206,11 @@ impl SuAppContext<'_> {
let mut pfd = [PollFd::new(fd.as_fd(), PollFlags::POLLIN)]; let mut pfd = [PollFd::new(fd.as_fd(), PollFlags::POLLIN)];
// Wait for data input for at most 70 seconds // Wait for data input for at most 70 seconds
nix::poll::poll(&mut pfd, PollTimeout::try_from(70 * 1000).unwrap()) nix::poll::poll(
.check_os_err("poll", None, None)?; &mut pfd,
PollTimeout::try_from(70 * 1000).unwrap_or(PollTimeout::NONE),
)
.check_os_err("poll", None, None)?;
fd fd
}; };

View File

@@ -7,11 +7,12 @@ use crate::socket::IpcRead;
use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn}; use base::{LoggedResult, ResultExt, WriteExt, debug, error, exit_on_error, libc, warn};
use std::os::fd::IntoRawFd; use std::os::fd::IntoRawFd;
use std::os::unix::net::{UCred, UnixStream}; use std::os::unix::net::{UCred, UnixStream};
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[allow(unused_imports)] #[allow(unused_imports)]
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
use std::sync::nonpoison::Mutex;
const DEFAULT_SHELL: &str = "/system/bin/sh"; const DEFAULT_SHELL: &str = "/system/bin/sh";
@@ -132,7 +133,7 @@ impl MagiskD {
let info = self.get_su_info(cred.uid as i32); let info = self.get_su_info(cred.uid as i32);
{ {
let mut access = info.access.lock().unwrap(); let mut access = info.access.lock();
// Talk to su manager // Talk to su manager
let mut app = SuAppContext { let mut app = SuAppContext {
@@ -203,7 +204,7 @@ impl MagiskD {
} }
let cached = self.cached_su_info.load(); let cached = self.cached_su_info.load();
if cached.uid == uid && cached.access.lock().unwrap().is_fresh() { if cached.uid == uid && cached.access.lock().is_fresh() {
return cached; return cached;
} }

View File

@@ -99,7 +99,6 @@ fn pump_tty_impl(ptmx: File, pump_stdin: bool) -> LoggedResult<()> {
let mut signal_fd: Option<SignalFd> = None; let mut signal_fd: Option<SignalFd> = None;
let raw_ptmx = ptmx.as_raw_fd(); let raw_ptmx = ptmx.as_raw_fd();
let mut raw_sig = -1;
let mut poll_fds = Vec::with_capacity(3); let mut poll_fds = Vec::with_capacity(3);
poll_fds.push(PollFd::new(ptmx.as_fd(), PollFlags::POLLIN)); poll_fds.push(PollFd::new(ptmx.as_fd(), PollFlags::POLLIN));
@@ -111,12 +110,14 @@ fn pump_tty_impl(ptmx: File, pump_stdin: bool) -> LoggedResult<()> {
.check_os_err("pthread_sigmask", None, None)?; .check_os_err("pthread_sigmask", None, None)?;
let sig = SignalFd::with_flags(&set, SfdFlags::SFD_CLOEXEC) let sig = SignalFd::with_flags(&set, SfdFlags::SFD_CLOEXEC)
.into_os_result("signalfd", None, None)?; .into_os_result("signalfd", None, None)?;
raw_sig = sig.as_raw_fd();
signal_fd = Some(sig); signal_fd = Some(sig);
poll_fds.push(PollFd::new( unsafe {
signal_fd.as_ref().unwrap().as_fd(), // SAFETY: signal_fd is always Some
PollFlags::POLLIN, poll_fds.push(PollFd::new(
)); signal_fd.as_ref().unwrap_unchecked().as_fd(),
PollFlags::POLLIN,
));
}
// We also need to pump stdin to ptmx // We also need to pump stdin to ptmx
poll_fds.push(PollFd::new( poll_fds.push(PollFd::new(
@@ -142,9 +143,11 @@ fn pump_tty_impl(ptmx: File, pump_stdin: bool) -> LoggedResult<()> {
pump_via_splice(FileOrStd::StdIn.as_file(), &ptmx, &pipe_fd)?; pump_via_splice(FileOrStd::StdIn.as_file(), &ptmx, &pipe_fd)?;
} else if raw_fd == raw_ptmx { } else if raw_fd == raw_ptmx {
pump_via_splice(&ptmx, FileOrStd::StdOut.as_file(), &pipe_fd)?; pump_via_splice(&ptmx, FileOrStd::StdOut.as_file(), &pipe_fd)?;
} else if raw_fd == raw_sig { } else if let Some(sig) = &signal_fd
&& raw_fd == sig.as_raw_fd()
{
sync_winsize(raw_ptmx); sync_winsize(raw_ptmx);
signal_fd.as_ref().unwrap().read_signal()?; sig.read_signal()?;
} }
} else if pfd } else if pfd
.revents() .revents()

View File

@@ -1,7 +1,8 @@
use base::{ResultExt, new_daemon_thread}; use base::{ResultExt, new_daemon_thread};
use nix::sys::signal::SigSet; use nix::sys::signal::SigSet;
use nix::unistd::{getpid, gettid}; use nix::unistd::{getpid, gettid};
use std::sync::{Condvar, LazyLock, Mutex, WaitTimeoutResult}; use std::sync::nonpoison::{Condvar, Mutex};
use std::sync::{LazyLock, WaitTimeoutResult};
use std::time::Duration; use std::time::Duration;
static THREAD_POOL: LazyLock<ThreadPool> = LazyLock::new(ThreadPool::default); static THREAD_POOL: LazyLock<ThreadPool> = LazyLock::new(ThreadPool::default);
@@ -33,16 +34,16 @@ impl ThreadPool {
let task: Option<Box<dyn FnOnce() + Send>>; let task: Option<Box<dyn FnOnce() + Send>>;
{ {
let mut info = self.info.lock().unwrap(); let mut info = self.info.lock();
info.idle_threads += 1; info.idle_threads += 1;
if info.task.is_none() { if info.task.is_none() {
if is_core_pool { if is_core_pool {
// Core pool never closes, wait forever. // Core pool never closes, wait forever.
info = self.task_is_some.wait(info).unwrap(); info = self.task_is_some.wait(info);
} else { } else {
let dur = Duration::from_secs(THREAD_IDLE_MAX_SEC); let dur = Duration::from_secs(THREAD_IDLE_MAX_SEC);
let result: WaitTimeoutResult; let result: WaitTimeoutResult;
(info, result) = self.task_is_some.wait_timeout(info, dur).unwrap(); (info, result) = self.task_is_some.wait_timeout(info, dur);
if result.timed_out() { if result.timed_out() {
// Terminate thread after timeout // Terminate thread after timeout
info.idle_threads -= 1; info.idle_threads -= 1;
@@ -72,10 +73,10 @@ impl ThreadPool {
0 0
} }
let mut info = self.info.lock().unwrap(); let mut info = self.info.lock();
while info.task.is_some() { while info.task.is_some() {
// Wait until task is none // Wait until task is none
info = self.task_is_none.wait(info).unwrap(); info = self.task_is_none.wait(info);
} }
info.task = Some(Box::new(f)); info.task = Some(Box::new(f));
if info.idle_threads == 0 { if info.idle_threads == 0 {

View File

@@ -87,25 +87,23 @@ impl ZygiskState {
} }
} }
let socket = if let Some(fd) = socket { if let Some(fd) = socket {
fd fd.send_fds(&[client.as_raw_fd()])?;
} else { } else {
// Create a new socket pair and fork zygiskd process // Create a new socket pair and fork zygiskd process
let (local, remote) = UnixStream::pair()?; let (mut local, remote) = UnixStream::pair()?;
if fork_dont_care() == 0 { if fork_dont_care() == 0 {
exec_zygiskd(is_64_bit, remote); exec_zygiskd(is_64_bit, remote);
} }
*socket = Some(local);
let local = socket.as_mut().unwrap();
if let Some(module_fds) = daemon.get_module_fds(is_64_bit) { if let Some(module_fds) = daemon.get_module_fds(is_64_bit) {
local.send_fds(&module_fds)?; local.send_fds(&module_fds)?;
} }
if local.read_decodable::<i32>()? != 0 { if local.read_decodable::<i32>()? != 0 {
return log_err!(); return log_err!();
} }
local local.send_fds(&[client.as_raw_fd()])?;
}; *socket = Some(local);
socket.send_fds(&[client.as_raw_fd()])?; }
Ok(()) Ok(())
} }
@@ -168,7 +166,6 @@ impl MagiskD {
ZygiskRequest::ConnectCompanion => self ZygiskRequest::ConnectCompanion => self
.zygisk .zygisk
.lock() .lock()
.unwrap()
.connect_zygiskd(client, self) .connect_zygiskd(client, self)
.log_with_msg(|w| w.write_str("zygiskd startup error"))?, .log_with_msg(|w| w.write_str("zygiskd startup error"))?,
ZygiskRequest::GetModDir => self.get_mod_dir(client)?, ZygiskRequest::GetModDir => self.get_mod_dir(client)?,
@@ -222,9 +219,12 @@ impl MagiskD {
let failed_ids: Vec<i32> = client.read_decodable()?; let failed_ids: Vec<i32> = client.read_decodable()?;
if let Some(module_list) = self.module_list.get() { if let Some(module_list) = self.module_list.get() {
for id in failed_ids { for id in failed_ids {
let Some(module) = module_list.get(id as usize) else {
continue;
};
let path = cstr::buf::default() let path = cstr::buf::default()
.join_path(MODULEROOT) .join_path(MODULEROOT)
.join_path(&module_list[id as usize].name) .join_path(&module.name)
.join_path("zygisk"); .join_path("zygisk");
// Create the unloaded marker file // Create the unloaded marker file
if let Ok(dir) = Directory::open(&path) { if let Ok(dir) = Directory::open(&path) {
@@ -240,7 +240,13 @@ impl MagiskD {
fn get_mod_dir(&self, mut client: UnixStream) -> LoggedResult<()> { fn get_mod_dir(&self, mut client: UnixStream) -> LoggedResult<()> {
let id: i32 = client.read_decodable()?; let id: i32 = client.read_decodable()?;
let module = &self.module_list.get().unwrap()[id as usize]; let Some(module) = self
.module_list
.get()
.and_then(|list| list.get(id as usize))
else {
return Ok(());
};
let dir = cstr::buf::default() let dir = cstr::buf::default()
.join_path(MODULEROOT) .join_path(MODULEROOT)
.join_path(&module.name); .join_path(&module.name);

View File

@@ -1,238 +1,591 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
primitives = ['jint', 'jboolean', 'jlong'] primitives = ["jint", "jboolean", "jlong"]
class JType: class JType:
def __init__(self, cpp, jni): def __init__(self, cpp: str, jni: str):
self.cpp = cpp self.cpp = cpp
self.jni = jni self.jni = jni
class JArray(JType): class JArray(JType):
def __init__(self, type): def __init__(self, type: JType):
if type.cpp in primitives: if type.cpp in primitives:
name = type.cpp + 'Array' name = type.cpp + "Array"
else: else:
name = 'jobjectArray' name = "jobjectArray"
super().__init__(name, '[' + type.jni) super().__init__(name, "[" + type.jni)
class Argument: class Argument:
def __init__(self, name, type, set_arg = False): def __init__(self, name: str, type: JType, set_arg=False):
self.name = name self.name = name
self.type = type self.type = type
self.set_arg = set_arg self.set_arg = set_arg
def cpp(self): def cpp(self) -> str:
return f'{self.type.cpp} {self.name}' return f"{self.type.cpp} {self.name}"
# Args we don't care, give it an auto generated name # Args we don't care, give it an auto generated name
class Anon(Argument): class Anon(Argument):
cnt = 0 cnt = 0
def __init__(self, type):
super().__init__(f'_{Anon.cnt}', type) def __init__(self, type: JType):
super().__init__(f"_{Anon.cnt}", type)
Anon.cnt += 1 Anon.cnt += 1
class Return: class Return:
def __init__(self, value, type): def __init__(self, value: str, type: JType):
self.value = value self.value = value
self.type = type self.type = type
class Method:
def __init__(self, name, ret, args): class JNIMethod:
def __init__(self, name: str, ret: Return, args: list[Argument]):
self.name = name self.name = name
self.ret = ret self.ret = ret
self.args = args self.args = args
def cpp(self): def arg_list_name(self) -> str:
return ', '.join(map(lambda x: x.cpp(), self.args)) return "env, clazz, " + ", ".join(map(lambda x: x.name, self.args))
def name_list(self): def arg_list_cpp(self) -> str:
return ', '.join(map(lambda x: x.name, self.args)) return "JNIEnv *env, jclass clazz, " + ", ".join(
map(lambda x: x.cpp(), self.args)
)
def jni(self): def cpp_fn_type(self) -> str:
args = ''.join(map(lambda x: x.type.jni, self.args)) return f"{self.ret.type.cpp}(*)({self.arg_list_cpp()}"
return f'({args}){self.ret.type.jni}'
def body(self, name, i): def cpp_lambda_sig(self) -> str:
return '' return f"[] [[clang::no_stack_protector]] ({self.arg_list_cpp()}) static -> {self.ret.type.cpp}"
class JNIHook(Method): def jni_sig(self):
def __init__(self, ver, ret, args): args = "".join(map(lambda x: x.type.jni, self.args))
name = f'{self.base_name()}_{ver}' return f"({args}){self.ret.type.jni}"
class JNIHook(JNIMethod):
def __init__(self, ver: str, ret: Return, args: list[Argument]):
name = f"{self.hook_target()}_{ver}"
super().__init__(name, ret, args) super().__init__(name, ret, args)
def base_name(self): def hook_target(self):
return '' return ""
def body(self, orig_fn_ptr: str):
return ""
def orig_method(self, name, i):
return f'reinterpret_cast<{self.ret.type.cpp}(*)(JNIEnv *env, jclass clazz, {self.cpp()})>(g_hook->{name}_methods[{i}].fnPtr)'
def ind(i): def ind(i):
return '\n' + ' ' * i return "\n" + " " * i
# Common types # Common types
jint = JType('jint', 'I') jint = JType("jint", "I")
jintArray = JArray(jint) jintArray = JArray(jint)
jstring = JType('jstring', 'Ljava/lang/String;') jstring = JType("jstring", "Ljava/lang/String;")
jboolean = JType('jboolean', 'Z') jboolean = JType("jboolean", "Z")
jlong = JType('jlong', 'J') jlong = JType("jlong", "J")
void = JType('void', 'V') void = JType("void", "V")
class ForkAndSpec(JNIHook):
class ForkApp(JNIHook):
def __init__(self, ver, args): def __init__(self, ver, args):
super().__init__(ver, Return('ctx.pid', jint), args) super().__init__(ver, Return("ctx.pid", jint), args)
def base_name(self): def hook_target(self):
return 'nativeForkAndSpecialize' return "nativeForkAndSpecialize"
def init_args(self): def init_args(self):
return 'AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);' return "AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);"
def body(self, name, i): def body(self, orig_fn_ptr: str):
decl = '' decl = ""
decl += ind(3) + self.init_args() decl += ind(3) + self.init_args()
for a in self.args: for a in self.args:
if a.set_arg: if a.set_arg:
decl += ind(3) + f'args.{a.name} = &{a.name};' decl += ind(3) + f"args.{a.name} = &{a.name};"
decl += ind(3) + 'ZygiskContext ctx(env, &args);' decl += ind(3) + "ZygiskContext ctx(env, &args);"
decl += ind(3) + f'ctx.{self.base_name()}_pre();' decl += ind(3) + f"ctx.{self.hook_target()}_pre();"
decl += ind(3) + self.orig_method(name, i) + '(' decl += ind(3) + f"reinterpret_cast<{self.cpp_fn_type()})>({orig_fn_ptr})("
decl += ind(4) + f'env, clazz, {self.name_list()}' decl += ind(4) + self.arg_list_name()
decl += ind(3) + ');' decl += ind(3) + ");"
decl += ind(3) + f'ctx.{self.base_name()}_post();' decl += ind(3) + f"ctx.{self.hook_target()}_post();"
if self.ret.value:
decl += ind(3) + f"return {self.ret.value};"
return decl return decl
class SpecApp(ForkAndSpec):
def __init__(self, ver, args): class SpecializeApp(ForkApp):
def __init__(self, ver: str, args: list[Argument]):
super().__init__(ver, args) super().__init__(ver, args)
self.ret = Return('', void) self.ret = Return("", void)
def base_name(self): def hook_target(self):
return 'nativeSpecializeAppProcess' return "nativeSpecializeAppProcess"
class ForkServer(ForkAndSpec):
def base_name(self): class ForkServer(ForkApp):
return 'nativeForkSystemServer' def hook_target(self):
return "nativeForkSystemServer"
def init_args(self): def init_args(self):
return 'ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);' return "ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);"
# Common args # Common args
uid = Argument('uid', jint) uid = Argument("uid", jint)
gid = Argument('gid', jint) gid = Argument("gid", jint)
gids = Argument('gids', jintArray) gids = Argument("gids", jintArray)
runtime_flags = Argument('runtime_flags', jint) runtime_flags = Argument("runtime_flags", jint)
rlimits = Argument('rlimits', JArray(jintArray)) rlimits = Argument("rlimits", JArray(jintArray))
mount_external = Argument('mount_external', jint) mount_external = Argument("mount_external", jint)
se_info = Argument('se_info', jstring) se_info = Argument("se_info", jstring)
nice_name = Argument('nice_name', jstring) nice_name = Argument("nice_name", jstring)
fds_to_close = Argument('fds_to_close', jintArray) fds_to_close = Argument("fds_to_close", jintArray)
instruction_set = Argument('instruction_set', jstring) instruction_set = Argument("instruction_set", jstring)
app_data_dir = Argument('app_data_dir', jstring) app_data_dir = Argument("app_data_dir", jstring)
# o # o
fds_to_ignore = Argument('fds_to_ignore', jintArray, True) fds_to_ignore = Argument("fds_to_ignore", jintArray, True)
# p # p
is_child_zygote = Argument('is_child_zygote', jboolean, True) is_child_zygote = Argument("is_child_zygote", jboolean, True)
# q_alt # q_alt
is_top_app = Argument('is_top_app', jboolean, True) is_top_app = Argument("is_top_app", jboolean, True)
# r # r
pkg_data_info_list = Argument('pkg_data_info_list', JArray(jstring), True) pkg_data_info_list = Argument("pkg_data_info_list", JArray(jstring), True)
whitelisted_data_info_list = Argument('whitelisted_data_info_list', JArray(jstring), True) whitelisted_data_info_list = Argument(
mount_data_dirs = Argument('mount_data_dirs', jboolean, True) "whitelisted_data_info_list", JArray(jstring), True
mount_storage_dirs = Argument('mount_storage_dirs', jboolean, True) )
mount_data_dirs = Argument("mount_data_dirs", jboolean, True)
mount_storage_dirs = Argument("mount_storage_dirs", jboolean, True)
# u # u
mount_sysprop_overrides = Argument('mount_sysprop_overrides', jboolean, True) mount_sysprop_overrides = Argument("mount_sysprop_overrides", jboolean, True)
# b
use_fifo_ui = Argument("use_fifo_ui", jboolean, False)
# server # server
permitted_capabilities = Argument('permitted_capabilities', jlong) permitted_capabilities = Argument("permitted_capabilities", jlong)
effective_capabilities = Argument('effective_capabilities', jlong) effective_capabilities = Argument("effective_capabilities", jlong)
# Method definitions # Method definitions
fas_l = ForkAndSpec('l', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_l = ForkApp(
se_info, nice_name, fds_to_close, instruction_set, app_data_dir]) "l",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
instruction_set,
app_data_dir,
],
)
fas_o = ForkAndSpec('o', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_o = ForkApp(
se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir]) "o",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
fds_to_ignore,
instruction_set,
app_data_dir,
],
)
fas_p = ForkAndSpec('p', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_p = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir]) "p",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
fds_to_ignore,
is_child_zygote,
instruction_set,
app_data_dir,
],
)
fas_q_alt = ForkAndSpec('q_alt', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_q_alt = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app]) "q_alt",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
fds_to_ignore,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
],
)
fas_r = ForkAndSpec('r', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_r = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, "r",
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
fds_to_ignore,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
],
)
fas_u = ForkAndSpec('u', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_u = ForkApp(
nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, "u",
pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
fds_to_ignore,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
mount_sysprop_overrides,
],
)
fas_samsung_m = ForkAndSpec('samsung_m', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_b = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, instruction_set, app_data_dir]) "b",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
fds_to_close,
fds_to_ignore,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
use_fifo_ui,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
mount_sysprop_overrides,
],
)
fas_samsung_n = ForkAndSpec('samsung_n', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_samsung_m = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, instruction_set, app_data_dir, Anon(jint)]) "samsung_m",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
instruction_set,
app_data_dir,
],
)
fas_samsung_o = ForkAndSpec('samsung_o', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_samsung_n = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir]) "samsung_n",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
instruction_set,
app_data_dir,
Anon(jint),
],
)
fas_samsung_p = ForkAndSpec('samsung_p', [uid, gid, gids, runtime_flags, rlimits, mount_external, fas_samsung_o = ForkApp(
se_info, Anon(jint), Anon(jint), nice_name, fds_to_close, fds_to_ignore, is_child_zygote, "samsung_o",
instruction_set, app_data_dir]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
fds_to_ignore,
instruction_set,
app_data_dir,
],
)
spec_q = SpecApp('q', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, fas_samsung_p = ForkApp(
nice_name, is_child_zygote, instruction_set, app_data_dir]) "samsung_p",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
fds_to_close,
fds_to_ignore,
is_child_zygote,
instruction_set,
app_data_dir,
],
)
spec_q_alt = SpecApp('q_alt', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, spec_q = SpecializeApp(
nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app]) "q",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
],
)
spec_r = SpecApp('r', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, spec_q_alt = SpecializeApp(
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, "q_alt",
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
],
)
spec_u = SpecApp('u', [uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, spec_r = SpecializeApp(
is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, "r",
whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides]) [
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
],
)
spec_samsung_q = SpecApp('samsung_q', [uid, gid, gids, runtime_flags, rlimits, mount_external, spec_u = SpecializeApp(
se_info, Anon(jint), Anon(jint), nice_name, is_child_zygote, instruction_set, app_data_dir]) "u",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
is_top_app,
pkg_data_info_list,
whitelisted_data_info_list,
mount_data_dirs,
mount_storage_dirs,
mount_sysprop_overrides,
],
)
server_l = ForkServer('l', [uid, gid, gids, runtime_flags, rlimits, spec_samsung_q = SpecializeApp(
permitted_capabilities, effective_capabilities]) "samsung_q",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
mount_external,
se_info,
Anon(jint),
Anon(jint),
nice_name,
is_child_zygote,
instruction_set,
app_data_dir,
],
)
server_samsung_q = ForkServer('samsung_q', [uid, gid, gids, runtime_flags, Anon(jint), Anon(jint), rlimits, server_l = ForkServer(
permitted_capabilities, effective_capabilities]) "l",
[
uid,
gid,
gids,
runtime_flags,
rlimits,
permitted_capabilities,
effective_capabilities,
],
)
def gen_jni_def(name, methods): server_samsung_q = ForkServer(
decl = '' "samsung_q",
decl += ind(0) + f'std::array<JNINativeMethod, {len(methods)}> {name}_methods = {{{{' [
uid,
gid,
gids,
runtime_flags,
Anon(jint),
Anon(jint),
rlimits,
permitted_capabilities,
effective_capabilities,
],
)
def gen_jni_def(field: str, methods: list[JNIHook]):
decl = ""
decl += ind(0) + f"std::array<JNINativeMethod, {len(methods)}> {field} = {{{{"
for i, m in enumerate(methods): for i, m in enumerate(methods):
decl += ind(1) + '{' decl += ind(1) + f"// {m.name}"
decl += ind(2) + f'"{m.base_name()}",' decl += ind(1) + "{"
decl += ind(2) + f'"{m.jni()}",' decl += ind(2) + f'"{m.hook_target()}",'
decl += ind(2) + f'(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, {m.cpp()}) static -> {m.ret.type.cpp} {{' decl += ind(2) + f'"{m.jni_sig()}",'
decl += m.body(name, i) decl += ind(2) + f"(void *) +{m.cpp_lambda_sig()} {{"
if m.ret.value: orig_fn_ptr = f"get_defs()->{field}[{i}].fnPtr"
decl += ind(3) + f'return {m.ret.value};' decl += m.body(orig_fn_ptr)
decl += ind(2) + '}' decl += ind(2) + "}"
decl += ind(1) + '},' decl += ind(1) + "},"
decl += ind(0) + '}};' decl += ind(0) + "}};"
decl += ind(0) decl += ind(0)
return decl return decl
with open('jni_hooks.hpp', 'w') as f:
f.write('// Generated by gen_jni_hooks.py\n')
f.write(gen_jni_def('zygote', [ with open("jni_hooks.hpp", "w") as f:
fas_l, fas_o, fas_p, fas_q_alt, fas_r, fas_u, fas_samsung_m, fas_samsung_n, fas_samsung_o, f.write("// Generated by gen_jni_hooks.py\n")
fas_samsung_p, spec_q, spec_q_alt, spec_r, spec_u, spec_samsung_q, server_l, server_samsung_q])) f.write("#pragma once\n\n")
f.write("struct JniHookDefinitions;\n")
f.write("static JniHookDefinitions *get_defs();\n\n")
f.write("struct JniHookDefinitions {\n")
f.write(
gen_jni_def(
"fork_app_methods",
[
fas_l,
fas_o,
fas_p,
fas_q_alt,
fas_r,
fas_u,
fas_b,
fas_samsung_m,
fas_samsung_n,
fas_samsung_o,
fas_samsung_p,
],
)
)
f.write('\n') f.write(
gen_jni_def(
"specialize_app_methods",
[spec_q, spec_q_alt, spec_r, spec_u, spec_samsung_q],
)
)
f.write(gen_jni_def("fork_server_methods", [server_l, server_samsung_q]))
f.write("\n};\n")

View File

@@ -8,10 +8,10 @@
#include <lsplt.hpp> #include <lsplt.hpp>
#include <base.hpp> #include <base.hpp>
#include <consts.hpp>
#include "zygisk.hpp" #include "zygisk.hpp"
#include "module.hpp" #include "module.hpp"
#include "jni_hooks.hpp"
using namespace std; using namespace std;
@@ -90,25 +90,15 @@ using namespace std;
constexpr const char *kZygoteInit = "com.android.internal.os.ZygoteInit"; constexpr const char *kZygoteInit = "com.android.internal.os.ZygoteInit";
constexpr const char *kZygote = "com/android/internal/os/Zygote"; constexpr const char *kZygote = "com/android/internal/os/Zygote";
constexpr const char *kForkApp = "nativeForkAndSpecialize";
// Global contexts: constexpr const char *kSpecializeApp = "nativeSpecializeAppProcess";
// constexpr const char *kForkServer = "nativeForkSystemServer";
// HookContext lives as long as Zygisk is loaded in memory. It tracks the process's function
// hooking state and bootstraps code injection until we replace the process specialization methods.
//
// ZygiskContext lives during the process specialization process. It implements Zygisk
// features, such as loading modules and customizing process fork/specialization.
ZygiskContext *g_ctx;
struct HookContext;
static HookContext *g_hook;
using JNIMethods = std::span<JNINativeMethod>; using JNIMethods = std::span<JNINativeMethod>;
using JNIMethodsDyn = std::pair<unique_ptr<JNINativeMethod[]>, size_t>;
struct HookContext { struct HookContext : JniHookDefinitions {
#include "jni_hooks.hpp"
// std::array<JNINativeMethod> zygote_methods
vector<tuple<dev_t, ino_t, const char *, void **>> plt_backup; vector<tuple<dev_t, ino_t, const char *, void **>> plt_backup;
const NativeBridgeRuntimeCallbacks *runtime_callbacks = nullptr; const NativeBridgeRuntimeCallbacks *runtime_callbacks = nullptr;
void *self_handle = nullptr; void *self_handle = nullptr;
@@ -119,15 +109,34 @@ struct HookContext {
void restore_plt_hook(); void restore_plt_hook();
void hook_zygote_jni(); void hook_zygote_jni();
void restore_zygote_hook(JNIEnv *env); void restore_zygote_hook(JNIEnv *env);
void hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods); void hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods) const;
void post_native_bridge_load(void *handle); void post_native_bridge_load(void *handle);
private: private:
void register_hook(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func); void register_hook(dev_t dev, ino_t inode, const char *symbol, void *new_func, void **old_func);
int hook_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods) const;
JNIMethodsDyn get_jni_methods(JNIEnv *env, jclass clazz) const;
}; };
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Global contexts:
//
// HookContext lives as long as Zygisk is loaded in memory. It tracks the process's function
// hooking state and bootstraps code injection until we replace the process specialization methods.
//
// ZygiskContext lives during the process specialization process. It implements Zygisk
// features, such as loading modules and customizing process fork/specialization.
ZygiskContext *g_ctx;
static HookContext *g_hook;
static JniHookDefinitions *get_defs() {
return g_hook;
}
// -----------------------------------------------------------------
#define DCL_HOOK_FUNC(ret, func, ...) \ #define DCL_HOOK_FUNC(ret, func, ...) \
ret (*old_##func)(__VA_ARGS__); \ ret (*old_##func)(__VA_ARGS__); \
ret new_##func(__VA_ARGS__) ret new_##func(__VA_ARGS__)
@@ -452,56 +461,74 @@ void HookContext::restore_plt_hook() {
// ----------------------------------------------------------------- // -----------------------------------------------------------------
void HookContext::hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods) { JNIMethodsDyn HookContext::get_jni_methods(JNIEnv *env, jclass clazz) const {
jclass clazz; size_t total = runtime_callbacks->getNativeMethodCount(env, clazz);
if (!runtime_callbacks || !env || !clz || !(clazz = env->FindClass(clz))) { auto methods = std::make_unique_for_overwrite<JNINativeMethod[]>(total);
for (auto &method : methods) { runtime_callbacks->getNativeMethods(env, clazz, methods.get(), total);
method.fnPtr = nullptr; return std::make_pair(std::move(methods), total);
} }
return;
}
// Backup existing methods static void register_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods) {
auto total = runtime_callbacks->getNativeMethodCount(env, clazz);
auto old_methods = std::make_unique_for_overwrite<JNINativeMethod[]>(total);
runtime_callbacks->getNativeMethods(env, clazz, old_methods.get(), total);
// WARNING: the signature field returned from getNativeMethods is in a non-standard format.
// DO NOT TRY TO USE IT. This is the reason why we try to call RegisterNatives on every single
// provided JNI methods directly to be 100% sure about whether a signature matches or not.
// Replace methods
for (auto &method : methods) { for (auto &method : methods) {
// It's useful to allow nullptr function pointer for restoring hook // It's useful to allow nullptr function pointer for restoring hook
if (!method.fnPtr) continue; if (!method.fnPtr) continue;
// It's normal that the method is not found // It's normal that the method is not found
if (env->RegisterNatives(clazz, &method, 1) == JNI_ERR || env->ExceptionCheck() == JNI_TRUE) { if (env->RegisterNatives(clazz, &method, 1) == JNI_ERR || env->ExceptionCheck() == JNI_TRUE) {
if (auto exception = env->ExceptionOccurred()) {
env->DeleteLocalRef(exception);
}
env->ExceptionClear(); env->ExceptionClear();
method.fnPtr = nullptr; method.fnPtr = nullptr;
} }
} }
}
int HookContext::hook_jni_methods(JNIEnv *env, jclass clazz, JNIMethods methods) const {
// Backup existing methods
auto o = get_jni_methods(env, clazz);
const auto old_methods = span(o.first.get(), o.second);
// WARNING: the signature field returned from getNativeMethods is in a non-standard format.
// DO NOT TRY TO USE IT. This is the reason why we try to call RegisterNatives on every single
// provided JNI methods directly to be 100% sure about whether a signature matches or not.
// Replace methods
register_jni_methods(env, clazz, methods);
// Fetch the new set of native methods // Fetch the new set of native methods
auto new_methods = std::make_unique_for_overwrite<JNINativeMethod[]>(total); auto n = get_jni_methods(env, clazz);
runtime_callbacks->getNativeMethods(env, clazz, new_methods.get(), total); const auto new_methods = span(n.first.get(), n.second);
// Find the old function pointer and return to caller // Find the old function pointer and return to caller
int hook_count = 0;
for (auto &method : methods) { for (auto &method : methods) {
if (!method.fnPtr) continue; if (!method.fnPtr) continue;
for (auto i = 0; i < total; ++i) { for (const auto &new_method : new_methods) {
auto &new_method = new_methods[i];
if (new_method.fnPtr == method.fnPtr) { if (new_method.fnPtr == method.fnPtr) {
auto &old_method = old_methods[i]; for (const auto &old_method : old_methods) {
ZLOGV("replace %s#%s%s %p -> %p\n", clz, method.name, method.signature, old_method.fnPtr, method.fnPtr); if (strcmp(old_method.name, new_method.name) == 0 &&
method.fnPtr = old_method.fnPtr; strcmp(old_method.signature, new_method.signature) == 0) {
break; ZLOGV("replace %s %s %p -> %p\n",
method.name, method.signature, old_method.fnPtr, method.fnPtr);
method.fnPtr = old_method.fnPtr;
++hook_count;
// Break 2 levels of for loop
goto next_method;
}
}
} }
} }
next_method:
} }
return hook_count;
}
void HookContext::hook_jni_methods(JNIEnv *env, const char *clz, JNIMethods methods) const {
jclass clazz;
if (!runtime_callbacks || !env || !clz || !((clazz = env->FindClass(clz)))) {
ranges::for_each(methods, [](auto &m) { m.fnPtr = nullptr; });
return;
}
hook_jni_methods(env, clazz, methods);
} }
void HookContext::hook_zygote_jni() { void HookContext::hook_zygote_jni() {
@@ -538,11 +565,54 @@ void HookContext::hook_zygote_jni() {
if (res != JNI_OK || env == nullptr) { if (res != JNI_OK || env == nullptr) {
ZLOGW("JNIEnv not found\n"); ZLOGW("JNIEnv not found\n");
} }
hook_jni_methods(env, kZygote, zygote_methods);
JNINativeMethod missing_method{};
bool replaced_fork_app = false;
bool replaced_specialize_app = false;
bool replaced_fork_server = false;
jclass clazz = env->FindClass(kZygote);
auto [ptr, count] = get_jni_methods(env, clazz);
for (const auto methods = span(ptr.get(), count); const auto &method : methods) {
if (strcmp(method.name, kForkApp) == 0) {
if (hook_jni_methods(env, clazz, fork_app_methods) == 0) {
missing_method = method;
break;
}
replaced_fork_app = true;
} else if (strcmp(method.name, kSpecializeApp) == 0) {
if (hook_jni_methods(env, clazz, specialize_app_methods) == 0) {
missing_method = method;
break;
}
replaced_specialize_app = true;
} else if (strcmp(method.name, kForkServer) == 0) {
if (hook_jni_methods(env, clazz, fork_server_methods) == 0) {
missing_method = method;
break;
}
replaced_fork_server = true;
}
}
if (missing_method.name != nullptr) {
ZLOGE("Cannot hook method: %s %s\n", missing_method.name, missing_method.signature);
// Restore methods that were already replaced
if (replaced_fork_app) register_jni_methods(env, clazz, fork_app_methods);
if (replaced_specialize_app) register_jni_methods(env, clazz, specialize_app_methods);
if (replaced_fork_server) register_jni_methods(env, clazz, fork_server_methods);
// Clear the method lists just in case
ranges::for_each(fork_app_methods, [](auto &m) { m.fnPtr = nullptr; });
ranges::for_each(specialize_app_methods, [](auto &m) { m.fnPtr = nullptr; });
ranges::for_each(fork_server_methods, [](auto &m) { m.fnPtr = nullptr; });
}
} }
void HookContext::restore_zygote_hook(JNIEnv *env) { void HookContext::restore_zygote_hook(JNIEnv *env) {
hook_jni_methods(env, kZygote, zygote_methods); jclass clazz = env->FindClass(kZygote);
register_jni_methods(env, clazz, fork_app_methods);
register_jni_methods(env, clazz, specialize_app_methods);
register_jni_methods(env, clazz, fork_server_methods);
} }
// ----------------------------------------------------------------- // -----------------------------------------------------------------
@@ -553,5 +623,5 @@ void hook_entry() {
} }
void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods) { void hookJniNativeMethods(JNIEnv *env, const char *clz, JNINativeMethod *methods, int numMethods) {
g_hook->hook_jni_methods(env, clz, { methods, (size_t) numMethods }); g_hook->hook_jni_methods(env, clz, { methods, static_cast<size_t>(numMethods) });
} }

View File

@@ -1,6 +1,13 @@
// Generated by gen_jni_hooks.py // Generated by gen_jni_hooks.py
#pragma once
std::array<JNINativeMethod, 17> zygote_methods = {{ struct JniHookDefinitions;
static JniHookDefinitions *get_defs();
struct JniHookDefinitions {
std::array<JNINativeMethod, 11> fork_app_methods = {{
// nativeForkAndSpecialize_l
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I", "(II[II[[IILjava/lang/String;Ljava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
@@ -8,13 +15,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[0].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[0].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, instruction_set, app_data_dir
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_o
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
@@ -23,13 +31,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.fds_to_ignore = &fds_to_ignore; args.fds_to_ignore = &fds_to_ignore;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[1].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[1].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_p
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
@@ -39,13 +48,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[2].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[2].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_q_alt
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z)I", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z)I",
@@ -56,13 +66,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(g_hook->zygote_methods[3].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(get_defs()->fork_app_methods[3].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_r
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
@@ -77,13 +88,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.mount_storage_dirs = &mount_storage_dirs; args.mount_storage_dirs = &mount_storage_dirs;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(g_hook->zygote_methods[4].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(get_defs()->fork_app_methods[4].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_u
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)I", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)I",
@@ -99,13 +111,37 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.mount_sysprop_overrides = &mount_sysprop_overrides; args.mount_sysprop_overrides = &mount_sysprop_overrides;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(g_hook->zygote_methods[5].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->fork_app_methods[5].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_b
{
"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;ZZ[Ljava/lang/String;[Ljava/lang/String;ZZZ)I",
(void *) +[] [[clang::no_stack_protector]] (JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) static -> jint {
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
args.fds_to_ignore = &fds_to_ignore;
args.is_child_zygote = &is_child_zygote;
args.is_top_app = &is_top_app;
args.pkg_data_info_list = &pkg_data_info_list;
args.whitelisted_data_info_list = &whitelisted_data_info_list;
args.mount_data_dirs = &mount_data_dirs;
args.mount_storage_dirs = &mount_storage_dirs;
args.mount_sysprop_overrides = &mount_sysprop_overrides;
ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jboolean use_fifo_ui, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->fork_app_methods[6].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir, is_top_app, use_fifo_ui, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
);
ctx.nativeForkAndSpecialize_post();
return ctx.pid;
}
},
// nativeForkAndSpecialize_samsung_m
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I", "(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;)I",
@@ -113,13 +149,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _0, jint _1, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[6].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _0, jint _1, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[7].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _0, _1, nice_name, fds_to_close, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _0, _1, nice_name, fds_to_close, instruction_set, app_data_dir
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_samsung_n
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;I)I", "(II[II[[IILjava/lang/String;IILjava/lang/String;[ILjava/lang/String;Ljava/lang/String;I)I",
@@ -127,13 +164,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir); AppSpecializeArgs_v5 args(uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, instruction_set, app_data_dir);
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _2, jint _3, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint _4)>(g_hook->zygote_methods[7].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _2, jint _3, jstring nice_name, jintArray fds_to_close, jstring instruction_set, jstring app_data_dir, jint _4)>(get_defs()->fork_app_methods[8].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _2, _3, nice_name, fds_to_close, instruction_set, app_data_dir, _4 env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _2, _3, nice_name, fds_to_close, instruction_set, app_data_dir, _4
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_samsung_o
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;IILjava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I", "(II[II[[IILjava/lang/String;IILjava/lang/String;[I[ILjava/lang/String;Ljava/lang/String;)I",
@@ -142,13 +180,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.fds_to_ignore = &fds_to_ignore; args.fds_to_ignore = &fds_to_ignore;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _5, jint _6, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[8].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _5, jint _6, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[9].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _5, _6, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _5, _6, nice_name, fds_to_close, fds_to_ignore, instruction_set, app_data_dir
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkAndSpecialize_samsung_p
{ {
"nativeForkAndSpecialize", "nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;IILjava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I", "(II[II[[IILjava/lang/String;IILjava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
@@ -158,13 +197,17 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkAndSpecialize_pre(); ctx.nativeForkAndSpecialize_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _7, jint _8, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[9].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _7, jint _8, jstring nice_name, jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->fork_app_methods[10].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _7, _8, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _7, _8, nice_name, fds_to_close, fds_to_ignore, is_child_zygote, instruction_set, app_data_dir
); );
ctx.nativeForkAndSpecialize_post(); ctx.nativeForkAndSpecialize_post();
return ctx.pid; return ctx.pid;
} }
}, },
}};
std::array<JNINativeMethod, 5> specialize_app_methods = {{
// nativeSpecializeAppProcess_q
{ {
"nativeSpecializeAppProcess", "nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
@@ -173,12 +216,13 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre(); ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[10].fnPtr)( reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->specialize_app_methods[0].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir
); );
ctx.nativeSpecializeAppProcess_post(); ctx.nativeSpecializeAppProcess_post();
} }
}, },
// nativeSpecializeAppProcess_q_alt
{ {
"nativeSpecializeAppProcess", "nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z)V", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z)V",
@@ -188,12 +232,13 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.is_top_app = &is_top_app; args.is_top_app = &is_top_app;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre(); ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(g_hook->zygote_methods[11].fnPtr)( reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app)>(get_defs()->specialize_app_methods[1].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app
); );
ctx.nativeSpecializeAppProcess_post(); ctx.nativeSpecializeAppProcess_post();
} }
}, },
// nativeSpecializeAppProcess_r
{ {
"nativeSpecializeAppProcess", "nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
@@ -207,12 +252,13 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.mount_storage_dirs = &mount_storage_dirs; args.mount_storage_dirs = &mount_storage_dirs;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre(); ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(g_hook->zygote_methods[12].fnPtr)( reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs)>(get_defs()->specialize_app_methods[2].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs
); );
ctx.nativeSpecializeAppProcess_post(); ctx.nativeSpecializeAppProcess_post();
} }
}, },
// nativeSpecializeAppProcess_u
{ {
"nativeSpecializeAppProcess", "nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)V", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)V",
@@ -227,12 +273,13 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.mount_sysprop_overrides = &mount_sysprop_overrides; args.mount_sysprop_overrides = &mount_sysprop_overrides;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre(); ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(g_hook->zygote_methods[13].fnPtr)( reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides)>(get_defs()->specialize_app_methods[3].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, nice_name, is_child_zygote, instruction_set, app_data_dir, is_top_app, pkg_data_info_list, whitelisted_data_info_list, mount_data_dirs, mount_storage_dirs, mount_sysprop_overrides
); );
ctx.nativeSpecializeAppProcess_post(); ctx.nativeSpecializeAppProcess_post();
} }
}, },
// nativeSpecializeAppProcess_samsung_q
{ {
"nativeSpecializeAppProcess", "nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;IILjava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V", "(II[II[[IILjava/lang/String;IILjava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
@@ -241,12 +288,16 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
args.is_child_zygote = &is_child_zygote; args.is_child_zygote = &is_child_zygote;
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeSpecializeAppProcess_pre(); ctx.nativeSpecializeAppProcess_pre();
reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _9, jint _10, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(g_hook->zygote_methods[14].fnPtr)( reinterpret_cast<void(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jint _9, jint _10, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir)>(get_defs()->specialize_app_methods[4].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _9, _10, nice_name, is_child_zygote, instruction_set, app_data_dir env, clazz, uid, gid, gids, runtime_flags, rlimits, mount_external, se_info, _9, _10, nice_name, is_child_zygote, instruction_set, app_data_dir
); );
ctx.nativeSpecializeAppProcess_post(); ctx.nativeSpecializeAppProcess_post();
} }
}, },
}};
std::array<JNINativeMethod, 2> fork_server_methods = {{
// nativeForkSystemServer_l
{ {
"nativeForkSystemServer", "nativeForkSystemServer",
"(II[II[[IJJ)I", "(II[II[[IJJ)I",
@@ -254,13 +305,14 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities); ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkSystemServer_pre(); ctx.nativeForkSystemServer_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(g_hook->zygote_methods[15].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(get_defs()->fork_server_methods[0].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities env, clazz, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities
); );
ctx.nativeForkSystemServer_post(); ctx.nativeForkSystemServer_post();
return ctx.pid; return ctx.pid;
} }
}, },
// nativeForkSystemServer_samsung_q
{ {
"nativeForkSystemServer", "nativeForkSystemServer",
"(II[IIII[[IJJ)I", "(II[IIII[[IJJ)I",
@@ -268,7 +320,7 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities); ServerSpecializeArgs_v1 args(uid, gid, gids, runtime_flags, permitted_capabilities, effective_capabilities);
ZygiskContext ctx(env, &args); ZygiskContext ctx(env, &args);
ctx.nativeForkSystemServer_pre(); ctx.nativeForkSystemServer_pre();
reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _11, jint _12, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(g_hook->zygote_methods[16].fnPtr)( reinterpret_cast<jint(*)(JNIEnv *env, jclass clazz, jint uid, jint gid, jintArray gids, jint runtime_flags, jint _11, jint _12, jobjectArray rlimits, jlong permitted_capabilities, jlong effective_capabilities)>(get_defs()->fork_server_methods[1].fnPtr)(
env, clazz, uid, gid, gids, runtime_flags, _11, _12, rlimits, permitted_capabilities, effective_capabilities env, clazz, uid, gid, gids, runtime_flags, _11, _12, rlimits, permitted_capabilities, effective_capabilities
); );
ctx.nativeForkSystemServer_post(); ctx.nativeForkSystemServer_post();
@@ -277,3 +329,4 @@ std::array<JNINativeMethod, 17> zygote_methods = {{
}, },
}}; }};
};

View File

@@ -19,7 +19,7 @@
#endif #endif
// Extreme verbose logging // Extreme verbose logging
//#define ZLOGV(...) ZLOGD(__VA_ARGS__) // #define ZLOGV(...) ZLOGD(__VA_ARGS__)
#define ZLOGV(...) (void*)0 #define ZLOGV(...) (void*)0
void hook_entry(); void hook_entry();

View File

@@ -7,6 +7,9 @@ edition.workspace = true
crate-type = ["staticlib"] crate-type = ["staticlib"]
path = "lib.rs" path = "lib.rs"
[lints]
workspace = true
[build-dependencies] [build-dependencies]
cxx-gen = { workspace = true } cxx-gen = { workspace = true }

View File

@@ -1,10 +1,9 @@
use crate::ffi::{BootConfig, MagiskInit, backup_init, magisk_proxy_main}; use crate::ffi::{BootConfig, MagiskInit, backup_init, magisk_proxy_main};
use crate::logging::setup_klog; use crate::logging::setup_klog;
use crate::mount::{is_rootfs, occupy, unoccupy}; use crate::mount::is_rootfs;
use crate::twostage::hexpatch_init_for_second_stage; use crate::twostage::hexpatch_init_for_second_stage;
use base::libc::{basename, getpid, mount, umask}; use base::libc::{basename, getpid, mount, umask};
use base::nix::mount::MsFlags; use base::{LibcReturn, LoggedResult, ResultExt, cstr, info, raw_cstr};
use base::{LibcReturn, LoggedResult, ResultExt, Utf8CStr, cstr, info, nix, raw_cstr};
use std::ffi::{CStr, c_char}; use std::ffi::{CStr, c_char};
use std::ptr::null; use std::ptr::null;
@@ -32,7 +31,7 @@ impl MagiskInit {
fn first_stage(&self) { fn first_stage(&self) {
info!("First Stage Init"); info!("First Stage Init");
let rootfs_magisktmp = self.prepare_data(true); self.prepare_data();
if !cstr!("/sdcard").exists() && !cstr!("/first_stage_ramdisk/sdcard").exists() { if !cstr!("/sdcard").exists() && !cstr!("/first_stage_ramdisk/sdcard").exists() {
self.hijack_init_with_switch_root(); self.hijack_init_with_switch_root();
@@ -42,28 +41,11 @@ impl MagiskInit {
// Fallback to hexpatch if /sdcard exists // Fallback to hexpatch if /sdcard exists
hexpatch_init_for_second_stage(true); hexpatch_init_for_second_stage(true);
} }
if rootfs_magisktmp {
info!("Occupy /data");
occupy(cstr!("/data"));
}
} }
fn second_stage(&mut self) { fn second_stage(&mut self) {
info!("Second Stage Init"); info!("Second Stage Init");
if unoccupy(cstr!("/data")) {
nix::mount::mount(
None::<&Utf8CStr>,
cstr!("/data"),
None::<&Utf8CStr>,
MsFlags::MS_REMOUNT,
Some(cstr!("size=100%")),
)
.check_os_err("mount", Some("/data"), Some("tmpfs"))
.log_ok();
}
cstr!("/init").unmount().ok(); cstr!("/init").unmount().ok();
cstr!("/system/bin/init").unmount().ok(); // just in case cstr!("/system/bin/init").unmount().ok(); // just in case
cstr!("/data/init").remove().ok(); cstr!("/data/init").remove().ok();
@@ -89,7 +71,7 @@ impl MagiskInit {
fn legacy_system_as_root(&mut self) { fn legacy_system_as_root(&mut self) {
info!("Legacy SAR Init"); info!("Legacy SAR Init");
self.prepare_data(false); self.prepare_data();
let is_two_stage = self.mount_system_root(); let is_two_stage = self.mount_system_root();
if is_two_stage { if is_two_stage {
hexpatch_init_for_second_stage(false); hexpatch_init_for_second_stage(false);
@@ -100,7 +82,7 @@ impl MagiskInit {
fn rootfs(&mut self) { fn rootfs(&mut self) {
info!("RootFS Init"); info!("RootFS Init");
self.prepare_data(false); self.prepare_data();
self.restore_ramdisk_init(); self.restore_ramdisk_init();
self.patch_rw_root(); self.patch_rw_root();
} }

View File

@@ -1,6 +1,4 @@
use crate::ffi::MagiskInit; use crate::ffi::MagiskInit;
use base::WalkResult::{Continue, Skip};
use base::nix::mount::{MntFlags, mount, umount2};
use base::{ use base::{
Directory, FsPathBuilder, LibcReturn, LoggedResult, ResultExt, Utf8CStr, cstr, debug, libc, Directory, FsPathBuilder, LibcReturn, LoggedResult, ResultExt, Utf8CStr, cstr, debug, libc,
nix, parse_mount_info, raw_cstr, nix, parse_mount_info, raw_cstr,
@@ -64,54 +62,6 @@ pub(crate) fn is_device_mounted(dev: u64, target: Pin<&mut CxxString>) -> bool {
false false
} }
pub(crate) fn occupy(path: &Utf8CStr) {
Directory::open(path)
.map(|mut dir| {
dir.pre_order_walk(|entry| {
let mut path = cstr::buf::default();
entry.resolve_path(&mut path)?;
let path = path.as_utf8_cstr();
mount(
Some(path),
path,
None::<&Utf8CStr>,
MsFlags::MS_BIND | MsFlags::MS_RDONLY,
None::<&Utf8CStr>,
)
.check_os_err("occupy", Some(path), None)?;
Ok(Continue)
})
.log_ok();
})
.log_ok();
}
pub(crate) fn unoccupy(path: &Utf8CStr) -> bool {
let mut ok = false;
Directory::open(path)
.map(|mut dir| {
ok = dir
.pre_order_walk(|entry| {
let mut path = cstr::buf::default();
entry.resolve_path(&mut path)?;
let path = path.as_utf8_cstr();
umount2(path, MntFlags::MNT_DETACH).check_os_err(
"unoccupy",
Some(path),
None,
)?;
if entry.is_dir() {
Ok(Skip)
} else {
Ok(Continue)
}
})
.is_ok();
})
.log_ok();
ok
}
const RAMFS_MAGIC: u32 = 0x858458f6; const RAMFS_MAGIC: u32 = 0x858458f6;
pub(crate) fn is_rootfs() -> bool { pub(crate) fn is_rootfs() -> bool {
@@ -123,44 +73,22 @@ pub(crate) fn is_rootfs() -> bool {
} }
impl MagiskInit { impl MagiskInit {
pub(crate) fn prepare_data(&self, use_rootfs: bool) -> bool { pub(crate) fn prepare_data(&self) {
debug!("Setup data tmp"); debug!("Setup data tmp");
cstr!("/data").mkdir(0o755).log_ok(); cstr!("/data").mkdir(0o755).log_ok();
nix::mount::mount(
Some(cstr!("magisk")),
cstr!("/data"),
Some(cstr!("tmpfs")),
MsFlags::empty(),
Some(cstr!("mode=755")),
)
.check_os_err("mount", Some("/data"), Some("tmpfs"))
.log_ok();
let mut rootfs_magisktmp = false; cstr!("/init").copy_to(cstr!("/data/magiskinit")).ok();
cstr!("/.backup").copy_to(cstr!("/data/.backup")).ok();
if use_rootfs { cstr!("/overlay.d").copy_to(cstr!("/data/overlay.d")).ok();
cstr!("/magisk").mkdir(0o755).log_ok();
rootfs_magisktmp = cstr!("/magisk")
.bind_mount_to(cstr!("/data"), false)
.is_ok();
}
if rootfs_magisktmp {
cstr!("/init")
.rename_to(cstr!("/magisk/magiskinit"))
.log_ok();
cstr!("/.backup").copy_to(cstr!("/magisk/.backup")).ok();
cstr!("/overlay.d")
.rename_to(cstr!("/magisk/overlay.d"))
.ok();
} else {
nix::mount::mount(
Some(cstr!("magisk")),
cstr!("/data"),
Some(cstr!("tmpfs")),
MsFlags::empty(),
Some(cstr!("mode=755")),
)
.check_os_err("mount", Some("/data"), Some("tmpfs"))
.log_ok();
cstr!("/init").copy_to(cstr!("/data/magiskinit")).ok();
cstr!("/.backup").copy_to(cstr!("/data/.backup")).ok();
cstr!("/overlay.d").copy_to(cstr!("/data/overlay.d")).ok();
}
rootfs_magisktmp
} }
pub(crate) fn exec_init(&mut self) { pub(crate) fn exec_init(&mut self) {

View File

@@ -84,14 +84,20 @@ impl MagiskInit {
.log_ok(); .log_ok();
debug!("Symlink /storage/self/primary -> /system/system/bin/init"); debug!("Symlink /storage/self/primary -> /system/system/bin/init");
} }
cstr!("/init").rename_to(cstr!("/sdcard")).log_ok();
// Binding mounting from rootfs is not supported before Linux 3.12 // First try to mount magiskinit from rootfs to workaround Samsung RKP
cstr!("/sdcard") if cstr!("/sdcard")
.create(OFlag::O_RDONLY | OFlag::O_CLOEXEC, 0)
.log_ok();
cstr!("/data/magiskinit")
.bind_mount_to(cstr!("/sdcard"), false) .bind_mount_to(cstr!("/sdcard"), false)
.log_ok(); .is_ok()
debug!("Bind mount /data/magiskinit -> /sdcard"); {
debug!("Bind mount /sdcard -> /sdcard");
} else {
// Binding mounting from rootfs is not supported before Linux 3.12
cstr!("/data/magiskinit")
.bind_mount_to(cstr!("/sdcard"), false)
.log_ok();
debug!("Bind mount /data/magiskinit -> /sdcard");
}
} }
} }

View File

@@ -7,12 +7,15 @@ edition.workspace = true
crate-type = ["staticlib", "rlib"] crate-type = ["staticlib", "rlib"]
path = "lib.rs" path = "lib.rs"
[build-dependencies]
cxx-gen = { workspace = true }
[features] [features]
no-main = [] no-main = []
[lints]
workspace = true
[build-dependencies]
cxx-gen = { workspace = true }
[dependencies] [dependencies]
base = { workspace = true } base = { workspace = true }
cxx = { workspace = true } cxx = { workspace = true }

View File

@@ -110,7 +110,7 @@ impl SePolicy {
allow(["kernel"], ["rootfs", "tmpfs"], ["chr_file"], ["write"]); allow(["kernel"], ["rootfs", "tmpfs"], ["chr_file"], ["write"]);
// Allow magiskinit daemon to handle mock selinuxfs // Allow magiskinit daemon to handle mock selinuxfs
allow(["kernel"], ["rootfs", "tmpfs"], ["fifo_file"], ["open", "read", "write"]); allow(["kernel"], ["tmpfs"], ["fifo_file"], ["open", "read", "write"]);
// For relabelling files // For relabelling files
allow(["rootfs"], ["labeledfs", "tmpfs"], ["filesystem"], ["associate"]); allow(["rootfs"], ["labeledfs", "tmpfs"], ["filesystem"], ["associate"]);

View File

@@ -32,17 +32,17 @@ case $(uname -m) in
esac esac
cleanup() { cleanup() {
pkill -INT -P $$
wait
trap - EXIT
rm -f magisk_*.img rm -f magisk_*.img
"$avd" delete avd -n test "$avd" delete avd -n test
exit 1
} }
test_error() { test_error() {
trap - EXIT
print_error "! An error occurred" print_error "! An error occurred"
pkill -INT -P $$
wait
cleanup cleanup
exit 1
} }
wait_for_boot() { wait_for_boot() {
@@ -72,13 +72,14 @@ wait_emu() {
dump_vars() { dump_vars() {
local val local val
for name in $@; do for name in $@ emu_args; do
eval val=\$$name eval val=\$$name
echo $name=\"$val\"\; echo $name=\"$val\"\;
done done
} }
resolve_vars() { resolve_vars() {
set +x
local arg_list="$1" local arg_list="$1"
local ver=$2 local ver=$2
local type=$3 local type=$3
@@ -138,8 +139,14 @@ dl_emu() {
setup_emu() { setup_emu() {
local avd_pkg=$1 local avd_pkg=$1
local ver=$2
dl_emu $avd_pkg dl_emu $avd_pkg
echo no | "$avd" create avd -f -n test -k $avd_pkg echo no | "$avd" create avd -f -n test -k $avd_pkg
# avdmanager is outdated, it might not set the proper target
local ini=$ANDROID_AVD_HOME/test.ini
sed "s:^target\s*=.*:target=android-$ver:g" $ini > $ini.new
mv $ini.new $ini
} }
test_emu() { test_emu() {
@@ -169,16 +176,15 @@ test_emu() {
} }
test_main() { test_main() {
local avd_pkg ramdisk vars local ver avd_pkg ramdisk
vars=$(resolve_vars "emu_args avd_pkg ramdisk" $1 $2) eval $(resolve_vars "ver avd_pkg ramdisk" $1 $2)
eval $vars
# Specify an explicit port so that tests can run with other emulators running at the same time # Specify an explicit port so that tests can run with other emulators running at the same time
local emu_port=5682 local emu_port=5682
emu_args="$emu_args -port $emu_port" emu_args="$emu_args -port $emu_port"
export ANDROID_SERIAL="emulator-$emu_port" export ANDROID_SERIAL="emulator-$emu_port"
setup_emu "$avd_pkg" setup_emu "$avd_pkg" $ver
# Restart ADB daemon just in case # Restart ADB daemon just in case
adb kill-server adb kill-server
@@ -211,24 +217,21 @@ test_main() {
test_emu release test_emu release
fi fi
# Cleanup cleanup
rm -f magisk_*.img
"$avd" delete avd -n test
} }
run_main() { run_main() {
local avd_pkg vars local ver avd_pkg
vars=$(resolve_vars "emu_args avd_pkg" $1 $2) eval $(resolve_vars "ver avd_pkg" $1 $2)
eval $vars setup_emu "$avd_pkg" $ver
setup_emu "$avd_pkg"
print_title "* Launching $avd_pkg" print_title "* Launching $avd_pkg"
"$emu" @test $emu_args 2>/dev/null "$emu" @test $emu_args 2>/dev/null
cleanup
} }
dl_main() { dl_main() {
local avd_pkg vars local avd_pkg
vars=$(resolve_vars "avd_pkg" $1 $2) eval $(resolve_vars "avd_pkg" $1 $2)
eval $vars
print_title "* Downloading $avd_pkg" print_title "* Downloading $avd_pkg"
dl_emu "$avd_pkg" dl_emu "$avd_pkg"
} }

View File

@@ -36,25 +36,6 @@ disable_version_config() {
sed -i "s:^version=:# version=:g" $CONFIG sed -i "s:^version=:# version=:g" $CONFIG
} }
bump_canary_version() {
# Update version code
local code=$(grep_prop magisk.versionCode $GCONFIG)
code=$((code + 1))
local tag="canary-$code"
sed -i "s:versionCode=.*:versionCode=${code}:g" $GCONFIG
# Commit version code changes
git add -u .
git status
git commit -m "Release new canary build" -m "[skip ci]"
git tag $tag
# Update version name
local ver=$(git rev-parse --short=8 HEAD)
sed -i "s:version=.*:version=${ver}:g" $CONFIG
sed -i "1s:.*:## Magisk (${ver}) (${code}):" $NOTES
}
# $1 = ver # $1 = ver
set_version() { set_version() {
local ver=$1 local ver=$1
@@ -69,89 +50,45 @@ set_version() {
git add -u . git add -u .
git status git status
git commit -m "Release Magisk v$ver" -m "[skip ci]" git commit -m "Release Magisk v$ver" -m "[skip ci]"
git tag $tag
} }
build_apk() { # $1 = ver
build() {
[ -z $1 ] && exit 1
local ver=$1
git pull
set_version $ver
$BUILDCMD clean $BUILDCMD clean
$BUILDCMD all $BUILDCMD all
$BUILDCMD -r all $BUILDCMD -r all
} }
build_canary() {
bump_canary_version
build_apk
}
# $1 = ver
build_public() {
[ -z $1 ] && exit 1
local ver=$1
set_version $ver
build_apk
}
upload() { upload() {
# Verify pattern
[[ "$1" =~ canary|beta|stable ]]
local type=$1
gh auth status gh auth status
local latest_tag=$(git describe --abbrev=0 --tags)
local ver=$(grep_prop version $CONFIG)
local code=$(grep_prop magisk.versionCode $GCONFIG) local code=$(grep_prop magisk.versionCode $GCONFIG)
local out=$(grep_prop outdir $CONFIG) local ver=$(echo - | awk "{ print $code / 1000 }")
local tag title local tag="v$ver"
local title="Magisk v$ver"
local out=$(grep_prop outdir $CONFIG)
if [ -z $out ]; then if [ -z $out ]; then
out=out out=out
fi fi
git tag $tag
git push origin master git push origin master
git push --tags git push --tags
# Prepare release notes # Prepare release notes
tail -n +3 $NOTES > release.md tail -n +3 $NOTES > release.md
case $type in # Publish release
canary ) local release_apk="Magisk-v${ver}.apk"
tag="canary-$code" cp $out/app-release.apk $release_apk
title="Magisk ($ver) ($code)" gh release create --verify-tag $tag -p -t "$title" -F release.md $release_apk $out/app-debug.apk $NOTES
# Assert tag format rm -f $release_apk release.md
[ $latest_tag = $tag ]
# Publish release
gh release create --verify-tag $tag -p -t "$title" -F release.md $out/app-release.apk $out/app-debug.apk $NOTES
;;
beta|stable )
tag="v$ver"
title="Magisk v$ver"
# Assert tag format
[ $latest_tag = $tag ]
# Publish release
local release_apk="Magisk-v${ver}.apk"
cp $out/app-release.apk $release_apk
gh release create --verify-tag $tag -p -t "$title" -F release.md $release_apk $out/app-debug.apk $NOTES
rm -f $release_apk
;;
esac
# If publishing stable, make it not prerelease and explicitly latest
if [ $type = "stable" ]; then
gh release edit $tag --prerelease=false --latest
fi
rm -f release.md
}
revert() {
local latest_tag=$(git describe --abbrev=0 --tags)
git tag -d $latest_tag
git reset --hard HEAD~
} }
# Use GNU sed on macOS # Use GNU sed on macOS
@@ -160,14 +97,10 @@ if command -v gsed >/dev/null; then
export -f sed export -f sed
fi fi
git pull
trap disable_version_config EXIT trap disable_version_config EXIT
ensure_config ensure_config
case $1 in case $1 in
canary ) build_canary ;; build ) build $2 ;;
public ) build_public $2 ;; upload ) upload ;;
upload ) upload $2 ;;
revert ) revert ;;
* ) exit 1 ;; * ) exit 1 ;;
esac esac