Skip to content

Commit

Permalink
Remove apktoolkit dependency
Browse files Browse the repository at this point in the history
 - Remove apktoolkit dependency;
 - Use baksmali lib to decode the apk;
 - Add and config Ivy Apache to download the depedencies;
 - Implement Smali decoder to decode an APK in smali code;

 #32
  • Loading branch information
victorrattis committed Jan 4, 2019
1 parent 13d67f4 commit 03d434c
Show file tree
Hide file tree
Showing 16 changed files with 332 additions and 92 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ META-INF
build
output
test.*
apk-dependency-graph-scripts-*.zip
apk-dependency-graph-scripts-*.zip

# lib/ directory where the dependencies are downloaded by Ivy Apache.
lib
52 changes: 49 additions & 3 deletions build.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
<project name="apk-dependency-graph" basedir="." default="main">
<project xmlns:ivy="antlib:org.apache.ivy.ant"
name="apk-dependency-graph" basedir="." default="main">

<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="main-class" value="code.Main"/>
<property name="lib.dir" value="lib" />

<target name="clean">
<delete dir="${build.dir}"/>
</target>

<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}"/>
<javac
srcdir="${src.dir}"
destdir="${classes.dir}"
classpathref="external-libraries-classpath"
includeantruntime="false" />
</target>

<target name="jar" depends="compile">
Expand All @@ -21,6 +27,11 @@
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
<zipgroupfileset dir="${basedir}/">
<include name="${lib.dir}/**/*.jar" />
<exclude name="${lib.dir}/**/*sources.jar"/>
<exclude name="${lib.dir}/**/*javadoc.jar"/>
</zipgroupfileset>
</jar>
</target>

Expand All @@ -30,6 +41,41 @@

<target name="clean-build" depends="clean,jar"/>

<target name="main" depends="clean,run"/>
<target name="main" depends="clean,resolve,run"/>

<!-- target: resolve -->
<property name="ivy.install.version" value="2.2.0" />
<condition property="ivy.home" value="${env.IVY_HOME}">
<isset property="env.IVY_HOME" />
</condition>
<property name="ivy.home" value="${user.home}/.ant" />
<property name="ivy.jar.dir" value="${ivy.home}/libs" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

<target name="download-ivy">
<mkdir dir="${ivy.jar.dir}"/>
<get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
dest="${ivy.jar.file}" usetimestamp="true"/>
</target>

<target name="init-ivy" depends="download-ivy">
<path id="ivy.lib.path">
<fileset dir="${ivy.jar.dir}" includes="*.jar"/>
</path>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
</target>

<target name="resolve" description="--> retreive dependencies with ivy" depends="init-ivy">
<ivy:retrieve/>
</target>

