The four stages
The path from an APK to a Python client runs through inspection, observation, reproduction and verification. Each stage answers a question the previous one raised, and skipping a stage usually sends you back to it later with less information.
- Inspection lists what the app can do. Decompilation reveals the client library, the endpoint declarations and the classes that build each request.
- Observation records what the app actually sends. A capture shows the headers, the body and the values that change from call to call.
- Reproduction turns the derived values into code. The signing, encryption and session logic moves into Python modules beside the transport.
- Verification compares the client with the app. The server accepts both, and the client keeps working after a token refresh.
Stage one: read the package
Start with a decompiler and list the networking library in use. Retrofit puts endpoint paths in annotations, OkHttp puts cross-cutting logic in interceptors, and Ktor assembles requests in coroutine code. Each layout puts the interesting values in a different file, so the library name decides where to read next.
Then find the client builder. It names the base URL, the converters and the interceptor list, and it is usually the one place where the host appears. If a packer or a protector is applied, the readable code stops at the loader, and unpacking comes before this stage can finish. Packed APKs covers that case.
Stage two: observe a real session
Run the app and record one complete flow: launch, login, a data call and a refresh. A proxy shows the plaintext when the app trusts the proxy certificate, and certificate pinning stops that. When pinning blocks the capture, the app has to be observed on a device where the check is neutralised, which is analysis work of its own. Intercepting pinned traffic describes the options.
Keep the capture aligned with the code you read. Note which captured request corresponds to which Retrofit method, because that mapping turns an endpoint list into a call sequence with real parameters.
Record the headers as well as the paths. Header order, casing and the presence of custom headers all matter when a client has to reproduce a request exactly, and a capture taken through a rewriting proxy hides that detail. Compare a direct capture with a proxied one when the server refuses a request that looks identical to the captured original.
Stage three: write the client
Build the Python package in layers, and keep each layer in its own module.
- A transport layer owns the session. It holds the base URL, the headers, the retry policy and the cookie jar, and it exposes one request method that everything else calls.
- A signing module owns the computed values. It takes the request as input and returns the signature header, the timestamp and any encrypted body, using test vectors from the captures.
- A session module owns the tokens. It logs in, stores the access token and the refresh token, and refreshes before expiry so callers never see an expired credential.
- Endpoint functions describe one call each. They name the path, the parameters and the response model, and they contain no signing logic.
- Models parse the responses. Generate them from a capture or from a schema when the response format is known.
This layout keeps the recovered logic testable. When the server changes something, the failure lands in one module rather than across the package. Keep the capture files beside the test vectors in the repository, because when a request starts failing months later, the difference between the working capture and the failing one shows which value the app now sends differently.
Stage four: verify against the server
Run the client through the same flow you captured, from login to a data call. Compare each request against the captured one, field by field, and compare the responses. A mismatch in the request explains a rejection, and a mismatch in the response points at parsing.
Then test the parts a single happy path does not cover: an expired token, a rotated token, an empty result, an error response and a paginated list. Those cases are where a client built by copying values breaks first. APK to Python is the service version of this process, and it delivers the package with documentation rather than a one-off script.
Related work
Reviewed 28 September 2026 · SReverse research desk