108 Commits
v8 ... v11

Author SHA1 Message Date
osm0sis
fe57017255 Prepare v11 2024-10-06 19:05:38 -03:00
osm0sis
0d05112f38 Replace autopif with autopif2 2024-10-06 18:51:30 -03:00
xqyz
d8ff58d9ca Add additional PixelPropsUtils props to workaround 2024-10-06 18:48:35 -03:00
Chris Renshaw
ea65aef097 Only show dialog auto-close message on Magisk where it's needed 2024-10-03 22:40:22 -03:00
Chris Renshaw
ebdbccdad2 Fix fallback whoami root check logic 2024-10-03 17:13:51 -03:00
Chris Renshaw
08e3fb4d29 Update AGP 2024-10-01 22:45:58 -03:00
Chris Renshaw
a671c3b0d3 README: latest Advanced Settings best practices 2024-09-28 23:44:38 -03:00
Chris Renshaw
c13a7b1299 20 second Action dialog timeout seems about right 2024-09-27 07:19:05 -03:00
Chris Renshaw
629de14990 Increase action.sh dialog timeout 2024-09-26 23:06:38 -03:00
osm0sis
bd4fdcf0c3 Update Gradle 2024-09-25 21:09:45 -03:00
Chris Renshaw
8c216e2d40 Add action.sh support to run autopif.sh from root managers 2024-09-25 12:43:39 -03:00
Chris Renshaw
27fc24337b Copy pasta fixes 2024-09-23 22:37:36 -03:00
Chris Renshaw
796f776534 autopif.sh: pass advanced argument to migrate script 2024-09-22 20:08:42 -03:00
Chris Renshaw
c972637347 migrate.sh: clean up user argument parsing 2024-09-22 19:28:23 -03:00
Chris Renshaw
b10828f199 Update NDK 2024-09-18 23:01:36 -03:00
osm0sis
726aecb2be Add nlohmann/json as submodule 2024-09-18 21:35:00 -03:00
Chris Renshaw
a790f2c0db Update AGP 2024-09-18 12:48:09 -03:00
Chris Renshaw
165d370bc8 migrate.sh: retain commented security_patch if advanced 2024-09-12 20:07:35 -03:00
Chris Renshaw
915b87e6be autopif.sh: retain commented security_patch if present
- commenting out the security_patch line is needed if enabling spoofProps (e.g. for api_level) when used with Tricky Store
2024-09-12 20:00:58 -03:00
Chris Renshaw
1255a49274 Fix Magisk version check
- the variable only gets set during installations, not in the boot script env
2024-09-12 06:51:24 -03:00
osm0sis
6cc82e3e7b Update Gradle 2024-09-11 22:22:44 -03:00
Chris Renshaw
87fde1f4ca Update AGP 2024-09-11 21:36:18 -03:00
Chris Renshaw
d535747c91 autopif.sh: improve error catching, busybox finding, etc.
- simplify error display
- catch missing inject_fields.xml or FINGERPRINT, from e.g. bad download/extraction failure
- more robust busybox finding function
- accept a local migrate.sh and give it precedence over the module copy
2024-09-11 21:31:25 -03:00
Chris Renshaw
98bb23460e autopif.sh: default to api_level 32 now that the betas are served 2024-09-11 21:21:42 -03:00
Chris Renshaw
027e5855a0 Begin next development cycle 2024-09-11 18:25:54 -03:00
Chris Renshaw
8d2d1ecbd2 README: document spoofSignature in Troubleshooting 2024-09-08 22:17:19 -03:00
Chris Renshaw
3775b5448e README: more accurate note about spoofProps
- spoofProps enabled is sometimes necessary to pass STRONG depending on the underlying device/ROM and the fingerprint attempting to be spoofed
2024-08-30 13:47:06 -03:00
Chris Renshaw
00023fb111 README: recommend only latest official Tricky Store 2024-08-20 09:51:02 -03:00
osm0sis
481491758c Hotfix v10(103000) 2024-08-18 21:23:44 -03:00
Chris Renshaw
b42773671a Add missing ro.adb.secure prop early with the others 2024-08-18 19:24:15 -03:00
Chris Renshaw
3d55dc59c1 Work around PixelPropsUtils framework spoof conflict where supported 2024-08-18 19:03:51 -03:00
osm0sis
33f4fa2ce0 Update Gradle 2024-08-18 17:08:35 -03:00
Chris Renshaw
2857e3d2f8 autopif.sh: prefer and use my busybox directly if somehow not on PATH 2024-08-16 14:52:37 -03:00
Chris Renshaw
4b8d6497d0 Hotfix v10(102000) 2024-08-16 00:37:23 -03:00
Chris Renshaw
f3a7f3d5e9 Move Xiaomi fix even later to avoid bootloops on yet more devices 2024-08-16 00:20:49 -03:00
Chris Renshaw
bee755d8f3 Update Changelog 2024-08-14 01:51:13 -03:00
osm0sis
dd4db53ca2 Hotfix v10(101000) 2024-08-14 01:00:22 -03:00
osm0sis
c30b3ab8a8 Fix bootloop on some Xiaomi devices 2024-08-14 00:50:48 -03:00
osm0sis
9d0a67d119 Prepare v10 2024-08-12 23:35:33 -03:00
Chris Renshaw
667efdd6a6 Update AGP 2024-08-09 22:17:44 -03:00
Chris Renshaw
6e7377b3de autopif.sh: tidy using existing if 2024-08-09 12:15:02 -03:00
pershoot
98ab70a0b6 autopif.sh: Do not execute killgms.sh if not in default directory 2024-08-08 22:05:51 -03:00
osm0sis
418e7306e3 Fix advanced spoofing always displaying enabled in logs 2024-08-07 14:16:09 -03:00
osm0sis
44420c89ab autopif.sh: retain previous Advanced Settings values, fixes 2024-08-07 12:01:16 -03:00
osm0sis
adcdd80d65 migrate.sh: tidy up now that functions are multi-line 2024-08-07 11:48:06 -03:00
Chris Renshaw
096c8941b9 migrate.sh: retain previous Advanced Settings values 2024-08-05 22:52:14 -03:00
osm0sis
a926c520f2 Restore resetprop_hexpatch for older Magisk which still needs it 2024-08-04 00:06:54 -03:00
osm0sis
9ef7a7f04e Revert "Use resetprop -w to wait for boot_completed"
This reverts commit 0bc9a45.

