rednaga / APKiD

Android Application Identifier for Packers, Protectors, Obfuscators and Oddities - PEiD for Android
Other
2.01k stars 293 forks source link

[DETECTION] Google Play protection? #329

Closed AndroidMaster24 closed 1 year ago

AndroidMaster24 commented 1 year ago

Provide the file BitLife - Life Simulator: https://apkcombo.com/bitlife/com.candywriter.bitlife/download/apk Ronin: The Last Samurai: https://apkcombo.com/ronin-the-last-samurai/com.dreamotion.ronin/download/apk 放肆武林: https://apkcombo.com/%E6%94%BE%E8%82%86%E6%AD%A6%E6%9E%97/com.wlgt.bd/download/apk Google Camera: https://apkcombo.com/google-camera/com.google.android.GoogleCamera/download/apk Epic Mecha Girls: Anime RPG: https://apkcombo.com/epic-mecha-girls-anime-rpg/com.vivastudios.mecha.robot.rpg.girls/download/apk Dead Ahead: Zombie Warfare: https://apkcombo.com/dead-ahead-zombie-warfare/com.mobirate.DeadAheadTactics/download/apk Fire Strike Online FPS: https://apkcombo.com/fire-strike-gun-shooter-fps/com.edkongames.aurora/download/apk

Describe the detection issue More and more apps comes with Pairip protection. There is almost no infomation about it, there are very few:

I suspect it has do to with Google Play automatically integrating pairipcore in APK file, maybe Google Play Integrity? The documentation says it is implemented for server communication but there must be something else within Google Play publishing system.

When APK is tampered, the app crashes immediately without error. In the logcat, the "Apk signature is invalid." exception is thrown. It also crashes but just throw lib exception without any details when trying to run in any emulators. I guess emulators are not supported to handle this protection

Doing some Google searching, I discovered Google Camera has the same protection, so I did some little reversing with Google Camera APK. Here is the result. There is signature check and VM runner

image

OnReciever methods are encrypted and replaced with VM invoke call, making it more difficult to disable the protection. This is similar to Jiagu360, encrypting all OnCreate methods. It would be super easy to disable protection if they didn't do this, right? XD

image

The VM runner reads encrypted files from assets

image

The libpairipcore.so lib. I checked it in IDA but it seems obfuscated with no useful strings exposed, so I didn't bother checking it further

image

Reversing BitLife APK, there is license check included

image

APKiD current results... Yes, it does give some hint that the app is protected

Google Camera

[+] APKiD 2.1.4 :: from RedNaga :: rednaga.io
[*] E:\Google Camera_8.7.250.494820638.44_apkcombo.com.apk
 |-> anti_disassembly : illegal class name
[*] E:\Google Camera_8.7.250.494820638.44_apkcombo.com.apk!classes.dex
 |-> anti_disassembly : illegal class name
 |-> anti_vm : Build.FINGERPRINT check, Build.HARDWARE check, Build.MANUFACTURER check, Build.TAGS check
 |-> compiler : dexlib 2.x
[*] E:\Google Camera_8.7.250.494820638.44_apkcombo.com.apk!classes2.dex
 |-> anti_disassembly : illegal class name
 |-> anti_vm : Build.FINGERPRINT check
 |-> compiler : dexlib 2.x
[*] E:\Google Camera_8.7.250.494820638.44_apkcombo.com.apk!classes3.dex
 |-> compiler : r8 without marker (suspicious)

BitLife

[+] APKiD 2.1.4 :: from RedNaga :: rednaga.io
[*] E:\BitLife_3.2.4_apkcombo.com.xapk!com.candywriter.bitlife.apk!assets/audience_network.dex
 |-> anti_debug : Debug.isDebuggerConnected() check
 |-> anti_vm : possible Build.SERIAL check
 |-> compiler : unknown (please file detection issue!)
[*] E:\BitLife_3.2.4_apkcombo.com.xapk!com.candywriter.bitlife.apk!classes.dex
 |-> anti_debug : Debug.isDebuggerConnected() check
 |-> anti_vm : Build.FINGERPRINT check, Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, network operator name check, possible VM check
 |-> compiler : dexlib 2.x
 |-> obfuscator : unreadable field names, unreadable method names
[*] E:\BitLife_3.2.4_apkcombo.com.xapk!com.candywriter.bitlife.apk!classes2.dex
 |-> anti_vm : Build.FINGERPRINT check, Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, Build.TAGS check, SIM operator check, network operator name check
 |-> compiler : dexlib 2.x
 |-> obfuscator : unreadable field names, unreadable method names
[*] E:\BitLife_3.2.4_apkcombo.com.xapk!com.candywriter.bitlife.apk!classes3.dex
 |-> anti_debug : Debug.isDebuggerConnected() check
 |-> anti_vm : Build.FINGERPRINT check, Build.HARDWARE check, Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, Build.TAGS check, SIM operator check, network operator name check, possible VM check
 |-> compiler : dexlib 2.x
 |-> obfuscator : unreadable field names, unreadable method names
[*] E:\BitLife_3.2.4_apkcombo.com.xapk!com.candywriter.bitlife.apk!classes4.dex
 |-> anti_vm : Build.BOARD check, Build.FINGERPRINT check, Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, Build.TAGS check, SIM operator check, network operator name check, ro.kernel.qemu check
 |-> compiler : dexlib 2.x
 |-> obfuscator : unreadable field names, unreadable method names
