]> git.saurik.com Git - backport.git/blob - src/com/saurik/backport/Hook.java
Add support for what I had previously called bug B.
[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 DataInputStream in = new DataInputStream(new ByteArrayInputStream(header));
90
91 in.skip(8);
92 for (int i = 0; i != 2; ++i)
93 if ((in.readShort() & 0x0080) != 0)
94 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
95
96 in.skip(16);
97 for (int i = 0; i != 3; ++i)
98 if ((in.readShort() & 0x0080) != 0)
99 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
100
101 return null;
102 }
103 }
104 );
105 }
106
107 private static void fixZipFile$getInputStream() {
108 final Field ZipEntry$nameLen = scanField(ZipEntry.class, "nameLen", "nameLength");
109 if (ZipEntry$nameLen == null)
110 return;
111 ZipEntry$nameLen.setAccessible(true);
112
113 final Field ZipFile$mRaf = scanField(ZipFile.class, "mRaf", "raf");
114 if (ZipFile$mRaf == null)
115 return;
116 ZipFile$mRaf.setAccessible(true);
117
118 final Field ZipEntry$mLocalHeaderRelOffset = scanField(ZipEntry.class, "mLocalHeaderRelOffset", "localHeaderRelOffset");
119 if (ZipEntry$mLocalHeaderRelOffset == null)
120 return;
121 ZipEntry$mLocalHeaderRelOffset.setAccessible(true);
122
123 final Method ZipFile$getInputStream = findMethod(ZipFile.class, "getInputStream", ZipEntry.class);
124 if (ZipFile$getInputStream == null)
125 return;
126
127 MS.hookMethod(ZipFile.class, ZipFile$getInputStream,
128 new MS.MethodAlteration<ZipFile, InputStream>() {
129 public InputStream invoked(ZipFile thiz, Object... args)
130 throws Throwable
131 {
132 ZipEntry entry = (ZipEntry) args[0];
133
134 RandomAccessFile raf = (RandomAccessFile) ZipFile$mRaf.get(thiz);
135 synchronized (raf) {
136 raf.seek(ZipEntry$mLocalHeaderRelOffset.getLong(entry));
137
138 raf.skipBytes(6);
139 if ((raf.readShort() & 0x0080) != 0)
140 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
141
142 raf.skipBytes(18);
143
144 int length = raf.readShort() & 0xffff;
145 if (length != ZipEntry$nameLen.getInt(entry))
146 throw new ZipException("bug #sauriks [" + thiz.getName() + "]");
147
148 if ((raf.readShort() & 0x0080) != 0)
149 throw new ZipException("bug #9695860 [" + thiz.getName() + "]");
150 }
151
152 return invoke(thiz, args);
153 }
154 }
155 );
156 }
157
158 private static void fixZipFile$readCentralDir() {
159 final Field ZipFile$entries = scanField(ZipFile.class, "mEntries", "entries");
160 if (ZipFile$entries == null)
161 return;
162 ZipFile$entries.setAccessible(true);
163
164 final Method ZipFile$readCentralDir = findMethod(ZipFile.class, "readCentralDir");
165 if (ZipFile$readCentralDir == null)
166 return;
167
168 MS.hookMethod(ZipFile.class, ZipFile$readCentralDir,
169 new MS.MethodAlteration<ZipFile, Void>() {
170 public Void invoked(ZipFile thiz, Object... args)
171 throws Throwable
172 {
173 ZipFile$entries.set(thiz, new LinkedHashMap<String, ZipEntry>() {
174 public ZipEntry put(String key, ZipEntry value) {
175 if (super.put(key, value) != null)
176 throw new WrongException(new ZipException("bug #8219321 [" + key + "]"));
177 return null;
178 }
179 });
180
181 try {
182 return invoke(thiz, args);
183 } catch (WrongException wrong) {
184 throw wrong.getCause();
185 }
186 }
187 }
188 );
189 }
190
191 public static void initialize() {
192 fixZipEntry$init$();
193 fixZipFile$getInputStream();
194 fixZipFile$readCentralDir();
195 }
196 }