Cater to the lowest common denominator (older Magisk) at least until after next Magisk stable
2024-08-03 23:49:42 -03:00
osm0sis
e9c1ec1d5d Clearer log messages for advanced spoof options 2024-08-03 23:49:14 -03:00
osm0sis
a7c2f348ea Add granular advanced spoofing options 2024-08-02 15:30:52 -03:00
4h9fbZ
a5d9980ea7 Fix Signature Data 2024-08-01 23:42:08 -03:00
4h9fbZ
58db03d596 Implement system signature spoofing 2024-08-01 23:42:08 -03:00
Chris Renshaw
64e412c351 Correct module description 2024-07-18 23:07:01 -03:00
Chris Renshaw
914750104f Update libcxx prefab 2024-07-17 23:36:00 -03:00
Chris Renshaw
1db82e2ccb Update NDK 2024-07-17 23:01:56 -03:00
osm0sis
f20c2dff1e Update Gradle 2024-07-12 07:59:05 -03:00
Chris Renshaw
620581b4de Update AGP 2024-07-12 06:20:11 -03:00
Chris Renshaw
ebfd0c292a Make example.pif.json reference more clear 2024-07-11 00:04:08 -03:00
osm0sis
3e58be1e24 Correct type prop heading, add another missing prop
- if sys.oem_unlock_allowed is reset to 0 before sys.boot_completed is 1 it results in a boot hang and then reboot or poweroff
- even though sys.oem_unlock_allowed has been reset to 0 the act of opening Developer Options will set it back to 1 until next reboot
2024-07-05 16:35:04 -03:00
Chris Renshaw
662b9c59c7 Ensure all prop changes bypass property_service
- resetprop only bypasses for ro.* properties by default
2024-07-05 14:15:19 -03:00
Chris Renshaw
0bc9a4543a Use resetprop -w to wait for boot_completed 2024-07-05 12:10:42 -03:00
Chris Renshaw
e3e4bbb95f Match file code style correctly 2024-07-04 15:41:41 -03:00
Chris Renshaw
7c3d0bd8dd Fix all tags and type props, add missing prop 2024-07-04 14:55:45 -03:00
Chris Renshaw
b26b043a02 Remove resetprop_hexpatch since counter is fixed in Magisk Canary 2024-07-04 14:43:50 -03:00
Chris Renshaw
5aabd4b6b5 Don't remove killgms.sh in Scripts-only mode
- it's still useful for testing purposes
2024-07-04 14:36:29 -03:00
Wang Han
6d173fb3a1 Use unique project name
This also works around a shamiko bug which will be
fixed later.
2024-07-04 14:29:20 -03:00
Chris Renshaw
efa3e98a94 Update AGP 2024-07-04 14:25:21 -03:00
Chris Renshaw
8756c3963a Begin next development cycle 2024-07-04 14:22:45 -03:00
Chris Renshaw
6109918511 README: more tweaks 2024-06-16 14:05:52 -03:00
Chris Renshaw
0386e085b5 README: next day fixes 2024-06-14 13:26:06 -03:00
Chris Renshaw
b24d538e82 README: final tidy 2024-06-13 22:30:21 -03:00
osm0sis
dc1ac51008 Prepare v9 2024-06-13 22:17:57 -03:00
Chris Renshaw
e0d288ac10 README: mention Zygisk Assistant as Shamiko alternative 2024-06-10 23:34:02 -03:00
Chris Renshaw
36084324bc Update AGP 2024-06-10 23:29:12 -03:00
osm0sis
07effe41b7 README: AOSP testkey ban, RCS print ban, PPS and testing automation 2024-06-10 22:32:21 -03:00
osm0sis
7b058d9bef Update Gradle 2024-06-01 20:53:22 -03:00
Chris Renshaw
ae1322b907 Work around AOSPA PropImitationHooks conflict 2024-06-01 01:50:52 -03:00
Chris Renshaw
ee8e56b401 migrate.sh: check only trailing quote
- fixes grep_check_json for prop entries with leading wildcard
2024-05-31 10:00:02 -03:00
Chris Renshaw
96a1475ac7 Update AGP 2024-05-20 23:05:47 -03:00
Chris Renshaw
2dd7a17ac2 autopif.sh: don't use half-baked wget-curl wrapper some ROMs include 2024-05-19 04:01:56 -03:00
Chris Renshaw
c7b87b944f autopif.sh: catch Termux issues properly 2024-05-15 23:36:56 -03:00
Chris Renshaw
31adf6b3b9 autopif.sh: install/output improvements
- back up old json if running as built-in to module
- keep Advanced Settings if present in old json
2024-05-11 09:10:13 -03:00
Chris Renshaw
2027b7ba74 migrate.sh: keep Advanced Settings if present in old json 2024-05-11 09:02:58 -03:00
osm0sis
bbfdc87724 Add new files to scripts-only-mode removals 2024-05-06 13:14:56 -03:00
Chris Renshaw
4d116c6a42 Add DroidGuard process killing script 2024-05-06 09:59:41 -03:00
Chris Renshaw
afddeaf340 Add Xiaomi.eu custom.pif.json extraction script 2024-05-06 09:58:44 -03:00
Chris Renshaw
61b018ae21 Update AGP 2024-04-30 23:35:31 -03:00
osm0sis
c7c8b549df Update Gradle 2024-04-25 00:38:06 -03:00
Chris Renshaw
7f1bdf69c5 Update NDK 2024-04-24 22:34:18 -03:00
Chris Renshaw
95e6cbe53b Update AGP 2024-04-10 13:44:43 -03:00
Chris Renshaw
ee714d1e21 Fix description sed with forgotten in-place flag 2024-04-09 08:44:14 -03:00
Chris Renshaw
6aa58bbc5e Indicate scripts-only mode in module description 2024-04-09 08:24:33 -03:00
Chris Renshaw
63bcb3c3f6 README: add examples of known incompatible modules 2024-04-08 12:55:44 -03:00
Chris Renshaw
25262ce07e migrate.sh: fix parsing/formatting regressions
- DEVICE_INITIAL_SDK_INT value would erroneously be used for DEVICE if DEVICE was missing
- a malformed json trailing , on the last input file entry would break the output format
2024-04-07 02:40:51 -03:00
Chris Renshaw
b4821fb03e migrate.sh: fix parsing for \" and , in json values 2024-04-07 01:45:25 -03:00
Chris Renshaw
8e4e7d1cef migrate.sh: add option to override values using derived fingerprint values 2024-04-06 01:37:55 -03:00
Chris Renshaw
58e04392bf migrate.sh: fix deriving from FINGERPRINT values with unexpected spaces 2024-04-05 16:14:14 -03:00
osm0sis
2c13b924ed Tweak/update comments 2024-03-30 15:19:30 -03:00
Chris Renshaw
8215587745 Make scripts-only mode opt-in only
- older custom ROMs may be able to pass only with the Zygisk components active, since the custom ROM doesn't have the correct stock ROM values
2024-03-28 11:33:08 -03:00
Chris Renshaw
8414785806 migrate.sh: allow specifying an output file 2024-03-27 02:27:15 -03:00
Chris Renshaw
75cf629015 Fix mistakenly forced scripts-only mode on clean installs
- use manual opt-in trigger of a scripts-only-mode file existing in the PIF module subdirectory
- clarify installer message since Magisk Zygisk actually functions on Android < 8 now, but the Zygisk attestation fallback and device spoofing aren't necessary to pass on these (and also some later) devices
2024-03-27 00:59:20 -03:00
osm0sis
36ce25f154 Bring back USNF's scripts-only mode for older Android, but better
- do scripts-only mode automatically on Android < 8.0 since it doesn't support Zygisk and generally doesn't need a PIF module to pass at all
- clean up all unused files in scripts-only mode, and modify script behaviors for it, including allowing GMS on DenyList, and even add it if missing since it's necessary on some scripts-only configurations
- allow manually entering scripts-only mode by deleting the zygisk directory of the currently installed module and reinstalling
- intentionally don't alter the Enforce DenyList state, since we don't know what other modules are installed that might depend on it one way or the other, so leave that up to the user
2024-03-25 15:00:49 -03:00
Chris Renshaw
af7063d055 migrate.sh: support compacted json and some CR/LF mangling 2024-03-23 00:26:20 -03:00
Chris Renshaw
ecbf6d1db8 Begin next development cycle 2024-03-22 19:11:02 -03:00
Chris Renshaw
287ce1ee9b Update AGP 2024-03-19 01:18:39 -03:00
Chris Renshaw
77653d25cb README: update for latest APatch supporting official Zygisk Next 2024-03-18 09:51:19 -03:00
Chris Renshaw
009815692c Update NDK 2024-03-12 01:56:54 -03:00
Chris Renshaw
c0618cf9d8 CI only on main again 2024-03-09 22:30:14 -04:00
30 changed files with 627 additions and 24883 deletions