<!-- path: external libraries classpath, we don't need sources and javadoc -->
<path id="external-libraries-classpath">
<fileset dir="${basedir}/">
<include name="${lib.dir}/*.jar" />
<exclude name="${lib.dir}/*sources.jar"/>
<exclude name="${lib.dir}/*javadoc.jar"/>
</fileset>
</path>
</project>
7 changes: 7 additions & 0 deletions ivy.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ivy-module version="2.0">
<info organisation="org.apache" module="apk-dependency-graph"/>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.smali/baksmali -->
<dependency org="org.smali" name="baksmali" rev="2.2.5"/>
</dependencies>
</ivy-module>
Binary file removed lib/apktool_2.3.4.jar
Binary file not shown.
3 changes: 1 addition & 2 deletions run.bat
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,4 @@ For %%A in ("%filename%") do (
Set outPath=%~dp0\output\%Name:~0,-4%
Set jsonPath=%~dp0\gui\analyzed.js

java -jar %~dp0\lib\apktool_2.3.4.jar d %1 -o %outPath% -f
java -jar %~dp0\build\jar\apk-dependency-graph.jar -i %outPath% -o %jsonPath% -f %2 -d %3
java -jar %~dp0\build\jar\apk-dependency-graph.jar -i %outPath% -o %jsonPath% -f %2 -d %3 -a %1
7 changes: 3 additions & 4 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ xpref=${xbase%.*}

dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
outPath=${dir}"/output/"${xpref}
jsonPath=${dir}"/gui/analyzed.js"

eval "java -jar ${dir}'/lib/apktool_2.3.4.jar' d ${fileName} -o ${outPath} -f"
eval "java -jar ${dir}'/build/jar/apk-dependency-graph.jar' -i ${outPath} -o ${jsonPath} -f $2 -d $3"
jsonPath=${dir}"/gui/analyzed.js"

eval "java -jar ${dir}'/build/jar/apk-dependency-graph.jar' -i ${outPath} -o ${jsonPath} -f $2 -d $3 -a $1"
2 changes: 2 additions & 0 deletions src/code/CodeUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package code;

import code.util.StringUtils;

public class CodeUtils {

public static boolean isClassR(String className) {
Expand Down
42 changes: 26 additions & 16 deletions src/code/Main.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
package code;


import java.io.File;

import code.decode.ApkSmaliDecoderController;
import code.io.ArgumentReader;
import code.io.Arguments;
import code.io.Writer;
import code.util.FileUtils;

import java.io.File;

public class Main {

public static void main(String[] args) {
Arguments arguments = new ArgumentReader(args).read();
if (arguments == null) {
return;
}

File resultFile = new File(arguments.getResultPath());
SmaliAnalyzer analyzer = new SmaliAnalyzer(arguments);
if (analyzer.run()) {
new Writer(resultFile).write(analyzer.getDependencies());
System.out.println("Success! Now open index.html in your browser.");
}
}
public static void main(String[] args) {
Arguments arguments = new ArgumentReader(args).read();
if (arguments == null) {
return;
}

// Delete the output directory for a better decoding result.
if (FileUtils.deleteDir(arguments.getProjectPath())) {
System.out.println("The output directory was deleted!");
}

// Decode the APK file for smali code in the output directory.
ApkSmaliDecoderController.decode(
arguments.getApkFilePath(), arguments.getProjectPath());

File resultFile = new File(arguments.getResultPath());
SmaliAnalyzer analyzer = new SmaliAnalyzer(arguments);
if (analyzer.run()) {
new Writer(resultFile).write(analyzer.getDependencies());
System.out.println("Success! Now open index.html in your browser.");
}
}
}
13 changes: 0 additions & 13 deletions src/code/StringUtils.java

This file was deleted.

109 changes: 109 additions & 0 deletions src/code/decode/ApkSmaliDecoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package code.decode;

import code.util.ZipFileUtils;

import org.jf.baksmali.Baksmali;
import org.jf.baksmali.BaksmaliOptions;
import org.jf.dexlib2.analysis.InlineMethodResolver;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
import org.jf.dexlib2.DexFileFactory;
import org.jf.dexlib2.Opcodes;

import java.io.File;
import java.io.IOException;
import java.util.List;

public final class ApkSmaliDecoder {
private static final int MAXIMUM_NUMBER_OF_PROCESSORS = 6;

private static final String WARNING_DISASSEMBLING_ODEX_FILE =
"Warning: You are disassembling an odex file without deodexing it.";

private final String apkFilePath;
private final String outDirPath;
private final int apiVersion;

ApkSmaliDecoder(String apkFilePath, String outDirPath, int api) {
this.apkFilePath = apkFilePath;
this.outDirPath = outDirPath;
this.apiVersion = api;
}

void decode() throws IOException {
File apkFile = new File(this.apkFilePath);
if (!apkFile.exists()) {
throw new IOException("Apk file not found!");
}
File outDir = new File(this.outDirPath);

// Read all dex files in the APK file and so decode each one.
for (String dexFileName : getDexFiles(this.apkFilePath)) {
decodeDexFile(apkFile, dexFileName, this.apiVersion, outDir);
}
}

private void decodeDexFile(
File apkFile, String dexFileName, int apiVersion, File outDir)
throws IOException {
try {
DexBackedDexFile dexFile =
loadDexFile(apkFile, dexFileName, apiVersion);

Baksmali.disassembleDexFile(
dexFile,
outDir,
getNumerOfAvailableProcessors(),
getSmaliOptions(dexFile));
} catch (Exception ex) {
throw new IOException(ex);
}
}

private int getNumerOfAvailableProcessors() {
int jobs = Runtime.getRuntime().availableProcessors();
return jobs > MAXIMUM_NUMBER_OF_PROCESSORS ?
MAXIMUM_NUMBER_OF_PROCESSORS : jobs;
}

private BaksmaliOptions getSmaliOptions(final DexBackedDexFile dexFile) {
final BaksmaliOptions options = new BaksmaliOptions();

options.deodex = false;
options.implicitReferences = false;
options.parameterRegisters = true;
options.localsDirective = true;
options.sequentialLabels = true;
options.debugInfo = false;
options.codeOffsets = false;
options.accessorComments = false;
options.registerInfo = 0;

if (dexFile instanceof DexBackedOdexFile) {
options.inlineResolver =
InlineMethodResolver.createInlineMethodResolver(
((DexBackedOdexFile)dexFile).getOdexVersion());
} else {
options.inlineResolver = null;
}

return options;
}

private DexBackedDexFile loadDexFile(
File apkFile, String dexFilePath, int apiVersion)
throws IOException {
DexBackedDexFile dexFile = DexFileFactory.loadDexEntry(
apkFile, dexFilePath, true, Opcodes.forApi(apiVersion));

if (dexFile == null || dexFile.isOdexFile()) {
throw new IOException(WARNING_DISASSEMBLING_ODEX_FILE);
}

return dexFile;
}

private List<String> getDexFiles(String apkFilePath) throws IOException {
return ZipFileUtils.filterByContainsName(apkFilePath, "classes");
}
}
17 changes: 17 additions & 0 deletions src/code/decode/ApkSmaliDecoderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package code.decode;

import java.io.IOException;

public class ApkSmaliDecoderController {
private static final int DEFAULT_ANDROID_VERSION = 28;

public static void decode(
final String apkFilePath, final String outDirPath) {
try {
new ApkSmaliDecoder(
apkFilePath, outDirPath, DEFAULT_ANDROID_VERSION).decode();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
34 changes: 17 additions & 17 deletions src/code/io/ArgumentReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ public class ArgumentReader {
"-i path : path to the decompiled project\n" +
"-o path : path to the result js file\n" +
"-f filter : java package to filter by (to show all dependencies pass '-f nofilter')\n" +
"-d boolean : if true it will contain inner class processing (the ones creating ClassName$InnerClass files)";
"-d boolean : if true it will contain inner class processing (the ones creating ClassName$InnerClass files)\n" +
"-a path : path to the apk file";

private String[] args;

Expand All @@ -17,11 +18,8 @@ public ArgumentReader(String[] args) {
}

public Arguments read() {
if (args.length != 5 && args.length != 8) {
System.err.println(USAGE_STRING);
return null;
}
String projectPath = null, resultPath = null, filter = null;
String apkPath = null;
boolean withInnerClasses = false;
for (int i = 0; i < args.length; i++) {
if (i < args.length - 1) {
Expand All @@ -33,10 +31,13 @@ public Arguments read() {
filter = args[i + 1];
} else if (args[i].equals("-d")) {
withInnerClasses = Boolean.valueOf(args[i + 1]);
}
} else if (args[i].equals("-a")) {
apkPath = args[i + 1];
}
}
}
if (projectPath == null || resultPath == null || filter == null) {
if (projectPath == null || resultPath == null || filter == null ||
apkPath == null) {
System.err.println("Arguments are incorrect!");
System.err.println(USAGE_STRING);
return null;
Expand All @@ -45,18 +46,17 @@ public Arguments read() {
filter = null;
System.out.println("Warning! Processing without filter.");
}
File projectFile = new File(projectPath);
if (!projectFile.exists()) {
System.err.println(projectPath + " doesn't exist!");
return null;
}
if (!projectFile.isDirectory()) {
System.err.println(projectPath + " must be a directory!");
return null;
}
if (!withInnerClasses) {
System.out.println("Warning! Processing without inner classes.");
}
return new Arguments(projectPath, resultPath, filter, withInnerClasses);

File apkFile = new File(apkPath);
if (!apkFile.exists()) {
System.out.println(apkFile + " not found!");
return null;
}

return new Arguments(
apkPath, projectPath, resultPath, filter, withInnerClasses);
}
}
Loading

0 comments on commit 03d434c

Please sign in to comment.