Skip to content

Commit 581a6e7

Browse files
committed
Split handling of fill-array-data instructions into two phases: first
the command transforming to jimple instructions and later a second phase in DexFillArrayDataTransformer that recovers the array data types and allies it to the values.
1 parent 249f856 commit 581a6e7

File tree

4 files changed

+174
-94
lines changed

4 files changed

+174
-94
lines changed

src/main/java/soot/dexpler/DexBody.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import java.util.List;
4141
import java.util.Set;
4242
import java.util.TreeMap;
43-
import java.util.concurrent.atomic.AtomicReference;
4443

4544
import org.jf.dexlib2.analysis.ClassPath;
4645
import org.jf.dexlib2.analysis.ClassPathResolver;
@@ -771,6 +770,7 @@ public Body jimplify(Body b, SootMethod m) {
771770
DeadAssignmentEliminator.v().transform(jBody);
772771
UnconditionalBranchFolder.v().transform(jBody);
773772
}
773+
DexFillArrayDataTransformer.v().transform(jBody);
774774

775775
TypeAssigner.v().transform(jBody);
776776

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package soot.dexpler;
2+
3+
/*-
4+
* #%L
5+
* Soot - a J*va Optimization Framework
6+
* %%
7+
* Copyright (C) 2012 Michael Markert, Frank Hartmann
8+
*
9+
* (c) 2012 University of Luxembourg - Interdisciplinary Centre for
10+
* Security Reliability and Trust (SnT) - All rights reserved
11+
* Alexandre Bartel
12+
*
13+
* %%
14+
* This program is free software: you can redistribute it and/or modify
15+
* it under the terms of the GNU Lesser General Public License as
16+
* published by the Free Software Foundation, either version 2.1 of the
17+
* License, or (at your option) any later version.
18+
*
19+
* This program is distributed in the hope that it will be useful,
20+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
* GNU General Lesser Public License for more details.
23+
*
24+
* You should have received a copy of the GNU General Lesser Public
25+
* License along with this program. If not, see
26+
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
27+
* #L%
28+
*/
29+
30+
import java.util.Iterator;
31+
import java.util.LinkedList;
32+
import java.util.List;
33+
import java.util.Map;
34+
import java.util.stream.Collectors;
35+
36+
import org.slf4j.Logger;
37+
import org.slf4j.LoggerFactory;
38+
39+
import soot.ArrayType;
40+
import soot.Body;
41+
import soot.BodyTransformer;
42+
import soot.G;
43+
import soot.Local;
44+
import soot.Type;
45+
import soot.Unit;
46+
import soot.Value;
47+
import soot.dexpler.instructions.FillArrayDataInstruction;
48+
import soot.dexpler.typing.UntypedConstant;
49+
import soot.jimple.ArrayRef;
50+
import soot.jimple.AssignStmt;
51+
import soot.jimple.InvokeExpr;
52+
import soot.jimple.NewArrayExpr;
53+
import soot.toolkits.graph.ExceptionalUnitGraph;
54+
import soot.toolkits.graph.ExceptionalUnitGraphFactory;
55+
import soot.toolkits.scalar.LocalDefs;
56+
57+
/**
58+
* If Dalvik bytecode can contain <code>fill-array-data</code> instructions that can fill an array with data elements we only
59+
* know the element size of.
60+
*
61+
* Therefore when processing such instructions in {@link FillArrayDataInstruction} we don't know the exact type of the data
62+
* that is loaded. Because of (conditional) branches in the code, identifying the type is not always possible at that stage.
63+
* Instead {@link UntypedConstant} constants are used. These constants are processed by this transformer and get their final
64+
* type.
65+
*
66+
*
67+
* @author Jan Peter Stotz
68+
*
69+
*/
70+
public class DexFillArrayDataTransformer extends BodyTransformer {
71+
private static final Logger logger = LoggerFactory.getLogger(DexFillArrayDataTransformer.class);
72+
73+
public static DexFillArrayDataTransformer v() {
74+
return new DexFillArrayDataTransformer();
75+
}
76+
77+
protected void internalTransform(final Body body, String phaseName, Map<String, String> options) {
78+
final ExceptionalUnitGraph g = ExceptionalUnitGraphFactory.createExceptionalUnitGraph(body, DalvikThrowAnalysis.v());
79+
final LocalDefs defs = G.v().soot_toolkits_scalar_LocalDefsFactory().newLocalDefs(g);
80+
81+
for (Iterator<Unit> unitIt = body.getUnits().snapshotIterator(); unitIt.hasNext();) {
82+
Unit u = unitIt.next();
83+
if (!(u instanceof AssignStmt)) {
84+
continue;
85+
}
86+
AssignStmt ass = (AssignStmt) u;
87+
Value rightOp = ass.getRightOp();
88+
if (rightOp instanceof UntypedConstant) {
89+
Value left = ass.getLeftOp();
90+
if (left instanceof ArrayRef) {
91+
ArrayRef leftArray = (ArrayRef) left;
92+
93+
Local l = (Local) leftArray.getBase();
94+
List<Unit> assDefs = defs.getDefsOfAt(l, ass);
95+
List<Type> arrayTypes = new LinkedList<>();
96+
for (Unit d : assDefs) {
97+
if (d instanceof AssignStmt) {
98+
AssignStmt arrayAssign = (AssignStmt) d;
99+
Value source = arrayAssign.getRightOp();
100+
if (source instanceof NewArrayExpr) {
101+
NewArrayExpr newArray = (NewArrayExpr) source;
102+
arrayTypes.add(newArray.getBaseType());
103+
continue;
104+
}
105+
if (source instanceof InvokeExpr) {
106+
InvokeExpr invExpr = (InvokeExpr) source;
107+
Type aType = invExpr.getMethodRef().getReturnType();
108+
if (!(aType instanceof ArrayType)) {
109+
throw new InternalError("Failed to identify the array type. The identified method invocation "
110+
+ "does not return an array type. Invocation: " + invExpr.getMethodRef());
111+
}
112+
arrayTypes.add(((ArrayType) aType).getArrayElementType());
113+
continue;
114+
}
115+
throw new InternalError("Unsupported array definition statement: " + d);
116+
}
117+
}
118+
if (arrayTypes.isEmpty()) {
119+
throw new InternalError("Failed to determine the array type ");
120+
}
121+
if (arrayTypes.size() > 1) {
122+
arrayTypes = arrayTypes.stream().distinct().collect(Collectors.toList());
123+
if (arrayTypes.size() > 1) {
124+
logger.warn("Found multiple possible array types, using first ignoreing the others: {}", arrayTypes);
125+
}
126+
}
127+
128+
// We found the array type, now convert the untyped constant value to it's final type
129+
Type elementType = arrayTypes.get(0);
130+
Value constant = ass.getRightOp();
131+
UntypedConstant untyped = (UntypedConstant) constant;
132+
ass.setRightOp(untyped.defineType(elementType));
133+
}
134+
}
135+
}
136+
}
137+
138+
}

