Skip to content

Commit

Permalink
fix: improve filled array detection
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Feb 18, 2019
1 parent 5cee498 commit 389caf1
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.utils.InsnUtils;
Expand Down Expand Up @@ -115,6 +113,7 @@ public void mergeName(InsnArg arg) {
}
}

@Override
public RegisterArg duplicate() {
return duplicate(getRegNum(), sVar);
}
Expand All @@ -130,8 +129,6 @@ public RegisterArg duplicate(int regNum, @Nullable SSAVar sVar) {

/**
* Return constant value from register assign or null if not constant
*
* @return LiteralArg, String or ArgType
*/
public Object getConstValue(DexNode dex) {
InsnNode parInsn = getAssignInsn();
Expand All @@ -149,22 +146,19 @@ public InsnNode getAssignInsn() {
return sVar.getAssign().getParentInsn();
}

public InsnNode getPhiAssignInsn() {
PhiInsn usePhi = sVar.getUsedInPhi();
if (usePhi != null) {
return usePhi;
}
InsnNode parent = sVar.getAssign().getParentInsn();
if (parent != null && parent.getType() == InsnType.PHI) {
return parent;
}
return null;
}

public boolean equalRegister(RegisterArg arg) {
return regNum == arg.regNum;
}

public boolean sameRegAndSVar(InsnArg arg) {
if (!arg.isRegister()) {
return false;
}
RegisterArg reg = (RegisterArg) arg;
return regNum == reg.getRegNum()
&& Objects.equals(sVar, reg.getSVar());
}

public boolean equalRegisterAndType(RegisterArg arg) {
return regNum == arg.regNum && type.equals(arg.type);
}
Expand Down
118 changes: 89 additions & 29 deletions jadx-core/src/main/java/jadx/core/dex/visitors/ReSugarCode.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jadx.core.dex.visitors;

import java.util.List;
import java.util.stream.Collectors;

import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
Expand All @@ -21,13 +22,18 @@
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.InsnWrapArg;
import jadx.core.dex.instructions.args.LiteralArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.DexNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.InsnList;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxException;

@JadxVisitor(
Expand Down Expand Up @@ -56,65 +62,119 @@ public void visit(MethodNode mth) throws JadxException {
List<InsnNode> instructions = block.getInstructions();
int size = instructions.size();
for (int i = 0; i < size; i++) {
InsnNode replacedInsn = process(mth, instructions, i, remover);
if (replacedInsn != null) {
instructions.set(i, replacedInsn);
}
process(mth, instructions, i, remover);
}
remover.perform();
}
}

private static InsnNode process(MethodNode mth, List<InsnNode> instructions, int i, InstructionRemover remover) {
private static void process(MethodNode mth, List<InsnNode> instructions, int i, InstructionRemover remover) {
InsnNode insn = instructions.get(i);
if (insn.contains(AFlag.REMOVE)) {
return;
}
switch (insn.getType()) {
case NEW_ARRAY:
return processNewArray(mth, instructions, i, remover);
processNewArray(mth, instructions, i, remover);
break;

case SWITCH:
processEnumSwitch(mth, (SwitchNode) insn);
return null;
break;

default:
return null;
break;
}
}

/**
* Replace new array and sequence of array-put to new filled-array instruction.
*/
private static InsnNode processNewArray(MethodNode mth, List<InsnNode> instructions, int i,
InstructionRemover remover) {
private static void processNewArray(MethodNode mth, List<InsnNode> instructions, int i,
InstructionRemover remover) {
NewArrayNode newArrayInsn = (NewArrayNode) instructions.get(i);
InsnArg arg = newArrayInsn.getArg(0);
if (!arg.isLiteral()) {
return null;
InsnArg arrLenArg = newArrayInsn.getArg(0);
if (!arrLenArg.isLiteral()) {
return;
}
int len = (int) ((LiteralArg) arg).getLiteral();
int size = instructions.size();
if (len <= 0
|| i + len >= size
|| instructions.get(i + len).getType() != InsnType.APUT) {
return null;
int len = (int) ((LiteralArg) arrLenArg).getLiteral();
if (len == 0) {
return;
}
RegisterArg arrArg = newArrayInsn.getResult();
SSAVar ssaVar = arrArg.getSVar();
List<RegisterArg> useList = ssaVar.getUseList();
if (useList.size() < len) {
return;
}
// check sequential array put with increasing index
int putIndex = 0;
for (RegisterArg useArg : useList) {
InsnNode insn = useArg.getParentInsn();
if (checkPutInsn(mth, insn, arrArg, putIndex)) {
putIndex++;
} else {
break;
}
}
for (int j = 0; j < len; j++) {
InsnNode put = instructions.get(i + 1 + j);
if (put.getType() != InsnType.APUT) {
LOG.debug("Not a APUT in expected new filled array: {}, method: {}", put, mth);
return null;
if (putIndex != len) {
return;
}
List<InsnNode> arrPuts = useList.subList(0, len).stream().map(InsnArg::getParentInsn).collect(Collectors.toList());
// check that all puts in current block
for (InsnNode arrPut : arrPuts) {
int index = InsnList.getIndex(instructions, arrPut);
if (index == -1) {
if (LOG.isDebugEnabled()) {
LOG.debug("TODO: APUT found in different block: {}, mth: {}", arrPut, mth);
}
return;
}
}

// checks complete, apply
ArgType arrType = newArrayInsn.getArrayType();
InsnNode filledArr = new FilledNewArrayNode(arrType.getArrayElement(), len);
filledArr.setResult(newArrayInsn.getResult().duplicate());
for (int j = 0; j < len; j++) {
InsnNode put = instructions.get(i + 1 + j);
filledArr.setResult(arrArg.duplicate());
for (InsnNode put : arrPuts) {
filledArr.addArg(put.getArg(2).duplicate());
remover.add(put);
remover.addAndUnbind(mth, put);
}
remover.addAndUnbind(mth, newArrayInsn);

int replaceIndex = InsnList.getIndex(instructions, Utils.last(arrPuts));
instructions.set(replaceIndex, filledArr);
}

private static boolean checkPutInsn(MethodNode mth, InsnNode insn, RegisterArg arrArg, int putIndex) {
if (insn == null || insn.getType() != InsnType.APUT) {
return false;
}
if (!arrArg.sameRegAndSVar(insn.getArg(0))) {
return false;
}
InsnArg indexArg = insn.getArg(1);
int index = -1;
if (indexArg.isLiteral()) {
index = (int) ((LiteralArg) indexArg).getLiteral();
} else if (indexArg.isRegister()) {
RegisterArg reg = (RegisterArg) indexArg;
index = getIntConst(reg.getConstValue(mth.dex()));
} else if (indexArg.isInsnWrap()) {
InsnNode constInsn = ((InsnWrapArg) indexArg).getWrapInsn();
index = getIntConst(InsnUtils.getConstValueByInsn(mth.dex(), constInsn));
}
return index == putIndex;
}

private static int getIntConst(Object value) {
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof Long) {
return ((Long) value).intValue();
}
return filledArr;
return -1;
}

private static void processEnumSwitch(MethodNode mth, SwitchNode insn) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public void setBlock(BlockNode block) {
public void add(InsnNode insn) {
toRemove.add(insn);
}
public void addAndUnbind(MethodNode mth, InsnNode insn) {
toRemove.add(insn);
unbindInsn(mth, insn);
}

public void perform() {
if (toRemove.isEmpty()) {
Expand All @@ -65,7 +69,7 @@ public static void unbindInsn(MethodNode mth, InsnNode insn) {
}
}
unbindResult(mth, insn);
insn.add(AFlag.INCONSISTENT_CODE);
insn.add(AFlag.REMOVE);
}

public static void fixUsedInPhiFlag(RegisterArg useReg) {
Expand Down
10 changes: 10 additions & 0 deletions jadx-core/src/main/java/jadx/core/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import java.util.Map;
import java.util.function.Function;

import org.jetbrains.annotations.Nullable;

import jadx.api.JadxDecompiler;
import jadx.core.codegen.CodeWriter;

Expand Down Expand Up @@ -174,4 +176,12 @@ public static Map<String, String> newConstStringMap(String... parameters) {
}
return Collections.unmodifiableMap(result);
}

@Nullable
public static <T> T last(List<T> list) {
if (list.isEmpty()) {
return null;
}
return list.get(list.size() - 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package jadx.tests.integration.arrays;

import org.junit.Test;

import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.IntegrationTest;

import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThat;

public class TestMultiDimArrayFill extends IntegrationTest {

public static class TestCls {

public static Obj test(int a, int b) {
return new Obj(
new int[][]{
new int[]{1},
new int[]{2},
{3},
new int[]{4, 5},
new int[0]
},
new int[]{a, a, a, a, b}
);
}

private static class Obj {
public Obj(int[][] ints, int[] ints2) {}
}
}

@Test
public void test() {
ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString();

assertThat(code, containsString("return new Obj("
+ "new int[][]{new int[]{1}, new int[]{2}, new int[]{3}, new int[]{4, 5}, new int[0]}, "
+ "new int[]{a, a, a, a, b});"));
}
}

0 comments on commit 389caf1

Please sign in to comment.