[*] E:\BitLife_3.2.4_apkcombo.com.xapk!com.candywriter.bitlife.apk!classes5.dex
 |-> anti_vm : Build.FINGERPRINT check, Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, SIM operator check, network operator name check
 |-> compiler : dexlib 2.x
 |-> obfuscator : unreadable field names, unreadable method names
[*] E:\BitLife_3.2.4_apkcombo.com.xapk!com.candywriter.bitlife.apk!classes6.dex
 |-> anti_vm : Build.FINGERPRINT check, Build.MANUFACTURER check, Build.MODEL check, Build.TAGS check, network operator name check
 |-> compiler : dexlib 2.x
 |-> obfuscator : unreadable field names, unreadable method names
enovella commented 1 year ago

Hi,

I knew about this library and some protections behind it, but I don't know much who's behind. More samples at Koodous (after log in): https://koodous.com/rules/L3nkVGvq2X14jXDY/general

Would you like to open a pull-request?

Best

AndroidMaster24 commented 1 year ago

No need, I just like to report some info about it 😀

Daniel-1647 commented 1 year ago

If you notice then many of the core strings in the dex file have been replaced with strings which are uninitialized and would be NULL by default. Thanks to research done by CmP, It seems that the libpairipcore.so is involved in initializing those strings. Now have to investigate more.

AndroidMaster24 commented 1 year ago

If you notice then many of the core strings in the dex file have been replaced with strings which are uninitialized and would be NULL by default. Thanks to research done by CmP, It seems that the libpairipcore.so is involved in initializing those strings. Now have to investigate more.

Yes, I'm aware of it

I have another info that pairipcore could possibly be related to Google Play. When pairipcore is enabled, APKCombo, the APK downloader that retrieves APK via Google Play API, would never be able to retrieve single APK and only give us option to download split APK (XAPK file). This is always the case for most apps with pairipcore... except Google Camera. Not sure why single APK is still available for Google Camera, it could be it's a system app for Google phones so split APK won't be supported for it. I once encountered single APK went available again on APKcombo after the removal of pairipcore.

It might sound non-sense but it is the fact in my experience

Daniel-1647 commented 1 year ago

Change the "could be related to Google Play" to "is related to Google Play". :)

There are some Google documentations on it which further prove it out that this protection is from Google.

Also if you would like to discuss more about it then perhaps you can send me a message on discord or telegram as I am interested in reverse engineering it.

AndroidMaster24 commented 1 year ago

There are some Google documentations on it which further prove it out that this protection is from Google.

Can you link to the Google documentation here?

Change the "could be related to Google Play" to "is related to Google Play". :)

There is no reason to change, all I need is collect info and get rednaga or APKiD contributers to check and confirm it to be proposed as new detection

Daniel-1647 commented 1 year ago

https://developer.android.com/google/play/integrity#automatic-integrity-protection

https://support.google.com/googleplay/android-developer/answer/10183279

jackwpa commented 1 year ago

It looks like the latest JEB Pro can recover those missing strings.

In the blog at https://www.pnfsoftware.com/blog/recovering-jni-registered-natives/ they showcase a plugin that does that. It doesn't seem specific to libpairipcore.so, but it can work on it.

enovella commented 1 year ago

Thanks for the info guys! Feel free to discuss more topics in this thread.

ghost commented 1 year ago

Did anyone actually bypassed it other than analyzing?

Daniel-1647 commented 1 year ago

It's not completely impossible. It will just take time to RE.

ghost commented 1 year ago

I see

Quicker666 commented 1 year ago

Did anyone actually bypassed it other than analyzing?

Modders already start released mod with pairipcore

enovella commented 1 year ago

Did anyone actually bypassed it other than analyzing?

Modders already start released mod with pairipcore

@Quicker666 could you provide modded samples? And also, could you elaborate and tell us why this sample was modded? I could write a rule for it.

enovella commented 1 year ago

Found one:

> md5 northgard-2.0.3-mod.apk
MD5 (northgard-2.0.3-mod.apk) = 8a7aa55725d8f2a0b7a470c0ec9e60b2

> apkid northgard-2.0.3-mod.apk
[+] APKiD 2.1.4 :: from RedNaga :: rednaga.io
[*] northgard-2.0.3-mod.apk!classes.dex
 |-> anti_vm : Build.FINGERPRINT check, Build.MANUFACTURER check, Build.TAGS check, possible VM check
 |-> compiler : dexlib 2.x
[*] northgard-2.0.3-mod.apk!classes2.dex
 |-> anti_debug : Debug.isDebuggerConnected() check
 |-> anti_vm : Build.FINGERPRINT check, Build.HARDWARE check, Build.MANUFACTURER check, Build.MODEL check, Build.PRODUCT check, Build.TAGS check, SIM operator check, network operator name check, possible VM check
 |-> compiler : dexlib 2.x
[*] northgard-2.0.3-mod.apk!classes3.dex
 |-> anti_debug : Debug.isDebuggerConnected() check
 |-> anti_vm : Build.FINGERPRINT check
 |-> compiler : dexlib 2.x
[*] northgard-2.0.3-mod.apk!classes4.dex
 |-> anti_vm : Build.FINGERPRINT check, Build.MANUFACTURER check, Build.TAGS check, possible VM check
 |-> compiler : dexlib 2.x
[*] northgard-2.0.3-mod.apk!lib/arm64-v8a/libpairipcore.so
 |-> protector : Google Play Integrity
