Skip to content

Commit 2b8ba62

Browse files
committed
feat: preliminary android support
1 parent 542a855 commit 2b8ba62

File tree

11 files changed

+312
-23
lines changed

11 files changed

+312
-23
lines changed

dev.skidfuscator.obfuscator/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
implementation 'com.github.matomo-org:matomo-java-tracker:v1.7'
2424
api 'com.github.Col-E:jphantom:1.4.3'
2525
implementation 'dev.dirs:directories:26'
26+
implementation 'de.femtopedia.dex2jar:dex2jar:2.4.24'
2627

2728
testAnnotationProcessor 'org.projectlombok:lombok:1.18.24'
2829
implementation 'org.fusesource.jansi:jansi:2.4.0'

dev.skidfuscator.obfuscator/src/main/java/dev/skidfuscator/obfuscator/Skidfuscator.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@
2525
import dev.skidfuscator.obfuscator.exempt.ExemptManager;
2626
import dev.skidfuscator.obfuscator.hierarchy.Hierarchy;
2727
import dev.skidfuscator.obfuscator.hierarchy.SkidHierarchy;
28+
import dev.skidfuscator.obfuscator.io.apk.ApkInputSource;
29+
import dev.skidfuscator.obfuscator.io.jar.JarInputSource;
2830
import dev.skidfuscator.obfuscator.number.hash.HashTransformer;
2931
import dev.skidfuscator.obfuscator.order.OrderAnalysis;
3032
import dev.skidfuscator.obfuscator.order.priority.MethodPriority;
31-
import dev.skidfuscator.obfuscator.phantom.jphantom.PhantomJarDownloader;
3233
import dev.skidfuscator.obfuscator.predicate.PredicateAnalysis;
3334
import dev.skidfuscator.obfuscator.predicate.SimplePredicateAnalysis;
3435
import dev.skidfuscator.obfuscator.predicate.opaque.impl.IntegerBlockOpaquePredicate;
@@ -79,7 +80,6 @@
7980
import org.mapleir.ir.cfg.ControlFlowGraph;
8081
import org.objectweb.asm.Opcodes;
8182
import org.piwik.java.tracking.PiwikRequest;
82-
import org.topdank.byteengineer.commons.data.JarClassData;
8383
import org.topdank.byteengineer.commons.data.JarContents;
8484

