AS: P-code reading fixes, better deobfuscation

This commit is contained in:
Jindra Petřík
2015-07-06 17:54:57 +02:00
parent 94d3544fde
commit df36ce3548
6 changed files with 107 additions and 62 deletions

View File

@@ -777,7 +777,7 @@ public class AVM2Code implements Cloneable {
}
public AVM2Code(ABCInputStream ais, MethodBody body) throws IOException {
Map<Long, AVM2Instruction> codeMap = new TreeMap<>();
Map<Long, AVM2Instruction> codeMap = new HashMap<>();
DumpInfo diParent = ais.dumpInfo;
List<Long> addresses = new ArrayList<>();
//Do not add new jumps when processing these addresses (unreachable code,etc.)
@@ -814,19 +814,20 @@ public class AVM2Code implements Cloneable {
address = unAdresses.remove(0);
handleJumps = false;
}
if (codeMap.containsKey(address) && !(codeMap.get(address).definition instanceof NopIns)) {
continue;
}
if (address < startPos) // no jump outside block
{
continue;
}
boolean afterExit = false;
try {
ais.seek(address);
while (ais.available() > 0) {
DumpInfo di = ais.newDumpLevel("instruction", "instruction");
long startOffset = ais.getPosition();
if (codeMap.containsKey(startOffset) && !(codeMap.get(startOffset).definition instanceof NopIns)) {
continue loopaddr;
}
int instructionCode = ais.read("instructionCode");
InstructionDefinition instr = instructionSet[instructionCode];
if (instr instanceof LookupSwitchIns) {
@@ -842,7 +843,6 @@ public class AVM2Code implements Cloneable {
}
if (instr != null) {
int[] actualOperands = null;
long beforeSwitchPos = ais.getPosition();
if (instructionCode == 0x1b) { // switch
int firstOperand = ais.readS24("default_offset");
@@ -851,7 +851,7 @@ public class AVM2Code implements Cloneable {
boolean invalidSwitch = false;
//If there are already some instructions in the lookupswitch bytes, the lookupswitch is invalid (obfuscation)
for (long a = beforeSwitchPos; a < afterCasePos; a++) {
for (long a = startOffset; a < afterCasePos; a++) {
if (codeMap.containsKey(a) && (!(codeMap.get(a).definition instanceof NopIns))) {
invalidSwitch = true;
break;
@@ -899,40 +899,67 @@ public class AVM2Code implements Cloneable {
AVM2Instruction ai = new AVM2Instruction(startOffset, instr, actualOperands);
long endOffset = ais.getPosition();
boolean hasRoom = true;
for (long p = startOffset; p < endOffset; p++) {
if (codeMap.containsKey(p) && !(codeMap.get(p).definition instanceof NopIns)) {
hasRoom = false;
}
}
//There is no room for this instruction (it is invalid?)
if (!hasRoom) {
continue loopaddr;
}
for (long p = startOffset; p < endOffset; p++) {
codeMap.put(p, ai);
}
ais.endDumpLevel(instr.instructionCode);
if (handleJumps) {
if ((instr instanceof IfTypeIns)) {
if ((instr instanceof IfTypeIns)) {
if (handleJumps) {
long target = ais.getPosition() + actualOperands[0];
addresses.add(target);
//addresses.add(ais.getPosition());
} else {
actualOperands[0] = 0;
}
}
if (instr instanceof JumpIns) {
if (instr instanceof JumpIns) {
if (handleJumps) {
long target = ais.getPosition() + actualOperands[0];
addresses.add(target);
unAdresses.add(ais.getPosition());
continue loopaddr;
} else {
actualOperands[0] = 0;
}
}
if (instr.isExitInstruction()) { //do not process jumps if there is return/throw instruction
unAdresses.add(ais.getPosition());
continue loopaddr;
}
if (instr instanceof LookupSwitchIns) {
addresses.add(beforeSwitchPos + actualOperands[0]);
for (int c = 2; c < actualOperands.length; c++) {
addresses.add(beforeSwitchPos + actualOperands[c]);
}
if (instr.isExitInstruction()) { //do not process jumps if there is return/throw instruction
if (handleJumps) {
unAdresses.add(ais.getPosition());
continue loopaddr;
}
}
if (instr instanceof LookupSwitchIns) {
if (handleJumps) {
addresses.add(startOffset + actualOperands[0]);
for (int c = 2; c < actualOperands.length; c++) {
addresses.add(startOffset + actualOperands[c]);
}
unAdresses.add(ais.getPosition());
continue loopaddr;
} else {
int swlen = (int) (endOffset - startOffset);
actualOperands[0] = swlen;
for (int c = 2; c < actualOperands.length; c++) {
actualOperands[c] = swlen;
}
}
}
} else {
ais.endDumpLevel();
break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions)
@@ -945,7 +972,8 @@ public class AVM2Code implements Cloneable {
}
}
AVM2Instruction prev = null;
for (AVM2Instruction ins : codeMap.values()) {
for (int i = 0; i < availableBytes; i++) {
AVM2Instruction ins = codeMap.get((long) i);
if (prev != ins) {
code.add(ins);
}
@@ -1284,11 +1312,22 @@ public class AVM2Code implements Cloneable {
}
public int adr2pos(long address) throws ConvertException {
return adr2pos(address, false);
}
public int adr2pos(long address, boolean nearest) throws ConvertException {
if (!cacheActual) {
buildCache();
}
int ret = posCache.indexOf(address);
if (ret == -1) {
if (nearest) {
for (long a : posCache) {
if (a > address) {
return posCache.indexOf(a);
}
}
}
throw new ConvertException("Invalid jump to ofs" + Helper.formatAddress(address), -1);
}
return ret;
@@ -1417,7 +1456,7 @@ public class AVM2Code implements Cloneable {
}
public int fixAddrAfterDebugLine(int addr) throws ConvertException {
return pos2adr(fixIPAfterDebugLine(adr2pos(addr)));
return pos2adr(fixIPAfterDebugLine(adr2pos(addr, true)));
}
public ConvertOutput toSourceOutput(String path, GraphPart part, boolean processJumps, boolean isStatic, int scriptIndex, int classIndex, HashMap<Integer, GraphTargetItem> localRegs, TranslateStack stack, ScopeStack scopeStack, ABC abc, AVM2ConstantPool constants, List<MethodInfo> method_info, MethodBody body, int start, int end, HashMap<Integer, String> localRegNames, List<String> fullyQualifiedNames, boolean[] visited, HashMap<Integer, Integer> localRegAssigmentIps, HashMap<Integer, List<Integer>> refs) throws ConvertException, InterruptedException {
@@ -1940,7 +1979,11 @@ public class AVM2Code implements Cloneable {
//Faster, but not so universal
if ((ins.definition instanceof JumpIns) || (ins.definition instanceof IfTypeIns)) {
long target = ins.offset + ins.getBytesLength() + ins.operands[0];
ins.operands[0] = updater.updateOperandOffset(ins.offset, target, ins.operands[0]);
try {
ins.operands[0] = updater.updateOperandOffset(ins.offset, target, ins.operands[0]);
} catch (ConvertException cex) {
throw new ConvertException("Invalid offset (" + ins + ")", i);
}
}
}
ins.offset = updater.updateInstructionOffset(ins.offset);
@@ -1991,6 +2034,7 @@ public class AVM2Code implements Cloneable {
}
public void checkValidOffsets(MethodBody body) {
invalidateCache();
updateOffsets(new OffsetUpdater() {
@Override
@@ -2001,6 +2045,9 @@ public class AVM2Code implements Cloneable {
@Override
public int updateOperandOffset(long insAddr, long targetAddress, int offset) {
if (insAddr == -1) { //do not check exceptions
return offset;
}
adr2pos(targetAddress);
return offset;
}
@@ -2013,18 +2060,9 @@ public class AVM2Code implements Cloneable {
throw new IndexOutOfBoundsException();
}
//checkValidOffsets(body);
AVM2Instruction ins = code.get(pos);
final long remOffset = ins.offset;
int bc;
if (pos < code.size() - 1) {
// Checking the instuction length is not enough because in some
// obfuscated files the length of the lookupswitch is not the same
// as the difference of the offsets
bc = (int) (code.get(pos + 1).offset - remOffset);
} else {
bc = ins.getBytesLength();
}
int bc = ins.getBytesLength();
final int byteCount = bc;
updateOffsets(new OffsetUpdater() {
@@ -2230,7 +2268,7 @@ public class AVM2Code implements Cloneable {
new AVM2DeobfuscatorSimple().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, info, body);
new AVM2DeobfuscatorRegisters().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, info, body);
new AVM2DeobfuscatorJumps().deobfuscate(path, classIndex, isStatic, scriptIndex, abc, constants, trait, info, body);
body.getCode().checkValidOffsets(body); // todo: only for debugging. checkValidOffsets can be made private later
//body.getCode().checkValidOffsets(body); // todo: only for debugging. checkValidOffsets can be made private later
return 1;
}
}
@@ -2470,12 +2508,12 @@ public class AVM2Code implements Cloneable {
for (ABCException e : body.exceptions) {
pos++;
try {
visitCode(adr2pos(e.start), adr2pos(e.start) - 1, refs);
visitCode(adr2pos(e.start), -1, refs);
visitCode(adr2pos(e.target), adr2pos(e.end), refs);
visitCode(adr2pos(e.end), -pos, refs);
visitCode(adr2pos(e.start, true), adr2pos(e.start, true) - 1, refs);
visitCode(adr2pos(e.start, true), -1, refs);
visitCode(adr2pos(e.target), adr2pos(e.end, true), refs);
visitCode(adr2pos(e.end, true), -pos, refs);
} catch (ConvertException ex) {
logger.log(Level.FINE, null, ex);
logger.log(Level.SEVERE, "Visitcode error", ex);
}
}
return refs;
@@ -2708,9 +2746,9 @@ public class AVM2Code implements Cloneable {
restoreControlFlow(0, refs, visited2, appended);
for (ABCException e : body.exceptions) {
try {
restoreControlFlow(adr2pos(e.start), refs, visited2, appended);
restoreControlFlow(adr2pos(e.start, true), refs, visited2, appended);
restoreControlFlow(adr2pos(e.target), refs, visited2, appended);
restoreControlFlow(adr2pos(e.end), refs, visited2, appended);
restoreControlFlow(adr2pos(e.end, true), refs, visited2, appended);
} catch (ConvertException ex) {
logger.log(Level.FINE, null, ex);
}
@@ -2782,17 +2820,19 @@ public class AVM2Code implements Cloneable {
}*/
public void removeIgnored(AVM2ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException {
//System.err.println("removing ignored...");
for (int i = 0; i < code.size(); i++) {
if (code.get(i).ignored) {
removeInstruction(i, body);
i--;
}
}
//System.err.println("/ignored removed");
}
public int removeDeadCode(AVM2ConstantPool constants, Trait trait, MethodInfo info, MethodBody body) throws InterruptedException {
invalidateCache();
HashMap<Integer, List<Integer>> refs = visitCode(body);
int cnt = 0;
for (int i = code.size() - 1; i >= 0; i--) {
if (refs.get(i).isEmpty()) {
@@ -2808,8 +2848,7 @@ public class AVM2Code implements Cloneable {
AVM2Instruction ins = code.get(i);
if (ins.definition instanceof JumpIns) {
if (ins.operands[0] == 0) {
code.get(i).ignored = true;
//removeInstruction(i, body);
ins.ignored = true;
cnt++;
}
}

View File

@@ -83,6 +83,5 @@ public class AVM2DeobfuscatorJumps extends AVM2DeobfuscatorSimple {
}
removeUnreachableActions(body.getCode(), cpool, trait, minfo, body);
} while (found);
}
}

View File

@@ -153,7 +153,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
}
}
private int getFirstRegisterSetter(Reference<AVM2Instruction> assignment, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body, Set<Integer> ignoredRegisters, Set<Integer> ignoredGets) {
private int getFirstRegisterSetter(Reference<AVM2Instruction> assignment, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body, Set<Integer> ignoredRegisters, Set<Integer> ignoredGets) throws InterruptedException {
AVM2Code code = body.getCode();
Map<Integer, GraphTargetItem> ret = new HashMap<>();
@@ -165,7 +165,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
return visitCode(assignment, new HashSet<>(), new TranslateStack("deo"), classIndex, isStatic, body, scriptIndex, abc, code, 0, code.code.size() - 1, res, ignoredRegisters, ignoredGets);
}
private int visitCode(Reference<AVM2Instruction> assignment, Set<Integer> visited, TranslateStack stack, int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, ExecutionResult result, Set<Integer> ignored, Set<Integer> ignoredGets) {
private int visitCode(Reference<AVM2Instruction> assignment, Set<Integer> visited, TranslateStack stack, int classIndex, boolean isStatic, MethodBody body, int scriptIndex, ABC abc, AVM2Code code, int idx, int endIdx, ExecutionResult result, Set<Integer> ignored, Set<Integer> ignoredGets) throws InterruptedException {
List<GraphTargetItem> output = new ArrayList<>();
AVM2LocalData localData = newLocalData(scriptIndex, abc, abc.constants, body, isStatic, classIndex);
localData.localRegs.put(0, new NullAVM2Item(null));//this
@@ -295,7 +295,9 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
break;
}
}
} catch (Exception ex) {
} catch (InterruptedException ex) {
throw ex;
} catch (Throwable ex) {
//ignore
}
}

View File

@@ -185,10 +185,12 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
boolean result = false;
for (int i = 0; i < actions.code.size(); i++) {
AVM2Instruction action = actions.code.get(i);
if (action.definition instanceof JumpIns && action.operands[0] == 0) {
actions.removeInstruction(i, body);
i--;
result = true;
if (action.definition instanceof JumpIns) {
if (action.operands[0] == 0) {
actions.removeInstruction(i, body);
i--;
result = true;
}
}
}
return result;
@@ -418,7 +420,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
public void deobfuscate(String path, int classIndex, boolean isStatic, int scriptIndex, ABC abc, AVM2ConstantPool cpool, Trait trait, MethodInfo minfo, MethodBody body) throws InterruptedException {
AVM2Code code = body.getCode();
code.fixJumps(body);
//code.fixJumps(body);
removeUnreachableActions(code, cpool, trait, minfo, body);
removeObfuscationIfs(classIndex, isStatic, scriptIndex, abc, cpool, trait, minfo, body, new ArrayList<>());
removeZeroJumps(code, body);

View File

@@ -145,18 +145,18 @@ public class AVM2Graph extends Graph {
@Override
protected void checkGraph(List<GraphPart> allBlocks) {
for (ABCException ex : body.exceptions) {
int startIp = avm2code.adr2pos(ex.start);
int endIp = avm2code.adr2pos(ex.end);
int targetIp = avm2code.adr2pos(ex.target);
/*int startAddr = avm2code.adr2pos(ex.start);
int endAddr = avm2code.adr2pos(ex.end);
int targetIp = avm2code.adr2pos(ex.target);*/
GraphPart target = null;
for (GraphPart p : allBlocks) {
if (p.start == targetIp) {
if (avm2code.pos2adr(p.start) == ex.target) {
target = p;
break;
}
}
for (GraphPart p : allBlocks) {
if (p.start >= startIp && p.end <= endIp) {
if (avm2code.pos2adr(p.start) >= ex.start && avm2code.pos2adr(p.end) <= ex.end) {
p.throwParts.add(target);
target.refs.add(p);
}

View File

@@ -141,8 +141,8 @@ public final class MethodBody implements Cloneable {
public List<Integer> getExceptionEntries() {
List<Integer> ret = new ArrayList<>();
for (ABCException e : exceptions) {
ret.add(getCode().adr2pos(e.start));
ret.add(getCode().adr2pos(e.end));
//ret.add(getCode().adr2pos(e.start));
//ret.add(getCode().adr2pos(e.end));
ret.add(getCode().adr2pos(e.target));
}
return ret;
@@ -344,8 +344,11 @@ public final class MethodBody implements Cloneable {
if (Configuration.autoDeobfuscate.get()) {
try {
body.getCode().removeTraps(constants, trait, method_info.get(this.method_info), body, abc, scriptIndex, classIndex, isStatic, path);
} catch (InterruptedException ex) {
throw ex;
} catch (Throwable ex) {
logger.log(Level.SEVERE, "Error during deobfuscation: " + path, ex);
//ignore
return this;
}
}