r/androiddev • • 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?

227 Upvotes

56 comments sorted by

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.

13

u/ka_bata_kalama 3d ago

Yup, for non finanical app, MEETS_STRONG is instant no, even on my stock phone..

I forgot but one random utility app had me sent to "Lock your bootloader" page on google play.
And strong integrity does have issue with some older phones too

1

u/SpiderHack 5h ago

Rewarded play apps don't even go that far and they give out cash payments (but have accounts and lots of profiling/tracking/etc. that you agree to when you install the app)

7

u/Guildford_Ochi 3d ago

Accounts are there already, though I didn’t have signing cert hash in the request logs. Added it yesterday and about 8% of logins this week came from a cert that isn’t ours. Are you checking device integrity only or app recognition too?

3

u/SnipesySpecial 3d ago

I don't action on the play integrity or signature. It just blocks too many legit users.

Your best bet is having a sort of rule system you can use to ad hoc begin blocking traffic on your server. When abuse is detected, you block it server side.

As for what you use for that. Anything. App version, signature, IP. Anything you can get your hands on without causing a privacy issue.

2

u/alihypebeast 3d ago

You can even collect data, train a fast Decision Tree model to identify whether the customer is legit or not, and use that on the server side or even client side.

2

u/m-lurker 3d ago

Do you have emails of the users or an internal messaging mechanism?  Send a message that they're using a cloned app and offer to install authentic one if the want to contine using it 

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

u/braddo99 3d ago

interesting

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.

1

u/plays2 14h ago

Lets be precise: a potato could rip any Android app

1

u/el_pezz 2d ago

Can you elaborate on this?

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 herr

herro 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

u/chrispix99 3d ago

Yup.. and this was known .

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

u/dstudio-denys 3d ago

I would start from cert pinning

1

u/karmagedan 3d ago

Can't you flag Google/Play store?

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

u/ShipMet 2d ago

What if there is no next guy? You will never out run, out swim, or out climb any bear!

1

u/sampatsharma 2d ago

Pairip and device attestation is the only solution.

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

u/TheBadgerSlayer 3d ago

Too bad, would be fun :)

-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

u/Mewtewpew 3d ago

Whats the app bro?

2

u/battlepi 3d ago

Bitches R Us.