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:
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:
running it on crackme0 from way of the android cracker 0, produces this:
using it, in dalvik, would look something like this:
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
Amazing! I was looking for it!
ReplyDeleteI 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!
Célio,
ReplyDeleteif 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.
Thank you very much!
ReplyDeletewe 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
ReplyDeleteHello?
ReplyDeleteHow 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?
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.
ReplyDeleteGreat!!
ReplyDeletebut... 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....
Hello,
ReplyDeleteThanks 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?
That's great!
ReplyDeleteBut.. I have a question.
Can I calculate the signature depends on the files in META-INF folder instead of using PackageManager in android?
Thanks
your question does not compute. you ask for what the source code above does. please correct if i am wrong.
ReplyDeleteI 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?
ReplyDeleteCryptographic 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.
DeleteIf 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.
Just use apkmanager v6 it can doo soo much! Just google for it
ReplyDeletei 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?
ReplyDeleteThis won't get your key. It will get a hash of the signature which was generated from your private key. Sorry for your loss.
DeleteGreat 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 ?
ReplyDeleteI 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.
DeleteMaybe a dumb question but lets say the smali codes looks like this:
ReplyDeleteinvoke-virtual (v3), Landroid/content/pm/Signature;->toByteArray()B
move-result-object v4
How would the result in v4 have to be spoofed?
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.
DeleteHi Lohan,
ReplyDeleteI 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
where can i get GetAndroidsig.jar :|
ReplyDeletesorry but i cant understand where
there's no jar file available. read the post, take the code, compile it yourself.
DeleteLohan, 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.
ReplyDeleteQuestion:
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";
@lohan+, can you bypass signature just one apk for me? PLEASE !!!!!
ReplyDeleteThanks 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
ReplyDeleteThanks for sharing this post. It is such an amazing post.
ReplyDeleteandroid-vs-ios
Grammar checker
ReplyDeleteWe 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.
get signature with jni:
ReplyDeletehttps://gist.github.com/wujinyuan/cb7786113c053142e1c0
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.
ReplyDeleteludo star mod apk
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
ReplyDeleteWow its a very good post. The information provided by you is really very good and helpful for me.
ReplyDelete@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?
ReplyDeleteLets play together
ReplyDeleteI pernolly like your post. You can involve valueable thing which is really usefull for everyone. Keep it up
ReplyDeleteclick here
Nice blog !! the issuue that you touch is very informative and great. Keep it up. we are waiting for more apk reviews.
ReplyDeleteMust watch this
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.
ReplyDeleteBest Review
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.
ReplyDeleteClick here
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.
ReplyDeleteclick Here
I really liked reading your post! Very high quality content and useful information similar to my post on place.
ReplyDeletevisit here Please
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.
ReplyDeleteClick it
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.
ReplyDeletevisit here Please
informative blog, keep posting java classes in pune
ReplyDeleteNice Post.
ReplyDeleteJava course in Pune