SReverseby Simpa Labs

Request signing

How to trace an Android request signature

Reproducing a signature starts with seeing one produced. A trace that captures the input bytes, the key and the output removes most of the guesswork that a capture alone leaves in place.

Pick the observation point

Three points on the path are worth instrumenting, and they answer different questions. The outgoing HTTP call shows the final headers as transmitted. The signing routine shows the canonical string and the key. The hash primitive shows the exact bytes that entered the digest.

Start at the far end of the stack and work backwards. The transmitted request is what the server sees, and it is cheap to obtain with a logging interceptor added after the app's own interceptors. From there, move inward until you reach the routine that produced the value.

Instrumenting the HTTP stack

If the app uses OkHttp, a Frida hook on the interceptor chain or a patched logging interceptor gives you the request object after every interceptor has run. Add your interceptor last in the chain so you observe the final state rather than an intermediate one. Retrofit hands work to OkHttp, so hooking Retrofit alone shows you the annotated method and not the resulting bytes.

Pin down the order before you trust a log line. The chain runs in the order interceptors were added, with retries and redirects re-entering the chain. A value you see in one log entry can be overwritten by a later interceptor, which is how a captured header ends up different from the one that was signed.

Ktor uses a plugin pipeline, Volley a request queue and a retry policy, and HttpURLConnection the connection itself. Find the layer that adds the credential, then hook that layer.

Hooking the signing routine

A useful hook logs inputs and outputs together:

  • The full canonical string before it is hashed, including invisible characters.
  • The key or the derived bytes used as an HMAC key.
  • The digest algorithm in use, which the class or method name often reveals.
  • The returned signature and the header name it was written to.
  • Byte lengths. A length that does not match your model of the string points at hidden material such as a trailing salt.

When the logic sits in native code behind JNI, hook the Java method that calls into the library and log the arguments crossing the boundary. Reversing the native routine is one option and often unnecessary, because you can reimplement the algorithm from the traced input and output pairs.

Hooking the digest itself is the fallback when the app builds the canonical string inline. On modern Android the usual targets are MessageDigest, Mac and the Cipher class, and a hook on the update and doFinal calls gives you the bytes in the order they were fed to the digest.

Reading a trace and reconstructing the string

Rebuild the canonical string from the trace and check it against the request. Compare its parts to the method, path, query and body you captured. Each part that appears in the string and in the request confirms the field is signed. A part present in the string but absent from the request is either a constant or a value the app derived from somewhere else.

An interceptor trace is also where you catch the app formatting a value before signing and sending a different form. A path signed with its query in the original order and transmitted with a reordered query will match on the device only because the app controls both sides.

When a hook does not fire

  • A classloader mismatch. Load the class through the application context instead of the system class loader.
  • An overloaded method. Hook each overload and log the descriptor to see which one the app calls.
  • Inlining. Simple helpers disappear under optimisation, leaving the crypto call site as the only place to observe.
  • Native code paths. Java hooks see only the boundary, so log the arguments and return value there.
  • Anti-debugging checks that terminate the process, in which case the tracing approach changes before the hook does.

A trace is only useful if it is reproducible. Log a request and its signature, then trigger the same request again and confirm both the canonical string and the signature changed in the way you expect. That pair is the test vector your Python client has to satisfy.

Reviewed 28 September 2026 · SReverse research desk

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?