View File

@@ -2,8 +2,10 @@ name: Android CI
on:
push:
branches: main
paths-ignore: '**.md'
pull_request:
branches: main
paths-ignore: '**.md'
jobs:

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[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

View File

@@ -1,17 +1,22 @@
## Custom Fork v8
- Rename VERBOSE_LOGS to verboseLogs to better differentiate Advanced Settings from Build Fields or System Properties
- Improve replace list to also allow file paths replacing/hiding systemlessly
- Improve migration script and add optional verboseLogs entry to output
- Improve replace list to automatically comment out any overlay APK config.xml entries systemlessly
- Update default/example app replace list for more ROM spoof injection methods
- Fix retaining disabled ROM apps through module updates in some scenarios
## 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 v7
- Fix non-/system ROM spoof injection app replacement
- Add missing XiaomiEUInject-Stub to the default/example replace list
- Improve code, scripts and logging
- Fix ROM spoof injection app replacement when using KernelSU and APatch
- Spoof init.svc.adbd to DroidGuard by default to further hide USB Debugging
- Improve hiding from detection by user apps
## 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)_

View File

@@ -10,13 +10,13 @@ A Zygisk module which fixes "ctsProfileMatch" (SafetyNet) and "MEETS_DEVICE_INTE
To use this module you must have one of the following (latest versions):
- [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), for best results)
- [KernelSU](https://github.com/tiann/KernelSU) with [ZygiskNext](https://github.com/Dr-TSNG/ZygiskNext) module installed
- [APatch](https://github.com/bmax121/APatch) with [ZygiskNext MOD](https://github.com/Yervant7/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.
@@ -26,7 +26,7 @@ You can fill out the included template [example.pif.json](https://raw.githubuser
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. See the Resources below for information and scripts to help find a working fingerprint.
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.
@@ -42,11 +42,12 @@ A migration may also be performed manually with `sh migrate.sh` and custom.pif.j
- [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 your device
- [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 fingerprint (though frequently banned) to test an initial setup
- [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>
@@ -55,6 +56,14 @@ A migration may also be performed manually with `sh migrate.sh` and custom.pif.j
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.
@@ -66,13 +75,14 @@ If you are failing basicIntegrity (SafetyNet) or MEETS_BASIC_INTEGRITY (Play Int
- Disable all modules except this one
- Try a different (ideally known working) custom.pif.json
Note: Some modules which modify system can trigger DroidGuard detections, as can any which 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/APatch)
- Disable ZygiskNext
- Disable Zygisk Next
- Reboot
- Enable ZygiskNext
- Enable Zygisk Next
- Reboot again
### Failing DEVICE verdict (on custom kernel/ROM)
@@ -80,13 +90,18 @@ Note: Some modules which modify system can trigger DroidGuard detections, as can
- 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
### Play Protect/Store Certification and Google Wallet Tap To Pay Setup Security Requirements
### Failing DEVICE verdict (on custom ROM)
Follow these steps:
- 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) cache and data
- 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
@@ -102,11 +117,43 @@ Add a "verboseLogs" entry with a value of "0", "1", "2", "3" or "100" to your cu
## Can this module pass MEETS_STRONG_INTEGRITY?
No.
No...
## About spoofing Advanced Settings
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)
[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

View File

@@ -5,7 +5,7 @@ plugins {
android {
namespace = "es.chiteroman.playintegrityfix"
compileSdk = 34
ndkVersion = "26.1.10909125"
ndkVersion = "27.1.12297006"
buildToolsVersion = "34.0.0"
buildFeatures {
@@ -62,7 +62,8 @@ android {
}
dependencies {
implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0")
implementation("org.lsposed.libcxx:libcxx:27.0.12077973")
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3")
}
tasks.register("copyFiles") {

View File

@@ -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

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.22.1)
project(zygisk)
project(playintegrityfix)
find_package(cxx REQUIRED CONFIG)

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

View File

@@ -3,7 +3,7 @@
#include <unistd.h>
#include "zygisk.hpp"
#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__)
@@ -14,6 +14,10 @@
#define CUSTOM_JSON_FILE_PATH "/data/adb/modules/playintegrityfix/custom.pif.json"
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;
@@ -164,7 +168,7 @@ public:
if (dexVector.empty() || json.empty()) return;
readJson();
doHook();
if (spoofProps > 0) doHook();
inject();
dexVector.clear();
@@ -184,7 +188,7 @@ private:
void readJson() {
LOGD("JSON contains %d keys!", static_cast<int>(json.size()));
// Verbose logging if verboseLogs with level number is present
// 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>());
@@ -195,6 +199,44 @@ private:
json.erase("verboseLogs");
}
// 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 spoofBuild!");
}
json.erase("spoofBuild");
}
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 spoofProps!");
}
json.erase("spoofProps");
}
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 spoofProvider!");
}
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());
@@ -244,8 +286,8 @@ private:
env->CallStaticVoidMethod(entryClass, receiveJson, javaStr);
LOGD("JNI: Calling init");
auto entryInit = env->GetStaticMethodID(entryClass, "init", "(I)V");
env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs);
auto entryInit = env->GetStaticMethodID(entryClass, "init", "(IIII)V");
env->CallStaticVoidMethod(entryClass, entryInit, verboseLogs, spoofBuild, spoofProvider, spoofSignature);
}
};

