]>
Commit | Line | Data |
---|---|---|
6e682be6 JF |
1 | /* Backport - Bring New Fixes to old Androids |
2 | * Copyright (C) 2013 Jay Freeman (saurik) | |
3 | */ | |
4 | ||
5 | /* GNU Lesser General Public License, Version 3 {{{ */ | |
6 | /* | |
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. | |
11 | * | |
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. | |
16 | * | |
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/>. | |
19 | **/ | |
20 | /* }}} */ | |
21 | ||
22 | package com.saurik.backport; | |
23 | ||
2202c245 | 24 | import java.lang.reflect.Constructor; |
6e682be6 JF |
25 | import java.lang.reflect.Field; |
26 | import java.lang.reflect.Method; | |
27 | ||
2202c245 JF |
28 | import java.io.ByteArrayInputStream; |
29 | import java.io.DataInputStream; | |
30 | import java.io.InputStream; | |
31 | import java.io.RandomAccessFile; | |
6e682be6 | 32 | |
2202c245 | 33 | import java.util.LinkedHashMap; |
6e682be6 JF |
34 | |
35 | import java.util.zip.ZipEntry; | |
36 | import java.util.zip.ZipException; | |
37 | import java.util.zip.ZipFile; | |
38 | ||
39 | import android.util.Log; | |
40 | ||
41 | import com.saurik.substrate.MS; | |
42 | ||
43 | public class Hook { | |
79cf0362 JF |
44 | private static class WrongException |
45 | extends RuntimeException | |
46 | { | |
47 | public WrongException(Throwable cause) { | |
48 | super(cause); | |
49 | } | |
50 | } | |
51 | ||
2202c245 JF |
52 | private static Field scanField(Class clazz, String... names) { |
53 | for (String name : names) try { | |
54 | return clazz.getDeclaredField(name); | |
55 | } catch (NoSuchFieldException e) {} | |
56 | return null; | |
57 | } | |
58 | ||
59 | private static Method findMethod(Class<?> clazz, String name, Class... args) { | |
60 | try { | |
61 | return clazz.getDeclaredMethod(name, args); | |
62 | } catch (NoSuchMethodException e) { | |
63 | return null; | |
64 | } | |
65 | } | |
66 | ||
67 | private static Constructor findConstructor(Class<?> clazz, Class... args) { | |
68 | try { | |
69 | return clazz.getDeclaredConstructor(args); | |
70 | } catch (NoSuchMethodException e) { | |
71 | return null; | |
72 | } | |
73 | } | |
74 | ||
75 | private static void fixZipEntry$init$() { | |
76 | final Constructor ZipEntry$init$ = findConstructor(ZipEntry.class, byte[].class, InputStream.class); | |
77 | if (ZipEntry$init$ == null) | |
78 | return; | |
79 | ||
80 | MS.hookMethod(ZipEntry.class, ZipEntry$init$, | |
81 | new MS.MethodAlteration<ZipEntry, Void>() { | |
82 | public Void invoked(ZipEntry thiz, Object... args) | |
83 | throws Throwable | |
84 | { | |
85 | byte[] header = (byte[]) args[0]; | |
86 | ||
87 | invoke(thiz, args); | |
88 | ||
04b93b54 JF |
89 | if (thiz.getName().indexOf(0) != -1) |
90 | throw new ZipException("bug #10148349 [" + thiz.getName() + "]"); | |
91 | ||
2202c245 JF |
92 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(header)); |
93 | ||
94 | in.skip(8); | |
95 | for (int i = 0; i != 2; ++i) | |
96 | if ((in.readShort() & 0x0080) != 0) | |
68e68412 | 97 | throw new ZipException("bug #9695860 [" + thiz.getName() + "]"); |
2202c245 JF |
98 | |
99 | in.skip(16); | |
100 | for (int i = 0; i != 3; ++i) | |
101 | if ((in.readShort() & 0x0080) != 0) | |
68e68412 | 102 | throw new ZipException("bug #9695860 [" + thiz.getName() + "]"); |
2202c245 JF |
103 | |
104 | return null; | |
6e682be6 | 105 | } |
2202c245 JF |
106 | } |
107 | ); | |
108 | } | |
109 | ||
110 | private static void fixZipFile$getInputStream() { | |
eee29e9e JF |
111 | final Field ZipEntry$compressedSize = scanField(ZipEntry.class, "compressedSize"); |
112 | if (ZipEntry$compressedSize == null) | |
113 | return; | |
114 | ZipEntry$compressedSize.setAccessible(true); | |
115 | ||
116 | final Field ZipEntry$compressionMethod = scanField(ZipEntry.class, "compressionMethod"); | |
117 | if (ZipEntry$compressionMethod == null) | |
118 | return; | |
119 | ZipEntry$compressionMethod.setAccessible(true); | |
120 | ||
121 | final Field ZipEntry$size = scanField(ZipEntry.class, "size"); | |
122 | if (ZipEntry$size == null) | |
123 | return; | |
124 | ZipEntry$size.setAccessible(true); | |
125 | ||
d6bb9203 JF |
126 | final Field ZipEntry$nameLen = scanField(ZipEntry.class, "nameLen", "nameLength"); |
127 | if (ZipEntry$nameLen == null) | |
128 | return; | |
129 | ZipEntry$nameLen.setAccessible(true); | |
130 | ||
2202c245 JF |
131 | final Field ZipFile$mRaf = scanField(ZipFile.class, "mRaf", "raf"); |
132 | if (ZipFile$mRaf == null) | |
133 | return; | |
134 | ZipFile$mRaf.setAccessible(true); | |
135 | ||
136 | final Field ZipEntry$mLocalHeaderRelOffset = scanField(ZipEntry.class, "mLocalHeaderRelOffset", "localHeaderRelOffset"); | |
137 | if (ZipEntry$mLocalHeaderRelOffset == null) | |
138 | return; | |
139 | ZipEntry$mLocalHeaderRelOffset.setAccessible(true); | |
140 | ||
141 | final Method ZipFile$getInputStream = findMethod(ZipFile.class, "getInputStream", ZipEntry.class); | |
142 | if (ZipFile$getInputStream == null) | |
143 | return; | |
144 | ||
145 | MS.hookMethod(ZipFile.class, ZipFile$getInputStream, | |
146 | new MS.MethodAlteration<ZipFile, InputStream>() { | |
147 | public InputStream invoked(ZipFile thiz, Object... args) | |
148 | throws Throwable | |
149 | { | |
150 | ZipEntry entry = (ZipEntry) args[0]; | |
6e682be6 | 151 | |
2202c245 JF |
152 | RandomAccessFile raf = (RandomAccessFile) ZipFile$mRaf.get(thiz); |
153 | synchronized (raf) { | |
154 | raf.seek(ZipEntry$mLocalHeaderRelOffset.getLong(entry)); | |
6e682be6 | 155 | |
2202c245 JF |
156 | raf.skipBytes(6); |
157 | if ((raf.readShort() & 0x0080) != 0) | |
68e68412 | 158 | throw new ZipException("bug #9695860 [" + thiz.getName() + "]"); |
2202c245 | 159 | |
d6bb9203 JF |
160 | raf.skipBytes(18); |
161 | ||
48bc4607 | 162 | int length = Short.reverseBytes(raf.readShort()) & 0xffff; |
d6bb9203 | 163 | if (length != ZipEntry$nameLen.getInt(entry)) |
4d4eb512 | 164 | throw new ZipException("bug #9950697 [" + thiz.getName() + "]"); |
d6bb9203 | 165 | |
2202c245 | 166 | if ((raf.readShort() & 0x0080) != 0) |
68e68412 | 167 | throw new ZipException("bug #9695860 [" + thiz.getName() + "]"); |
2202c245 JF |
168 | } |
169 | ||
eee29e9e JF |
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() + "]"); | |
173 | ||
2202c245 | 174 | return invoke(thiz, args); |
6e682be6 | 175 | } |
2202c245 JF |
176 | } |
177 | ); | |
178 | } | |
179 | ||
180 | private static void fixZipFile$readCentralDir() { | |
181 | final Field ZipFile$entries = scanField(ZipFile.class, "mEntries", "entries"); | |
182 | if (ZipFile$entries == null) | |
183 | return; | |
184 | ZipFile$entries.setAccessible(true); | |
6e682be6 | 185 | |
2202c245 JF |
186 | final Method ZipFile$readCentralDir = findMethod(ZipFile.class, "readCentralDir"); |
187 | if (ZipFile$readCentralDir == null) | |
188 | return; | |
189 | ||
190 | MS.hookMethod(ZipFile.class, ZipFile$readCentralDir, | |
191 | new MS.MethodAlteration<ZipFile, Void>() { | |
192 | public Void invoked(ZipFile thiz, Object... args) | |
193 | throws Throwable | |
194 | { | |
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 + "]")); | |
199 | return null; | |
6e682be6 | 200 | } |
2202c245 JF |
201 | }); |
202 | ||
203 | try { | |
204 | return invoke(thiz, args); | |
205 | } catch (WrongException wrong) { | |
206 | throw wrong.getCause(); | |
6e682be6 | 207 | } |
2202c245 | 208 | } |
6e682be6 | 209 | } |
2202c245 JF |
210 | ); |
211 | } | |
212 | ||
213 | public static void initialize() { | |
214 | fixZipEntry$init$(); | |
215 | fixZipFile$getInputStream(); | |
216 | fixZipFile$readCentralDir(); | |
6e682be6 JF |
217 | } |
218 | } |