[*] northgard-2.0.3-mod.apk!lib/arm64-v8a/libRMS.so
 |-> packer : 5play.ru
[*] northgard-2.0.3-mod.apk!classes5.dex
 |-> compiler : dexlib 2.x
TIABRO commented 1 year ago

hello, has anyone got a solution for bypass pairip?

nitanmarcel commented 1 year ago

The weird asset you guys see being loaded are bytecode which get loaded and executed by the VM.

Normally, but not mandatory the protection should be in the first bytecode file it gets loaded, which can be seen by enabling debugging in com.pairip.VMRunner and adb logcat | grep VMRunner.

You can just block the asset from loading, but be careful that they might also contain things needed by the app so just block the one you need to block.

Also as an example, idk if there's something else hidden in the app, or the app needed something from the blocked asset but it can't verify the play integrity anymore with their server:

Screenshot_20230802-195524_ChatGPT

Aaand frida attached to the pairip protected application as a bonus xD

20230802_204714

nitanmarcel commented 1 year ago

The weird asset you guys see being loaded are bytecode which get loaded and executed by the VM.

Normally, but not mandatory the protection should be in the first bytecode file it gets loaded, which can be seen by enabling debugging in com.pairip.VMRunner and adb logcat | grep VMRunner.

You can just block the asset from loading, but be careful that they might also contain things needed by the app so just block the one you need to block.

Also as an example, idk if there's something else hidden in the app, or the app needed something from the blocked asset but it can't verify the play integrity anymore with their server:

Screenshot_20230802-195524_ChatGPT

Aaand frida attached to the pairip protected application as a bonus xD

20230802_204714

Found what's the deal with it, it was actuality even explained on the play integrity docs.

There's also a server side integrity check, the play server also sends the app singing certificate to the app server, which is then evaluated and the app server chooses if it will fulfill the API request or not.

Can't be bypassed by just modifying the app, since not the app sends the signature to the play server, as far as I saw.

Played a little and faked the signature of the app read by com.android.vending but

{
    "detail": {
        "description": "Something went wrong.",
        "param": "deviceRecognitionVerdict",
        "type": "invalid_verdict_response"
    }
}
I6MOxSGcHVo1bgUndxIyeIbrvRcs1rSYRtf1pl4 commented 1 year ago

The weird asset you guys see being loaded are bytecode which get loaded and executed by the VM. Normally, but not mandatory the protection should be in the first bytecode file it gets loaded, which can be seen by enabling debugging in com.pairip.VMRunner and adb logcat | grep VMRunner. You can just block the asset from loading, but be careful that they might also contain things needed by the app so just block the one you need to block. Also as an example, idk if there's something else hidden in the app, or the app needed something from the blocked asset but it can't verify the play integrity anymore with their server: Screenshot_20230802-195524_ChatGPT Aaand frida attached to the pairip protected application as a bonus xD 20230802_204714

Found what's the deal with it, it was actuality even explained on the play integrity docs.

There's also a server side integrity check, the play server also sends the app singing certificate to the app server, which is then evaluated and the app server chooses if it will fulfill the API request or not.

Can't be bypassed by just modifying the app, since not the app sends the signature to the play server, as far as I saw.

Played a little and faked the signature of the app read by com.android.vending but

{
    "detail": {
        "description": "Something went wrong.",
        "param": "deviceRecognitionVerdict",
        "type": "invalid_verdict_response"
    }
}

In older version of parip, on frida attach app will crash until you reboot your device (emulators, real devices no matter). Now, how I can see, they making less protection, but pairip still easily removable as it was before.

nitanmarcel commented 1 year ago

The weird asset you guys see being loaded are bytecode which get loaded and executed by the VM. Normally, but not mandatory the protection should be in the first bytecode file it gets loaded, which can be seen by enabling debugging in com.pairip.VMRunner and adb logcat | grep VMRunner. You can just block the asset from loading, but be careful that they might also contain things needed by the app so just block the one you need to block. Also as an example, idk if there's something else hidden in the app, or the app needed something from the blocked asset but it can't verify the play integrity anymore with their server: Screenshot_20230802-195524_ChatGPT Aaand frida attached to the pairip protected application as a bonus xD 20230802_204714

Found what's the deal with it, it was actuality even explained on the play integrity docs. There's also a server side integrity check, the play server also sends the app singing certificate to the app server, which is then evaluated and the app server chooses if it will fulfill the API request or not. Can't be bypassed by just modifying the app, since not the app sends the signature to the play server, as far as I saw. Played a little and faked the signature of the app read by com.android.vending but

{
    "detail": {
        "description": "Something went wrong.",
        "param": "deviceRecognitionVerdict",
        "type": "invalid_verdict_response"
    }
}

In older version of parip, on frida attach app will crash until you reboot your device (emulators, real devices no matter). Now, how I can see, they making less protection, but pairip still easily removable as it was before.

You're going yo have unity games/apps.

They use pairip in their libunity.so ELF and calling it's ExecuteProgram which I have no idea what it does.

~ $ ldd libunity.so | grep pairip                                  libpairipcore.so => not found

Anyway, if you bypass pirip the way I explained above, the game crashes with SIGESIGV_MAPERR


08-05 09:47:04.921 30739 30739 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

08-05 09:47:04.921 30739 30739 F DEBUG   : LineageOS Version: '20.0-20230715-UNOFFICIAL-miatoll'