View File

@@ -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);
}
}

View File

@@ -13,7 +13,9 @@ public final class CustomProvider extends Provider {
@Override
public synchronized Service getService(String type, String algorithm) {
if (EntryPoint.getVerboseLogs() > 2) EntryPoint.LOG(String.format("Service: Caller type '%s' with algorithm '%s'", type, algorithm));
if (type.equals("KeyStore")) EntryPoint.spoofDevice();
if (EntryPoint.getSpoofBuildEnabled() > 0) {
if (type.equals("KeyStore")) EntryPoint.spoofDevice();
}
return super.getService(type, algorithm);
}
}

View File

@@ -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;
@@ -17,6 +26,35 @@ 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<>();
@@ -24,11 +62,17 @@ public final class EntryPoint {
return verboseLogs;
}
public static void init(int level) {
verboseLogs = level;
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();
spoofProvider();
spoofDevice();
if (spoofProviderVal > 0) spoofProvider();
if (spoofBuildVal > 0) spoofDevice();
if (spoofSignatureVal > 0) spoofPackageManager();
}
public static void receiveJson(String data) {
@@ -78,6 +122,63 @@ public final class EntryPoint {
}
}
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) {
for (Field field : className.getDeclaredFields()) {
if (field.getName().equals(fieldName)) return true;

View File

@@ -1,3 +1,3 @@
plugins {
id("com.android.application") version "8.3.0" apply false
id("com.android.application") version "8.7.0" apply false
}

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

7
gradlew vendored
View File

@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# 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/.
@@ -84,7 +86,8 @@ done
# 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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
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

2
gradlew.bat vendored
View File

@@ -13,6 +13,8 @@
@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 ##########################################################################

15
module/action.sh Normal file
View 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
View 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;

View File

@@ -1,3 +1,6 @@
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
@@ -37,7 +40,7 @@ resetprop_if_diff() {
local EXPECTED="$2"
local CURRENT="$(resetprop "$NAME")"
[ -z "$CURRENT" ] || [ "$CURRENT" = "$EXPECTED" ] || resetprop_hexpatch "$NAME" "$EXPECTED"
[ -z "$CURRENT" ] || [ "$CURRENT" = "$EXPECTED" ] || $RESETPROP "$NAME" "$EXPECTED"
}
# resetprop_if_match <prop name> <value match string> <new value>
@@ -46,7 +49,7 @@ resetprop_if_match() {
local CONTAINS="$2"
local VALUE="$3"
[[ "$(resetprop "$NAME")" = *"$CONTAINS"* ]] && resetprop_hexpatch "$NAME" "$VALUE"
[[ "$(resetprop "$NAME")" = *"$CONTAINS"* ]] && $RESETPROP "$NAME" "$VALUE"
}
# stub for boot-time

View File

@@ -58,3 +58,17 @@ for APP in $(grep -v '^#' $LIST); do
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

View File

@@ -1,10 +1,17 @@
# 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
# Copy any disabled app files to updated module
if [ -d "/data/adb/modules/playintegrityfix/system" ]; then
if [ -d /data/adb/modules/playintegrityfix/system ]; then
ui_print "- Restoring disabled ROM apps configuration"
cp -arf /data/adb/modules/playintegrityfix/system $MODPATH
fi
@@ -23,7 +30,7 @@ if [ -d /data/adb/modules/MagiskHidePropsConf ]; then
fi
# Run common tasks for installation and boot-time
. $MODPATH/common_setup.sh
[ -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

View File

@@ -1,6 +1,8 @@
// Rename to custom.pif.json once completed
//
// See android.os.Build source for field value 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
{
@@ -22,5 +24,5 @@
// System Properties
"*.build.id": "", // for ro.build.id
"*.security_patch": "", // for ro.build.version.security_patch
"*api_level": "" // for ro.board.first_api_level, ro.product.first_api_level and ro.vendor.api_level
"*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
View 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;

View File

@@ -1,13 +1,10 @@
#!/bin/sh
case "$1" in
-h|--help|help) echo "sh migrate.sh [-f] [-a] [file]"; exit 0;;
esac;
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";;
@@ -15,20 +12,30 @@ esac;
item() { echo "- $@"; }
die() { [ "$INSTALL" ] || echo "$N$N! $@"; exit 1; }
grep_get_json() { grep -m1 "$1" "$FILE" | cut -d\" -f4; }
grep_check_json() { grep -q "$1" "$FILE" && [ "$(grep_get_json "$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")" ];
}
case "$1" in
-f|--force|force) FORCE=1; shift;;
esac;
case "$1" in
-a|--advanced|advanced) ADVANCED=1; shift;;
esac;
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";
shift;
else
case "$0" in
*.sh) DIR="$0";;
@@ -38,6 +45,9 @@ 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";
@@ -48,7 +58,7 @@ 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\")\";
eval $FIELD=\"$(grep_get_json $FIELD)\";
done;
if [ -n "$ID" ] && ! grep_check_json build.id; then
@@ -78,14 +88,18 @@ if [ -z "$DEVICE_INITIAL_SDK_INT" ] && grep_check_json FIRST_API_LEVEL; then
DEVICE_INITIAL_SDK_INT="$(grep_get_json FIRST_API_LEVEL)";
fi;
if [ -z "$RELEASE" -o -z "$INCREMENTAL" -o -z "$TYPE" -o -z "$TAGS" ]; then
item "Missing default fields found, deriving from FINGERPRINT ...";
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" ] \&\& $FIELD=\"\$F$i\";
eval [ -z \"\$$FIELD\" -o \"$OVERRIDE\" ] \&\& $FIELD=\"\$F$i\";
i=$((i+1));
done;
fi;
@@ -100,9 +114,25 @@ if [ -z "$DEVICE_INITIAL_SDK_INT" -o "$DEVICE_INITIAL_SDK_INT" = "null" ]; then
DEVICE_INITIAL_SDK_INT=25;
fi;
if [ -f "$DIR/custom.pif.json" ]; then
item "Renaming old file to custom.pif.json.bak ...";
mv -f "$DIR/custom.pif.json" "$DIR/custom.pif.json.bak";
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 ...";
@@ -114,13 +144,14 @@ for FIELD in $ALLFIELDS; do
done;
echo "$N // System Properties";
echo ' "*.build.id": "'$ID'",';
echo ' "*.security_patch": "'$SECURITY_PATCH'",';
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";
echo ' "verboseLogs": "0",';
fi) | sed '$s/,/\n}/' > "$DIR/custom.pif.json";
[ "$INSTALL" ] || cat "$DIR/custom.pif.json";
for SETTING in $ADVSETTINGS; do
eval echo '\ \ \ \ \"$SETTING\": \"'\$$SETTING'\",';
done;
fi) | sed '$s/,/\n}/' > "$OUT";
[ "$INSTALL" ] || cat "$OUT";