8585
import java.io.File;
@@ -533,19 +533,21 @@ protected void _importJvm() {
533533

534534
protected void _importClasspath() {
535535
LOGGER.post("Importing jar...");
536-
/*
537-
* This is the main jar download. We'll be keeping a cache of the jar
538-
* download as it will simplify our output methods. In several cases,
539-
* many jars have classes with names that do not align with their
540-
* respective ClassNode#getName, causing conflicts, hence why the cache
541-
* of the jar downloader.
542-
*/
543-
final PhantomJarDownloader<ClassNode> downloader = MapleJarUtil.importPhantomJar(
544-
session.getInput(),
545-
this
546-
);
547536

548-
this.jarContents = downloader.getJarContents();
537+
final String path = session.getInput().getPath();
538+
539+
if (path.endsWith(".apk")) {
540+
this.jarContents = session.getInput().isDirectory()
541+
? new ApkInputSource(this).download(session.getInput())
542+
: new ApkInputSource(this).download(new File(path));
543+
} else if (path.endsWith(".dex")) {
544+
this.jarContents = new JarInputSource(this)
545+
.download(session.getInput());
546+
} else {
547+
this.jarContents = new JarInputSource(this)
548+
.download(session.getInput());
549+
}
550+
549551
this.classSource = new SkidApplicationClassSource(
550552
session.getInput().getName(),
551553
session.isFuckIt(),
@@ -633,7 +635,8 @@ protected void _importClasspath() {
633635
* Furthermore, since it computes classes which could be present in other
634636
* libraries, we set the priority to -1, making it the last fallback result.
635637
*/
636-
this.classSource.addLibraries(new LibraryClassSource(
638+
// TODO: perma remove jphantom
639+
/*this.classSource.addLibraries(new LibraryClassSource(
637640
new ApplicationClassSource(
638641
"phantom",
639642
true,
@@ -644,7 +647,7 @@ protected void _importClasspath() {
644647
.collect(Collectors.toList())
645648
),
646649
1
647-
));
650+
));*/
648651
LOGGER.log("Finished importing classpath!");
649652
}
650653

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.skidfuscator.obfuscator.io;
2+
3+
import dev.skidfuscator.obfuscator.Skidfuscator;
4+
5+
public abstract class AbstractInputSource implements InputSource{
6+
protected final Skidfuscator skidfuscator;
7+
8+
public AbstractInputSource(Skidfuscator skidfuscator) {
9+
this.skidfuscator = skidfuscator;
10+
}
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package dev.skidfuscator.obfuscator.io;
2+
3+
import org.topdank.byteengineer.commons.data.JarContents;
4+
5+
import java.io.File;
6+
7+
public interface InputSource {
8+
JarContents download(final File input);
9+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package dev.skidfuscator.obfuscator.io;
2+
3+
public interface OutputSource {
4+
5+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package dev.skidfuscator.obfuscator.io;
2+
3+
import java.util.Set;
4+
5+
public interface Workspace {
6+
Set<InputSource> inputSources();
7+
8+
Set<InputSource> librarySources();
9+
10+
11+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package dev.skidfuscator.obfuscator.io.apk;
2+
3+
import dev.skidfuscator.obfuscator.Skidfuscator;
4+
import dev.skidfuscator.obfuscator.io.dex.DexInputSource;
5+
import org.topdank.byteengineer.commons.data.JarContents;
6+
7+
import java.io.File;
8+
import java.io.FileOutputStream;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.nio.file.Files;
12+
import java.nio.file.Path;
13+
import java.nio.file.Paths;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
import java.util.zip.ZipEntry;
17+
import java.util.zip.ZipFile;
18+
19+
public class ApkInputSource extends DexInputSource {
20+
21+
public ApkInputSource(Skidfuscator skidfuscator) {
22+
super(skidfuscator);
23+
}
24+
25+
@Override
26+
public JarContents download(final File input) {
27+
final JarContents contents = new JarContents();
28+
29+
try {
30+
Path tempDir = Paths.get("tempDex");
31+
if (!Files.exists(tempDir)) {
32+
Files.createDirectories(tempDir);
33+
}
34+
35+
36+
List<String> dexFiles = extractDexFiles(input, tempDir.toString());
37+
for (String dexFile : dexFiles) {
38+
JarContents localContents = processDexFile(dexFile);
39+
contents.add(localContents);
40+
}
41+
} catch (IOException e) {
42+
e.printStackTrace();
43+
}
44+
return contents;
45+
}
46+
47+
private List<String> extractDexFiles(File apkFile, String outputDir) throws IOException {
48+
if (!apkFile.exists()) {
49+
throw new IOException("APK file not found: " + apkFile.getAbsolutePath());
50+
}
51+
52+
File outputDirectory = new File(outputDir);
53+
if (!outputDirectory.exists() && !outputDirectory.mkdirs()) {
54+
throw new IOException("Failed to create output directory: " + outputDir);
55+
}
56+
57+
List<String> extractedFiles = new ArrayList<>();
58+
try (ZipFile zipFile = new ZipFile(apkFile)) {
59+
zipFile.stream()
60+
.filter(entry -> entry.getName().endsWith(".dex"))
61+
.forEach(entry -> {
62+
try {
63+
String extractedPath = extractDexFile(zipFile, entry, outputDirectory);
64+
extractedFiles.add(extractedPath);
65+
} catch (IOException e) {
66+
throw new RuntimeException("Failed to extract " + entry.getName(), e);
67+
}
68+
});
69+
}
70+
return extractedFiles;
71+
}
72+
73+
private String extractDexFile(ZipFile zipFile, ZipEntry dexEntry, File outputDir) throws IOException {
74+
File outputFile = new File(outputDir, dexEntry.getName());
75+
File parent = outputFile.getParentFile();
76+
if (parent != null && !parent.exists() && !parent.mkdirs()) {
77+
throw new IOException("Failed to create directory: " + parent.getPath());
78+
}
79+
80+
try (InputStream input = zipFile.getInputStream(dexEntry);
81+
FileOutputStream output = new FileOutputStream(outputFile)) {
82+
byte[] buffer = new byte[8192];
83+
int bytesRead;
84+
while ((bytesRead = input.read(buffer)) != -1) {
85+
output.write(buffer, 0, bytesRead);
86+
}
87+
}
88+
return outputFile.getAbsolutePath();
89+
}
90+
91+
private JarContents processDexFile(String dexFile) {
92+
DexInputSource dexInputSource = new DexInputSource(skidfuscator);
93+
return dexInputSource.download(new File(dexFile));
94+
}
95+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dev.skidfuscator.obfuscator.io.dex;
2+
3+
import dev.skidfuscator.obfuscator.Skidfuscator;
4+
import dev.skidfuscator.obfuscator.io.AbstractInputSource;
5+
import dev.skidfuscator.obfuscator.util.MapleJarUtil;
6+
import org.mapleir.asm.ClassNode;
7+
import org.topdank.byteengineer.commons.data.JarContents;
8+
9+
import java.io.File;
10+
11+
public class DexInputSource extends AbstractInputSource {
12+
public DexInputSource(Skidfuscator skidfuscator) {
13+
super(skidfuscator);
14+
}
15+
16+
@Override
17+
public JarContents download(final File input) {
18+
final DexJarDownloader<ClassNode> downloader = MapleJarUtil.importDex(
19+
input,
20+
skidfuscator
21+
);
22+
23+
return downloader.getJarContents();
24+
}
25+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package dev.skidfuscator.obfuscator.io.dex;
2+
3+
import com.google.common.io.ByteStreams;
4+
import com.googlecode.d2j.dex.ClassVisitorFactory;
5+
import com.googlecode.d2j.dex.Dex2Asm;
6+
import com.googlecode.d2j.node.DexFileNode;
7+
import com.googlecode.d2j.reader.DexFileReader;
8+
import dev.skidfuscator.obfuscator.Skidfuscator;
9+
import dev.skidfuscator.obfuscator.skidasm.SkidClassNode;
10+
import org.mapleir.asm.ClassNode;
11+
import org.topdank.byteengineer.commons.asm.ASMFactory;
12+
import org.topdank.byteengineer.commons.data.*;
13+
import org.topdank.byteio.in.AbstractJarDownloader;
14+
15+
import java.io.IOException;
16+
import java.io.InputStream;
17+
import java.net.JarURLConnection;
18+
import java.net.URL;
19+
import java.util.Enumeration;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.jar.JarEntry;
23+
import java.util.jar.JarFile;
24+
25+
public class DexJarDownloader<C extends ClassNode> extends AbstractJarDownloader<C> {
26+
27+
private final Skidfuscator skidfuscator;
28+
protected final JarInfo jarInfo;
29+
30+
public DexJarDownloader(Skidfuscator skidfuscator, JarInfo jarInfo) {
31+
this.skidfuscator = skidfuscator;
32+
this.jarInfo = jarInfo;
33+
}
34+
35+
public DexJarDownloader(Skidfuscator skidfuscator, ASMFactory<C> factory, JarInfo jarInfo) {
36+
super(factory);
37+
this.skidfuscator = skidfuscator;
38+
this.jarInfo = jarInfo;
39+
}
40+
41+
@Override
42+
public void download() throws IOException {
43+
// STEP 1: Download the DEX raw
44+
URL url = null;
45+
JarURLConnection connection = (JarURLConnection) (url = new URL(jarInfo.formattedURL())).openConnection();
46+
JarFile jarFile = connection.getJarFile();
47+
Enumeration<JarEntry> entries = jarFile.entries();
48+
contents = new LocateableJarContents(url);
49+
50+
Map<String, byte[]> data = new HashMap<>();
51+
while (entries.hasMoreElements()) {
52+
JarEntry entry = entries.nextElement();
53+
byte[] bytes = read(jarFile.getInputStream(entry));
54+
if (entry.getName().endsWith(".class")) {
55+
data.put(entry.getName(), bytes);
56+
//System.out.println("[+] " + entry.getName());
57+
} else {
58+
JarResource resource = new JarResource(entry.getName(), bytes);
59+
contents.getResourceContents().add(resource);
60+
}
61+
}
62+
63+
JarContents.ClassNodeContainer classes = new JarContents.ClassNodeContainer();
64+
ClassVisitorFactory factory = classInternalName -> {
65+
org.objectweb.asm.tree.ClassNode asmNode = new org.objectweb.asm.tree.ClassNode();
66+
asmNode.name = classInternalName;
67+
68+
contents.getClassContents().add(new JarClassData(
69+
classInternalName, data.get(classInternalName),
70+
new SkidClassNode(asmNode, skidfuscator)
71+
));
72+
73+
return asmNode;
74+
};
75+
76+
DexFileReader reader = new DexFileReader(connection.getInputStream());
77+
DexFileNode node = new DexFileNode();
78+
reader.accept(node);
79+
80+
81+
new Dex2Asm().convertDex(
82+
node,
83+
factory
84+
);
85+
}
86+
87+
private byte[] read(InputStream inputStream) throws IOException {
88+
return ByteStreams.toByteArray(inputStream);
89+
}
90+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package dev.skidfuscator.obfuscator.io.jar;
2+
3+
import dev.skidfuscator.obfuscator.Skidfuscator;
4+
import dev.skidfuscator.obfuscator.io.AbstractInputSource;
5+
import dev.skidfuscator.obfuscator.phantom.jphantom.PhantomJarDownloader;
6+
import dev.skidfuscator.obfuscator.util.MapleJarUtil;
7+
import org.mapleir.asm.ClassNode;
8+
import org.topdank.byteengineer.commons.data.JarContents;
9+
10+
import java.io.File;
11+
12+
public class JarInputSource extends AbstractInputSource {
13+
public JarInputSource(Skidfuscator skidfuscator) {
14+
super(skidfuscator);
15+
}
16+
17+
@Override
18+
public JarContents download(File input) {
19+
final PhantomJarDownloader<ClassNode> downloader = MapleJarUtil.importPhantomJar(
20+
input,
21+
skidfuscator
22+
);
23+
24+
return downloader.getJarContents();
25+
}
26+
}

0 commit comments

Comments
 (0)