08-05 09:47:04.921 30739 30739 F DEBUG   : Build fingerprint: 'Redmi/joyeuse_global/joyeuse:12/RKQ1.211019.001/V13.0.1.0.SJZMIXM:user/release-keys'

08-05 09:47:04.921 30739 30739 F DEBUG   : Revision: '0'

08-05 09:47:04.921 30739 30739 F DEBUG   : ABI: 'arm64'

08-05 09:47:04.921 30739 30739 F DEBUG   : Timestamp: 2023-08-05 09:47:04.821552126+0300

08-05 09:47:04.921 30739 30739 F DEBUG   : Process uptime: 1s

08-05 09:47:04.921 30739 30739 F DEBUG   : Cmdline: com.teenageengineering.pocketoperatorforpixel

08-05 09:47:04.921 30739 30739 F DEBUG   : pid: 30710, tid: 30710, name: peratorforpixel  >>> com.teenageengineering.pocketoperatorforpixel <<<

08-05 09:47:04.921 30739 30739 F DEBUG   : uid: 10295

08-05 09:47:04.921 30739 30739 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000000

08-05 09:47:04.921 30739 30739 F DEBUG   : Cause: null pointer dereference

08-05 09:47:04.921 30739 30739 F DEBUG   :     x0  0000007fe1318251  x1  0000006fe8f75040  x2  0000000000000010  x3  0000000000000000

08-05 09:47:04.921 30739 30739 F DEBUG   :     x4  0000006fe8f75050  x5  0000007fe1318261  x6  413738415a72396c  x7  486678526a4a434b

08-05 09:47:04.921 30739 30739 F DEBUG   :     x8  0000000000000020  x9  0000007fe1318250  x10 0000000000000003  x11 0000000000000003

08-05 09:47:04.921 30739 30739 F DEBUG   :     x12 00000070e54e0f80  x13 000000705f625f40  x14 fffffffffc000000  x15 fffffffffffffff9

08-05 09:47:04.921 30739 30739 F DEBUG   :     x16 0000007040670070  x17 00000070e55427a0  x18 000000710492c000  x19 0000000000000001

08-05 09:47:04.921 30739 30739 F DEBUG   :     x20 0000006fe8f75000  x21 0000000000000000  x22 0000006fe8f75040  x23 0000000000000010

08-05 09:47:04.921 30739 30739 F DEBUG   :     x24 0000007fe1318251  x25 000000710525a000  x26 0000007103df6000  x27 0000007fe13193e0

08-05 09:47:04.921 30739 30739 F DEBUG   :     x28 000000710525b9d8  x29 0000007fe1318350

08-05 09:47:04.921 30739 30739 F DEBUG   :     lr  0000007040669c34  sp  0000007fe1318150  pc  0000007040669c38  pst 0000000020001000

08-05 09:47:04.921 30739 30739 F DEBUG   : backtrace:

08-05 09:47:04.921 30739 30739 F DEBUG   :       #00 pc 0000000000050c38  /data/app/~~P0-wSvWFoFv990jstjMtCg==/com.teenageengineering.pocketoperatorforpixel-g8N9aBofc2NJIB-n1d2kVg==/lib/arm64/libpairipcore.so (ExecuteProgram+196)
Daniel-1647 commented 1 year ago

@nitanmarcel Pairip has 3 levels of protection. Signature check and LicenseCheck make up the first level. It is easily removable and are found in smali. In the second level, Many strings from Dex files (generally around 1500 for most of the unity games) are removed by Google Play. Pairip restores them in runtime. In the third level, Data from library files is removed by Pairip. In runtime the library calls ExecuteProgram in the pairip library to restore that data.

You can check if a library invokes ExecuteProgram by searching for it in that library with some hex editor but not always the data gets removed as I have seen some exceptions.

Currently I am trying to analyze the VM and Bytecode of pairip. It's not very hard and with some persistence, You can remove it :)

Edit: I forgot to include the removed code from onReceived function of broadcast receivers. Pairip can replace the code there.

nitanmarcel commented 1 year ago

@nitanmarcel Pairip has 3 levels of protection. Signature check and LicenseCheck make up the first level. It is easily removable and are found in smali. In the second level, Many strings from Dex files (generally around 1500 for most of the unity games) are removed by Google Play. Pairip restores them in runtime. In the third level, Data from library files is removed by Pairip. In runtime the library calls ExecuteProgram in the pairip library to restore that data.

You can check if a library invokes ExecuteProgram by searching for it in that library with some hex editor but not always the data gets removed as I have seen some exceptions.

Currently I am trying to analyze the VM and Bytecode of pairip. It's not very hard and with some persistence, You can remove it :)

More like anti tempering protection than signature and license checks, since it includes multiple things such as frida detection and root most likely.

If you want to analyze the bytecode you can put a logcat on the VMRunner;->invoke(). You can see there that it reads and I think decrypts the asset file :)

About unity, I think ExecuteProgram is their main entry point for the game, unless they just put it there randomly to add a little but of protection, which sounds weird.

Anyway I won't be able to find what it does until I stop the library from calling that the specific function.

You can check if a library invokes ExecuteProgram by searching for it in that library with some hex editor but not always the data gets removed as I have seen some exceptions.

Yep, that's how I found libunity.so calls ExecuteProgram

nitanmarcel commented 1 year ago

