Last active
April 2, 2020 13:12
-
-
Save 0xdeki/be00a5c5c027dc23d878729840ef4808 to your computer and use it in GitHub Desktop.
pretty sturdy mult finder for osrs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.deki.updater.asm.deob; | |
import io.deki.updater.asm.FieldMult; | |
import io.deki.updater.hook.FieldHook; | |
import org.objectweb.asm.Opcodes; | |
import org.objectweb.asm.tree.*; | |
import java.util.*; | |
/** | |
* Created by Deki on 09.07.2019 | |
* This class helps deobfuscate an integer/long obfuscation technique | |
* used by Jagex. It works by multiplying values of field calls with | |
* some large number, making it harder to read, especially due to the | |
* clever use of overflows. Knowing a field's multiplier is also required | |
* to get the proper values in ex. a cheat client. However, all obfuscators | |
* produce patterns by design and this is no exception. Namely one of the | |
* easier obfuscations to deobfuscate, we simply look for field calls | |
* multiplied by some number. To make our efforts harder, Jagex added | |
* unused/unreachable field calls with multiplier numbers that result | |
* in the wrong values, but the same fake number never appears more | |
* than once. This makes for an easily identifiable pattern which we | |
* can extract the values needed to get the right values from. | |
*/ | |
public class MultFinder { | |
private static List<FieldMult> potentialMults, mults; | |
/** | |
* main function | |
* @param nodes List of classnodes from gamepack | |
*/ | |
public static void mapMults(Collection<ClassNode> nodes) { | |
potentialMults = new ArrayList<>(); | |
mults = new ArrayList<>(); | |
for (ClassNode node : nodes) { | |
process(node); | |
} | |
} | |
/** | |
* Finds a specified hooks' mult in the mappings and sets it. | |
* FUN FACT: streams are better for big datasets than iterators, | |
* not sure if this counts as a big dataset though :) | |
* @param hook Field hook object used to store owner/name/mult | |
*/ | |
public static void setMult(FieldHook hook) { | |
FieldMult mult = mults.stream().filter(m -> m.getOwner().equals(hook.getClazz()) | |
&& m.getName().equals(hook.getField())).findFirst().orElse(null); | |
if (mult != null) hook.setMultiplier(mult.getMult()); | |
} | |
/** | |
* Internal method to process each class node. | |
* We're looking for this pattern: | |
* GETFIELD/GETSTATIC/PUTFIELD | |
* LDC | |
* IMUL/LMUL | |
* (ldc and getter/setter can be swapped) | |
* @param node Class node to be processed | |
*/ | |
private static void process(ClassNode node) { | |
//we want to loop through all methods | |
node.methods.forEach(methodNode -> methodNode.instructions.iterator().forEachRemaining(insn -> { | |
//we're looking for IMUL/LMULs, no need for the other ones | |
if (insn.opcode() != Opcodes.IMUL && insn.opcode() != Opcodes.LMUL) return; | |
//get the instruction from before the multiplication instruction | |
AbstractInsnNode first = insn.previous(); | |
long constant = decodeLdc(first); | |
FieldInsnNode field = decodeField(first); | |
//here we split the path into two for optimal results (thanks spencer!): | |
//first option is if the pattern is GET/LDC/MUL | |
//second is LDC/GET/MUL | |
if (constant == -1) { | |
AbstractInsnNode second = first.previous(); | |
constant = decodeLdc(second); | |
} else { | |
AbstractInsnNode second = first.previous(); | |
field = decodeField(second); | |
} | |
//check if anything is null or if the mult is an even number (thanks spencer!) | |
if (field == null || constant == -1 || (constant & 1) == 0) return; | |
boolean decoder = field.opcode() == Opcodes.GETFIELD || field.opcode() == Opcodes.GETSTATIC; | |
//create a FieldMult object, just used for storing values | |
FieldMult mult = new FieldMult(field.owner, field.name, constant, decoder); | |
//check if we already mapped the mult in question | |
if (mults.contains(mult)) return; | |
//check if we already encountered this specific mult. | |
//jagex's obfuscator sprinkles in dummy constants, but these never occur more than once. | |
//if it's present more than once we're confident that it's the real deal. | |
if (potentialMults.contains(mult)) { | |
mults.add(mult); | |
return; | |
} | |
//add the field to encountered mults. | |
potentialMults.add(mult); | |
})); | |
} | |
private static long decodeLdc(AbstractInsnNode node) { | |
//if this isn't an ldc instruction it doesn't match our pattern and we don't need it | |
if (node == null || node.opcode() != Opcodes.LDC) return -1; | |
//at this point we know it's an ldc and we can store its value for later use. | |
//we're parsing the value as a string as the jvm gets angry if we just cast | |
return Long.parseLong("" + ((LdcInsnNode) node).cst); | |
} | |
private static FieldInsnNode decodeField(AbstractInsnNode node) { | |
//check if its a getfield/getstatic/putfield, if not we don't want it | |
if (node == null || (node.opcode() != Opcodes.GETFIELD && node.opcode() != Opcodes.GETSTATIC | |
&& node.opcode() != Opcodes.PUTFIELD && node.opcode() != Opcodes.PUTSTATIC)) return null; | |
//store this as a FieldInsnNode as I cba to cast it all the time | |
return (FieldInsnNode) node; | |
} | |
public static List<FieldMult> getMults() { | |
return mults; | |
} | |
} |
Cuck
nice
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
very good