You've already forked PlayIntegrityFork
mirror of
https://github.com/osm0sis/PlayIntegrityFork.git
synced 2025-09-06 06:37:06 +00:00
Compare commits
198 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe57017255 | ||
|
|
0d05112f38 | ||
|
|
d8ff58d9ca | ||
|
|
ea65aef097 | ||
|
|
ebdbccdad2 | ||
|
|
08e3fb4d29 | ||
|
|
a671c3b0d3 | ||
|
|
c13a7b1299 | ||
|
|
629de14990 | ||
|
|
bd4fdcf0c3 | ||
|
|
8c216e2d40 | ||
|
|
27fc24337b | ||
|
|
796f776534 | ||
|
|
c972637347 | ||
|
|
b10828f199 | ||
|
|
726aecb2be | ||
|
|
a790f2c0db | ||
|
|
165d370bc8 | ||
|
|
915b87e6be | ||
|
|
1255a49274 | ||
|
|
6cc82e3e7b | ||
|
|
87fde1f4ca | ||
|
|
d535747c91 | ||
|
|
98bb23460e | ||
|
|
027e5855a0 | ||
|
|
8d2d1ecbd2 | ||
|
|
3775b5448e | ||
|
|
00023fb111 | ||
|
|
481491758c | ||
|
|
b42773671a | ||
|
|
3d55dc59c1 | ||
|
|
33f4fa2ce0 | ||
|
|
2857e3d2f8 | ||
|
|
4b8d6497d0 | ||
|
|
f3a7f3d5e9 | ||
|
|
bee755d8f3 | ||
|
|
dd4db53ca2 | ||
|
|
c30b3ab8a8 | ||
|
|
9d0a67d119 | ||
|
|
667efdd6a6 | ||
|
|
6e7377b3de | ||
|
|
98ab70a0b6 | ||
|
|
418e7306e3 | ||
|
|
44420c89ab | ||
|
|
adcdd80d65 | ||
|
|
096c8941b9 | ||
|
|
a926c520f2 | ||
|
|
9ef7a7f04e | ||
|
|
e9c1ec1d5d | ||
|
|
a7c2f348ea | ||
|
|
a5d9980ea7 | ||
|
|
58db03d596 | ||
|
|
64e412c351 | ||
|
|
914750104f | ||
|
|
1db82e2ccb | ||
|
|
f20c2dff1e | ||
|
|
620581b4de | ||
|
|
ebfd0c292a | ||
|
|
3e58be1e24 | ||
|
|
662b9c59c7 | ||
|
|
0bc9a4543a | ||
|
|
e3e4bbb95f | ||
|
|
7c3d0bd8dd | ||
|
|
b26b043a02 | ||
|
|
5aabd4b6b5 | ||
|
|
6d173fb3a1 | ||
|
|
efa3e98a94 | ||
|
|
8756c3963a | ||
|
|
6109918511 | ||
|
|
0386e085b5 | ||
|
|
b24d538e82 | ||
|
|
dc1ac51008 | ||
|
|
e0d288ac10 | ||
|
|
36084324bc | ||
|
|
07effe41b7 | ||
|
|
7b058d9bef | ||
|
|
ae1322b907 | ||
|
|
ee8e56b401 | ||
|
|
96a1475ac7 | ||
|
|
2dd7a17ac2 | ||
|
|
c7b87b944f | ||
|
|
31adf6b3b9 | ||
|
|
2027b7ba74 | ||
|
|
bbfdc87724 | ||
|
|
4d116c6a42 | ||
|
|
afddeaf340 | ||
|
|
61b018ae21 | ||
|
|
c7c8b549df | ||
|
|
7f1bdf69c5 | ||
|
|
95e6cbe53b | ||
|
|
ee714d1e21 | ||
|
|
6aa58bbc5e | ||
|
|
63bcb3c3f6 | ||
|
|
25262ce07e | ||
|
|
b4821fb03e | ||
|
|
8e4e7d1cef | ||
|
|
58e04392bf | ||
|
|
2c13b924ed | ||
|
|
8215587745 | ||
|
|
8414785806 | ||
|
|
75cf629015 | ||
|
|
36ce25f154 | ||
|
|
af7063d055 | ||
|
|
ecbf6d1db8 | ||
|
|
287ce1ee9b | ||
|
|
77653d25cb | ||
|
|
009815692c | ||
|
|
c0618cf9d8 | ||
|
|
7d216c30f1 | ||
|
|
d620dcdecb | ||
|
|
d0acafd34a | ||
|
|
f99696e894 | ||
|
|
12b55630fb | ||
|
|
5703c9bca1 | ||
|
|
ffd510aee5 | ||
|
|
b30b43f2ae | ||
|
|
affe99b5ae | ||
|
|
e2bb20b077 | ||
|
|
79052ff455 | ||
|
|
4ab07b71f8 | ||
|
|
34966f58a0 | ||
|
|
0299c76061 | ||
|
|
163678c49a | ||
|
|
f61fd571e0 | ||
|
|
26979bd73e | ||
|
|
c19224b138 | ||
|
|
ccb696d334 | ||
|
|
8014d71934 | ||
|
|
52edfcfb7c | ||
|
|
938ae86c8e | ||
|
|
bdd846add6 | ||
|
|
1a7c5103d1 | ||
|
|
686af702dc | ||
|
|
18f8277f16 | ||
|
|
484ab2aa63 | ||
|
|
87eb713cbd | ||
|
|
fa418290b3 | ||
|
|
608e60adbb | ||
|
|
fae28159b9 | ||
|
|
71b40dcb09 | ||
|
|
1f0c921773 | ||
|
|
3b00a7249b | ||
|
|
48014b9450 | ||
|
|
3451dd72ec | ||
|
|
9cc8451c90 | ||
|
|
7997954f74 | ||
|
|
fc8e5108de | ||
|
|
3ec6a4b039 | ||
|
|
5b9d0dfdd3 | ||
|
|
278cbf1732 | ||
|
|
594ac7c8ac | ||
|
|
b8e68d059e | ||
|
|
198c2e5486 | ||
|
|
919526bcd7 | ||
|
|
0b34f95282 | ||
|
|
63e4c571bf | ||
|
|
bdcfd283f1 | ||
|
|
dbd60afe68 | ||
|
|
e2cb4e1523 | ||
|
|
5901844ce3 | ||
|
|
efb8ee930a | ||
|
|
d36b35b7e9 | ||
|
|
4c08eb1e42 | ||
|
|
2fa007afa3 | ||
|
|
05c2474963 | ||
|
|
e0931c829d | ||
|
|
b57f46eed0 | ||
|
|
73d89d7a50 | ||
|
|
475185c3ee | ||
|
|
802c0a7617 | ||
|
|
9a24cd6f23 | ||
|
|
22e3a14a6f | ||
|
|
24db525cf3 | ||
|
|
21a309dd12 | ||
|
|
05927c60e1 | ||
|
|
008c0d0ada | ||
|
|
93ac718229 | ||
|
|
5aea2e877f | ||
|
|
d106e2a964 | ||
|
|
a9630901b3 | ||
|
|
d7090f3f73 | ||
|
|
3088f4b179 | ||
|
|
9673971b70 | ||
|
|
f6a9f855ef | ||
|
|
edcb94e179 | ||
|
|
a3cbed9a4e | ||
|
|
e9b7289950 | ||
|
|
63fab9cca7 | ||
|
|
c3ff39c00d | ||
|
|
29a97b49e3 | ||
|
|
38f8861f30 | ||
|
|
63e4191745 | ||
|
|
6f8706a817 | ||
|
|
89348bb75c | ||
|
|
dc10cae2b6 | ||
|
|
18ecfd6e32 | ||
|
|
676c5047b9 | ||
|
|
cff8ca8b61 |
27
.github/workflows/android.yml
vendored
27
.github/workflows/android.yml
vendored
@@ -2,36 +2,39 @@ name: Android CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
branches: main
|
||||
paths-ignore: '**.md'
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
branches: main
|
||||
paths-ignore: '**.md'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "recursive"
|
||||
submodules: 'recursive'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
java-version: 17
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew assembleRelease
|
||||
|
||||
|
||||
- name: Upload CI module zip as artifact zip
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: PlayIntegrityFork-CI
|
||||
path: module/*
|
||||
name: PlayIntegrityFork-CI_#${{ github.run_number }}
|
||||
path: 'module/*'
|
||||
compression-level: 9
|
||||
|
||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -1,6 +1,6 @@
|
||||
[submodule "libcxx"]
|
||||
path = app/src/main/cpp/libcxx
|
||||
url = https://github.com/topjohnwu/libcxx.git
|
||||
[submodule "shadowhook"]
|
||||
path = app/src/main/cpp/shadowhook
|
||||
url = https://github.com/bytedance/android-inline-hook
|
||||
[submodule "Dobby"]
|
||||
path = app/src/main/cpp/Dobby
|
||||
url = https://github.com/jmpews/Dobby
|
||||
[submodule "json"]
|
||||
path = app/src/main/cpp/json
|
||||
url = https://github.com/nlohmann/json
|
||||
|
||||
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@@ -3,4 +3,4 @@
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
2
.idea/deploymentTargetDropDown.xml
generated
2
.idea/deploymentTargetDropDown.xml
generated
@@ -7,4 +7,4 @@
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
2
.idea/gradle.xml
generated
2
.idea/gradle.xml
generated
@@ -16,4 +16,4 @@
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
2
.idea/inspectionProfiles/Project_Default.xml
generated
2
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -7,4 +7,4 @@
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
</component>
|
||||
|
||||
2
.idea/inspectionProfiles/profiles_settings.xml
generated
2
.idea/inspectionProfiles/profiles_settings.xml
generated
@@ -3,4 +3,4 @@
|
||||
<option name="PROJECT_PROFILE" value="Default" />
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
</settings>
|
||||
</component>
|
||||
</component>
|
||||
|
||||
2
.idea/migrations.xml
generated
2
.idea/migrations.xml
generated
@@ -7,4 +7,4 @@
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -6,4 +6,4 @@
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
4
.idea/vcs.xml
generated
4
.idea/vcs.xml
generated
@@ -2,6 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/app/src/main/cpp/libcxx" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/app/src/main/cpp/Dobby" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -1,16 +1,22 @@
|
||||
# Custom Fork v4
|
||||
- Very verbose logging (for now)
|
||||
- Allow spoofing literally any field from android.os.Build or android.os.Build.VERSION
|
||||
- Add BUILD_ID and VNDK_VERSION support to keep cross-fork API compatibility
|
||||
- Add exceptions for FIRST_API_VERSION (actually DEVICE_INITIAL_SDK_INT) and BUILD_ID (actually ID) for backwards API compatibility
|
||||
- Add empty example.pif.json with reference link
|
||||
- Fix GMS crashes if a null/bad value was read from json
|
||||
## Custom Fork v11
|
||||
- Improve autopif busybox tests, default api_level 32, pass --advanced arg
|
||||
- Improve scripts for special configs with Tricky Store
|
||||
- Fix resetprop fallback Magisk check
|
||||
- Add Action button support to run autopif in root managers
|
||||
- Fix root check for Termux/KSU/APatch
|
||||
- Improve PixelPropsUtils workaround on more setups
|
||||
- Replace autopif with autopif2 to generate a random print from latest Pixel Beta
|
||||
|
||||
# Custom Fork v3
|
||||
- Combine system.prop (runs at post-fs-data) entries into service.sh so that they're only set if needed
|
||||
- Clean up GMS data pif.prop/pif.json files left over from previous releases to ensure they don't trigger detection
|
||||
- Use custom.pif.json for custom spoofing if it exists, self-contained in the module directory, and restore it after module updates
|
||||
- Move props that need to be changed earlier into post-fs-data.sh
|
||||
- Warn of possible conflict if MagiskHidePropsConfig (MHPC) is installed
|
||||
- Continue to use ShadowHook for now
|
||||
## Custom Fork v10
|
||||
- Work around Shamiko 1.1 bug causing DenyList app hangs
|
||||
- Keep killgms in Scripts-only mode for testing
|
||||
- Add missing global props, fix all tags/type props
|
||||
- Use newer resetprop, fall back to custom func
|
||||
- Add ROM sig spoof to Advanced, default off
|
||||
- Add granular spoof to Advanced for Tricky Store
|
||||
- Improve scripts to keep Advanced values
|
||||
- Improve autopif to catch Magisk Canary busybox regression
|
||||
- Fix bootloop on some Xiaomi devices
|
||||
- Work around PixelPropsUtils conflict
|
||||
|
||||
_[Previous changelogs](https://github.com/osm0sis/PlayIntegrityFork/releases)_
|
||||
|
||||
141
README.md
141
README.md
@@ -1,76 +1,159 @@
|
||||
# Play Integrity Fork
|
||||
*PIF forked to bring back the custom.pif.json restore feature and develop more methodically*
|
||||
*PIF forked to be more futureproof and develop more methodically*
|
||||
|
||||
[](https://github.com/osm0sis/PlayIntegrityFork/releases/latest)
|
||||
[](https://github.com/osm0sis/PlayIntegrityFork/releases)
|
||||
[](https://github.com/osm0sis/PlayIntegrityFork/releases/latest)
|
||||
[](https://github.com/osm0sis/PlayIntegrityFork/releases)
|
||||
|
||||
A Zygisk module which fixes "ctsProfileMatch" (SafetyNet) and "MEETS_DEVICE_INTEGRITY" (Play Integrity).
|
||||
|
||||
To use this module you must have one of the following:
|
||||
To use this module you must have one of the following (latest versions):
|
||||
|
||||
- Magisk with Zygisk enabled.
|
||||
- KernelSU with [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) module installed.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk) with Zygisk enabled (and Enforce DenyList enabled if NOT also using [Shamiko](https://github.com/LSPosed/LSPosed.github.io/releases) or [Zygisk Assistant](https://github.com/snake-4/Zygisk-Assistant), for best results)
|
||||
- [KernelSU](https://github.com/tiann/KernelSU) with [Zygisk Next](https://github.com/Dr-TSNG/ZygiskNext) module installed
|
||||
- [APatch](https://github.com/bmax121/APatch) with [Zygisk Next](https://github.com/Dr-TSNG/ZygiskNext) module installed
|
||||
|
||||
## About module
|
||||
|
||||
It injects a classes.dex file to modify a few fields in the android.os.Build class. Also, it creates a hook in the native code to modify system properties. These are spoofed only to Google Play Services' DroidGuard (SafetyNet/Play Integrity) service.
|
||||
It injects a classes.dex file to modify fields in the android.os.Build class. Also, it creates a hook in the native code to modify system properties. These are spoofed only to Google Play Services' DroidGuard (SafetyNet/Play Integrity) service.
|
||||
|
||||
The purpose of the module is to avoid hardware attestation.
|
||||
|
||||
## About 'custom.pif.json' file
|
||||
|
||||
You can create this file in the module directory to spoof custom values to the GMS unstable process. It will be used instead of any included pif.json.
|
||||
You can fill out the included template [example.pif.json](https://raw.githubusercontent.com/osm0sis/PlayIntegrityFork/main/module/example.pif.json) from the module directory (/data/adb/modules/playintegrityfix) then rename it to custom.pif.json to spoof custom values to the GMS unstable process. It will be used instead of any included pif.json (none included currently).
|
||||
|
||||
You can't use values from recent devices due them triggering hardware backed attestation.
|
||||
Note this is just a template with the current suggested defaults, but with this fork you can include as few or as many android.os.Build class fields and Android system properties as needed to pass DEVICE verdict now and in the future if the enforced checks by Play Integrity change.
|
||||
|
||||
As a general rule you can't use values from recent devices due to them only being allowed with full hardware backed attestation. A script to extract the latest Pixel Beta fingerprint is included with the module; see the autopif section below for usage and caveats, and see the Resources below for information and scripts to help find a working private fingerprint.
|
||||
|
||||
Older formatted custom.pif.json files from cross-forks and previous releases will be automatically migrated to the latest format. Simply ensure the filename is custom.pif.json and place it in the module directory before upgrading.
|
||||
|
||||
A migration may also be performed manually with `sh migrate.sh` and custom.pif.json in the same directory, or from a file explorer app that supports script execution.
|
||||
|
||||
<details>
|
||||
<summary>Resources</summary>
|
||||
<summary><strong>Resources</strong></summary>
|
||||
|
||||
- [How-To Guide - Info to help find build.prop files, then create and use a custom.pif.json](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/post-89189572)
|
||||
- [gen_pif_custom.sh - Script to generate a custom.pif.json from device dump build.prop files](https://xdaforums.com/t/tools-zips-scripts-osm0sis-odds-and-ends-multiple-devices-platforms.2239421/post-89173470)
|
||||
- [UI Workflow Guide - Build, edit and test custom.pif.json using PixelFlasher on PC](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/post-89189970)
|
||||
- FAQ:
|
||||
- [PIF FAQ](https://xdaforums.com/t/pif-faq.4653307/) - Frequently Asked Questions (READ FIRST!)
|
||||
|
||||
- Guides:
|
||||
- [How-To Guide](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/post-89189572) - Info to help find build.prop files, then manually create and use a custom.pif.json
|
||||
- [Complete Noobs' Guide](https://xdaforums.com/t/how-to-search-find-your-own-fingerprints-noob-friendly-a-comprehensive-guide-w-tips-discussion-for-complete-noobs-from-one.4645816/) - A more in-depth basic explainer of the How-To Guide above
|
||||
- [UI Workflow Guide](https://xdaforums.com/t/pixelflasher-a-gui-tool-for-flashing-updating-rooting-managing-pixel-phones.4415453/post-87412305) - Build/find, edit, and test custom.pif.json using PixelFlasher on PC
|
||||
- [Tasker PIF Testing Helper](https://xdaforums.com/t/pif-testing-helper-tasker-profile-for-testing-fingerprints.4644827/) - Test custom.pif.json using Tasker on device
|
||||
|
||||
- Scripts:
|
||||
- [gen_pif_custom.sh](https://xdaforums.com/t/tools-zips-scripts-osm0sis-odds-and-ends-multiple-devices-platforms.2239421/post-89173470) - Script to generate a custom.pif.json from device dump build.prop files
|
||||
- [autopif.sh](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/post-89233630) - Script to extract the latest working Xiaomi.eu public fingerprint (though frequently banned and may be banned for RCS use while otherwise passing) to test an initial setup
|
||||
- [pif-test-json-file.sh](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/post-89561228) - Script to automate generating and testing json files to attempt to find working fingerprints
|
||||
- [install-random-fp.sh](https://xdaforums.com/t/script-for-randomly-installing-custom-device-fingerprints.4647408/) - Script to randomly switch between multiple working fingerprints found by the user
|
||||
|
||||
</details>
|
||||
|
||||
## About 'custom.app_replace.list' file
|
||||
|
||||
You can customize the included default [example.app_replace.list](https://raw.githubusercontent.com/osm0sis/PlayIntegrityFork/main/module/example.app_replace.list) from the module directory (/data/adb/modules/playintegrityfix) then rename it to custom.app_replace.list to systemlessly replace any additional conflicting custom ROM spoof injection app paths to disable them.
|
||||
|
||||
## About 'autopif2.sh' and 'killgms.sh' script files
|
||||
|
||||
There's intentionally no pif.json in the module because the goal remains to be futureproof, and including something that may be banned and obsolete within days would be contrary to that goal. If you don't care to have your own private fingerprint to use or don't have time to look for one currently then simply run the generation script from a root manager app that supports the module Action button, a root prompt with `sh autopif2.sh` in the module directory (/data/adb/modules/playintegrityfix), or from a file explorer app that supports script execution.
|
||||
|
||||
The autopif2 script generates a random device fingerprint from the latest Pixel Beta, ideally only to test an initial setup, since they expire roughly every 6 weeks from the Pixel Beta release date (dates included in the generated fingerprint), and the public mass-used ones from other modules or ROMs may also get banned or may be banned for RCS use while otherwise passing Play Integrity and SafetyNet in that time.
|
||||
|
||||
The killgms script forces the Google Play Services DroidGuard process (com.google.android.gms.unstable) to end, making it restart with the next attestation attempt; useful for testing out different fingerprints without requiring a reboot in between.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Make sure Google Play Services (com.google.android.gms) is NOT on the Magisk DenyList if Enforce DenyList is enabled since this interferes with the module; the module does prevent this using scripts but it only happens once during each reboot.
|
||||
|
||||
### Failing BASIC verdict
|
||||
|
||||
If you are failing basicIntegrity (SafetyNet) or MEETS_BASIC_INTEGRITY (Play Integrity) something is wrong in your setup. Recommended steps in order to find the problem:
|
||||
|
||||
- Disable all modules except this one
|
||||
- Try a different (ideally known working) custom.pif.json
|
||||
|
||||
Some modules which modify system can trigger DroidGuard detection, never hook GMS processes.
|
||||
Note: Some modules which modify system (e.g. Xposed) can trigger DroidGuard detections, as can any which hook GMS processes (e.g. custom fonts).
|
||||
|
||||
### Failing DEVICE verdict (on KernelSU)
|
||||
### Failing DEVICE verdict (on KernelSU/APatch)
|
||||
|
||||
- Disable ZygiskNext
|
||||
- Disable Zygisk Next
|
||||
- Reboot
|
||||
- Enable ZygiskNext
|
||||
- Enable Zygisk Next
|
||||
- Reboot again
|
||||
|
||||
### Play Protect/Store Certification and Google Wallet Tap To Pay Setup Security Requirements
|
||||
### Failing DEVICE verdict (on custom kernel/ROM)
|
||||
|
||||
Follow these steps:
|
||||
- Check the kernel release string with command `adb shell uname -r` or `uname -r`
|
||||
- If it's on the [Known Banned Kernel List](https://xdaforums.com/t/module-play-integrity-fix-safetynet-fix.4607985/post-89308909) then inform your kernel developer/ROM maintainer to remove their branding for their next build
|
||||
- You may also try a different custom kernel, or go back to the default kernel for your ROM, if available/possible
|
||||
|
||||
- Flash the module in Magisk/KernelSU
|
||||
- Clear Google Wallet cache (if you have it)
|
||||
- Clear Google Play Store cache and data
|
||||
- Clear Google Play Services (com.google.android.gms) cache and data (Optionally skip clearing data and wait some time, ~24h, for it to resolve on its own)
|
||||
### Failing DEVICE verdict (on custom ROM)
|
||||
|
||||
- Check the ROM signing keys with command `adb shell unzip -l /system/etc/security/otacerts.zip` or `unzip -l /system/etc/security/otacerts.zip`
|
||||
- If the output shows the ROM is signed with the AOSP testkey then inform your ROM maintainer to start signing their builds with a private key for their next build and ideally also provide a ROM signature migration build to allow users to update to it without requiring a data wipe
|
||||
- There is an experimental advanced feature to attempt to work around this by spoofing the ROM signature in Package Manager, see the spoofing Advanced Settings section below
|
||||
- You may also try a different custom ROM, or go back to the stock ROM for your device, if available/possible
|
||||
|
||||
### Failing Play Protect/Store Certification and/or Google Wallet Tap To Pay Setup Security Requirements
|
||||
|
||||
- Reflash the module in your root manager app
|
||||
- Clear Google Wallet (com.google.android.apps.walletnfcrel) and/or Google Pay (com.google.android.apps.nbu.paisa.user) cache, if you have them installed
|
||||
- Clear Google Play Store (com.android.vending) and, if present, Google Play Protect Service (com.google.android.odad) cache and data
|
||||
- Clear Google Play Services (com.google.android.gms) cache and data, or, optionally skip clearing data and wait some time (~24h) for it to resolve on its own
|
||||
- Reboot
|
||||
|
||||
Note: Clearing Google Play Services app ***data*** will then require you to reset any WearOS devices paired to your device.
|
||||
|
||||
### Read module logs
|
||||
|
||||
You can read module logs using this command directly after boot:
|
||||
You can read module logs using one of these commands directly after boot:
|
||||
|
||||
```
|
||||
adb shell "logcat | grep 'PIF'"
|
||||
```
|
||||
`adb shell "logcat | grep 'PIF/'"` or `su -c "logcat | grep 'PIF/'"`
|
||||
|
||||
Add a "verboseLogs" entry with a value of "0", "1", "2", "3" or "100" to your custom.pif.json to enable higher logging levels; "100" will dump all Build fields, and all the system properties that DroidGuard is checking. Adding the entry can also be done using the migration script with the `sh migrate.sh --force --advanced` or `sh migrate.sh -f -a` command.
|
||||
|
||||
## Can this module pass MEETS_STRONG_INTEGRITY?
|
||||
|
||||
No.
|
||||
No...
|
||||
|
||||
## About Play Integrity, SafetyNet is deprecated
|
||||
## About spoofing Advanced Settings
|
||||
|
||||
You can read more
|
||||
here: [Play Integrity API Info - XDA Forums](https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/)
|
||||
The advanced spoofing options add granular control over what exactly gets spoofed, allowing one to disable the parts that may conflict with other kinds of spoofing modules, and provide an option to work around the testkey ROM ban for those needing that feature. See more in the Details area below.
|
||||
|
||||
<details>
|
||||
<summary><strong>Details</strong></summary>
|
||||
|
||||
- Adding the Advanced Settings entries is best done using the migration script with the `sh migrate.sh --force --advanced` or `sh migrate.sh -f -a` command. Other than for the "verboseLogs" entry (see above), they are all 0 (disabled) or 1 (enabled).
|
||||
|
||||
- The "spoofBuild" entry (default 1) controls spoofing the Build Fields from the fingerprint; the "spoofProps" entry (default 1) controls spoofing the System Properties from the fingerprint; the "spoofProvider" entry (default 1) controls spoofing the Keystore Provider, and the "spoofSignature" entry (default 0) controls spoofing the ROM Signature.
|
||||
|
||||
- For spoofing locked bootloader and attempting to pass Play Integrity STRONG verdict I only recommend using the latest official [Tricky Store](https://github.com/5ec1cff/TrickyStore) build.
|
||||
|
||||
- Note: Using Tricky Store to achieve STRONG integrity (with an unrevoked hardware keybox.xml) requires the Advanced Settings "spoofProvider" disabled and either "spoofProps" disabled or the "*.security_patch" entry commented out, but to achieve DEVICE integrity (with Tricky Store default AOSP software keybox.xml or a revoked keybox.xml) requires at least "spoofProps" enabled, and some fingerprints may also require "spoofProvider". More known working private fingerprints can achieve DEVICE/STRONG integrity on more devices using these Advanced Settings in conjunction with Tricky Store than was possible with Tricky Store alone since they require fingerprint props spoofing.
|
||||
|
||||
</details>
|
||||
|
||||
## About Scripts-only mode
|
||||
|
||||
An advanced feature intended for older Android <10 ROMs, mostly stock ROMs or those with stock-like values, (and some other rare special cases), since they generally only need a few prop changes to pass Play Integrity DEVICE verdict. Due to this the majority of the previous information does not apply to or contradicts that of Scripts-only mode, so to avoid confusion it's contained in the Details area below.
|
||||
|
||||
<details>
|
||||
<summary><strong>Details</strong></summary>
|
||||
|
||||
- Manually opt-in by creating a file named scripts-only-mode in the module directory, either from a root prompt with `mkdir -p /data/adb/modules/playintegrityfix; touch /data/adb/modules/playintegrityfix/scripts-only-mode` or from a file explorer app, and then re/flashing the module. Scripts-only mode will remain enabled until this file is removed and the module is reflashed again.
|
||||
|
||||
- During install all unused default mode files (including custom.pif.json) are removed from the module directory, effectively disabling the Zygisk components of PIF: attestation fallback and device spoofing. You'll see "Scripts-only mode" indicated in the module description in your root manager app.
|
||||
|
||||
- For best results, you should still most likely enable Magisk's Enforce DenyList option if NOT also using [Shamiko](https://github.com/LSPosed/LSPosed.github.io/releases) or [Zygisk Assistant](https://github.com/snake-4/Zygisk-Assistant). The module will automatically add the Google Play Services DroidGuard process (com.google.android.gms.unstable) to the Magisk DenyList, if missing, since for Scripts-only mode it's necessary on some configurations (generally Android 9).
|
||||
|
||||
</details>
|
||||
|
||||
## About Play Integrity (SafetyNet is deprecated)
|
||||
|
||||
[Play Integrity API](https://xdaforums.com/t/info-play-integrity-api-replacement-for-safetynet.4479337/) - FAQ/information about PI (Play Integrity) replacing SN (SafetyNet)
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
@@ -5,9 +5,20 @@ plugins {
|
||||
android {
|
||||
namespace = "es.chiteroman.playintegrityfix"
|
||||
compileSdk = 34
|
||||
ndkVersion = "26.1.10909125"
|
||||
ndkVersion = "27.1.12297006"
|
||||
buildToolsVersion = "34.0.0"
|
||||
|
||||
buildFeatures {
|
||||
prefab = true
|
||||
}
|
||||
|
||||
packaging {
|
||||
jniLibs {
|
||||
excludes += "**/liblog.so"
|
||||
excludes += "**/libdobby.so"
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "es.chiteroman.playintegrityfix"
|
||||
minSdk = 26
|
||||
@@ -16,10 +27,15 @@ android {
|
||||
versionName = "1.0"
|
||||
|
||||
externalNativeBuild {
|
||||
ndk {
|
||||
jobs = Runtime.getRuntime().availableProcessors()
|
||||
abiFilters += "armeabi-v7a"
|
||||
abiFilters += "arm64-v8a"
|
||||
cmake {
|
||||
arguments += "-DANDROID_STL=none"
|
||||
arguments += "-DCMAKE_BUILD_TYPE=Release"
|
||||
|
||||
cppFlags += "-std=c++20"
|
||||
cppFlags += "-fno-exceptions"
|
||||
cppFlags += "-fno-rtti"
|
||||
cppFlags += "-fvisibility=hidden"
|
||||
cppFlags += "-fvisibility-inlines-hidden"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,17 +54,23 @@ android {
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
ndkBuild {
|
||||
path = file("src/main/cpp/Android.mk")
|
||||
cmake {
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
version = "3.22.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.lsposed.libcxx:libcxx:27.0.12077973")
|
||||
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
|
||||
}
|
||||
|
||||
tasks.register("copyFiles") {
|
||||
doLast {
|
||||
val moduleFolder = project.rootDir.resolve("module")
|
||||
val dexFile = project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex")
|
||||
val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/out/lib")
|
||||
val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/stripReleaseDebugSymbols/out/lib")
|
||||
|
||||
dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true)
|
||||
|
||||
|
||||
1
app/proguard-rules.pro
vendored
1
app/proguard-rules.pro
vendored
@@ -1,3 +1,4 @@
|
||||
-keep class es.chiteroman.playintegrityfix.EntryPoint {public <methods>;}
|
||||
-keep class es.chiteroman.playintegrityfix.CustomProvider
|
||||
-keep class es.chiteroman.playintegrityfix.CustomKeyStoreSpi
|
||||
-keep class es.chiteroman.playintegrityfix.CustomPackageInfoCreator
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := zygisk
|
||||
LOCAL_SRC_FILES := main.cpp
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||
|
||||
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/*.c)
|
||||
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/common/*.c)
|
||||
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/third_party/xdl/*.c)
|
||||
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/common
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/include
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/third_party/bsd
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/third_party/lss
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/third_party/xdl
|
||||
|
||||
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
|
||||
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/arch/arm/*.c)
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/arch/arm
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
|
||||
LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/arch/arm64/*.c)
|
||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/shadowhook/src/main/cpp/arch/arm64
|
||||
endif
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libcxx
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(LOCAL_PATH)/libcxx/Android.mk
|
||||
@@ -1,3 +0,0 @@
|
||||
APP_STL := none
|
||||
APP_CFLAGS := -Oz -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden
|
||||
APP_CPPFLAGS := -std=c++20
|
||||
@@ -1,15 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
project(zygisk)
|
||||
project(playintegrityfix)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/libcxx/include)
|
||||
find_package(cxx REQUIRED CONFIG)
|
||||
|
||||
link_libraries(${CMAKE_SOURCE_DIR}/libcxx/${CMAKE_ANDROID_ARCH_ABI}.a)
|
||||
link_libraries(cxx::cxx)
|
||||
|
||||
add_library(${CMAKE_PROJECT_NAME} SHARED ${CMAKE_SOURCE_DIR}/main.cpp)
|
||||
|
||||
add_subdirectory(Dobby)
|
||||
|
||||
SET_OPTION(Plugin.Android.BionicLinkerUtil ON)
|
||||
|
||||
target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE c_std_23 cxx_std_23)
|
||||
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log dobby_static)
|
||||
|
||||
1
app/src/main/cpp/Dobby
Submodule
1
app/src/main/cpp/Dobby
Submodule
Submodule app/src/main/cpp/Dobby added at b0176de574
1
app/src/main/cpp/json
Submodule
1
app/src/main/cpp/json
Submodule
Submodule app/src/main/cpp/json added at 9cca280a4d
File diff suppressed because it is too large
Load Diff
Submodule app/src/main/cpp/libcxx deleted from 12c8f4e93f
@@ -3,8 +3,8 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zygisk.hpp"
|
||||
#include "shadowhook.h"
|
||||
#include "json.hpp"
|
||||
#include "json/single_include/nlohmann/json.hpp"
|
||||
#include "dobby.h"
|
||||
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__)
|
||||
|
||||
@@ -13,52 +13,49 @@
|
||||
#define JSON_FILE_PATH "/data/adb/modules/playintegrityfix/pif.json"
|
||||
#define CUSTOM_JSON_FILE_PATH "/data/adb/modules/playintegrityfix/custom.pif.json"
|
||||
|
||||
static std::string FIRST_API_LEVEL, SECURITY_PATCH, BUILD_ID, VNDK_VERSION;
|
||||
static int verboseLogs = 0;
|
||||
static int spoofBuild = 1;
|
||||
static int spoofProps = 1;
|
||||
static int spoofProvider = 1;
|
||||
static int spoofSignature = 0;
|
||||
|
||||
static std::map<std::string, std::string> jsonProps;
|
||||
|
||||
typedef void (*T_Callback)(void *, const char *, const char *, uint32_t);
|
||||
|
||||
static std::map<void *, T_Callback> callbacks;
|
||||
|
||||
static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) {
|
||||
if (cookie == nullptr || name == nullptr || value == nullptr || !callbacks.contains(cookie)) return;
|
||||
|
||||
if (cookie == nullptr || name == nullptr || value == nullptr ||
|
||||
!callbacks.contains(cookie))
|
||||
return;
|
||||
const char *oldValue = value;
|
||||
|
||||
std::string_view prop(name);
|
||||
std::string prop(name);
|
||||
|
||||
if (prop.ends_with("api_level")) {
|
||||
if (FIRST_API_LEVEL.empty()) {
|
||||
LOGD("FIRST_API_LEVEL is empty, ignoring it...");
|
||||
return;
|
||||
} else {
|
||||
value = FIRST_API_LEVEL.c_str();
|
||||
// Spoof specific property values
|
||||
if (prop == "init.svc.adbd") {
|
||||
value = "stopped";
|
||||
} else if (prop == "sys.usb.state") {
|
||||
value = "mtp";
|
||||
}
|
||||
|
||||
if (jsonProps.count(prop)) {
|
||||
// Exact property match
|
||||
value = jsonProps[prop].c_str();
|
||||
} else {
|
||||
// Leading * wildcard property match
|
||||
for (const auto &p: jsonProps) {
|
||||
if (p.first.starts_with("*") && prop.ends_with(p.first.substr(1))) {
|
||||
value = p.second.c_str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOGD("[%s] -> %s", name, value);
|
||||
} else if (prop.ends_with("security_patch")) {
|
||||
if (SECURITY_PATCH.empty()) {
|
||||
LOGD("SECURITY_PATCH is empty, ignoring it...");
|
||||
return;
|
||||
} else {
|
||||
value = SECURITY_PATCH.c_str();
|
||||
}
|
||||
LOGD("[%s] -> %s", name, value);
|
||||
} else if (prop == "ro.build.id") {
|
||||
if (BUILD_ID.empty()) {
|
||||
LOGD("BUILD_ID is empty, ignoring it...");
|
||||
return;
|
||||
} else {
|
||||
value = BUILD_ID.c_str();
|
||||
}
|
||||
LOGD("[%s] -> %s", name, value);
|
||||
} else if (prop.ends_with("vndk.version")) {
|
||||
if (VNDK_VERSION.empty()) {
|
||||
LOGD("VNDK_VERSION is empty, ignoring it...");
|
||||
return;
|
||||
} else {
|
||||
value = VNDK_VERSION.c_str();
|
||||
}
|
||||
LOGD("[%s] -> %s", name, value);
|
||||
}
|
||||
|
||||
if (oldValue == value) {
|
||||
if (verboseLogs > 99) LOGD("[%s]: %s (unchanged)", name, oldValue);
|
||||
} else {
|
||||
LOGD("[%s]: %s -> %s", name, oldValue, value);
|
||||
}
|
||||
|
||||
return callbacks[cookie](cookie, name, value, serial);
|
||||
@@ -75,18 +72,14 @@ static void my_system_property_read_callback(const prop_info *pi, T_Callback cal
|
||||
}
|
||||
|
||||
static void doHook() {
|
||||
shadowhook_init(SHADOWHOOK_MODE_UNIQUE, false);
|
||||
void *handle = shadowhook_hook_sym_name(
|
||||
"libc.so",
|
||||
"__system_property_read_callback",
|
||||
reinterpret_cast<void *>(my_system_property_read_callback),
|
||||
reinterpret_cast<void **>(&o_system_property_read_callback)
|
||||
);
|
||||
void *handle = DobbySymbolResolver(nullptr, "__system_property_read_callback");
|
||||
if (handle == nullptr) {
|
||||
LOGD("Couldn't find '__system_property_read_callback' handle. Report to @chiteroman");
|
||||
LOGD("Couldn't find '__system_property_read_callback' handle");
|
||||
return;
|
||||
}
|
||||
LOGD("Found '__system_property_read_callback' handle at %p", handle);
|
||||
DobbyHook(handle, reinterpret_cast<dobby_dummy_func_t>(my_system_property_read_callback),
|
||||
reinterpret_cast<dobby_dummy_func_t *>(&o_system_property_read_callback));
|
||||
}
|
||||
|
||||
class PlayIntegrityFix : public zygisk::ModuleBase {
|
||||
@@ -100,21 +93,30 @@ public:
|
||||
bool isGms = false, isGmsUnstable = false;
|
||||
|
||||
auto rawProcess = env->GetStringUTFChars(args->nice_name, nullptr);
|
||||
auto rawDir = env->GetStringUTFChars(args->app_data_dir, nullptr);
|
||||
|
||||
if (rawProcess) {
|
||||
std::string_view process(rawProcess);
|
||||
|
||||
isGms = process.starts_with("com.google.android.gms");
|
||||
isGmsUnstable = process.compare("com.google.android.gms.unstable") == 0;
|
||||
// Prevent crash on apps with no data dir
|
||||
if (rawDir == nullptr) {
|
||||
env->ReleaseStringUTFChars(args->nice_name, rawProcess);
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string_view process(rawProcess);
|
||||
std::string_view dir(rawDir);
|
||||
|
||||
isGms = dir.ends_with("/com.google.android.gms");
|
||||
isGmsUnstable = process == "com.google.android.gms.unstable";
|
||||
|
||||
env->ReleaseStringUTFChars(args->nice_name, rawProcess);
|
||||
env->ReleaseStringUTFChars(args->app_data_dir, rawDir);
|
||||
|
||||
if (!isGms) {
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
// We are in GMS now, force unmount
|
||||
api->setOption(zygisk::FORCE_DENYLIST_UNMOUNT);
|
||||
|
||||
if (!isGmsUnstable) {
|
||||
@@ -122,7 +124,9 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<char> jsonVector;
|
||||
long dexSize = 0, jsonSize = 0;
|
||||
|
||||
int fd = api->connectCompanion();
|
||||
|
||||
read(fd, &dexSize, sizeof(long));
|
||||
@@ -130,43 +134,41 @@ public:
|
||||
|
||||
if (dexSize < 1) {
|
||||
close(fd);
|
||||
LOGD("Couldn't read classes.dex");
|
||||
LOGD("Couldn't read dex file");
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsonSize < 1) {
|
||||
close(fd);
|
||||
LOGD("Couldn't read pif.json");
|
||||
LOGD("Couldn't read json file");
|
||||
api->setOption(zygisk::DLCLOSE_MODULE_LIBRARY);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD("Read from file descriptor for 'dex' -> %ld bytes", dexSize);
|
||||
LOGD("Read from file descriptor for 'json' -> %ld bytes", jsonSize);
|
||||
|
||||
dexVector.resize(dexSize);
|
||||
read(fd, dexVector.data(), dexSize);
|
||||
|
||||
std::vector<char> jsonVector(jsonSize);
|
||||
jsonVector.resize(jsonSize);
|
||||
read(fd, jsonVector.data(), jsonSize);
|
||||
|
||||
close(fd);
|
||||
|
||||
LOGD("Read from file descriptor file 'classes.dex' -> %ld bytes", dexSize);
|
||||
LOGD("Read from file descriptor file 'pif.json' -> %ld bytes", jsonSize);
|
||||
|
||||
std::string data(jsonVector.cbegin(), jsonVector.cend());
|
||||
json = nlohmann::json::parse(data, nullptr, false, true);
|
||||
std::string jsonString(jsonVector.cbegin(), jsonVector.cend());
|
||||
json = nlohmann::json::parse(jsonString, nullptr, false, true);
|
||||
|
||||
jsonVector.clear();
|
||||
data.clear();
|
||||
jsonString.clear();
|
||||
}
|
||||
|
||||
void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override {
|
||||
if (dexVector.empty() || json.empty()) return;
|
||||
|
||||
readJson();
|
||||
|
||||
doHook();
|
||||
|
||||
if (spoofProps > 0) doHook();
|
||||
inject();
|
||||
|
||||
dexVector.clear();
|
||||
@@ -186,110 +188,106 @@ private:
|
||||
void readJson() {
|
||||
LOGD("JSON contains %d keys!", static_cast<int>(json.size()));
|
||||
|
||||
// Direct property spoofing workarounds
|
||||
if (json.contains("ID")) {
|
||||
if (json["ID"].is_null()) {
|
||||
LOGD("Key ID is null!");
|
||||
} else if (json["ID"].is_string()) {
|
||||
BUILD_ID = json["ID"].get<std::string>();
|
||||
// Verbose logging level
|
||||
if (json.contains("verboseLogs")) {
|
||||
if (!json["verboseLogs"].is_null() && json["verboseLogs"].is_string() && json["verboseLogs"] != "") {
|
||||
verboseLogs = stoi(json["verboseLogs"].get<std::string>());
|
||||
if (verboseLogs > 0) LOGD("Verbose logging (level %d) enabled!", verboseLogs);
|
||||
} else {
|
||||
LOGD("Error parsing ID!");
|
||||
LOGD("Error parsing verboseLogs!");
|
||||
}
|
||||
} else {
|
||||
LOGD("Key ID doesn't exist in JSON file!");
|
||||
}
|
||||
if (json.contains("SECURITY_PATCH")) {
|
||||
if (json["SECURITY_PATCH"].is_null()) {
|
||||
LOGD("Key SECURITY_PATCH is null!");
|
||||
} else if (json["SECURITY_PATCH"].is_string()) {
|
||||
SECURITY_PATCH = json["SECURITY_PATCH"].get<std::string>();
|
||||
} else {
|
||||
LOGD("Error parsing SECURITY_PATCH!");
|
||||
}
|
||||
} else {
|
||||
LOGD("Key SECURITY_PATCH doesn't exist in JSON file!");
|
||||
}
|
||||
if (json.contains("DEVICE_INITIAL_SDK_INT")) {
|
||||
if (json["DEVICE_INITIAL_SDK_INT"].is_null()) {
|
||||
LOGD("Key DEVICE_INITIAL_SDK_INT is null!");
|
||||
} else if (json["DEVICE_INITIAL_SDK_INT"].is_string()) {
|
||||
FIRST_API_LEVEL = json["DEVICE_INITIAL_SDK_INT"].get<std::string>();
|
||||
} else {
|
||||
LOGD("Error parsing DEVICE_INITIAL_SDK_INT!");
|
||||
}
|
||||
} else {
|
||||
LOGD("Key DEVICE_INITIAL_SDK_INT doesn't exist in JSON file!");
|
||||
json.erase("verboseLogs");
|
||||
}
|
||||
|
||||
// Backwards compatibility for chiteroman's alternate API naming
|
||||
if (json.contains("BUILD_ID")) {
|
||||
if (json["BUILD_ID"].is_null()) {
|
||||
LOGD("Key BUILD_ID is null!");
|
||||
} else if (json["BUILD_ID"].is_string()) {
|
||||
BUILD_ID = json["BUILD_ID"].get<std::string>();
|
||||
// Advanced spoofing settings
|
||||
if (json.contains("spoofBuild")) {
|
||||
if (!json["spoofBuild"].is_null() && json["spoofBuild"].is_string() && json["spoofBuild"] != "") {
|
||||
spoofBuild = stoi(json["spoofBuild"].get<std::string>());
|
||||
if (verboseLogs > 0) LOGD("Spoofing Build Fields %s!", (spoofBuild > 0) ? "enabled" : "disabled");
|
||||
} else {
|
||||
LOGD("Error parsing BUILD_ID!");
|
||||
LOGD("Error parsing spoofBuild!");
|
||||
}
|
||||
} else {
|
||||
LOGD("Key BUILD_ID doesn't exist in JSON file!");
|
||||
json.erase("spoofBuild");
|
||||
}
|
||||
if (json.contains("FIRST_API_LEVEL")) {
|
||||
if (json["FIRST_API_LEVEL"].is_null()) {
|
||||
LOGD("Key FIRST_API_LEVEL is null!");
|
||||
} else if (json["FIRST_API_LEVEL"].is_string()) {
|
||||
FIRST_API_LEVEL = json["FIRST_API_LEVEL"].get<std::string>();
|
||||
if (json.contains("spoofProps")) {
|
||||
if (!json["spoofProps"].is_null() && json["spoofProps"].is_string() && json["spoofProps"] != "") {
|
||||
spoofProps = stoi(json["spoofProps"].get<std::string>());
|
||||
if (verboseLogs > 0) LOGD("Spoofing System Properties %s!", (spoofProps > 0) ? "enabled" : "disabled");
|
||||
} else {
|
||||
LOGD("Error parsing FIRST_API_LEVEL!");
|
||||
LOGD("Error parsing spoofProps!");
|
||||
}
|
||||
} else {
|
||||
LOGD("Key FIRST_API_LEVEL doesn't exist in JSON file!");
|
||||
json.erase("spoofProps");
|
||||
}
|
||||
if (json.contains("VNDK_VERSION")) {
|
||||
if (json["VNDK_VERSION"].is_null()) {
|
||||
LOGD("Key VNDK_VERSION is null!");
|
||||
} else if (json["VNDK_VERSION"].is_string()) {
|
||||
VNDK_VERSION = json["VNDK_VERSION"].get<std::string>();
|
||||
if (json.contains("spoofProvider")) {
|
||||
if (!json["spoofProvider"].is_null() && json["spoofProvider"].is_string() && json["spoofProvider"] != "") {
|
||||
spoofProvider = stoi(json["spoofProvider"].get<std::string>());
|
||||
if (verboseLogs > 0) LOGD("Spoofing Keystore Provider %s!", (spoofProvider > 0) ? "enabled" : "disabled");
|
||||
} else {
|
||||
LOGD("Error parsing VNDK_VERSION!");
|
||||
LOGD("Error parsing spoofProvider!");
|
||||
}
|
||||
json.erase("VNDK_VERSION"); // Doesn't exist in Build or Build.VERSION
|
||||
} else {
|
||||
LOGD("Key VNDK_VERSION doesn't exist in JSON file!");
|
||||
json.erase("spoofProvider");
|
||||
}
|
||||
if (json.contains("spoofSignature")) {
|
||||
if (!json["spoofSignature"].is_null() && json["spoofSignature"].is_string() && json["spoofSignature"] != "") {
|
||||
spoofSignature = stoi(json["spoofSignature"].get<std::string>());
|
||||
if (verboseLogs > 0) LOGD("Spoofing ROM Signature %s!", (spoofSignature > 0) ? "enabled" : "disabled");
|
||||
} else {
|
||||
LOGD("Error parsing spoofSignature!");
|
||||
}
|
||||
json.erase("spoofSignature");
|
||||
}
|
||||
|
||||
std::vector<std::string> eraseKeys;
|
||||
for (auto &jsonList: json.items()) {
|
||||
if (verboseLogs > 1) LOGD("Parsing %s", jsonList.key().c_str());
|
||||
if (jsonList.key().find_first_of("*.") != std::string::npos) {
|
||||
// Name contains . or * (wildcard) so assume real property name
|
||||
if (!jsonList.value().is_null() && jsonList.value().is_string()) {
|
||||
if (jsonList.value() == "") {
|
||||
LOGD("%s is empty, skipping", jsonList.key().c_str());
|
||||
} else {
|
||||
if (verboseLogs > 0) LOGD("Adding '%s' to properties list", jsonList.key().c_str());
|
||||
jsonProps[jsonList.key()] = jsonList.value();
|
||||
}
|
||||
} else {
|
||||
LOGD("Error parsing %s!", jsonList.key().c_str());
|
||||
}
|
||||
eraseKeys.push_back(jsonList.key());
|
||||
}
|
||||
}
|
||||
// Remove properties from parsed JSON
|
||||
for (auto key: eraseKeys) {
|
||||
if (json.contains(key)) json.erase(key);
|
||||
}
|
||||
}
|
||||
|
||||
void inject() {
|
||||
LOGD("get system classloader");
|
||||
LOGD("JNI: Getting system classloader");
|
||||
auto clClass = env->FindClass("java/lang/ClassLoader");
|
||||
auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader",
|
||||
"()Ljava/lang/ClassLoader;");
|
||||
auto getSystemClassLoader = env->GetStaticMethodID(clClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
auto systemClassLoader = env->CallStaticObjectMethod(clClass, getSystemClassLoader);
|
||||
|
||||
LOGD("create class loader");
|
||||
LOGD("JNI: Creating module classloader");
|
||||
auto dexClClass = env->FindClass("dalvik/system/InMemoryDexClassLoader");
|
||||
auto dexClInit = env->GetMethodID(dexClClass, "<init>",
|
||||
"(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
auto buffer = env->NewDirectByteBuffer(dexVector.data(),
|
||||
static_cast<jlong>(dexVector.size()));
|
||||
auto dexClInit = env->GetMethodID(dexClClass, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||
auto buffer = env->NewDirectByteBuffer(dexVector.data(), static_cast<jlong>(dexVector.size()));
|
||||
auto dexCl = env->NewObject(dexClClass, dexClInit, buffer, systemClassLoader);
|
||||
|
||||
LOGD("load class");
|
||||
auto loadClass = env->GetMethodID(clClass, "loadClass",
|
||||
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
LOGD("JNI: Loading module class");
|
||||
auto loadClass = env->GetMethodID(clClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
auto entryClassName = env->NewStringUTF("es.chiteroman.playintegrityfix.EntryPoint");
|
||||
auto entryClassObj = env->CallObjectMethod(dexCl, loadClass, entryClassName);
|
||||
|
||||
auto entryClass = (jclass) entryClassObj;
|
||||
|
||||
LOGD("read json");
|
||||
auto readProps = env->GetStaticMethodID(entryClass, "readJson",
|
||||
"(Ljava/lang/String;)V");
|
||||
LOGD("JNI: Sending JSON");
|
||||
auto receiveJson = env->GetStaticMethodID(entryClass, "receiveJson", "(Ljava/lang/String;)V");
|
||||
auto javaStr = env->NewStringUTF(json.dump().c_str());
|
||||
env->CallStaticVoidMethod(entryClass, readProps, javaStr);
|
||||
env->CallStaticVoidMethod(entryClass, receiveJson, javaStr);
|
||||
|
||||
LOGD("call init");
|
||||
auto entryInit = env->GetStaticMethodID(entryClass, "init", "()V");
|
||||
env->CallStaticVoidMethod(entryClass, entryInit);
|
||||
LOGD("JNI: Calling init");
|
||||
auto entryInit = env->GetStaticMethodID(entryClass, "init", "(IIII)V");
|
||||
env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofBuild, spoofProvider, spoofSignature);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Submodule app/src/main/cpp/shadowhook deleted from b2537df077
@@ -0,0 +1,41 @@
|
||||
package es.chiteroman.playintegrityfix;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class CustomPackageInfoCreator implements Parcelable.Creator<PackageInfo> {
|
||||
private final Parcelable.Creator<PackageInfo> originalCreator;
|
||||
private final Signature spoofedSignature;
|
||||
|
||||
public CustomPackageInfoCreator(Parcelable.Creator<PackageInfo> originalCreator, Signature spoofedSignature) {
|
||||
this.originalCreator = originalCreator;
|
||||
this.spoofedSignature = spoofedSignature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo createFromParcel(Parcel source) {
|
||||
PackageInfo packageInfo = originalCreator.createFromParcel(source);
|
||||
if (packageInfo.packageName.equals("android")) {
|
||||
if (packageInfo.signatures != null && packageInfo.signatures.length > 0) {
|
||||
packageInfo.signatures[0] = spoofedSignature;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
if (packageInfo.signingInfo != null) {
|
||||
Signature[] signaturesArray = packageInfo.signingInfo.getApkContentsSigners();
|
||||
if (signaturesArray != null && signaturesArray.length > 0) {
|
||||
signaturesArray[0] = spoofedSignature;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return packageInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo[] newArray(int size) {
|
||||
return originalCreator.newArray(size);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@ public final class CustomProvider extends Provider {
|
||||
|
||||
@Override
|
||||
public synchronized Service getService(String type, String algorithm) {
|
||||
EntryPoint.spoofDevice();
|
||||
if (EntryPoint.getVerboseLogs() > 2) EntryPoint.LOG(String.format("Service: Caller type '%s' with algorithm '%s'", type, algorithm));
|
||||
if (EntryPoint.getSpoofBuildEnabled() > 0) {
|
||||
if (type.equals("KeyStore")) EntryPoint.spoofDevice();
|
||||
}
|
||||
return super.getService(type, algorithm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
package es.chiteroman.playintegrityfix;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Base64;
|
||||
import android.util.JsonReader;
|
||||
import android.util.Log;
|
||||
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.KeyStoreSpi;
|
||||
@@ -16,14 +25,57 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class EntryPoint {
|
||||
private static Integer verboseLogs = 0;
|
||||
private static Integer spoofBuildEnabled = 1;
|
||||
|
||||
private static final String signatureData = "MIIFyTCCA7GgAwIBAgIVALyxxl+zDS9SL68SzOr48309eAZyMA0GCSqGSIb3DQEBCwUAMHQxCzAJ\n" +
|
||||
"BgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQw\n" +
|
||||
"EgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAg\n" +
|
||||
"Fw0yMjExMDExODExMzVaGA8yMDUyMTEwMTE4MTEzNVowdDELMAkGA1UEBhMCVVMxEzARBgNVBAgT\n" +
|
||||
"CkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC0dvb2dsZSBJbmMu\n" +
|
||||
"MRAwDgYDVQQLEwdBbmRyb2lkMRAwDgYDVQQDEwdBbmRyb2lkMIICIjANBgkqhkiG9w0BAQEFAAOC\n" +
|
||||
"Ag8AMIICCgKCAgEAsqtalIy/nctKlrhd1UVoDffFGnDf9GLi0QQhsVoJkfF16vDDydZJOycG7/kQ\n" +
|
||||
"ziRZhFdcoMrIYZzzw0ppBjsSe1AiWMuKXwTBaEtxN99S1xsJiW4/QMI6N6kMunydWRMsbJ6aAxi1\n" +
|
||||
"lVq0bxSwr8Sg/8u9HGVivfdG8OpUM+qjuV5gey5xttNLK3BZDrAlco8RkJZryAD40flmJZrWXJmc\n" +
|
||||
"r2HhJJUnqG4Z3MSziEgW1u1JnnY3f/BFdgYsA54SgdUGdQP3aqzSjIpGK01/vjrXvifHazSANjvl\n" +
|
||||
"0AUE5i6AarMw2biEKB2ySUDp8idC5w12GpqDrhZ/QkW8yBSa87KbkMYXuRA2Gq1fYbQx3YJraw0U\n" +
|
||||
"gZ4M3fFKpt6raxxM5j0sWHlULD7dAZMERvNESVrKG3tQ7B39WAD8QLGYc45DFEGOhKv5Fv8510h5\n" +
|
||||
"sXK502IvGpI4FDwz2rbtAgJ0j+16db5wCSW5ThvNPhCheyciajc8dU1B5tJzZN/ksBpzne4Xf9gO\n" +
|
||||
"LZ9ZU0+3Z5gHVvTS/YpxBFwiFpmL7dvGxew0cXGSsG5UTBlgr7i0SX0WhY4Djjo8IfPwrvvA0QaC\n" +
|
||||
"FamdYXKqBsSHgEyXS9zgGIFPt2jWdhaS+sAa//5SXcWro0OdiKPuwEzLgj759ke1sHRnvO735dYn\n" +
|
||||
"5whVbzlGyLBh3L0CAwEAAaNQME4wDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUU1eXQ7NoYKjvOQlh\n" +
|
||||
"5V8jHQMoxA8wHwYDVR0jBBgwFoAUU1eXQ7NoYKjvOQlh5V8jHQMoxA8wDQYJKoZIhvcNAQELBQAD\n" +
|
||||
"ggIBAHFIazRLs3itnZKllPnboSd6sHbzeJURKehx8GJPvIC+xWlwWyFO5+GHmgc3yh/SVd3Xja/k\n" +
|
||||
"8Ud59WEYTjyJJWTw0Jygx37rHW7VGn2HDuy/x0D+els+S8HeLD1toPFMepjIXJn7nHLhtmzTPlDW\n" +
|
||||
"DrhiaYsls/k5Izf89xYnI4euuOY2+1gsweJqFGfbznqyqy8xLyzoZ6bvBJtgeY+G3i/9Be14HseS\n" +
|
||||
"Na4FvI1Oze/l2gUu1IXzN6DGWR/lxEyt+TncJfBGKbjafYrfSh3zsE4N3TU7BeOL5INirOMjre/j\n" +
|
||||
"VgB1YQG5qLVaPoz6mdn75AbBBm5a5ahApLiKqzy/hP+1rWgw8Ikb7vbUqov/bnY3IlIU6XcPJTCD\n" +
|
||||
"b9aRZQkStvYpQd82XTyxD/T0GgRLnUj5Uv6iZlikFx1KNj0YNS2T3gyvL++J9B0Y6gAkiG0EtNpl\n" +
|
||||
"z7Pomsv5pVdmHVdKMjqWw5/6zYzVmu5cXFtR384Ti1qwML1xkD6TC3VIv88rKIEjrkY2c+v1frh9\n" +
|
||||
"fRJ2OmzXmML9NgHTjEiJR2Ib2iNrMKxkuTIs9oxKZgrJtJKvdU9qJJKM5PnZuNuHhGs6A/9gt9Oc\n" +
|
||||
"cetYeQvVSqeEmQluWfcunQn9C9Vwi2BJIiVJh4IdWZf5/e2PlSSQ9CJjz2bKI17pzdxOmjQfE0JS\n" +
|
||||
"F7Xt\n";
|
||||
|
||||
private static final Map<String, String> map = new HashMap<>();
|
||||
|
||||
public static void init() {
|
||||
spoofProvider();
|
||||
spoofDevice();
|
||||
public static Integer getVerboseLogs() {
|
||||
return verboseLogs;
|
||||
}
|
||||
|
||||
public static void readJson(String data) {
|
||||
public static Integer getSpoofBuildEnabled() {
|
||||
return spoofBuildEnabled;
|
||||
}
|
||||
|
||||
public static void init(int logLevel, int spoofBuildVal, int spoofProviderVal, int spoofSignatureVal) {
|
||||
verboseLogs = logLevel;
|
||||
spoofBuildEnabled = spoofBuildVal;
|
||||
if (verboseLogs > 99) logFields();
|
||||
if (spoofProviderVal > 0) spoofProvider();
|
||||
if (spoofBuildVal > 0) spoofDevice();
|
||||
if (spoofSignatureVal > 0) spoofPackageManager();
|
||||
}
|
||||
|
||||
public static void receiveJson(String data) {
|
||||
try (JsonReader reader = new JsonReader(new StringReader(data))) {
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
@@ -66,15 +118,65 @@ public final class EntryPoint {
|
||||
|
||||
static void spoofDevice() {
|
||||
for (String key : map.keySet()) {
|
||||
// Backwards compatibility for chiteroman's alternate API naming
|
||||
if (key.equals("BUILD_ID")) {
|
||||
setField("ID", map.get("BUILD_ID"));
|
||||
} else if (key.equals("FIRST_API_LEVEL")) {
|
||||
setField("DEVICE_INITIAL_SDK_INT", map.get("FIRST_API_LEVEL"));
|
||||
} else {
|
||||
setField(key, map.get(key));
|
||||
setField(key, map.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
private static void spoofPackageManager() {
|
||||
Signature spoofedSignature = new Signature(Base64.decode(signatureData, Base64.DEFAULT));
|
||||
Parcelable.Creator<PackageInfo> originalCreator = PackageInfo.CREATOR;
|
||||
Parcelable.Creator<PackageInfo> customCreator = new CustomPackageInfoCreator(originalCreator, spoofedSignature);
|
||||
|
||||
try {
|
||||
Field creatorField = findField(PackageInfo.class, "CREATOR");
|
||||
creatorField.setAccessible(true);
|
||||
creatorField.set(null, customCreator);
|
||||
} catch (Exception e) {
|
||||
LOG("Couldn't replace PackageInfoCreator: " + e);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
HiddenApiBypass.addHiddenApiExemptions("Landroid/os/Parcel;", "Landroid/content/pm", "Landroid/app");
|
||||
}
|
||||
|
||||
try {
|
||||
Field cacheField = findField(PackageManager.class, "sPackageInfoCache");
|
||||
cacheField.setAccessible(true);
|
||||
Object cache = cacheField.get(null);
|
||||
Method clearMethod = cache.getClass().getMethod("clear");
|
||||
clearMethod.invoke(cache);
|
||||
} catch (Exception e) {
|
||||
LOG("Couldn't clear PackageInfoCache: " + e);
|
||||
}
|
||||
|
||||
try {
|
||||
Field creatorsField = findField(Parcel.class, "mCreators");
|
||||
creatorsField.setAccessible(true);
|
||||
Map<?, ?> mCreators = (Map<?, ?>) creatorsField.get(null);
|
||||
mCreators.clear();
|
||||
} catch (Exception e) {
|
||||
LOG("Couldn't clear Parcel mCreators: " + e);
|
||||
}
|
||||
|
||||
try {
|
||||
Field creatorsField = findField(Parcel.class, "sPairedCreators");
|
||||
creatorsField.setAccessible(true);
|
||||
Map<?, ?> sPairedCreators = (Map<?, ?>) creatorsField.get(null);
|
||||
sPairedCreators.clear();
|
||||
} catch (Exception e) {
|
||||
LOG("Couldn't clear Parcel sPairedCreators: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Field findField(Class<?> currentClass, String fieldName) throws NoSuchFieldException {
|
||||
while (currentClass != null && !currentClass.equals(Object.class)) {
|
||||
try {
|
||||
return currentClass.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException("Field '" + fieldName + "' not found in class hierarchy of " + currentClass.getName());
|
||||
}
|
||||
|
||||
private static boolean classContainsField(Class className, String fieldName) {
|
||||
@@ -86,7 +188,7 @@ public final class EntryPoint {
|
||||
|
||||
private static void setField(String name, String value) {
|
||||
if (value.isEmpty()) {
|
||||
LOG(String.format("%s is empty, skipping...", name));
|
||||
LOG(String.format("%s is empty, skipping", name));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,7 +202,7 @@ public final class EntryPoint {
|
||||
} else if (classContainsField(Build.VERSION.class, name)) {
|
||||
field = Build.VERSION.class.getDeclaredField(name);
|
||||
} else {
|
||||
LOG(String.format("Couldn't determine '%s' class name", name));
|
||||
if (verboseLogs > 1) LOG(String.format("Couldn't determine '%s' class name", name));
|
||||
return;
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
@@ -115,7 +217,7 @@ public final class EntryPoint {
|
||||
return;
|
||||
}
|
||||
if (value.equals(oldValue)) {
|
||||
LOG(String.format("[%s]: already '%s', skipping...", name, value));
|
||||
if (verboseLogs > 2) LOG(String.format("[%s]: %s (unchanged)", name, value));
|
||||
return;
|
||||
}
|
||||
Class<?> fieldType = field.getType();
|
||||
@@ -141,6 +243,27 @@ public final class EntryPoint {
|
||||
LOG(String.format("[%s]: %s -> %s", name, oldValue, value));
|
||||
}
|
||||
|
||||
private static String logParseField(Field field) {
|
||||
Object value = null;
|
||||
String type = field.getType().getName();
|
||||
String name = field.getName();
|
||||
try {
|
||||
value = field.get(null);
|
||||
} catch (IllegalAccessException|NullPointerException e) {
|
||||
return String.format("Couldn't access '%s' field value: " + e, name);
|
||||
}
|
||||
return String.format("<%s> %s: %s", type, name, String.valueOf(value));
|
||||
}
|
||||
|
||||
private static void logFields() {
|
||||
for (Field field : Build.class.getDeclaredFields()) {
|
||||
LOG("Build " + logParseField(field));
|
||||
}
|
||||
for (Field field : Build.VERSION.class.getDeclaredFields()) {
|
||||
LOG("Build.VERSION " + logParseField(field));
|
||||
}
|
||||
}
|
||||
|
||||
static void LOG(String msg) {
|
||||
Log.d("PIF/Java", msg);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.0" apply false
|
||||
id("com.android.application") version "8.7.0" apply false
|
||||
}
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
||||
#Tue Oct 31 00:21:30 CET 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
297
gradlew
vendored
297
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,69 +15,104 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
@@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
183
gradlew.bat
vendored
183
gradlew.bat
vendored
@@ -1,89 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
@@ -1 +1 @@
|
||||
#MAGISK
|
||||
#MAGISK
|
||||
|
||||
15
module/action.sh
Normal file
15
module/action.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
MODPATH="${0%/*}"
|
||||
|
||||
# ensure not running in busybox ash standalone shell
|
||||
set +o standalone
|
||||
unset ASH_STANDALONE
|
||||
|
||||
sh $MODPATH/autopif2.sh || exit 1
|
||||
|
||||
echo -e "\nDone!"
|
||||
|
||||
# warn since Magisk's implementation automatically closes if successful
|
||||
if [ "$KSU" != "true" -a "$APATCH" != "true" ]; then
|
||||
echo -e "\nClosing dialog in 20 seconds ..."
|
||||
sleep 20
|
||||
fi
|
||||
164
module/autopif2.sh
Normal file
164
module/autopif2.sh
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
if [ "$USER" != "root" -a "$(whoami 2>/dev/null)" != "root" ]; then
|
||||
echo "autopif2: need root permissions";
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
case "$1" in
|
||||
-h|--help|help) echo "sh autopif2.sh [-a]"; exit 0;;
|
||||
-a|--advanced|advanced) ARGS="-a"; shift;;
|
||||
esac;
|
||||
|
||||
echo "Pixel Beta pif.json generator script \
|
||||
\n by osm0sis @ xda-developers";
|
||||
|
||||
case "$0" in
|
||||
*.sh) DIR="$0";;
|
||||
*) DIR="$(lsof -p $$ 2>/dev/null | grep -o '/.*autopif2.sh$')";;
|
||||
esac;
|
||||
DIR=$(dirname "$(readlink -f "$DIR")");
|
||||
|
||||
item() { echo "\n- $@"; }
|
||||
die() { echo "\nError: $@, install busybox!"; exit 1; }
|
||||
|
||||
find_busybox() {
|
||||
[ -n "$BUSYBOX" ] && return 0;
|
||||
local path;
|
||||
for path in /data/adb/modules/busybox-ndk/system/*/busybox /data/adb/magisk/busybox /data/adb/ksu/bin/busybox /data/adb/ap/bin/busybox; do
|
||||
if [ -f "$path" ]; then
|
||||
BUSYBOX="$path";
|
||||
return 0;
|
||||
fi;
|
||||
done;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ! which wget >/dev/null || grep -q "wget-curl" $(which wget); then
|
||||
if ! find_busybox; then
|
||||
die "wget not found";
|
||||
elif $BUSYBOX ping -c1 -s2 android.com 2>&1 | grep -q "bad address"; then
|
||||
die "wget broken";
|
||||
else
|
||||
wget() { $BUSYBOX wget "$@"; }
|
||||
fi;
|
||||
fi;
|
||||
|
||||
if date -D '%s' -d "$(date '+%s')" 2>&1 | grep -q "bad date"; then
|
||||
if ! find_busybox; then
|
||||
die "date broken";
|
||||
else
|
||||
date() { $BUSYBOX date "$@"; }
|
||||
fi;
|
||||
fi;
|
||||
|
||||
if [ "$DIR" = /data/adb/modules/playintegrityfix ]; then
|
||||
DIR=$DIR/autopif2;
|
||||
mkdir -p $DIR;
|
||||
fi;
|
||||
cd "$DIR";
|
||||
|
||||
item "Crawling Android Developers for latest Pixel Beta ...";
|
||||
wget -q -O PIXEL_GSI_HTML --no-check-certificate https://developer.android.com/topic/generic-system-image/releases 2>&1 || exit 1;
|
||||
grep -m1 -o 'li>.*(Beta)' PIXEL_GSI_HTML | cut -d\> -f2;
|
||||
|
||||
BETA_REL_DATE="$(date -D '%B %e, %Y' -d "$(grep -m1 -o 'Date:.*' PIXEL_GSI_HTML | cut -d\ -f2-4)" '+%Y-%m-%d')";
|
||||
BETA_EXP_DATE="$(date -D '%s' -d "$(($(date -D '%Y-%m-%d' -d "$BETA_REL_DATE" '+%s') + 60 * 60 * 24 * 7 * 6))" '+%Y-%m-%d')";
|
||||
echo "Beta Released: $BETA_REL_DATE \
|
||||
\nEstimated Expiry: $BETA_EXP_DATE";
|
||||
|
||||
RELEASE="$(grep -m1 'corresponding Google Pixel builds' PIXEL_GSI_HTML | grep -o '/versions/.*' | cut -d\/ -f3)";
|
||||
ID="$(grep -m1 -o 'Build:.*' PIXEL_GSI_HTML | cut -d\ -f2)";
|
||||
INCREMENTAL="$(grep -m1 -o "$ID-.*-" PIXEL_GSI_HTML | cut -d- -f2)";
|
||||
|
||||
wget -q -O PIXEL_GET_HTML --no-check-certificate https://developer.android.com$(grep -m1 'corresponding Google Pixel builds' PIXEL_GSI_HTML | grep -o 'href.*' | cut -d\" -f2) 2>&1 || exit 1;
|
||||
wget -q -O PIXEL_BETA_HTML --no-check-certificate https://developer.android.com$(grep -m1 'Factory images for Google Pixel' PIXEL_GET_HTML | grep -o 'href.*' | cut -d\" -f2) 2>&1 || exit 1;
|
||||
|
||||
MODEL_LIST="$(grep -A1 'tr id=' PIXEL_BETA_HTML | grep 'td' | sed 's;.*<td>\(.*\)</td>;\1;')";
|
||||
PRODUCT_LIST="$(grep -o 'factory/.*_beta' PIXEL_BETA_HTML | cut -d\/ -f2)";
|
||||
|
||||
wget -q -O PIXEL_SECBULL_HTML --no-check-certificate https://source.android.com/docs/security/bulletin/pixel 2>&1 || exit 1;
|
||||
|
||||
SECURITY_PATCH="$(grep -A15 "$(grep -m1 -o 'Security patch level:.*' PIXEL_GSI_HTML | cut -d\ -f4-)" PIXEL_SECBULL_HTML | grep -m1 -B1 '</tr>' | grep 'td' | sed 's;.*<td>\(.*\)</td>;\1;')";
|
||||
|
||||
case "$1" in
|
||||
-m)
|
||||
DEVICE="$(getprop ro.product.device)";
|
||||
case "$PRODUCT_LIST" in
|
||||
*${DEVICE}_beta*)
|
||||
MODEL="$(getprop ro.product.model)";
|
||||
PRODUCT="${DEVICE}_beta";
|
||||
;;
|
||||
esac;
|
||||
;;
|
||||
esac;
|
||||
item "Selecting Pixel Beta device ...";
|
||||
if [ -z "$PRODUCT" ]; then
|
||||
set_random_beta() {
|
||||
local list_count="$(echo "$MODEL_LIST" | wc -l)";
|
||||
local list_rand="$((RANDOM % $list_count + 1))";
|
||||
local IFS=$'\n';
|
||||
set -- $MODEL_LIST;
|
||||
MODEL="$(eval echo \${$list_rand})";
|
||||
set -- $PRODUCT_LIST;
|
||||
PRODUCT="$(eval echo \${$list_rand})";
|
||||
DEVICE="$(echo "$PRODUCT" | sed 's/_beta//')";
|
||||
}
|
||||
set_random_beta;
|
||||
fi;
|
||||
echo "$MODEL ($PRODUCT)";
|
||||
|
||||
item "Dumping values to minimal pif.json ...";
|
||||
cat <<EOF | tee pif.json;
|
||||
{
|
||||
"MANUFACTURER": "Google",
|
||||
"MODEL": "$MODEL",
|
||||
"FINGERPRINT": "google/$PRODUCT/$DEVICE:$RELEASE/$ID/$INCREMENTAL:user/release-keys",
|
||||
"PRODUCT": "$PRODUCT",
|
||||
"DEVICE": "$DEVICE",
|
||||
"SECURITY_PATCH": "$SECURITY_PATCH",
|
||||
"DEVICE_INITIAL_SDK_INT": "32"
|
||||
}
|
||||
EOF
|
||||
|
||||
for MIGRATE in migrate.sh /data/adb/modules/playintegrityfix/migrate.sh; do
|
||||
[ -f "$MIGRATE" ] && break;
|
||||
done;
|
||||
if [ -f "$MIGRATE" ]; then
|
||||
OLDJSON=/data/adb/modules/playintegrityfix/custom.pif.json;
|
||||
[ -f "$OLDJSON" ] && grep -qE "verboseLogs|VERBOSE_LOGS" $OLDJSON && ARGS="-a";
|
||||
item "Converting pif.json to custom.pif.json with migrate.sh:";
|
||||
rm -f custom.pif.json;
|
||||
sh $MIGRATE -i $ARGS pif.json;
|
||||
if [ -n "$ARGS" ]; then
|
||||
grep_json() { grep -m1 "$1" $2 | cut -d\" -f4; }
|
||||
verboseLogs=$(grep_json "VERBOSE_LOGS" $OLDJSON);
|
||||
ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature verboseLogs";
|
||||
for SETTING in $ADVSETTINGS; do
|
||||
eval [ -z \"\$$SETTING\" ] \&\& $SETTING=$(grep_json "$SETTING" $OLDJSON);
|
||||
eval TMPVAL=\$$SETTING;
|
||||
[ -n "$TMPVAL" ] && sed -i "s;\($SETTING\": \"\).;\1$TMPVAL;" custom.pif.json;
|
||||
done;
|
||||
fi;
|
||||
grep -q '//"\*.security_patch"' $OLDJSON && sed -i 's;"\*.security_patch";//"\*.security_patch";' custom.pif.json;
|
||||
sed -i "s;};\n // Beta Released: $BETA_REL_DATE\n // Estimated Expiry: $BETA_EXP_DATE\n};" custom.pif.json;
|
||||
cat custom.pif.json;
|
||||
fi;
|
||||
|
||||
if [ "$DIR" = /data/adb/modules/playintegrityfix/autopif2 ]; then
|
||||
if [ -f /data/adb/modules/playintegrityfix/migrate.sh ]; then
|
||||
NEWNAME="custom.pif.json";
|
||||
else
|
||||
NEWNAME="pif.json";
|
||||
fi;
|
||||
if [ -f "../$NEWNAME" ]; then
|
||||
item "Renaming old file to $NEWNAME.bak ...";
|
||||
mv -fv ../$NEWNAME ../$NEWNAME.bak;
|
||||
fi;
|
||||
item "Installing new json ...";
|
||||
cp -fv $NEWNAME ..;
|
||||
if [ -f /data/adb/modules/playintegrityfix/killgms.sh ]; then
|
||||
item "Killing any running GMS DroidGuard process ...";
|
||||
sh /data/adb/modules/playintegrityfix/killgms.sh 2>&1 || true;
|
||||
fi;
|
||||
fi;
|
||||
56
module/common_func.sh
Normal file
56
module/common_func.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
RESETPROP="resetprop -n"
|
||||
[ -f /data/adb/magisk/util_functions.sh ] && [ "$(grep MAGISK_VER_CODE /data/adb/magisk/util_functions.sh | cut -d= -f2)" -lt 27003 ] && RESETPROP=resetprop_hexpatch
|
||||
|
||||
# resetprop_hexpatch [-f|--force] <prop name> <new value>
|
||||
resetprop_hexpatch() {
|
||||
case "$1" in
|
||||
-f|--force) local FORCE=1; shift;;
|
||||
esac
|
||||
|
||||
local NAME="$1"
|
||||
local NEWVALUE="$2"
|
||||
local CURVALUE="$(resetprop "$NAME")"
|
||||
|
||||
[ ! "$NEWVALUE" -o ! "$CURVALUE" ] && return 1
|
||||
[ "$NEWVALUE" = "$CURVALUE" -a ! "$FORCE" ] && return 2
|
||||
|
||||
local NEWLEN=${#NEWVALUE}
|
||||
if [ -f /dev/__properties__ ]; then
|
||||
local PROPFILE=/dev/__properties__
|
||||
else
|
||||
local PROPFILE="/dev/__properties__/$(resetprop -Z "$NAME")"
|
||||
fi
|
||||
[ ! -f "$PROPFILE" ] && return 3
|
||||
local NAMEOFFSET=$(echo $(strings -t d "$PROPFILE" | grep "$NAME") | cut -d ' ' -f 1)
|
||||
|
||||
#<hex 2-byte change counter><flags byte><hex length of prop value><prop value + nul padding to 92 bytes><prop name>
|
||||
local NEWHEX="$(printf '%02x' "$NEWLEN")$(printf "$NEWVALUE" | od -A n -t x1 -v | tr -d ' \n')$(printf "%$((92-NEWLEN))s" | sed 's/ /00/g')"
|
||||
|
||||
printf "Patch '$NAME' to '$NEWVALUE' in '$PROPFILE' @ 0x%08x -> \n[0000??$NEWHEX]\n" $((NAMEOFFSET-96))
|
||||
|
||||
echo -ne "\x00\x00" \
|
||||
| dd obs=1 count=2 seek=$((NAMEOFFSET-96)) conv=notrunc of="$PROPFILE"
|
||||
echo -ne "$(printf "$NEWHEX" | sed -e 's/.\{2\}/&\\x/g' -e 's/^/\\x/' -e 's/\\x$//')" \
|
||||
| dd obs=1 count=93 seek=$((NAMEOFFSET-93)) conv=notrunc of="$PROPFILE"
|
||||
}
|
||||
|
||||
# resetprop_if_diff <prop name> <expected value>
|
||||
resetprop_if_diff() {
|
||||
local NAME="$1"
|
||||
local EXPECTED="$2"
|
||||
local CURRENT="$(resetprop "$NAME")"
|
||||
|
||||
[ -z "$CURRENT" ] || [ "$CURRENT" = "$EXPECTED" ] || $RESETPROP "$NAME" "$EXPECTED"
|
||||
}
|
||||
|
||||
# resetprop_if_match <prop name> <value match string> <new value>
|
||||
resetprop_if_match() {
|
||||
local NAME="$1"
|
||||
local CONTAINS="$2"
|
||||
local VALUE="$3"
|
||||
|
||||
[[ "$(resetprop "$NAME")" = *"$CONTAINS"* ]] && $RESETPROP "$NAME" "$VALUE"
|
||||
}
|
||||
|
||||
# stub for boot-time
|
||||
ui_print() { return; }
|
||||
74
module/common_setup.sh
Normal file
74
module/common_setup.sh
Normal file
@@ -0,0 +1,74 @@
|
||||
# Remove any definitely conflicting modules that are installed
|
||||
if [ -d /data/adb/modules/safetynet-fix ]; then
|
||||
touch /data/adb/modules/safetynet-fix/remove
|
||||
ui_print "! Universal SafetyNet Fix (USNF) module will be removed on next reboot"
|
||||
fi
|
||||
|
||||
# Replace/hide conflicting custom ROM injection app folders/files to disable them
|
||||
LIST=$MODPATH/example.app_replace.list
|
||||
[ -f "$MODPATH/custom.app_replace.list" ] && LIST=$MODPATH/custom.app_replace.list
|
||||
for APP in $(grep -v '^#' $LIST); do
|
||||
if [ -e "$APP" ]; then
|
||||
case $APP in
|
||||
/system/*) ;;
|
||||
*) PREFIX=/system;;
|
||||
esac
|
||||
HIDEPATH=$MODPATH$PREFIX$APP
|
||||
if [ -d "$APP" ]; then
|
||||
mkdir -p $HIDEPATH
|
||||
if [ "$KSU" = "true" -o "$APATCH" = "true" ]; then
|
||||
setfattr -n trusted.overlay.opaque -v y $HIDEPATH
|
||||
else
|
||||
touch $HIDEPATH/.replace
|
||||
fi
|
||||
else
|
||||
mkdir -p $(dirname $HIDEPATH)
|
||||
if [ "$KSU" = "true" -o "$APATCH" = "true" ]; then
|
||||
mknod $HIDEPATH c 0 0
|
||||
else
|
||||
touch $HIDEPATH
|
||||
fi
|
||||
fi
|
||||
case $APP in
|
||||
*/overlay/*)
|
||||
CFG=$(echo $APP | grep -oE '.*/overlay')/config/config.xml
|
||||
if [ -f "$CFG" ]; then
|
||||
if [ -d "$APP" ]; then
|
||||
APK=$(readlink -f $APP/*.apk);
|
||||
elif [[ "$APP" = *".apk" ]]; then
|
||||
APK=$(readlink -f $APP);
|
||||
fi
|
||||
if [ -s "$APK" ]; then
|
||||
PKGNAME=$(unzip -p $APK AndroidManifest.xml | tr -d '\0' | grep -oE '[[:alnum:].-_]+\*http' | cut -d\* -f1)
|
||||
if [ "$PKGNAME" ] && grep -q "overlay package=\"$PKGNAME" $CFG; then
|
||||
HIDECFG=$MODPATH$PREFIX$CFG
|
||||
if [ ! -f "$HIDECFG" ]; then
|
||||
mkdir -p $(dirname $HIDECFG)
|
||||
cp -af $CFG $HIDECFG
|
||||
fi
|
||||
sed -i 's;<overlay \(package="'"$PKGNAME"'".*\) />;<!-- overlay \1 -->;' $HIDECFG
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if [[ -d "$APP" || "$APP" = *".apk" ]]; then
|
||||
ui_print "! $(basename $APP .apk) ROM app disabled, please uninstall any user app versions/updates after next reboot"
|
||||
[ "$HIDECFG" ] && ui_print "! + $PKGNAME entry commented out in copied overlay config"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Work around AOSPA PropImitationHooks conflict when their persist props don't exist
|
||||
if [ -n "$(resetprop ro.aospa.version)" ]; then
|
||||
for PROP in persist.sys.pihooks.first_api_level persist.sys.pihooks.security_patch; do
|
||||
resetprop | grep -q "\[$PROP\]" || resetprop -n -p "$PROP" ""
|
||||
done
|
||||
fi
|
||||
|
||||
# Work around supported custom ROM PixelPropsUtils conflict when spoofProvider is disabled
|
||||
if [ -n "$(resetprop persist.sys.pixelprops.pi)" ]; then
|
||||
resetprop -n -p persist.sys.pixelprops.pi false
|
||||
resetprop -n -p persist.sys.pixelprops.gapps false
|
||||
resetprop -n -p persist.sys.pixelprops.gms false
|
||||
fi
|
||||
@@ -1,23 +1,46 @@
|
||||
# Error on < Android 8
|
||||
if [ "$API" -lt 26 ]; then
|
||||
abort "! You can't use this module on Android < 8.0"
|
||||
# Allow a scripts-only mode for older Android (<10) which may not require the Zygisk components
|
||||
if [ -f /data/adb/modules/playintegrityfix/scripts-only-mode ]; then
|
||||
ui_print "! Installing global scripts only; Zygisk attestation fallback and device spoofing disabled"
|
||||
touch $MODPATH/scripts-only-mode
|
||||
sed -i 's/\(description=\)\(.*\)/\1[Scripts-only mode] \2/' $MODPATH/module.prop
|
||||
rm -rf $MODPATH/autopif.sh $MODPATH/classes.dex $MODPATH/common_setup.sh \
|
||||
$MODPATH/custom.pif.json $MODPATH/example.app_replace.list $MODPATH/example.pif.json \
|
||||
$MODPATH/migrate.sh $MODPATH/pif.json $MODPATH/zygisk \
|
||||
/data/adb/modules/playintegrityfix/custom.app_replace.list \
|
||||
/data/adb/modules/playintegrityfix/custom.pif.json /data/adb/modules/playintegrityfix/system
|
||||
fi
|
||||
|
||||
# Remove/warn if conflicting modules are installed
|
||||
if [ -d /data/adb/modules/safetynet-fix ]; then
|
||||
touch /data/adb/modules/safetynet-fix/remove
|
||||
ui_print "! Universal SafetyNet Fix (USNF) module will be removed on next reboot"
|
||||
# Copy any disabled app files to updated module
|
||||
if [ -d /data/adb/modules/playintegrityfix/system ]; then
|
||||
ui_print "- Restoring disabled ROM apps configuration"
|
||||
cp -arf /data/adb/modules/playintegrityfix/system $MODPATH
|
||||
fi
|
||||
|
||||
# Copy any supported custom files to updated module
|
||||
for FILE in custom.app_replace.list custom.pif.json; do
|
||||
if [ -f "/data/adb/modules/playintegrityfix/$FILE" ]; then
|
||||
ui_print "- Restoring $FILE"
|
||||
cp -af /data/adb/modules/playintegrityfix/$FILE $MODPATH/$FILE
|
||||
fi
|
||||
done
|
||||
|
||||
# Warn if potentially conflicting modules are installed
|
||||
if [ -d /data/adb/modules/MagiskHidePropsConf ]; then
|
||||
ui_print "! MagiskHidePropsConfig (MHPC) module may cause issues with PIF"
|
||||
fi
|
||||
|
||||
# Copy any custom.pif.json to updated module
|
||||
if [ -f /data/adb/modules/playintegrityfix/custom.pif.json ]; then
|
||||
ui_print "- Restoring custom.pif.json"
|
||||
cp -af /data/adb/modules/playintegrityfix/custom.pif.json $MODPATH/custom.pif.json
|
||||
# Run common tasks for installation and boot-time
|
||||
[ -d "$MODPATH/zygisk" ] && . $MODPATH/common_setup.sh
|
||||
|
||||
# Migrate custom.pif.json to latest defaults if needed
|
||||
if [ -f "$MODPATH/custom.pif.json" ] && ! grep -q "api_level" $MODPATH/custom.pif.json; then
|
||||
ui_print "- Running migration script on custom.pif.json:"
|
||||
ui_print " "
|
||||
chmod 755 $MODPATH/migrate.sh
|
||||
sh $MODPATH/migrate.sh install $MODPATH/custom.pif.json
|
||||
ui_print " "
|
||||
fi
|
||||
|
||||
# Clean up any leftover files from previous deprecated methods
|
||||
rm -f /data/data/com.google.android.gms/cache/pif.prop /data/data/com.google.android.gms/pif.prop
|
||||
rm -f /data/data/com.google.android.gms/cache/pif.json /data/data/com.google.android.gms/pif.json
|
||||
rm -f /data/data/com.google.android.gms/cache/pif.prop /data/data/com.google.android.gms/pif.prop \
|
||||
/data/data/com.google.android.gms/cache/pif.json /data/data/com.google.android.gms/pif.json
|
||||
|
||||
21
module/example.app_replace.list
Normal file
21
module/example.app_replace.list
Normal file
@@ -0,0 +1,21 @@
|
||||
# Rename to custom.app_replace.list once customized
|
||||
#
|
||||
# Add conflicting custom ROM injection app folder/file paths to this list
|
||||
# and they will be replaced/hidden systemlessly to disable them
|
||||
|
||||
# Xiaomi.eu
|
||||
/product/app/XiaomiEUInject
|
||||
/product/app/XiaomiEUInject-Stub
|
||||
|
||||
# EliteRoms
|
||||
/system/app/EliteDevelopmentModule
|
||||
/system/app/XInjectModule
|
||||
|
||||
# hentaiOS
|
||||
/system_ext/app/hentaiLewdbSVTDummy
|
||||
|
||||
# Evolution X
|
||||
/system_ext/app/PifPrebuilt
|
||||
|
||||
# PixelOS
|
||||
/system_ext/overlay/CertifiedPropsOverlay.apk
|
||||
@@ -1,20 +1,28 @@
|
||||
// Rename to custom.pif.json once completed
|
||||
//
|
||||
// See android.os.Build source for corresponding properties:
|
||||
// These are the current suggested defaults but you may add as many other fields/properties as needed
|
||||
//
|
||||
// See android.os.Build source for all fields and field values' corresponding properties:
|
||||
// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/Build.java
|
||||
|
||||
{
|
||||
"MANUFACTURER": "",
|
||||
"MODEL": "",
|
||||
"FINGERPRINT": "",
|
||||
"BRAND": "",
|
||||
"PRODUCT": "",
|
||||
"DEVICE": "",
|
||||
"RELEASE": "",
|
||||
"ID": "",
|
||||
"INCREMENTAL": "",
|
||||
"TYPE": "",
|
||||
"TAGS": "",
|
||||
"SECURITY_PATCH": "",
|
||||
"DEVICE_INITIAL_SDK_INT": ""
|
||||
// Build Fields
|
||||
"MANUFACTURER": "",
|
||||
"MODEL": "",
|
||||
"FINGERPRINT": "",
|
||||
"BRAND": "",
|
||||
"PRODUCT": "",
|
||||
"DEVICE": "",
|
||||
"RELEASE": "",
|
||||
"ID": "",
|
||||
"INCREMENTAL": "",
|
||||
"TYPE": "",
|
||||
"TAGS": "",
|
||||
"SECURITY_PATCH": "",
|
||||
"DEVICE_INITIAL_SDK_INT": "",
|
||||
|
||||
// System Properties
|
||||
"*.build.id": "", // for ro.build.id
|
||||
"*.security_patch": "", // for ro.build.version.security_patch
|
||||
"*api_level": "" // for ro.board.api_level, ro.board.first_api_level, ro.product.first_api_level and ro.vendor.api_level
|
||||
}
|
||||
|
||||
12
module/killgms.sh
Normal file
12
module/killgms.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/system/bin/sh
|
||||
# killgms.sh by osm0sis @ xda-developers
|
||||
#
|
||||
# Kill the Google Play services DroidGuard process
|
||||
# (com.google.android.gms.unstable)
|
||||
|
||||
if [ "$USER" != "root" -a "$(whoami 2>/dev/null)" != "root" ]; then
|
||||
echo "killgms: need root permissions";
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
killall -v com.google.android.gms.unstable;
|
||||
157
module/migrate.sh
Normal file
157
module/migrate.sh
Normal file
@@ -0,0 +1,157 @@
|
||||
#!/bin/sh
|
||||
|
||||
N="
|
||||
";
|
||||
|
||||
case "$1" in
|
||||
-h|--help|help) echo "sh migrate.sh [-f] [-o] [-a] [in-file] [out-file]"; exit 0;;
|
||||
-i|--install|install) INSTALL=1; shift;;
|
||||
*) echo "custom.pif.json migration script \
|
||||
$N by osm0sis @ xda-developers $N";;
|
||||
esac;
|
||||
|
||||
item() { echo "- $@"; }
|
||||
die() { [ "$INSTALL" ] || echo "$N$N! $@"; exit 1; }
|
||||
grep_get_json() {
|
||||
local target="$FILE";
|
||||
[ -n "$2" ] && target="$2";
|
||||
eval set -- "$(cat "$target" | tr -d '\r\n' | grep -m1 -o "$1"'".*' | cut -d: -f2-)";
|
||||
echo "$1" | sed -e 's|"|\\\\\\"|g' -e 's|[,}]*$||';
|
||||
}
|
||||
grep_check_json() {
|
||||
local target="$FILE";
|
||||
[ -n "$2" ] && target="$2";
|
||||
grep -q "$1" "$target" && [ "$(grep_get_json $1 "$target")" ];
|
||||
}
|
||||
|
||||
until [ -z "$1" -o -f "$1" ]; do
|
||||
case "$1" in
|
||||
-f|--force|force) FORCE=1; shift;;
|
||||
-o|--override|override) OVERRIDE=1; shift;;
|
||||
-a|--advanced|advanced) ADVANCED=1; shift;;
|
||||
*) die "Invalid argument/file not found: $1";;
|
||||
esac;
|
||||
done;
|
||||
|
||||
if [ -f "$1" ]; then
|
||||
FILE="$1";
|
||||
DIR="$1";
|
||||
else
|
||||
case "$0" in
|
||||
*.sh) DIR="$0";;
|
||||
*) DIR="$(lsof -p $$ 2>/dev/null | grep -o '/.*migrate.sh$')";;
|
||||
esac;
|
||||
fi;
|
||||
DIR=$(dirname "$(readlink -f "$DIR")");
|
||||
[ -z "$FILE" ] && FILE="$DIR/custom.pif.json";
|
||||
|
||||
OUT="$2";
|
||||
[ -z "$OUT" ] && OUT="$DIR/custom.pif.json";
|
||||
|
||||
[ -f "$FILE" ] || die "No json file found";
|
||||
|
||||
grep_check_json api_level && [ ! "$FORCE" ] && die "No migration required";
|
||||
|
||||
[ "$INSTALL" ] || item "Parsing fields ...";
|
||||
|
||||
FPFIELDS="BRAND PRODUCT DEVICE RELEASE ID INCREMENTAL TYPE TAGS";
|
||||
ALLFIELDS="MANUFACTURER MODEL FINGERPRINT $FPFIELDS SECURITY_PATCH DEVICE_INITIAL_SDK_INT";
|
||||
|
||||
for FIELD in $ALLFIELDS; do
|
||||
eval $FIELD=\"$(grep_get_json $FIELD)\";
|
||||
done;
|
||||
|
||||
if [ -n "$ID" ] && ! grep_check_json build.id; then
|
||||
item 'Simple entry ID found, changing to ID field and "*.build.id" property ...';
|
||||
fi;
|
||||
|
||||
if [ -z "$ID" ] && grep_check_json BUILD_ID; then
|
||||
item 'Deprecated entry BUILD_ID found, changing to ID field and "*.build.id" property ...';
|
||||
ID="$(grep_get_json BUILD_ID)";
|
||||
fi;
|
||||
|
||||
if [ -n "$SECURITY_PATCH" ] && ! grep_check_json security_patch; then
|
||||
item 'Simple entry SECURITY_PATCH found, changing to SECURITY_PATCH field and "*.security_patch" property ...';
|
||||
fi;
|
||||
|
||||
if grep_check_json VNDK_VERSION; then
|
||||
item 'Deprecated entry VNDK_VERSION found, changing to "*.vndk.version" property ...';
|
||||
VNDK_VERSION="$(grep_get_json VNDK_VERSION)";
|
||||
fi;
|
||||
|
||||
if [ -n "$DEVICE_INITIAL_SDK_INT" ] && ! grep_check_json api_level; then
|
||||
item 'Simple entry DEVICE_INITIAL_SDK_INT found, changing to DEVICE_INITIAL_SDK_INT field and "*api_level" property ...';
|
||||
fi;
|
||||
|
||||
if [ -z "$DEVICE_INITIAL_SDK_INT" ] && grep_check_json FIRST_API_LEVEL; then
|
||||
item 'Deprecated entry FIRST_API_LEVEL found, changing to DEVICE_INITIAL_SDK_INT field and "*api_level" property ...';
|
||||
DEVICE_INITIAL_SDK_INT="$(grep_get_json FIRST_API_LEVEL)";
|
||||
fi;
|
||||
|
||||
if [ -z "$RELEASE" -o -z "$INCREMENTAL" -o -z "$TYPE" -o -z "$TAGS" -o "$OVERRIDE" ]; then
|
||||
if [ "$OVERRIDE" ]; then
|
||||
item "Overriding values for fields derivable from FINGERPRINT ...";
|
||||
else
|
||||
item "Missing default fields found, deriving from FINGERPRINT ...";
|
||||
fi;
|
||||
IFS='/:' read F1 F2 F3 F4 F5 F6 F7 F8 <<EOF
|
||||
$(grep_get_json FINGERPRINT)
|
||||
EOF
|
||||
i=1;
|
||||
for FIELD in $FPFIELDS; do
|
||||
eval [ -z \"\$$FIELD\" -o \"$OVERRIDE\" ] \&\& $FIELD=\"\$F$i\";
|
||||
i=$((i+1));
|
||||
done;
|
||||
fi;
|
||||
|
||||
if [ -z "$SECURITY_PATCH" -o "$SECURITY_PATCH" = "null" ]; then
|
||||
item 'Missing required SECURITY_PATCH field and "*.security_patch" property value found, leaving empty ...';
|
||||
unset SECURITY_PATCH;
|
||||
fi;
|
||||
|
||||
if [ -z "$DEVICE_INITIAL_SDK_INT" -o "$DEVICE_INITIAL_SDK_INT" = "null" ]; then
|
||||
item 'Missing required DEVICE_INITIAL_SDK_INT field and "*api_level" property value found, setting to 25 ...';
|
||||
DEVICE_INITIAL_SDK_INT=25;
|
||||
fi;
|
||||
|
||||
ADVSETTINGS="spoofBuild spoofProps spoofProvider spoofSignature verboseLogs";
|
||||
|
||||
spoofBuild=1;
|
||||
spoofProps=1;
|
||||
spoofProvider=1;
|
||||
spoofSignature=0;
|
||||
verboseLogs=0;
|
||||
|
||||
if [ -f "$OUT" ]; then
|
||||
item "Renaming old file to $(basename "$OUT").bak ...";
|
||||
mv -f "$OUT" "$OUT.bak";
|
||||
if grep -qE "verboseLogs|VERBOSE_LOGS" "$OUT.bak"; then
|
||||
ADVANCED=1;
|
||||
grep_check_json VERBOSE_LOGS "$OUT.bak" && verboseLogs="$(grep_get_json VERBOSE_LOGS "$OUT.bak")";
|
||||
for SETTING in $ADVSETTINGS; do
|
||||
eval grep_check_json $SETTING \"$OUT.bak\" \&\& $SETTING=\"$(grep_get_json $SETTING "$OUT.bak")\";
|
||||
done;
|
||||
grep -q '//"\*.security_patch"' "$OUT.bak" && SECURITY_COMMENT='//';
|
||||
fi;
|
||||
fi;
|
||||
|
||||
[ "$INSTALL" ] || item "Writing fields and properties to updated custom.pif.json ...";
|
||||
|
||||
(echo "{";
|
||||
echo " // Build Fields";
|
||||
for FIELD in $ALLFIELDS; do
|
||||
eval echo '\ \ \ \ \"$FIELD\": \"'\$$FIELD'\",';
|
||||
done;
|
||||
echo "$N // System Properties";
|
||||
echo ' "*.build.id": "'$ID'",';
|
||||
echo " $SECURITY_COMMENT"'"*.security_patch": "'$SECURITY_PATCH'",';
|
||||
[ -z "$VNDK_VERSION" ] || echo ' "*.vndk.version": "'$VNDK_VERSION'",';
|
||||
echo ' "*api_level": "'$DEVICE_INITIAL_SDK_INT'",';
|
||||
if [ "$ADVANCED" ]; then
|
||||
echo "$N // Advanced Settings";
|
||||
for SETTING in $ADVSETTINGS; do
|
||||
eval echo '\ \ \ \ \"$SETTING\": \"'\$$SETTING'\",';
|
||||
done;
|
||||
fi) | sed '$s/,/\n}/' > "$OUT";
|
||||
|
||||
[ "$INSTALL" ] || cat "$OUT";
|
||||
@@ -1,7 +1,7 @@
|
||||
id=playintegrityfix
|
||||
name=Play Integrity Fork
|
||||
version=v4
|
||||
versionCode=4000
|
||||
version=v11
|
||||
versionCode=110000
|
||||
author=osm0sis & chiteroman @ xda-developers
|
||||
description=Fix CTS profile (SafetyNet) and DEVICE verdict (Play Integrity)
|
||||
description=Fix ctsProfile (SafetyNet) and DEVICE (Play Integrity) verdicts
|
||||
updateJson=https://raw.githubusercontent.com/osm0sis/PlayIntegrityFork/main/update.json
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
# Remove Play Services from Magisk Denylist when set to enforcing
|
||||
if magisk --denylist status; then
|
||||
magisk --denylist rm com.google.android.gms
|
||||
fi
|
||||
MODPATH="${0%/*}"
|
||||
. $MODPATH/common_func.sh
|
||||
|
||||
# Remove conflicting modules if installed
|
||||
if [ -d /data/adb/modules/safetynet-fix ]; then
|
||||
touch /data/adb/modules/safetynet-fix/remove
|
||||
if [ -d "$MODPATH/zygisk" ]; then
|
||||
# Remove Play Services from Magisk DenyList when set to Enforce in normal mode
|
||||
if magisk --denylist status; then
|
||||
magisk --denylist rm com.google.android.gms
|
||||
fi
|
||||
# Run common tasks for installation and boot-time
|
||||
. $MODPATH/common_setup.sh
|
||||
else
|
||||
# Add Play Services DroidGuard process to Magisk DenyList for better results in scripts-only mode
|
||||
magisk --denylist add com.google.android.gms com.google.android.gms.unstable
|
||||
fi
|
||||
|
||||
# Conditional early sensitive properties
|
||||
|
||||
resetprop_if_diff() {
|
||||
local NAME=$1
|
||||
local EXPECTED=$2
|
||||
local CURRENT=$(resetprop $NAME)
|
||||
|
||||
[ -z "$CURRENT" ] || [ "$CURRENT" == "$EXPECTED" ] || resetprop $NAME $EXPECTED
|
||||
}
|
||||
resetprop_if_match() {
|
||||
local NAME=$1
|
||||
local CONTAINS=$2
|
||||
local VALUE=$3
|
||||
|
||||
[[ "$(resetprop $NAME)" == *"$CONTAINS"* ]] && resetprop $NAME $VALUE
|
||||
}
|
||||
|
||||
# RootBeer, Microsoft
|
||||
resetprop_if_diff ro.build.tags release-keys
|
||||
|
||||
# Samsung
|
||||
resetprop_if_diff ro.boot.warranty_bit 0
|
||||
resetprop_if_diff ro.vendor.boot.warranty_bit 0
|
||||
resetprop_if_diff ro.vendor.warranty_bit 0
|
||||
resetprop_if_diff ro.warranty_bit 0
|
||||
|
||||
# Realme
|
||||
resetprop_if_diff ro.boot.realmebootstate green
|
||||
|
||||
# OnePlus
|
||||
resetprop_if_diff ro.is_ever_orange 0
|
||||
|
||||
# Microsoft
|
||||
for PROP in $(resetprop | grep -oE 'ro.*.build.tags'); do
|
||||
resetprop_if_diff $PROP release-keys
|
||||
done
|
||||
|
||||
# Other
|
||||
resetprop_if_diff ro.build.type user
|
||||
for PROP in $(resetprop | grep -oE 'ro.*.build.type'); do
|
||||
resetprop_if_diff $PROP user
|
||||
done
|
||||
resetprop_if_diff ro.adb.secure 1
|
||||
resetprop_if_diff ro.debuggable 0
|
||||
resetprop_if_diff ro.force.debuggable 0
|
||||
resetprop_if_diff ro.secure 1
|
||||
|
||||
@@ -1,53 +1,49 @@
|
||||
MODPATH="${0%/*}"
|
||||
. $MODPATH/common_func.sh
|
||||
|
||||
# Conditional sensitive properties
|
||||
|
||||
resetprop_if_diff() {
|
||||
local NAME=$1
|
||||
local EXPECTED=$2
|
||||
local CURRENT=$(resetprop $NAME)
|
||||
|
||||
[ -z "$CURRENT" ] || [ "$CURRENT" == "$EXPECTED" ] || resetprop $NAME $EXPECTED
|
||||
}
|
||||
resetprop_if_match() {
|
||||
local NAME=$1
|
||||
local CONTAINS=$2
|
||||
local VALUE=$3
|
||||
|
||||
[[ "$(resetprop $NAME)" == *"$CONTAINS"* ]] && resetprop $NAME $VALUE
|
||||
}
|
||||
|
||||
# Magisk recovery mode
|
||||
resetprop_if_match ro.bootmode recovery unknown
|
||||
# Magisk Recovery Mode
|
||||
resetprop_if_match ro.boot.mode recovery unknown
|
||||
resetprop_if_match ro.bootmode recovery unknown
|
||||
resetprop_if_match vendor.boot.mode recovery unknown
|
||||
|
||||
# SELinux
|
||||
resetprop_if_diff ro.boot.selinux enforcing
|
||||
# use delete since it can be 0 or 1 for enforcing depending on OEM
|
||||
if [ -n "$(resetprop ro.build.selinux)" ]; then
|
||||
resetprop --delete ro.build.selinux
|
||||
fi
|
||||
# use toybox to protect *stat* access time reading
|
||||
if [ "$(toybox cat /sys/fs/selinux/enforce)" == "0" ]; then
|
||||
# use toybox to protect stat access time reading
|
||||
if [ "$(toybox cat /sys/fs/selinux/enforce)" = "0" ]; then
|
||||
chmod 640 /sys/fs/selinux/enforce
|
||||
chmod 440 /sys/fs/selinux/policy
|
||||
fi
|
||||
|
||||
# SafetyNet/Play Integrity
|
||||
# Conditional late sensitive properties
|
||||
|
||||
# must be set after boot_completed for various OEMs
|
||||
{
|
||||
# late props which must be set after boot_completed for various OEMs
|
||||
until [ "$(getprop sys.boot_completed)" == "1" ]; do
|
||||
sleep 1
|
||||
done
|
||||
until [ "$(getprop sys.boot_completed)" = "1" ]; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Avoid breaking Realme fingerprint scanners
|
||||
resetprop_if_diff ro.boot.flash.locked 1
|
||||
# SafetyNet/Play Integrity + OEM
|
||||
# avoid bootloop on some Xiaomi devices
|
||||
resetprop_if_diff ro.secureboot.lockstate locked
|
||||
# avoid breaking Realme fingerprint scanners
|
||||
resetprop_if_diff ro.boot.flash.locked 1
|
||||
resetprop_if_diff ro.boot.realme.lockstate 1
|
||||
# avoid breaking Oppo fingerprint scanners
|
||||
resetprop_if_diff ro.boot.vbmeta.device_state locked
|
||||
# avoid breaking OnePlus display modes/fingerprint scanners
|
||||
resetprop_if_diff vendor.boot.verifiedbootstate green
|
||||
# avoid breaking OnePlus/Oppo fingerprint scanners on OOS/ColorOS 12+
|
||||
resetprop_if_diff ro.boot.verifiedbootstate green
|
||||
resetprop_if_diff ro.boot.veritymode enforcing
|
||||
resetprop_if_diff vendor.boot.vbmeta.device_state locked
|
||||
|
||||
# Avoid breaking Oppo fingerprint scanners
|
||||
resetprop_if_diff ro.boot.vbmeta.device_state locked
|
||||
# Other
|
||||
resetprop_if_diff sys.oem_unlock_allowed 0
|
||||
|
||||
# Avoid breaking OnePlus display modes/fingerprint scanners
|
||||
resetprop_if_diff vendor.boot.verifiedbootstate green
|
||||
|
||||
# Avoid breaking OnePlus/Oppo display fingerprint scanners on OOS/ColorOS 12+
|
||||
resetprop_if_diff ro.boot.verifiedbootstate green
|
||||
resetprop_if_diff ro.boot.veritymode enforcing
|
||||
resetprop_if_diff vendor.boot.vbmeta.device_state locked
|
||||
}&
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "v4",
|
||||
"versionCode": 4000,
|
||||
"zipUrl": "https://github.com/osm0sis/PlayIntegrityFork/releases/download/v4/PlayIntegrityFork-v4.zip",
|
||||
"version": "v11",
|
||||
"versionCode": 110000,
|
||||
"zipUrl": "https://github.com/osm0sis/PlayIntegrityFork/releases/download/v11/PlayIntegrityFork-v11.zip",
|
||||
"changelog": "https://raw.githubusercontent.com/osm0sis/PlayIntegrityFork/main/CHANGELOG.md"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user