Sunday, January 30, 2011

quick antilvl fix

got overeager with antilvl's new found powers and released without doing some extra bug testing. just uploaded a less broken version just now. download links haven't changed.

antilvl 1.1

finished antilvl 1.1. it's kind of scary how well it's working. now that the anti-cracking methods are added in, it should be able to crack the android license verification library and any type of protection i have ever seen, including a few i haven't.

the number of modifications done to an app has increased a bit, so there may be some mix up. let me know how it works for you.

here's the main gist of the changes:
  • introduced many anti-cracking bypassing methods. it's better than me!
  • improved --sign-only behavior, though it still errors every other time
  • fixed issue with modification's sometimes being done improperly
  • several under the hood improvements for future features
 you can download it here: http://androidcracking.blogspot.com/p/antilvl_01.html

next up i intend to tackle slidelock protection and api key requirements.

Wednesday, January 26, 2011

smali syntax highlighting for syntaxhighlighter

Update: this is now being maintained by someone else here: https://github.com/CalebFenton/syntaxhighlighter-smali

to facilitate the eye-pleasing posting of smali on blogs and html and such, here is a brush for syntaxhighlighter. i'm in the process of converting my previous tutorials to html and they use this instead of screen shots of code or simple green coloring. it looks much nicer.

/**
 * shBrushSmali.js
 *
 * For SyntaxHighlighter
 * http://alexgorbatchev.com/SyntaxHighlighter
 *
 * @version
 * 1.0.1 (November 10 2011)
 *
 * By lohan+
 */
 
;(function()
{
 // CommonJS
 typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;

 function Brush()
 {
 
  // Must put these in reverse order or else 'const' will catch but 'const/4' will not
  var funcs = 
   'xor-long/2addr xor-long xor-int/lit8 xor-int/lit16 xor-int/2addr xor-int'
   + ' ushr-long/2addr ushr-long ushr-int/lit8 ushr-int/2addr ushr-int throw throw-verification-error'
   + ' sub-long/2addr sub-long sub-int/lit8 sub-int/lit16 sub-int/2addr sub-int'
   + ' sub-float/2addr sub-float sub-double/2addr sub-double sput-wide sput-short'
   + ' sput-object sput-char sput-byte sput-boolean sput sparse-switch shr-long/2addr'
   + ' shr-long shr-int/lit8 shr-int/2addr shr-int shl-long/2addr shl-long shl-int/lit8'
   + ' shl-int/2addr shl-int sget-wide sget-short sget-object sget-char sget-byte'
   + ' sget-boolean sget return-wide return-void return-object return rem-long/2addr'
   + ' rem-long rem-int/lit8 rem-int/lit16 rem-int/2addr rem-int rem-float/2addr'
   + ' rem-float rem-double/2addr rem-double rsub-int rsub-int/lit8'
   + ' or-long/2addr or-long or-int/lit8'
   + ' or-int/lit16 or-int/2addr or-int not-long not-int nop new-instance new-array'
   + ' neg-long neg-int neg-float neg-double mul-long/2addr mul-long mul-int/lit16'
   + ' mul-int/lit8 mul-int/2addr mul-int mul-float/2addr mul-float mul-double/2addr'
   + ' mul-double move/from16 move/16 move-wide/from16 move-wide/16 move-wide'
   + ' move-result-wide move-result-object move-result move-object/from16 move-object/16'
   + ' move-object move-exception move monitor-exit monitor-enter long-to-int'
   + ' long-to-float long-to-double iput-wide-quick iput-wide iput-short iput-quick'
   + ' iput-object-quick iput-object iput-char iput-byte iput-boolean iput'
   + ' invoke-virtual/range invoke-virtual-quick/range invoke-virtual-quick'
   + ' invoke-virtual invoke-super/range invoke-super-quick/range invoke-super-quick'
   + ' invoke-super invoke-static/range invoke-static invoke-interface/range'
   + ' invoke-interface invoke-direct/range invoke-direct-empty invoke-direct'
   + ' int-to-short int-to-long int-to-float int-to-double int-to-char int-to-byte'
   + ' instance-of iget-wide-quick iget-wide iget-short iget-quick'
   + ' iget-object-quick iget-object iget-char iget-byte iget-boolean iget if-nez'
   + ' if-ne if-ltz if-lt if-lez if-le if-gtz if-gt if-gez if-ge if-eqz if-eq goto/32'
   + ' goto/16 goto float-to-long float-to-int float-to-double filled-new-array/range'
   + ' filled-new-array fill-array-data execute-inline double-to-long double-to-int'
   + ' double-to-float div-long/2addr div-long div-int/lit8 div-int/lit16 div-int/2addr'
   + ' div-int div-float/2addr div-float div-double/2addr div-double const/high16 const/4'
   + ' const/16 const-wide/high16 const-wide/32 const-wide/16 const-wide'
   + ' const-string-jumbo const-string const-class const cmpl-float cmpl-double cmpg-float'
   + ' cmpg-double cmp-long check-cast array-length aput-wide aput-short aput-object'
   + ' aput-char aput-byte aput-boolean aput and-long/2addr and-long and-int/lit8'
   + ' and-int/lit16 and-int/2addr and-int aget-wide aget-short aget-object aget-char'
   + ' aget-byte aget-boolean aget add-long/2addr add-long add-int/lit8 add-int/lit16'
   + ' add-int/2addr add-int add-float/2addr add-float add-double/2addr add-double';

  var directives =
   'annotation array-data catch catchall class end enum epilogue field'
   + ' implements line local locals method .packed-switch parameter prologue'
   + ' registers restart source sparse-switch subannotation super';

  var keywords =
   'abstract annotation bridge constructor declared-synchronized enum final'
   + ' interface native private protected public static strictfp synchronized synthetic'
   + ' system transient varargs volatile';

  this.regexList = [
   { regex: new RegExp('#[^!].*$', 'gm'),          css: 'comments' }
   , { regex: SyntaxHighlighter.regexLib.doubleQuotedString,css: 'string' }
   , { regex: new RegExp('[vp]\\d{1,2}', 'g'),        css: 'variable' }
   , { regex: new RegExp(this.getKeywords(funcs), 'gmi'),  css: 'functions' }
   , { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }
   , { regex: new RegExp('0x[0-9a-f]+', 'gi'),        css: 'constants' }
   , { regex: new RegExp(this.getKeywords(directives), 'gm'),css: 'value' }
   , { regex: new RegExp('L[\\w|/]+;', 'gm'),        css: 'color2' }
   , { regex: new RegExp('>\\w+\\S*\\(\\S*\\)', 'gm'),  css: 'color3' }
   , { regex: new RegExp('\\w+:[IZJ]', 'gm'),        css: 'color1' }
  ];

  this.forHtmlScript(SyntaxHighlighter.regexLib.phpScriptTags);
 }

 Brush.prototype = new SyntaxHighlighter.Highlighter();
 Brush.aliases  = ['smali', 'Smali'];

 SyntaxHighlighter.brushes.Smali = Brush;

 // CommonJS
 typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
})();

