Issues #944,#991,#939 AS3 obfuscated switch read fix, wrong constants indices fix

This commit is contained in:
Jindra Petřík
2015-07-05 12:53:03 +02:00
parent 68cd20fa9c
commit cbbdded296
11 changed files with 154 additions and 36 deletions

View File

@@ -553,7 +553,7 @@ public class ABC {
bodies = new ArrayList<>(bodies_count);
for (int i = 0; i < bodies_count; i++) {
ais.newDumpLevel("method_body", "method_body_info");
MethodBody mb = new MethodBody(null, null, null); // do not create Traits in constructor
MethodBody mb = new MethodBody(this, null, null, null); // do not create Traits in constructor
mb.method_info = ais.readU30("method_info");
mb.max_stack = ais.readU30("max_stack");
mb.max_regs = ais.readU30("max_regs");

View File

@@ -194,7 +194,9 @@ public class ABCInputStream implements AutoCloseable {
}
private int readU30Internal() throws IOException {
return (int) readU32Internal();
long u32 = readU32Internal();
//no bits above bit 30
return (int) (u32 & 0x3FFFFFFF);
}
public int readU30(String name) throws IOException {

View File

@@ -745,17 +745,59 @@ public class AVM2Code implements Cloneable {
return true;
}
/**
* Removes nonexistent indices to constants from instruction operands.
*
* @param constants
*/
public void removeWrongIndices(AVM2ConstantPool constants) {
for (AVM2Instruction ins : code) {
for (int i = 0; i < ins.definition.operands.length; i++) {
if (ins.definition.operands[i] == DAT_MULTINAME_INDEX && ins.operands[i] >= constants.constant_multiname.size()) {
ins.operands[i] = 0;
}
if (ins.definition.operands[i] == DAT_DOUBLE_INDEX && ins.operands[i] >= constants.constant_double.size()) {
ins.operands[i] = 0;
}
if (ins.definition.operands[i] == DAT_INT_INDEX && ins.operands[i] >= constants.constant_int.size()) {
ins.operands[i] = 0;
}
if (ins.definition.operands[i] == DAT_UINT_INDEX && ins.operands[i] >= constants.constant_uint.size()) {
ins.operands[i] = 0;
}
if (ins.definition.operands[i] == DAT_STRING_INDEX && ins.operands[i] >= constants.constant_string.size()) {
ins.operands[i] = 0;
}
}
}
}
public AVM2Code(ABCInputStream ais) throws IOException {
Map<Long, AVM2Instruction> codeMap = new TreeMap<>();
DumpInfo diParent = ais.dumpInfo;
List<Long> addresses = new ArrayList<>();
//Handle lookupswitches at the end - they can be invalid. Handle other instruction first so we can decide lookupswitch to be invalid based on other instructions inside it
//Flashplayer does not check casecount in lookupswitch instruction so the instruction can "be" long and over other instructions
List<Long> switchAddresses = new ArrayList<>();
int availableBytes = ais.available();
for (int i = 0; i < availableBytes; i++) {
codeMap.put((long) i, new AVM2Instruction(i, new NopIns(), new int[]{}));
}
long startPos = ais.getPosition();
addresses.add(startPos);
while (!addresses.isEmpty()) {
long address = addresses.remove(0);
boolean afterExit = false;
if (codeMap.containsKey(address)) {
loopaddr:
while (!addresses.isEmpty() || !switchAddresses.isEmpty()) {
long address;
boolean isSwitch = false;
if (!addresses.isEmpty()) {
address = addresses.remove(0);
} else {
address = switchAddresses.remove(0);
isSwitch = true;
}
if (codeMap.containsKey(address) && !(codeMap.get(address).definition instanceof NopIns)) {
continue;
}
if (address < startPos) // no jump outside block
@@ -769,19 +811,51 @@ public class AVM2Code implements Cloneable {
long startOffset = ais.getPosition();
int instructionCode = ais.read("instructionCode");
InstructionDefinition instr = instructionSet[instructionCode];
if (instr instanceof LookupSwitchIns) {
if (!isSwitch) {
switchAddresses.add(startOffset);
continue loopaddr;
} else {
isSwitch = false;
}
}
if (di != null) {
di.name = instr.instructionName;
}
if (instr != null) {
int[] actualOperands = null;
long beforeSwitchPos = ais.getPosition();
if (instructionCode == 0x1b) { // switch
int firstOperand = ais.readS24("default_offset");
int case_count = ais.readU30("case_count");
actualOperands = new int[case_count + 3];
actualOperands[0] = firstOperand;
actualOperands[1] = case_count;
for (int c = 0; c < case_count + 1; c++) {
actualOperands[2 + c] = ais.readS24("actualOperand");
long afterCasePos = ais.getPosition() + 3 * (case_count + 1);
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++) {
if (codeMap.containsKey(a) && (!(codeMap.get(a).definition instanceof NopIns))) {
invalidSwitch = true;
break;
}
}
long totalBytes = ais.getPosition() + ais.available();
//If the lookupswitch case_count are larger than available bytes, the lookupswitch is invalid (obfuscation)
if (afterCasePos > totalBytes) {
invalidSwitch = true;
}
if (invalidSwitch) {
continue loopaddr;
} else {
actualOperands = new int[case_count + 3];
actualOperands[0] = firstOperand;
actualOperands[1] = case_count;
for (int c = 0; c < case_count + 1; c++) {
actualOperands[2 + c] = ais.readS24("actualOperand");
}
}
} else {
if (instr.operands.length > 0) {
@@ -805,15 +879,37 @@ public class AVM2Code implements Cloneable {
}
}
if (!afterExit && (instr instanceof IfTypeIns)) {
AVM2Instruction ai = new AVM2Instruction(startOffset, instr, actualOperands);
long endOffset = ais.getPosition();
for (long p = startOffset; p < endOffset; p++) {
codeMap.put(p, ai);
}
ais.endDumpLevel(instr.instructionCode);
if ((instr instanceof IfTypeIns)) {
long target = ais.getPosition() + actualOperands[0];
addresses.add(target);
//addresses.add(ais.getPosition());
}
codeMap.put(startOffset, new AVM2Instruction(startOffset, instr, actualOperands));
ais.endDumpLevel(instr.instructionCode);
if (instr.isExitInstruction()) { //do not process jumps if there is return/throw instruction
afterExit = true;
}
/*if (instr instanceof JumpIns) {
long target = ais.getPosition() + actualOperands[0];
addresses.add(target);
continue loopaddr;
}
if (instr.isExitInstruction()) { //do not process jumps if there is return/throw instruction
continue loopaddr;
}
if (instr instanceof LookupSwitchIns) {
addresses.add(beforeSwitchPos + actualOperands[0]);
for (int c = 2; c < actualOperands.length; c++) {
addresses.add(beforeSwitchPos + actualOperands[c]);
}
continue loopaddr;
}*/
} else {
ais.endDumpLevel();
break; // Unknown instructions are ignored (Some of the obfuscators add unknown instructions)
@@ -825,7 +921,13 @@ public class AVM2Code implements Cloneable {
ais.endDumpLevelUntil(diParent);
}
}
code.addAll(codeMap.values());
AVM2Instruction prev = null;
for (AVM2Instruction ins : codeMap.values()) {
if (prev != ins) {
code.add(ins);
}
prev = ins;
}
}
public void compact() {
@@ -1819,6 +1921,9 @@ public class AVM2Code implements Cloneable {
public void fixJumps(MethodBody body) {
buildCache();
if (code.isEmpty()) {
return;
}
AVM2Instruction lastInstuction = code.get(code.size() - 1);
final long endOffset = lastInstuction.offset + lastInstuction.getBytesLength();
updateOffsets(new OffsetUpdater() {

View File

@@ -230,7 +230,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
break;
}
}
} catch (EmptyStackException | TranslateException | InterruptedException ex) {
} catch (Exception ex) {
//ignore
}
}

View File

@@ -373,9 +373,7 @@ public class AVM2DeobfuscatorSimple implements SWFDecompilerListener {
break;
}
}
} catch (EmptyStackException | TranslateException | InterruptedException ex) {
//result.idx = -1;
//result.isIf = false;
} catch (Exception ex) {
//ignore
}
}

View File

@@ -93,11 +93,14 @@ public class InstructionDefinition implements Serializable {
protected FullMultinameAVM2Item resolveMultiname(TranslateStack stack, AVM2ConstantPool constants, int multinameIndex, AVM2Instruction ins) {
GraphTargetItem ns = null;
GraphTargetItem name = null;
if (constants.getMultiname(multinameIndex).needsName()) {
name = stack.pop();
}
if (constants.getMultiname(multinameIndex).needsNs()) {
ns = stack.pop();
if (multinameIndex < constants.constant_multiname.size()) {
if (constants.getMultiname(multinameIndex).needsName()) {
name = stack.pop();
}
if (constants.getMultiname(multinameIndex).needsNs()) {
ns = stack.pop();
}
}
return new FullMultinameAVM2Item(ins, multinameIndex, name, ns);
}

View File

@@ -1665,7 +1665,7 @@ public class AVM2SourceGenerator implements SourceGenerator {
int mindex;
if (!isInterface) {
MethodBody mbody = new MethodBody();
MethodBody mbody = new MethodBody(abc);
if (needsActivation) {
int slotId = 1;
@@ -2146,7 +2146,7 @@ public class AVM2SourceGenerator implements SourceGenerator {
Trait[] traitArr = generateTraitsPhase1(null, null, true, localData, commands, si.traits, class_index);
generateTraitsPhase2(new ArrayList<DottedChain>(), null/*FIXME*/, commands, traitArr, new ArrayList<Integer>(), localData);
MethodInfo mi = new MethodInfo(new int[0], 0, 0, 0, new ValueKind[0], new int[0]);
MethodBody mb = new MethodBody();
MethodBody mb = new MethodBody(abc);
mb.method_info = abc.addMethodInfo(mi);
mb.setCode(new AVM2Code());
List<AVM2Instruction> mbCode = mb.getCode().code;

View File

@@ -86,16 +86,21 @@ public final class MethodBody implements Cloneable {
@Internal
public transient Throwable convertException;
public MethodBody() {
@Internal
private ABC abc;
public MethodBody(ABC abc) {
this.traits = new Traits();
this.codeBytes = new byte[0];
this.exceptions = new ABCException[0];
this.abc = abc;
}
public MethodBody(Traits traits, byte[] codeBytes, ABCException[] exceptions) {
public MethodBody(ABC abc, Traits traits, byte[] codeBytes, ABCException[] exceptions) {
this.traits = traits;
this.codeBytes = codeBytes;
this.exceptions = exceptions;
this.abc = abc;
}
public synchronized void setCodeBytes(byte codeBytes[]) {
@@ -117,6 +122,7 @@ public final class MethodBody implements Cloneable {
try {
ABCInputStream ais = new ABCInputStream(new MemoryInputStream(codeBytes));
avm2Code = new AVM2Code(ais);
avm2Code.removeWrongIndices(abc.constants);
} catch (UnknownInstructionCode | IOException ex) {
avm2Code = new AVM2Code();
logger.log(Level.SEVERE, null, ex);

View File

@@ -289,14 +289,18 @@ public class TraitClass extends Trait implements TraitWithSlot {
|| (ins.definition instanceof AsTypeIns)) {
int m = ins.operands[0];
if (m != 0) {
parseImportsUsagesFromMultiname(abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames);
if (m < abc.constants.constant_multiname.size()) {
parseImportsUsagesFromMultiname(abc, imports, uses, abc.constants.getMultiname(m), ignorePackage, fullyQualifiedNames);
}
}
} else {
for (int k = 0; k < ins.definition.operands.length; k++) {
if (ins.definition.operands[k] == AVM2Code.DAT_MULTINAME_INDEX) {
int multinameIndex = ins.operands[k];
parseUsagesFromMultiname(abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames);
if (multinameIndex < abc.constants.constant_multiname.size()) {
parseUsagesFromMultiname(abc, imports, uses, abc.constants.getMultiname(multinameIndex), ignorePackage, fullyQualifiedNames);
}
}
}
}