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