Tho, I have a feeling that blocking the bytecode from the startup application also don't call ExecuteProgram, so stopping the library from calling it should work.

Daniel-1647 commented 1 year ago

@nitanmarcel Yes it is a complete anti tampering solution. I mentioned them and not the anti debugging ones because I haven't paid much attention to them, yet.. :)

The asset file contains encrypted bytecode and they are passed over to the execVM function inside the library through JNI. The execVM function further calls the bytecode handling function in the library after the decryption of bytecode but due to the huge number of instructions and complex pseudocode...I haven't been able to understand it yet. I need to surely work up on my abilities.

The ExecuteProgram is not the main entry of the library. You are wrong there. The entry point is DT_INIT/.init_proc() function in the library which calls ExecuteProgram.

If you stop the execution of ExecuteProgram without restoring the library data then you will get a crash.

nitanmarcel commented 1 year ago

@nitanmarcel Yes it is a complete anti tampering solution. I mentioned them and not the anti debugging ones because I haven't paid much attention to them, yet.. :)

The asset file contains encrypted bytecode and they are passed over to the execVM function inside the library through JNI. The execVM function further calls the bytecode handling function in the library after the decryption of bytecode but due to the huge number of instructions and complex pseudocode...I haven't been able to understand it yet. I need to surely work up on my abilities.

The ExecuteProgram is not the main entry of the library. You are wrong there. The entry point is DT_INIT/.init_proc() function in the library which calls ExecuteProgram.

If you stop the execution of ExecuteProgram without restoring the library data then you will get a crash.

Ah, I didn't really took a long look at the lib.

DT_INIT is the library initialization (e.g: int main()..). So that's called when the library is loaded. But there's a function in libunity that's named ExecuteProgram 🤔 well it could mean something else

nitanmarcel commented 1 year ago

Removed libpairipcore.so link from libunity.so. and at least it seems that on it's own calls ExecuteProgram of pairip library

Screenshot_20230805-115202_Logcat Extreme

Daniel-1647 commented 1 year ago

IDA calls it as .init_proc() but ghidra calls it DT_INIT. Same thing basically and this function further calls ExecuteProgram.

The ExecuteProgram inside the libUnity is a reference to the ExecuteProgram function in libpairipcore.so

nitanmarcel commented 1 year ago

IDA calls it as .init_proc() but ghidra calls it DT_INIT. Same thing basically and this function further calls ExecuteProgram.

The ExecuteProgram inside the libUnity is a reference to the ExecuteProgram function in libpairipcore.so

Yes. But question is, if ExecuteProgram is also called in the pairip initialization, why does libunity needs to call it?

Daniel-1647 commented 1 year ago

Are you sure that it's called in Pairip initialisation?

nitanmarcel commented 1 year ago

Are you sure that it's called in Pairip initialisation?

You said that is called in DT_INIT/.init_proc() with DT_INIT being the initialization function:


DT_INIT
--

Points to the address of an initialization function that must be called when the file is loaded.</span>

https://android.googlesource.com/platform/bionic/+/android-4.2_r1/linker/README.TXT

nitanmarcel commented 1 year ago

so if I don't understand wrong. when the library is loaded, it calls .init_proc() which calls ExecuteProgram as you mentioned. so when libunity loads pairip, it calls the initialization one more time which ends up calling ExecuteProgram

I6MOxSGcHVo1bgUndxIyeIbrvRcs1rSYRtf1pl4 commented 1 year ago

IDA calls it as .init_proc() but ghidra calls it DT_INIT. Same thing basically and this function further calls ExecuteProgram. The ExecuteProgram inside the libUnity is a reference to the ExecuteProgram function in libpairipcore.so

Yes. But question is, if ExecuteProgram is also called in the pairip initialization, why does libunity needs to call it?

ExecuteProgram called not only in libunity. libil2cpp has it too. Unity games easily bypassable too.

nitanmarcel commented 1 year ago

IDA calls it as .init_proc() but ghidra calls it DT_INIT. Same thing basically and this function further calls ExecuteProgram. The ExecuteProgram inside the libUnity is a reference to the ExecuteProgram function in libpairipcore.so

Yes. But question is, if ExecuteProgram is also called in the pairip initialization, why does libunity needs to call it?

ExecuteProgram called not only in libunity. libil2cpp has it too. Unity games easily bypassable too.

Yeah I just found out after I replaced the one in libunity with my own hook xD.

Now with both replaced, the SEGV_MAPERR crash is gone, and replace with another error xD:

