SReverseby Simpa Labs

String encryption

Analyzing string encryption inside Android applications

String encryption hides every endpoint and parameter in the app at once, and one decode routine usually sits behind all of them.

What string encryption changes

A minified release build keeps its string constants in the dex. Host names, endpoints, header names, parameter keys and error messages sit in the constant pool, and a decompiler shows them directly. String encryption removes them. Each constant becomes encoded data plus a call to a routine that turns it back into text at the point of use.

This is not part of the default Android toolchain. It arrives with a commercial protector layered on top of the build, and it is one of the reasons a protected APK reads differently from a merely minified one.

How the pattern looks in decompiled code

  • A field or array of numbers or bytes where a string used to be.
  • A small function that combines those values with a key, often through XOR, addition or a table lookup, and returns a String or byte array.
  • Many call sites for one implementation, since a single decode routine serves the whole app.
  • A StringBuilder or char array assembled in a loop when the result is built piece by piece.

The decode routine is the target. Every constant the app uses passes through it, so the cost of understanding it is paid once and recovered everywhere.

Call-graph fan-in identifies the routine faster than any naming convention. Sort methods by how many distinct classes call them, and a decode helper called from many unrelated classes stands out from ordinary code. If the decompiler instead shows a stack of tiny methods that each have one caller, follow the calls down until a shared implementation appears.

Where the key comes from

Implementations differ in how they obtain the key, and that detail decides how hard the routine is to reproduce.

  • A constant compiled into the routine, which makes reimplementation in another language straightforward.
  • A value derived from other constants at run time, which means the routine has to be read as a whole.
  • A value read from an asset, a resource or a native library, which adds a second artifact to the analysis.
  • A value returned by a JNI call into a shipped native library, which moves the key out of the dex entirely.

The last two cases remain workable because the decoded string has to exist in memory as an ordinary Java string before the app can use it.

Decode by observation when reading stalls

Static reconstruction of the algorithm is one route. The other is to let the app do the work and read the result. Instrument the decode routine, or the native method behind it, and log its return value together with the caller. Running the full application flow then produces a map of decoded constants in the order the app needed them, which includes endpoints and parameter names that reading alone would not connect to a specific call site.

Keep the hook narrow. A routine called for every string in the app produces a large volume of output, and the useful signal is the window around the action being reconstructed. Filtering by caller or by call order keeps the log readable. Where the routine runs during startup, decode once and cache the result.

Complications worth planning for

Some builds split decoding across several routines that share a key, so a single hook returns only part of the plaintext. Others decode lazily inside the method that uses the string, which means the plaintext appears immediately before a request is built and that timing helps attribute it to a call site. A few implementations place the decode call inside a method that also runs an environment check, so the hook is worth reviewing before it is left in place.

Some values never come from the dex at all. Asset encryption, resource obfuscation and encrypted payloads each produce their own decode step, and they are separate problems from constant encryption even though they appear in the same build. Where a value arrives from the server or from a file, look for the parsing step rather than for a decoder.

Encrypted constants also appear inside generated code, such as the classes a serialization library produces, so the callers of the decode routine do not map neatly onto application classes. That is a reason to filter by the full application flow rather than to attempt a complete decode of the build.

Confirm the result

A decoded string is only useful once it is tied to behavior. Take the recovered endpoint, build the request by hand and send it. If the server accepts it, the decoding work is validated and the surrounding parameters can be filled in the same way. Constants that never appear in a live request are usually error text or configuration that the full application flow does not need.

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?