ae62a812af503c8c1522e104ba35cb32421ebe1d
[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$nameLen = scanField(ZipEntry.class, "nameLen", "nameLength");
112 if (ZipEntry$nameLen == null)
113 return;
114 ZipEntry$nameLen.setAccessible(true);
115
116 final Field ZipFile$mRaf = scanField(ZipFile.class, "mRaf", "raf");
117 if (ZipFile$mRaf == null)
118 return;
119 ZipFile$mRaf.setAccessible(true);
120
121 final Field ZipEntry$mLocalHeaderRelOffset = scanField(ZipEntry.class, "mLocalHeaderRelOffset", "localHeaderRelOffset");
122 if (ZipEntry$mLocalHeaderRelOffset == null)
123 return;
124 ZipEntry$mLocalHeaderRelOffset.setAccessible(true);
125
126 final Method ZipFile$getInputStream = findMethod(ZipFile.class, "getInputStream", ZipEntry.class);
127 if (ZipFile$getInputStream == null)
128 return;
129
130 MS.hookMethod(ZipFile.class, ZipFile$getInputStream,
131 new MS.MethodAlteration<ZipFile, InputStream>() {
132 public InputStream invoked(ZipFile thiz, Object... args)
133 throws Throwable
134 {
135 ZipEntry entry = (ZipEntry) args[0];
136
137 RandomAccessFile raf = (RandomAccessFile) ZipFile$mRaf.get(thiz);
138 synchronized (raf) {
139 raf.seek(ZipEntry$mLocalHeaderRelOffset.getLong(entry));
140
141 raf.skipBytes(6);
142 if ((raf.readShort() & 0x0080) != 0)
143 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
144
145 raf.skipBytes(18);
146
147 int length = Short.reverseBytes(raf.readShort()) & 0xffff;
148 if (length != ZipEntry$nameLen.getInt(entry))
149 throw new ZipException("bug #9950697 [" + thiz.getName() + "]");
150
151 if ((raf.readShort() & 0x0080) != 0)
152 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
153 }
154
155 return invoke(thiz, args);
156 }
157 }
158 );
159 }
160
161 private static void fixZipFile$readCentralDir() {
162 final Field ZipFile$entries = scanField(ZipFile.class, "mEntries", "entries");
163 if (ZipFile$entries == null)
164 return;
165 ZipFile$entries.setAccessible(true);
166
167 final Method ZipFile$readCentralDir = findMethod(ZipFile.class, "readCentralDir");
168 if (ZipFile$readCentralDir == null)
169 return;
170
171 MS.hookMethod(ZipFile.class, ZipFile$readCentralDir,
172 new MS.MethodAlteration<ZipFile, Void>() {
173 public Void invoked(ZipFile thiz, Object... args)
174 throws Throwable
175 {
176 ZipFile$entries.set(thiz, new LinkedHashMap<String, ZipEntry>() {
177 public ZipEntry put(String key, ZipEntry value) {
178 if (super.put(key, value) != null)
179 throw new WrongException(new ZipException("bug #8219321 [" + key + "]"));
180 return null;
181 }
182 });
183
184 try {
185 return invoke(thiz, args);
186 } catch (WrongException wrong) {
187 throw wrong.getCause();
188 }
189 }
190 }
191 );
192 }
193
194 public static void initialize() {
195 fixZipEntry$init$();
196 fixZipFile$getInputStream();
197 fixZipFile$readCentralDir();
198 }
199 }