Refactor JarMap

This commit is contained in:
topjohnwu 2019-11-03 04:45:35 -05:00
parent e7d668502c
commit 6f7c13b814
4 changed files with 108 additions and 76 deletions

View File

@ -118,7 +118,7 @@ object PatchAPK {
@JvmOverloads @JvmOverloads
fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean { fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean {
try { try {
val jar = JarMap(apk) val jar = JarMap.open(apk)
val je = jar.getJarEntry(Const.ANDROID_MANIFEST) val je = jar.getJarEntry(Const.ANDROID_MANIFEST)
val xml = jar.getRawData(je) val xml = jar.getRawData(je)

View File

@ -15,117 +15,149 @@ import java.util.jar.Manifest;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
/* public abstract class JarMap implements Closeable {
* A universal random access interface for both JarFile and JarInputStream
*
* In the case when JarInputStream is provided to constructor, the whole stream
* will be loaded into memory for random access purposes.
* On the other hand, when a JarFile is provided, it simply works as a wrapper.
* */
public class JarMap implements Closeable { LinkedHashMap<String, JarEntry> entryMap;
private JarFile jarFile; public static JarMap open(String file) throws IOException {
private JarInputStream jis; return new FileMap(new File(file), true, ZipFile.OPEN_READ);
private LinkedHashMap<String, JarEntry> bufMap;
private Manifest manifest;
public JarMap(File file) throws IOException {
this(file, true);
} }
public JarMap(File file, boolean verify) throws IOException { public static JarMap open(File file, boolean verify) throws IOException {
this(file, verify, ZipFile.OPEN_READ); return new FileMap(file, verify, ZipFile.OPEN_READ);
} }
public JarMap(File file, boolean verify, int mode) throws IOException { public static JarMap open(String file, boolean verify) throws IOException {
jarFile = new JarFile(file, verify, mode); return new FileMap(new File(file), verify, ZipFile.OPEN_READ);
manifest = jarFile.getManifest();
} }
public JarMap(String name) throws IOException { public static JarMap open(InputStream is, boolean verify) throws IOException {
this(new File(name)); return new StreamMap(is, verify);
}
public JarMap(String name, boolean verify) throws IOException {
this(new File(name), verify);
}
public JarMap(InputStream is) throws IOException {
this(is, true);
}
public JarMap(InputStream is, boolean verify) throws IOException {
jis = new JarInputStream(is, verify);
bufMap = new LinkedHashMap<>();
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
bufMap.put(entry.getName(), new JarMapEntry(entry, jis));
}
manifest = jis.getManifest();
} }
public File getFile() { public File getFile() {
return jarFile == null ? null : new File(jarFile.getName()); return null;
} }
public Manifest getManifest() { public abstract Manifest getManifest() throws IOException;
return manifest;
}
public InputStream getInputStream(ZipEntry ze) throws IOException { public InputStream getInputStream(ZipEntry ze) throws IOException {
if (bufMap != null) { JarMapEntry e = getMapEntry(ze.getName());
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); return e != null ? e.data.getInputStream() : null;
if (e != null)
return e.data.getInputStream();
}
return jarFile.getInputStream(ze);
} }
public OutputStream getOutputStream(ZipEntry ze) { public OutputStream getOutputStream(ZipEntry ze) {
manifest = null; /* Invalidate the manifest */ if (entryMap == null)
if (bufMap == null) entryMap = new LinkedHashMap<>();
bufMap = new LinkedHashMap<>();
JarMapEntry e = new JarMapEntry(ze.getName()); JarMapEntry e = new JarMapEntry(ze.getName());
bufMap.put(ze.getName(), e); entryMap.put(ze.getName(), e);
return e.data; return e.data;
} }
public byte[] getRawData(ZipEntry ze) throws IOException { public byte[] getRawData(ZipEntry ze) throws IOException {
if (bufMap != null) { JarMapEntry e = getMapEntry(ze.getName());
JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); return e != null ? e.data.toByteArray() : null;
if (e != null)
return e.data.toByteArray();
} }
public abstract Enumeration<JarEntry> entries();
public final ZipEntry getEntry(String name) {
return getJarEntry(name);
}
public JarEntry getJarEntry(String name) {
return getMapEntry(name);
}
JarMapEntry getMapEntry(String name) {
JarMapEntry e = null;
if (entryMap != null)
e = (JarMapEntry) entryMap.get(name);
return e;
}
private static class FileMap extends JarMap {
private JarFile jarFile;
FileMap(File file, boolean verify, int mode) throws IOException {
jarFile = new JarFile(file, verify, mode);
}
@Override
public File getFile() {
return new File(jarFile.getName());
}
@Override
public Manifest getManifest() throws IOException {
return jarFile.getManifest();
}
@Override
public InputStream getInputStream(ZipEntry ze) throws IOException {
InputStream is = super.getInputStream(ze);
return is != null ? is : jarFile.getInputStream(ze);
}
@Override
public byte[] getRawData(ZipEntry ze) throws IOException {
byte[] b = super.getRawData(ze);
if (b != null)
return b;
ByteArrayStream bytes = new ByteArrayStream(); ByteArrayStream bytes = new ByteArrayStream();
bytes.readFrom(jarFile.getInputStream(ze)); bytes.readFrom(jarFile.getInputStream(ze));
return bytes.toByteArray(); return bytes.toByteArray();
} }
@Override
public Enumeration<JarEntry> entries() { public Enumeration<JarEntry> entries() {
return jarFile == null ? Collections.enumeration(bufMap.values()) : jarFile.entries(); return jarFile.entries();
}
public ZipEntry getEntry(String name) {
return getJarEntry(name);
} }
@Override
public JarEntry getJarEntry(String name) { public JarEntry getJarEntry(String name) {
JarEntry e = jarFile == null ? bufMap.get(name) : jarFile.getJarEntry(name); JarEntry e = getMapEntry(name);
if (e == null && bufMap != null) return e != null ? e : jarFile.getJarEntry(name);
return bufMap.get(name);
return e;
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if (jarFile != null)
jarFile.close(); jarFile.close();
else }
}
private static class StreamMap extends JarMap {
private JarInputStream jis;
StreamMap(InputStream is, boolean verify) throws IOException {
jis = new JarInputStream(is, verify);
entryMap = new LinkedHashMap<>();
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null) {
entryMap.put(entry.getName(), new JarMapEntry(entry, jis));
}
}
@Override
public Manifest getManifest() {
return jis.getManifest();
}
@Override
public Enumeration<JarEntry> entries() {
return Collections.enumeration(entryMap.values());
}
@Override
public void close() throws IOException {
jis.close(); jis.close();
} }
}
private static class JarMapEntry extends JarEntry { private static class JarMapEntry extends JarEntry {
ByteArrayStream data; ByteArrayStream data;
JarMapEntry(JarEntry je, InputStream is) { JarMapEntry(JarEntry je, InputStream is) {

View File

@ -72,7 +72,7 @@ public class SignAPK {
ZipAdjust.adjust(temp1, temp2); ZipAdjust.adjust(temp1, temp2);
try (JarMap map = new JarMap(temp2, false)) { try (JarMap map = JarMap.open(temp2, false)) {
sign(cert, key, map, output, true); sign(cert, key, map, output, true);
} }
} finally { } finally {

View File

@ -65,7 +65,7 @@ public class ZipSigner {
Security.insertProviderAt(new BouncyCastleProvider(), 1); Security.insertProviderAt(new BouncyCastleProvider(), 1);
try (JarMap in = new JarMap(args[args.length - 2], false); try (JarMap in = JarMap.open(args[args.length - 2], false);
OutputStream out = new FileOutputStream(args[args.length - 1])) { OutputStream out = new FileOutputStream(args[args.length - 1])) {
if (args.length == 2) { if (args.length == 2) {
sign(in, out); sign(in, out);