React Native API reverse engineering
The JS bundle is readable. The endpoint is the part you rebuild.
SReverse · frameworks / flutter
A Flutter app calls its server the same way any app does. It opens a socket and sends bytes. What you cannot read is where those bytes are built, because the Dart code is compiled into the app binary. So we watch the requests leave the device and rebuild the logic from what we see.
// attached to a running Flutter app // Dart is AOT in libapp.so, so we read the wire, not the Dart. const engine = Process.getModuleByName("libflutter.so"); Interceptor.attach( Module.findExportByName(null, "SSL_write"), { onEnter(args) { this.buf = args[1]; this.n = args[2].toInt32(); }, onLeave(ret) { const n = ret.toInt32(); if (n <= 0) return; send({ dir: "out", n, data: Array.from(new Uint8Array(Memory.readByteArray(this.buf, n))) }); } });
Fig. 01 · reading the app at runtime, not decompiling libapp.so
The request path
Flutter networking is Dart. An app uses the http package, dio, or a gRPC client. Those libraries call into the Dart engine, and the engine talks to the socket. Your server sees a normal HTTPS request. Nothing about Flutter changes the wire format.
That matters. The hard part is not the protocol. It is finding what the app sends before it sends: the headers, the signature, the body shape.
Static analysis
When a Flutter app ships to the Play Store, the Dart code is not on disk as readable Dart. It is Ahead-of-Time compiled, so the classes and methods live in libapp.so as native machine code, or in a kernel snapshot blob.
Strings often survive. Endpoints are a different story. Apps build URLs from parts, load hostnames from a config endpoint, or keep the strings inside a Dart snapshot that is not plain text on two sides. Searching the binary text for a URL is a starting point. It is not a guarantee.
Runtime
The reliable way to recover a Flutter API is to watch the app talk. Put the app behind a proxy, let it make its real calls, and read each request as it leaves. When a Flutter app refuses the proxy, it usually does so through the platform transport. In that case we attach with Frida at runtime so the app tolerates the capture.
Where the proxy is not enough, we hook the engine socket layer and read the bytes. We are observing the app, not translating its Dart. That is the honest boundary of the work, and it is enough to reproduce the call.
# inbound path: capture request bodies as the app sends them ssl = Module.findExportByName("libflutter.so", "SSL_write"); Interceptor.attach(ssl, { onEnter(args) { this.p = args[1]; this.len = args[2].toInt32(); }, onLeave(ret) { log(hexdump(this.p, { length: ret.toInt32() })); } });
Fig. 02 · the request as the app wrote it, before the server saw it
Where the friction is
When a Flutter app signs requests, the logic lives in Dart or behind a platform channel that calls into native code. We find the value the app computes for each request and reproduce it, rather than copying a signature from a capture.
When the app uses dio or gRPC, the wire format is usually protobuf. The app ships the message schema inside the binary, not as a .proto file. We read the field layout from the bytes and rebuild the schema. The client then serializes the same message the app sends.
Output
We take the flow we observed, put the signing and body back together, and return a program that makes the same call. If the endpoint changes or a value is regenerated, the client keeps working because it builds state the way the app does instead of replaying a captured request.
# rebuilds the signing the Dart layer performs per call ts = str(int(time.time())) body = proto.Encode(Feed(limit=24, cursor=cursor)) sig = hmac.new(key, f"{ts}:{len(body)}", sha256).hexdigest() r = requests.post( host + "/v1/feed", data=body, headers={"X-Time": ts, "X-Signature": sig, "Content-Type": "application/x-protobuf"}, )
Fig. 03 · the observed Flutter flow, now callable from Python
The JS bundle is readable. The endpoint is the part you rebuild.
The values an app computes before the request leaves the device.
Send us the app
Send the full APK. We return the complete callable API in Python, JavaScript/TypeScript and Postman, with full API documentation.
Projects start at $120. Most are delivered in 24 to 72 hours.
Python, JavaScript/TypeScript, Postman and complete API documentation cover the same full endpoint set. Your team runs the clients in its own server or system.
The delivery window starts after we receive the APK and any account access needed to run it. The fixed quote states the deadline. Most projects finish sooner.
The package includes signing, encryption, decryption and session handling. We verify device-bound keys and server integrity checks during review and document runtime requirements before you commit.
Report a defect within 30 days of delivery. We fix any delivered call that does not match the tested APK at no extra cost.