src/main/java/soot/dexpler/instructions/FillArrayDataInstruction.java

+26-93
Original file line numberDiff line numberDiff line change
@@ -27,41 +27,35 @@
2727
* #L%
2828
*/
2929

30-
import java.util.HashSet;
3130
import java.util.List;
32-
import java.util.Set;
3331

3432
import org.jf.dexlib2.iface.instruction.Instruction;
3533
import org.jf.dexlib2.iface.instruction.formats.ArrayPayload;
36-
import org.jf.dexlib2.iface.instruction.formats.Instruction22c;
3734
import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
38-
import org.jf.dexlib2.iface.reference.TypeReference;
3935
import org.slf4j.Logger;
4036
import org.slf4j.LoggerFactory;
4137

42-
import soot.ArrayType;
43-
import soot.BooleanType;
44-
import soot.ByteType;
45-
import soot.CharType;
46-
import soot.DoubleType;
47-
import soot.FloatType;
48-
import soot.IntType;
4938
import soot.Local;
50-
import soot.LongType;
51-
import soot.ShortType;
52-
import soot.Type;
5339
import soot.dexpler.DexBody;
54-
import soot.dexpler.DexType;
40+
import soot.dexpler.DexFillArrayDataTransformer;
41+
import soot.dexpler.typing.UntypedConstant;
42+
import soot.dexpler.typing.UntypedIntOrFloatConstant;
43+
import soot.dexpler.typing.UntypedLongOrDoubleConstant;
5544
import soot.jimple.ArrayRef;
5645
import soot.jimple.AssignStmt;
57-
import soot.jimple.DoubleConstant;
58-
import soot.jimple.FloatConstant;
46+
import soot.jimple.Constant;
5947
import soot.jimple.IntConstant;
6048
import soot.jimple.Jimple;
61-
import soot.jimple.LongConstant;
62-
import soot.jimple.NumericConstant;
6349
import soot.jimple.Stmt;
6450

