Sunday, January 23, 2011

anti-cracking example

there are only a handful of anti-cracking techniques for android, at least that i know of. this seems to be because of how android apps must behave. they must operate inside their own virtual machine and are not allowed to store permenent data. when they are uninstalled, they must remove all traces of themselves. this makes things like timed trials difficult to implement because a simple reinstall will reset timers.

if an app wants to prevent cracking, it only has a few options. it can check file properties of the apk such as file size, last modified date and signature. it could also check the installer package name to test if it was installed via market. a cracker or pirate would install via adb, not market. antilvl can already hook and subvert signature checks. file size, last modified and installer package hooking are in the works. :D

this is what a installer package check may look like in java:
private boolean InstalledFromMarketEasy() {
  String pname = this.getPackageName();
  PackageManager pm = this.getPackageManager();
  String installPM = pm.getInstallerPackageName(pname);
 
  if ( installPM == null ) {
    // Definitely not installed from Android Market
    return false;
  }
  else if ( installPM.equals("com.google.android.feedback") ) {
    // Installed from the Android Market
    return true;
  }

  return false;
}


but there's another way to perform any check in a more clever way that makes detection more difficult... by using java reflection. instead of calling the method directly, the app could use reflection to store the method in a variable and call it later indirectly. this makes searching for the direct method call difficult. it also allows the method name to be obfuscated since it is just a strong. let me show you in code what it may look like:
const-class v1, Landroid/content/pm/PackageManager;

# do not assume this will not always be a plain text
# it could be put together in a much more complicated way such as building character by character using ascii codes
# you'll have to hook reflection methods to test the name or figure it out manually
const-string v2, "getInstallerPackageName"

# make array of size 1
const/4 v3, 0x1
new-array v3, v3, [Ljava/lang/Class;

const/4 v4, 0x0
const-class v5, Ljava/lang/String;

# store String class in array v3, at index v4 (0)
aput-object v5, v3, v4

# get method of name v2 ("getInstallerPackageName") and move to v0
# http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#getMethod(java.lang.String, java.lang.Class...)
invoke-virtual {v1, v2, v3}, Ljava/lang/Class;->getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
move-result-object v0

# do something with v0, like store in class variable
# ....
# and then sometime later, somewhere else in code

# get the package manager and store in v1
invoke-virtual {p0}, Lcom/clever/app/Main;->getPackageManager()Landroid/content/pm/PackageManager;
move-result-object v1

const/4 v2, 0x1
new-array v2, v2, [Ljava/lang/Object;

const/4 v3, 0x0
invoke-virtual {p0}, Lcom/clever/app/Main;->getPackageName()Ljava/lang/String;
move-result-object v4

# put package name in array v2 at index v3 (0)
aput-object v4, v2, v3

# actually call "getInstallerPackageName" with the array we built as parameters
# which contains this package name
# http://download.oracle.com/javase/1.5.0/docs/api/java/lang/reflect/Method.html#invoke%28java.lang.Object,(java.lang.Object...)
invoke-virtual {v0, v1, v2}, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
move-result-object v0
check-cast v0, Ljava/lang/String;

# clever to not use any type of full string, just "goog"
# remember the full string is expected to be "com.google.android.feedback"
const-string v1, "goog"
invoke-virtual {v0, v1}, Ljava/lang/String;->indexOf(Ljava/lang/String;)I
move-result v0

# check to see if v0 has the correct value. if not... error :D

the next lesson in way of the android cracker will cover all anti-cracking techniques i know in detail, and antilvl will support these types of checks soon, but this is enough for anyone to figure it out. :D

for some more information on reflection:
Using Java Reflection
Java programming dynamics, Part 2: Introducing reflection

2 comments :

  1. Hi,

    For me, pm.getInstallerPackageName(pname) always returns null... And I don't understand why you check if installPM.equals("com.google.android.feedback") to know if the app is installed from the Android Market. Indeed, what we want to know is if the name of the apk file has been changed, not if the package name has been changed (has no reason to change). I would appreciate if you could do some clarification and send an email to me once you have updated this article. Thanks.

    ReplyDelete
  2. Regis, i will answer here for all. this method is, of course, a partial solution, at best. some phones always show null, and the market name is different depending on what market was used.

    the apk is given a name automatically by the system based on the package name, so checking to see if it has been renamed is not possible. it's also not possible to check at install time.

    you could always do size/crc/md5 checks of classes.dex, which i plan to make a post about.

    ReplyDelete

Do NOT post about or link to specific apps!