Wednesday, December 29, 2010

getting apk signature outside of android

one way to circumvent signature checking in an android app is to spoof with the correct signature (usually a hashcode but sometimes a tochar representation). but how do we get the apk signature? well, we could write a program in android to do it, which is pretty simple:
public void WriteSignature(String packageName)
{
 // all of this is fairly well documented
 // if it doesn't work, just search around.
 
 PackageManager pm = this.getPackageManager();
 PackageInfo pi = null;
 try {
  pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
 } catch (NameNotFoundException e1) {
  e1.printStackTrace();
 }
 Signature[] s = pi.signatures;
 
 // you can use toChars or get the hashcode, whatever
 String sig = new String(s[0].toChars());

 try {
  File root = Environment.getExternalStorageDirectory();
  if ( root.canWrite() )
  {
   // toChars is long, so i write it to a file on the external storage
   File f = new File(root, "signature.txt");
   FileWriter fw = new FileWriter(f);
   BufferedWriter out = new BufferedWriter(fw);
   out.write(packageName + "\nSignature: " + sig);
   out.close();
   fw.close();
  }
 } catch (IOException e) {
  e.printStackTrace();
 }
}

but i wanted to be able to get the signature from my computer, and google wasn't helping. so i dug into the android code. then i found this: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.0_r1/android/content/pm/PackageParser.java#442

the packageparser code was exactly what i needed. nothing about it was particularly magical and i could have probably figured it out on my own if i knew java.security better, but here is what i came up with:
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.security.Signature;
import java.security.cert.*;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {

 private static final Object mSync = new Object();
 private static WeakReference<byte[]> mReadBuffer;

 public static void main(String[] args) {
  if (args.length < 1) {
   System.out.println("Usage: java -jar GetAndroidSig.jar <apk/jar>");
   System.exit(-1);
  }

  System.out.println(args[0]);

  String mArchiveSourcePath = args[0];

  WeakReference<byte[]> readBufferRef;
  byte[] readBuffer = null;
  synchronized (mSync) {
   readBufferRef = mReadBuffer;
   if (readBufferRef != null) {
    mReadBuffer = null;
    readBuffer = readBufferRef.get();
   }
   if (readBuffer == null) {
    readBuffer = new byte[8192];
    readBufferRef = new WeakReference<byte[]>(readBuffer);
   }
  }

  try {
   JarFile jarFile = new JarFile(mArchiveSourcePath);
   java.security.cert.Certificate[] certs = null;

   Enumeration entries = jarFile.entries();
   while (entries.hasMoreElements()) {
    JarEntry je = (JarEntry) entries.nextElement();
    if (je.isDirectory()) {
     continue;
    }
    if (je.getName().startsWith("META-INF/")) {
     continue;
    }
    java.security.cert.Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer);
    if (false) {
     System.out.println("File " + mArchiveSourcePath + " entry " + je.getName()
         + ": certs=" + certs + " ("
         + (certs != null ? certs.length : 0) + ")");
    }
    if (localCerts == null) {
     System.err.println("Package has no certificates at entry "
         + je.getName() + "; ignoring!");
     jarFile.close();
     return;
    } else if (certs == null) {
     certs = localCerts;
    } else {
     // Ensure all certificates match.
     for (int i = 0; i < certs.length; i++) {
      boolean found = false;
      for (int j = 0; j < localCerts.length; j++) {
       if (certs[i] != null
           && certs[i].equals(localCerts[j])) {
        found = true;
        break;
       }
      }
      if (!found || certs.length != localCerts.length) {
       System.err.println("Package has mismatched certificates at entry "
           + je.getName() + "; ignoring!");
       jarFile.close();
       return; // false
      }
     }
    }
   }

   jarFile.close();

   synchronized (mSync) {
    mReadBuffer = readBufferRef;
   }

   if (certs != null && certs.length > 0) {
    final int N = certs.length;
    
    for (int i = 0; i < N; i++) {
     String charSig = new String(toChars(certs[i].getEncoded()));
     System.out.println("Cert#: " + i + "  Type:" + certs[i].getType()
      + "\nPublic key: " + certs[i].getPublicKey()
      + "\nHash code: " + certs[i].hashCode()
       + " / 0x" + Integer.toHexString(certs[i].hashCode())
      + "\nTo char: " + charSig);
    }
   } else {
    System.err.println("Package has no certificates; ignoring!");
    return;
   }
  } catch (CertificateEncodingException ex) {
   Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
  } catch (IOException e) {
   System.err.println("Exception reading " + mArchiveSourcePath + "\n" + e);
   return;
  } catch (RuntimeException e) {
   System.err.println("Exception reading " + mArchiveSourcePath + "\n" + e);
   return;
  }
 }

 private static char[] toChars(byte[] mSignature) {
    byte[] sig = mSignature;
    final int N = sig.length;
    final int N2 = N*2;
    char[] text = new char[N2];

    for (int j=0; j<N; j++) {
      byte v = sig[j];
      int d = (v>>4)&0xf;
      text[j*2] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d));
      d = v&0xf;
      text[j*2+1] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d));
    }

    return text;
    }

 private static java.security.cert.Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer) {
  try {
   // We must read the stream for the JarEntry to retrieve
   // its certificates.
   InputStream is = jarFile.getInputStream(je);
   while (is.read(readBuffer, 0, readBuffer.length) != -1) {
    // not using
   }
   is.close();

   return (java.security.cert.Certificate[]) (je != null ? je.getCertificates() : null);
  } catch (IOException e) {
   System.err.println("Exception reading " + je.getName() + " in "
       + jarFile.getName() + ": " + e);
  }
  return null;
 }
}