08-05 13:47:28.127 11080 11080 I IL2CPP  : JNI_OnLoad
08-05 13:47:28.142  1453  2012 E DatabaseUtils: Writing exception to parcel
08-05 13:47:28.142  1453  2012 E DatabaseUtils: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at com.android.providers.settings.SettingsProvider.enforceSettingReadable(SettingsProvider.java:2135)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at com.android.providers.settings.SettingsProvider.getSystemSetting(SettingsProvider.java:1838)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at com.android.providers.settings.SettingsProvider.call(SettingsProvider.java:434)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.content.ContentProvider.call(ContentProvider.java:2511)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.content.ContentProvider$Transport.call(ContentProvider.java:525)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:295)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.os.Binder.execTransactInternal(Binder.java:1280)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.os.Binder.execTransact(Binder.java:1244)
08-05 13:47:28.142 11080 11080 D AndroidRuntime: Shutting down VM
08-05 13:47:28.143 11080 11080 E AndroidRuntime: FATAL EXCEPTION: main
08-05 13:47:28.143 11080 11080 E AndroidRuntime: Process: com.teenageengineering.pocketoperatorforpixel, PID: 11080
08-05 13:47:28.143 11080 11080 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.teenageengineering.pocketoperatorforpixel/com.unity3d.player.UnityPlayerActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4773)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4806)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:57)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:179)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2306)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:106)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Looper.loopOnce(Looper.java:201)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:288)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:7918)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
08-05 13:47:28.143 11080 11080 E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Parcel.createExceptionOrNull(Parcel.java:3021)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Parcel.createException(Parcel.java:2999)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Parcel.readException(Parcel.java:2982)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.content.ContentProviderProxy.call(ContentProviderNative.java:732)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$NameValueCache.getStringForUser(Settings.java:3161)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$System.getStringForUser(Settings.java:3755)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$System.getIntForUser(Settings.java:3860)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$System.getInt(Settings.java:3854)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.unity3d.player.OrientationLockListener.<init>(Unknown Source:22)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.unity3d.player.UnityPlayer.resume(Unknown Source:59)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.unity3d.player.UnityPlayerActivity.onResume(UnityPlayerActivity.java:116)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1570)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.Activity.performResume(Activity.java:8474)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4763)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        ... 13 more
08-05 13:47:28.143 11080 11080 E AndroidRuntime: Caused by: android.os.RemoteException: Remote stack trace:
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.providers.settings.SettingsProvider.enforceSettingReadable(SettingsProvider.java:2135)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.providers.settings.SettingsProvider.getSystemSetting(SettingsProvider.java:1838)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.providers.settings.SettingsProvider.call(SettingsProvider.java:434)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.content.ContentProvider.call(ContentProvider.java:2511)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.content.ContentProvider$Transport.call(ContentProvider.java:525)
08-05 13:47:28.143 11080 11080 E AndroidRuntime: 
nitanmarcel commented 1 year ago

I still couldn't hook in it with frida, so I created an empty library and patch libunity and libil2cpp with patchelf

#include <cstdio>

extern "C" void ExecuteProgram(int arg1, int arg2) {
}    
nitanmarcel commented 1 year ago

IDA calls it as .init_proc() but ghidra calls it DT_INIT. Same thing basically and this function further calls ExecuteProgram. The ExecuteProgram inside the libUnity is a reference to the ExecuteProgram function in libpairipcore.so

Yes. But question is, if ExecuteProgram is also called in the pairip initialization, why does libunity needs to call it?

ExecuteProgram called not only in libunity. libil2cpp has it too. Unity games easily bypassable too.

Yeah I just found out after I replaced the one in libunity with my own hook xD.

Now with both replaced, the SEGV_MAPERR crash is gone, and replace with another error xD:

08-05 13:47:28.127 11080 11080 I IL2CPP  : JNI_OnLoad
08-05 13:47:28.142  1453  2012 E DatabaseUtils: Writing exception to parcel
08-05 13:47:28.142  1453  2012 E DatabaseUtils: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at com.android.providers.settings.SettingsProvider.enforceSettingReadable(SettingsProvider.java:2135)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at com.android.providers.settings.SettingsProvider.getSystemSetting(SettingsProvider.java:1838)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at com.android.providers.settings.SettingsProvider.call(SettingsProvider.java:434)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.content.ContentProvider.call(ContentProvider.java:2511)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.content.ContentProvider$Transport.call(ContentProvider.java:525)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:295)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.os.Binder.execTransactInternal(Binder.java:1280)
08-05 13:47:28.142  1453  2012 E DatabaseUtils:         at android.os.Binder.execTransact(Binder.java:1244)
08-05 13:47:28.142 11080 11080 D AndroidRuntime: Shutting down VM
08-05 13:47:28.143 11080 11080 E AndroidRuntime: FATAL EXCEPTION: main
08-05 13:47:28.143 11080 11080 E AndroidRuntime: Process: com.teenageengineering.pocketoperatorforpixel, PID: 11080
08-05 13:47:28.143 11080 11080 E AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.teenageengineering.pocketoperatorforpixel/com.unity3d.player.UnityPlayerActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4773)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4806)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:57)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:179)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2306)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:106)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Looper.loopOnce(Looper.java:201)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:288)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.main(ActivityThread.java:7918)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
08-05 13:47:28.143 11080 11080 E AndroidRuntime: Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a null object reference
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Parcel.createExceptionOrNull(Parcel.java:3021)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Parcel.createException(Parcel.java:2999)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.os.Parcel.readException(Parcel.java:2982)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:142)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.content.ContentProviderProxy.call(ContentProviderNative.java:732)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$NameValueCache.getStringForUser(Settings.java:3161)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$System.getStringForUser(Settings.java:3755)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$System.getIntForUser(Settings.java:3860)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.provider.Settings$System.getInt(Settings.java:3854)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.unity3d.player.OrientationLockListener.<init>(Unknown Source:22)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.unity3d.player.UnityPlayer.resume(Unknown Source:59)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.unity3d.player.UnityPlayerActivity.onResume(UnityPlayerActivity.java:116)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1570)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.Activity.performResume(Activity.java:8474)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4763)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        ... 13 more
08-05 13:47:28.143 11080 11080 E AndroidRuntime: Caused by: android.os.RemoteException: Remote stack trace:
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.providers.settings.SettingsProvider.enforceSettingReadable(SettingsProvider.java:2135)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.providers.settings.SettingsProvider.getSystemSetting(SettingsProvider.java:1838)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at com.android.providers.settings.SettingsProvider.call(SettingsProvider.java:434)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.content.ContentProvider.call(ContentProvider.java:2511)
08-05 13:47:28.143 11080 11080 E AndroidRuntime:        at android.content.ContentProvider$Transport.call(ContentProvider.java:525)
08-05 13:47:28.143 11080 11080 E AndroidRuntime: 

