Hidden SDKs in Your App Binary: How to Find What You Did Not Install
8 Apr 2026 · 9 min read · Erik van Kammen
TL;DR: Industry research consistently shows that the majority of code in a typical mobile app comes from third-party libraries and SDKs. Many of those components arrive without explicit developer intent, through transitive dependencies, build tool bundling, and pre-built frameworks. If you don't audit your compiled binary, you don't actually know what you're shipping to users and app stores.
You picked your dependencies carefully. You reviewed each one before adding it to your project. But the binary you submit to the App Store or Google Play contains far more than what you explicitly installed. There are three primary vectors.
When you add a single SDK, you also pull in everything that SDK depends on. Your Podfile might list 10 pods, but Podfile.lock might resolve to 40. On Android, a single Gradle dependency can fan out into dozens of transitive libraries. These sub-dependencies are real code that ships in your binary, with their own permissions, API calls, and data collection behaviors.
Build systems can inject code you never referenced in your source files. Xcode build phases can copy frameworks silently. Gradle plugins can merge manifests and inject classes at compile time. React Native and Flutter toolchains bundle their own runtime libraries. These additions are invisible at the source level but fully present in the compiled output.
Some SDKs distribute as pre-compiled .xcframework or .aar files. You can't inspect their source. They may contain nested dependencies, telemetry modules, or deprecated API calls that only become visible when you analyze the final binary. What the vendor describes in their documentation and what the binary actually contains are sometimes two different things.
Hidden SDKs create three categories of real-world risk.
Apple requires a Privacy Manifest (PrivacyInfo.xcprivacy) that declares all data collection and required reason APIs used by your app and its SDKs. Google requires accurate Data Safety Section declarations. If a transitive dependency collects device identifiers or contacts an analytics endpoint, and your declarations don't reflect that, you face rejection or removal.
Code you didn't know about can contain known CVEs. A buried transitive dependency with a critical vulnerability expands your attack surface without your awareness. Supply chain attacks specifically target this blind spot because developers trust their direct dependencies without examining the full dependency tree.
Both Apple and Google review the compiled binary, not your source code. If the binary contains restricted APIs, deprecated frameworks, or undeclared tracking SDKs, your app gets rejected. The rejection notice often references code you didn't know was there.
These aren't hypothetical risks. The mobile ecosystem has a documented history of supply chain compromises.
Attackers distributed trojanized Xcode installers hosted on Baidu Yunpan (a Chinese cloud storage service), targeting developers in regions where downloading from Apple's official servers was slow. Apps built with the compromised Xcode injected data-harvesting code into the compiled binary without any visible change to the source project. According to documents surfaced during the Epic v. Apple trial, approximately 128 million users downloaded affected apps. Developers had no idea their toolchain was compromised.
Security firm Dr.Web identified a malicious SDK called SpinOK embedded in 101 Google Play apps with a combined 421 million downloads. The SDK presented itself as a legitimate engagement tool offering mini-games and rewards. In reality, it exfiltrated files from devices and replaced clipboard contents. Developers integrated it intentionally, but its malicious behavior was hidden beneath a functional surface.
Researchers disclosed CVE-2024-38366, rated CVSS 10.0, affecting the CocoaPods trunk server infrastructure. The vulnerability could have allowed attackers to take over pods through the package management system itself. Additionally, approximately 1,870 orphaned pods were identified where the original maintainer's email had lapsed, making them susceptible to account takeover. This wasn't about a single bad SDK. It was about the infrastructure that delivers SDKs to millions of iOS projects.
Security researchers discovered that the Mintegral ad SDK, present in over 1,200 iOS apps, was performing ad attribution fraud by intercepting and replaying competitor ad network calls. The SDK was functioning as intended from the developer's perspective (showing ads and generating revenue), while silently hijacking attribution data in the background.
Check the Frameworks directory. After building your IPA, expand it and inspect the Frameworks/ folder. Every .framework bundle in there ships with your app. Compare this list against your known direct dependencies. Anything unexpected deserves investigation.
Review Podfile.lock or Package.resolved. These lockfiles show the full resolved dependency tree, including transitive dependencies. Run pod outdated or check Swift Package Manager resolution to identify unexpected entries.
Use the Privacy Report. In Xcode 15+, build your app and generate a Privacy Report. This surfaces all Privacy Manifest declarations across your app and its embedded frameworks, showing which SDKs declare what data collection.
Inspect symbols. Use nm and strings on your compiled binary to identify class names, method selectors, and string literals from SDKs you didn't expect. This low-level inspection reveals what code actually exists in the binary, regardless of what your project configuration says.
Run gradlew dependencies. Execute ./gradlew app:dependencies --configuration releaseRuntimeClasspath to see the full resolved dependency tree for your release build. This reveals every transitive dependency, including version conflict resolutions.
Use APK Analyzer. Android Studio's APK Analyzer (or the command-line apkanalyzer) shows the actual contents of your compiled APK or AAB: DEX files, resources, native libraries, and their sizes. Compare what you see against your expected dependency list.
Check merged manifests. Open app/build/intermediates/merged_manifest/ after a build. This merged AndroidManifest.xml shows permissions and components contributed by all dependencies, not just your own manifest. Permissions you didn't declare might appear here.
Inspect DEX contents. Tools like dexdump or jadx let you decompile DEX files and inspect class hierarchies. Search for package names that don't match your known dependencies to find hidden components.
Manual auditing works but doesn't scale. AppCompliance automates binary-level scanning, identifying embedded SDKs, transitive dependencies, privacy-relevant API usage, and known vulnerabilities directly from your compiled IPA or APK. This turns a multi-hour manual investigation into a continuous, repeatable check.
Use this checklist before every release:
The most effective place to catch hidden SDKs is in your build pipeline, not the week before submission.
Build-time dependency diffing. Add a pipeline step that generates the resolved dependency tree and diffs it against a committed baseline file. Any new or changed dependency triggers a review gate. This catches transitive additions the moment they appear.
Post-build binary analysis. After your CI produces an IPA or APK, run binary analysis as a pipeline step. Flag any new frameworks, unexpected permissions, or unrecognized symbols. Treat new findings the same way you treat failing tests: block the build until reviewed.
Scheduled CVE checks. Dependencies that were clean last month might have new CVEs today. Run vulnerability checks on your resolved dependency list on a schedule, not just at release time. Subscribe to security advisories for your critical dependencies.
Manifest validation. Automate comparison of your privacy declarations against what the binary actually contains. If the binary uses the AdSupport framework but your Privacy Manifest doesn't declare advertising tracking, the pipeline should flag it before Apple does.
It varies widely, but most apps contain two to five times more libraries in their compiled binary than developers explicitly added. A project with 15 direct dependencies might resolve to 50 or more after transitive resolution.
Yes, but carefully. In CocoaPods, you can use pod spec modifications. In Gradle, you can use exclude group directives. In Swift Package Manager, options are more limited. Always test thoroughly after excluding transitive dependencies because the parent SDK may rely on them at runtime.
Both Apple and Google perform automated binary scanning during review. Apple specifically scans for private API usage, known malicious code patterns, and Privacy Manifest completeness. Google Play Protect scans for known malware signatures. However, neither store catches everything, and their scanning focuses on policy violations rather than comprehensive inventory.
At minimum, audit before every release. Ideally, run automated checks on every CI build. Dependencies can change even when your code doesn't, for example when a dependency range resolves to a newer version that pulls in new transitive dependencies.
A lockfile audit shows what your package manager resolved. A binary audit shows what the compiler and linker actually included. These can differ because of build caching, conditional compilation, and framework bundling that happens outside the package manager's scope. Both are valuable; neither is sufficient alone.
Generally, yes. Pre-built binaries (.xcframework, .aar) cannot be source-reviewed before compilation. You trust the vendor's build process entirely. Source-distributed SDKs at least allow inspection before they're compiled into your app, even if few teams actually do that inspection manually.
A well-configured pipeline catches the majority of issues automatically: new dependencies, known CVEs, permission changes, and privacy declaration mismatches. But edge cases like evaluating whether a new transitive dependency's data practices align with your privacy policy still require human judgment. Use automation for detection and humans for decision-making.
Everything technical in this post is checkable from the build itself, and that is exactly what AppCompliance does: upload an iOS or Android build, and minutes later you have every issue that would get it rejected, with the exact fix next to it.
One email when we open the doors. No spam, just the launch.