// package me.piebridge; import java.io.IOException; import java.io.RandomAccessFile; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by thom on 2018/5/31. */ public class SimpleApkV2 { private SimpleApkV2() { } private static abstract class ApkV2Parser { private final String sourceDir; ApkV2Parser(String sourceDir) { this.sourceDir = sourceDir; parse(); } private void parse() { final byte[] apkSignV2 = {'A', 'P', 'K', ' ', 'S', 'i', 'g', ' ', 'B', 'l', 'o', 'c', 'k', ' ', '4', '2'}; try ( RandomAccessFile apk = new RandomAccessFile(sourceDir, "r") ) { ByteBuffer buffer = ByteBuffer.allocate(0x10); buffer.order(ByteOrder.LITTLE_ENDIAN); apk.seek(apk.length() - 0x6); apk.readFully(buffer.array(), 0x0, 0x6); int offset = buffer.getInt(); if (buffer.getShort() != 0) { return; } apk.seek(offset - 0x10); apk.readFully(buffer.array(), 0x0, 0x10); if (!Arrays.equals(apkSignV2, buffer.array())) { return; } // Read and compare size fields apk.seek(offset - 0x18); apk.readFully(buffer.array(), 0x0, 0x8); buffer.rewind(); int size = (int) buffer.getLong(); ByteBuffer block = ByteBuffer.allocate(size + 0x8); block.order(ByteOrder.LITTLE_ENDIAN); apk.seek(offset - block.capacity()); apk.readFully(block.array(), 0x0, block.capacity()); if (size != block.getLong()) { return; } while (block.remaining() > 0x18) { size = (int) block.getLong(); int id = block.getInt(); int newPosition = block.position() + size - 0x4; if (handle(id, size, block)) { return; } block.position(newPosition); } } catch (IOException ignore) { // do nothing } } protected abstract boolean handle(int id, int size, ByteBuffer block); } public static void main(String[] args) { new DummyParser(args[0]); } private static class DummyParser extends ApkV2Parser { private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; DummyParser(String sourceDir) { super(sourceDir); } @Override protected boolean handle(int id, int size, ByteBuffer block) { if (id == 0x7109871a) { System.out.println("id: 0x" + Integer.toHexString(id) + "(signature scheme v2), size: " + size); } else if (id == 0xf05368c0) { System.out.println("id: 0x" + Integer.toHexString(id) + "(signature scheme v3), size: " + size); } else if (id == 0x1b93ad61) { System.out.println("id: 0x" + Integer.toHexString(id) + "(signature scheme v3.1), size: " + size); } else if (id == 0x2b09189e) { System.out.println("id: 0x" + Integer.toHexString(id) + "(source stamp V1), size: " + size); } else if (id == 0x6dff800d) { System.out.println("id: 0x" + Integer.toHexString(id) + "(source stamp V2), size: " + size); } else if (id == 0x42726577) { System.out.println("id: 0x" + Integer.toHexString(id) + "(padding), size: " + size); } else if (id == 0x2146444e) { System.out.println("id: 0x" + Integer.toHexString(id) + "(play store security metadata), " + size); } else if (id == 0x504b4453) { System.out.println("id: 0x" + Integer.toHexString(id) + "(dependencies metadata), size: " + size); } else if (id == 0x71777777) { byte[] bytes = new byte[size - 0x4]; block.get(bytes, 0, bytes.length); String channel = new String(bytes); System.out.println("id: 0x" + Integer.toHexString(id) + "(meituan channel), " + channel); } else { System.out.println("id: 0x" + Integer.toHexString(id) + "(unknown), size: " + size); byte[] bytes = new byte[size - 0x4]; block.get(bytes, 0, bytes.length); System.out.println(" " + hex(bytes)); } return false; } private static String hex(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte b : bytes) { sb.append(HEX[(b >> 4) & 0xf]); sb.append(HEX[b & 0xf]); } return sb.toString(); } } }