mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-06-05 06:15:04 +00:00
Issues #944,#991,#939 AS3 obfuscated switch read fix, wrong constants indices fix
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -230,7 +230,7 @@ public class AVM2DeobfuscatorRegisters extends AVM2DeobfuscatorSimple {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (EmptyStackException | TranslateException | InterruptedException ex) {
|
||||
} catch (Exception ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user