From: Jay Freeman (saurik) Date: Fri, 26 Jul 2013 09:51:06 +0000 (-0700) Subject: Implement a complete fix for Android bug #9695860. X-Git-Tag: v0.9.2^0 X-Git-Url: https://git.saurik.com/backport.git/commitdiff_plain/2202c245710754106c7af5a3d891f9f312af99f6 Implement a complete fix for Android bug #9695860. --- diff --git a/src/com/saurik/backport/Hook.java b/src/com/saurik/backport/Hook.java index 6d0e134..2ddd07a 100644 --- a/src/com/saurik/backport/Hook.java +++ b/src/com/saurik/backport/Hook.java @@ -21,12 +21,16 @@ package com.saurik.backport; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.LinkedHashMap; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.RandomAccessFile; -import java.util.jar.JarFile; +import java.util.LinkedHashMap; import java.util.zip.ZipEntry; import java.util.zip.ZipException; @@ -45,53 +49,138 @@ public class Hook { } } - public static void initialize() { - MS.hookClassLoad("java.util.zip.ZipFile", new MS.ClassLoadHook() { - public void classLoaded(Class ZipFile$) { - Field entries = null; { - if (entries == null) try { - entries = ZipFile$.getDeclaredField("entries"); - } catch (NoSuchFieldException e) {} - - if (entries == null) try { - entries = ZipFile$.getDeclaredField("mEntries"); - } catch (NoSuchFieldException e) {} + private static Field scanField(Class clazz, String... names) { + for (String name : names) try { + return clazz.getDeclaredField(name); + } catch (NoSuchFieldException e) {} + return null; + } + + private static Method findMethod(Class clazz, String name, Class... args) { + try { + return clazz.getDeclaredMethod(name, args); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static Constructor findConstructor(Class clazz, Class... args) { + try { + return clazz.getDeclaredConstructor(args); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static void fixZipEntry$init$() { + final Constructor ZipEntry$init$ = findConstructor(ZipEntry.class, byte[].class, InputStream.class); + if (ZipEntry$init$ == null) + return; + + MS.hookMethod(ZipEntry.class, ZipEntry$init$, + new MS.MethodAlteration() { + public Void invoked(ZipEntry thiz, Object... args) + throws Throwable + { + byte[] header = (byte[]) args[0]; + + invoke(thiz, args); + + DataInputStream in = new DataInputStream(new ByteArrayInputStream(header)); + + in.skip(8); + for (int i = 0; i != 2; ++i) + if ((in.readShort() & 0x0080) != 0) + throw new ZipException("bug #99695860 [" + thiz.getName() + "]"); + + in.skip(16); + for (int i = 0; i != 3; ++i) + if ((in.readShort() & 0x0080) != 0) + throw new ZipException("bug #99695860 [" + thiz.getName() + "]"); + + return null; } + } + ); + } + + private static void fixZipFile$getInputStream() { + final Field ZipFile$mRaf = scanField(ZipFile.class, "mRaf", "raf"); + if (ZipFile$mRaf == null) + return; + ZipFile$mRaf.setAccessible(true); + + final Field ZipEntry$mLocalHeaderRelOffset = scanField(ZipEntry.class, "mLocalHeaderRelOffset", "localHeaderRelOffset"); + if (ZipEntry$mLocalHeaderRelOffset == null) + return; + ZipEntry$mLocalHeaderRelOffset.setAccessible(true); + + final Method ZipFile$getInputStream = findMethod(ZipFile.class, "getInputStream", ZipEntry.class); + if (ZipFile$getInputStream == null) + return; + + MS.hookMethod(ZipFile.class, ZipFile$getInputStream, + new MS.MethodAlteration() { + public InputStream invoked(ZipFile thiz, Object... args) + throws Throwable + { + ZipEntry entry = (ZipEntry) args[0]; - if (entries == null) - return; - final Field ZipFile$entries = entries; - ZipFile$entries.setAccessible(true); + RandomAccessFile raf = (RandomAccessFile) ZipFile$mRaf.get(thiz); + synchronized (raf) { + raf.seek(ZipEntry$mLocalHeaderRelOffset.getLong(entry)); - final Method ZipFile$readCentralDir; try { - ZipFile$readCentralDir = ZipFile$.getDeclaredMethod("readCentralDir"); - } catch (NoSuchMethodException e) { - Log.e("Backport", "ZipFile$readCentralDir", e); - return; + raf.skipBytes(6); + if ((raf.readShort() & 0x0080) != 0) + throw new ZipException("bug #99695860 [" + thiz.getName() + "]"); + + raf.skipBytes(20); + if ((raf.readShort() & 0x0080) != 0) + throw new ZipException("bug #99695860 [" + thiz.getName() + "]"); + } + + return invoke(thiz, args); } + } + ); + } + + private static void fixZipFile$readCentralDir() { + final Field ZipFile$entries = scanField(ZipFile.class, "mEntries", "entries"); + if (ZipFile$entries == null) + return; + ZipFile$entries.setAccessible(true); - MS.hookMethod(ZipFile$, ZipFile$readCentralDir, - new MS.MethodAlteration() { - public Void invoked(ZipFile thiz, Object... args) - throws Throwable - { - ZipFile$entries.set(thiz, new LinkedHashMap() { - public ZipEntry put(String key, ZipEntry value) { - if (super.put(key, value) != null) - throw new WrongException(new ZipException("Duplicate entry name: " + key)); - return null; - } - }); - - try { - return invoke(thiz, args); - } catch (WrongException wrong) { - throw wrong.getCause(); - } + final Method ZipFile$readCentralDir = findMethod(ZipFile.class, "readCentralDir"); + if (ZipFile$readCentralDir == null) + return; + + MS.hookMethod(ZipFile.class, ZipFile$readCentralDir, + new MS.MethodAlteration() { + public Void invoked(ZipFile thiz, Object... args) + throws Throwable + { + ZipFile$entries.set(thiz, new LinkedHashMap() { + public ZipEntry put(String key, ZipEntry value) { + if (super.put(key, value) != null) + throw new WrongException(new ZipException("bug #8219321 [" + key + "]")); + return null; } + }); + + try { + return invoke(thiz, args); + } catch (WrongException wrong) { + throw wrong.getCause(); } - ); + } } - }); + ); + } + + public static void initialize() { + fixZipEntry$init$(); + fixZipFile$getInputStream(); + fixZipFile$readCentralDir(); } }