Tuesday, January 25, 2011

why wont apk tool recompile?

if you are getting strange errors related to resources. for example:
W: Could not find sources
I: Checking whether resources has changed...
I: Building resources...
C:\someapp\res\values\public.xml:2395: error: Public entry identifier 0x107001d entry index is larger than available symbols (index 29, total symbols 29).
C:\someapp\res\values\public.xml:2395: error: Public symbol array/carrier_properties declared here is not defined.
Exception in thread "main" brut.androlib.AndrolibException: brut.common.BrutException: could not exec command: [aapt, p, -F, C:\Users\Soontir\AppData\Local\Temp\APKTOOL5080969896487671571.tmp, -x, -S, C:\someapp\res, -M, C:\framework\framework-res1\AndroidManifest.xml]
        at brut.androlib.res.AndrolibResources.aaptPackage(Unknown Source)
        at brut.androlib.Androlib.buildResourcesFull(Unknown Source)
        at brut.androlib.Androlib.buildResources(Unknown Source)
        at brut.androlib.Androlib.build(Unknown Source)
        at brut.androlib.Androlib.build(Unknown Source)
        at brut.apktool.Main.cmdBuild(Unknown Source)
        at brut.apktool.Main.main(Unknown Source)
Caused by: brut.common.BrutException: could not exec command: [aapt, p, -F, C:\Users\Soontir\AppData\Local\Temp\APKTOOL5080969896487671571.tmp, -x, -S, C:\framework\framework-res1\res, -M, C:\someapp\AndroidManifest.xml]
        at brut.util.OS.exec(Unknown Source)
        ... 7 more
the problem could be non-standard resource qualifiers. it affects current versions of apktool (1.3.2), but may be fixed in a later version. the solution is to decomile using --keep-broken-res and fix the xml file manually. for an explanation of how to do this, read this issue by the developer himself: http://code.google.com/p/android-apktool/issues/detail?id=128#c1

why wont google maps work after resigning or recompiling?

this is because access to google's stuff requires an api key, stored in xml as a resource or perhaps in code, that is linked with the certificate of the app. if you modified and resigned it, the certificate is different and will not match the api key. you will have to get a valid key for the signature you are using.

you should already have your own certificate you sign everything with. this way if you release an updated version of the app, users of your previous modified version can update without needing to uninstall the old one first. if you don't already have your own certificate, follow the directions here: Signing Your Applications | Android Developers. you will need to use keytool and examples are given in the link.

once you're done or if you already have your own certificate, follow the instructions here: Obtaining a Maps API Key - Google Projects for Android.

when finished with that, and you have your own api key, decompile your apk with apktool so you can modify resources. you are looking for an xml file with a string like:
android:apiKey="3BCknOWFD_vyechU238a1f9YOWyYP2Z91CEfQWw"

or if it is created dynamically in code, you will see the initialization of a mapview object. the constructor takes a string as the second parameter which is the api key. here's an example:
# replace p2 with our api key here:
const-string p2, "3BCknOWFD_vyechU238a1f9YOWyYP2Z91CEfQWw"

# create the mapview object with our modified api key
# this line should already be here and doesn't need to be changed
invoke-direct {p0, p1, p2}, Lcom/google/android/maps/MapView;-><init>(Landroid/content/Context;Ljava/lang/String;)V

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

Saturday, January 22, 2011

antilvl 1.0

antilvl 1.0 is finished and ready for action. here are some changes:
  • complete rewrite of previous versions
  • acts more like an engine, with modification information stored in fingerprints.xml
  • many, many more apk files can be decompiled and recompiled
  • handles several new types of protection methods
  • signature checking, a common anti-tampering technique, is subverted
  • file size / last modification checks are more accurately detected
  • pro / full unlock app protection is correctly handled sometimes
  • much more compact / optimized java bytecode

you can pick it up here: http://androidcracking.blogspot.com/p/antilvl_01.html

doing a total rewrite has opened up many more possibilities for new ideas. the next major thing i plan to implement is complete file size / last modified hooking.