SReverseby Simpa Labs

Query recovery

Finding GraphQL queries inside an APK

A GraphQL app exposes one endpoint, so the interesting material sits in the request body. The query documents are usually stored in the APK as string constants.

One endpoint, many operations

A GraphQL client posts to a single path, often something like /graphql. The document that selects fields travels in the request, so the API surface is not visible from the URL alone. Everything you need is in the body, and the job is to recover the documents the app sends along with the variables it fills in.

A typical request body is JSON with three members: operationName, query and variables. A mutation uses the same shape. Some builds send the document as a GET query parameter, which puts the whole selection set into the URL and into server logs.

Where the documents live in the APK

Apollo and similar code generators turn each operation into a class, and the document text becomes a string constant inside that class. That constant is what you search for.

  • Query and mutation text starts with tokens such as query, mutation, fragment or subscription.
  • Generated classes carry names such as Operation or Mutation and a document constant beside them.
  • Selection sets contain __typename, which is a reliable marker for a document string.
  • Persisted query builds store a SHA-256 hash instead of the document, often next to the strings sha256Hash or persistedQuery.
  • Raw .graphql and .json files occasionally ship in assets, and a React Native bundle keeps its documents inside the JavaScript bundle.

String encryption and obfuscation can hide the text. When a document is assembled at run time from smaller pieces, search for the fragments individually and reassemble them by watching a real request.

Persisted queries and automatic persisted queries

Many production clients send only a hash. The server either recognises the hash or rejects the request and asks for the full document. Automatic persisted queries implement that exchange the first time a document is used, after which the hash alone is accepted.

This changes how much freedom you have. If you can send a full document once and the server accepts it, you can register operations of your own. If the server accepts only documents registered when the app was built, you must reuse the app's documents and hashes exactly. Test that case deliberately, because the answer decides how the rest of the work goes.

Recovering the schema from responses

Introspection is usually disabled in production. You can still reconstruct much of the schema by reading responses. Field names in the response match the selection set, nullability shows up as null, and error objects often name the field or argument that failed validation. GraphQL errors usually arrive with HTTP 200 and an errors array, so a failed operation can look like a transport success.

Build a document incrementally. Start with a small selection set you know exists, then add fields and read the validation errors for names the server does not recognise.

Recording the endpoint and its constraints

Record the endpoint exactly, including any query string and any path prefix added by a gateway. Some clients add a header that selects a schema or a tenant, and that header changes which fields are valid for the call. Response shapes also show how the server handles lists: a cursor and a page info object mean pagination follows an opaque cursor rather than an offset, which changes how a client walks a full result set.

Variables, batching and auth

Variables carry the values that change per call. Record them from live traffic rather than guessing, because types matter: an identifier sent as a string and the same identifier sent as an integer are different arguments to the server. Some clients batch several operations into a JSON array, which changes how you count requests and how you handle a partial failure. Subscriptions run over a WebSocket transport with its own handshake and message types, so a GraphQL app can require both a document reconstruction and a frame protocol reconstruction.

Authentication and signing apply to the single endpoint the same way they apply to a REST API. Any token, cookie or signature the client attaches is checked per request, and a replayed document without current credentials returns the same errors you would see anywhere else.

A document that works from one account can fail from another when a variable references an object the account cannot read. GraphQL often returns a partial result with a data member and an errors member side by side, so a client has to check both before treating a call as a success.

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?