running it on crackme0 from way of the android cracker 0, produces this:



using it, in dalvik, would look something like this:
# get signatures array
iget-object v0, v0, Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature;

# get element at index v10 in array v0 and store in v0
aget-object v0, v0, v10
 
# call Signature.hashCode() on v0, returns an integer
invoke-virtual {v0}, Landroid/content/pm/Signature;->hashCode()I
move-result v9

# spoof original signature hashcode. grin smugly.
const/16 v9, 0x58404

43 comments :

  1. Amazing! I was looking for it!
    I have some doubt:

    If an apk returns more the one certificate? What is the really certificate that signs the apk? Do you already code something for this?

    Thank you very much!

    ReplyDelete
  2. Célio,
    if multiple certificates are returned then the apk was signed with multiple certificates. they are all valid. you can read a little more about that here:
    http://stackoverflow.com/questions/2682105/sign-application-with-several-certificates

    the above code, which is borrowed mainly from the PackageParser class in android, has a loop at around line 97 which handles the possibility of multiple certificates.

    ReplyDelete
  3. we both learned something. i had wondered about this before, but never bothered looking into it. your question helped me learn, so thanks for asking. :D

    ReplyDelete
  4. Hello?
    How to extract the signature of the APK?
    And how to re-sign the APK on the extracted signatures?
    Would you approve of this tell you?

    ReplyDelete
  5. Anonymous, your question does not make sense and, forgive me for presuming, reveals a lack of understanding on what, exactly, signatures are and how they work. To get the signature of an Apk, use the code I posted above. You cannot sign an Apk with that signature unless you have the private key.

    ReplyDelete
  6. Great!!
    but... can i have a question?
    In the last line of this post, i don't know what 'in dalvik' points.
    is it a app? or dalvik machine itself??
    plz give me more example....

    ReplyDelete
  7. Hello,

    Thanks For This Wonderful Article!
    Ok So Now, We Got The Hash Code Of An APK File, I Can't Get How Can We Spoof It After That If I Edited An APK?

    ReplyDelete
  8. That's great!
    But.. I have a question.
    Can I calculate the signature depends on the files in META-INF folder instead of using PackageManager in android?

    Thanks

    ReplyDelete
  9. your question does not compute. you ask for what the source code above does. please correct if i am wrong.

    ReplyDelete
  10. I am really confused, the name of the game is the `key` file: how does this get me the `key` file from an apk? I am not a newbie but java is not my main (in my usage) my "java" is built for me basically by eclipse, that's all I have ever used: wham bam thank you mam. So how do I run this DOS> "java -jar" code windows style (i.e. how does it ratify all those import statements etc etc) similarly how do I parse the dos printed output to `key` a file?

    ReplyDelete
    Replies
    1. Cryptographic signatures are specifically designed such that you can't get the private key. All this does is get the signature hash from outside of Android, which you need to compute if you want to spoof it later, after you've resigned the apk.

      If you're using Eclipse, it's easy. Copy the java code. Create a java project. Edit -> Paste, and it will automagically create a new class with the code. Run that class with the arguments you want, or make a jar from there.

      Delete
  11. Just use apkmanager v6 it can doo soo much! Just google for it

    ReplyDelete
  12. i not understand step step to use this. is really i can get again my key? for now i forget my key. so i can not to update my aplication on google play store because forget key. please can you release step by step to get my key?

    ReplyDelete
    Replies
    1. This won't get your key. It will get a hash of the signature which was generated from your private key. Sorry for your loss.

      Delete
  13. Great find! First to all asking, you can not get the certificate file with this (and hopefully any other method) except maybe brute forcing random certificates until one gives you same hashcode but this is next to impossible. To the writer of the article, could you please explain or link to some answer on how do you do "circumvent signature checking" part ?

    ReplyDelete
    Replies
    1. I thought it would be too obvious. You just add the code to provide the spoofed value. What's the hold up? antilvl does this already.

      Delete
  14. Maybe a dumb question but lets say the smali codes looks like this:

    invoke-virtual (v3), Landroid/content/pm/Signature;->toByteArray()B
    move-result-object v4

    How would the result in v4 have to be spoofed?

    ReplyDelete
    Replies
    1. What I would do is write java with a public static method that returns the byte array I want (i.e. the original). Then compile the java to smali with javac and dx. Then add that method somewhere, and call it in place of Signature;->toByteArray()B. You could also have a public static field and access that. It's just code.

      Delete
  15. Hi Lohan,

    I uploaded an app and now lost the original key. I want to update the app now. Can your trick work for me? Can i now trick google into thinking this APK has the correct signature. Can we just replace the hash code/fingerprint from old keystore to new keystore. I found the fingerprint from CERT.RSA file.

    Please help. Thanks

    ReplyDelete
  16. where can i get GetAndroidsig.jar :|
    sorry but i cant understand where

    ReplyDelete
    Replies
    1. there's no jar file available. read the post, take the code, compile it yourself.

      Delete
  17. Lohan, I'd like to spoof a signature in an app. The app creates a signature from a string using the strings below. I already patched the smali code and removed the signature check programmatically (works well), but I'd prefer to spoof the signature in string format, because it is easier to patch a string with automated patching tools.

    Question:
    How can I convert my own signature to that string format? I did a Google research but could not find any hints to get my signature to that format.

    Thanks for any help!


    The lazy string seems to be a substring of the main signature

    String signatureLayz = "0\u0082\u0002\u00a70\u0082\u0002e\u00a0\u0003\u0002\u0001\u0002\u0002\u0004P\u0005|B0\u000b\u0006\u0007*\u0086";

    String signature = "0\u0082\u0002\u00a70\u0082\u0002e\u00a0\u0003\u0002\u0001\u0002\u0002\u0004P\u0005|B0\u000b\u0006\u0007*\u0086H\u00ce8\u0004\u0003\u0005\u0000071\u000b0\t\u0006\u0003U\u0004\u0006\u0013\u0002US1\u00100\u000e\u0006\u0003U\u0004\n\u0013\u0007Android1\u00160\u0014\u0006\u0003U\u0004\u0003\u0013\rAndroid Debug0\u001e\u0017\r120717145250Z\u0017\r220715145250Z071\u000b0\t\u0006\u0003U\u0004\u0006\u0013\u0002US1\u00100\u000e\u0006\u0003U\u0004\n\u0013\u0007Android1\u00160\u0014\u0006\u0003U\u0004\u0003\u0013\rAndroid Debug0\u0082\u0001\u00b70\u0082\u0001,\u0006\u0007*\u0086H\u00ce8\u0004\u00010\u0082\u0001\u001f\u0002\u0081\u0081\u0000\u00fd\u007fS\u0081\u001du\u0012)R\u00dfJ\u009c.\u00ec\u00e4\u00e7\u00f6\u0011\u00b7R<\u00efD\u0000\u00c3\u001e?\u0080\u00b6Q&iE]@\"Q\u00fbY=\u008dX\u00fa\u00bf\u00c5\u00f5\u00ba0\u00f6\u00cb\u009bUl\u00d7\u0081;\u0080\u001d4o\u00f2f`\u00b7k\u0099P\u00a5\u00a4\u009f\u009f\u00e8\u0004{\u0010\"\u00c2O\u00bb\u00a9\u00d7\u00fe\u00b7\u00c6\u001b\u00f8;W\u00e7\u00c6\u00a8\u00a6\u0015\u000f\u0004\u00fb\u0083\u00f6\u00d3\u00c5\u001e\u00c3\u00025T\u0013Z\u0016\u00912\u00f6u\u00f3\u00ae+a\u00d7*\u00ef\u00f2\"\u0003\u0019\u009d\u00d1H\u0001\u00c7\u0002\u0015\u0000\u0097`P\u008f\u0015#\u000b\u00cc\u00b2\u0092\u00b9\u0082\u00a2\u00eb\u0084\u000b\u00f0X\u001c\u00f5\u0002\u0081\u0081\u0000\u00f7\u00e1\u00a0\u0085\u00d6\u009b=\u00de\u00cb\u00bc\u00ab\\6\u00b8W\u00b9y\u0094\u00af\u00bb\u00fa:\u00ea\u0082\u00f9WL\u000b=\u0007\u0082gQYW\u008e\u00ba\u00d4YO\u00e6q\u0007\u0010\u0081\u0080\u00b4I\u0016q#\u00e8L(\u0016\u0013\u00b7\u00cf\t2\u008c\u00c8\u00a6\u00e1<\u0016z\u008bT|\u008d(\u00e0\u00a3\u00ae\u001e+\u00b3\u00a6u\u0091n\u00a3\u007f\u000b\u00fa!5b\u00f1\u00fbbz\u0001$;\u00cc\u00a4\u00f1\u00be\u00a8Q\u0090\u0089\u00a8\u0083\u00df\u00e1Z\u00e5\u009f\u0006\u0092\u008bf^\u0080{U%d\u0001L;\u00fe\u00cfI*\u0003\u0081\u0084\u0000\u0002\u0081\u0080j\u00d1\u001b\u00d7\u00d5f\u00d2z\u00f49\u00c0.Ah\u00ac\u00fdE\u00b4\u00be\u0085\u00bc\u0099\u008c{\u009b\u008e\u001cwTi?\u008c\rB\u008a\u00a4\u00fc\u00e1\u0010\u0084\u00818BO\u00a6\u008c\u00d10RN\u00ef\u00f6\u00f178c\u0082/\u00a67)\u008b\u00feMF\u00a0\u00b8fe\u00ee\u00f0A\u00179\u0001\u0003[\u001c\u0080j\u00a3\u0018\u0018\r0:\u00a8\u00cc\u009eY#\u00e0jo\u00ab\u00fauh<E;\u00b2\u0007w|\u00f2\u00fd\u00e7\u00cf\u00b1\u009b\u001408\u0014\u00aa\u001d\u00f7\u00b4=[\"+W\u0006\u00b4\u008b\u00940\u000b\u0006\u0007*\u0086H\u00ce8\u0004\u0003\u0005\u0000\u0003/\u00000,\u0002\u0014\t\u00d2\u00d1\u00b0G\u0002)\u00b5\u00be\u00d2\u0090&a\u00d1\u0012\u00f2p\u00c5\u00e6\u001d\u0002\u0014gP\u0002\u0006\u00a7\u0080P\u00bax\u00ae\u00c7\u0017O\u0016\u0004\u007f\u0084\u00ea\u00a2\u00f7";

    ReplyDelete
  18. @lohan+, can you bypass signature just one apk for me? PLEASE !!!!!

    ReplyDelete
  19. Thanks for the information that you have shared in this blog post. I also write on hard disks to help people in making decisions before buying hard disk. please check this link

    ReplyDelete
  20. Thanks for sharing this post. It is such an amazing post.
    android-vs-ios

    ReplyDelete
  21. Grammar checker
    We learn to talk from our caregivers. If we are brought up with the help of a nanny, we will often grasp their way of speaking. This can include improper usage of grammar or even the utterance of the wrong words. No one actually pays heed to grammar once they get it done in the school.

    ReplyDelete
  22. get signature with jni:

    https://gist.github.com/wujinyuan/cb7786113c053142e1c0

    ReplyDelete
  23. Positive site, where did u come up with the information on this posting? I'm pleased I discovered it though, ill be checking back soon to find out what additional posts you include.
    ludo star mod apk

    ReplyDelete
  24. Positive site, where did u come up with the information on this posting? I'm pleased I discovered it though, ill be checking back soon to find out what additional posts you include. cyberflix tv apk for pc

    ReplyDelete
  25. Wow its a very good post. The information provided by you is really very good and helpful for me.

    ReplyDelete
  26. @lohan+ I am trying to get apps to install on my LG flip phone. It is running an android system, but it seems to programmed to only allow installation of apps signed with an LG certificate. Is there a possible way to fake that?

    ReplyDelete
  27. I pernolly like your post. You can involve valueable thing which is really usefull for everyone. Keep it up
    click here

    ReplyDelete
  28. Nice blog !! the issuue that you touch is very informative and great. Keep it up. we are waiting for more apk reviews.
    Must watch this

    ReplyDelete
  29. I know your post give me the great information. I am totally impressed with your article and must want to say your posting style is different then others. I am really very thankful to you.
    Best Review

    ReplyDelete
  30. Grand Blog! I should need to thank for the endeavors you have made in shaping this post. I am trusting in a practically identical best work from you later on also.
    Click here

    ReplyDelete
  31. Thanks for according to critical information including all of us. It was surprisingly soft to work. I absolutely like your opinions. I personally relish your work. Your article is very practical and helpful for us.
    click Here

    ReplyDelete
  32. I really liked reading your post! Very high quality content and useful information similar to my post on place.
    visit here Please

    ReplyDelete
  33. Very interesting post thank you for sharing this information with us. I really enjoy simply reading all of your weblogs. if there is a time simply to visit.
    Click it

    ReplyDelete
  34. I am very Impress for this website because its provide all think and all data can be available for this website and all data used for very informative.
    visit here Please

    ReplyDelete

Do NOT post about or link to specific apps!