1 /* Backport - Bring New Fixes to old Androids
2 * Copyright (C) 2013 Jay Freeman (saurik)
5 /* GNU Lesser General Public License, Version 3 {{{ */
7 * Substrate is free software: you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
12 * Substrate is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Substrate. If not, see <http://www.gnu.org/licenses/>.
22 package com
.saurik
.backport
;
24 import java
.lang
.reflect
.Constructor
;
25 import java
.lang
.reflect
.Field
;
26 import java
.lang
.reflect
.Method
;
28 import java
.io
.ByteArrayInputStream
;
29 import java
.io
.DataInputStream
;
30 import java
.io
.InputStream
;
31 import java
.io
.RandomAccessFile
;
33 import java
.util
.LinkedHashMap
;
35 import java
.util
.zip
.ZipEntry
;
36 import java
.util
.zip
.ZipException
;
37 import java
.util
.zip
.ZipFile
;
39 import android
.util
.Log
;
41 import com
.saurik
.substrate
.MS
;
44 private static class WrongException
45 extends RuntimeException
47 public WrongException(Throwable cause
) {
52 private static Field
scanField(Class clazz
, String
... names
) {
53 for (String name
: names
) try {
54 return clazz
.getDeclaredField(name
);
55 } catch (NoSuchFieldException e
) {}
59 private static Method
findMethod(Class
<?
> clazz
, String name
, Class
... args
) {
61 return clazz
.getDeclaredMethod(name
, args
);
62 } catch (NoSuchMethodException e
) {
67 private static Constructor
findConstructor(Class
<?
> clazz
, Class
... args
) {
69 return clazz
.getDeclaredConstructor(args
);
70 } catch (NoSuchMethodException e
) {
75 private static void fixZipEntry$init$
() {
76 final Constructor ZipEntry$init$
= findConstructor(ZipEntry
.class, byte[].class, InputStream
.class);
77 if (ZipEntry$init$
== null)
80 MS
.hookMethod(ZipEntry
.class, ZipEntry$init$
,
81 new MS
.MethodAlteration
<ZipEntry
, Void
>() {
82 public Void
invoked(ZipEntry thiz
, Object
... args
)
85 byte[] header
= (byte[]) args
[0];
89 if (thiz
.getName().indexOf(0) != -1)
90 throw new ZipException("bug #10148349 [" + thiz
.getName() + "]");
92 DataInputStream in
= new DataInputStream(new ByteArrayInputStream(header
));
95 for (int i
= 0; i
!= 2; ++i
)
96 if ((in
.readShort() & 0x0080) != 0)
97 throw new ZipException("bug #9695860 [" + thiz
.getName() + "]");
100 for (int i
= 0; i
!= 3; ++i
)
101 if ((in
.readShort() & 0x0080) != 0)
102 throw new ZipException("bug #9695860 [" + thiz
.getName() + "]");
110 private static void fixZipFile$
getInputStream() {
111 final Field ZipEntry$compressedSize
= scanField(ZipEntry
.class, "compressedSize");
112 if (ZipEntry$compressedSize
== null)
114 ZipEntry$compressedSize
.setAccessible(true);
116 final Field ZipEntry$compressionMethod
= scanField(ZipEntry
.class, "compressionMethod");
117 if (ZipEntry$compressionMethod
== null)
119 ZipEntry$compressionMethod
.setAccessible(true);
121 final Field ZipEntry$size
= scanField(ZipEntry
.class, "size");
122 if (ZipEntry$size
== null)
124 ZipEntry$size
.setAccessible(true);
126 final Field ZipEntry$nameLen
= scanField(ZipEntry
.class, "nameLen", "nameLength");
127 if (ZipEntry$nameLen
== null)
129 ZipEntry$nameLen
.setAccessible(true);
131 final Field ZipFile$mRaf
= scanField(ZipFile
.class, "mRaf", "raf");
132 if (ZipFile$mRaf
== null)
134 ZipFile$mRaf
.setAccessible(true);
136 final Field ZipEntry$mLocalHeaderRelOffset
= scanField(ZipEntry
.class, "mLocalHeaderRelOffset", "localHeaderRelOffset");
137 if (ZipEntry$mLocalHeaderRelOffset
== null)
139 ZipEntry$mLocalHeaderRelOffset
.setAccessible(true);
141 final Method ZipFile$getInputStream
= findMethod(ZipFile
.class, "getInputStream", ZipEntry
.class);
142 if (ZipFile$getInputStream
== null)
145 MS
.hookMethod(ZipFile
.class, ZipFile$getInputStream
,
146 new MS
.MethodAlteration
<ZipFile
, InputStream
>() {
147 public InputStream
invoked(ZipFile thiz
, Object
... args
)
150 ZipEntry entry
= (ZipEntry
) args
[0];
152 RandomAccessFile raf
= (RandomAccessFile
) ZipFile$mRaf
.get(thiz
);
154 raf
.seek(ZipEntry$mLocalHeaderRelOffset
.getLong(entry
));
157 if ((raf
.readShort() & 0x0080) != 0)
158 throw new ZipException("bug #9695860 [" + thiz
.getName() + "]");
162 int length
= Short
.reverseBytes(raf
.readShort()) & 0xffff;
163 if (length
!= ZipEntry$nameLen
.getInt(entry
))
164 throw new ZipException("bug #9950697 [" + thiz
.getName() + "]");
166 if ((raf
.readShort() & 0x0080) != 0)
167 throw new ZipException("bug #9695860 [" + thiz
.getName() + "]");
170 if (ZipEntry$compressionMethod
.getInt(entry
) == ZipEntry
.STORED
)
171 if (ZipEntry$compressedSize
.getLong(entry
) != ZipEntry$size
.getLong(entry
))
172 throw new ZipException("bug #10227498 [" + thiz
.getName() + "]");
174 return invoke(thiz
, args
);
180 private static void fixZipFile$
readCentralDir() {
181 final Field ZipFile$entries
= scanField(ZipFile
.class, "mEntries", "entries");
182 if (ZipFile$entries
== null)
184 ZipFile$entries
.setAccessible(true);
186 final Method ZipFile$readCentralDir
= findMethod(ZipFile
.class, "readCentralDir");
187 if (ZipFile$readCentralDir
== null)
190 MS
.hookMethod(ZipFile
.class, ZipFile$readCentralDir
,
191 new MS
.MethodAlteration
<ZipFile
, Void
>() {
192 public Void
invoked(ZipFile thiz
, Object
... args
)
195 ZipFile$entries
.set(thiz
, new LinkedHashMap
<String
, ZipEntry
>() {
196 public ZipEntry
put(String key
, ZipEntry value
) {
197 if (super.put(key
, value
) != null)
198 throw new WrongException(new ZipException("bug #8219321 [" + key
+ "]"));
204 return invoke(thiz
, args
);
205 } catch (WrongException wrong
) {
206 throw wrong
.getCause();
213 public static void initialize() {
215 fixZipFile$
getInputStream();
216 fixZipFile$
readCentralDir();