View File

@@ -1,7 +1,7 @@
id=playintegrityfix
name=Play Integrity Fork
version=v8
versionCode=80000
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

View File

@@ -1,14 +1,18 @@
MODPATH="${0%/*}"
. $MODPATH/common_func.sh
# Remove Play Services from Magisk Denylist when set to enforcing
if magisk --denylist status; then
magisk --denylist rm com.google.android.gms
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
# Run common tasks for installation and boot-time
. $MODPATH/common_setup.sh
# Conditional early sensitive properties
# Samsung
@@ -17,19 +21,22 @@ resetprop_if_diff ro.vendor.boot.warranty_bit 0
resetprop_if_diff ro.vendor.warranty_bit 0
resetprop_if_diff ro.warranty_bit 0
# Xiaomi
resetprop_if_diff ro.secureboot.lockstate locked
# Realme
resetprop_if_diff ro.boot.realmebootstate green
# OnePlus
resetprop_if_diff ro.is_ever_orange 0
# Microsoft, RootBeer
resetprop_if_diff ro.build.tags release-keys
# 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

View File

@@ -3,9 +3,9 @@ MODPATH="${0%/*}"
# Conditional sensitive properties
# 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
@@ -14,7 +14,7 @@ resetprop_if_diff ro.boot.selinux enforcing
if [ -n "$(resetprop ro.build.selinux)" ]; then
resetprop --delete ro.build.selinux
fi
# use toybox to protect *stat* access time reading
# 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
@@ -22,25 +22,28 @@ fi
# Conditional late sensitive properties
# SafetyNet/Play Integrity
# must be set after boot_completed for various OEMs
{
# 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
resetprop_if_diff ro.boot.realme.lockstate 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 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
}&

View File

@@ -1,6 +1,6 @@
{
"version": "v8",
"versionCode": 80000,
"zipUrl": "https://github.com/osm0sis/PlayIntegrityFork/releases/download/v8/PlayIntegrityFork-v8.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"
}