SReverseby Simpa Labs

PairIP · Google Play Automatic Protection

Understanding libpairipcore.so

PairIP is the name reverse engineers use for the distribution-time protection Google Play applies to a submitted build, shown in the Play Console as Automatic Protection or "Protected with Play". It packs a native core, libpairipcore.so, plus a small Java stub, and at startup they check the environment before the real application runs. The guard raises the cost of debugging, hooking and repacking. It does not change how the app talks to its own backend, which is where the reconstruction work actually is.

What PairIP is, in practice

You upload a build to Google Play. Play can replace what it distributes with a wrapped version of that build. This is Automatic Protection, also called "Protected with Play". The wrapping component, and the runtime guard it adds, are what reverse engineers refer to as PairIP. The guard ships inside the APK you download from Play; it is part of the app, not a policy the app asks about.

Because it is applied at distribution, you cannot opt a single file in or out and the guard is present on the delivered artifact. Because it is an environment guard, it changes what instrumenting the app looks like. It does not add endpoints, it does not replace the request logic, and it does not sit between the app and the server it calls.

The three artifacts that confirm it

Two files on disk and one manifest value are enough to confirm PairIP. The manifest points the application class at com.pairip.application.Application. The native library libpairipcore.so is present for each ABI the app ships. A verification package sits under com.pairip.licensecheck, which is the part that shows the "get this app from Play" message when the install source fails.

Spotting PairIP in the APK
identification
# 1. the manifest names the PairIP application stub
$ grep -o 'android:name="[^"]*Application"' AndroidManifest.xml
  com.pairip.application.Application

# 2. the native runtime guard ships once per ABI
$ ls lib/arm64-v8a/
  libpairipcore.so

# 3. the distribution-time verification package
$ apktool d app.apk -o out && find out/com/pairip -type f
  out/com/pairip/application/Application.smali
  out/com/pairip/licensecheck/...

Fig. 01 · PairIP announces itself through the application stub and the native core, both of which survive normal obfuscation.

What to verify in the build

The guard runs before the real application starts and asks the native core a set of environment questions. The exact set is decided per build, so verify what this APK actually implements rather than assuming a fixed list. Read the stub and the library, then confirm each signal against the code that uses it.

Signal to look forWhat it may indicateHow to confirm it
Signature comparisonThe build checks the signing certificate.Read the callers and the strings that name the certificate.
Dex or package hashingTampering with classes or resources is detected.Find where the digest is computed and compared.
Debugger and tracer stringsAn anti-debugging path exists.Search for debugger and tracer markers, then trace the branch.
Hook and instrumentation markersThe guard knows common hook frameworks.Search the strings and the GOT or PLT for rewritten entries.
Root and Magisk markersA root or Magisk check may exist.Search for su, Magisk and mount strings, then trace their use.
Emulator markersAn emulator check may exist.Search build properties and hardware strings, then trace the branch.
Install-source handlingThe source of the "get this app from Play" prompt.Read the license-check path and what triggers the prompt.

These are leads, not conclusions. A string match does not prove a check runs; read the code that uses it.

The decision the stub makes

com.pairip.application.Application loads libpairipcore.so and calls it in onCreate. The native function returns a result, and the Java stub decides the next move: start the real application, start a degraded version, or stop and surface a message. This is a small, decidable gate.

The stub is a thin call surface
com/pairip/application/Application.java
static { System.loadLibrary("pairipcore"); }

// Illustrative structure, not a capture from one build. The real method
// names and arities differ per version, so read them from the decompiled
// stub. This is only the shape to look for in the Application stub.
private native int environmentCheck();   // integrity or signature
private native boolean isDebuggerAttached();  // debugger or tracer check
private native boolean hasRootMarkers();    // root, su or mount markers

public void onCreate() {
  // run the checks, then decide how to start the app
}

Fig. 02 · the Java layer is a thin decision point. The checks themselves live in native code inside libpairipcore.so.

PairIP and Play Integrity are different mechanisms

The two are frequently conflated and they are not the same thing. PairIP (Automatic Protection) is applied to the build and shipped inside the APK; it runs locally and guards the environment the app starts in. The Play Integrity API is a runtime attestation the app calls to ask Google about the device and the app's integrity; it is a separate, network-queryable signal. A PairIP-guarded app may also use Play Integrity, and it may not. They do not share a check surface.

PairIP / Automatic ProtectionPlay Integrity API
Applied at distribution, shipped in the APKCalled at runtime by the app
Local check at startup, in the native coreRemote attestation via a Google-provided verdict
Guards the environment the app starts inReports device and app integrity to the app's backend
Failure blocks or degrades startupVerdict is read by the app and can gate a server call

How analysis proceeds

Static first. Confirm the markers, dump the exports and strings of libpairipcore.so, and list the stub's native methods to learn the check surface. That tells you what the app will test and where the gate is. The strings in the library and the method table of the stub are the fastest read.

Reading the check surface
read-surface
# the stub's native methods tell you the call surface.
# jadx reads the DEX directly to Java; apktool produces Smali, not .class files.
$ jadx -d out app.apk
$ sed -n '/class Application/,/^}/p' out/sources/com/pairip/application/Application.java

# exported symbols in the native core
$ nm -D lib/arm64-v8a/libpairipcore.so | head

# strings the library references
$ strings lib/arm64-v8a/libpairipcore.so | grep -iE 'tracerpid|magisk|frida|ptrace|su' | sort -u

Fig. 03 · the native exports and the stub's method table map out which checks the gate runs.

Then get the app running under the conditions it expects, so the gate passes, and observe the layer that matters. The reliable path is a non-rooted, non-debuggable, Play-installed environment. If you instrument, do it after the gate, and expect the guard to know the common instrumentation markers.

The endpoint list, the authentication flow, the request signing, the payload serialization: those belong to the app and survive once it is running. That is the output you are after.

The failure modes that look like a wall

The failures that read as "impenetrable" are almost always the startup gate checking the place you are standing, not the protocol you want.

SymptomWhat it usually isRoute
App shows "get this app from Play"Install-source check failedInstall from the expected distribution, or satisfy the source check
App exits right after a toastSignature or package-integrity check failedVerify the build is untouched; satisfy the signature it expects
App runs but the API returns 403A request-generation issue, not the gateReproduce the signing, session and nonce the app builds
App diverges under a debuggerAnti-debugging pathObserve after the gate, in an unmodified environment

What this buys the reconstruction

Once the guard is satisfied, the protocol recovery proceeds like any other Android app. The extra work is in the analysis environment: getting the app to run where the gate passes, and observing the request layer afterward. The request logic itself is not a PairIP problem.

A PairIP guard changes the cost of getting inside to observe. It does not change what needs to be reconstructed once you are.

Related research

Protection

Understanding runtime-loaded DEX

DexClassLoader and InMemoryDexClassLoader defer the real code to startup, so a static decompiler shows only a stub.

Read

Relevant capability

For PairIP-guarded apps we recover the endpoints, the signing and the request flow, working through the runtime checks rather than around them. PairIP reverse engineering

Start a project

Send the app you need to understand.

We rebuild the complete application flow and return the full API in Python, JavaScript/TypeScript, Postman and complete API documentation.

Projects start at $120. Most are delivered in 24 to 72 hours.

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?