来源
享学堂
主函数
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
public class MyMain {
public static void main(String[] args) throws Exception {
byte[] mainDexData; //存储源apk中的源dex文件
byte[] aarData; // 存储壳中的壳dex文件
byte[] mergeDex; // 存储壳dex 和源dex 的合并的新dex文件
File tempFileApk = new File("source/apk/temp");
if (tempFileApk.exists()) {
File[]files = tempFileApk.listFiles();
for(File file: files){
if (file.isFile()) {
file.delete();
}
}
}
File tempFileAar = new File("source/aar/temp");
if (tempFileAar.exists()) {
File[]files = tempFileAar.listFiles();
for(File file: files){
if (file.isFile()) {
file.delete();
}
}
}
/**
* 第一步 处理原始apk 加密dex
*
*/
AES.init(AES.DEFAULT_PWD);
//解压apk
File apkFile = new File("source/apk/app-debug.apk");
File newApkFile = new File(apkFile.getParent() + File.separator + "temp");
if(!newApkFile.exists()) {
newApkFile.mkdirs();
}
File mainDexFile = AES.encryptAPKFile(apkFile,newApkFile);
if (newApkFile.isDirectory()) {
File[] listFiles = newApkFile.listFiles();
for (File file : listFiles) {
if (file.isFile()) {
if (file.getName().endsWith(".dex")) {
String name = file.getName();
System.out.println("rename step1:"+name);
int cursor = name.indexOf(".dex");
String newName = file.getParent()+ File.separator + name.substring(0, cursor) + "_" + ".dex";
System.out.println("rename step2:"+newName);
file.renameTo(new File(newName));
}
}
}
}
/**
* 第二步 处理aar 获得壳dex
*/
File aarFile = new File("source/aar/mylibrary-debug.aar");
File aarDex = Dx.jar2Dex(aarFile);
// aarData = Utils.getBytes(aarDex); //将dex文件读到byte 数组
File tempMainDex = new File(newApkFile.getPath() + File.separator + "classes.dex");
if (!tempMainDex.exists()) {
tempMainDex.createNewFile();
}
// System.out.println("MyMain" + tempMainDex.getAbsolutePath());
FileOutputStream fos = new FileOutputStream(tempMainDex);
byte[] fbytes = Utils.getBytes(aarDex);
fos.write(fbytes);
fos.flush();
fos.close();
/**
* 第3步 打包签名
*/
File unsignedApk = new File("result/apk-unsigned.apk");
unsignedApk.getParentFile().mkdirs();
// File disFile = new File(apkFile.getAbsolutePath() + File.separator+ "temp");
Zip.zip(newApkFile, unsignedApk);
//不用插件就不能自动使用原apk的签名...
File signedApk = new File("result/apk-signed.apk");
Signature.signature(unsignedApk, signedApk);
}
private static File getMainDexFile(File apkFile) {
// TODO Auto-generated method stub
File disFile = new File(apkFile.getAbsolutePath() + "unzip");
Zip.unZip(apkFile, disFile);
File[] files = disFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (name.endsWith(".dex")) {
return true;
}
return false;
}
});
for (File file: files) {
if (file.getName().endsWith("classes.dex")) {
return file;
}
}
return null;
}
}
AES.java
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* Created by xiang on 16/6/8.
*/
public class AES {
public static final String DEFAULT_PWD = "abcdefghijklmnop";
private static final String algorithmStr = "AES/ECB/PKCS5Padding";
private static Cipher encryptCipher;
private static Cipher decryptCipher;
public static void init(String password) {
try {
// 生成一个实现指定转换的 Cipher 对象。
encryptCipher = Cipher.getInstance(algorithmStr);
decryptCipher = Cipher.getInstance(algorithmStr);// algorithmStr
byte[] keyStr = password.getBytes();
SecretKeySpec key = new SecretKeySpec(keyStr, "AES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
decryptCipher.init(Cipher.DECRYPT_MODE, key);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
/**
*
* @param srcAPKfile 源文件所在位置
* @param dstApkFile 目标文件
* @return 加密后的新dex 文件
* @throws Exception
*/
public static File encryptAPKFile(File srcAPKfile, File dstApkFile) throws Exception {
if (srcAPKfile == null) {
System.out.println("encryptAPKFile :srcAPKfile null");
return null;
}
// File disFile = new File(srcAPKfile.getAbsolutePath() + "unzip");
// Zip.unZip(srcAPKfile, disFile);
Zip.unZip(srcAPKfile, dstApkFile);
//获得所有的dex (需要处理分包情况)
File[] dexFiles = dstApkFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file, String s) {
return s.endsWith(".dex");
}
});
File mainDexFile = null;
byte[] mainDexData = null;
for (File dexFile: dexFiles) {
//读数据
byte[] buffer = Utils.getBytes(dexFile);
//加密
byte[] encryptBytes = AES.encrypt(buffer);
if (dexFile.getName().endsWith("classes.dex")) {
mainDexData = encryptBytes;
mainDexFile = dexFile;
}
//写数据 替换原来的数据
FileOutputStream fos = new FileOutputStream(dexFile);
fos.write(encryptBytes);
fos.flush();
fos.close();
}
return mainDexFile;
}
public static byte[] encrypt(byte[] content) {
try {
byte[] result = encryptCipher.doFinal(content);
return result;
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] content) {
try {
byte[] result = decryptCipher.doFinal(content);
return result;
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception {
// byte[] newfs = Utils.int2Bytes(2312312);
// byte[] refs = new byte[4];
// //楂樹綅鍦ㄥ墠锛屼綆浣嶅湪鍓嶆帀涓釜
// for (int i = 0; i < 4; i++) {
// refs[i] = newfs[newfs.length - 1 - i];
// }
// System.out.println(Arrays.toString(newfs));
// System.out.println(Arrays.toString(refs));
//
// ByteBuf byteBuf = Unpooled.buffer();
//
// byteBuf.writeInt(2312312);
// byte[] a = new byte[4];
// byteBuf.order(ByteOrder.LITTLE_ENDIAN);
// byteBuf.readBytes(a);
// System.out.println(Arrays.toString(a));
// AES.init(AES.DEFAULT_PWD);
// String msg = Base64.encode(AES.encrypt(new byte[]{1, 2, 3, 4, 5}));
// System.out.println(msg);
// byte[] aes = AES.decrypt(Base64.decode(msg));
// System.out.println(Arrays.toString(aes));
File zip = new File("/Users/xiang/develop/source.apk");
String absolutePath = zip.getAbsolutePath();
File dir = new File(absolutePath.substring(0, absolutePath.lastIndexOf(".")));
Zip.unZip(zip,dir);
File zip2 = new File("/Users/xiang/develop/app-debug2.apk");
Zip.zip(dir,zip2);
String[] argv = {
"jarsigner","-verbose", "-sigalg", "MD5withRSA",
"-digestalg", "SHA1",
"-keystore", "/Users/xiang/develop/debug.keystore",
"-storepass","android",
"-keypass", "android",
"-signedjar", "/Users/xiang/develop/app-debug2-sign.apk",
"/Users/xiang/develop/app-debug2.apk",
"androiddebugkey"
};
Process pro = null;
try {
pro = Runtime.getRuntime().exec(argv);
//destroy the stream
try {
pro.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
if (pro != null) {
pro.destroy();
}
}
}
}
Dx.java
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by LK on 2017/9/5.
*
*/
public class Dx {
public static File jar2Dex(File aarFile) throws IOException, InterruptedException {
File fakeDex = new File(aarFile.getParent() + File.separator + "temp");
System.out.println("jar2Dex: aarFile.getParent(): " + aarFile.getParent());
//解压aar到 fakeDex 目录下
Zip.unZip(aarFile, fakeDex);
//过滤找到对应的fakeDex 下的classes.jar
File[] files = fakeDex.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file, String s) {
return s.equals("classes.jar");
}
});
if (files == null || files.length <= 0) {
throw new RuntimeException("the aar is invalidate");
}
File classes_jar = files[0];
// 将classes.jar 变成classes.dex
File aarDex = new File(classes_jar.getParentFile(), "classes.dex");
//我们要将jar 转变成为dex 需要使用android tools 里面的dx.bat
//使用java 调用windows 下的命令
Dx.dxCommand(aarDex, classes_jar);
return aarDex;
}
public static void dxCommand(File aarDex, File classes_jar) throws IOException, InterruptedException {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("cmd.exe /C dx --dex --output=" + aarDex.getAbsolutePath() + " " +
classes_jar.getAbsolutePath());
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
throw e;
}
if (process.exitValue() != 0) {
InputStream inputStream = process.getErrorStream();
int len;
byte[] buffer = new byte[2048];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((len=inputStream.read(buffer)) != -1){
bos.write(buffer,0,len);
}
System.out.println(new String(bos.toByteArray(),"GBK"));
throw new RuntimeException("dx run failed");
}
process.destroy();
}
}
Signature.java
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by LK on 2017/9/4.
*
*/
public class Signature {
public static void signature(File unsignedApk, File signedApk) throws InterruptedException, IOException {
String cmd[] = {"cmd.exe", "/C ","jarsigner", "-sigalg", "MD5withRSA",
"-digestalg", "SHA1",
"-keystore", "C:/Users/allen/.android/debug.keystore",
"-storepass", "android",
"-keypass", "android",
"-signedjar", signedApk.getAbsolutePath(),
unsignedApk.getAbsolutePath(),
"androiddebugkey"};
Process process = Runtime.getRuntime().exec(cmd);
System.out.println("start sign");
// BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
// String line;
// while ((line = reader.readLine()) != null)
// System.out.println("tasklist: " + line);
try {
int waitResult = process.waitFor();
System.out.println("waitResult: " + waitResult);
} catch (InterruptedException e) {
e.printStackTrace();
throw e;
}
System.out.println("process.exitValue() " + process.exitValue() );
if (process.exitValue() != 0) {
InputStream inputStream = process.getErrorStream();
int len;
byte[] buffer = new byte[2048];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((len=inputStream.read(buffer)) != -1){
bos.write(buffer,0,len);
}
System.out.println(new String(bos.toByteArray(),"GBK"));
throw new RuntimeException("签名执行失败");
}
System.out.println("finish signed");
process.destroy();
}
}
Utils.java
import java.io.File;
import java.io.RandomAccessFile;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.Adler32;
/**
* Created by xiang on 2017/5/17.
*/
public class Utils {
// public static byte[] int2Bytes(int number) {
// byte[] b = new byte[4];
// for (int i = 3; i >= 0; i--) {
// b[i] = (byte) (number % 256);
// number >>= 8;
// }
// return b;
// }
public static byte[] int2Bytes(int value) {
byte[] src = new byte[4];
src[3] = (byte) ((value >> 24) & 0xFF);
src[2] = (byte) ((value >> 16) & 0xFF);
src[1] = (byte) ((value >> 8) & 0xFF);
src[0] = (byte) (value & 0xFF);
return src;
}
public static int bytes2Int(byte[] src) {
int value;
value = (int) ((src[0] & 0xFF)
| ((src[1] & 0xFF)<<8)
| ((src[2] & 0xFF)<<16)
| ((src[3] & 0xFF)<<24));
return value;
}
public static void changeSignature(byte[] newDex) throws NoSuchAlgorithmException {
System.out.println("更换dex文件 签名信息...");
MessageDigest md = MessageDigest.getInstance("SHA-1");
//从32个字节开始 计算sha1值
md.update(newDex, 32, newDex.length - 32);
byte[] sha1 = md.digest();
//从第12位开始拷贝20字节内容
//替换signature
System.arraycopy(sha1, 0, newDex, 12, 20);
System.out.println("更换dex文件 checksum...");
}
public static void changeCheckSum(byte[] newDex) {
Adler32 adler = new Adler32();
adler.update(newDex, 12, newDex.length - 12);
int value = (int) adler.getValue();
byte[] checkSum = Utils.int2Bytes(value);
System.arraycopy(checkSum, 0, newDex, 8, 4);
}
public static byte[] getBytes(File dexFile) throws Exception {
RandomAccessFile fis = new RandomAccessFile(dexFile, "r");
byte[] buffer = new byte[(int)fis.length()];
fis.readFully(buffer);
fis.close();
return buffer;
}
public static void changeFileSize(byte[] mainDexData, byte[] newDex, byte[] aarData ) {
byte[] bytes = Utils.int2Bytes(mainDexData.length);
System.out.println("拷贝原来dex长度到新的dex:" + Utils.bytes2Int(bytes));
//更该文件头长度信息
//修改
System.out.println("更换dex 文件头长度信息...");
byte[] file_size = Utils.int2Bytes(newDex.length);
System.arraycopy(file_size, 0, newDex, 32, 4);
}
// public static void main(String[] args) throws Exception {
// ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
// byteBuf.writeInt(241241143);
// byte[] a = new byte[4];
// byteBuf.markReaderIndex();
// byteBuf.readBytes(a);
// byteBuf.resetReaderIndex();
//
// System.out.println(Arrays.toString(a));
// System.out.println(Arrays.toString(int2Bytes(241241143)));
// System.out.println(Arrays.toString(intToBytes(241241143)));
// }
}
Zip.java
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* Created by xiang on 2017/5/17.
*/
public class Zip {
public static void unZip(File zip, File dir) {
try {
dir.delete();
ZipFile zipFile = new ZipFile(zip);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
String name = zipEntry.getName();
if (name.equals("META-INF/CERT.RSA") || name.equals("META-INF/CERT.SF") || name
.equals("META-INF/MANIFEST.MF")) {
continue;
}
if (!zipEntry.isDirectory()) {
File file = new File(dir, name);
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
InputStream is = zipFile.getInputStream(zipEntry);
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
is.close();
fos.close();
}
}
zipFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void zip(File dir, File zip) throws Exception {
zip.delete();
// 对输出文件做CRC32校验
CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
zip), new CRC32());
ZipOutputStream zos = new ZipOutputStream(cos);
compress(dir, zos, "");
zos.flush();
zos.close();
}
private static void compress(File srcFile, ZipOutputStream zos,
String basePath) throws Exception {
if (srcFile.isDirectory()) {
compressDir(srcFile, zos, basePath);
} else {
compressFile(srcFile, zos, basePath);
}
}
private static void compressDir(File dir, ZipOutputStream zos,
String basePath) throws Exception {
File[] files = dir.listFiles();
// 构建空目录
if (files.length < 1) {
ZipEntry entry = new ZipEntry(basePath + dir.getName() + "/");
zos.putNextEntry(entry);
zos.closeEntry();
}
for (File file : files) {
// 递归压缩
compress(file, zos, basePath + dir.getName() + "/");
}
}
private static void compressFile(File file, ZipOutputStream zos, String dir)
throws Exception {
String dirName = dir + file.getName();
String[] dirNameNew = dirName.split("/");
StringBuffer buffer = new StringBuffer();
if (dirNameNew.length > 1) {
for (int i = 1; i < dirNameNew.length; i++) {
buffer.append("/");
buffer.append(dirNameNew[i]);
}
} else {
buffer.append("/");
}
ZipEntry entry = new ZipEntry(buffer.toString().substring(1));
zos.putNextEntry(entry);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
file));
int count;
byte data[] = new byte[1024];
while ((count = bis.read(data, 0, 1024)) != -1) {
zos.write(data, 0, count);
}
bis.close();
zos.closeEntry();
}
}
==Android
app
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.protectapp">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name="com.example.mylibrary.ShellApplication"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java
package com.example.protectapp;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
lib
ShellApplication.java
package com.example.mylibrary;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* @author 享学课堂 Alvin
* @package com.xiangxue.alvin.applibrary
* @fileName ShellApplication
* @date on 2019/4/21
* @qq 2464061231
**/
public class ShellApplication extends Application {
private static final String TAG = "ShellApplication";
public static String getPassword(){
return "abcdefghijklmnop";
}
// static {
// System.loadLibrary("native-lib");
// }
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
AES.init(getPassword());
File apkFile = new File(getApplicationInfo().sourceDir);
//data/data/包名/files/fake_apk/
File unZipFile = getDir("fake_apk", MODE_PRIVATE);
File app = new File(unZipFile, "app");
if (!app.exists()) {
Zip.unZip(apkFile, app);
File[] files = app.listFiles();
for (File file : files) {
String name = file.getName();
if (name.equals("classes.dex")) {
} else if (name.endsWith(".dex")) {
try {
byte[] bytes = getBytes(file);
FileOutputStream fos = new FileOutputStream(file);
byte[] decrypt = AES.decrypt(bytes);
// fos.write(bytes);
fos.write(decrypt);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
List list = new ArrayList<>();
Log.d("FAKE", Arrays.toString(app.listFiles()));
for (File file : app.listFiles()) {
if (file.getName().endsWith(".dex")) {
list.add(file);
}
}
Log.d("FAKE", list.toString());
try {
V19.install(getClassLoader(), list, unZipFile);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
private static Field findField(Object instance, String name) throws NoSuchFieldException {
Class clazz = instance.getClass();
while (clazz != null) {
try {
Field e = clazz.getDeclaredField(name);
if (!e.isAccessible()) {
e.setAccessible(true);
}
return e;
} catch (NoSuchFieldException var4) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
}
private static Method findMethod(Object instance, String name, Class... parameterTypes)
throws NoSuchMethodException {
Class clazz = instance.getClass();
// Method[] declaredMethods = clazz.getDeclaredMethods();
// System.out.println(" findMethod ");
// for (Method m : declaredMethods) {
// System.out.print(m.getName() + " : ");
// Class<?>[] parameterTypes1 = m.getParameterTypes();
// for (Class clazz1 : parameterTypes1) {
// System.out.print(clazz1.getName() + " ");
// }
// System.out.println("");
// }
while (clazz != null) {
try {
Method e = clazz.getDeclaredMethod(name, parameterTypes);
if (!e.isAccessible()) {
e.setAccessible(true);
}
return e;
} catch (NoSuchMethodException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchMethodException("Method " + name + " with parameters " + Arrays.asList
(parameterTypes) + " not found in " + instance.getClass());
}
private static void expandFieldArray(Object instance, String fieldName, Object[]
extraElements) throws NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
Field jlrField = findField(instance, fieldName);
Object[] original = (Object[]) ((Object[]) jlrField.get(instance));
Object[] combined = (Object[]) ((Object[]) Array.newInstance(original.getClass()
.getComponentType(), original.length + extraElements.length));
System.arraycopy(original, 0, combined, 0, original.length);
System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
jlrField.set(instance, combined);
}
private static final class V19 {
private V19() {
}
private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
File optimizedDirectory) throws IllegalArgumentException,
IllegalAccessException, NoSuchFieldException, InvocationTargetException,
NoSuchMethodException {
Field pathListField = findField(loader, "pathList");
Object dexPathList = pathListField.get(loader);
ArrayList suppressedExceptions = new ArrayList();
Log.d(TAG, "Build.VERSION.SDK_INT " + Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT >= 23) {
expandFieldArray(dexPathList, "dexElements", makePathElements(dexPathList, new
ArrayList(additionalClassPathEntries), optimizedDirectory,
suppressedExceptions));
} else {
expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, new
ArrayList(additionalClassPathEntries), optimizedDirectory,
suppressedExceptions));
}
if (suppressedExceptions.size() > 0) {
Iterator suppressedExceptionsField = suppressedExceptions.iterator();
while (suppressedExceptionsField.hasNext()) {
IOException dexElementsSuppressedExceptions = (IOException)
suppressedExceptionsField.next();
Log.w("MultiDex", "Exception in makeDexElement",
dexElementsSuppressedExceptions);
}
Field suppressedExceptionsField1 = findField(loader,
"dexElementsSuppressedExceptions");
IOException[] dexElementsSuppressedExceptions1 = (IOException[]) ((IOException[])
suppressedExceptionsField1.get(loader));
if (dexElementsSuppressedExceptions1 == null) {
dexElementsSuppressedExceptions1 = (IOException[]) suppressedExceptions
.toArray(new IOException[suppressedExceptions.size()]);
} else {
IOException[] combined = new IOException[suppressedExceptions.size() +
dexElementsSuppressedExceptions1.length];
suppressedExceptions.toArray(combined);
System.arraycopy(dexElementsSuppressedExceptions1, 0, combined,
suppressedExceptions.size(), dexElementsSuppressedExceptions1.length);
dexElementsSuppressedExceptions1 = combined;
}
suppressedExceptionsField1.set(loader, dexElementsSuppressedExceptions1);
}
}
private static Object[] makeDexElements(Object dexPathList,
ArrayList<File> files, File
optimizedDirectory,
ArrayList<IOException> suppressedExceptions) throws
IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Method makeDexElements = findMethod(dexPathList, "makeDexElements", new
Class[]{ArrayList.class, File.class, ArrayList.class});
return ((Object[]) makeDexElements.invoke(dexPathList, new Object[]{files,
optimizedDirectory, suppressedExceptions}));
}
}
/**
* A wrapper around
* {@code private static final dalvik.system.DexPathList#makePathElements}.
*/
private static Object[] makePathElements(
Object dexPathList, ArrayList<File> files, File optimizedDirectory,
ArrayList<IOException> suppressedExceptions)
throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Method makePathElements;
try {
makePathElements = findMethod(dexPathList, "makePathElements", List.class, File.class,
List.class);
} catch (NoSuchMethodException e) {
Log.e(TAG, "NoSuchMethodException: makePathElements(List,File,List) failure");
try {
makePathElements = findMethod(dexPathList, "makePathElements", ArrayList.class, File.class, ArrayList.class);
} catch (NoSuchMethodException e1) {
Log.e(TAG, "NoSuchMethodException: makeDexElements(ArrayList,File,ArrayList) failure");
try {
Log.e(TAG, "NoSuchMethodException: try use v19 instead");
return V19.makeDexElements(dexPathList, files, optimizedDirectory, suppressedExceptions);
} catch (NoSuchMethodException e2) {
Log.e(TAG, "NoSuchMethodException: makeDexElements(List,File,List) failure");
throw e2;
}
}
}
return (Object[]) makePathElements.invoke(dexPathList, files, optimizedDirectory, suppressedExceptions);
}
private byte[] getBytes(File file) throws Exception {
RandomAccessFile r = new RandomAccessFile(file, "r");
byte[] buffer = new byte[(int) r.length()];
r.readFully(buffer);
r.close();
return buffer;
}
}
AES.java
package com.example.mylibrary;
import java.io.File;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
/**
* Created by xiang on 16/6/8.
*/
public class AES {
public static final String DEFAULT_PWD = "abcdefghijklmnop";
private static final String algorithmStr = "AES/ECB/PKCS5Padding";
private static Cipher encryptCipher;
private static Cipher decryptCipher;
public static void init(String password) {
try {
// 生成一个实现指定转换的 Cipher 对象。
encryptCipher = Cipher.getInstance(algorithmStr);
decryptCipher = Cipher.getInstance(algorithmStr);// algorithmStr
byte[] keyStr = password.getBytes();
SecretKeySpec key = new SecretKeySpec(keyStr, "AES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
decryptCipher.init(Cipher.DECRYPT_MODE, key);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
public static byte[] encrypt(byte[] content) {
try {
byte[] result = encryptCipher.doFinal(content);
return result;
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static byte[] decrypt(byte[] content) {
try {
byte[] result = decryptCipher.doFinal(content);
return result;
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception {
File zip = new File("/Users/xiang/develop/app-debug.apk");
String absolutePath = zip.getAbsolutePath();
File dir = new File(absolutePath.substring(0, absolutePath.lastIndexOf(".")));
Zip.unZip(zip,dir);
File zip2 = new File("/Users/xiang/develop/app-debug2.apk");
Zip.zip(dir,zip2);
String[] argv = {
"jarsigner","-verbose", "-sigalg", "MD5withRSA",
"-digestalg", "SHA1",
"-keystore", "/Users/xiang/develop/debug.keystore",
"-storepass","android",
"-keypass", "android",
"-signedjar", "/Users/xiang/develop/app-debug2-sign.apk",
"/Users/xiang/develop/app-debug2.apk",
"androiddebugkey"
};
Process pro = null;
try {
pro = Runtime.getRuntime().exec(argv);
//destroy the stream
try {
pro.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
if (pro != null) {
pro.destroy();
}
}
}
}
Base64.java
package com.example.mylibrary;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Created by xiang on 16/5/11.
*/
public final class Base64 {
private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.toCharArray();
public static String encode(byte[] data) {
int start = 0;
int len = data.length;
StringBuffer buf = new StringBuffer(data.length * 3 / 2);
int end = len - 3;
int i = start;
int n = 0;
while (i <= end) {
int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 0x0ff) << 8)
| (((int) data[i + 2]) & 0x0ff);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append(legalChars[d & 63]);
i += 3;
if (n++ >= 14) {
n = 0;
buf.append(" ");
}
}
if (i == start + len - 2) {
int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 255) << 8);
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append(legalChars[(d >> 6) & 63]);
buf.append("=");
} else if (i == start + len - 1) {
int d = (((int) data[i]) & 0x0ff) << 16;
buf.append(legalChars[(d >> 18) & 63]);
buf.append(legalChars[(d >> 12) & 63]);
buf.append("==");
}
return buf.toString();
}
private static int decode(char c) {
if (c >= 'A' && c <= 'Z')
return ((int) c) - 65;
else if (c >= 'a' && c <= 'z')
return ((int) c) - 97 + 26;
else if (c >= '0' && c <= '9')
return ((int) c) - 48 + 26 + 26;
else
switch (c) {
case '+':
return 62;
case '/':
return 63;
case '=':
return 0;
default:
throw new RuntimeException("unexpected code: " + c);
}
}
/**
* Decodes the given Base64 encoded String to a new byte array. The byte
* array holding the decoded data is returned.
*/
public static byte[] decode(String s) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
decode(s, bos);
} catch (IOException e) {
throw new RuntimeException();
}
byte[] decodedBytes = bos.toByteArray();
try {
bos.close();
bos = null;
} catch (IOException ex) {
System.err.println("Error while decoding BASE64: " + ex.toString());
}
return decodedBytes;
}
private static void decode(String s, OutputStream os) throws IOException {
int i = 0;
int len = s.length();
while (true) {
while (i < len && s.charAt(i) <= ' ')
i++;
if (i == len)
break;
int tri = (decode(s.charAt(i)) << 18) + (decode(s.charAt(i + 1)) << 12) + (decode(s.charAt(i + 2)) << 6)
+ (decode(s.charAt(i + 3)));
os.write((tri >> 16) & 255);
if (s.charAt(i + 2) == '=')
break;
os.write((tri >> 8) & 255);
if (s.charAt(i + 3) == '=')
break;
os.write(tri & 255);
i += 4;
}
}
}
Utils.java
package com.example.mylibrary;
/**
* Created by xiang on 2017/5/17.
*/
public class Utils {
// public static byte[] int2Bytes(int number) {
// byte[] b = new byte[4];
// for (int i = 3; i >= 0; i--) {
// b[i] = (byte) (number % 256);
// number >>= 8;
// }
// return b;
// }
public static byte[] int2Bytes(int value) {
byte[] src = new byte[4];
src[3] = (byte) ((value >> 24) & 0xFF);
src[2] = (byte) ((value >> 16) & 0xFF);
src[1] = (byte) ((value >> 8) & 0xFF);
src[0] = (byte) (value & 0xFF);
return src;
}
public static int bytes2Int(byte[] src) {
int value;
value = (int) ((src[0] & 0xFF)
| ((src[1] & 0xFF)<<8)
| ((src[2] & 0xFF)<<16)
| ((src[3] & 0xFF)<<24));
return value;
}
public static void main(String[] args) throws Exception {
// ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
// byteBuf.writeInt(241241143);
// byte[] a = new byte[4];
// byteBuf.markReaderIndex();
// byteBuf.readBytes(a);
// byteBuf.resetReaderIndex();
//
// System.out.println(Arrays.toString(a));
// System.out.println(Arrays.toString(int2Bytes(241241143)));
// System.out.println(Arrays.toString(intToBytes(241241143)));
}
}
Zip.java
package com.example.mylibrary;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* Created by xiang on 2017/5/17.
*/
public class Zip {
public static void unZip(File zip, File dir) {
try {
dir.delete();
ZipFile zipFile = new ZipFile(zip);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
String name = zipEntry.getName();
if (name.equals("META-INF/CERT.RSA") || name.equals("META-INF/CERT.SF") || name
.equals("META-INF/MANIFEST.MF")) {
continue;
}
if (!zipEntry.isDirectory()) {
File file = new File(dir, name);
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
InputStream is = zipFile.getInputStream(zipEntry);
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
is.close();
fos.close();
}
}
zipFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void zip(File dir, File zip) throws Exception {
zip.delete();
// 对输出文件做CRC32校验
CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
zip), new CRC32());
ZipOutputStream zos = new ZipOutputStream(cos);
compress(dir, zos, "");
zos.flush();
zos.close();
}
private static void compress(File srcFile, ZipOutputStream zos,
String basePath) throws Exception {
if (srcFile.isDirectory()) {
compressDir(srcFile, zos, basePath);
} else {
compressFile(srcFile, zos, basePath);
}
}
private static void compressDir(File dir, ZipOutputStream zos,
String basePath) throws Exception {
File[] files = dir.listFiles();
// 构建空目录
if (files.length < 1) {
ZipEntry entry = new ZipEntry(basePath + dir.getName() + "/");
zos.putNextEntry(entry);
zos.closeEntry();
}
for (File file : files) {
// 递归压缩
compress(file, zos, basePath + dir.getName() + "/");
}
}
private static void compressFile(File file, ZipOutputStream zos, String dir)
throws Exception {
String dirName = dir + file.getName();
String[] dirNameNew = dirName.split("/");
StringBuffer buffer = new StringBuffer();
if (dirNameNew.length > 1) {
for (int i = 1; i < dirNameNew.length; i++) {
buffer.append("/");
buffer.append(dirNameNew[i]);
}
} else {
buffer.append("/");
}
ZipEntry entry = new ZipEntry(buffer.toString().substring(1));
zos.putNextEntry(entry);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
file));
int count;
byte data[] = new byte[1024];
while ((count = bis.read(data, 0, 1024)) != -1) {
zos.write(data, 0, count);
}
bis.close();
zos.closeEntry();
}
}