/* Soot - a J*va Optimization Framework
* Copyright (C) 2006 Nomair A. Naeem
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package soot.dava.toolkits.base.AST.interProcedural;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.LongType;
import soot.PrimType;
import soot.ShortType;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Type;
import soot.Value;
import soot.dava.DavaBody;
import soot.dava.DecompilationException;
import soot.dava.internal.AST.ASTNode;
import soot.dava.toolkits.base.AST.traversals.AllDefinitionsFinder;
import soot.jimple.DefinitionStmt;
import soot.jimple.DoubleConstant;
import soot.jimple.FieldRef;
import soot.jimple.FloatConstant;
import soot.jimple.IntConstant;
import soot.jimple.LongConstant;
import soot.jimple.NumericConstant;
import soot.tagkit.DoubleConstantValueTag;
import soot.tagkit.FloatConstantValueTag;
import soot.tagkit.IntegerConstantValueTag;
import soot.tagkit.LongConstantValueTag;
import soot.util.Chain;
/*
* Deemed important because of obfuscation techniques which add crazy
* control flow under some condition which is never executed because
* it uses some field which is always false!!
*
* Goal:
* Prove that a field is never assigned a value or that if it is assigned a value
* we can statically tell this value
*
*
*/
public class ConstantFieldValueFinder {
public final boolean DEBUG = false;
public static String combiner = "_$p$g_";
HashMap<String, SootField> classNameFieldNameToSootFieldMapping = new HashMap<String, SootField>();
HashMap<String, ArrayList> fieldToValues = new HashMap<String, ArrayList>();
HashMap<String, Object> primTypeFieldValueToUse = new HashMap<String, Object>();
Chain appClasses;
public ConstantFieldValueFinder(Chain classes){
appClasses = classes;
debug("ConstantFieldValueFinder -- applyAnalyses","computing Method Summaries");
computeFieldToValuesAssignedList();
valuesForPrimTypeFields();
}
/*
* The hashMap returned contains a mapping of
* class + combiner + field ----> Double/Float/Long/Integer
* if there is no mapping for a particular field then that means we couldnt detect a constant value for it
*/
public HashMap<String, Object> getFieldsWithConstantValues(){
return primTypeFieldValueToUse;
}
public HashMap<String, SootField> getClassNameFieldNameToSootFieldMapping(){
return classNameFieldNameToSootFieldMapping;
}
/*
* This method gives values to all the fields in all the classes if they can be determined statically
* We only care about fields which have primitive types
*/
private void valuesForPrimTypeFields(){
//go through all the classes
Iterator classIt = appClasses.iterator();
while(classIt.hasNext()){
SootClass s = (SootClass) classIt.next();
debug("\nvaluesforPrimTypeFields","Processing class "+s.getName());
String declaringClass = s.getName();
Iterator fieldIt = s.getFields().iterator();
while(fieldIt.hasNext()){
SootField f = (SootField)fieldIt.next();
String fieldName = f.getName();
Type fieldType = f.getType();
if(! (fieldType instanceof PrimType ) )
continue;
String combined = declaringClass + combiner + fieldName;
classNameFieldNameToSootFieldMapping.put(combined,f);
Object value=null;
//check for constant value tags
if(fieldType instanceof DoubleType && f.hasTag("DoubleConstantValueTag")){
double val = ((DoubleConstantValueTag)f.getTag("DoubleConstantValueTag")).getDoubleValue();
value = new Double(val);
}
else if (fieldType instanceof FloatType && f.hasTag("FloatConstantValueTag")){
float val = ((FloatConstantValueTag)f.getTag("FloatConstantValueTag")).getFloatValue();
value = new Float(val);
}
else if (fieldType instanceof LongType && f.hasTag("LongConstantValueTag")){
long val = ((LongConstantValueTag)f.getTag("LongConstantValueTag")).getLongValue();
value = new Long(val);
}
else if (fieldType instanceof CharType && f.hasTag("IntegerConstantValueTag")){
int val = ((IntegerConstantValueTag)f.getTag("IntegerConstantValueTag")).getIntValue();
value = new Integer(val);
}
else if (fieldType instanceof BooleanType && f.hasTag("IntegerConstantValueTag")){
int val = ((IntegerConstantValueTag)f.getTag("IntegerConstantValueTag")).getIntValue();
if (val ==0)
value = new Boolean(false);
else
value = new Boolean(true);
}
else if ( (fieldType instanceof IntType || fieldType instanceof ByteType || fieldType instanceof ShortType) &&
f.hasTag("IntegerConstantValueTag")){
int val = ((IntegerConstantValueTag)f.getTag("IntegerConstantValueTag")).getIntValue();
value = new Integer(val);
}
//if there was a constant value tag we have its value now
if(value != null){
debug("TAGGED value found for field: "+combined);
primTypeFieldValueToUse.put(combined,value);
//continue with next field
continue;
}
//see if the field was never assigned in which case it gets default values
Object temp = fieldToValues.get(combined);
if(temp == null){
//no value list found is good
//add default value to primTypeFieldValueToUse hashmap
if(fieldType instanceof DoubleType )
value = new Double(0);
else if (fieldType instanceof FloatType )
value = new Float(0);
else if (fieldType instanceof LongType )
value = new Long(0);
else if (fieldType instanceof BooleanType)
value = new Boolean(false);
else if ( (fieldType instanceof IntType || fieldType instanceof ByteType ||
fieldType instanceof ShortType) || fieldType instanceof CharType){
value = new Integer(0);
}
else
throw new DecompilationException("Unknown primitive type...please report to developer");
primTypeFieldValueToUse.put(combined,value);
debug("DEFAULT value for field: "+combined);
//continue with next field
continue;
}
//havent got a tag with value and havent use default since SOME method did define the field atleast once
//there was some value assigned!!!!!!!!!
debug("CHECKING USER ASSIGNED VALUES FOR: "+combined);
ArrayList values = (ArrayList)temp;
//check if they are all constants and that too the same constant
Iterator it = values.iterator();
NumericConstant tempConstant = null;
while(it.hasNext()){
Value val = (Value)it.next();
if(! (val instanceof NumericConstant)){
tempConstant=null;
debug("Not numeric constant hence giving up");
break;
}
if(tempConstant == null){
tempConstant = (NumericConstant)val;
}
else{
//check that this value is the same as previous
if( ! tempConstant.equals(val)){
tempConstant = null;
break;
}
}
}
if(tempConstant == null){
//continue with next field cant do anything about this one
continue;
}
//agreed on a unique constant value
/*
* Since these are fields are we are doing CONTEXT INSENSITIVE
* WE need to make sure that the agreed unique constant value is the default value
*
* I KNOW IT SUCKS BUT HEY WHAT CAN I DO!!!
*/
if(tempConstant instanceof LongConstant){
Long tempVal = new Long( ((LongConstant)tempConstant).value );
if(tempVal.compareTo(new Long(0)) ==0)
primTypeFieldValueToUse.put(combined,tempVal);
else
debug("Not assigning the agreed value since that is not the default value for "+combined);
}
else if(tempConstant instanceof DoubleConstant){
Double tempVal = new Double( ((DoubleConstant)tempConstant).value );
if(tempVal.compareTo(new Double(0)) ==0)
primTypeFieldValueToUse.put(combined,tempVal);
else
debug("Not assigning the agreed value since that is not the default value for "+combined);
}
else if(tempConstant instanceof FloatConstant){
Float tempVal = new Float( ((FloatConstant)tempConstant).value );
if(tempVal.compareTo(new Float(0)) ==0)
primTypeFieldValueToUse.put(combined,tempVal);
else
debug("Not assigning the agreed value since that is not the default value for "+combined);
}
else if(tempConstant instanceof IntConstant){
Integer tempVal = new Integer( ((IntConstant)tempConstant).value );
if(tempVal.compareTo(new Integer(0)) ==0){
SootField tempField = classNameFieldNameToSootFieldMapping.get(combined);
if(tempField.getType() instanceof BooleanType){
primTypeFieldValueToUse.put(combined,new Boolean(false));
//System.out.println("puttingvalue false for"+combined);
}
else{
primTypeFieldValueToUse.put(combined,tempVal);
//System.out.println("puttingvalue 0 for"+combined);
}
}
else
debug("Not assigning the agreed value since that is not the default value for "+combined);
}
else{
throw new DecompilationException("Un handled Numberic Constant....report to programmer");
}
} //all fields of the class
} //all classes
}
/*
* Go through all the methods in the application and make a mapping of className+methodName ---> values assigned
* There can obviously be more than one value assigned to each field
*/
private void computeFieldToValuesAssignedList(){
//go through all the classes
Iterator classIt = appClasses.iterator();
while(classIt.hasNext()){
SootClass s = (SootClass) classIt.next();
debug("\ncomputeMethodSummaries","Processing class "+s.getName());
//go though all the methods
Iterator methodIt = s.methodIterator();
while (methodIt.hasNext()) {
SootMethod m = (SootMethod) methodIt.next();
DavaBody body = null;
if(m.hasActiveBody()){
/*
* Added to try to fix the no active body found exception
*/
body = (DavaBody)m.getActiveBody();
}
else{
continue;
}
ASTNode AST = (ASTNode) body.getUnits().getFirst();
//find all definitions in the program
AllDefinitionsFinder defFinder = new AllDefinitionsFinder();
AST.apply(defFinder);
Iterator<DefinitionStmt> allDefIt = defFinder.getAllDefs().iterator();
//go through each definition
while(allDefIt.hasNext()){
DefinitionStmt stmt = allDefIt.next();
//debug("DefinitionStmt")
Value left = stmt.getLeftOp();
/*
* Only care if we have fieldRef on the left
*/
if(! (left instanceof FieldRef) ){
continue;
}
//we know definition is to a field
debug("computeMethodSummaries method: "+m.getName(),"Field ref is: "+left);
// Information we want to store is class of field and name of field and the right op
FieldRef ref = (FieldRef)left;
SootField field = ref.getField();
/*
* Only care about fields with primtype
*/
if(!( field.getType() instanceof PrimType))
continue;
String fieldName = field.getName();
String declaringClass = field.getDeclaringClass().getName();
debug("\tField Name: "+ fieldName);
debug("\tField DeclaringClass: "+ declaringClass);
//get the valueList for this class+field combo
String combined = declaringClass + combiner + fieldName;
Object temp = fieldToValues.get(combined);
ArrayList valueList;
if(temp == null){
//no value of this field was yet assigned
valueList = new ArrayList();
fieldToValues.put(combined,valueList);
}
else{
valueList = (ArrayList)temp;
}
valueList.add(stmt.getRightOp());
}//going through all the definitions
}//going through methods of class s
}//going through classes
}
public void printConstantValueFields(){
System.out.println("\n\n Printing Constant Value Fields (method: printConstantValueFields)");
Iterator<String> it = primTypeFieldValueToUse.keySet().iterator();
while(it.hasNext()){
String combined = it.next();
int temp = combined.indexOf(combiner,0);
if(temp > 0){
System.out.println("Class: "+ combined.substring(0,temp)+" Field: "+combined.substring(temp+combiner.length()) +" Value: "+ primTypeFieldValueToUse.get(combined));
}
}
}
public void debug(String methodName, String debug){
if(DEBUG)
System.out.println(methodName+ " DEBUG: "+debug);
}
public void debug(String debug){
if(DEBUG)
System.out.println("DEBUG: "+debug);
}
}