diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md index 5467eb1..6b08418 100644 --- a/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/references/api-extraction-patterns.md @@ -55,6 +55,65 @@ grep -rn 'Interceptor\|addInterceptor\|addNetworkInterceptor\|intercept(' source grep -rn '\.execute()\|\.enqueue(' sources/ ``` +## Ktor (Kotlin) + +Ktor is the dominant HTTP client in Kotlin Multiplatform and modern +Kotlin-only Android apps. Unlike Retrofit, Ktor does **not** use annotations +to declare endpoints — paths appear as plain string arguments to +`client.get(...)` / `client.post(...)`, often inside an extension function. + +```bash +# Calls +grep -rn '\b\(client\|httpClient\|HttpClient\)\.\(get\|post\|put\|delete\|patch\|head\|request\)\s*[<(]' sources/ + +# Default request / base URL configuration +grep -rn 'HttpRequestBuilder\|defaultRequest\s*{\|\burl\s*(\s*"\|URLBuilder' sources/ + +# Auth plugin (bearer / refresh) +grep -rn '\bbearer\s*{\|BearerTokens\s*(\|loadTokens\s*{\|refreshTokens\s*{' sources/ +``` + +Typical Ktor call (after decompile): + +```java +client.get("api/v1/users/profile") { + parameter("locale", "en-US"); +} +``` + +The base URL is usually applied via `defaultRequest { url { host = "..." } }` +in the client builder. Search for `host =` and `URLProtocol.HTTPS` references +to pin it down. + +**Note on obfuscation:** in heavily R8-shrunk apps the call site +`client.get("path")` is inlined to something like `aVar.a(dVar, "path")` +and the `client.(` regex misses it. The path string itself is **not** +obfuscated, however — fall back to the generic path-literal search +(`--paths`) for the endpoint inventory in those cases. Ktor library +internals (`BearerTokens`, `loadTokens`, `refreshTokens`, `URLProtocol`) +remain searchable because Ktor keeps these on its public API. + +Ktor's authentication plugin uses the +[`Auth { bearer { loadTokens { ... }; refreshTokens { ... } } }`](https://ktor.io/docs/auth.html) +DSL — bearer access tokens with automatic refresh. After R8, the DSL +lambdas appear as `Function2`/`Function3` impls referencing +`BearerTokens(...)` calls. + +## Apollo Kotlin (GraphQL) + +```bash +# Client setup +grep -rn 'ApolloClient\|\.serverUrl(\|HttpNetworkTransport' sources/ + +# Operations (queries / mutations / subscriptions) +grep -rn '\.query(\s*[A-Z]\|\.mutation(\s*[A-Z]\|\.subscription(\s*[A-Z]' sources/ +``` + +Apollo generates one class per operation under a generated package; once you +find the GraphQL endpoint URL via `ApolloClient.serverUrl("...")`, use the +operation classes themselves as the API documentation — each carries its +GraphQL document text in `OPERATION_DOCUMENT`. + ## Volley ```bash diff --git a/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh index db52acf..db01038 100755 --- a/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh +++ b/plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh @@ -14,6 +14,8 @@ Arguments: Options: --retrofit Search only for Retrofit annotations --okhttp Search only for OkHttp patterns + --ktor Search only for Ktor client patterns + --apollo Search only for Apollo (GraphQL) patterns --volley Search only for Volley patterns --urls Search only for hardcoded URLs --auth Search only for auth-related patterns @@ -29,6 +31,8 @@ EOF SOURCE_DIR="" SEARCH_RETROFIT=false SEARCH_OKHTTP=false +SEARCH_KTOR=false +SEARCH_APOLLO=false SEARCH_VOLLEY=false SEARCH_URLS=false SEARCH_AUTH=false @@ -38,6 +42,8 @@ while [[ $# -gt 0 ]]; do case "$1" in --retrofit) SEARCH_RETROFIT=true; SEARCH_ALL=false; shift ;; --okhttp) SEARCH_OKHTTP=true; SEARCH_ALL=false; shift ;; + --ktor) SEARCH_KTOR=true; SEARCH_ALL=false; shift ;; + --apollo) SEARCH_APOLLO=true; SEARCH_ALL=false; shift ;; --volley) SEARCH_VOLLEY=true; SEARCH_ALL=false; shift ;; --urls) SEARCH_URLS=true; SEARCH_ALL=false; shift ;; --auth) SEARCH_AUTH=true; SEARCH_ALL=false; shift ;; @@ -90,6 +96,27 @@ if [[ "$SEARCH_ALL" == true || "$SEARCH_OKHTTP" == true ]]; then run_grep '(\.url\s*\(|\.addQueryParameter|\.addPathSegment|\.scheme\s*\(|\.host\s*\()' fi +# --- Ktor (Kotlin) --- +# Ktor doesn't use annotations. Endpoints appear as string args to +# client.get/post/etc., or are built via HttpRequestBuilder.url(...). Auth +# is configured via the bearer { loadTokens / refreshTokens } DSL. +if [[ "$SEARCH_ALL" == true || "$SEARCH_KTOR" == true ]]; then + section "Ktor — Client Calls" + run_grep '\b(client|httpClient|HttpClient)\.(get|post|put|delete|patch|head|request)\s*[<(]' + section "Ktor — Request Building / Default Request" + run_grep '(HttpRequestBuilder|defaultRequest\s*\{|\burl\s*\(\s*"|URLBuilder|URLProtocol)' + section "Ktor — Auth Plugin (Bearer / Refresh)" + run_grep '(\bbearer\s*\{|BearerTokens\s*\(|loadTokens\s*\{|refreshTokens\s*\{|\bAuth\s*\)\s*\{)' +fi + +# --- Apollo (GraphQL) --- +if [[ "$SEARCH_ALL" == true || "$SEARCH_APOLLO" == true ]]; then + section "Apollo — GraphQL Client" + run_grep '(ApolloClient|\.serverUrl\s*\(|\.subscriptionNetworkTransport|HttpNetworkTransport)' + section "Apollo — Operations" + run_grep '(\.query\s*\(\s*[A-Z]|\.mutation\s*\(\s*[A-Z]|\.subscription\s*\(\s*[A-Z])' +fi + # --- Volley --- if [[ "$SEARCH_ALL" == true || "$SEARCH_VOLLEY" == true ]]; then section "Volley Requests"