[Java for Android App Development] Quinny899:
There's a few easy methods anyone could use to crack the protection of your app that you worked very hard on, and in the same way there's methods to stop this from happening as well

The first one, the big one, there's the app "Lucky Patcher". What this app does is patches the dalvik files to tell the app that it's activated, even if the Play Store disagrees. There's two ways of protecting from this:

Implement a simple piece of code to check if Lucky Patcher is installed, and if it is, force the user to uninstall it (But by then it might be too late!)

Here's a sample piece of code that stops the user from opening the app if Lucky Patcher is installed and prompts them to uninstall it

public void checkLP(){
android.content.pm.PackageManager mPm = getPackageManager();
try {
PackageInfo info = mPm.getPackageInfo("com.chelpus.lackypatch", 0);
if(info != null){
AlertDialog.Builder ad3 = new AlertDialog.Builder(this);

ad3.setCancelable(false);
ad3.setTitle("Lucky Patcher");
ad3.setMessage("I have detected the presense of the app 'Lucky Patcher', which could be used maliciously within this app. You need to uninstall it to continue");
ad3.setPositiveButton("OK", new DialogInterface.OnClickListener() {

[MENTION=439709]override[/MENTION]
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
startActivity(new Intent(MainActivity.this, LpUninstallActivity.class));
finish();
}});

AlertDialog alertDialog3 = ad3.create();
alertDialog3.show();
}
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
return;
}

}

Once you've implemented this code, call checkLP(); in your code where you need it, and add a UninstallLpActivity.class to respond to the user pressing OK, which uninstalls it (automatically if you have root, manually if you don't) and then returns the user to the main activity, at which point it checks again

However, this will not always work. What happens if the user patches it and then uninstalls Lucky Patcher? What then? What about if they patched the apk itself?

That's where method 2 comes in.

For method 2, the alternative is to download an unpatched version of your app from the internet and install it on top, either automatically if you have root (Which is recommended where possible) or manually, which could lead to you hitting issues with signatures

I don't have the code for this one, but the best way is with RootTools to call a download normally and then use "pm install -r" to overwrite it. Note that Lucky Patcher also has a method that adds ODEX files to /data/app/ which you will want to remove also

But I don't have a paid version, only IAPs and people are using Freedom! :crying:

Freedom is a complex app that circumvents the Play Store and makes the app think it's been bought when it hasn't. There's two very similar and simple ways to stop Freedom working though, both of which need root (which is fine, because Freedom needs root anyway)

1.) Just stop freedom, kill its service and hopefully stop it from working

Again, I recommend RootTools to make this easier.

When your activity with IAPs starts, call a command that runs the following:

pkill cc.cz.madkite.freedom

This will stop the freedom app from running and hopefully stop the user from using it to crack purchases

2.) The better, more permanent method, forcibly uninstall freedom

Again, I recommend RootTools to make this easier.

In your class with IAPs, add the following code:

public void checkFreedom(){
android.content.pm.PackageManager mPm = getPackageManager();
try {
PackageInfo info = mPm.getPackageInfo("cc.cz.madkite.freedom", 0);
if(info != null){
AlertDialog.Builder ad3 = new AlertDialog.Builder(this);

ad3.setCancelable(false);
ad3.setTitle("Freedom");
ad3.setMessage("I have detected the presense of the app 'Freedom', which could be used maliciously within this section of the app. You need to uninstall it to continue");
ad3.setPositiveButton("OK", new DialogInterface.OnClickListener() {

[MENTION=439709]override[/MENTION]
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
startActivity(new Intent(IapActivity.this, FreedomUninstallActivity.class));
finish();
}});

AlertDialog alertDialog3 = ad3.create();
alertDialog3.show();
}
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
return;
}

}

And then call it where you want to with checkFreedom();

Similar to the Lucky Patcher one, you need a second class that uninstalls it. Mine is as simple as follows:

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;

import com.stericson.RootTools.*;
import com.stericson.RootTools.exceptions.RootDeniedException;
import com.stericson.RootTools.execution.CommandCapture;

public class FreedomUninstallActivity extends Activity{
CheckBox RootCheckBox;
CheckBox BusyboxCheckBox;

[MENTION=439709]override[/MENTION]
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ProgressDialog dialog =
ProgressDialog.show(FreedomUninstallActivity.this, "", "Uninstalling Freedom...", true);
dialog.setCancelable(false);
dialog.show();
dialog.setMessage("Uninstalling Freedom...");
CommandCapture command = new CommandCapture(0, "pm uninstall cc.cz.madkite.freedom");

try {
RootTools.getShell(true).add(command).waitForFinish();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RootDeniedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

startActivity(new Intent(this, IapActivity.class));
finish();

}

}

This uses root to uninstall it, which is easiest because the user cannot press cancel, then loops back around to check again to make sure it worked

Finally, and most importantly, obfuscate.

Even the biggest pirates I've seen haven't ever tried to crack apps that use other methods and are obfuscated. Therefore, best practice where possible is to obfuscate, or even just run it from a remote server on a secure connection. ProGuard instructions are available here

Help! They still get past it

Use the good old methods of reporting then, try and keep the amount of people who are able to download it illegitimately to a minimum

Further ideas:

Improve the reinstall because of Lucky Patcher by just re-building the dex file - Looking into it

Further reading:

Android Developers site on best practices for in app billing