51+
/**
52+
* Converts <code>fill-array-data</code> instructions and associated data blocks into a series of assignment instructions
53+
* (one for each array index the data block contains a value).
54+
*
55+
* As the data block contains untyped data, only the number of bytes per element is known. Recovering the array type at the
56+
* stage this class is used on would require a detailed analysis on the dex code. Therefore we save the data elements as
57+
* {@link UntypedConstant} and later use {@link DexFillArrayDataTransformer} to convert the values to their final type.
58+
*/
6559
public class FillArrayDataInstruction extends PseudoInstruction {
6660
private static final Logger logger = LoggerFactory.getLogger(FillArrayDataInstruction.class);
6761

@@ -95,13 +89,11 @@ public void jimplify(DexBody body) {
9589
List<Number> elements = arrayTable.getArrayElements();
9690
int numElements = elements.size();
9791

92+
int elementsWidth = arrayTable.getElementWidth();
9893
Stmt firstAssign = null;
9994
for (int i = 0; i < numElements; i++) {
10095
ArrayRef arrayRef = Jimple.v().newArrayRef(arrayReference, IntConstant.v(i));
101-
NumericConstant element = getArrayElement(elements.get(i), body, destRegister);
102-
if (element == null) {
103-
break;
104-
}
96+
Constant element = getArrayElement(elements.get(i), elementsWidth);
10597
AssignStmt assign = Jimple.v().newAssignStmt(arrayRef, element);
10698
addTags(assign);
10799
body.add(assign);
@@ -110,6 +102,8 @@ public void jimplify(DexBody body) {
110102
}
111103
}
112104
if (firstAssign == null) { // if numElements == 0. Is it possible?
105+
logger.warn("No assign statements created for array at address 0x{} - empty array data section?",
106+
Integer.toHexString(targetAddress));
113107
firstAssign = Jimple.v().newNopStmt();
114108
body.add(firstAssign);
115109
}
@@ -122,80 +116,19 @@ public void jimplify(DexBody body) {
122116

123117
}
124118

125-
private NumericConstant getArrayElement(Number element, DexBody body, int arrayRegister) {
126-
127-
List<DexlibAbstractInstruction> instructions = body.instructionsBefore(this);
128-
Set<Integer> usedRegisters = new HashSet<Integer>();
129-
usedRegisters.add(arrayRegister);
130-
131-
Type elementType = null;
132-
Outer: for (DexlibAbstractInstruction i : instructions) {
133-
if (usedRegisters.isEmpty()) {
134-
break;
135-
}
136-
137-
for (int reg : usedRegisters) {
138-
if (i instanceof NewArrayInstruction) {
139-
NewArrayInstruction newArrayInstruction = (NewArrayInstruction) i;
140-
Instruction22c instruction22c = (Instruction22c) newArrayInstruction.instruction;
141-
if (instruction22c.getRegisterA() == reg) {
142-
ArrayType arrayType = (ArrayType) DexType.toSoot((TypeReference) instruction22c.getReference());
143-
elementType = arrayType.getElementType();
144-
break Outer;
145-
}
146-
}
147-
}
148-
149-
// // look for obsolete registers
150-
// for (int reg : usedRegisters) {
151-
// if (i.overridesRegister(reg)) {
152-
// usedRegisters.remove(reg);
153-
// break; // there can't be more than one obsolete
154-
// }
155-
// }
156-
157-
// look for new registers
158-
for (int reg : usedRegisters) {
159-
int newRegister = i.movesToRegister(reg);
160-
if (newRegister != -1) {
161-
usedRegisters.add(newRegister);
162-
usedRegisters.remove(reg);
163-
break; // there can't be more than one new
164-
}
165-
}
119+
private Constant getArrayElement(Number element, int elementsWidth) {
120+
if (elementsWidth == 2) {
121+
// For size = 2 the only possible array type is short[]
122+
return IntConstant.v(element.shortValue());
166123
}
167124

168-
if (elementType == null) {
169-
// throw new InternalError("Unable to find array type to type array elements!");
170-
logger.warn("Unable to find array type to type array elements! Array was not defined! (obfuscated bytecode?)");
171-
return null;
172-
}
173-
174-
NumericConstant value;
175-
176-
if (elementType instanceof BooleanType) {
177-
value = IntConstant.v(element.intValue());
178-
IntConstant ic = (IntConstant) value;
179-
if (ic.value != 0) {
180-
value = IntConstant.v(1);
181-
}
182-
} else if (elementType instanceof ByteType) {
183-
value = IntConstant.v(element.byteValue());
184-
} else if (elementType instanceof CharType || elementType instanceof ShortType) {
185-
value = IntConstant.v(element.shortValue());
186-
} else if (elementType instanceof DoubleType) {
187-
value = DoubleConstant.v(Double.longBitsToDouble(element.longValue()));
188-
} else if (elementType instanceof FloatType) {
189-
value = FloatConstant.v(Float.intBitsToFloat(element.intValue()));
190-
} else if (elementType instanceof IntType) {
191-
value = IntConstant.v(element.intValue());
192-
} else if (elementType instanceof LongType) {
193-
value = LongConstant.v(element.longValue());
194-
} else {
195-
throw new RuntimeException("Invalid Array Type occured in FillArrayDataInstruction: " + elementType);
125+
if (elementsWidth <= 4) {
126+
// can be array of int, char, boolean, float
127+
return UntypedIntOrFloatConstant.v(element.intValue());
196128
}
197-
return value;
198129

130+
// can be array of long or double
131+
return UntypedLongOrDoubleConstant.v(element.longValue());
199132
}
200133

201134
@Override

src/main/java/soot/dexpler/typing/UntypedIntOrFloatConstant.java

+9
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,22 @@ public IntConstant toIntConstant() {
7070
return IntConstant.v(value);
7171
}
7272

73+
public IntConstant toBooleanConstant() {
74+
if (value != 0) {
75+
return IntConstant.v(1);
76+
}
77+
return IntConstant.v(value);
78+
}
79+
7380
@Override
7481
public Value defineType(Type t) {
7582
if (t instanceof FloatType) {
7683
return this.toFloatConstant();
7784
} else if (t instanceof IntType || t instanceof CharType || t instanceof BooleanType || t instanceof ByteType
7885
|| t instanceof ShortType) {
7986
return this.toIntConstant();
87+
} else if (t instanceof BooleanType) {
88+
return toBooleanConstant();
8089
} else {
8190
if (value == 0 && t instanceof RefLikeType) {
8291
return NullConstant.v();

0 commit comments

Comments
 (0)