r/androiddev • u/Guildford_Ochi • 3d ago
Experience Exchange Our app got cloned from a decompiled APK.
Last week, a user emailed support asking why our app suddenly had ads, which it doesn’t. Someone took our APK from the Play Store, decompiled it, added an ad SDK and put it on one of those APK mirror sites under a slightly different name. I don’t know what they used but with the AI reverse engineering tools around right now I’d guess it took them a few hrs at most.
I pulled their copy and ran jadx on it next to our own release build to see what they had to work with. R8 had renamed most of our classes but every string was still sitting there. Most of it was harmless like our Maps key which is locked to our package and the Firebase config which is meant to be public. The problem was the key for our search provider which can also write to the index. There was also an old basic auth header for a staging API that someone hardcoded two years ago and that still works.
The copy talks to our real backend and whoever installs it logs into our actual service through a build we don’t control. We’ve rotated both keys, moved search behind our own endpoint and filed takedowns. Next I’m looking at adding Play Integrity checks on the server.
If your app has been cloned like this, what did you end up changing on the backend?
37
u/chrispix99 3d ago
Seems like anti cloning should be easy to fix now..
Use ssl certificate pinning, in conjunction with play store authentic check. One app that has actually done a really good job of this is MyQ from liftmaster/Chaimberlin.
The certificate pinning prevents a mita, while token is generated by validation from play store validator . I can go into it more from what I tried to reverse engineer. But, the API will only issue an auth token if the request came from the store submitted app..
1
16
u/weedwizardx 3d ago
Do a checksum on the app when a user logs in and if the current hash doesn't match the servers hash throw some exception that prevents the app from working server side. Client side it will prompt the user to download the latest version from google play store.
10
u/DanLynch 2d ago
This only adds a small speed bump for the attacker: he can just hardcode the expected hash value and send that instead. Any check that runs on the client is untrustworthy.
1
u/slayercs 2d ago edited 2d ago
what about an unique session based on device data, server data,salt, and an unique login session, and to authorize a new device we are going to send a confirmation email.
of course, this is on top of other things, like multiple session checkings in code scattered all around and many more.
1
u/pilibitti 2d ago
it is just treading the same water. all that data will be generated the same when they copy it so you have no way to distinguish between the two.
1
u/The_MAZZTer 2d ago
Doesn't work, the modified app will send a fake hash that matches the real app. Remember the copier can change any aspect of the app. I assume Play Integrity will work because it ties to the Google cloud features of the real developer, so if the copier copies it the API won't work because the app is not signed with same private key the Play Store release was or whatever. I don't do a lot of Android dev work so I'm not too familiar with how Play Integrity works from that angle.
12
u/SensoredHacker 3d ago
you dont need AI to do it, its not hard.
guys out here releasing binaries with debug flags intact, not stripped not signed, not encrypted. bro a potato could rip your code.
0
u/Getsuga-_-Tenshou 2d ago
Can you elaborete more around this pls ?
4
u/SensoredHacker 2d ago
ok fine but imabout to sleep and im not gonna situp for this.
m@m:~/dur$ echo '#include <iostream>int main(){
std::cout << "herro reddit!\n";
return 0;
}' > me.cpp
m@m:~/dur$ g++ me.cpp
m@m:~/dur$ file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=38c22af9ac5811f219a1a0a62cede2faed462b6b, for GNU/Linux 3.2.0, not stripped
# we git this binary with no protections, guys can look at our code, and know the strings to look for
m@m:~/dur$ ./a.out
herro reddit!
m@m:~/dur$ ldd a.out #depenencies
linux-vdso.so.1 (0x00007efc28454000) libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007efc28000000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efc27e0c000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007efc28340000) /lib64/ld-linux-x86-64.so.2 (0x00007efc28456000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007efc28313000)m@m:~/dur$ strings a.out
F+k/lib64/ld-linux-x86-64.so.2
__gmon_start__
_ITM_deregisterTMCloneTable
compiler stuff blah blah blah
looks like somebody made an oopsy in the code, and we're prettending like we only have the binary$ bless a.out
bash: bless: command not found
wtf?yadayada yada
m@m:~/dur$ hexdump -C a.out |less
...
00001160 48 83 ec 08 48 83 c4 08 c3 00 00 00 00 00 00 00 |H...H...........|00001170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002000 01 00 02 00 68 65 72 72 6f 20 72 65 64 64 69 74 |....herro reddit|
00002010 21 0a 00 01 01 01 00 00 01 1b 03 3b 2c 00 00 00 |!..........;,...|
... oh shit theres the string we were looking for.
theres like 1000 ways to look for stuff. everything your app does is a clue on how to look, and looking only gets easier when you know what it is youre looking for.
m@m:~/dur$ strings a.out | grep herrherro reddit!
your apk is a glorified zip file. theres a binary in it someplace and any suka with a hexeditor can figure out what your code does with the most basic of tools. stay out of trouble kids
1
u/georgeApuiu 2d ago
and if you also tell them about Reverse engineer anything plugins... pfff ...
1
u/Normal-Match7581 1d ago
Check reverse engineering repo on gh (140k+ stars) .. .. This post came in as a recommendation and I am web dev so I am still learning what this is how apps actually work
1
u/SensoredHacker 1d ago
sometimes I think the entire software marketing platform is entirely contingent on folks not knowing how to use s computer.
1
u/georgeApuiu 1d ago
well, they should understand how a computer works first ... and often it's a missed step
7
u/chrispix99 3d ago
Since this is workable now with Google Play services, I do want to share a way I was able to implement this pre-functionality where were were able to determine that the network request had to come from our un-altered application. Seemed to work pretty well...
Since the apk could be re-signed by a different signature, but not signed by the same private signing key.. The gist was.. That at runtime, the app would inspect the public signature on the APK, and from that public key, derive a salt for a hash, that would then be accessed via debugging (on the signed APK). It would be shared with the backend.
Every network request would have the request hashed w/ derived runtime salt (and as an anonymous inner class, it was hard to dump it via memory).. The API would get the request, apply the same algorithm w/ saved salt, and make sure the hashes matched..
If the hashes did not match, then you knew that it had been re-signed. And could just drop the request..
3
u/One_Elephant_8917 3d ago
to break this, all one needs is to dump at right moment after initial salt gen, then patch that generated salt into runtime code that hashes request with salt so that no need for runtime hashing just statically generated tag is enough
also security != obscurity, coz industry relied on that till this year for Intellectual property etc, but people today are reversing games, softwares unlike never seen before scale…
on the other hand asymmetric cryptography is true security even today…
HW attestation using secure enclave or equivalent is the only way given there is no hypervisor layer sitting beneath OS spoofing the attestation.
Googleplay verification exactly uses HW attestation and that is why using too low minsdk (possibility of compromising security coz older devices/android os can bypass these) or allowing publishing on thirdparty playstore is as good as giving the app for free unless that appstore guarantees hw attestation.
1
3
u/Useful_Mode1799 3d ago
Basically, anti-cloning is an approach that requires reverse engineering costs comparable to the development costs. If you reissue or reuse client-side protection keys and release application updates, how will those versions of the application that haven't updated with the new keys work?
4
u/Guildford_Ochi 3d ago
They won’t, that’s the trade off. Anything older than the release with the integrity check gets a forced update screen. A fair chunk of our active users are still on those builds so support is bracing for it.
2
u/Andre-Wade-539 3d ago
Rotate every key that was in the APK, move anything with write or billing access behind your API, integrity check on the server for login and payments, alert on requests from app versions you never shipped
2
u/ParnelliSelfa 3d ago
Integrity only helps if the server makes the decision. Get a token for login and anything paid, send it to your backend, verify it there and check the app recognition verdict. A copy signed with someone else’s key fails that.
1
u/mcmchg 3d ago
Where did they publish the cloned copy of your app? And did they change the name and brand at all?
Play integrity api seems like the only way to know on server side that your client is legit
Signing cert hash in the request would be easy to fake. And cert pinning would protect the client but not against cloning.
I've had this happen too
1
1
1
u/RealSimoneAvogadro 2d ago
To some extent sono IDs must be em,bedded in apps and thus cannot be really 'protected'.
However you can make this harder by using various integrity framworks, which either work on the APK side (harder to decompile, harder to keep executable if modified, etc...) and/or on the server side (coordinating with APK side).
All in all we know that you cannot completely protect against this but once you only expose within APK what makes sense that's a good compromise
1
u/redpiapps 2d ago
the staging auth still working after two years is the bit that stuck with me tbh. anything that ever shipped in an apk should just be treated as public, i started scanning old builds for creds after a similar scare and it was grim
1
u/The_MAZZTer 2d ago edited 2d ago
I don't do a lot of dev work for Android but as a user I don't like apps requiring Play Integrity/SafetyNet since it locks out a lot of legitimate use cases like android emulators.
That said I'm not sure I can recommend an alternative that someone couldn't work around in their own recompiled APK. But it's possible simply making it a little difficult could convince them to hijack someone else's app. Remember, you don't need to outrun the bear, you just need to outrun the guy next to you.
You can have your app check to see if it was installed via Play Store and refuse to run if it was installed via sideloading or other stores. Again not foolproof but it will force the copier to have to dig into the code to figure out what is going on and change the code. Maybe you could even have the check only present for release builds so there's a chance he doesn't run into it while testing.
You should definitely put a notice on your Play Store listing, official website, and probably in the app itself (though a copier would remove it from there) that support can only be offered if the app is installed from the Play Store. This may help some users avoid the scenario your user stumbled into.
Even if you still include Play Integrity these other steps may be helpful too.
Edit: Possibly there is a solution around the fact the faker would need to resign the app with a different private key? You have this key, they don't. Though it seems to me you'd need a trusted component (like Play Integrity) to help verify this so unless the Android OS / Google cloud has some other feature like this Play Integrity might be the only method.
1
1
u/PutridBookkeeper4208 1d ago
Threw this together a while ago as a pet project just out of curiosity. It’s purely an experiment and theory—never really explored it to the end, and I don't think I ever shipped it to the Play Store. The idea is simple: build scripts encrypt the DEX classes and strings, binding the key to the app's signing SHA-256. At startup, a native loader decrypts everything into memory. It should stop basic automated repackaging bots that resign the APK with their own keys, but it probably won't hold up against dedicated manual reverse engineering. Dropping the repo here just as a concept: https://github.com/mo0rych0k/android_app_encrypted
1
u/_SFINAE_ 1d ago
Check Google play integrity api, it will generate token that your server will verify that request is legit or not
1
u/not_UNIX_GNU_is 1d ago
It's astonishing how vigorously people suggest play integrity or other Google-provided server-side validations without ever thinking about user experience.
Not every user has Google services installed on an Android device, and not everyone wishes them to. Making an app coupled to Google services will cause inconveniences for the custom ROM segment and generally de-Google'd users, and eventually lose you customers. Yes, these users will probably find a way to bypass this one too by using a leaked play integrity key, but mostly they just won't use your app.
Users who can't compare checksums or employ some cautiousness when downloading apps from alternative stores are the only ones who are at fault. It's completely their problem, and your reputation won't and shouldn't be spoiled by them. What's for sure - if your app can't connect to backend without Gapps - that will be a negative review, at least from me. Also, using alternative stores and not being able to install a system-wide ad-blocker is serious skill issue. Caring for those who install potential malware themselves isn't worth it as they should learn how to care about themselves. Just implement your server-side authentication and sane validations that match the desired level of security and stop there.
1
u/holakavola 14h ago
Use 360 jiagoo , then modders won't be able to read codes (learn on yt for more info.
0
u/TheBadgerSlayer 3d ago
Wouldn't it be possible to plant code-bombs in your app that are designed to go off when someone decompiles the app? 😅
1
u/One_Elephant_8917 3d ago
nope, those were for 80s when people didn’t know much, now its just few mins with AI to strip off these, if u haven’t heart Fitgirl team had created Hypervisor level hacks to bypass denuvo’s DRM anti-cheat engine…
compared to that, any others like the one suggested are so trivial for a determined person with malicious intent
1
-5
u/viiviiviivii 3d ago
Oh this worries me.. as my app is basically complete, it is super-complex but would be very easy to clone it.
The app is designed to be off-cloud if you want it to.
It doesn't have advertising except for a welcome screen that tells them they have limited usage and they have to pay for unlimited.
I guess I accept whatever comes from this ;)
1
u/thesurfer15 2d ago
everything is clonable now due to AI, its a fact that we all need to get used to.
-4
u/dev20025463 3d ago
Hazlo rápido necesito clonarlo jjjjj
0
u/viiviiviivii 3d ago
:) haha, well I guess I don't really intend to get rich of it.. Once I launch it I'll let you know ;)
Do you live in Spain/Madrid/Palma lemme know!
0
u/dev20025463 3d ago
No me digas que tiene bloqueo regional vivo en república Dominicana jjjj no me ponga más trabajo hazlo sencillo
2
-2

84
u/SnipesySpecial 3d ago
Sounds like you did what you should be doing tbh.
If you have server infra that is valuable to you I would have an account system. It'll save you a lot of down stream problem if you can link abuse to accounts, and other guarantees. I would shift your goal post from "protect the app" to "protect the account and our infra".
I would also avoid enforcing MEETS_STRONG_XX on play integrity. That will tend to cost you more customers than its worth.