~/posts/mobile/obfuscated-okhttp-certificate-pinning.md

Finding obfuscated OkHttp pinning through parameter types

Compare three request paths in normal and obfuscated Android 7.1.2 builds. Separate platform policy from OkHttp pinning, then identify the obfuscated method through Smali descriptors, arguments, and Builder timing.

date[31:24]
read[23:16]
6 min
cat[15:8]
Mobile
Contents
  1. 0x00Separate the three request paths
  2. 0x01A tool match is not a successful request
  3. 0x02Parameter descriptors outlast short names
  4. 0x03Confirm the construction point from search results
  5. 0x04Why returning the Builder changes policy
  6. 0x05Keep the conclusion reproducible
  7. 0x06References

A name-based certificate-pinning script may work on a normal APK and stop matching after obfuscation. The policy still runs; names such as okhttp3.CertificatePinner are what disappeared. Parameter types, input data, and object-construction timing offer a different route to the same code.

Jeroen Beckers' OkHttp experiment uses Android 7.1.2, Objection 1.4.3, and Apktool 2.3.4 to compare a normal build with default ProGuard obfuscation. The conclusions are specific to that environment. The focus is identifying a construction method through its type descriptor, rather than assuming stable class names across versions.

Separate the three request paths

The test app retrieves the same service's robots.txt through three clients: the platform client, OkHttp without an explicit pinner, and OkHttp with CertificatePinner. Its network_security_config.xml also configures a SHA-256 pin for the target domain and its subdomains.

No OkHttp pinner does not mean no pinning anywhere in the connection. Platform configuration and library policy can overlap. Likewise, trusting the proxy CA at the system level does not establish that it matches an additional app pin.

Controls in the historical experiment5 rows
Condition Recorded value Why it matters
Device Android 7.1.2 in the terminal capture Fixes the platform verification implementation
Tools Objection 1.4.3; Apktool 2.3.4 Keeps historical behavior separate from current versions
Proxy CA Installed in the system trust store Helps exclude ordinary chain-trust failures
Configuration expiry expiration="2022-01-01" Preserves the timing condition of the 2019 experiment
Comparison variable Normal build versus default ProGuard obfuscation Helps distinguish failed name lookup from changed request logic

A tool match is not a successful request

Objection's output shows that it found OkHttp and TrustManagerImpl. The excerpt omits repeated calls, session identifiers, the app package name, and local paths:

Objection 1.4.3 · historical output excerpt
$ android sslpinning disable
Custom, Empty TrustManager ready
OkHTTP 3.x Found
TrustManagerImpl

Finding a class establishes that lookup succeeded. It does not establish coverage of every verification entry point. The app's three result labels provide a request-level comparison:

Results for the three request paths3 rows
UI label Normal build: Objection defaults only Obfuscated build: platform and Builder handling combined
SecurityPolicy ERROR OK
OKHTTP OK OK
Pinned OKHTTP OK OK

These columns are not a strict A/B test that changes one hook on the same build. They represent different stages: the platform request first fails under the default handling, extra platform handling is added, obfuscation is introduced, and the obfuscated Builder is handled last. The table shows the two endpoint states, not a complete record of every intermediate stage.

On this device, the tool's default entry points did not cover the entire platform check. That observation is narrower than a claim about every Android 7 minor release.

Parameter descriptors outlast short names

After obfuscation, the name-based script stops identifying OkHttp and the request with additional pins fails again. Inspect the lookup chain before assuming that the app introduced a new cryptographic mechanism.

The target is CertificatePinner.Builder.add(), a policy-construction method. It receives a hostname pattern and an array of pins, adds rules to the Builder's collection, and returns the Builder for chaining. Its first argument is a hostname or pattern, not an arbitrary full URL.

At the bytecode level, String... is String[]. The descriptor shape used to filter candidates is:

JVM method descriptortext
(Ljava/lang/String;[Ljava/lang/String;)Lreturn/type;

Here, Lreturn/type; is an illustrative object return type. The [ denotes an array. Standard-library type descriptors often survive default name obfuscation, but ordinary business methods may share the same signature. Treat it as a candidate filter, not proof of identity.

Confirm the construction point from search results

After Apktool 2.3.4 decodes obfuscated.apk, the search results include call sites and method declarations. The candidate's declaration is:

okhttp3/g$a.smali · method declarationsmali
.method public varargs a(Ljava/lang/String;[Ljava/lang/String;)Lokhttp3/g$a;

A fixed-string search avoids interpreting [ as regular-expression syntax. The following are verification commands, not an execution log:

Locate descriptor candidatesbash
apktool d obfuscated.apk -o application
rg -n -F 'Ljava/lang/String;[Ljava/lang/String;)L' application/smali*

Search every smali directory rather than only the first DEX. Deduplicate declarations and call sites so that one method is not counted as several candidates.

Confirm the candidate's meaning4 steps
  1. 1

    Record its identity

    Save the class, method, full parameter descriptor, return type, and DEX. This APK uses okhttp3.g$a and a; those names are specific to that build.

  2. 2

    Observe arguments

    Check that the first argument is the expected hostname pattern and the second array contains the expected pins, rather than unrelated business strings.

  3. 3

    Inspect the callers

    Use callers and stack context to establish that certificate-pinning policy is being constructed. Check the writes to the Builder's collection.

  4. 4

    Check timing

    Confirm that instrumentation is active before the client and policy objects are built, then record request outcomes.

Why returning the Builder changes policy

In this sample, the method both adds rules and returns itself. Returning the object without performing that addition preserves chaining but omits those pins. The following script retains the historical APK's obfuscated names:

builder-hook.jsjavascript
Java.perform(function () {
  const Builder = Java.use('okhttp3.g$a');
  const add = Builder.a.overload(
    'java.lang.String', '[Ljava.lang.String;'
  );
  add.implementation = function (hostPattern, pins) {
    console.log('Observed pin configuration: ' + hostPattern);
    return this;
  };
});
  1. Select both argument types to avoid a different overload with the same name.
  2. Preserve the chained return value without calling the original method. This skips one rule addition; it does not remove rules from existing clients.

Timing is essential. Instrumenting a Builder after the client is built does not retroactively change the old instance. Early static initialization, another class loader, different overloads, direct policy construction, and Builder inlining can all prevent this path from covering the policy.

The final result depends on the existing platform handling and the added Builder handling together. It connects the configuration entry point to the later requests in this sample. It does not establish a universal result for every OkHttp build.

Keep the conclusion reproducible

Obfuscation removes convenient names, but type shapes, argument meaning, and construction relationships can still establish identity. Start with per-request baselines, confirm tool matches, introduce obfuscation, narrow candidates by descriptor, and verify them through arguments and construction timing.

Client-side observation and behavior changes also do not replace server-side authentication or data authorization checks. Use a separate debug configuration during development, and record platform trust, library pinning, and server permissions independently. “Traffic became visible” is not a complete assessment.

References

NORMAL~/posts/mobile/obfuscated-okhttp-certificate-pinning.md§--
0%en