Add class mapper and use it in annotation mapper, which seems to work more reliably
This commit is contained in:
@@ -211,6 +211,7 @@ public class ModArith implements Deobfuscator
|
|||||||
{
|
{
|
||||||
Number value;
|
Number value;
|
||||||
boolean other;
|
boolean other;
|
||||||
|
boolean constant;
|
||||||
}
|
}
|
||||||
private MultiValueMap<Field, AssociatedConstant> constants = new MultiValueMap();
|
private MultiValueMap<Field, AssociatedConstant> constants = new MultiValueMap();
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,19 @@ public class AnnotationMapper
|
|||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
|
ClassGroupMapper m = new ClassGroupMapper(source, target);
|
||||||
|
m.map();
|
||||||
|
|
||||||
for (ClassFile c : source.getClasses())
|
for (ClassFile c : source.getClasses())
|
||||||
{
|
{
|
||||||
ClassFile other = (ClassFile) mapping.get(c);
|
ClassFile other = m.get(c);
|
||||||
|
|
||||||
|
if (other == null)
|
||||||
|
{
|
||||||
|
other = (ClassFile) mapping.get(c);
|
||||||
|
System.out.println("FALLBACK " + c + " -> " + other);
|
||||||
|
}
|
||||||
|
|
||||||
if (other == null)
|
if (other == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package net.runelite.deob.deobfuscators.rename;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.runelite.asm.ClassFile;
|
||||||
|
import net.runelite.asm.ClassGroup;
|
||||||
|
|
||||||
|
public class ClassGroupMapper
|
||||||
|
{
|
||||||
|
private final ClassGroup one, two;
|
||||||
|
private final Map<ClassFile, ClassFile> map = new HashMap<>();
|
||||||
|
|
||||||
|
public ClassGroupMapper(ClassGroup one, ClassGroup two)
|
||||||
|
{
|
||||||
|
this.one = one;
|
||||||
|
this.two = two;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map()
|
||||||
|
{
|
||||||
|
for (ClassFile cf1 : one.getClasses())
|
||||||
|
for (ClassFile cf2 : two.getClasses())
|
||||||
|
{
|
||||||
|
if (!MappingExecutorUtil.isMaybeEqual(cf1, cf2))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ClassMapper m = new ClassMapper(cf1, cf2);
|
||||||
|
if (!m.same())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
map.put(cf1, cf2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ClassFile, ClassFile> getMap()
|
||||||
|
{
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassFile get(ClassFile c)
|
||||||
|
{
|
||||||
|
return map.get(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package net.runelite.deob.deobfuscators.rename;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import net.runelite.asm.ClassFile;
|
||||||
|
import net.runelite.asm.signature.Signature;
|
||||||
|
import net.runelite.asm.signature.Type;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
|
||||||
|
public class ClassMapper
|
||||||
|
{
|
||||||
|
private final ClassFile one, two;
|
||||||
|
|
||||||
|
public ClassMapper(ClassFile one, ClassFile two)
|
||||||
|
{
|
||||||
|
this.one = one;
|
||||||
|
this.two = two;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> fieldCardinalities(ClassFile cf)
|
||||||
|
{
|
||||||
|
List<Type> t = cf.getFields().getFields().stream()
|
||||||
|
.filter(f -> !f.isStatic())
|
||||||
|
.map(f -> f.getType())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
Map<Type, Integer> m = CollectionUtils.getCardinalityMap(t);
|
||||||
|
|
||||||
|
List<Integer> occurances = new ArrayList<>(m.values());
|
||||||
|
Collections.sort(occurances);
|
||||||
|
return occurances;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> methodCardinalities(ClassFile cf)
|
||||||
|
{
|
||||||
|
List<Signature> t = cf.getMethods().getMethods().stream()
|
||||||
|
.filter(m -> !m.isStatic())
|
||||||
|
.filter(m -> !m.getName().startsWith("<"))
|
||||||
|
.map(m -> m.getDescriptor())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
Map<Signature, Integer> m = CollectionUtils.getCardinalityMap(t);
|
||||||
|
|
||||||
|
List<Integer> occurances = new ArrayList<>(m.values());
|
||||||
|
Collections.sort(occurances);
|
||||||
|
return occurances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean same()
|
||||||
|
{
|
||||||
|
List<Integer> c1 = fieldCardinalities(one), c2 = fieldCardinalities(two);
|
||||||
|
|
||||||
|
if (!c1.equals(c2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
c1 = methodCardinalities(one);
|
||||||
|
c2 = methodCardinalities(two);
|
||||||
|
|
||||||
|
if (!c1.equals(c2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,8 @@ public class Mapper
|
|||||||
finalm.merge(mapStaticMethods(source, target));
|
finalm.merge(mapStaticMethods(source, target));
|
||||||
finalm.merge(mapMethods(source, target));
|
finalm.merge(mapMethods(source, target));
|
||||||
finalm.merge(mapPackets(finalm, source, target));
|
finalm.merge(mapPackets(finalm, source, target));
|
||||||
|
|
||||||
|
finalm.buildClasses();
|
||||||
|
|
||||||
mapping = finalm;
|
mapping = finalm;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package net.runelite.deob.deobfuscators.rename;
|
package net.runelite.deob.deobfuscators.rename;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import net.runelite.asm.ClassFile;
|
import net.runelite.asm.ClassFile;
|
||||||
import net.runelite.asm.ClassGroup;
|
import net.runelite.asm.ClassGroup;
|
||||||
|
import net.runelite.asm.Interfaces;
|
||||||
import net.runelite.asm.Method;
|
import net.runelite.asm.Method;
|
||||||
import net.runelite.asm.attributes.code.Instruction;
|
import net.runelite.asm.attributes.code.Instruction;
|
||||||
import net.runelite.asm.attributes.code.instruction.types.ArrayLoad;
|
import net.runelite.asm.attributes.code.instruction.types.ArrayLoad;
|
||||||
@@ -330,6 +327,19 @@ public class MappingExecutorUtil
|
|||||||
|
|
||||||
public static boolean isMaybeEqual(ClassFile cf1, ClassFile cf2)
|
public static boolean isMaybeEqual(ClassFile cf1, ClassFile cf2)
|
||||||
{
|
{
|
||||||
|
if (cf1 == null && cf2 == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (cf1 == null || cf2 == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!isMaybeEqual(cf1.getParent(), cf2.getParent()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Interfaces i1 = cf1.getInterfaces(), i2 = cf2.getInterfaces();
|
||||||
|
if (i1.getInterfaces().size() != i2.getInterfaces().size())
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.google.common.collect.HashMultimap;
|
|||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@@ -80,7 +81,7 @@ public class ParallelExecutorMapping
|
|||||||
|
|
||||||
public void map(Object one, Object two)
|
public void map(Object one, Object two)
|
||||||
{
|
{
|
||||||
mapClass(one, two);
|
//mapClass(one, two);
|
||||||
|
|
||||||
Mapping m = getMapping(one, two);
|
Mapping m = getMapping(one, two);
|
||||||
|
|
||||||
@@ -101,6 +102,11 @@ public class ParallelExecutorMapping
|
|||||||
|
|
||||||
Field f1 = (Field) one;
|
Field f1 = (Field) one;
|
||||||
Field f2 = (Field) two;
|
Field f2 = (Field) two;
|
||||||
|
|
||||||
|
assert f1.isStatic() == f2.isStatic();
|
||||||
|
|
||||||
|
if (f1.isStatic() || f2.isStatic())
|
||||||
|
return;
|
||||||
|
|
||||||
cf1 = f1.getFields().getClassFile();
|
cf1 = f1.getFields().getClassFile();
|
||||||
cf2 = f2.getFields().getClassFile();
|
cf2 = f2.getFields().getClassFile();
|
||||||
@@ -112,6 +118,11 @@ public class ParallelExecutorMapping
|
|||||||
|
|
||||||
Method m1 = (Method) one;
|
Method m1 = (Method) one;
|
||||||
Method m2 = (Method) two;
|
Method m2 = (Method) two;
|
||||||
|
|
||||||
|
assert m1.isStatic() == m1.isStatic();
|
||||||
|
|
||||||
|
if (m1.isStatic() || m2.isStatic())
|
||||||
|
return;
|
||||||
|
|
||||||
cf1 = m1.getMethods().getClassFile();
|
cf1 = m1.getMethods().getClassFile();
|
||||||
cf2 = m2.getMethods().getClassFile();
|
cf2 = m2.getMethods().getClassFile();
|
||||||
@@ -130,6 +141,21 @@ public class ParallelExecutorMapping
|
|||||||
m.inc();
|
m.inc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void buildClasses()
|
||||||
|
{
|
||||||
|
for (Object o : new HashSet<>(map.keySet()))
|
||||||
|
if (o instanceof ClassFile)
|
||||||
|
map.removeAll(o);
|
||||||
|
|
||||||
|
Map<Object, Object> map = getMap();
|
||||||
|
for (Object key : map.keySet())
|
||||||
|
{
|
||||||
|
Object value = map.get(key);
|
||||||
|
|
||||||
|
mapClass(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Object get(Object o)
|
public Object get(Object o)
|
||||||
{
|
{
|
||||||
return highest(o);
|
return highest(o);
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package net.runelite.deob.deobfuscators.rename;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.runelite.asm.ClassFile;
|
||||||
|
import net.runelite.asm.ClassGroup;
|
||||||
|
import net.runelite.deob.util.JarUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adam
|
||||||
|
*/
|
||||||
|
public class ClassGroupMapperTest
|
||||||
|
{
|
||||||
|
private static final String JAR1 = "C:\\Users\\Adam\\.m2\\repository\\net\\runelite\\rs\\rs-client\\1.0-SNAPSHOT\\rs-client-1.0-SNAPSHOT.jar",
|
||||||
|
JAR2 = "c:/rs/gamepack_v19_postmath.jar",
|
||||||
|
OUT = "c:/rs/adamout.jar";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRun() throws IOException
|
||||||
|
{
|
||||||
|
ClassGroup group1 = JarUtil.loadJar(new File(JAR1));
|
||||||
|
ClassGroup group2 = JarUtil.loadJar(new File(JAR2));
|
||||||
|
|
||||||
|
ClassGroupMapper m = new ClassGroupMapper(group1, group2);
|
||||||
|
m.map();
|
||||||
|
|
||||||
|
for (ClassFile cf : group1.getClasses())
|
||||||
|
{
|
||||||
|
ClassFile cf2 = m.get(cf);
|
||||||
|
|
||||||
|
System.out.println(cf + " -> " + cf2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package net.runelite.deob.deobfuscators.rename;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import net.runelite.asm.ClassGroup;
|
||||||
|
import net.runelite.deob.util.JarUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Adam
|
||||||
|
*/
|
||||||
|
public class ClassMapperTest
|
||||||
|
{
|
||||||
|
private static final String JAR1 = "C:\\Users\\Adam\\.m2\\repository\\net\\runelite\\rs\\rs-client\\1.0-SNAPSHOT\\rs-client-1.0-SNAPSHOT.jar",
|
||||||
|
JAR2 = "c:/rs/gamepack_v19_postmath.jar";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRun() throws IOException
|
||||||
|
{
|
||||||
|
ClassGroup group1 = JarUtil.loadJar(new File(JAR1));
|
||||||
|
ClassGroup group2 = JarUtil.loadJar(new File(JAR2));
|
||||||
|
|
||||||
|
ClassMapper m = new ClassMapper(group1.findClass("class118"), group2.findClass("class119"));
|
||||||
|
Assert.assertTrue(m.same());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user