();
174 |
175 | final ZipFile apk = new ZipFile(sourceApk);
176 | try {
177 |
178 | int secondaryNumber = 2;
179 |
180 | ZipEntry dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX);
181 | while (dexFile != null) {
182 | String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
183 | File extractedFile = new File(dexDir, fileName);
184 | files.add(extractedFile);
185 |
186 | Log.i(TAG, "Extraction is needed for file " + extractedFile);
187 | int numAttempts = 0;
188 | boolean isExtractionSuccessful = false;
189 | while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isExtractionSuccessful) {
190 | numAttempts++;
191 |
192 | // Create a zip file (extractedFile) containing only the secondary dex file
193 | // (dexFile) from the apk.
194 | extract(apk, dexFile, extractedFile, extractedFilePrefix);
195 |
196 | // Verify that the extracted file is indeed a zip file.
197 | isExtractionSuccessful = verifyZipFile(extractedFile);
198 |
199 | // Log the sha1 of the extracted zip file
200 | Log.i(TAG, "Extraction " + (isExtractionSuccessful ? "success" : "failed") +
201 | " - length " + extractedFile.getAbsolutePath() + ": " +
202 | extractedFile.length());
203 | if (!isExtractionSuccessful) {
204 | // Delete the extracted file
205 | extractedFile.delete();
206 | if (extractedFile.exists()) {
207 | Log.w(TAG, "Failed to delete corrupted secondary dex '" +
208 | extractedFile.getPath() + "'");
209 | }
210 | }
211 | }
212 | if (!isExtractionSuccessful) {
213 | throw new IOException("Could not create zip file " +
214 | extractedFile.getAbsolutePath() + " for secondary dex (" +
215 | secondaryNumber + ")");
216 | }
217 | secondaryNumber++;
218 | dexFile = apk.getEntry(DEX_PREFIX + secondaryNumber + DEX_SUFFIX);
219 | }
220 | } finally {
221 | try {
222 | apk.close();
223 | } catch (IOException e) {
224 | Log.w(TAG, "Failed to close resource", e);
225 | }
226 | }
227 |
228 | return files;
229 | }
230 |
231 | private static void putStoredApkInfo(Context context, long timeStamp, long crc,
232 | int totalDexNumber) {
233 | SharedPreferences prefs = getMultiDexPreferences(context);
234 | SharedPreferences.Editor edit = prefs.edit();
235 | edit.putLong(KEY_TIME_STAMP, timeStamp);
236 | edit.putLong(KEY_CRC, crc);
237 | /* SharedPreferences.Editor doc says that apply() and commit() "atomically performs the
238 | * requested modifications" it should be OK to rely on saving the dex files number (getting
239 | * old number value would go along with old crc and time stamp).
240 | */
241 | edit.putInt(KEY_DEX_NUMBER, totalDexNumber);
242 | apply(edit);
243 | }
244 |
245 | private static SharedPreferences getMultiDexPreferences(Context context) {
246 | return context.getSharedPreferences(PREFS_FILE,
247 | Build.VERSION.SDK_INT < 11 /* Build.VERSION_CODES.HONEYCOMB */
248 | ? Context.MODE_PRIVATE
249 | : Context.MODE_PRIVATE | 0x0004 /* Context.MODE_MULTI_PROCESS */);
250 | }
251 |
252 | /**
253 | * This removes any files that do not have the correct prefix.
254 | */
255 | private static void prepareDexDir(File dexDir, final String extractedFilePrefix)
256 | throws IOException {
257 | /* mkdirs() has some bugs, especially before jb-mr1 and we have only a maximum of one parent
258 | * to create, lets stick to mkdir().
259 | */
260 | File cache = dexDir.getParentFile();
261 | mkdirChecked(cache);
262 | mkdirChecked(dexDir);
263 |
264 | // Clean possible old files
265 | FileFilter filter = new FileFilter() {
266 |
267 | @Override
268 | public boolean accept(File pathname) {
269 | return !pathname.getName().startsWith(extractedFilePrefix);
270 | }
271 | };
272 | File[] files = dexDir.listFiles(filter);
273 | if (files == null) {
274 | Log.w(TAG, "Failed to list secondary dex dir content (" + dexDir.getPath() + ").");
275 | return;
276 | }
277 | for (File oldFile : files) {
278 | Log.i(TAG, "Trying to delete old file " + oldFile.getPath() + " of size " +
279 | oldFile.length());
280 | if (!oldFile.delete()) {
281 | Log.w(TAG, "Failed to delete old file " + oldFile.getPath());
282 | } else {
283 | Log.i(TAG, "Deleted old file " + oldFile.getPath());
284 | }
285 | }
286 | }
287 |
288 | private static void mkdirChecked(File dir) throws IOException {
289 | dir.mkdir();
290 | if (!dir.isDirectory()) {
291 | File parent = dir.getParentFile();
292 | if (parent == null) {
293 | Log.e(TAG, "Failed to create dir " + dir.getPath() + ". Parent file is null.");
294 | } else {
295 | Log.e(TAG, "Failed to create dir " + dir.getPath() +
296 | ". parent file is a dir " + parent.isDirectory() +
297 | ", a file " + parent.isFile() +
298 | ", exists " + parent.exists() +
299 | ", readable " + parent.canRead() +
300 | ", writable " + parent.canWrite());
301 | }
302 | throw new IOException("Failed to create cache directory " + dir.getPath());
303 | }
304 | }
305 |
306 | private static void extract(ZipFile apk, ZipEntry dexFile, File extractTo,
307 | String extractedFilePrefix) throws IOException, FileNotFoundException {
308 |
309 | InputStream in = apk.getInputStream(dexFile);
310 | ZipOutputStream out = null;
311 | File tmp = File.createTempFile(extractedFilePrefix, EXTRACTED_SUFFIX,
312 | extractTo.getParentFile());
313 | Log.i(TAG, "Extracting " + tmp.getPath());
314 | try {
315 | out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(tmp)));
316 | try {
317 | ZipEntry classesDex = new ZipEntry("classes.dex");
318 | // keep zip entry time since it is the criteria used by Dalvik
319 | classesDex.setTime(dexFile.getTime());
320 | out.putNextEntry(classesDex);
321 |
322 | byte[] buffer = new byte[BUFFER_SIZE];
323 | int length = in.read(buffer);
324 | while (length != -1) {
325 | out.write(buffer, 0, length);
326 | length = in.read(buffer);
327 | }
328 | out.closeEntry();
329 | } finally {
330 | out.close();
331 | }
332 | Log.i(TAG, "Renaming to " + extractTo.getPath());
333 | if (!tmp.renameTo(extractTo)) {
334 | throw new IOException("Failed to rename \"" + tmp.getAbsolutePath() +
335 | "\" to \"" + extractTo.getAbsolutePath() + "\"");
336 | }
337 | } finally {
338 | closeQuietly(in);
339 | tmp.delete(); // return status ignored
340 | }
341 | }
342 |
343 | /**
344 | * Returns whether the file is a valid zip file.
345 | */
346 | static boolean verifyZipFile(File file) {
347 | try {
348 | ZipFile zipFile = new ZipFile(file);
349 | try {
350 | zipFile.close();
351 | return true;
352 | } catch (IOException e) {
353 | Log.w(TAG, "Failed to close zip file: " + file.getAbsolutePath());
354 | }
355 | } catch (ZipException ex) {
356 | Log.w(TAG, "File " + file.getAbsolutePath() + " is not a valid zip file.", ex);
357 | } catch (IOException ex) {
358 | Log.w(TAG, "Got an IOException trying to open zip file: " + file.getAbsolutePath(), ex);
359 | }
360 | return false;
361 | }
362 |
363 | /**
364 | * Closes the given {@code Closeable}. Suppresses any IO exceptions.
365 | */
366 | private static void closeQuietly(Closeable closeable) {
367 | try {
368 | closeable.close();
369 | } catch (IOException e) {
370 | Log.w(TAG, "Failed to close resource", e);
371 | }
372 | }
373 |
374 | // The following is taken from SharedPreferencesCompat to avoid having a dependency of the
375 | // multidex support library on another support library.
376 | private static Method sApplyMethod; // final
377 | static {
378 | try {
379 | Class> cls = SharedPreferences.Editor.class;
380 | sApplyMethod = cls.getMethod("apply");
381 | } catch (NoSuchMethodException unused) {
382 | sApplyMethod = null;
383 | }
384 | }
385 |
386 | private static void apply(SharedPreferences.Editor editor) {
387 | if (sApplyMethod != null) {
388 | try {
389 | sApplyMethod.invoke(editor);
390 | return;
391 | } catch (InvocationTargetException unused) {
392 | // fall through
393 | } catch (IllegalAccessException unused) {
394 | // fall through
395 | }
396 | }
397 | editor.commit();
398 | }
399 | }
400 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/dex/ZipUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | /* Apache Harmony HEADER because the code in this class comes mostly from ZipFile, ZipEntry and
18 | * ZipConstants from android libcore.
19 | */
20 |
21 | package com.baidu.music.classlib.dex;
22 |
23 | import java.io.File;
24 | import java.io.IOException;
25 | import java.io.RandomAccessFile;
26 | import java.util.zip.CRC32;
27 | import java.util.zip.ZipException;
28 |
29 | /**
30 | * Tools to build a quick partial crc of zip files.
31 | * @modify by Jarlene on 2015/11/23.
32 | */
33 | final class ZipUtil {
34 | static class CentralDirectory {
35 | long offset;
36 | long size;
37 | }
38 |
39 | /* redefine those constant here because of bug 13721174 preventing to compile using the
40 | * constants defined in ZipFile */
41 | private static final int ENDHDR = 22;
42 | private static final int ENDSIG = 0x6054b50;
43 |
44 | /**
45 | * Size of reading buffers.
46 | */
47 | private static final int BUFFER_SIZE = 0x4000;
48 |
49 | /**
50 | * Compute crc32 of the central directory of an apk. The central directory contains
51 | * the crc32 of each entries in the zip so the computed result is considered valid for the whole
52 | * zip file. Does not support zip64 nor multidisk but it should be OK for now since ZipFile does
53 | * not either.
54 | */
55 | static long getZipCrc(File apk) throws IOException {
56 | RandomAccessFile raf = new RandomAccessFile(apk, "r");
57 | try {
58 | CentralDirectory dir = findCentralDirectory(raf);
59 |
60 | return computeCrcOfCentralDir(raf, dir);
61 | } finally {
62 | raf.close();
63 | }
64 | }
65 |
66 | /* Package visible for testing */
67 | static CentralDirectory findCentralDirectory(RandomAccessFile raf) throws IOException,
68 | ZipException {
69 | long scanOffset = raf.length() - ENDHDR;
70 | if (scanOffset < 0) {
71 | throw new ZipException("File too short to be a zip file: " + raf.length());
72 | }
73 |
74 | long stopOffset = scanOffset - 0x10000 /* ".ZIP file comment"'s max length */;
75 | if (stopOffset < 0) {
76 | stopOffset = 0;
77 | }
78 |
79 | int endSig = Integer.reverseBytes(ENDSIG);
80 | while (true) {
81 | raf.seek(scanOffset);
82 | if (raf.readInt() == endSig) {
83 | break;
84 | }
85 |
86 | scanOffset--;
87 | if (scanOffset < stopOffset) {
88 | throw new ZipException("End Of Central Directory signature not found");
89 | }
90 | }
91 | // Read the End Of Central Directory. ENDHDR includes the signature
92 | // bytes,
93 | // which we've already read.
94 |
95 | // Pull out the information we need.
96 | raf.skipBytes(2); // diskNumber
97 | raf.skipBytes(2); // diskWithCentralDir
98 | raf.skipBytes(2); // numEntries
99 | raf.skipBytes(2); // totalNumEntries
100 | CentralDirectory dir = new CentralDirectory();
101 | dir.size = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL;
102 | dir.offset = Integer.reverseBytes(raf.readInt()) & 0xFFFFFFFFL;
103 | return dir;
104 | }
105 |
106 | /* Package visible for testing */
107 | static long computeCrcOfCentralDir(RandomAccessFile raf, CentralDirectory dir)
108 | throws IOException {
109 | CRC32 crc = new CRC32();
110 | long stillToRead = dir.size;
111 | raf.seek(dir.offset);
112 | int length = (int) Math.min(BUFFER_SIZE, stillToRead);
113 | byte[] buffer = new byte[BUFFER_SIZE];
114 | length = raf.read(buffer, 0, length);
115 | while (length != -1) {
116 | crc.update(buffer, 0, length);
117 | stillToRead -= length;
118 | if (stillToRead == 0) {
119 | break;
120 | }
121 | length = (int) Math.min(BUFFER_SIZE, stillToRead);
122 | length = raf.read(buffer, 0, length);
123 | }
124 | return crc.getValue();
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/exception/ReflectException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2011-2013, Lukas Eder, lukas.eder@gmail.com
3 | * All rights reserved.
4 | *
5 | * This software is licensed to you under the Apache License, Version 2.0
6 | * (the "License"); You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Redistribution and use in source and binary forms, with or without
11 | * modification, are permitted provided that the following conditions are met:
12 | *
13 | * . Redistributions of source code must retain the above copyright notice, this
14 | * list of conditions and the following disclaimer.
15 | *
16 | * . Redistributions in binary form must reproduce the above copyright notice,
17 | * this list of conditions and the following disclaimer in the documentation
18 | * and/or other materials provided with the distribution.
19 | *
20 | * . Neither the name "jOOR" nor the names of its contributors may be
21 | * used to endorse or promote products derived from this software without
22 | * specific prior written permission.
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | */
36 | package com.baidu.music.classlib.exception;
37 |
38 | import java.lang.reflect.InvocationTargetException;
39 |
40 | /**
41 | * A unchecked wrapper for any of Java's checked reflection exceptions:
42 | *
43 | * These exceptions are
44 | *
45 | * - {@link ClassNotFoundException}
46 | * - {@link IllegalAccessException}
47 | * - {@link IllegalArgumentException}
48 | * - {@link InstantiationException}
49 | * - {@link InvocationTargetException}
50 | * - {@link NoSuchMethodException}
51 | * - {@link NoSuchFieldException}
52 | * - {@link SecurityException}
53 | *
54 | *
55 | * @author Lukas Eder
56 | * @modify by Jarlene on 2015/11/23.
57 | */
58 | public class ReflectException extends RuntimeException {
59 |
60 | /**
61 | * Generated UID
62 | */
63 | private static final long serialVersionUID = -6213149635297151442L;
64 |
65 | public ReflectException(String message) {
66 | super(message);
67 | }
68 |
69 | public ReflectException(String message, Throwable cause) {
70 | super(message, cause);
71 | }
72 |
73 | public ReflectException() {
74 | super();
75 | }
76 |
77 | public ReflectException(Throwable cause) {
78 | super(cause);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/jni/HookBridge.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.jni;
2 |
3 | import android.os.Build;
4 | import android.text.TextUtils;
5 |
6 | import java.lang.reflect.Member;
7 | import java.lang.reflect.Method;
8 | import java.util.List;
9 |
10 | /**
11 | * Jni接口,只对dalvik有效,art模式不需要,原因是在art模式下,可以原生实现class补丁。
12 | * 但是method替换在art模式下不能使用,之后会将这部分代码单独移除。
13 | * Created by Jarlene on 2015/11/23.
14 | */
15 | public final class HookBridge {
16 |
17 | private static final String vmVersion = System.getProperty("java.vm.version");
18 | private static final boolean isArt = (vmVersion != null && vmVersion.startsWith("2")) || (Build.VERSION.SDK_INT > 19);
19 |
20 | static{
21 | if (!isArt) {
22 | try {
23 | System.loadLibrary("commonHook");
24 | } catch (Exception e) {
25 | e.printStackTrace();
26 | }
27 | }
28 |
29 | }
30 |
31 | /**
32 | * 初始化
33 | * @return
34 | */
35 | public static boolean initJNIEnv() {
36 | if (isArt) {
37 | return false;
38 | }
39 | try {
40 | int apiLevel = Build.VERSION.SDK_INT;
41 | return initHookEnv(isArt, apiLevel);
42 | } catch (Exception e) {
43 | e.printStackTrace();
44 | }
45 | return false;
46 | }
47 |
48 | /**
49 | * hook Java层Method
50 | * @param src
51 | * @param target
52 | */
53 | public static void hookJavaMethod(Method src, Method target) {
54 | if (src == null || target == null) {
55 | return;
56 | }
57 | if (isArt) {
58 | return;
59 | }
60 | try {
61 | replaceJavaMethod(src, target);
62 | } catch (Exception e) {
63 | e.printStackTrace();
64 | }
65 |
66 | }
67 |
68 | /**
69 | * hook Native层 Method
70 | * @param oldSoName
71 | * @param newSoName
72 | * @param methodName
73 | * @param sym
74 | */
75 | public static void hookNativeMethod(String oldSoName, String newSoName,
76 | String methodName, String sym) {
77 | if (TextUtils.isEmpty(oldSoName) || TextUtils.isEmpty(newSoName)
78 | || TextUtils.isEmpty(sym) || TextUtils.isEmpty(methodName)) {
79 | return;
80 | }
81 | if (isArt) {
82 | return;
83 | }
84 | try {
85 | replaceNativeMethod(oldSoName, newSoName, methodName, sym);
86 | } catch (Exception e) {
87 | e.printStackTrace();
88 | }
89 | }
90 |
91 | public static void switchOpenResolved(boolean open) {
92 | if (isArt) {
93 | return;
94 | }
95 | closeOrOpenGetResolvedClass(open);
96 | }
97 |
98 | /**
99 | * 添加过滤的class名,用于实时生效。
100 | * @param clazzNames
101 | */
102 | @Deprecated
103 | public static void classesFilter(List clazzNames) {
104 | if (clazzNames != null && !clazzNames.isEmpty()) {
105 | String[] clazz = new String[clazzNames.size()];
106 | int i = 0;
107 | for (String cl : clazzNames) {
108 | clazz[i++] = cl;
109 | }
110 | classesResolvedFilter(clazz);
111 | }
112 | }
113 |
114 | private static native boolean initHookEnv(boolean isArt, int apiLevel);
115 |
116 | private static native void replaceJavaMethod(Member src, Member target);
117 |
118 | private static native void replaceNativeMethod(String oldSoName, String newSoName,
119 | String oldSymbol, String newSymbol);
120 |
121 | private static native void classesResolvedFilter(String[] classNames);
122 |
123 | private static native void closeOrOpenGetResolvedClass(boolean close);
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/listener/FileOperatorListener.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.listener;
2 |
3 | /**
4 | * 文件操作回调
5 | * Created by Jarlenr on 2015/10/15.
6 | */
7 | public interface FileOperatorListener {
8 |
9 | public static final int ERROR_CODE_NOSPACE = 0; // 空间不足
10 | public static final int ERROR_CODE_UNKNOW = 1; // 未知错误
11 | public static final int ERROR_CODE_INVALIDATE = 2; // 文件无效
12 | public static final int ERROR_VERSION_CODE = 3; // 版本错误
13 |
14 | void notifyCompleted();
15 | void notifyError(int errorCode);
16 | }
17 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/manager/BaseManager.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.manager;
2 |
3 | import java.util.Collections;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | /**
8 | * 基础管理类
9 | * Created by Jarlene on 2015/12/7.
10 | */
11 | public abstract class BaseManager {
12 | protected final Map patchMap = Collections.synchronizedMap(new HashMap());
13 |
14 | /**
15 | * 添加Item
16 | * @param key
17 | * @param value
18 | */
19 | public void addItem(K key, V value){
20 | patchMap.put(key, value);
21 | }
22 |
23 | /**
24 | * 移除Item
25 | * @param key
26 | */
27 | public void removeItem(K key){
28 | if (patchMap.containsKey(key)) {
29 | patchMap.remove(key);
30 | }
31 | }
32 |
33 | /**
34 | * 获取Map
35 | * @return
36 | */
37 | public Map getHashmap(){
38 | return patchMap;
39 | }
40 |
41 | /**
42 | * 得到Item
43 | */
44 | public V getItem(K key){
45 | return patchMap.get(key);
46 | }
47 |
48 | /**
49 | * 是否包含key
50 | * @param key
51 | * @return
52 | */
53 | public boolean isContainKey(K key) {
54 | return patchMap.containsKey(key);
55 | }
56 |
57 | /**
58 | * 是否包含Value
59 | * @param value
60 | * @return
61 | */
62 | public boolean isContainValue(V value) {
63 | return patchMap.containsValue(value);
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/manager/ContextManager.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.manager;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 |
6 | import com.baidu.music.classlib.resource.PatchContext;
7 |
8 | /**
9 | * patch apk的伪Context管理类,主要是用于获取patch中的资源文件
10 | * Created by Jarlene on 2015/12/2.
11 | */
12 | public class ContextManager extends BaseManager{
13 |
14 | private static ContextManager instance = null;
15 |
16 | /**
17 | * 获得patch上下文管理实例
18 | *
19 | * @return 管理器实例
20 | */
21 | public static ContextManager getInstance() {
22 | if (instance == null) {
23 | synchronized (ContextManager.class) {
24 | if (instance == null) {
25 | instance = new ContextManager();
26 | }
27 | }
28 | }
29 | return instance;
30 | }
31 |
32 | /**
33 | * 获取Context
34 | * @param context
35 | * @param apkPath
36 | * @return
37 | */
38 | public PatchContext getContext(Context context, String apkPath) {
39 | PatchContext patchContext = getItem(apkPath);
40 | if (patchContext == null) {
41 | if (context == null) {
42 | return null;
43 | }
44 | patchContext = new PatchContext(context, apkPath);
45 | if (patchContext != null) {
46 | addItem(apkPath, patchContext);
47 | }
48 |
49 | }
50 | return patchContext;
51 | }
52 |
53 | /**
54 | * 移除Context
55 | * @param apkPath
56 | */
57 | public void removeContext(String apkPath) {
58 | if (TextUtils.isEmpty(apkPath)) {
59 | throw new NullPointerException("Context is null");
60 | }
61 | removeItem(apkPath);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/manager/HookManager.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.manager;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.os.HandlerThread;
6 | import android.text.TextUtils;
7 |
8 | import com.baidu.music.classlib.build.DexClient;
9 | import com.baidu.music.classlib.listener.FileOperatorListener;
10 | import com.baidu.music.classlib.utils.ApkUtils;
11 | import com.baidu.music.classlib.utils.FileUtils;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * 如果想对UI界面进行调整,要将相关的资源文件打包进patch中,
20 | * 我已经为其提供了PatchResource工具,在主apk中,
21 | * 在用到资源文件的时候,使用就行了。
22 | * Created by Jarlene on 2015/11/23.
23 | */
24 | public class HookManager extends BaseManager{
25 |
26 |
27 | private static volatile HookManager instance;
28 | private final IOHandlerProvider ioHandlerProvider;
29 |
30 | private String signture = null;
31 |
32 | private HookManager() {
33 | ioHandlerProvider = new IOHandlerProvider();
34 | }
35 |
36 | public static HookManager getInstance() {
37 | if (instance == null) {
38 | synchronized (HookManager.class) {
39 | if (instance == null) {
40 | instance = new HookManager();
41 | }
42 | }
43 | }
44 | return instance;
45 | }
46 |
47 | /**
48 | * 注册签名信息,所以有的patch apk签名都要一样。
49 | * @param signture
50 | */
51 | public void registerSignture(String signture) {
52 | if (TextUtils.isEmpty(signture)) {
53 | throw new NullPointerException("the signture is null");
54 | }
55 | this.signture = signture;
56 | }
57 |
58 | /**
59 | * 检查签名是否有效
60 | */
61 | private void checkSignture() {
62 | if (TextUtils.isEmpty(signture)) {
63 | throw new NullPointerException("the signture is null");
64 | }
65 | }
66 |
67 | /**
68 | * 添加patch
69 | * @param patchName
70 | * @param file
71 | */
72 | public void addPatch(String patchName, File file) {
73 | if ((TextUtils.isEmpty(patchName)) || file == null || !file.exists()) {
74 | return;
75 | }
76 | addItem(patchName, file);
77 | }
78 |
79 | /**
80 | * 移除patch
81 | * @param patchName
82 | */
83 | public void removePatch(String patchName) {
84 | if ((TextUtils.isEmpty(patchName))) {
85 | return;
86 | }
87 | removeItem(patchName);
88 | }
89 |
90 | /**
91 | * 获取Patch
92 | * @param patchName
93 | * @return
94 | */
95 | public File getPatch(String patchName) {
96 | if ((TextUtils.isEmpty(patchName))) {
97 | return null;
98 | }
99 | return getItem(patchName);
100 | }
101 |
102 | /**
103 | * 验证patch的有效性
104 | * 这个验证有点草率,以后会采用更好的验证
105 | * 采用签名进行验证
106 | * @param context
107 | * @param apkPath
108 | * @return
109 | */
110 | public boolean isValidate(Context context, String apkPath) {
111 | boolean validate = false;
112 | if (!TextUtils.isEmpty(apkPath)) {
113 | File apkFile = new File(apkPath);
114 | if (apkFile.getAbsolutePath().endsWith(".apk")) {
115 | if (ApkUtils.getPackageInfo(context, apkPath) != null
116 | /*&& signture.equals(ApkUtils.getApkSignatures(context,apkPath)[0])*/) {
117 | validate = true;
118 | }
119 | }
120 | }
121 | return validate;
122 | }
123 |
124 | /**
125 | * 取得patch的版本
126 | * 这个有点草率,以后修改
127 | * @param context
128 | * @param apkPath
129 | * @return
130 | */
131 | public int getVersion(Context context, String apkPath) {
132 | return ApkUtils.getApkVersio(context, apkPath);
133 | }
134 |
135 | /**
136 | * 去掉无效的patch
137 | * @param context
138 | * @param files
139 | * @return
140 | */
141 | public List patchFileFilter(Context context, List files) {
142 | if (context == null || files == null) {
143 | throw new NullPointerException("Context is null or files is Empty");
144 | }
145 | // checkSignture();
146 | List fileList = new ArrayList();
147 | for (File file : files) {
148 | if (isValidate(context, file.getAbsolutePath())) {
149 | fileList.add(file);
150 | }
151 | }
152 | return fileList;
153 | }
154 |
155 | /**
156 | * 将Apk文件拷贝到相关的目录下。
157 | * @param context
158 | * @param apkPath
159 | * @param listener
160 | */
161 | public void copyFile(final Context context, final String apkPath,
162 | final FileOperatorListener listener) {
163 | if (TextUtils.isEmpty(apkPath) || context == null || listener == null) {
164 | throw new NullPointerException("apkPath is null or context is null or callback is null");
165 | }
166 | final File srcFile = new File(apkPath);
167 | final File destFile = new File(getPatchDir(context), srcFile.getName());
168 | if (destFile.exists()) {
169 | if (getVersion(context, apkPath) > getVersion(context, destFile.getAbsolutePath())) {
170 | gotoCopy(srcFile, destFile, listener);
171 | } else {
172 | listener.notifyError(FileOperatorListener.ERROR_VERSION_CODE);
173 | }
174 | } else {
175 | gotoCopy(srcFile, destFile, listener);
176 | }
177 | }
178 |
179 | /**
180 | * 执行文件复制操作
181 | * @param srcFile
182 | * @param destFile
183 | * @param listener
184 | */
185 | private void gotoCopy(final File srcFile, final File destFile, final FileOperatorListener listener) {
186 | if (!srcFile.exists()) {
187 | listener.notifyError(FileOperatorListener.ERROR_CODE_INVALIDATE);
188 | return;
189 | }
190 | ioHandlerProvider.getIOHandler().post(new Runnable() {
191 | @Override
192 | public void run() {
193 | try {
194 | FileUtils.copyFile(srcFile, destFile);
195 | listener.notifyCompleted();
196 | } catch (IOException e) {
197 | listener.notifyError(FileOperatorListener.ERROR_CODE_UNKNOW);
198 | e.printStackTrace();
199 | }
200 | }
201 | });
202 | }
203 |
204 | /**
205 | * 将文件复制到相关目录下面
206 | * @param context
207 | * @param savePath
208 | * @param srcPath
209 | * @param listener
210 | */
211 | public void copyFile(final Context context, final String savePath, final String srcPath,
212 | final FileOperatorListener listener) {
213 | if (TextUtils.isEmpty(srcPath) || TextUtils.isEmpty(savePath)
214 | || context == null || listener == null) {
215 | throw new NullPointerException("savePath or srcPath is null or context is null or callback is null");
216 | }
217 | File srcFile = new File(srcPath);
218 | File destFile = new File(savePath);
219 | gotoCopy(srcFile, destFile, listener);
220 | }
221 |
222 | /**
223 | * 生成dex文件 ,主要是在线程中做,通知回调
224 | * 主要是将.class文件和。jar文件生成dex
225 | * @param classDir
226 | * @param dexPath
227 | * @param listener
228 | */
229 | public void makeDex(final String classDir, final String dexPath,
230 | final FileOperatorListener listener) {
231 | if (TextUtils.isEmpty(classDir) || TextUtils.isEmpty(dexPath) || listener == null) {
232 | throw new NullPointerException("classDir is null or dexPath is null or listener is null");
233 | }
234 | ioHandlerProvider.getIOHandler().post(new Runnable() {
235 | @Override
236 | public void run() {
237 | File classPathFileDir = new File(classDir);
238 | if (!classPathFileDir.exists()) {
239 | return;
240 | }
241 | File[] classPathF = classPathFileDir.listFiles();
242 | if (classPathF != null && classPathF.length > 0) {
243 | int size = classPathF.length;
244 | List classList = new ArrayList();
245 | for (int i = 0; i < size; i++) {
246 | String path = classPathF[i].getAbsolutePath();
247 | if (path.endsWith(".class") || path.endsWith(".jar")) {
248 | classList.add(classPathF[i].getAbsolutePath());
249 | }
250 | }
251 | DexClient client = new DexClient();
252 | client.makeDex(classList, dexPath);
253 | }
254 | if (FileUtils.isExist(dexPath)) {
255 | listener.notifyCompleted();
256 | } else {
257 | listener.notifyError(FileOperatorListener.ERROR_CODE_UNKNOW);
258 | }
259 | }
260 | });
261 | }
262 |
263 | /**
264 | * 获取patch存放的位置目录
265 | * data/data/packageName/aap_patch/
266 | * @param context
267 | * @return
268 | */
269 | public File getPatchDir(Context context) {
270 | if (context == null) {
271 | throw new NullPointerException("context is null");
272 | }
273 | return context.getDir("patch", Context.MODE_PRIVATE);
274 | }
275 |
276 | /**
277 | * 获取patch opt后存放的位置目录
278 | * data/data/packageName/aap_patchOpt/
279 | * @param context
280 | * @return
281 | */
282 | public File getPatchOptDir(Context context) {
283 | if (context == null) {
284 | throw new NullPointerException("context is null");
285 | }
286 | return context.getDir("patchOpt", Context.MODE_PRIVATE);
287 | }
288 |
289 |
290 | /**
291 | * so patch保存的路径
292 | * @param context
293 | * @return
294 | */
295 | public File getSoPatchDir(Context context) {
296 | if (context == null) {
297 | throw new NullPointerException("context is null");
298 | }
299 | return context.getDir("soPatch", Context.MODE_PRIVATE);
300 | }
301 |
302 | /**
303 | * IO操作线程
304 | */
305 | private static class IOHandlerProvider {
306 |
307 | private Handler mIOHandler;
308 | private IOHandlerProvider(){
309 | HandlerThread thread = new HandlerThread("patch");
310 | thread.start();
311 | mIOHandler = new Handler(thread.getLooper());
312 | }
313 | public Handler getIOHandler(){
314 | return mIOHandler;
315 | }
316 | }
317 |
318 | }
319 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/manager/ResourceManager.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.manager;
2 |
3 | import android.content.Context;
4 | import android.text.TextUtils;
5 |
6 | import com.baidu.music.classlib.resource.PatchContext;
7 | import com.baidu.music.classlib.resource.PatchResource;
8 |
9 | /**
10 | * patch Apk的资源文件管理
11 | * Created by Jarlene on 2015/12/2.
12 | */
13 | public class ResourceManager extends BaseManager{
14 |
15 | private static ResourceManager instance = null;
16 |
17 | /**
18 | * 获得Patch单例实例
19 | *
20 | * @return 管理器实例
21 | */
22 | public static ResourceManager getInstance() {
23 | if (instance == null) {
24 | synchronized (ResourceManager.class) {
25 | if (instance == null) {
26 | instance = new ResourceManager();
27 | }
28 | }
29 | }
30 | return instance;
31 | }
32 |
33 | /**
34 | * 获取PatchResource
35 | * @param context
36 | * @param apkPath
37 | * @return
38 | */
39 | public PatchResource getPatchResource(Context context, String apkPath) {
40 | PatchResource patchResource = getItem(apkPath);
41 | if (patchResource == null) {
42 | if (context == null) {
43 | return null;
44 | }
45 | PatchContext patchContext = ContextManager.getInstance().getContext(context, apkPath);
46 | if (patchContext != null) {
47 | patchResource = new PatchResource(patchContext, apkPath);
48 | }
49 | if (patchResource != null) {
50 | addItem(apkPath, patchResource);
51 | }
52 | }
53 | return patchResource;
54 | }
55 |
56 | /**
57 | * 移除Context
58 | * @param apkPath
59 | */
60 | public void removePatchResource(String apkPath) {
61 | if (TextUtils.isEmpty(apkPath)) {
62 | throw new NullPointerException("Context is null");
63 | }
64 | removeItem(apkPath);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/resource/PatchContext.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.resource;
2 |
3 | import android.content.Context;
4 | import android.content.res.AssetManager;
5 | import android.content.res.Resources;
6 | import android.view.ContextThemeWrapper;
7 |
8 | import java.lang.reflect.InvocationTargetException;
9 | import java.lang.reflect.Method;
10 |
11 | /**
12 | * 主要为patch apk实现资源提取(伪Context)
13 | * Created by Jarlene on 2015/12/1.
14 | */
15 | public class PatchContext extends ContextThemeWrapper {
16 |
17 | private AssetManager mAssetManager;
18 | private Resources mResources;
19 | private Resources mProxyResource;
20 | private Context mContext;
21 | private String mPatchPath;
22 |
23 | public PatchContext(Context base, String apkPath) {
24 | super(base, 0);
25 | this.mContext = base;
26 | this.mProxyResource = base.getResources();
27 | this.mPatchPath = apkPath;
28 |
29 | }
30 |
31 | @Override
32 | public Resources getResources() {
33 | if (mResources == null) {
34 | mResources = new Resources(getAssets(), mProxyResource.getDisplayMetrics(),
35 | mProxyResource.getConfiguration());
36 | }
37 | return mResources;
38 | }
39 |
40 | @Override
41 | public AssetManager getAssets() {
42 | if (mAssetManager == null) {
43 | mAssetManager = (AssetManager) newInstanceObject(AssetManager.class);
44 | invokeMethod(mAssetManager, "addAssetPath", new Class[]{String.class}, new Object[]{mPatchPath});
45 | }
46 | return mAssetManager;
47 | }
48 |
49 | private Object invokeMethod(Object obj, String methodName, Class[] valueType, Object[] values) {
50 | try {
51 | Class> clazz = obj.getClass();
52 | Method method = clazz.getDeclaredMethod(methodName, valueType);
53 | method.setAccessible(true);
54 | return method.invoke(obj, values);
55 | } catch (IllegalAccessException e) {
56 | e.printStackTrace();
57 | } catch (InvocationTargetException e) {
58 | e.printStackTrace();
59 | } catch (NoSuchMethodException e) {
60 | e.printStackTrace();
61 | }
62 | return null;
63 | }
64 |
65 | private Object newInstanceObject(Class> clazz){
66 | try {
67 | return clazz.getConstructor().newInstance();
68 | } catch (Exception e) {
69 | e.printStackTrace();
70 | }
71 | return null;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/resource/PatchResource.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.resource;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.graphics.drawable.Drawable;
6 | import android.util.AttributeSet;
7 | import android.util.Log;
8 | import android.util.Xml;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.animation.AlphaAnimation;
12 | import android.view.animation.Animation;
13 | import android.view.animation.AnimationSet;
14 | import android.view.animation.RotateAnimation;
15 | import android.view.animation.ScaleAnimation;
16 | import android.view.animation.TranslateAnimation;
17 |
18 | import com.baidu.music.classlib.utils.ApkUtils;
19 |
20 | import org.xmlpull.v1.XmlPullParser;
21 | import org.xmlpull.v1.XmlPullParserException;
22 |
23 | import java.io.IOException;
24 |
25 | /**
26 | * 获取apk里面的资源文件
27 | * Created by Jarlene on 2015/11/23.
28 | */
29 | public class PatchResource {
30 |
31 | public static final String TAG = PatchResource.class.getSimpleName();
32 |
33 | private Resources res;// 获取的资源apk里面的res
34 | private String apkPackageName;// 资源apk里面的包名
35 | private PatchContext mPatchContext;
36 |
37 | public PatchResource(Context context, String apkPatch) {
38 | mPatchContext = new PatchContext(context, apkPatch);
39 | res = mPatchContext.getResources();
40 | apkPackageName = ApkUtils.getPackageName(context, apkPatch);
41 | if (!apkPackageName.equals("com.baidu.music.classpatch")) {
42 | apkPackageName = "com.baidu.music.classpatch";
43 | }
44 | }
45 |
46 |
47 | public PatchResource(Resources res, String apkPackageName) {
48 | this.res = res;
49 | this.apkPackageName = apkPackageName;
50 | }
51 |
52 | /**
53 | * 获取layout文件中的id号
54 | *
55 | * @param layoutName
56 | * layout名
57 | */
58 | public int getResApkLayoutId(String layoutName) {
59 | Log.d(TAG, "getResApkLayoutId");
60 | return res.getIdentifier(layoutName, "layout", apkPackageName);
61 | }
62 |
63 | /**
64 | * 获取布局layout文件
65 | *
66 | * @param context
67 | * 上下文
68 | * @params layoutName
69 | * @return view
70 | */
71 | public View getResApkLayoutView(Context context, String layoutName) {
72 | Log.d(TAG,"getResApkLayoutView");
73 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
74 | return inflater.inflate(res.getLayout(getResApkLayoutId(layoutName)), null);
75 | }
76 |
77 | /**
78 | * 获取控件view的id号
79 | *
80 | * @param widgetName
81 | * 控件名
82 | */
83 | public int getResApkWidgetViewID(String widgetName) {
84 | Log.d(TAG,"getResApkWidgetViewID");
85 | return res.getIdentifier(widgetName, "id", apkPackageName);
86 | }
87 |
88 | /**
89 | * 获取布局文件中的控件
90 | *
91 | * @params layout,资源apk中的布局(view)
92 | * @params widgetName 控件名称
93 | * @return widgetView
94 | */
95 | public View getResApkWidgetView(View layout, String widgetName) {
96 | Log.d(TAG,"getResApkWidgetView");
97 | return layout.findViewById(getResApkWidgetViewID(widgetName));
98 | }
99 |
100 | /**
101 | * 获取drawable文件的id
102 | *
103 | * @param imgName
104 | * 图片名字
105 | */
106 | public int getDrawableId(String imgName) {
107 | Log.d(TAG,"getDrawableId");
108 | return res.getIdentifier(imgName, "drawable", apkPackageName);
109 | }
110 |
111 | /**
112 | * 获取图片资源
113 | *
114 | * @param imgName
115 | * @return drawable
116 | */
117 | public Drawable getResApkDrawable(String imgName) {
118 | Log.d(TAG,"getResApkDrawable");
119 | return res.getDrawable(getDrawableId(imgName));
120 | }
121 |
122 | /**
123 | * 获取string文件中的id号
124 | *
125 | * @param stringName
126 | * 字符串在String文件中的名字
127 | */
128 | public int getResApkStringId(String stringName) {
129 | Log.d(TAG,"getResApkStringId");
130 | return res.getIdentifier(stringName, "string", apkPackageName);
131 | }
132 |
133 | /**
134 | * 获取String字符串
135 | *
136 | * @param stringName
137 | * @return string
138 | */
139 | public String getResApkString(String stringName) {
140 | Log.d(TAG,"getResApkString");
141 | return res.getString(getResApkStringId(stringName));
142 | }
143 |
144 | /**
145 | * 获取anim文件中的id号
146 | *
147 | * @param animationName
148 | */
149 | public int getResApkAnimId(String animationName) {
150 | Log.d(TAG,"getResApkAnimId");
151 | return res.getIdentifier(animationName, "anim", apkPackageName);
152 | }
153 |
154 | /**
155 | * 获取anim文件 XmlPullParser
156 | *
157 | * @param animationName
158 | * @return XmlPullParser
159 | */
160 | public XmlPullParser getResApkAnimXml(String animationName) {
161 | Log.d(TAG,"getResApkAnimXml");
162 | return res.getAnimation(getResApkAnimId(animationName));
163 | }
164 |
165 | /**
166 | * 获取动画anim
167 | *
168 | * @params animationName
169 | * @param context
170 | */
171 | public Animation getResApkAnim(Context context, String animationName) {
172 | Log.d(TAG,"getResApkAnim");
173 | Animation animation = null;
174 | XmlPullParser parser = getResApkAnimXml(animationName);
175 | AttributeSet attrs = Xml.asAttributeSet(parser);
176 | try {
177 | animation = createAnimationFromXml(context, parser, null, attrs);
178 | } catch (XmlPullParserException e) {
179 | e.printStackTrace();
180 | } catch (IOException e) {
181 | e.printStackTrace();
182 | }
183 | return animation;
184 | }
185 |
186 | /**
187 | * 获取anim动画
188 | */
189 | private Animation createAnimationFromXml(Context c, XmlPullParser parser,
190 | AnimationSet parent, AttributeSet attrs)
191 | throws XmlPullParserException, IOException {
192 | Log.d(TAG,"createAnimationFromXml");
193 | Animation anim = null;
194 | int type;
195 | int depth = parser.getDepth();
196 | while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
197 |
198 | if (type != XmlPullParser.START_TAG) {
199 | continue;
200 | }
201 | String name = parser.getName();
202 | if (name.equals("set")) {
203 | anim = new AnimationSet(c, attrs);
204 | createAnimationFromXml(c, parser, (AnimationSet) anim, attrs);
205 | } else if (name.equals("alpha")) {
206 | anim = new AlphaAnimation(c, attrs);
207 | } else if (name.equals("scale")) {
208 | anim = new ScaleAnimation(c, attrs);
209 | } else if (name.equals("rotate")) {
210 | anim = new RotateAnimation(c, attrs);
211 | } else if (name.equals("translate")) {
212 | anim = new TranslateAnimation(c, attrs);
213 | } else {
214 | throw new RuntimeException("Unknown animation name: "+ parser.getName());
215 | }
216 | if (parent != null) {
217 | parent.addAnimation(anim);
218 | }
219 | }
220 | return anim;
221 | }
222 |
223 | /**
224 | * 获取 color文件中的id号
225 | *
226 | * @param colorName
227 | */
228 | public int getResApkColorId(String colorName) {
229 | Log.d(TAG,"getResApkColorId");
230 | return res.getIdentifier(colorName, "color", apkPackageName);
231 | }
232 |
233 | /**
234 | * 获取color 值
235 | *
236 | * @param colorName
237 | * @return int
238 | */
239 |
240 | public int getResApkColor(String colorName) {
241 | Log.d(TAG,"getResApkColor");
242 | return res.getColor(getResApkColorId(colorName));
243 | }
244 |
245 | /**
246 | * 获取 dimens文件中的id号
247 | *
248 | * @param dimenName
249 | */
250 | public int getResApkDimensId(String dimenName) {
251 | Log.d(TAG,"getResApkDimensId");
252 | return res.getIdentifier(dimenName, "dimen", apkPackageName);
253 | }
254 |
255 | /**
256 | * 获取dimens文件中值
257 | *
258 | * @param dimenName
259 | * @return float
260 | */
261 | public float getResApkDimens(String dimenName) {
262 | Log.d(TAG,"getResApkDimens");
263 | return res.getDimension(getResApkDimensId(dimenName));
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/utils/ApkUtils.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.utils;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.content.pm.Signature;
7 | import android.text.TextUtils;
8 |
9 | /**
10 | * 对Patch Apk进行处理
11 | * Created by Jarlene on 2015/11/23.
12 | */
13 | public class ApkUtils {
14 |
15 |
16 | /**
17 | * 获取Apk的版本
18 | * @param context
19 | * @param apkPath
20 | * @return
21 | */
22 | public static int getApkVersio(Context context, String apkPath){
23 | if (context == null || TextUtils.isEmpty(apkPath)) {
24 | throw new NullPointerException("Context is null or apk path is null");
25 | }
26 | PackageInfo info = getPackageInfo(context, apkPath);
27 | if (info != null) {
28 | return info.versionCode;
29 | }
30 | return -1;
31 |
32 | }
33 |
34 |
35 | /**
36 | * 获取pakageName
37 | * @param context
38 | * @param apkPath
39 | * @return
40 | */
41 | public static String getPackageName(Context context, String apkPath) {
42 | if (context == null || TextUtils.isEmpty(apkPath)) {
43 | throw new NullPointerException("Context is null or apk path is null");
44 | }
45 | PackageInfo info = getPackageInfo(context, apkPath);
46 | if (info != null) {
47 | return info.packageName;
48 | }
49 | return "";
50 | }
51 |
52 | /**
53 | * 获取Apk相关的信息
54 | * @param context
55 | * @param apkPath
56 | * @return
57 | */
58 | public static PackageInfo getPackageInfo(Context context, String apkPath){
59 | if (context == null || TextUtils.isEmpty(apkPath)) {
60 | throw new NullPointerException("Context is null or apk path is null");
61 | }
62 | return context.getPackageManager().getPackageArchiveInfo(apkPath, 1);
63 | }
64 |
65 | /**
66 | * 获取Apk的签名证书。
67 | * @param context
68 | * @param apkPath
69 | * @return
70 | */
71 | public static Signature[] getApkSignatures(Context context, String apkPath) {
72 | if (context == null || TextUtils.isEmpty(apkPath)) {
73 | throw new NullPointerException("Context is null or apk path is null");
74 | }
75 | return context.getPackageManager().getPackageArchiveInfo(apkPath,
76 | PackageManager.GET_SIGNATURES).signatures;
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/classlib/src/main/java/com/baidu/music/classlib/utils/Md5Utils.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classlib.utils;
2 |
3 | import android.text.TextUtils;
4 |
5 | import java.io.FileInputStream;
6 | import java.io.IOException;
7 | import java.security.DigestInputStream;
8 | import java.security.MessageDigest;
9 | import java.security.NoSuchAlgorithmException;
10 |
11 | /**
12 | * 加密计算,对文件的MD5进行计算。
13 | * Created by Jarlene on 2015/12/7.
14 | */
15 | public class Md5Utils {
16 | private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5",
17 | "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
18 |
19 | /**
20 | * 将byte数组对象转换为Hex
21 | * @param aBytes
22 | * @return
23 | */
24 | public static String byteArrayToHexString(byte[] aBytes) {
25 | StringBuffer resultSb = new StringBuffer();
26 | for (int i = 0; i < aBytes.length; i++) {
27 | resultSb.append(byteToHexString(aBytes[i]));
28 | }
29 | return resultSb.toString();
30 | }
31 |
32 | /**
33 | * 将byte对象转换为Hex
34 | * @param aBytes
35 | * @return
36 | */
37 | private static String byteToHexString(byte aBytes) {
38 | int n = aBytes;
39 | if (n < 0)
40 | n = 256 + n;
41 | int d1 = n / 16;
42 | int d2 = n % 16;
43 | return hexDigits[d1] + hexDigits[d2];
44 | }
45 |
46 | /**
47 | * 对string进行md5加密
48 | * @param string
49 | * @return
50 | */
51 | public static String encode(String string) {
52 | if (TextUtils.isEmpty(string))
53 | return null;
54 | String resultString = null;
55 |
56 | try {
57 | resultString = string;
58 | MessageDigest md = MessageDigest.getInstance("MD5");
59 | resultString = byteArrayToHexString(md.digest(resultString
60 | .getBytes()));
61 | } catch (Exception ex) {
62 | ex.printStackTrace();
63 | }
64 | return resultString;
65 | }
66 |
67 | /**
68 | * 计算文件前n个字节的md5
69 | * @param inputFile
70 | * @param length n个字节
71 | * @return md5
72 | */
73 | public static String fileMD5(String inputFile, int length) {
74 | FileInputStream fileInputStream = null;
75 | DigestInputStream digestInputStream = null;
76 |
77 | try {
78 | // 拿到一个MD5转换器(同样,这里可以换成SHA1)
79 | MessageDigest messageDigest = MessageDigest.getInstance("MD5");
80 |
81 | // 使用DigestInputStream
82 | fileInputStream = new FileInputStream(inputFile);
83 | digestInputStream = new DigestInputStream(fileInputStream,
84 | messageDigest);
85 |
86 | // read的过程中进行MD5处理,直到读完文件
87 | byte[] buffer = new byte[length];
88 | digestInputStream.read(buffer);
89 |
90 | // 获取最终的MessageDigest
91 | messageDigest = digestInputStream.getMessageDigest();
92 |
93 | // 拿到结果,也是字节数组,包含16个元素
94 | byte[] resultByteArray = messageDigest.digest();
95 |
96 | // 同样,把字节数组转换成字符串
97 | return byteArrayToHexString(resultByteArray);
98 | } catch (NoSuchAlgorithmException e) {
99 | e.printStackTrace();
100 | return null;
101 | } catch (IOException e) {
102 | e.printStackTrace();
103 | return null;
104 | } finally {
105 | try {
106 | digestInputStream.close();
107 | } catch (Exception e) {
108 | e.printStackTrace();
109 | }
110 | try {
111 | fileInputStream.close();
112 | } catch (Exception e) {
113 | e.printStackTrace();
114 | }
115 | }
116 | }
117 |
118 | /**
119 | * 计算文件的md5
120 | * @param inputFile
121 | * @return
122 | */
123 | public static String fileMD5(String inputFile) {
124 | // 缓冲区大小(这个可以抽出一个参数)
125 | int bufferSize = 256 * 1024;
126 | FileInputStream fileInputStream = null;
127 | DigestInputStream digestInputStream = null;
128 |
129 | try {
130 | // 拿到一个MD5转换器(同样,这里可以换成SHA1)
131 | MessageDigest messageDigest = MessageDigest.getInstance("MD5");
132 |
133 | // 使用DigestInputStream
134 | fileInputStream = new FileInputStream(inputFile);
135 | digestInputStream = new DigestInputStream(fileInputStream,
136 | messageDigest);
137 |
138 | // read的过程中进行MD5处理,直到读完文件
139 | byte[] buffer = new byte[bufferSize];
140 | while (digestInputStream.read(buffer) > 0)
141 | ;
142 |
143 | // 获取最终的MessageDigest
144 | messageDigest = digestInputStream.getMessageDigest();
145 |
146 | // 拿到结果,也是字节数组,包含16个元素
147 | byte[] resultByteArray = messageDigest.digest();
148 |
149 | // 同样,把字节数组转换成字符串
150 | return byteArrayToHexString(resultByteArray);
151 | } catch (NoSuchAlgorithmException e) {
152 | e.printStackTrace();
153 | return null;
154 | } catch (IOException e) {
155 | e.printStackTrace();
156 | return null;
157 | } finally {
158 | try {
159 | digestInputStream.close();
160 | } catch (Exception e) {
161 | e.printStackTrace();
162 | }
163 | try {
164 | fileInputStream.close();
165 | } catch (Exception e) {
166 | e.printStackTrace();
167 | }
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 | subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
3 | hook \
4 | test \
5 | ))
6 |
7 |
8 |
9 | LOCAL_SRC_FILES:= base/base64.cpp \
10 | base/thread.cpp \
11 | base/NativeException.cpp
12 | CXX11_FLAGS := -std=c++11
13 | LOCAL_CFLAGS += $(CXX11_FLAGS)
14 | include $(subdirs)
15 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/Application.mk:
--------------------------------------------------------------------------------
1 | # The ARMv7 is significanly faster due to the use of the hardware FPU
2 | APP_ABI := armeabi
3 | APP_PLATFORM := android-9
4 | APP_STL := gnustl_static
5 |
6 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/Lock.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by jarlene on 2015/9/21.
3 | //
4 |
5 | #ifndef COMMONDEMO_LOCK_H
6 | #define COMMONDEMO_LOCK_H
7 |
8 | #include
9 |
10 | class CLock {
11 | public:
12 | CLock() {
13 | pthread_mutexattr_t ma;
14 | pthread_mutexattr_init(&ma);
15 | pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE);
16 | pthread_mutex_init(&m_mutex,&ma);
17 | }
18 | ~CLock() {
19 | pthread_mutex_destroy(&m_mutex);
20 | }
21 |
22 | inline void Lock() {
23 | pthread_mutex_lock(&m_mutex);
24 | }
25 |
26 | inline void Unlock() {
27 | pthread_mutex_unlock(&m_mutex);
28 | }
29 | protected:
30 | pthread_mutex_t m_mutex;
31 | };
32 |
33 | // 自动线程锁对象
34 | class CAutoLock {
35 | public:
36 | CAutoLock(CLock &lock) : m_pLock(&lock){
37 | m_pLock->Lock();
38 | }
39 | ~CAutoLock() {
40 | m_pLock->Unlock();
41 | }
42 | protected:
43 | CLock * m_pLock;
44 | };
45 |
46 | #endif //COMMONDEMO_LOCK_H
47 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/NativeException.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/10/15.
3 | //
4 |
5 | #include "NativeException.h"
6 |
7 | static jint NativeException::throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args) {
8 | char szMsg[MSG_SIZE];
9 | vsnprintf(szMsg, MSG_SIZE, szFmt, va_args);
10 | jclass exClass = pEnv->FindClass(szClassName);
11 | return pEnv->ThrowNew(exClass, szMsg);
12 | }
13 |
14 | static jint NativeException::throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...) {
15 | va_list va_args;
16 | va_start(va_args, szFmt);
17 | jint ret = throwException(pEnv, "java/lang/NoClassDefFoundError", szFmt, va_args);
18 | va_end(va_args);
19 | return ret;
20 | }
21 |
22 | static jint NativeException::throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...) {
23 | va_list va_args;
24 | va_start(va_args, szFmt);
25 | jint ret = throwException(pEnv, "java/lang/RuntimeException", szFmt, va_args);
26 | va_end(va_args);
27 | return ret;
28 | }
29 |
30 | static jint NativeException::throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...) {
31 | va_list va_args;
32 | va_start(va_args, szFmt);
33 | jint ret = throwException(pEnv, "java/lang/IllegalArgumentException", szFmt, va_args);
34 | va_end(va_args);
35 | return ret;
36 | }
37 |
38 | static jint NativeException::throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...) {
39 | va_list va_args;
40 | va_start(va_args, szFmt);
41 | jint ret = throwException(pEnv, "java/lang/IllegalStateException", szFmt, va_args);
42 | va_end(va_args);
43 | return ret;
44 | }
45 |
46 | static jint NativeException::throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...) {
47 | va_list va_args;
48 | va_start(va_args, szFmt);
49 | jint ret = throwException(pEnv, "java/lang/OutOfMemoryError", szFmt, va_args);
50 | va_end(va_args);
51 | return ret;
52 | }
53 |
54 | static jint NativeException::throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...) {
55 | va_list va_args;
56 | va_start(va_args, szFmt);
57 | jint ret = throwException(pEnv, "java/lang/AssertionError", szFmt, va_args);
58 | va_end(va_args);
59 | return ret;
60 | }
61 |
62 | static jint NativeException::throwIOException(JNIEnv* pEnv, const char* szFmt, ...) {
63 | va_list va_args;
64 | va_start(va_args, szFmt);
65 | jint ret = throwException(pEnv, "java/io/IOException", szFmt, va_args);
66 | va_end(va_args);
67 | return ret;
68 | }
69 |
70 | static jint NativeException::throwNullPointerException(JNIEnv* pEnv, const char* szFmt, ...) {
71 | va_list va_args;
72 | va_start(va_args, szFmt);
73 | jint ret = throwException(pEnv, "java/lang/NullPointerException", szFmt, va_args);
74 | va_end(va_args);
75 | return ret;
76 | }
77 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/NativeException.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/10/15.
3 | //
4 |
5 | #ifndef COMMONDEMO_NATIVEEXCEPTION_H
6 | #define COMMONDEMO_NATIVEEXCEPTION_H
7 |
8 | #include
9 | #include
10 | #include
11 |
12 | #define MSG_SIZE 1024
13 |
14 |
15 | class NativeException {
16 |
17 | public:
18 | static jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...);
19 |
20 | static jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...);
21 |
22 | static jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...);
23 |
24 | static jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...);
25 |
26 | static jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...);
27 |
28 | static jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...);
29 |
30 | static jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...);
31 |
32 | static jint throwNullPointerException(JNIEnv* pEnv, const char* szFmt, ...);
33 |
34 | private:
35 | static jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args);
36 | };
37 |
38 |
39 | #endif //COMMONDEMO_NATIVEEXCEPTION_H
40 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/base64.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/9/29.
3 | //
4 |
5 | #include "base64.h"
6 | #include
7 |
8 |
9 | static int encode[] =
10 | {
11 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
12 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
13 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
14 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
15 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
16 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
17 | 'w', 'x', 'y', 'z', '0', '1', '2', '3',
18 | '4', '5', '6', '7', '8', '9', '+', '/'
19 | };
20 |
21 |
22 | int encode_base64 (const void *data, size_t length, char **code) {
23 | const unsigned char *s, *end;
24 | unsigned char *buf;
25 | unsigned int x;
26 | int n;
27 | int i, j;
28 |
29 | if (length == 0)
30 | return 0;
31 |
32 | end = ( unsigned char *)data + length - 3;
33 |
34 | buf = (unsigned char *)malloc (4 * ((length + 2) / 3) + 1);
35 | if (buf == NULL)
36 | return -1;
37 |
38 | n = 0;
39 |
40 | for (s = (unsigned char *)data; s < end;)
41 | {
42 | x = *s++ << 24;
43 | x |= *s++ << 16;
44 | x |= *s++ << 8;
45 |
46 | *buf++ = encode[x >> 26];
47 | x <<= 6;
48 | *buf++ = encode[x >> 26];
49 | x <<= 6;
50 | *buf++ = encode[x >> 26];
51 | x <<= 6;
52 | *buf++ = encode[x >> 26];
53 | n += 4;
54 | }
55 |
56 | end += 3;
57 |
58 | x = 0;
59 | for (i = 0; s < end; i++)
60 | x |= *s++ << (24 - 8 * i);
61 |
62 | for (j = 0; j < 4; j++)
63 | {
64 | if (8 * i >= 6 * j)
65 | {
66 | *buf++ = encode [x >> 26];
67 | x <<= 6;
68 | n++;
69 | }
70 | else
71 | {
72 | *buf++ = '=';
73 | n++;
74 | }
75 | }
76 |
77 | *buf = 0;
78 |
79 | *code = (char*)buf - n;
80 | return n;
81 | }
82 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/base64.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/9/29.
3 | //
4 |
5 | #ifndef COMMONDEMO_BASE64_H
6 | #define COMMONDEMO_BASE64_H
7 | #pragma once
8 |
9 | #include
10 |
11 | class base64 {
12 | int encode_base64 (const void *data, size_t length, char **code);
13 | };
14 |
15 |
16 | #endif //COMMONDEMO_BASE64_H
17 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/log.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by jarlene on 2015/9/21.
3 | //
4 |
5 | #ifndef _LOGGING_H_
6 | #define _LOGGING_H_
7 |
8 | #include
9 |
10 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
11 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
12 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
13 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
14 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
15 |
16 | #endif /* _LOGGING_H_ */
17 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/thread.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // 线程的封装,可以知道线程的状态,线程名,线程号等特点。
3 | // Created by Jarlene on 2015/9/21.
4 | //
5 |
6 | #include "thread.h"
7 | #include "log.h"
8 | #include "Lock.h"
9 | #include
10 | #include
11 |
12 | #define LOG_TAG "Native thread"
13 |
14 | enum THREAD_STATUS {
15 | THREAD_STATUS_START_PENDING,
16 | THREAD_STATUS_START,
17 | THREAD_STATUS_STOP_PENDING,
18 | THREAD_STATUS_STOP,
19 | };
20 |
21 | CThread::CThread(const char* pName)
22 | : m_uThreadStatus(THREAD_STATUS_STOP)
23 | , m_hThread((THREAD_HANDLE) NULL)
24 | , m_uThreadID(0)
25 | , pThreadNameM(pName) {
26 |
27 | }
28 |
29 | CThread::~CThread() {
30 | Terminate(1000);
31 | }
32 |
33 |
34 | bool CThread::IsActivate() {
35 | CAutoLock lock(m_lockStatus);
36 | if ( m_uThreadStatus == THREAD_STATUS_START ) {
37 | return true;
38 | }
39 | return false;
40 | }
41 |
42 |
43 | bool CThread::Start() {
44 | CAutoLock lock(m_lockStatus);
45 | if ( m_uThreadStatus != THREAD_STATUS_STOP) {
46 | return true;
47 | } else {
48 | m_uThreadStatus = THREAD_STATUS_START_PENDING;
49 | m_uThreadID = 0;
50 | m_hThread = (THREAD_HANDLE) NULL;
51 | }
52 | if(0 == pthread_create(&m_hThread,NULL,ThreadProc,this)) {
53 | CAutoLock lock(m_lockStatus);
54 | pthread_detach(m_hThread);
55 | m_uThreadID = (uintptr_t)m_hThread;
56 | return true;
57 | } else {
58 | CAutoLock lock(m_lockStatus);
59 | m_uThreadStatus = THREAD_STATUS_STOP;
60 | return false;
61 | }
62 | return true;
63 | }
64 |
65 | bool CThread::Terminate( uint32_t uTimeout /*= INFINITE*/ ) {
66 | CAutoLock lock(m_lockStatus);
67 | if (m_uThreadStatus == THREAD_STATUS_START_PENDING
68 | || m_uThreadStatus == THREAD_STATUS_START) {
69 | m_uThreadStatus = THREAD_STATUS_STOP_PENDING;
70 | } else if ( m_uThreadStatus == THREAD_STATUS_STOP ) {
71 | return true;
72 | }
73 | int loop_count = uTimeout/10;
74 | bool bForce = true;
75 | do {
76 | CAutoLock lock(m_lockStatus);
77 | if ( m_uThreadStatus == THREAD_STATUS_STOP) {
78 | bForce = false;
79 | break;
80 | }
81 | if (loop_count > 0) {
82 | SleepMS(10);
83 | loop_count --;
84 | }
85 | } while (loop_count > 0);
86 |
87 | if ( bForce == true) {
88 | CAutoLock lock(m_lockStatus);
89 | }
90 | return !bForce;
91 | }
92 |
93 |
94 |
95 | bool CThread::WaitToExit( uint32_t uTimeout /*= 0*/ ) {
96 | int32_t loop_count = uTimeout/10;
97 | bool bExit = false;
98 | do {
99 | CAutoLock lock(m_lockStatus);
100 | if ( m_uThreadStatus == THREAD_STATUS_STOP_PENDING) {
101 | bExit = true;
102 | break;
103 | }
104 | if (loop_count > 0) {
105 | SleepMS(10);
106 | }
107 | loop_count --;
108 | } while (loop_count >= 0);
109 | return bExit;
110 | }
111 |
112 |
113 | THREAD_PROC CThread::ThreadProc( void* pParam ) {
114 | CThread *pThis = (CThread *) pParam;
115 | pThis->m_lockStatus.Lock();
116 | pThis->m_uThreadStatus = THREAD_STATUS_START;
117 | pThis->m_lockStatus.Unlock();
118 | pThis->m_uThreadID = pThis->GetCurrentThreadId();
119 | pThis->Run();
120 | pThis->m_lockStatus.Lock();
121 | pThis->m_uThreadStatus = THREAD_STATUS_STOP;
122 | pThis->m_hThread = (THREAD_HANDLE) NULL;
123 | pThis->m_lockStatus.Unlock();
124 | return 0;
125 | }
126 |
127 |
128 | uint32_t CThread::GetCurrentThreadId() {
129 | uint32_t uTreadId = 0;
130 | uTreadId = syscall(__NR_gettid);
131 | return uTreadId;
132 | }
133 |
134 |
135 | bool CThread::IsSelfThread() {
136 | return m_uThreadID == this->GetCurrentThreadId();
137 | }
138 |
139 | bool CThread::WaitUntilExit() {
140 | int count = 0x0;
141 | while(1) {
142 | CAutoLock lock(m_lockStatus);
143 | if(THREAD_STATUS_STOP == m_uThreadStatus) {
144 | break;
145 | }
146 | SleepMS(10);
147 | count++;
148 | }
149 | LOGW("m_uThreadID %d %s WaitUntilExit end",(uint32_t)pthread_self(), GetName());
150 | return true;
151 | }
152 |
153 | uint32_t CThread::GetStatus() {
154 | CAutoLock lock(m_lockStatus);
155 | return m_uThreadStatus;
156 | }
157 |
158 | const char* CThread::GetName() {
159 | return ((pThreadNameM) ? pThreadNameM : "unknown");
160 | }
161 |
162 | void CThread::SetName(const char* pName) {
163 | CAutoLock lock(m_lockStatus);
164 | pThreadNameM = pName;
165 | }
--------------------------------------------------------------------------------
/classlib/src/main/jni/base/thread.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/9/21.
3 | //
4 |
5 | #ifndef COMMONDEMO_THREAD_H
6 | #define COMMONDEMO_THREAD_H
7 |
8 | #include "thread.h"
9 | #include
10 | #include
11 | #include
12 | #include "Lock.h"
13 |
14 |
15 | #define THREAD_HANDLE pthread_t
16 | #define THREAD_PROC void *
17 | #ifndef INFINITE
18 | #define INFINITE 0xFFFFFFFF
19 | #endif
20 |
21 |
22 | #define THREAD_MAX_PRIORITY 100
23 | #define THREAD_MIN_PRIORITY 0
24 | #define THREAD_NORMAL_PRIORITY 10
25 |
26 | #ifdef SleepMS
27 | #undef SleepMS
28 | #endif
29 | #define SleepMS(uMillisecond) usleep(uMillisecond*1000)
30 |
31 | class CThread {
32 | public:
33 | CThread(const char* pName = NULL);
34 | virtual ~CThread();
35 | public:
36 | inline uint32_t GetThreadID(){return m_uThreadID;}; ///< 返回线程号
37 |
38 | bool IsActivate();///< 线程是否正在运行
39 |
40 | bool IsSelfThread();
41 |
42 | virtual bool Start(); ///< 启动线程
43 | virtual bool Terminate(uint32_t uTimeout = INFINITE); ///< 结束线程,超时强制结束,该函数只能由外部线程调用
44 | protected:
45 | bool WaitToExit(uint32_t uTimeout = 0);
46 | bool WaitUntilExit();
47 | uint32_t GetStatus();
48 | const char* GetName();
49 | void SetName(const char* pName);
50 | virtual void Run(){}; ///< 线程主函数,如果是循环,则在循环中调用WaitToExit函数,返回ture时退出
51 | private:
52 | static THREAD_PROC ThreadProc(void* pParam);
53 | static uint32_t GetCurrentThreadId();
54 | private:
55 | THREAD_HANDLE m_hThread;
56 | uint32_t m_uThreadID;
57 | uint32_t m_uThreadStatus;
58 | CLock m_lockStatus;
59 | const char* pThreadNameM;
60 | };
61 |
62 |
63 | #endif //COMMONDEMO_THREAD_H
64 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 | include $(CLEAR_VARS)
3 |
4 | LOCAL_SRC_FILES:= Hook.cpp \
5 | hookDalvik/HookDalvikMethod.cpp \
6 | hookArt/HookArtMethod.cpp \
7 | hookNative/relocate.cpp \
8 | hookNative/InlineHook.cpp \
9 | hookNative/HookNativeMethod.cpp \
10 | hookNative/NativeHook.cpp \
11 | hookNative/NativeElfHook.cpp \
12 | hookNative/NativeInlineHook.cpp \
13 | hookNative/InlineUtils.cpp
14 |
15 |
16 |
17 | LOCAL_CFLAGS := -std=gnu++11 -fpermissive -DDEBUG -O0
18 | #LOCAL_CFLAGS += -std=c99 -Wall
19 |
20 | LOCAL_LDLIBS := -llog
21 | #LOCAL_LDLIBS += -L$(LOCAL_PATH) -lsubstrate-dvm -lsubstrate -lTKHooklib
22 |
23 | LOCAL_MODULE:= commonHook
24 |
25 | include $(BUILD_SHARED_LIBRARY)
26 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/Application.mk:
--------------------------------------------------------------------------------
1 | # The ARMv7 is significanly faster due to the use of the hardware FPU
2 | APP_ABI := armeabi
3 | APP_PLATFORM := android-9
4 | APP_STL := gnustl_static
5 | APP_STL += stlport_static
6 | APP_STL +=c++_shared
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/Hook.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/9/29.
3 | //
4 |
5 | #include
6 | #include
7 | #include
8 | #include "jni.h"
9 | #include "../base/log.h"
10 |
11 |
12 | #define LOG_TAG "Hook"
13 | #define HOOK_CLASS "com/baidu/music/classlib/jni/HookBridge"
14 |
15 | extern jboolean ArtModelInit(JNIEnv* env, int apiLevel);
16 | extern void HookArtMethod(JNIEnv* env, jobject srcMethod, jobject targetMethod);
17 |
18 | extern jboolean DalvikModelInit(JNIEnv* env, jclass mainClass, int apiLevel);
19 | extern void HookDalvikMethod(JNIEnv* env, jobject srcMethod, jobject targetMethod);
20 |
21 | extern void HookNativeMethod(JNIEnv* env, jstring oldSoName, jstring newSoName, jstring oldSymbol, jstring newSymbol);
22 |
23 | extern void DalvikFilterClass(JNIEnv* env, jstring className);
24 |
25 | extern void resolveOpenHot(JNIEnv* env, jboolean open);
26 |
27 | static bool isArtModel;
28 | static jclass mainClass;
29 |
30 |
31 |
32 |
33 | extern "C" void testString(JNIEnv* env) {
34 | jclass clazz = env->FindClass("com/baidu/music/classpatch/MainActivity");
35 | if (clazz == NULL) {
36 | return;
37 | }
38 |
39 | jobject obj = env->NewGlobalRef(clazz);
40 | jmethodID method = env->GetStaticMethodID(clazz, "toastSoString", "(Ljava/lang/String;)V");
41 | if (method == NULL) {
42 | return;
43 | }
44 | jstring str = env->NewStringUTF("come from libcommonHook.so ---testString");
45 | env->CallStaticVoidMethod(obj, method, str);
46 | env->DeleteGlobalRef(obj);
47 | }
48 |
49 | static bool initHookEnv(JNIEnv* env, jclass clazz, jboolean isArt, jint apiLevel) {
50 | isArtModel = isArt;
51 | LOGV("the runtime api level is %d, android's model is %s", (int)apiLevel, (isArt ? "art" : "dalvik"));
52 | if (isArt) {
53 | ArtModelInit(env, (int) apiLevel);
54 | } else {
55 | DalvikModelInit(env, mainClass, (int) apiLevel);
56 | }
57 | return true;
58 | }
59 |
60 | static void HookJavaMethod(JNIEnv* env, jclass clazz, jobject srcMethod, jobject targetMethod) {
61 | LOGV("HookMethod android's model is %s", (isArtModel ? "art" : "dalvik"));
62 | if (isArtModel) {
63 | HookArtMethod(env, srcMethod, targetMethod);
64 | } else {
65 | HookDalvikMethod(env, srcMethod, targetMethod);
66 | }
67 | }
68 |
69 | static void classesFilter(JNIEnv* env, jclass clazz, jobjectArray classArray) {
70 | if (!isArtModel) {
71 | int size = env->GetArrayLength(classArray);
72 | for (int i = 0; i < size; ++i) {
73 | jstring clazzName = (jstring) env->GetObjectArrayElement(classArray, i);
74 | DalvikFilterClass(env, clazzName);
75 | }
76 | }
77 | }
78 |
79 | static void OpenOrCloseHot(JNIEnv* env, jclass clazz, jboolean open) {
80 | resolveOpenHot(env, open);
81 | }
82 |
83 | static void HookNativeMethodSo(JNIEnv* env, jclass clazz, jstring oldSoName, jstring newSoName, jstring oldSymbol, jstring newSymbol) {
84 | HookNativeMethod(env, oldSoName, newSoName, oldSymbol, newSymbol);
85 | testString(env);
86 | }
87 |
88 |
89 | static int registerNativeMethods(JNIEnv* env, const char* className,
90 | JNINativeMethod* gMethods, int numMethods) {
91 | jclass clazz= env->FindClass(className);
92 | mainClass = env->NewGlobalRef(clazz);
93 | if (clazz == NULL) {
94 | return JNI_FALSE;
95 | }
96 | if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
97 | return JNI_FALSE;
98 | }
99 |
100 | return JNI_TRUE;
101 | }
102 |
103 | /*
104 | * native方法映射
105 | */
106 | static JNINativeMethod nativeMethods[] = {
107 | { "initHookEnv", "(ZI)Z", (void*)initHookEnv },
108 | { "replaceJavaMethod", "(Ljava/lang/reflect/Member;Ljava/lang/reflect/Member;)V", (void*)HookJavaMethod },
109 | { "replaceNativeMethod", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void*)HookNativeMethodSo },
110 | { "classesResolvedFilter", "([Ljava/lang/String;)V", (void*) classesFilter},
111 | { "closeOrOpenGetResolvedClass", "(Z)V", (void*) OpenOrCloseHot}
112 | };
113 |
114 |
115 |
116 |
117 |
118 | /**
119 | * 注册native方法
120 | */
121 | static int registerNatives(JNIEnv* env) {
122 | if (!registerNativeMethods(env, HOOK_CLASS, nativeMethods,
123 | sizeof(nativeMethods) / sizeof(nativeMethods[0])))
124 | return JNI_FALSE;
125 |
126 | return JNI_TRUE;
127 | }
128 |
129 | /*
130 | * Set some test stuff up.
131 | *
132 | * Returns the JNI version on success, -1 on failure.
133 | */
134 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
135 | JNIEnv* env = NULL;
136 | jint result = -1;
137 |
138 | if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
139 | return -1;
140 | }
141 | assert(env != NULL);
142 | if (!registerNatives(env)) { //注册
143 | return -1;
144 | }
145 | /* success -- return valid version number */
146 | result = JNI_VERSION_1_6;
147 |
148 | return result;
149 | }
150 |
151 | JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
152 | LOGI("JNI_OnUnload");
153 | JNIEnv *env;
154 | int nJNIVersionOK = vm->GetEnv((void **)&env, JNI_VERSION_1_6) ;
155 | if (nJNIVersionOK == JNI_OK) {
156 | if(mainClass) {
157 | env->DeleteGlobalRef(mainClass);
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookArt/HookArtMethod.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/9/29.
3 | //
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #include "art.h"
23 |
24 | #define LOG_TAG "hookArtMethod"
25 | using namespace art;
26 | using namespace mirror;
27 |
28 |
29 | static int ArtApiLevel;
30 |
31 |
32 | static void* artDlsym(void *hand, const char *name) {
33 | void* ret = dlsym(hand, name);
34 | char msg[1024] = { 0 };
35 | snprintf(msg, sizeof(msg) - 1, "0x%x", ret);
36 | LOGD("%s = %s\n", name, msg);
37 | return ret;
38 | }
39 |
40 | extern jboolean __attribute__((visibility ("hidden"))) ArtModelInit(JNIEnv* env, int apiLevel) {
41 | ArtApiLevel = apiLevel;
42 | LOGV("init Art Model Method");
43 | void* artHand = dlopen("libart.so", RTLD_NOW);
44 | if (artHand) {
45 | char* verifyClass = "_ZN3art11ClassLinker23VerifyClassUsingOatFileERKNS_7DexFileEPNS_6mirror5ClassERNS5_6StatusE";
46 | artVerifyClass_fnPtr = artDlsym(artHand, verifyClass);
47 | if (!artVerifyClass_fnPtr) {
48 | LOGE("artVerifyClass_fnPtr error");
49 | return JNI_FALSE;
50 | }
51 | }
52 | return JNI_TRUE;
53 | }
54 |
55 | extern void __attribute__((visibility ("hidden"))) HookArtMethod(JNIEnv* env, jobject srcMethod, jobject targetMethod) {
56 | ArtMethod* srcMeth = (ArtMethod*) env->FromReflectedMethod(srcMethod);
57 | ArtMethod* targetMeth = (ArtMethod*) env->FromReflectedMethod(targetMethod);
58 |
59 | targetMeth->declaring_class_->class_loader_ = srcMeth->declaring_class_->class_loader_; // ClassLoader
60 | targetMeth->declaring_class_->clinit_thread_id_ = srcMeth->declaring_class_->clinit_thread_id_;
61 | targetMeth->declaring_class_->status_ = srcMeth->declaring_class_->status_;
62 |
63 | srcMeth->declaring_class_ = targetMeth->declaring_class_;
64 | if (ArtApiLevel > 22) {
65 | srcMeth->dex_cache_resolved_types_ = targetMeth->dex_cache_resolved_types_;
66 | srcMeth->access_flags_ = targetMeth->access_flags_;
67 | srcMeth->dex_cache_resolved_methods_ = targetMeth->dex_cache_resolved_methods_;
68 | srcMeth->dex_code_item_offset_ = targetMeth->dex_code_item_offset_;
69 | srcMeth->method_index_ = targetMeth->method_index_;
70 | srcMeth->dex_method_index_ = targetMeth->dex_method_index_;
71 |
72 | srcMeth->ptr_sized_fields_.entry_point_from_interpreter_ = targetMeth->ptr_sized_fields_.entry_point_from_interpreter_;
73 | srcMeth->ptr_sized_fields_.entry_point_from_jni_ = targetMeth->ptr_sized_fields_.entry_point_from_jni_;
74 | srcMeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_ = targetMeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
75 | LOGV("this is running on android 6.0 device");
76 | } else if (ArtApiLevel > 21) {
77 | srcMeth->dex_cache_resolved_types_ = targetMeth->dex_cache_resolved_types_;
78 | srcMeth->access_flags_ = targetMeth->access_flags_;
79 | srcMeth->dex_cache_resolved_methods_ = targetMeth->dex_cache_resolved_methods_;
80 | srcMeth->dex_code_item_offset_ = targetMeth->dex_code_item_offset_;
81 | srcMeth->method_index_ = targetMeth->method_index_;
82 | srcMeth->dex_method_index_ = targetMeth->dex_method_index_;
83 |
84 | srcMeth->ptr_sized_fields_.entry_point_from_interpreter_ = targetMeth->ptr_sized_fields_.entry_point_from_interpreter_;
85 | srcMeth->ptr_sized_fields_.entry_point_from_jni_ = targetMeth->ptr_sized_fields_.entry_point_from_jni_;
86 | srcMeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_ = targetMeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
87 | LOGV("this is running on android 5.1 device");
88 | } else {
89 | srcMeth->access_flags_ = targetMeth->access_flags_;
90 | srcMeth->frame_size_in_bytes_ = targetMeth->frame_size_in_bytes_;
91 | srcMeth->dex_cache_initialized_static_storage_ = targetMeth->dex_cache_initialized_static_storage_;
92 | srcMeth->dex_cache_resolved_types_ = targetMeth->dex_cache_resolved_types_;
93 | srcMeth->dex_cache_resolved_methods_ = targetMeth->dex_cache_resolved_methods_;
94 | srcMeth->vmap_table_ = targetMeth->vmap_table_;
95 | srcMeth->core_spill_mask_ = targetMeth->core_spill_mask_;
96 | srcMeth->fp_spill_mask_ = targetMeth->fp_spill_mask_;
97 | srcMeth->mapping_table_ = targetMeth->mapping_table_;
98 | srcMeth->code_item_offset_ = targetMeth->code_item_offset_;
99 | srcMeth->entry_point_from_compiled_code_ = targetMeth->entry_point_from_compiled_code_;
100 |
101 | srcMeth->entry_point_from_interpreter_ = targetMeth->entry_point_from_interpreter_;
102 | srcMeth->native_method_ = targetMeth->native_method_;
103 | srcMeth->method_index_ = targetMeth->method_index_;
104 | srcMeth->method_dex_index_ = targetMeth->method_dex_index_;
105 | LOGV("this is running on android 5.0 device");
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookArt/art.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/9/29.
3 | //
4 |
5 | #ifndef COMMONDEMO_ART_H
6 | #define COMMONDEMO_ART_H
7 |
8 | #include
9 | #include
10 | #include
11 | #include "../../base/log.h"
12 |
13 | using namespace std;
14 |
15 | #ifdef HAVE_STDINT_H
16 | # include /* C99 */
17 | typedef uint8_t u1;
18 | typedef uint16_t u2;
19 | typedef uint32_t u4;
20 | typedef uint64_t u8;
21 | typedef int8_t s1;
22 | typedef int16_t s2;
23 | typedef int32_t s4;
24 | typedef int64_t s8;
25 | #else
26 | typedef unsigned char u1;
27 | typedef unsigned short u2;
28 | typedef unsigned int u4;
29 | typedef unsigned long long u8;
30 | typedef signed char s1;
31 | typedef signed short s2;
32 | typedef signed int s4;
33 | typedef signed long long s8;
34 | #endif
35 |
36 |
37 | namespace art {
38 | namespace mirror {
39 |
40 | class Object {
41 | public:
42 | static constexpr size_t kVTableLength = 11;
43 | static uint32_t hash_code_seed;
44 | // 5.0系统只有以下两个数据,5.1以及6.0多了上面两个。
45 | void* klass_;
46 | uint32_t monitor_;
47 |
48 | };
49 |
50 | class Class: public Object {
51 | public:
52 |
53 |
54 | // 5.1以上系统独有的参数
55 | static constexpr uint32_t kClassWalkSuper = 0xC0000000;
56 |
57 | // Interface method table size. Increasing this value reduces the chance of two interface methods
58 | // colliding in the interface method table but increases the size of classes that implement
59 | // (non-marker) interfaces.
60 | static constexpr size_t kImtSize = 64; //IMT_SIZE;
61 | // defining class loader, or NULL for the "bootstrap" system loader
62 | void* class_loader_;
63 | // For array classes, the component class object for instanceof/checkcast
64 | // (for String[][][], this will be String[][]). NULL for non-array classes.
65 | void* component_type_;
66 | // DexCache of resolved constant pool entries (will be NULL for classes generated by the
67 | // runtime such as arrays and primitive classes).
68 | void* dex_cache_;
69 | // static, private, and methods
70 | void* direct_methods_;
71 | // instance fields
72 | //
73 | // These describe the layout of the contents of an Object.
74 | // Note that only the fields directly declared by this class are
75 | // listed in ifields; fields declared by a superclass are listed in
76 | // the superclass's Class.ifields.
77 | //
78 | // All instance fields that refer to objects are guaranteed to be at
79 | // the beginning of the field list. num_reference_instance_fields_
80 | // specifies the number of reference fields.
81 | void* ifields_;
82 | // The interface table (iftable_) contains pairs of a interface class and an array of the
83 | // interface methods. There is one pair per interface supported by this class. That means one
84 | // pair for each interface we support directly, indirectly via superclass, or indirectly via a
85 | // superinterface. This will be null if neither we nor our superclass implement any interfaces.
86 | //
87 | // Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()".
88 | // Invoke faceObj.blah(), where "blah" is part of the Face interface. We can't easily use a
89 | // single vtable.
90 | //
91 | // For every interface a concrete class implements, we create an array of the concrete vtable_
92 | // methods for the methods in the interface.
93 | void* iftable_;
94 | // Interface method table (imt), for quick "invoke-interface".
95 | void* imtable_;
96 | // Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
97 | void* name_;
98 | // Static fields
99 | void* sfields_;
100 | // The superclass, or NULL if this is java.lang.Object, an interface or primitive type.
101 | void* super_class_;
102 | // If class verify fails, we must return same error on subsequent tries.
103 | void* verify_error_class_;
104 | // Virtual methods defined in this class; invoked through vtable.
105 | void* virtual_methods_;
106 | // Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass is
107 | // copied in, and virtual methods from our class either replace those from the super or are
108 | // appended. For abstract classes, methods may be created in the vtable that aren't in
109 | // virtual_ methods_ for miranda methods.
110 | void* vtable_;
111 | // Access flags; low 16 bits are defined by VM spec.
112 | uint32_t access_flags_;
113 | // Total size of the Class instance; used when allocating storage on gc heap.
114 | // See also object_size_.
115 | uint32_t class_size_;
116 | // Tid used to check for recursive invocation.
117 | pid_t clinit_thread_id_;
118 | // ClassDef index in dex file, -1 if no class definition such as an array.
119 | // TODO: really 16bits
120 | int32_t dex_class_def_idx_;
121 | // Type index in dex file.
122 | // TODO: really 16bits
123 | int32_t dex_type_idx_;
124 | // Number of instance fields that are object refs.
125 | uint32_t num_reference_instance_fields_;
126 | // Number of static fields that are object refs,
127 | uint32_t num_reference_static_fields_;
128 | // Total object size; used when allocating storage on gc heap.
129 | // (For interfaces and abstract classes this will be zero.)
130 | // See also class_size_.
131 | uint32_t object_size_;
132 | // Primitive type value, or Primitive::kPrimNot (0); set for generated primitive classes.
133 | void* primitive_type_;
134 | // Bitmap of offsets of ifields.
135 | uint32_t reference_instance_offsets_;
136 | // Bitmap of offsets of sfields.
137 | uint32_t reference_static_offsets_;
138 | // State of class initialization.
139 | void* status_;
140 | // TODO: ?
141 | // initiating class loader list
142 | // NOTE: for classes with low serialNumber, these are unused, and the
143 | // values are kept in a table in gDvm.
144 | // InitiatingLoaderList initiating_loader_list_;
145 | // The following data exist in real class objects.
146 | // Embedded Imtable, for class object that's not an interface, fixed size.
147 | void* embedded_imtable_[0];
148 | // Embedded Vtable, for class object that's not an interface, variable size.
149 | void* embedded_vtable_[0];
150 | // Static fields, variable size.
151 | uint32_t fields_[0];
152 | // java.lang.Class
153 | static void* java_lang_Class_;
154 |
155 | };
156 | // art所有的系统都一样
157 | class ArtField: public Object {
158 | void* declaring_class_;
159 | int32_t access_flags_;
160 | int32_t field_dex_idx_;
161 | int32_t offset_;
162 | };
163 |
164 | class ArtMethod : public Object{
165 | public:
166 | // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
167 | // The class we are a part of
168 | Class* declaring_class_;
169 |
170 | // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
171 | void* dex_cache_initialized_static_storage_;
172 |
173 | // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
174 | void* dex_cache_resolved_methods_;
175 |
176 | // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
177 | void* dex_cache_resolved_types_;
178 |
179 | // short cuts to declaring_class_->dex_cache_ member for fast compiled code access
180 | void* dex_cache_strings_;
181 |
182 | // Access flags; low 16 bits are defined by spec.
183 | uint32_t access_flags_;
184 |
185 | // Offset to the CodeItem.
186 | uint32_t code_item_offset_;
187 |
188 | // Architecture-dependent register spill mask
189 | uint32_t core_spill_mask_;
190 |
191 | // Compiled code associated with this method for callers from managed code.
192 | // May be compiled managed code or a bridge for invoking a native method.
193 | // TODO: Break apart this into portable and quick.
194 | const void* entry_point_from_compiled_code_;
195 |
196 | // Called by the interpreter to execute this method.
197 | void* entry_point_from_interpreter_;
198 |
199 | // Architecture-dependent register spill mask
200 | uint32_t fp_spill_mask_;
201 |
202 | // Total size in bytes of the frame
203 | size_t frame_size_in_bytes_;
204 |
205 | // Garbage collection map of native PC offsets (quick) or dex PCs (portable) to reference bitmaps.
206 | const uint8_t* gc_map_;
207 |
208 | // Mapping from native pc to dex pc
209 | const uint32_t* mapping_table_;
210 |
211 | /* Dex file fields. The defining dex file is available via declaring_class_->dex_cache_ */
212 | // Offset to the CodeItem.
213 | uint32_t dex_code_item_offset_;
214 | // Index into method_ids of the dex file associated with this method.
215 | uint32_t dex_method_index_;
216 |
217 | // Index into method_ids of the dex file associated with this method
218 | uint32_t method_dex_index_;
219 |
220 | // For concrete virtual methods, this is the offset of the method in Class::vtable_.
221 | //
222 | // For abstract methods in an interface class, this is the offset of the method in
223 | // "iftable_->Get(n)->GetMethodArray()".
224 | //
225 | // For static and direct methods this is the index in the direct methods table.
226 | uint32_t method_index_;
227 |
228 | // The target native method registered with this method
229 | const void* native_method_;
230 |
231 | // When a register is promoted into a register, the spill mask holds which registers hold dex
232 | // registers. The first promoted register's corresponding dex register is vmap_table_[1], the Nth
233 | // is vmap_table_[N]. vmap_table_[0] holds the length of the table.
234 | const uint16_t* vmap_table_;
235 |
236 | static void* java_lang_reflect_ArtMethod_;
237 |
238 | struct PtrSizedFields {
239 | // Method dispatch from the interpreter invokes this pointer which may cause a bridge into
240 | // compiled code.
241 | void* entry_point_from_interpreter_;
242 | // Pointer to JNI function registered to this method, or a function to resolve the JNI function.
243 | void* entry_point_from_jni_;
244 | // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
245 | // the interpreter.
246 | void* entry_point_from_quick_compiled_code_;
247 | } ptr_sized_fields_;
248 | };
249 |
250 | }
251 | }
252 |
253 | typedef void* (*artVerifyClass_func) (art::mirror::Class * ,bool, std::string*);
254 |
255 | artVerifyClass_func artVerifyClass_fnPtr;
256 |
257 |
258 | #endif //COMMONDEMO_ART_H
259 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/ElfHook.h:
--------------------------------------------------------------------------------
1 | int elfHook(char *targetSoName, char * targetFunName, unsigned replaceFunAddr);
2 |
3 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/HookNativeMethod.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/9/30.
3 | //
4 | #include "../../base/log.h"
5 | #include "NativeElfHook.h"
6 | #include "NativeHook.h"
7 | #include "TKHooklib.h"
8 | #include "InlineHook.h"
9 | #include "NativeInlineHook.h"
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #define LOG_TAG "HookNativeMethod"
18 |
19 |
20 |
21 |
22 | static void* dvmDlsym(void *hand, const char *name) {
23 | void* ret = dlsym(hand, name);
24 | char msg[1024] = { 0 };
25 | snprintf(msg, sizeof(msg) - 1, "0x%x", ret);
26 | LOGD("%s = %s\n", name, msg);
27 | return ret;
28 | }
29 |
30 |
31 |
32 | extern void __attribute__ ((visibility ("hidden"))) HookNativeMethod(JNIEnv* env, jstring oldSoName, jstring newSoName, jstring oldSymbol, jstring newSymbol) {
33 | jboolean isCopy = JNI_TRUE;
34 | char* old_so_name = env->GetStringUTFChars(oldSoName, &isCopy);
35 | if (old_so_name == NULL) {
36 | return;
37 | }
38 | char* new_so_name = env->GetStringUTFChars(newSoName, &isCopy);
39 | if (new_so_name == NULL) {
40 | return;
41 | }
42 | char* old_symbol = env->GetStringUTFChars(oldSymbol, &isCopy);
43 | if (old_symbol == NULL) {
44 | return;
45 | }
46 | char* new_symbol = env->GetStringUTFChars(newSymbol, &isCopy);
47 | if (new_symbol == NULL) {
48 | return;
49 | }
50 |
51 |
52 | void* oldHandle = dlopen(old_so_name, RTLD_NOW);
53 | if (oldHandle == NULL) {
54 | return;
55 | }
56 | void* newHandle = dlopen(new_so_name, RTLD_NOW);
57 | if (newHandle == NULL) {
58 | return;
59 | }
60 |
61 |
62 | void* oldMethod = dvmDlsym(oldHandle, old_symbol);
63 | if (oldMethod == NULL) {
64 | return;
65 | }
66 | void* pluginNativeMethod = dvmDlsym(newHandle, new_symbol);
67 | if (pluginNativeMethod == NULL) {
68 | return;
69 | }
70 |
71 | void* OldFunc = NULL;
72 |
73 | if (registerInlineHook(oldMethod, pluginNativeMethod, (uint32_t **)&OldFunc) == INLINE_OK) {
74 | LOGD("registerInlineHook Ok");
75 | }
76 | if (inlineHook(oldMethod) == INLINE_OK ) {
77 | LOGD("inlineHook Ok");
78 | }
79 |
80 | //TK_InlineHookFunction(oldMethod, pluginNativeMethod, &OldFunc);
81 |
82 |
83 | // if (newHandle) {
84 | // void* pluginNativeMethod = dvmDlsym(newHandle, new_symbol);
85 | // LOGD("the new so method addr is %p", pluginNativeMethod);
86 | // addElfHook(old_so_name, old_symbol, pluginNativeMethod);
87 | // void* oldHandle = elfLoadLibrary(old_so_name);
88 | // elfHookSymbol(oldHandle, old_symbol, (void**)&pluginNativeMethod);
89 | // }
90 |
91 | }
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/InlineHook.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 16/5/16.
3 | //
4 |
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #include "relocate.h"
18 | #include "InlineHook.h"
19 |
20 | #ifndef PAGE_SIZE
21 | #define PAGE_SIZE 4096
22 | #endif
23 |
24 | #define PAGE_START(addr) (~(PAGE_SIZE - 1) & (addr))
25 | #define SET_BIT0(addr) (addr | 1)
26 | #define CLEAR_BIT0(addr) (addr & 0xFFFFFFFE)
27 | #define TEST_BIT0(addr) (addr & 1)
28 |
29 | #define ACTION_ENABLE 0
30 | #define ACTION_DISABLE 1
31 |
32 | enum hook_status {
33 | REGISTERED,
34 | HOOKED,
35 | };
36 |
37 | struct inlineHookItem {
38 | uint32_t target_addr;
39 | uint32_t new_addr;
40 | uint32_t **proto_addr;
41 | void *orig_instructions;
42 | int orig_boundaries[4];
43 | int trampoline_boundaries[20];
44 | int count;
45 | void *trampoline_instructions;
46 | int length;
47 | int status;
48 | int mode;
49 | };
50 |
51 | struct inlineHookInfo {
52 | struct inlineHookItem item[1024];
53 | int size;
54 | };
55 |
56 | static struct inlineHookInfo info = {0};
57 |
58 | static int getAllTids(pid_t pid, pid_t *tids) {
59 | char dir_path[32];
60 | DIR *dir;
61 | int i;
62 | struct dirent *entry;
63 | pid_t tid;
64 |
65 | if (pid < 0) {
66 | snprintf(dir_path, sizeof(dir_path), "/proc/self/task");
67 | }
68 | else {
69 | snprintf(dir_path, sizeof(dir_path), "/proc/%d/task", pid);
70 | }
71 |
72 | dir = opendir(dir_path);
73 | if (dir == NULL) {
74 | return 0;
75 | }
76 |
77 | i = 0;
78 | while((entry = readdir(dir)) != NULL) {
79 | tid = atoi(entry->d_name);
80 | if (tid != 0 && tid != getpid()) {
81 | tids[i++] = tid;
82 | }
83 | }
84 | closedir(dir);
85 | return i;
86 | }
87 |
88 |
89 | static bool doProcessThreadPC(struct inlineHookItem *item, struct pt_regs *regs, int action) {
90 | int offset;
91 | int i;
92 |
93 | switch (action)
94 | {
95 | case ACTION_ENABLE:
96 | offset = regs->ARM_pc - CLEAR_BIT0(item->target_addr);
97 | for (i = 0; i < item->count; ++i) {
98 | if (offset == item->orig_boundaries[i]) {
99 | regs->ARM_pc = (uint32_t) item->trampoline_instructions + item->trampoline_boundaries[i];
100 | return true;
101 | }
102 | }
103 | break;
104 | case ACTION_DISABLE:
105 | offset = regs->ARM_pc - (int) item->trampoline_instructions;
106 | for (i = 0; i < item->count; ++i) {
107 | if (offset == item->trampoline_boundaries[i]) {
108 | regs->ARM_pc = CLEAR_BIT0(item->target_addr) + item->orig_boundaries[i];
109 | return true;
110 | }
111 | }
112 | break;
113 | }
114 |
115 | return false;
116 | }
117 |
118 | static void processThreadPC(pid_t tid, struct inlineHookItem *item, int action) {
119 | struct pt_regs regs;
120 |
121 | if (ptrace(PTRACE_GETREGS, tid, NULL, ®s) == 0) {
122 | if (item == NULL) {
123 | int pos;
124 |
125 | for (pos = 0; pos < info.size; ++pos) {
126 | if (doProcessThreadPC(&info.item[pos], ®s, action) == true) {
127 | break;
128 | }
129 | }
130 | }
131 | else {
132 | doProcessThreadPC(item, ®s, action);
133 | }
134 |
135 | ptrace(PTRACE_SETREGS, tid, NULL, ®s);
136 | }
137 | }
138 |
139 | static pid_t freeze(struct inlineHookItem *item, int action) {
140 | int count;
141 | pid_t tids[1024];
142 | pid_t pid;
143 |
144 | pid = -1;
145 | count = getAllTids(getpid(), tids);
146 | if (count > 0) {
147 | pid = fork();
148 |
149 | if (pid == 0) {
150 | int i;
151 |
152 | for (i = 0; i < count; ++i) {
153 | if (ptrace(PTRACE_ATTACH, tids[i], NULL, NULL) == 0) {
154 | waitpid(tids[i], NULL, WUNTRACED);
155 | processThreadPC(tids[i], item, action);
156 | }
157 | }
158 |
159 | raise(SIGSTOP);
160 |
161 | for (i = 0; i < count; ++i) {
162 | ptrace(PTRACE_DETACH, tids[i], NULL, NULL);
163 | }
164 |
165 | exit(0);
166 | }
167 |
168 | else if (pid > 0) {
169 | waitpid(pid, NULL, WUNTRACED);
170 | }
171 | }
172 |
173 | return pid;
174 | }
175 |
176 | static void unFreeze(pid_t pid) {
177 | if (pid < 0) {
178 | return;
179 | }
180 |
181 | kill(pid, SIGCONT);
182 | while (1) {
183 | if (wait(NULL) < 0) {
184 | break;
185 | }
186 | }
187 | }
188 |
189 | static bool isExecutableAddr(uint32_t addr) {
190 | FILE *fp;
191 | char line[1024];
192 | uint32_t start;
193 | uint32_t end;
194 |
195 | fp = fopen("/proc/self/maps", "r");
196 | if (fp == NULL) {
197 | return false;
198 | }
199 |
200 | while (fgets(line, sizeof(line), fp)) {
201 | if (strstr(line, "r-xp")) {
202 | start = strtoul(strtok(line, "-"), NULL, 16);
203 | end = strtoul(strtok(NULL, " "), NULL, 16);
204 | if (addr >= start && addr <= end) {
205 | fclose(fp);
206 | return true;
207 | }
208 | }
209 | }
210 |
211 | fclose(fp);
212 |
213 | return false;
214 | }
215 |
216 | static struct inlineHookItem *findInlineHookItem(uint32_t target_addr) {
217 | int i;
218 |
219 | for (i = 0; i < info.size; ++i) {
220 | if (info.item[i].target_addr == target_addr) {
221 | return &info.item[i];
222 | }
223 | }
224 |
225 | return NULL;
226 | }
227 |
228 | static struct inlineHookItem *addInlineHookItem() {
229 | struct inlineHookItem *item;
230 |
231 | if (info.size >= 1024) {
232 | return NULL;
233 | }
234 |
235 | item = &info.item[info.size];
236 | ++info.size;
237 |
238 | return item;
239 | }
240 |
241 | static void deleteInlineHookItem(int pos) {
242 | info.item[pos] = info.item[info.size - 1];
243 | --info.size;
244 | }
245 |
246 | enum inline_hook_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr) {
247 | struct inlineHookItem *item;
248 |
249 | if (!isExecutableAddr(target_addr) || !isExecutableAddr(new_addr)) {
250 | return INLINE_ERROR_NOT_EXECUTABLE;
251 | }
252 |
253 | item = findInlineHookItem(target_addr);
254 | if (item != NULL) {
255 | if (item->status == REGISTERED) {
256 | return INLINE_ERROR_ALREADY_REGISTERED;
257 | }
258 | else if (item->status == HOOKED) {
259 | return INLINE_ERROR_ALREADY_HOOKED;
260 | }
261 | else {
262 | return INLINE_ERROR_UNKNOWN;
263 | }
264 | }
265 |
266 | item = addInlineHookItem();
267 |
268 | item->target_addr = target_addr;
269 | item->new_addr = new_addr;
270 | item->proto_addr = proto_addr;
271 |
272 | item->length = TEST_BIT0(item->target_addr) ? 12 : 8;
273 | item->orig_instructions = malloc(item->length);
274 | memcpy(item->orig_instructions, (void *) CLEAR_BIT0(item->target_addr), item->length);
275 |
276 | item->trampoline_instructions = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
277 | relocateInstruction(item->target_addr, item->orig_instructions, item->length, item->trampoline_instructions, item->orig_boundaries, item->trampoline_boundaries, &item->count);
278 |
279 | item->status = REGISTERED;
280 |
281 | return INLINE_OK;
282 | }
283 |
284 | static void doInlineUnHook(struct inlineHookItem *item, int pos)
285 | {
286 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
287 | memcpy((void *) CLEAR_BIT0(item->target_addr), item->orig_instructions, item->length);
288 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE, PROT_READ | PROT_EXEC);
289 | munmap(item->trampoline_instructions, PAGE_SIZE);
290 | free(item->orig_instructions);
291 |
292 | deleteInlineHookItem(pos);
293 |
294 | cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0);
295 | }
296 |
297 | enum inline_hook_status inlineUnHook(uint32_t target_addr) {
298 | int i;
299 |
300 | for (i = 0; i < info.size; ++i) {
301 | if (info.item[i].target_addr == target_addr && info.item[i].status == HOOKED) {
302 | pid_t pid;
303 |
304 | pid = freeze(&info.item[i], ACTION_DISABLE);
305 |
306 | doInlineUnHook(&info.item[i], i);
307 |
308 | unFreeze(pid);
309 |
310 | return INLINE_OK;
311 | }
312 | }
313 |
314 | return INLINE_ERROR_NOT_HOOKED;
315 | }
316 |
317 | void inlineUnHookAll() {
318 | pid_t pid;
319 | int i;
320 |
321 | pid = freeze(NULL, ACTION_DISABLE);
322 |
323 | for (i = 0; i < info.size; ++i) {
324 | if (info.item[i].status == HOOKED) {
325 | doInlineUnHook(&info.item[i], i);
326 | }
327 | }
328 |
329 | unFreeze(pid);
330 | }
331 |
332 | static void doInlineHook(struct inlineHookItem *item) {
333 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
334 |
335 | if (TEST_BIT0(item->target_addr)) {
336 | int i;
337 |
338 | i = 0;
339 | if (CLEAR_BIT0(item->target_addr) % 4 != 0) {
340 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00; // NOP
341 | }
342 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF;
343 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC]
344 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF;
345 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16;
346 | }
347 | else {
348 | ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4]
349 | ((uint32_t *) (item->target_addr))[1] = item->new_addr;
350 | }
351 |
352 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE, PROT_READ | PROT_EXEC);
353 |
354 | if (item->proto_addr != NULL) {
355 | *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0((uint32_t) item->trampoline_instructions) : item->trampoline_instructions;
356 | }
357 |
358 | item->status = HOOKED;
359 |
360 | cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0);
361 | }
362 |
363 | enum inline_hook_status inlineHook(uint32_t target_addr) {
364 | int i;
365 | struct inlineHookItem *item;
366 |
367 | item = NULL;
368 | for (i = 0; i < info.size; ++i) {
369 | if (info.item[i].target_addr == target_addr) {
370 | item = &info.item[i];
371 | break;
372 | }
373 | }
374 |
375 | if (item == NULL) {
376 | return INLINE_ERROR_NOT_REGISTERED;
377 | }
378 |
379 | if (item->status == REGISTERED) {
380 | pid_t pid;
381 |
382 | pid = freeze(item, ACTION_ENABLE);
383 |
384 | doInlineHook(item);
385 |
386 | unFreeze(pid);
387 |
388 | return INLINE_OK;
389 | }
390 | else if (item->status == HOOKED) {
391 | return INLINE_ERROR_ALREADY_HOOKED;
392 | }
393 | else {
394 | return INLINE_ERROR_UNKNOWN;
395 | }
396 | }
397 |
398 | void inlineHookAll() {
399 | pid_t pid;
400 | int i;
401 |
402 | pid = freeze(NULL, ACTION_ENABLE);
403 |
404 | for (i = 0; i < info.size; ++i) {
405 | if (info.item[i].status == REGISTERED) {
406 | doInlineHook(&info.item[i]);
407 | }
408 | }
409 |
410 | unFreeze(pid);
411 | }
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/InlineHook.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 16/5/16.
3 | //
4 |
5 | #ifndef CLASSPATCH_INLINEHOOK_H
6 | #define CLASSPATCH_INLINEHOOK_H
7 |
8 | #include
9 |
10 | enum inline_hook_status {
11 | INLINE_ERROR_UNKNOWN = -1,
12 | INLINE_OK = 0,
13 | INLINE_ERROR_NOT_INITIALIZED,
14 | INLINE_ERROR_NOT_EXECUTABLE,
15 | INLINE_ERROR_NOT_REGISTERED,
16 | INLINE_ERROR_NOT_HOOKED,
17 | INLINE_ERROR_ALREADY_REGISTERED,
18 | INLINE_ERROR_ALREADY_HOOKED,
19 | INLINE_ERROR_SO_NOT_FOUND,
20 | INLINE_ERROR_FUNCTION_NOT_FOUND
21 | };
22 |
23 | enum inline_hook_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr);
24 | enum inline_hook_status inlineUnHook(uint32_t target_addr);
25 | void inlineUnHookAll();
26 | enum inline_hook_status inlineHook(uint32_t target_addr);
27 | void inlineHookAll();
28 |
29 |
30 |
31 | #endif //CLASSPATCH_INLINEHOOK_H
32 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/InlineUtils.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2016/5/4.
3 | //
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #include "InlineUtils.h"
23 | #include "NativeInlineHook.h"
24 |
25 | #define _XOPEN_SOURCE 500
26 |
27 | /* memory map for libraries */
28 | #define MAX_NAME_LEN 256
29 | #define MEMORY_ONLY "[memory]"
30 | struct mm {
31 | char name[MAX_NAME_LEN];
32 | unsigned long start, end;
33 | };
34 |
35 | typedef struct symtab *symtab_t;
36 | struct symlist {
37 | Elf32_Sym *sym; /* symbols */
38 | char *str; /* symbol strings */
39 | unsigned num; /* number of symbols */
40 | };
41 |
42 | struct symtab {
43 | struct symlist *st; /* "static" symbols */
44 | struct symlist *dyn; /* dynamic symbols */
45 | };
46 |
47 | void* xmalloc(size_t size) {
48 | void *p;
49 | p = malloc(size);
50 | if (!p) {
51 | printf("Out of memory\n");
52 | exit(1);
53 | }
54 | return p;
55 | }
56 |
57 | int my_pread(int fd, void *buf, size_t count, off_t offset) {
58 | lseek(fd, offset, SEEK_SET);
59 | return read(fd, buf, count);
60 | }
61 |
62 | struct symlist* get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) {
63 | struct symlist *sl, *ret;
64 | int rv;
65 |
66 | ret = NULL;
67 | sl = (struct symlist *) xmalloc(sizeof(struct symlist));
68 | sl->str = NULL;
69 | sl->sym = NULL;
70 |
71 | /* sanity */
72 | if (symh->sh_size % sizeof(Elf32_Sym)) {
73 | //printf("elf_error\n");
74 | goto out;
75 | }
76 |
77 | /* symbol table */
78 | sl->num = symh->sh_size / sizeof(Elf32_Sym);
79 | sl->sym = (Elf32_Sym *) xmalloc(symh->sh_size);
80 | rv = my_pread(fd, sl->sym, symh->sh_size, symh->sh_offset);
81 | if (0 > rv) {
82 | //perror("read");
83 | goto out;
84 | }
85 | if (rv != symh->sh_size) {
86 | //printf("elf error\n");
87 | goto out;
88 | }
89 |
90 | /* string table */
91 | sl->str = (char *) xmalloc(strh->sh_size);
92 | rv = my_pread(fd, sl->str, strh->sh_size, strh->sh_offset);
93 | if (0 > rv) {
94 | //perror("read");
95 | goto out;
96 | }
97 | if (rv != strh->sh_size) {
98 | //printf("elf error");
99 | goto out;
100 | }
101 |
102 | ret = sl;
103 | out:
104 | return ret;
105 | }
106 |
107 | int do_load(int fd, symtab_t symtab) {
108 | int rv;
109 | size_t size;
110 | Elf32_Ehdr ehdr;
111 | Elf32_Shdr *shdr = NULL, *p;
112 | Elf32_Shdr *dynsymh, *dynstrh;
113 | Elf32_Shdr *symh, *strh;
114 | char *shstrtab = NULL;
115 | int i;
116 | int ret = -1;
117 |
118 | /* elf header */
119 | rv = read(fd, &ehdr, sizeof(ehdr));
120 | if (0 > rv) {
121 | LOGD("read\n");
122 | goto out;
123 | }
124 | if (rv != sizeof(ehdr)) {
125 | LOGD("elf error 1\n");
126 | goto out;
127 | }
128 | if (strncmp(ELFMAG, ehdr.e_ident, SELFMAG)) { /* sanity */
129 | LOGD("not an elf\n");
130 | goto out;
131 | }
132 | if (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */
133 | LOGD("elf error 2\n");
134 | goto out;
135 | }
136 |
137 | /* section header table */
138 | size = ehdr.e_shentsize * ehdr.e_shnum;
139 | shdr = (Elf32_Shdr *) xmalloc(size);
140 | rv = my_pread(fd, shdr, size, ehdr.e_shoff);
141 | if (0 > rv) {
142 | LOGD("read\n");
143 | goto out;
144 | }
145 | if (rv != size) {
146 | LOGD("elf error 3 %d %d\n", rv, size);
147 | goto out;
148 | }
149 |
150 | /* section header string table */
151 | size = shdr[ehdr.e_shstrndx].sh_size;
152 | shstrtab = (char *) xmalloc(size);
153 | rv = my_pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset);
154 | if (0 > rv) {
155 | LOGD("read\n");
156 | goto out;
157 | }
158 | if (rv != size) {
159 | LOGD("elf error 4 %d %d\n", rv, size);
160 | goto out;
161 | }
162 |
163 | /* symbol table headers */
164 | symh = dynsymh = NULL;
165 | strh = dynstrh = NULL;
166 | for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++)
167 | if (SHT_SYMTAB == p->sh_type) {
168 | if (symh) {
169 | LOGD("too many symbol tables\n");
170 | goto out;
171 | }
172 | symh = p;
173 | } else if (SHT_DYNSYM == p->sh_type) {
174 | if (dynsymh) {
175 | LOGD("too many symbol tables\n");
176 | goto out;
177 | }
178 | dynsymh = p;
179 | } else if (SHT_STRTAB == p->sh_type
180 | && !strncmp(shstrtab+p->sh_name, ".strtab", 7)) {
181 | if (strh) {
182 | LOGD("too many string tables\n");
183 | goto out;
184 | }
185 | strh = p;
186 | } else if (SHT_STRTAB == p->sh_type
187 | && !strncmp(shstrtab+p->sh_name, ".dynstr", 7)) {
188 | if (dynstrh) {
189 | LOGD("too many string tables\n");
190 | goto out;
191 | }
192 | dynstrh = p;
193 | }
194 | /* sanity checks */
195 | if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) {
196 | LOGD("bad dynamic symbol table\n");
197 | goto out;
198 | }
199 | if ((!symh && strh) || (symh && !strh)) {
200 | LOGD("bad symbol table\n");
201 | goto out;
202 | }
203 | if (!dynsymh && !symh) {
204 | LOGD("no symbol table\n");
205 | goto out;
206 | }
207 |
208 | /* symbol tables */
209 | if (dynsymh)
210 | symtab->dyn = get_syms(fd, dynsymh, dynstrh);
211 | if (symh)
212 | symtab->st = get_syms(fd, symh, strh);
213 | ret = 0;
214 | out:
215 | free(shstrtab);
216 | free(shdr);
217 | return ret;
218 | }
219 |
220 | symtab_t load_symtab(char *filename) {
221 | int fd;
222 | symtab_t symtab;
223 |
224 | symtab = (symtab_t) xmalloc(sizeof(*symtab));
225 | memset(symtab, 0, sizeof(*symtab));
226 |
227 | fd = open(filename, O_RDONLY);
228 | if (0 > fd) {
229 | LOGD("%s open\n", __func__);
230 | return NULL;
231 | }
232 | if (0 > do_load(fd, symtab)) {
233 | LOGD("Error ELF parsing %s\n", filename);
234 | free(symtab);
235 | symtab = NULL;
236 | }
237 | close(fd);
238 | return symtab;
239 | }
240 |
241 | int load_memmap(pid_t pid, struct mm *mm, int *nmmp) {
242 | char raw[80000]; // increase this if needed for larger "maps"
243 | char name[MAX_NAME_LEN];
244 | char *p;
245 | unsigned long start, end;
246 | struct mm *m;
247 | int nmm = 0;
248 | int fd, rv;
249 | int i;
250 |
251 | sprintf(raw, "/proc/%d/maps", pid);
252 | fd = open(raw, O_RDONLY);
253 | if (0 > fd) {
254 | //printf("Can't open %s for reading\n", raw);
255 | return -1;
256 | }
257 |
258 | /* Zero to ensure data is null terminated */
259 | memset(raw, 0, sizeof(raw));
260 |
261 | p = raw;
262 | while (1) {
263 | rv = read(fd, p, sizeof(raw)-(p-raw));
264 | if (0 > rv) {
265 | //perror("read");
266 | return -1;
267 | }
268 | if (0 == rv)
269 | break;
270 | p += rv;
271 | if (p-raw >= sizeof(raw)) {
272 | //printf("Too many memory mapping\n");
273 | return -1;
274 | }
275 | }
276 | close(fd);
277 |
278 | p = strtok(raw, "\n");
279 | m = mm;
280 | while (p) {
281 | /* parse current map line */
282 | rv = sscanf(p, "%08lx-%08lx %*s %*s %*s %*s %s\n",
283 | &start, &end, name);
284 |
285 | p = strtok(NULL, "\n");
286 |
287 | if (rv == 2) {
288 | m = &mm[nmm++];
289 | m->start = start;
290 | m->end = end;
291 | strcpy(m->name, MEMORY_ONLY);
292 | continue;
293 | }
294 |
295 | /* search backward for other mapping with same name */
296 | for (i = nmm-1; i >= 0; i--) {
297 | m = &mm[i];
298 | if (!strcmp(m->name, name))
299 | break;
300 | }
301 |
302 | if (i >= 0) {
303 | if (start < m->start)
304 | m->start = start;
305 | if (end > m->end)
306 | m->end = end;
307 | } else {
308 | /* new entry */
309 | m = &mm[nmm++];
310 | m->start = start;
311 | m->end = end;
312 | strcpy(m->name, name);
313 | }
314 | }
315 |
316 | *nmmp = nmm;
317 | return 0;
318 | }
319 |
320 | /* Find libc in MM, storing no more than LEN-1 chars of
321 | its name in NAME and set START to its starting
322 | address. If libc cannot be found return -1 and
323 | leave NAME and START untouched. Otherwise return 0
324 | and null-terminated NAME. */
325 | int find_libname(char *libn, char *name, int len, unsigned long *start, struct mm *mm, int nmm) {
326 | int i;
327 | struct mm *m;
328 | char *p;
329 | for (i = 0, m = mm; i < nmm; i++, m++) {
330 | if (!strcmp(m->name, MEMORY_ONLY))
331 | continue;
332 | p = strrchr(m->name, '/');
333 | if (!p)
334 | continue;
335 | p++;
336 | if (strncmp(libn, p, strlen(libn)))
337 | continue;
338 | p += strlen(libn);
339 |
340 | /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */
341 | if (!strncmp("so", p, 2) || 1) // || (p[0] == '-' && isdigit(p[1])))
342 | break;
343 | }
344 | if (i >= nmm)
345 | /* not found */
346 | return -1;
347 |
348 | *start = m->start;
349 | strncpy(name, m->name, len);
350 | if (strlen(m->name) >= len)
351 | name[len-1] = '\0';
352 |
353 | mprotect((void*)m->start, m->end - m->start, PROT_READ|PROT_WRITE|PROT_EXEC);
354 | return 0;
355 | }
356 |
357 | int lookup2(struct symlist *sl, unsigned char type,
358 | char *name, unsigned long *val) {
359 | Elf32_Sym *p;
360 | int len;
361 | int i;
362 |
363 | len = strlen(name);
364 | for (i = 0, p = sl->sym; i < sl->num; i++, p++) {
365 | //LOGD("name: %s %x\n", sl->str+p->st_name, p->st_value);
366 | if (!strncmp(sl->str+p->st_name, name, len) && *(sl->str+p->st_name+len) == 0
367 | && ELF32_ST_TYPE(p->st_info) == type) {
368 | //if (p->st_value != 0) {
369 | *val = p->st_value;
370 | return 0;
371 | //}
372 | }
373 | }
374 | return -1;
375 | }
376 |
377 | int lookup_sym(symtab_t s, unsigned char type,
378 | char *name, unsigned long *val) {
379 | if (s->dyn && !lookup2(s->dyn, type, name, val))
380 | return 0;
381 | if (s->st && !lookup2(s->st, type, name, val))
382 | return 0;
383 | return -1;
384 | }
385 |
386 | int lookup_func_sym(symtab_t s, char *name, unsigned long *val)
387 | {
388 | return lookup_sym(s, STT_FUNC, name, val);
389 | }
390 |
391 | int find_name(pid_t pid, char *name, char *libn, unsigned long *addr) {
392 | struct mm mm[1000];
393 | unsigned long libcaddr;
394 | int nmm;
395 | char libc[1024];
396 | symtab_t s;
397 |
398 | if (0 > load_memmap(pid, mm, &nmm)) {
399 | LOGD("cannot read memory map\n");
400 | return -1;
401 | }
402 | if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) {
403 | LOGD("cannot find lib: %s\n", libn);
404 | return -1;
405 | }
406 | //LOGD("lib: >%s<\n", libc);
407 | s = load_symtab(libc);
408 | if (!s) {
409 | LOGD("cannot read symbol table\n");
410 | return -1;
411 | }
412 | if (0 > lookup_func_sym(s, name, addr)) {
413 | LOGD("cannot find function: %s\n", name);
414 | return -1;
415 | }
416 | *addr += libcaddr;
417 | return 0;
418 | }
419 |
420 | int find_libbase(pid_t pid, char *libn, unsigned long *addr) {
421 | struct mm mm[1000];
422 | unsigned long libcaddr;
423 | int nmm;
424 | char libc[1024];
425 | symtab_t s;
426 |
427 | if (0 > load_memmap(pid, mm, &nmm)) {
428 | LOGD("cannot read memory map\n");
429 | return -1;
430 | }
431 | if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) {
432 | LOGD("cannot find lib\n");
433 | return -1;
434 | }
435 | *addr = libcaddr;
436 | return 0;
437 | }
438 |
439 | // --------------------------------------------------------------
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/InlineUtils.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2016/5/4.
3 | //
4 |
5 | #ifndef CLASSPATCH_INLINEUTILS_H
6 | #define CLASSPATCH_INLINEUTILS_H
7 |
8 |
9 | #include
10 |
11 | int find_name(pid_t pid, char *name, char *libn, unsigned long *addr);
12 | int find_libbase(pid_t pid, char *libn, unsigned long *addr);
13 |
14 |
15 |
16 |
17 | #endif //CLASSPATCH_INLINEUTILS_H
18 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/Linker.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/11/27.
3 | //
4 |
5 | #ifndef CLASSPATCH_LINKER_H
6 | #define CLASSPATCH_LINKER_H
7 | #include
8 |
9 | #define ANDROID_ARM_LINKER 1
10 |
11 | #define SOINFO_NAME_LEN 128
12 |
13 | struct LinkMap
14 | {
15 | uintptr_t l_addr;
16 | char * l_name;
17 | uintptr_t l_ld;
18 | struct link_map * l_next;
19 | struct link_map * l_prev;
20 | };
21 |
22 | struct SoInfo
23 | {
24 | const char name[SOINFO_NAME_LEN];
25 | Elf32_Phdr *phdr;
26 | int phnum;
27 | unsigned entry;
28 | unsigned base;
29 | unsigned size;
30 | int unused; // DO NOT USE, maintained for compatibility.
31 | unsigned *dynamic;
32 | unsigned wrprotect_start;
33 | unsigned wrprotect_end;
34 | struct soinfo *next;
35 | unsigned flags;
36 | const char *strtab;
37 | Elf32_Sym *symtab;
38 | unsigned nbucket;
39 | unsigned nchain;
40 | unsigned *bucket;
41 | unsigned *chain;
42 | unsigned *plt_got;
43 | Elf32_Rel *plt_rel;
44 | unsigned plt_rel_count;
45 | Elf32_Rel *rel;
46 | unsigned rel_count;
47 | unsigned *preinit_array;
48 | unsigned preinit_array_count;
49 | unsigned *init_array;
50 | unsigned init_array_count;
51 | unsigned *fini_array;
52 | unsigned fini_array_count;
53 | void (*init_func)(void);
54 | void (*fini_func)(void);
55 | #ifdef ANDROID_ARM_LINKER
56 | /* ARM EABI section used for stack unwinding. */
57 | unsigned *ARM_exidx;
58 | unsigned ARM_exidx_count;
59 | #endif
60 | unsigned refcount;
61 | struct LinkMap linkmap;
62 | int constructors_called;
63 | Elf32_Addr gnu_relro_start;
64 | unsigned gnu_relro_len;
65 | };
66 |
67 | #define R_ARM_ABS32 2
68 | #define R_ARM_COPY 20
69 | #define R_ARM_GLOB_DAT 21
70 | #define R_ARM_JUMP_SLOT 22
71 | #define R_ARM_RELATIVE 23
72 |
73 |
74 | #endif //CLASSPATCH_LINKER_H
75 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/NativeElfHook.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/11/27.
3 | //
4 |
5 | #include "NativeElfHook.h"
6 | #include
7 |
8 | #define LOG_TAG "NativeElfHook"
9 |
10 |
11 | static unsigned elfHash(const char *_name) {
12 | const unsigned char *name = (const unsigned char *) _name;
13 | unsigned h = 0, g;
14 | while(*name) {
15 | h = (h << 4) + *name++;
16 | g = h & 0xf0000000;
17 | h ^= g;
18 | h ^= g >> 24;
19 | }
20 | return h;
21 | }
22 |
23 | static Elf32_Sym * soInfoElfLookUp(struct SoInfo *si, unsigned hash, const char *name) {
24 | Elf32_Sym *symtab = si->symtab;
25 | const char *strtab = si->strtab;
26 | unsigned n;
27 |
28 | for( n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n] ) {
29 | Elf32_Sym *s = symtab + n;
30 | const char *methodName = strtab + s->st_name;
31 | LOGD("symbol name is %s", methodName);
32 | if( strcmp(methodName, name) == 0 ) {
33 | return s;
34 | }
35 | }
36 | return NULL;
37 | }
38 |
39 | //elfLDModule getHookedMap() {
40 | // elfLDModule modules;
41 | // char buffer[1024] = {0};
42 | // uintptr_t address;
43 | // std::string name;
44 | //
45 | // FILE *fp = fopen( "/proc/self/maps", "rt" );
46 | // if( fp == NULL ){
47 | // perror("fopen");
48 | // goto done;
49 | // }
50 | //
51 | // while( fgets( buffer, sizeof(buffer), fp ) ) {
52 | // if( strstr( buffer, "r-xp" ) ){
53 | // address = (uintptr_t)strtoul( buffer, NULL, 16 );
54 | // name = strrchr( buffer, ' ' ) + 1;
55 | // name.resize( name.size() - 1 );
56 | //
57 | // modules.push_back( ld_module_t( address, name ) );
58 | // }
59 | // }
60 | //
61 | // done:
62 | //
63 | // if(fp){
64 | // fclose(fp);
65 | // }
66 | //
67 | // return modules;
68 | //}
69 |
70 | unsigned patchAddress( unsigned addr, unsigned newval ) {
71 | unsigned original = -1;
72 | size_t pagesize = sysconf(_SC_PAGESIZE);
73 | const void *aligned_pointer = (const void*)(addr & ~(pagesize - 1));
74 |
75 | mprotect(aligned_pointer, pagesize, PROT_WRITE | PROT_READ | PROT_EXEC);
76 |
77 | original = *(unsigned *)addr;
78 | *((unsigned*)addr) = newval;
79 |
80 | mprotect(aligned_pointer, pagesize, PROT_READ);
81 |
82 | return original;
83 | }
84 |
85 | unsigned addElfHook(const char* soName, const char* symbol, void* replaceFunc) {
86 | struct SoInfo *si = NULL;
87 | Elf32_Rel *rel = NULL;
88 | Elf32_Sym *s = NULL;
89 | unsigned int sym_offset = 0;
90 | size_t i;
91 |
92 | // since we know the module is already loaded and mostly
93 | // we DO NOT want its constructors to be called again,
94 | // ise RTLD_NOLOAD to just get its soinfo address.
95 | si = (struct SoInfo *)dlopen( soName, RTLD_GLOBAL/* RTLD_NOLOAD */ );
96 | if( !si ){
97 | LOGE( "dlopen error: %s.", dlerror() );
98 | return 0;
99 | }
100 | s = soInfoElfLookUp( si, elfHash(symbol), symbol );
101 | if( !s ){
102 | return 0;
103 | }
104 | sym_offset = s - si->symtab;
105 | LOGD("the sym offset is %d", sym_offset);
106 |
107 | // loop reloc table to find the symbol by index
108 | for( i = 0, rel = si->plt_rel; i < si->plt_rel_count; ++i, ++rel ) {
109 | unsigned type = ELF32_R_TYPE(rel->r_info);
110 | unsigned sym = ELF32_R_SYM(rel->r_info);
111 | unsigned reloc = (unsigned)(rel->r_offset + si->base);
112 |
113 | if( sym_offset == sym ) {
114 | switch(type) {
115 | case R_ARM_JUMP_SLOT:
116 | return patchAddress( reloc, replaceFunc );
117 | default:
118 | LOGE( "Expected R_ARM_JUMP_SLOT, found 0x%X", type );
119 | }
120 | }
121 | }
122 |
123 | unsigned original = 0;
124 |
125 | // loop dyn reloc table
126 | for( i = 0, rel = si->rel; i < si->rel_count; ++i, ++rel ) {
127 | unsigned type = ELF32_R_TYPE(rel->r_info);
128 | unsigned sym = ELF32_R_SYM(rel->r_info);
129 | unsigned reloc = (unsigned)(rel->r_offset + si->base);
130 | if( sym_offset == sym ) {
131 | switch(type) {
132 | case R_ARM_ABS32:
133 | case R_ARM_GLOB_DAT:
134 | original = patchAddress( reloc, replaceFunc );
135 | default:
136 | LOGE( "Expected R_ARM_ABS32 or R_ARM_GLOB_DAT, found 0x%X", type );
137 | }
138 | }
139 | }
140 |
141 | if( original == 0 ){
142 | LOGE( "Unable to find symbol in the reloc tables ( plt_rel_count=%u - rel_count=%u ).", si->plt_rel_count, si->rel_count );
143 | }
144 |
145 | return original;
146 | }
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/NativeElfHook.h:
--------------------------------------------------------------------------------
1 | // 针对Native Method进行Elf Hook。
2 | // Created by Jarlene on 2015/11/27.
3 | //
4 |
5 | #ifndef CLASSPATCH_NATIVEELFHOOK_H
6 | #define CLASSPATCH_NATIVEELFHOOK_H
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "Linker.h"
14 | #include "../../base/log.h"
15 |
16 |
17 | #define ORIGINAL( TYPENAME, ... ) \
18 | ((TYPENAME ## _t)find_original( #TYPENAME ))( __VA_ARGS__ )
19 |
20 | #define DEFINEHOOK( RET_TYPE, NAME, ARGS ) \
21 | typedef RET_TYPE (* NAME ## _t)ARGS; \
22 | RET_TYPE hook_ ## NAME ARGS
23 |
24 | #define ADDHOOK( NAME ) \
25 | { #NAME, 0, (uintptr_t)&hook_ ## NAME }
26 |
27 | typedef struct LdModule {
28 | uintptr_t address;
29 | std::string name;
30 | LdModule( uintptr_t a, const std::string& n ) : address(a), name(n) {
31 | }
32 | } elfLDModule;
33 |
34 | typedef std::vector elfLDModuleMap;
35 |
36 | unsigned addElfHook(const char* soName, const char* symbol, void* replaceFunc);
37 |
38 | #endif //CLASSPATCH_NATIVEELFHOOK_H
39 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/NativeHook.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/11/24.
3 | //
4 |
5 | #include "NativeHook.h"
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "../../base/log.h"
14 |
15 |
16 | static Elf_Sym* lookup_symbol(soinfo *si, const char *symbolname) {
17 | Elf_Sym* symtab = si->symtab;
18 | const char *strtab = si->strtab;
19 |
20 | for(unsigned int i=1; i < (si->nchain); i++) {
21 | Elf_Sym *s = symtab + i;
22 | const char *str = strtab + s->st_name;
23 | LOGD("the symbol name is %s", str);
24 | if (!strcmp(str, symbolname)) {
25 | LOGD("Found symbol %s @ index %d with offset %x\n", str, i, s->st_value);
26 | return s;
27 | }
28 | }
29 | return NULL;
30 | }
31 |
32 | void *elfLoadLibrary(const char *libname) {
33 | void *handle = dlopen(libname, RTLD_GLOBAL);
34 | if (!handle) {
35 | LOGD("Failed to load lib %s\n", libname);
36 | }
37 |
38 | return handle;
39 | }
40 |
41 | void *elfHookSymbol(void * libhandle, const char *symbolname, void *hookfunc) {
42 | soinfo *si = (soinfo *)libhandle;
43 | if (!si) {
44 | LOGD("Invalid handle\n");
45 | return NULL;
46 | }
47 | Elf_Sym* symbol = lookup_symbol(si, symbolname);
48 | if (!symbol) {
49 | LOGD("Failed to find symbol %s\n", symbolname);
50 | return NULL;
51 | }
52 | LOGD("so bias addr is %p, symbol off is %p",si->load_bias,symbol->st_value);
53 | Elf32_Word originalfunc = si->load_bias + symbol->st_value ;
54 | LOGD("the original func addr is %p", originalfunc);
55 |
56 | if (originalfunc == (Elf32_Word)hookfunc) {
57 | LOGD("Symbol %s already patched\n", symbolname);
58 | return NULL;
59 | }
60 |
61 | if (mprotect((void *)si->base, si->size, PROT_READ|PROT_WRITE|PROT_EXEC)) {
62 | LOGD("Failed to make symbol table writable: %s\n", strerror(errno));
63 | }
64 | LOGD("hookfunc addr is %p",hookfunc);
65 | Elf32_Word hookoffset = (uint32_t)hookfunc - si->load_bias;
66 | symbol->st_value = hookoffset;
67 | mprotect((void *)si->base, si->size, PROT_READ);
68 | return (void *)originalfunc;
69 |
70 | }
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/NativeHook.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2015/11/24.
3 | //
4 |
5 | #ifndef CLASSPATCH_NATIVEHOOK_H
6 | #define CLASSPATCH_NATIVEHOOK_H
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 |
19 | #define LOG_TAG "NativeHook"
20 |
21 | #define PAGE_START(addr, size) ~((size) - 1) & (addr)
22 | #define SOINFO_NAME_LEN 128
23 |
24 |
25 | typedef void (*linker_function_t)();
26 |
27 | typedef struct link_map_t {
28 | uintptr_t l_addr;
29 | char* l_name;
30 | uintptr_t l_ld;
31 | struct link_map_t* l_next;
32 | struct link_map_t* l_prev;
33 | } link_map_t;
34 |
35 | typedef struct soinfo {
36 | char name[SOINFO_NAME_LEN];
37 | const Elf_Phdr* phdr;
38 | size_t phnum;
39 | Elf_Addr entry;
40 | Elf_Addr base;
41 | unsigned size;
42 |
43 | uint32_t unused1; // DO NOT USE, maintained for compatibility.
44 |
45 | Elf_Dyn* dynamic;
46 |
47 | uint32_t unused2; // DO NOT USE, maintained for compatibility
48 | uint32_t unused3; // DO NOT USE, maintained for compatibility
49 |
50 | struct soinfo* next;
51 |
52 | unsigned flags;
53 |
54 | const char* strtab;
55 | Elf_Sym* symtab;
56 | size_t nbucket;
57 | size_t nchain;
58 | unsigned* bucket;
59 | unsigned* chain;
60 |
61 | //------------------
62 |
63 | // This is only used by 32-bit MIPS, but needs to be here for
64 | // all 32-bit architectures to preserve binary compatibility.
65 | unsigned* plt_got;
66 |
67 | Elf_Rel* plt_rel;
68 | size_t plt_rel_count;
69 |
70 | Elf_Rel* rel;
71 | size_t rel_count;
72 |
73 | linker_function_t* preinit_array;
74 | size_t preinit_array_count;
75 |
76 | linker_function_t* init_array;
77 | size_t init_array_count;
78 | linker_function_t* fini_array;
79 | size_t fini_array_count;
80 |
81 | linker_function_t init_func;
82 | linker_function_t fini_func;
83 |
84 | // ARM EABI section used for stack unwinding.
85 | unsigned* ARM_exidx;
86 | size_t ARM_exidx_count;
87 |
88 | size_t ref_count;
89 | link_map_t link_map;
90 |
91 | int constructors_called;
92 |
93 | // When you read a virtual address from the ELF file, add this
94 | // value to get the corresponding address in the process' address space.
95 | Elf_Addr load_bias;
96 |
97 | } soinfo;
98 |
99 |
100 |
101 | void *elfLoadLibrary(const char *libname);
102 | void *elfHookSymbol(void * libhandle, const char *symbolname, void *hookfunc);
103 |
104 |
105 |
106 | #endif //CLASSPATCH_NATIVEHOOK_H
107 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/NativeInlineHook.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Administrator on 2016/5/4.
3 | //
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #include "NativeInlineHook.h"
19 | #include "InlineUtils.h"
20 |
21 |
22 |
23 | void inline hook_cacheflush(unsigned int begin, unsigned int end) {
24 | const int syscall = 0xf0002;
25 | __asm __volatile (
26 | "mov r0, %0\n"
27 | "mov r1, %1\n"
28 | "mov r7, %2\n"
29 | "mov r2, #0x0\n"
30 | "svc 0x00000000\n"
31 | :
32 | : "r" (begin), "r" (end), "r" (syscall)
33 | : "r0", "r1", "r7"
34 | );
35 | }
36 |
37 | int hook_direct(struct hook_t *h, unsigned int addr, void *hookf) {
38 | int i;
39 |
40 | LOGD("addr = %x\n", addr);
41 | LOGD("hookf = %x\n", hookf);
42 |
43 | if ((addr % 4 == 0 && (unsigned int)hookf % 4 != 0) || (addr % 4 != 0 && (unsigned int)hookf % 4 == 0))
44 | LOGD("addr 0x%x and hook 0x%x\n don't match!\n", addr, hookf);
45 |
46 | //LOGD("ARM\n")
47 | h->thumb = 0;
48 | h->patch = (unsigned int)hookf;
49 | h->orig = addr;
50 | LOGD("orig = %x\n", h->orig);
51 | h->jump[0] = 0xe59ff000; // LDR pc, [pc, #0]
52 | h->jump[1] = h->patch;
53 | h->jump[2] = h->patch;
54 | for (i = 0; i < 3; i++)
55 | h->store[i] = ((int*)h->orig)[i];
56 | for (i = 0; i < 3; i++)
57 | ((int*)h->orig)[i] = h->jump[i];
58 |
59 | hook_cacheflush((unsigned int)h->orig, (unsigned int)h->orig+sizeof(h->jumpt));
60 | return 1;
61 | }
62 |
63 | int hook(struct hook_t *h, int pid, char *libname, char *funcname, void *hook_arm, void *hook_thumb) {
64 | unsigned long int addr;
65 | int i;
66 |
67 | if (find_name(pid, funcname, libname, &addr) < 0) {
68 | LOGD("can't find: %s\n", funcname);
69 | return 0;
70 | }
71 |
72 | LOGD("hooking: %s = 0x%x ", funcname, addr);
73 | strncpy(h->name, funcname, sizeof(h->name)-1);
74 |
75 | if (addr % 4 == 0) {
76 | LOGD("ARM using 0x%x\n", hook_arm);
77 | h->thumb = 0;
78 | h->patch = (unsigned int)hook_arm;
79 | h->orig = addr;
80 | h->jump[0] = 0xe59ff000; // LDR pc, [pc, #0]
81 | h->jump[1] = h->patch;
82 | h->jump[2] = h->patch;
83 | for (i = 0; i < 3; i++)
84 | h->store[i] = ((int*)h->orig)[i];
85 | for (i = 0; i < 3; i++)
86 | ((int*)h->orig)[i] = h->jump[i];
87 | }
88 | else {
89 | if ((unsigned long int)hook_thumb % 4 == 0)
90 | LOGD("warning hook is not thumb 0x%x\n", hook_thumb);
91 | h->thumb = 1;
92 | LOGD("THUMB using 0x%x\n", hook_thumb);
93 | h->patch = (unsigned int)hook_thumb;
94 | h->orig = addr;
95 | h->jumpt[1] = 0xb4;
96 | h->jumpt[0] = 0x30; // push {r4,r5}
97 | h->jumpt[3] = 0xa5;
98 | h->jumpt[2] = 0x03; // add r5, pc, #12
99 | h->jumpt[5] = 0x68;
100 | h->jumpt[4] = 0x2d; // ldr r5, [r5]
101 | h->jumpt[7] = 0xb0;
102 | h->jumpt[6] = 0x02; // add sp,sp,#8
103 | h->jumpt[9] = 0xb4;
104 | h->jumpt[8] = 0x20; // push {r5}
105 | h->jumpt[11] = 0xb0;
106 | h->jumpt[10] = 0x81; // sub sp,sp,#4
107 | h->jumpt[13] = 0xbd;
108 | h->jumpt[12] = 0x20; // pop {r5, pc}
109 | h->jumpt[15] = 0x46;
110 | h->jumpt[14] = 0xaf; // mov pc, r5 ; just to pad to 4 byte boundary
111 | memcpy(&h->jumpt[16], (unsigned char*)&h->patch, sizeof(unsigned int));
112 | unsigned int orig = addr - 1; // sub 1 to get real address
113 | for (i = 0; i < 20; i++) {
114 | h->storet[i] = ((unsigned char*)orig)[i];
115 | //LOGD("%0.2x ", h->storet[i]);
116 | }
117 | //LOGD("\n");
118 | for (i = 0; i < 20; i++) {
119 | ((unsigned char*)orig)[i] = h->jumpt[i];
120 | //LOGD("%0.2x ", ((unsigned char*)orig)[i]);
121 | }
122 | }
123 | LOGD("h->store = 0x%x,0x%x,0x%x\n", ((int*)h->store)[0],((int*)h->store)[1],((int*)h->store)[2]);
124 | LOGD("h->orig = 0x%x,0x%x,0x%x\n", ((int*)h->orig)[0],((int*)h->orig)[1],((int*)h->orig)[2]);
125 | hook_cacheflush((unsigned int)h->orig, (unsigned int)h->orig+sizeof(h->jumpt));
126 | return 1;
127 | }
128 |
129 | void hook_precall(struct hook_t *h) {
130 | int i;
131 |
132 | if (h->thumb) {
133 | unsigned int orig = h->orig - 1;
134 | for (i = 0; i < 20; i++) {
135 | ((unsigned char*)orig)[i] = h->storet[i];
136 | }
137 | }
138 | else {
139 | for (i = 0; i < 3; i++)
140 | ((int*)h->orig)[i] = h->store[i];
141 | }
142 | hook_cacheflush((unsigned int)h->orig, (unsigned int)h->orig+sizeof(h->jumpt));
143 | }
144 |
145 | void hook_postcall(struct hook_t *h) {
146 | int i;
147 |
148 | if (h->thumb) {
149 | unsigned int orig = h->orig - 1;
150 | for (i = 0; i < 20; i++)
151 | ((unsigned char*)orig)[i] = h->jumpt[i];
152 | }
153 | else {
154 | for (i = 0; i < 3; i++)
155 | ((int*)h->orig)[i] = h->jump[i];
156 | }
157 | hook_cacheflush((unsigned int)h->orig, (unsigned int)h->orig+sizeof(h->jumpt));
158 | }
159 |
160 | void unhook(struct hook_t *h) {
161 | LOGD("unhooking %s = %x hook = %x ", h->name, h->orig, h->patch);
162 | hook_precall(h);
163 | }
164 | /////////////////////////////////////////////////////////////
165 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/NativeInlineHook.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Administrator on 2016/5/4.
3 | //
4 |
5 | #ifndef CLASSPATCH_NATIVEINLINEHOOK_H
6 | #define CLASSPATCH_NATIVEINLINEHOOK_H
7 |
8 | #include "../../base/log.h"
9 |
10 | #define LOG_TAG "NativeInlineHook"
11 |
12 | struct hook_t {
13 | unsigned int jump[3];
14 | unsigned int store[3];
15 | unsigned char jumpt[20];
16 | unsigned char storet[20];
17 | unsigned int orig;
18 | unsigned int patch;
19 | unsigned char thumb;
20 | unsigned char name[128];
21 | void *data;
22 | };
23 |
24 | void hook_cacheflush(unsigned int begin, unsigned int end);
25 | void hook_precall(struct hook_t *h);
26 | void hook_postcall(struct hook_t *h);
27 | int hook(struct hook_t *h, int pid, char *libname, char *funcname, void *hook_arm, void *hook_thumb);
28 | void unhook(struct hook_t *h);
29 |
30 | #endif //CLASSPATCH_NATIVEINLINEHOOK_H
31 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/TKHooklib.h:
--------------------------------------------------------------------------------
1 | #ifndef _TKHOOKLIB_H
2 | #define _TKHOOKLIB_H
3 |
4 | #define SOINFO_NAME_LEN 128
5 |
6 | #define HOOK_SUCCESS 0
7 | #define HOOK_FAILED -1
8 |
9 | typedef struct _HookStruct{
10 | char SOName[SOINFO_NAME_LEN];
11 | char FunctionName[SOINFO_NAME_LEN];
12 | void *NewFunc;
13 | void *OldFunc;
14 | void *occPlace;
15 | }HookStruct;
16 |
17 | #ifdef __cplusplus
18 | extern "C" {
19 | #endif
20 |
21 | void TK_UnHookExportFunction(HookStruct *pHookStruct);
22 |
23 | void TK_UnHookImportFunction(HookStruct *pHookStruct);
24 |
25 | /*
26 | ** Return: HOOK_SUCCESS or HOOK_FAILED
27 | ** Example: Hook libfoo.so Import Function: puts
28 | ** HookStruct HookPara;
29 | ** strncpy(HookPara.SOName, "libfoo.so", strlen("libfoo.so"));
30 | ** strncpy(HookPara.FunctionName, "puts", strlen("puts"));
31 | ** HookPara.NewFunc = myputs;
32 | ** TK_HookImportFunction(&HookPara);
33 | */
34 | int TK_HookImportFunction(HookStruct *pHookStruct);
35 |
36 | /*
37 | ** Return: HOOK_SUCCESS or HOOK_FAILED
38 | ** Example: Hook libc.so Export Function: puts
39 | ** HookStruct HookPara;
40 | ** strncpy(HookPara.SOName, "libc.so", strlen("libc.so"));
41 | ** strncpy(HookPara.FunctionName, "puts", strlen("puts"));
42 | ** HookPara.NewFunc = myputs;
43 | ** TK_HookExportFunction(&HookPara);
44 | */
45 | int TK_HookExportFunction(HookStruct *pHookStruct);
46 |
47 | /*
48 | ** Return: HOOK_SUCCESS or HOOK_FAILED
49 | ** Example: Inline Hook libc.so Function: puts
50 | ** void* OldFunc = NULL;
51 | ** TK_InlineHookFunction(puts, myputs, &OldFunc);
52 | ** ----------------------------------------------
53 | ** For the new implementation of HOOK Bridge, I suggest you add some lock to enhance the stability.
54 | **
55 | **
56 | int myputs(const char *string){
57 | // Do sth before calling ori-function
58 |
59 | pthread_mutex_lock(&hook_mutex);
60 | puts(string);
61 | pthread_mutex_unlock(&hook_mutex);
62 |
63 | // Do sth after calling ori-function
64 | }
65 | **
66 | */
67 | int TK_InlineHookFunction(void *TargetFunc, void *NewFunc, void** OldFunc);
68 |
69 | #ifdef __cplusplus
70 | };
71 | #endif
72 |
73 | #endif
74 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/hooktest.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "ElfHook.h"
5 |
6 | #define LOG_TAG "HookTest"
7 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
8 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
9 |
10 | int hook_memcpy(const void *s1, const void *s2, int count) {
11 | LOGE("***%s: p1=%p, p2=%p\n", __FUNCTION__, s1, s2);
12 | int nRet = memcmp(s1, s2, count);
13 | return nRet;
14 | }
15 |
16 | jint Java_com_example_myndk_MainActivity_hookTest(JNIEnv* env, jobject thiz) {
17 | char * soName = "libhello-jni.so";
18 | char * funName = "memcpy";
19 | LOGE("hook_memcpy addr: %p", hook_memcpy);
20 | elfHook(soName, funName, (unsigned) hook_memcpy);
21 | return -1;
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/hookNative/relocate.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Jarlene on 2016/4/9.
3 | //
4 |
5 | #ifndef _RELOCATE_H
6 | #define _RELOCATE_H
7 |
8 | #include
9 | #ifdef __cplusplus
10 | extern "C" {
11 | #endif
12 | void relocateInstruction(uint32_t target_addr, void *orig_instructions, int length, void *trampoline_instructions, int *orig_boundaries, int *trampoline_boundaries, int *count);
13 | #ifdef __cplusplus
14 | };
15 | #endif
16 | #endif
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/libTKHooklib.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/classlib/src/main/jni/hook/libTKHooklib.so
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/libsubstrate-dvm.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/classlib/src/main/jni/hook/libsubstrate-dvm.so
--------------------------------------------------------------------------------
/classlib/src/main/jni/hook/libsubstrate.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/classlib/src/main/jni/hook/libsubstrate.so
--------------------------------------------------------------------------------
/classlib/src/main/jni/test/Android.mk:
--------------------------------------------------------------------------------
1 | LOCAL_PATH:= $(call my-dir)
2 |
3 | include $(CLEAR_VARS)
4 |
5 | LOCAL_SRC_FILES:= test.cpp
6 |
7 | LOCAL_CFLAGS := -std=gnu++11 -fpermissive
8 |
9 | LOCAL_LDLIBS := -llog
10 |
11 | LOCAL_MODULE:= patch
12 |
13 | include $(BUILD_SHARED_LIBRARY)
14 |
--------------------------------------------------------------------------------
/classlib/src/main/jni/test/Application.mk:
--------------------------------------------------------------------------------
1 | # The ARMv7 is significanly faster due to the use of the hardware FPU
2 | APP_ABI := armeabi
3 | APP_PLATFORM := android-9
4 | APP_STL := gnustl_static
5 | APP_STL += stlport_static
6 | APP_STL +=c++_shared
--------------------------------------------------------------------------------
/classlib/src/main/jni/test/test.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Administrator on 2016/3/29.
3 | //
4 |
5 | #include
6 | #include
7 | #include
8 | #include "jni.h"
9 |
10 |
11 | extern "C" void testString(JNIEnv* env) {
12 | jclass clazz = env->FindClass("com/baidu/music/classpatch/MainActivity");
13 | if (clazz == NULL) {
14 | return;
15 | }
16 | jobject obj = env->NewGlobalRef(clazz);
17 | jmethodID method = env->GetStaticMethodID(clazz, "toastSoString", "(Ljava/lang/String;)V");
18 | if (method == NULL) {
19 | return;
20 | }
21 | jstring str = env->NewStringUTF("come from test.so ---testString");
22 | env->CallStaticVoidMethod(obj, method, str);
23 | env->DeleteGlobalRef(obj);
24 | }
--------------------------------------------------------------------------------
/classlib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ClassLib
3 |
4 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 | #Mon Nov 23 16:29:50 CST 2015
16 | systemProp.http.proxyHost=agent.baidu.com
17 | systemProp.http.proxyPort=8118
18 | android.useDeprecatedNdk=true
19 | ndk.path=D\:\\DevelopmentTools\\android-ndk-r10b
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Apr 14 16:14:36 CST 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/patch/DexTest.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/patch/DexTest.apk
--------------------------------------------------------------------------------
/patch/classes/DexTest.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/patch/classes/DexTest.class
--------------------------------------------------------------------------------
/patch/classes/MainActivity.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/patch/classes/MainActivity.class
--------------------------------------------------------------------------------
/patch/classes/classes.dex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/patch/classes/classes.dex
--------------------------------------------------------------------------------
/patch/libpatch.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/patch/libpatch.so
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion '23.0.2'
6 |
7 | defaultConfig {
8 | applicationId "com.baidu.music.classpatch"
9 | minSdkVersion 9
10 | targetSdkVersion 11
11 | versionCode 1000
12 | versionName "1.0.0.0"
13 | }
14 |
15 | signingConfigs {
16 | debug {
17 | storeFile file("keystore")
18 | storePassword "123456"
19 | keyAlias "keystore"
20 | keyPassword "123456"
21 | }
22 | }
23 |
24 | buildTypes {
25 | debug {
26 | signingConfig signingConfigs.debug
27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 | }
31 |
32 | dependencies {
33 | compile fileTree(dir: 'libs', include: ['*.jar'])
34 | testCompile 'junit:junit:4.12'
35 | compile 'com.android.support:appcompat-v7:22.2.1'
36 | compile 'com.android.support:design:22.2.1'
37 | compile project(':classlib')
38 | }
39 |
--------------------------------------------------------------------------------
/sample/keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/sample/keystore
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\DevelopmentTools\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/src/androidTest/java/baiducom/music/classpatch/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package baiducom.music.classpatch;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/baidu/music/classpatch/ClassPatchApplication.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classpatch;
2 |
3 | import com.baidu.music.classlib.dex.PatchDexApplication;
4 |
5 | /**
6 | * Created by Jarlene on 2015/11/27.
7 | */
8 | public class ClassPatchApplication extends PatchDexApplication {
9 |
10 | @Override
11 | public void onCreate() {
12 | super.onCreate();
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/baidu/music/classpatch/DexTest.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classpatch;
2 |
3 | import android.content.Context;
4 | import android.widget.Toast;
5 |
6 | /**
7 | * Created by Jarlene on 2015/11/27.
8 | */
9 | public class DexTest {
10 |
11 | private static String from = "来自Patch Dex";
12 |
13 | public void showToast(Context context) {
14 | Toast.makeText(context, "Toast from " + from, Toast.LENGTH_SHORT ).show();
15 | }
16 |
17 |
18 | public static String getStringStatic(String a, String b){
19 | b = b + from;
20 | return String.format("%s %s from getStringStatic ", a, b);
21 | }
22 |
23 | public static String getTestString(String a, String b) {
24 | b = b + from;
25 | return "hook ok, get string from getTestString" + a + b;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/baidu/music/classpatch/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classpatch;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.Environment;
7 | import android.support.design.widget.FloatingActionButton;
8 | import android.support.design.widget.Snackbar;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.support.v7.widget.Toolbar;
11 | import android.util.Log;
12 | import android.view.Menu;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.widget.Button;
16 | import android.widget.Toast;
17 |
18 | import com.baidu.music.classlib.dex.PatchDex;
19 | import com.baidu.music.classlib.jni.HookBridge;
20 | import com.baidu.music.classlib.listener.FileOperatorListener;
21 | import com.baidu.music.classlib.manager.HookManager;
22 | import com.baidu.music.classlib.manager.ResourceManager;
23 | import com.baidu.music.classlib.resource.PatchResource;
24 |
25 | import java.io.File;
26 | import java.lang.reflect.Method;
27 |
28 |
29 | public class MainActivity extends AppCompatActivity implements View.OnClickListener{
30 |
31 | private Button classToDexBtn, showToastBtn, changeMethodBtn, changeSoBtn, copyFileBtn;
32 |
33 | private static Context mContext;
34 |
35 | private FileOperatorListener listener = new FileOperatorListener() {
36 | @Override
37 | public void notifyCompleted() {
38 | Toast.makeText(mContext, "复制成功",Toast.LENGTH_SHORT).show();
39 | PatchDex.addAllDexFile(mContext,
40 | HookManager.getInstance().getPatchDir(mContext).getAbsolutePath(),
41 | HookManager.getInstance().getPatchOptDir(mContext).getAbsolutePath(), true);
42 | }
43 |
44 | @Override
45 | public void notifyError(int errorCode) {
46 | Toast.makeText(mContext, "复制失败",Toast.LENGTH_SHORT).show();
47 | }
48 | };
49 |
50 | @Override
51 | protected void onCreate(Bundle savedInstanceState) {
52 | super.onCreate(savedInstanceState);
53 | mContext = this;
54 | String apkPath = HookManager.getInstance().getPatchDir(mContext).getAbsolutePath() + File.separator + "DexTest.apk";
55 | PatchResource patchResource = ResourceManager.getInstance().getPatchResource(mContext, apkPath);
56 | View resId = patchResource.getResApkLayoutView(this, "activity_main");
57 | if (resId == null) {
58 | setContentView(R.layout.activity_main);
59 | } else {
60 | setContentView(resId);
61 | }
62 | classToDexBtn = (Button) findViewById(R.id.classesToDex);
63 | showToastBtn = (Button) findViewById(R.id.showToast);
64 | changeMethodBtn = (Button) findViewById(R.id.changeMethod);
65 | changeSoBtn = (Button) findViewById(R.id.changeSo);
66 | copyFileBtn = (Button) findViewById(R.id.copyFile);
67 |
68 | classToDexBtn.setOnClickListener(this);
69 | showToastBtn.setOnClickListener(this);
70 | changeMethodBtn.setOnClickListener(this);
71 | changeSoBtn.setOnClickListener(this);
72 | copyFileBtn.setOnClickListener(this);
73 |
74 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
75 | setSupportActionBar(toolbar);
76 |
77 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
78 | fab.setOnClickListener(this);
79 | }
80 |
81 | @Override
82 | protected void onDestroy() {
83 | super.onDestroy();
84 | android.os.Process.killProcess(android.os.Process.myPid());
85 | }
86 |
87 | @Override
88 | public boolean onCreateOptionsMenu(Menu menu) {
89 | // Inflate the menu; this adds items to the action bar if it is present.
90 | getMenuInflater().inflate(R.menu.menu_main, menu);
91 | return true;
92 | }
93 |
94 | @Override
95 | public boolean onOptionsItemSelected(MenuItem item) {
96 | // Handle action bar item clicks here. The action bar will
97 | // automatically handle clicks on the Home/Up button, so long
98 | // as you specify a parent activity in AndroidManifest.xml.
99 | int id = item.getItemId();
100 |
101 | //noinspection SimplifiableIfStatement
102 | if (id == R.id.action_settings) {
103 | return true;
104 | }
105 |
106 | return super.onOptionsItemSelected(item);
107 | }
108 |
109 |
110 | // native 执行该方法
111 | public static void toastSoString(String from) {
112 | Log.d("MainActivity", from);
113 | Toast.makeText(mContext, from, Toast.LENGTH_LONG).show();
114 | }
115 |
116 |
117 | @Override
118 | public void onClick(View v) {
119 | switch (v.getId()) {
120 | case R.id.classesToDex:
121 | classToDexButtonClicked();
122 | break;
123 | case R.id.showToast:
124 | showToastButtonClicked();
125 | break;
126 | case R.id.changeMethod:
127 | javaHookButtonClicked();
128 | break;
129 | case R.id.changeSo:
130 | nativeHookButtonClicked();
131 | break;
132 | case R.id.fab:
133 | Snackbar.make(v, "the class come from Patch Dex", Snackbar.LENGTH_LONG)
134 | .setAction("Action", null).show();
135 | break;
136 | case R.id.copyFile:
137 | String patch = Environment.getExternalStorageDirectory().getAbsolutePath()
138 | + File.separator + "ClassPatch"
139 | + File.separator + "DexTest.apk";
140 | HookManager.getInstance().copyFile(mContext, patch, listener);
141 | Intent intent = new Intent(mContext, SecondActivity.class);
142 | startActivity(intent);
143 | break;
144 | default:
145 | break;
146 | }
147 | }
148 |
149 | private void classToDexButtonClicked() {
150 | String classPathDir = Environment.getExternalStorageDirectory().getAbsolutePath()
151 | + File.separator + "ClassPatch" + File.separator + "classes";
152 | String dexPath = new File(classPathDir, "classes.dex").getAbsolutePath();
153 | HookManager.getInstance().makeDex(classPathDir, dexPath, new FileOperatorListener() {
154 | @Override
155 | public void notifyCompleted() {
156 | Toast.makeText(mContext, "生成dex Ok", Toast.LENGTH_LONG).show();
157 | }
158 |
159 | @Override
160 | public void notifyError(int errorCode) {
161 | Toast.makeText(mContext, "生成dex Error", Toast.LENGTH_LONG).show();
162 | }
163 | });
164 | }
165 |
166 | private void showToastButtonClicked() {
167 | DexTest test = new DexTest();
168 | test.showToast(MainActivity.this);
169 | }
170 |
171 | private void nativeHookButtonClicked() {
172 | final String soPath = Environment.getExternalStorageDirectory().getAbsolutePath()
173 | + File.separator + "ClassPatch"
174 | + File.separator + "libpatch.so";
175 | final String destFile = new File(HookManager.getInstance().getSoPatchDir(mContext),
176 | "libpatch.so").getAbsolutePath();
177 | HookManager.getInstance().copyFile(mContext, destFile, soPath, new FileOperatorListener() {
178 | @Override
179 | public void notifyCompleted() {
180 | try {
181 | System.load(destFile);
182 | } catch (Exception e) {
183 | e.printStackTrace();
184 | }
185 | HookBridge.hookNativeMethod("libcommonHook.so", "libpatch.so", "testString", "testString");
186 | }
187 |
188 | @Override
189 | public void notifyError(int errorCode) {
190 | // Toast.makeText(mContext, "copy so error",Toast.LENGTH_SHORT).show();
191 | HookBridge.hookNativeMethod("libcommonHook.so", "libpatch.so", "testString", "testString");
192 | }
193 | });
194 | }
195 |
196 | private void javaHookButtonClicked() {
197 | try {
198 | Method src = DexTest.class.getDeclaredMethod("getStringStatic", int.class, String.class);
199 | Method target = DexTest.class.getDeclaredMethod("getTestString", int.class, String.class);
200 | HookBridge.hookJavaMethod(src, target);
201 | } catch (NoSuchMethodException e) {
202 | e.printStackTrace();
203 | }
204 | Toast.makeText(MainActivity.this, DexTest.getStringStatic("das", " from aa "), Toast.LENGTH_SHORT).show();
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/baidu/music/classpatch/SecondActivity.java:
--------------------------------------------------------------------------------
1 | package com.baidu.music.classpatch;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 |
8 | /**
9 | * Created by Jarlene on 2015/11/25.
10 | */
11 | public class SecondActivity extends Activity {
12 |
13 |
14 | private static Context context;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | context = this;
20 | }
21 |
22 | @Override
23 | protected void onDestroy() {
24 | super.onDestroy();
25 |
26 | Log.d("SecondActivity", "destroy");
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/sample/src/main/jniLibs/armeabi/libcommonHook.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/sample/src/main/jniLibs/armeabi/libcommonHook.so
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
21 |
22 |
28 |
34 |
40 |
46 |
47 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 | >
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 9dp
4 | 9dp
5 | 9dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ClassPatch
3 | Settings
4 | 显示Toast
5 | 生成Dex
6 | 替换Method
7 | 替换So
8 | 复制文件
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/test/java/baiducom/music/classpatch/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package baiducom.music.classpatch;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sample', ':classlib'
2 |
--------------------------------------------------------------------------------
/tool/apkpatch.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jarlene/ClassPatch/37c97cc4938879460189a680b0e7d8d3b3ee4bea/tool/apkpatch.zip
--------------------------------------------------------------------------------