Ok, these are unitialized strings:


    OrientationLockListener(Context context) {
        this.b = context;
        this.a = new k(context);
        ContentResolver contentResolver = this.b.getContentResolver();
        String str = fJRqkwjZyCbJn.MCPNoFLU; // Here
        nativeUpdateOrientationLockState(Settings.System.getInt(contentResolver, str, 0));
        this.a.a(this, str);
    }
Daniel-1647 commented 1 year ago

Are you sure that it's called in Pairip initialisation?

You said that is called in DT_INIT/.init_proc() with DT_INIT being the initialization function:


DT_INIT
--

Points to the address of an initialization function that must be called when the file is loaded.</span>

https://android.googlesource.com/platform/bionic/+/android-4.2_r1/linker/README.TXT

To clear confusion, The .init_proc() function is found in libraries which will call ExecuteProgram. You can check libunity.so. The code inside init_proc will have a call to ExecuteProgram. During runtime, The reference inside libunity.so will be resolved by the linker and it will have the address of ExecuteProgram function which is inside libpairipcore.so

Sorry, I am bad at explaining.

nitanmarcel commented 1 year ago

@Daniel-1647 the ExecuteProgram command could exactly be the function pairip calls the bytecode.

For example given the asset l9rZA87AKCJjRxfH:

image

nitanmarcel commented 1 year ago

Broke the smali and no idea where xD. Going to start again and hopefully I should be able to find where more exactly to load the assets. Maybe I'm onto something

nitanmarcel commented 1 year ago

@Daniel-1647 at least it doesn't crash anymore

Screenshot_20230805-182204_PO Pixel

nitanmarcel commented 1 year ago

@nitanmarcel Yes it is a complete anti tampering solution. I mentioned them and not the anti debugging ones because I haven't paid much attention to them, yet.. :)

The asset file contains encrypted bytecode and they are passed over to the execVM function inside the library through JNI. The execVM function further calls the bytecode handling function in the library after the decryption of bytecode but due to the huge number of instructions and complex pseudocode...I haven't been able to understand it yet. I need to surely work up on my abilities.

The ExecuteProgram is not the main entry of the library. You are wrong there. The entry point is DT_INIT/.init_proc() function in the library which calls ExecuteProgram.

If you stop the execution of ExecuteProgram without restoring the library data then you will get a crash.

Any with engendering ExecuteProgram? It has the first argument as the asset file, I'm not sure about the other two, and I'm not sure how it reads the asset file. It could just be there for doing some checks, and the arg2 could be the byte array, and third the length of the array

Daniel-1647 commented 1 year ago

ExecuteProgram is not really hard to understand. Read the pseudocode and try to analyze the JNI calls

wenboly commented 1 year ago

2222 Want to know the address of this native function!

ghost commented 1 year ago

Can you guys also analyze 5play mod? They are the one who got it work on Unity games without using signature killers. Best game to check is Motor Depot https://5play.ru/8121-motor-depot.html What's interesting is, they replaced libpairipcore.so string to their own lib libRMS.so in libunity.so and libil2cpp.so, so that ExecuteProgram calls to libRMS.so instead

image

Unfortunately, I don't have much knowledge on the lib side so I have no idea what their own ExecuteProgram is doing. Here's the pseduocode. Also the lib is packed using a private program

void __fastcall ExecuteProgram(const char *a1)
{
  __int64 v1; // x30
  __int64 v3; // x0
  __int64 v4; // x19
  __int64 v5; // x21
  __int64 v6; // x0
  int v7; // w0
  unsigned __int64 i; // x8
  char *v9; // x20
  unsigned int v10; // w14
  __int64 v11; // x13
  bool v12; // cc
  __int64 v13; // x19
  __int64 v14; // x1
  unsigned int v15; // w8
  void *v16; // x0
  char *v17; // x9
  unsigned __int64 v18; // x8
  __int64 v19; // x10
  char v20; // t1
  void *ptr[2]; // [xsp+0h] [xbp-10h] BYREF

  ptr[1] = *(void **)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v3 = sub_3B10(v1);
  if ( a1 )
  {
    if ( qword_ED858 )
    {
      if ( qword_ED860 )
      {
        if ( qword_ED868 )
        {
          if ( qword_ED870 )
          {
            v4 = v3;
            if ( v3 )
            {
              v5 = (unsigned int)crc32(0LL, 0LL, 0LL);
              v6 = strlen(a1);
              v7 = crc32(v5, a1, v6);
              for ( i = 0LL; i < 0x5816D; i += v11 + 17 )
              {
                v9 = (char *)&byte_956BC[i];
                v10 = byte_956BC[i];
                if ( v10 > 1 )
                  break;
                v11 = *(unsigned int *)(v9 + 13);
                v12 = v10 != 1 || (unsigned int)(v11 - 1) > 0x5816B;
                if ( !v12 && *(_DWORD *)(v9 + 1) == v7 && *(_DWORD *)(v9 + 5) && *(_DWORD *)(v9 + 9) && qword_ED878 )
                {
                  v13 = sub_3CF4(v4);
                  mprotect((void *)(v13 + *(unsigned int *)(v9 + 5)), *(_DWORD *)(v9 + 9), 7);
                  v14 = *(unsigned int *)(v9 + 13);
                  ptr[0] = 0LL;
                  v15 = sub_3838(v9 + 17, v14, ptr);
                  v16 = ptr[0];
                  if ( v15 && v13 && ptr[0] && v15 >= 5 )
                  {
                    v17 = (char *)ptr[0] + 4;
                    v18 = v15 / 5uLL;
                    do
                    {
                      v19 = *((unsigned int *)v17 - 1);
                      --v18;
                      v20 = *v17;
                      v17 += 5;
                      *(_BYTE *)(v13 + v19) = v20;
                    }
                    while ( v18 );
                  }
                  free(v16);
                  return;
                }
              }
            }
          }
        }
      }
    }
  }
}
wenboly commented 1 year ago

