Add support for protecting against bug #10227498.
[backport.git] / src / com / saurik / backport / Hook.java
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
24 import java.lang.reflect.Constructor;
25 import java.lang.reflect.Field;
26 import java.lang.reflect.Method;
27
28 import java.io.ByteArrayInputStream;
29 import java.io.DataInputStream;
30 import java.io.InputStream;
31 import java.io.RandomAccessFile;
32
33 import java.util.LinkedHashMap;
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 {
44 private static class WrongException
45 extends RuntimeException
46 {
47 public WrongException(Throwable cause) {
48 super(cause);
49 }
50 }
51
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
89 if (thiz.getName().indexOf(0) != -1)
90 throw new ZipException("bug #10148349 [" + thiz.getName() + "]");
91
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)
97 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
98
99 in.skip(16);
100 for (int i = 0; i != 3; ++i)
101 if ((in.readShort() & 0x0080) != 0)
102 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
103
104 return null;
105 }
106 }
107 );
108 }
109
110 private static void fixZipFile$getInputStream() {
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
126 final Field ZipEntry$nameLen = scanField(ZipEntry.class, "nameLen", "nameLength");
127 if (ZipEntry$nameLen == null)
128 return;
129 ZipEntry$nameLen.setAccessible(true);
130
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];
151
152 RandomAccessFile raf = (RandomAccessFile) ZipFile$mRaf.get(thiz);
153 synchronized (raf) {
154 raf.seek(ZipEntry$mLocalHeaderRelOffset.getLong(entry));
155
156 raf.skipBytes(6);
157 if ((raf.readShort() & 0x0080) != 0)
158 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
159
160 raf.skipBytes(18);
161
162 int length = Short.reverseBytes(raf.readShort()) & 0xffff;
163 if (length != ZipEntry$nameLen.getInt(entry))
164 throw new ZipException("bug #9950697 [" + thiz.getName() + "]");
165
166 if ((raf.readShort() & 0x0080) != 0)
167 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
168 }
169
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
174 return invoke(thiz, args);
175 }
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);
185
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;
200 }
201 });
202
203 try {
204 return invoke(thiz, args);
205 } catch (WrongException wrong) {
206 throw wrong.getCause();
207 }
208 }
209 }
210 );
211 }
212
213 public static void initialize() {
214 fixZipEntry$init$();
215 fixZipFile$getInputStream();
216 fixZipFile$readCentralDir();
217 }
218 }