package com.wj.dexknife.shell;
import com.googlecode.dex2jar.tools.Dex2jarCmd;
import com.googlecode.dex2jar.tools.Jar2Dex;
import com.googlecode.dex2jar.tools.StdApkCmd;
import com.wj.dexknife.shell.jiagu.KeystoreConfig;
import com.wj.dexknife.shell.utils.Cmd;
import com.wj.dexknife.shell.utils.Debug;
import com.wj.dexknife.shell.utils.FileHelper;
import com.wj.dexknife.shell.utils.ZipHelper;
import org.jf.baksmali.baksmali;
import org.jf.baksmali.baksmaliOptions;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import brut.androlib.AndrolibException;
import brut.androlib.res.util.ExtFile;
import brut.androlib.src.SmaliBuilder;
import brut.apktool.Main;
import brut.common.BrutException;
/**
* Created by linchaolong on 2015/8/30.
*/
public class ApkToolPlus {
public static final String TAG = ApkToolPlus.class.getSimpleName();
public static ClassLoader initClassPath(String[] classpaths){
if (classpaths == null || classpaths.length == 0)
return null;
// Add the conf dir to the classpath
// Chain the current thread classloader
try {
List<URL> urls = new ArrayList<>(classpaths.length);
for(String path : classpaths) {
urls.add(new File(path).toURI().toURL());
}
ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
URLClassLoader urlClassLoader = new URLClassLoader((URL[]) urls.toArray(), currentThreadClassLoader);
// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(urlClassLoader);
return urlClassLoader;
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
public static boolean decompile(File apk, File outDir, Callback<Exception> onExceptioin){
try {
if(!outDir.exists()){
outDir.mkdirs();
}
if(outDir == null){
runApkTool(new String[]{"d", apk.getPath()});
}else{
runApkTool(new String[]{"d", apk.getPath(), "-o", outDir.getPath(), "-f"});
}
} catch (Exception e) {
e.printStackTrace();
if(onExceptioin != null){
onExceptioin.callback(e);
}
return false;
}
return true;
}
public static boolean recompile(File folder, File outApk, Callback<Exception> onExceptioin){
try {
if(outApk == null){
runApkTool(new String[]{"b", folder.getPath()});
}else{
runApkTool(new String[]{"b", folder.getPath(), "-o", outApk.getPath()});
}
} catch (Exception e) {
e.printStackTrace();
if(onExceptioin != null){
onExceptioin.callback(e);
}
return false;
}
return true;
}
public static boolean jar2dex(File jarFile, String outputDexPath){
return class2dex(jarFile,outputDexPath);
}
public static boolean class2dex(File classesDir, String outputDexPath){
if (!classesDir.exists()){
Debug.w("class2dex error : classPath is not exists.");
return false;
}
if (!FileHelper.makePath(outputDexPath)){
Debug.w( "makePath error : outputDexPath '" + outputDexPath + "' make fail");
return false;
}
// class -> dex
com.android.dx.command.dexer.Main.Arguments arguments = new com.android.dx.command.dexer.Main.Arguments();
arguments.outName = outputDexPath;
arguments.strictNameCheck = false;
arguments.fileNames = new String[]{classesDir.getPath()};
try {
new com.android.dx.command.dexer.Main(null).run(arguments);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
public static boolean dex2smali(File dexFile, File outDir){
DexBackedDexFile dexBackedDexFile = null;
if (dexFile == null || !dexFile.exists()){
Debug.w( "dex2smali dexFile is null or not exists : " + dexFile.getPath());
return false;
}
try {
//brut/androlib/ApkDecoder.mApi default value is 15
dexBackedDexFile = DexFileFactory.loadDexFile(dexFile, 15, false);
} catch (IOException e) {
e.printStackTrace();
return false;
}
baksmaliOptions options = new baksmaliOptions();
options.outputDirectory = outDir.getPath();
// default value -1 will lead to an exception
// this setup is copied from Baksmali project
options.jobs = Runtime.getRuntime().availableProcessors();
if (options.jobs > 6) {
options.jobs = 6;
}
return baksmali.disassembleDexFile(dexBackedDexFile, options);
}
public static boolean jar2smali(File jarFile, File outDir){
if (!jarFile.exists() || jarFile.isDirectory()) {
Debug.w( "jar2smali error : jar file '" + jarFile.getPath() + "' is not exists or is a directory.");
return false;
}
return class2smali(jarFile, outDir);
}
public static boolean class2smali(File classesDir, File outDir){
if (!classesDir.exists()){
Debug.w("class2smali error : classpath '" + classesDir.getPath() + "' is not exists.");
return false;
}
// clean temp
File dexFile = new File(classesDir.getParentFile(), "temp.dex");
dexFile.delete();
// class -> dex
if (class2dex(classesDir, dexFile.getPath())){
// dex -> smali
if (dex2smali(dexFile,outDir)){
Debug.d("class2smali succcess");
}else{
Debug.e("class2smali error : dex2smali error");
}
// clean temp
dexFile.delete();
return true;
}else {
Debug.e( "class2smali error : class2dex error");
return false;
}
}
public static boolean smali2dex(String smaliDirPath, String dexOutputPath){
ExtFile smaliDir = new ExtFile(new File(smaliDirPath));
if (!smaliDir.exists()){
Debug.w("smali2dex error : smali dir '" + smaliDirPath + "' is not exists");
return false;
}
if (!FileHelper.makePath(dexOutputPath)){
Debug.w("makePath error : dexOutputPath '" + dexOutputPath + "' make fail");
return false;
}
File dexFile = new File(dexOutputPath);
dexFile.delete();
try {
// smali -> dex
SmaliBuilder.build(smaliDir, dexFile);
return true;
} catch (AndrolibException e) {
e.printStackTrace();
}
return false;
}
public static boolean dex2jar(File file, File jarFile){
//d2j-dex2jar classes.dex --output output.jar
if(file == null || !file.exists() || jarFile == null){
return false;
}
Dex2jarCmd.main(file.getPath(),"--output",jarFile.getPath(),"--force"); //--force���Ǵ����ļ�
return jarFile.exists();
}
/**
* jar2dex
*
* @param jarFile
* @param dexFile
*/
public static boolean jar2dex(File jarFile, File dexFile){
if(jarFile == null || !jarFile.exists() || dexFile == null){
return false;
}
Jar2Dex.main(jarFile.getPath(),"--output",dexFile.getPath());
return dexFile.exists();
}
public static boolean apk2zip(File apkFile, File zipFile){
//d2j-std-apk hqg.apk -o hqg.zip
if(apkFile == null || !apkFile.exists() || zipFile == null){
return false;
}
StdApkCmd.main(apkFile.getPath(),"-o",zipFile.getPath());
return zipFile.exists();
}
public static File signApk(File apk, KeystoreConfig config){
if (!apk.exists() || !apk.isFile()){
throw new RuntimeException("sign apk error : file '" + apk.getPath() + "' is no exits or not a file.");
}
File apkCopy = new File(apk.getParentFile(), "copy_"+apk.getName());
FileHelper.delete(apkCopy);
FileHelper.copyFile(apk,apkCopy);
ZipHelper.removeFileFromZip(apkCopy,"META-INF");
File signedApk = new File(apk.getParentFile(), FileHelper.getNoSuffixName(apk) + ".apk");
FileHelper.delete(signedApk);
//jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore keystore·�� -storepass ���� -keypass �������� -signedjar signed_xxx.apk xxx.apk ����
StringBuilder cmdBuilder = new StringBuilder("jarsigner -digestalg SHA1 -sigalg MD5withRSA");
cmdBuilder.append(" -keystore ").append(config.keystorePath);
cmdBuilder.append(" -storepass ").append(config.keystorePassword);
cmdBuilder.append(" -keypass ").append(config.aliasPassword);
cmdBuilder.append(" -signedjar ").append(signedApk.getPath()).append(" ").append(apkCopy.getPath()).append(" ");
cmdBuilder.append(" ").append(config.alias);
String cmd = cmdBuilder.toString();
Cmd.exec(cmd);
// clean
FileHelper.delete(apkCopy);
return signedApk;
}
private static void safeRunApkTool(String[] args){
try {
Main.main(args);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrutException e) {
e.printStackTrace();
}
}
public static void installFramework(File apkToolFile, File frameworkFile){
Cmd.exec("java -jar " + apkToolFile.getAbsolutePath() + " if " + frameworkFile.getAbsolutePath());
}
private static void runApkTool(String[] args) throws InterruptedException, BrutException, IOException {
AppManager.initApkTool();
//java -jar apktool.jar d test.apk -f
StringBuilder cmdBuilder = new StringBuilder();
cmdBuilder.append("java -jar ")
.append(AppManager.getApkTool().getPath());
for(String arg : args){
cmdBuilder.append(" ").append(arg);
}
Cmd.exec(cmdBuilder.toString());
}
}