/
FrameworkHack.java
181 lines (167 loc) · 7.45 KB
/
FrameworkHack.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
* Copyright 2013 ThinkFree
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ruboto;
import android.util.Log;
import dalvik.system.DexFile;
import dalvik.system.PathClassLoader;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipFile;
/**
* Collection of dirty codes. don't tell android team this mess.
* @author Alan Goo
*/
public class FrameworkHack {
private static final String TAG = "FrameworkHack";
public static boolean debug = false;
private FrameworkHack() {
// do not create an instance
}
/**
* dalvik do not have security manager
*/
private static void forceSet(Object obj, Field f, Object val) throws IllegalAccessException {
f.setAccessible(true);
f.set(obj, val);
}
private static Object forceGetFirst(Object obj, Field fArray) throws IllegalAccessException {
fArray.setAccessible(true);
Object[] vArray = (Object[]) fArray.get(obj);
return vArray[0];
}
private static String joinPaths(String[] paths) {
if (paths == null) {
return "";
}
StringBuilder buf = new StringBuilder();
for (int i = 0; i < paths.length; i++) {
buf.append(paths[i]);
buf.append(':');
}
return buf.toString();
}
// https://android.googlesource.com/platform/dalvik/+/android-1.6_r1/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
public static void appendDexListImplUnderICS(String[] jarPathsToAppend, PathClassLoader pcl, File optDir)
throws Exception {
int oldSize = 1; // gonna assume the original path had single entry for simplicity
Class pclClass = pcl.getClass();
Field fPath = pclClass.getDeclaredField("path");
fPath.setAccessible(true);
String orgPath = fPath.get(pcl).toString();
String pathToAdd = joinPaths(jarPathsToAppend);
String path = orgPath + ':' + pathToAdd;
forceSet(pcl, fPath, path);
boolean wantDex = System.getProperty("android.vm.dexfile", "").equals("true");
File[] files = new File[oldSize + jarPathsToAppend.length];
ZipFile[] zips = new ZipFile[oldSize + jarPathsToAppend.length];
DexFile[] dexs = new DexFile[oldSize + jarPathsToAppend.length];
Field fmPaths = pclClass.getDeclaredField("mPaths");
String[] newMPaths = new String[oldSize + jarPathsToAppend.length];
// set originals
newMPaths[0] = (String) forceGetFirst(pcl, fmPaths);
forceSet(pcl, fmPaths, newMPaths);
Field fmFiles = pclClass.getDeclaredField("mFiles");
files[0] = (File) forceGetFirst(pcl, fmFiles);
Field fmZips = pclClass.getDeclaredField("mZips");
zips[0] = (ZipFile) forceGetFirst(pcl, fmZips);
Field fmDexs = pclClass.getDeclaredField("mDexs");
dexs[0] = (DexFile) forceGetFirst(pcl, fmDexs);
for (int i = 0; i < jarPathsToAppend.length; i++) {
newMPaths[oldSize + i] = jarPathsToAppend[i];
File pathFile = new File(jarPathsToAppend[i]);
files[oldSize + i] = pathFile;
zips[oldSize + i] = new ZipFile(pathFile);
if (wantDex) {
String outDexName = pathFile.getName() + ".dex";
File outFile = new File(optDir, outDexName);
dexs[oldSize + i] = DexFile.loadDex(pathFile.getAbsolutePath(), outFile.getAbsolutePath(), 0);
}
}
forceSet(pcl, fmFiles, files);
forceSet(pcl, fmZips, zips);
forceSet(pcl, fmDexs, dexs);
}
// https://android.googlesource.com/platform/libcore/+/master/libdvm/src/main/java/dalvik/system/BaseDexClassLoader.java
// https://android.googlesource.com/platform/libcore/+/master/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
public static void appendDexListImplICS(ArrayList<File> jarFiles, PathClassLoader pcl, File optDir,
boolean kitkatPlus, boolean marshmallowPlus) throws Exception {
if(debug) {
Log.d(TAG, "appendDexListImplICS(" + jarFiles);
}
// to save original values
Class bdclClass = Class.forName("dalvik.system.BaseDexClassLoader");
// ICS+ - pathList
Field fPathList = bdclClass.getDeclaredField("pathList");
fPathList.setAccessible(true);
Object dplObj = fPathList.get(pcl);
// to call DexPathList.makeDexElements() for additional jar(apk)s
Class dplClass = dplObj.getClass();
Field fDexElements = dplClass.getDeclaredField("dexElements");
fDexElements.setAccessible(true);
Object objOrgDexElements = fDexElements.get(dplObj);
int orgDexCount = Array.getLength(objOrgDexElements);
if(debug) {
Log.d(TAG, "orgDexCount : " + orgDexCount);
debugDexElements(objOrgDexElements);
}
Class clazzElement = Class.forName("dalvik.system.DexPathList$Element");
// create new merged array
int jarCount = jarFiles.size();
Object newDexElemArray = Array.newInstance(clazzElement, orgDexCount + jarCount);
System.arraycopy(objOrgDexElements, 0, newDexElemArray, 0, orgDexCount);
final Method mMakeDexElements;
if (marshmallowPlus) {
mMakeDexElements =
dplClass.getDeclaredMethod("makePathElements", List.class, File.class, List.class);
} else if (kitkatPlus) {
mMakeDexElements =
dplClass.getDeclaredMethod("makeDexElements", ArrayList.class, File.class, ArrayList.class);
} else {
mMakeDexElements = dplClass.getDeclaredMethod("makeDexElements", ArrayList.class, File.class);
}
mMakeDexElements.setAccessible(true);
Object elemsToAdd;
if (kitkatPlus) {
elemsToAdd = mMakeDexElements.invoke(null, jarFiles, optDir, new ArrayList());
} else {
elemsToAdd = mMakeDexElements.invoke(null, jarFiles, optDir);
}
for (int i = 0; i < jarCount; i++) {
int pos = orgDexCount + i;
Object elemToAdd = Array.get(elemsToAdd, i);
Array.set(newDexElemArray, pos, elemToAdd);
}
if(debug) {
Log.d(TAG, "appendDexListImplICS() " + Arrays.deepToString((Object[]) newDexElemArray));
}
forceSet(dplObj, fDexElements, newDexElemArray);
}
private static void debugDexElements(Object dexElements) throws Exception {
Object[] objArray = (Object[]) dexElements;
Class clazzElement = Class.forName("dalvik.system.DexPathList$Element");
Field fFile = clazzElement.getDeclaredField("file");
fFile.setAccessible(true);
for (int i = 0; i < objArray.length; i++) {
File f = (File) fFile.get(objArray[i]);
Log.d(TAG, "[" + i + "] " + f);
}
}
}