Skip to content

Commit

Permalink
Stringer 3.x transformers
Browse files Browse the repository at this point in the history
  • Loading branch information
samczsun committed Dec 2, 2017
1 parent 5d9d5bb commit 202702c
Show file tree
Hide file tree
Showing 37 changed files with 2,809 additions and 16 deletions.
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>java-deobfuscator-repository</id>
<url>https://repo.samczsun.com/repository/java-deobfuscator</url>
</repository>
</repositories>

<dependencies>
<dependency>
Expand Down Expand Up @@ -136,6 +142,11 @@
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.javadeobfuscator</groupId>
<artifactId>javavm</artifactId>
<version>1.0.0</version>
</dependency>

<!-- ASM related dependencies -->
<dependency>
Expand Down
23 changes: 20 additions & 3 deletions src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
Expand All @@ -57,11 +56,16 @@ public class Deobfuscator {
private Map<String, ClassTree> hierachy = new HashMap<>();
private Set<ClassNode> libraryClassnodes = new HashSet<>();

public Map<String, byte[]> getInputPassthrough() {
return inputPassthrough;
}

// Entries from the input jar that will be passed through to the output
private Map<String, byte[]> inputPassthrough = new HashMap<>();

// Constant pool data since ClassNodes don't support custom data
private Map<ClassNode, ConstantPool> constantPools = new HashMap<>();
private Map<ClassNode, ClassReader> readers = new HashMap<>();

private final Configuration configuration;
private final Logger logger = LoggerFactory.getLogger(Deobfuscator.class);
Expand Down Expand Up @@ -99,7 +103,7 @@ private Map<String, ClassNode> loadClasspathFile(File file, boolean skipCode) th
if (ent.getName().endsWith(".class")) {
ClassReader reader = new ClassReader(zipIn.getInputStream(ent));
ClassNode node = new ClassNode();
reader.accept(node, (skipCode ? ClassReader.SKIP_CODE : 0) | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
reader.accept(node, (skipCode ? 0 : 0) | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
map.put(node.name, node);

setConstantPool(node, new ConstantPool(reader));
Expand Down Expand Up @@ -182,6 +186,7 @@ private void loadInput() throws IOException {
ClassNode node = new ClassNode();
reader.accept(node, ClassReader.SKIP_FRAMES);

readers.put(node, reader);
setConstantPool(node, new ConstantPool(reader));

if (!isClassIgnored(node)) {
Expand Down Expand Up @@ -293,7 +298,7 @@ public void start() throws Throwable {

public boolean runFromConfig(TransformerConfig config) throws Throwable {
Transformer transformer = config.getImplementation().newInstance();
transformer.init(this, config, classes, classpath);
transformer.init(this, config, classes, classpath, readers);
return transformer.transform();
}

Expand Down Expand Up @@ -450,6 +455,10 @@ public byte[] toByteArray(ClassNode node) {
System.out.println("Error: " + ex.getClassName() + " could not be found while writing " + node.name + ". Using COMPUTE_MAXS");
writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS);
node.accept(writer);
} else if (e instanceof NegativeArraySizeException || e instanceof ArrayIndexOutOfBoundsException) {
System.out.println("Error: failed to compute frames");
writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS);
node.accept(writer);
} else if (e.getMessage() != null) {
if (e.getMessage().contains("JSR/RET")) {
System.out.println("ClassNode contained JSR/RET so COMPUTE_MAXS instead");
Expand Down Expand Up @@ -485,6 +494,14 @@ public Configuration getConfig() {
return this.configuration;
}

public Map<String, ClassNode> getClasses() {
return this.classes;
}

public Map<ClassNode, ClassReader> getReaders() {
return readers;
}

public class CustomClassWriter extends ClassWriter {
public CustomClassWriter(int flags) {
super(flags);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2017 Sam Sun <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.javadeobfuscator.deobfuscator.exceptions;

public class WrongTransformerException extends RuntimeException {
public WrongTransformerException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2017 Sam Sun <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.javadeobfuscator.deobfuscator.graph;

import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class GraphHelper {
public static Map<String, ClassNode> validateUniqueClasses(Collection<ClassNode> nodes) {
Map<String, ClassNode> map = new HashMap<>();
for (ClassNode classNode : nodes) {
if (map.put(classNode.name, classNode) != null) {
throw new IllegalArgumentException("Duplicate class " + classNode.name);
}
}
return map;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2017 Sam Sun <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.javadeobfuscator.deobfuscator.graph.callgraph;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class CallGraph {
private final Map<Key, CallGraphNode> map;
private final Set<CallGraphNode> entryPoints;

CallGraph(Map<Key, CallGraphNode> callGraph, Set<CallGraphNode> entryPoints) {
this.map = ImmutableMap.copyOf(callGraph);
this.entryPoints = ImmutableSet.copyOf(entryPoints);
}

public final CallGraphNode get(ClassNode owner, MethodNode method) {
return map.get(getKey(owner, method));
}

public final CallGraphNode get(String owner, String name, String desc) {
return map.get(getKey(owner, name, desc));
}

public final Collection<CallGraphNode> values() {
return map.values();
}

/**
* An entry point being any node with no incoming edges, not necessarily a Java entry point
*/
public final Set<CallGraphNode> getEntryPoints() {
return entryPoints;
}

static Key getKey(ClassNode classNode, MethodNode methodNode) {
return new Key(classNode.name, methodNode.name, methodNode.desc);
}

static Key getKey(String owner, String name, String desc) {
return new Key(owner, name, desc);
}


static class Key {
final String owner;
final String name;
final String desc;

public Key(String owner, String name, String desc) {
this.owner = owner;
this.name = name;
this.desc = desc;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
return Objects.equals(owner, key.owner) &&
Objects.equals(name, key.name) &&
Objects.equals(desc, key.desc);
}

@Override
public int hashCode() {
return Objects.hash(owner, name, desc);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2017 Sam Sun <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.javadeobfuscator.deobfuscator.graph.callgraph;

import com.javadeobfuscator.deobfuscator.graph.inheritancegraph.InheritanceGraph;
import com.javadeobfuscator.deobfuscator.graph.inheritancegraph.InheritanceGraphNode;
import com.javadeobfuscator.deobfuscator.utils.TransformerHelper;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.*;

import static com.javadeobfuscator.deobfuscator.graph.GraphHelper.validateUniqueClasses;

public class CallGraphBuilder {
private List<ClassNode> nodes = new ArrayList<>();
private InheritanceGraph inheritanceGraph;

private CallGraphBuilder() {
}

public static CallGraphBuilder newBuilder() {
return new CallGraphBuilder();
}

public final CallGraphBuilder withNode(ClassNode node) {
nodes.add(node);
return this;
}

public final CallGraphBuilder withNodes(Collection<ClassNode> nodes) {
this.nodes.addAll(nodes);
return this;
}

public final CallGraphBuilder withInheritanceGraph(InheritanceGraph inheritanceGraph) {
this.inheritanceGraph = inheritanceGraph;
return this;
}

public final CallGraph build() {
Map<String, ClassNode> classMap = validateUniqueClasses(nodes);

Map<CallGraph.Key, CallGraphNode> callGraph = new HashMap<>();

// First, compute the call graph
for (ClassNode classNode : nodes) {
for (MethodNode methodNode : classNode.methods) {
CallGraphNode thisNode = callGraph.computeIfAbsent(CallGraph.getKey(classNode, methodNode), CallGraphNode::new);

if (methodNode.instructions == null) {
continue;
}

for (ListIterator<AbstractInsnNode> it = methodNode.instructions.iterator(); it.hasNext(); ) {
AbstractInsnNode insn = it.next();

if (insn instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
CallGraph.Key otherKey = CallGraph.getKey(methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc);
CallGraphNode otherNode = callGraph.computeIfAbsent(otherKey, CallGraphNode::new);

thisNode.addXrefTo(otherNode);
otherNode.addXrefFrom(thisNode);

// Handle inheritance
if (inheritanceGraph != null) {
InheritanceGraphNode inheritanceGraphNode = inheritanceGraph.get(methodInsnNode.owner);
if (inheritanceGraphNode != null) {
for (InheritanceGraphNode child : inheritanceGraphNode.getChildren()) {
ClassNode childNode = classMap.get(child.getOwner());
if (childNode == null) {
continue;
}
MethodNode method = TransformerHelper.findMethodNode(childNode, methodInsnNode.name, methodInsnNode.desc);
if (method == null) {
continue;
}
otherKey = CallGraph.getKey(child.getOwner(), methodInsnNode.name, methodInsnNode.desc);
otherNode = callGraph.computeIfAbsent(otherKey, CallGraphNode::new);

thisNode.addXrefTo(otherNode);
otherNode.addXrefFrom(thisNode);
}

for (InheritanceGraphNode parent : inheritanceGraphNode.getParents()) {
ClassNode parentNode = classMap.get(parent.getOwner());
if (parentNode == null) {
continue;
}
MethodNode method = TransformerHelper.findMethodNode(parentNode, methodInsnNode.name, methodInsnNode.desc);
if (method == null) {
continue;
}
otherKey = CallGraph.getKey(parent.getOwner(), methodInsnNode.name, methodInsnNode.desc);
otherNode = callGraph.computeIfAbsent(otherKey, CallGraphNode::new);

thisNode.addXrefTo(otherNode);
otherNode.addXrefFrom(thisNode);
}
}
}
}
}
}
}

// Freeze it!
callGraph.values().forEach(CallGraphNode::freeze);

// Next, compute the entry points
Set<CallGraphNode> entryPoints = new HashSet<>();
for (CallGraphNode node : callGraph.values()) {
if (!classMap.containsKey(node.getOwner())) {
continue;
}

if (node.getXrefsFrom().isEmpty()) {
entryPoints.add(node);
}
}

return new CallGraph(callGraph, entryPoints);
}
}
Loading

0 comments on commit 202702c

Please sign in to comment.