good !

ufrprogramm commented 1 year ago

Ребята, можете ли вы также проанализировать мод 5play? Именно они заставили его работать в играх Unity без использования сигнатурных убийц. Лучшая игра для проверки — Motor Depot https://5play.ru/8121-motor-depot.html Что интересно, они заменили libpairipcore.soстроку на свою собственную библиотеку в libunity.so и libil2cpp.so, так что вместо этого libRMS.soвызывается ExecuteProgramlibRMS.so

изображение

К сожалению, у меня мало знаний о библиотеках, поэтому я понятия не имею, что делает их собственная программа ExecuteProgram. Вот псевдокод. Также библиотека упакована с помощью приватной программы.

void __fastcall ExecuteProgram(const char *a1)
{
  __int64 v1; // x30
  __int64 v3; // x0
  __int64 v4; // x19
  __int64 v5; // x21
  __int64 v6; // x0
  int v7; // w0
  unsigned __int64 i; // x8
  char *v9; // x20
  unsigned int v10; // w14
  __int64 v11; // x13
  bool v12; // cc
  __int64 v13; // x19
  __int64 v14; // x1
  unsigned int v15; // w8
  void *v16; // x0
  char *v17; // x9
  unsigned __int64 v18; // x8
  __int64 v19; // x10
  char v20; // t1
  void *ptr[2]; // [xsp+0h] [xbp-10h] BYREF

  ptr[1] = *(void **)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v3 = sub_3B10(v1);
  if ( a1 )
  {
    if ( qword_ED858 )
    {
      if ( qword_ED860 )
      {
        if ( qword_ED868 )
        {
          if ( qword_ED870 )
          {
            v4 = v3;
            if ( v3 )
            {
              v5 = (unsigned int)crc32(0LL, 0LL, 0LL);
              v6 = strlen(a1);
              v7 = crc32(v5, a1, v6);
              for ( i = 0LL; i < 0x5816D; i += v11 + 17 )
              {
                v9 = (char *)&byte_956BC[i];
                v10 = byte_956BC[i];
                if ( v10 > 1 )
                  break;
                v11 = *(unsigned int *)(v9 + 13);
                v12 = v10 != 1 || (unsigned int)(v11 - 1) > 0x5816B;
                if ( !v12 && *(_DWORD *)(v9 + 1) == v7 && *(_DWORD *)(v9 + 5) && *(_DWORD *)(v9 + 9) && qword_ED878 )
                {
                  v13 = sub_3CF4(v4);
                  mprotect((void *)(v13 + *(unsigned int *)(v9 + 5)), *(_DWORD *)(v9 + 9), 7);
                  v14 = *(unsigned int *)(v9 + 13);
                  ptr[0] = 0LL;
                  v15 = sub_3838(v9 + 17, v14, ptr);
                  v16 = ptr[0];
                  if ( v15 && v13 && ptr[0] && v15 >= 5 )
                  {
                    v17 = (char *)ptr[0] + 4;
                    v18 = v15 / 5uLL;
                    do
                    {
                      v19 = *((unsigned int *)v17 - 1);
                      --v18;
                      v20 = *v17;
                      v17 += 5;
                      *(_BYTE *)(v13 + v19) = v20;
                    }
                    while ( v18 );
                  }
                  free(v16);
                  return;
                }
              }
            }
          }
        }
      }
    }
  }
}

they using "libRMS" for hooking il2cpp games.

ghost commented 1 year ago

they using "libRMS" for hooking il2cpp games.

Of course, it unpacks the lib to unknown region and hooks the game, but in pairip mods, they are using custom ExecuteProgram code in libRMS

Daniel-1647 commented 1 year ago

Guys... Even if you read the crash logs when you try to fix the modified dependencies... You will get closer to the answer. This is a great hint itself

qyzhaojinxi commented 12 months ago

Hi, is there any way to bypass it? I decomplie the apk and change nothing. Then pack it and resign the apk. But it crash when open it.

eebssk1 commented 10 months ago

Does this work ? https://platinmods.com/threads/how-to-bypass-pairip-protections-latest-too-easy.203105/ And it seems use xposed on the protected app will crash on startup too.

qyzhaojinxi commented 10 months ago

I tried last tutorial by platinmods but failed. I will try this new later. Thank you!