/* Soot - a J*va Optimization Framework * Copyright (C) 1997-1999 Raja Vallee-Rai * * 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.jbco.jimpleTransformations; import java.util.*; import soot.*; import soot.jbco.IJbcoTransform; import soot.jbco.util.*; import soot.jimple.*; /** * @author Michael Batchelder * * Created on 26-Jan-2006 */ public class FieldRenamer extends SceneTransformer implements IJbcoTransform { public void outputSummary() {} public static String dependancies[] = new String[] { "wjtp.jbco_fr" }; public String[] getDependancies() { return dependancies; } public static String name = "wjtp.jbco_fr"; public String getName() { return name; } private static final char stringChars[][] = { {'S','5','$'}, {'l','1','I'}, {'_'} }; public static Vector namesToNotRename = new Vector(); public static Hashtable<String, String> oldToNewFieldNames = new Hashtable<String, String>(); public static Hashtable<SootClass, SootField> opaquePreds1ByClass = new Hashtable<SootClass, SootField>(); public static Hashtable<SootClass, SootField> opaquePreds2ByClass = new Hashtable<SootClass, SootField>(); public static ArrayList<SootField> sootFieldsRenamed = new ArrayList<SootField>(); public static SootField opaquePairs[][] = null; public static int handedOutPairs[] = null; public static int handedOutRunPairs[] = null; public static boolean rename_fields = false; RefType boolRef; protected void internalTransform(String phaseName, Map options) { Scene scene = G.v().soot_Scene(); //Hierarchy hierarchy = scene.getActiveHierarchy(); boolRef = scene.getRefType("java.lang.Boolean"); if (output) { if (rename_fields) out.println("Transforming Field Names and Adding Opaque Predicates..."); else out.println("Adding Opaques..."); } soot.jbco.util.BodyBuilder.retrieveAllBodies(); soot.jbco.util.BodyBuilder.retrieveAllNames(); Iterator it = scene.getApplicationClasses().iterator(); while (it.hasNext()) { SootClass c = (SootClass)it.next(); String cName = c.getName(); if (cName.indexOf(".") >= 0) cName = cName.substring(cName.lastIndexOf(".") + 1, cName.length()); oldToNewFieldNames.put(cName,cName); if (rename_fields) { if (output) out.println("\tClassName: "+cName); // rename all the fields in the class Iterator fIt = c.getFields().iterator(); while (fIt.hasNext()) { SootField f = (SootField)fIt.next(); int weight = soot.jbco.Main.getWeight(phaseName, f.getName()); if (weight > 0) renameField(cName,f); } } // skip interfaces - they can only hold final fields if (c.isInterface()) continue; // add one opaq predicate for true and one for false to each class String bool = "opPred1"; Type t = Rand.getInt() % 2 == 0 ? (Type)BooleanType.v() : (Type)boolRef; while (oldToNewFieldNames.containsKey(bool)) bool += "_"; SootField f = new SootField(bool, t, Modifier.PUBLIC | Modifier.STATIC); renameField(cName,f); opaquePreds1ByClass.put(c,f); c.addField(f); setBooleanTo(c,f,true); bool = "opPred2"; t = t == BooleanType.v() ? (Type)boolRef : (Type)BooleanType.v(); while (oldToNewFieldNames.containsKey(bool)) bool += "_"; f = new SootField(bool, t, Modifier.PUBLIC | Modifier.STATIC); renameField(cName,f); opaquePreds2ByClass.put(c,f); c.addField(f); if (t == boolRef) setBooleanTo(c,f,false); } buildOpaquePairings(); if (!rename_fields) return; if (output) out.println("\r\tUpdating field references in bytecode"); it = scene.getApplicationClasses().iterator(); while (it.hasNext()) { SootClass c = (SootClass)it.next(); Iterator mIt = c.getMethods().iterator(); while (mIt.hasNext()) { SootMethod m = (SootMethod)mIt.next(); if (!m.isConcrete()) continue; if (!m.hasActiveBody()) m.retrieveActiveBody(); Iterator uIt = m.getActiveBody().getUnits().iterator(); while (uIt.hasNext()) { Iterator udbIt = ((Unit)uIt.next()).getUseAndDefBoxes().iterator(); while (udbIt.hasNext()) { Value v = ((ValueBox)udbIt.next()).getValue(); if (v instanceof FieldRef) { FieldRef fr = (FieldRef)v; SootFieldRef sfr = fr.getFieldRef(); if (sfr.declaringClass().isLibraryClass()) continue; String oldName = sfr.name(); String fullName = sfr.declaringClass().getName() + '.' + oldName; String newName = oldToNewFieldNames.get(oldName); if (newName == null || namesToNotRename.contains(fullName)) continue; if (newName.equals(oldName)) { System.out.println("Strange.. Should not find a field with the same old and new name."); } sfr = scene.makeFieldRef(sfr.declaringClass(), newName, sfr.type(), sfr.isStatic()); fr.setFieldRef(sfr); try { sfr.resolve(); } catch (Exception exc) { System.out.println("********ERROR Updating "+sfr.name()+" to "+newName); System.out.println("Fields of "+sfr.declaringClass().getName() + ": "+sfr.declaringClass().getFields()); //System.out.println("Fields of "+_c.getName() + ": "+_c.getFields()); System.out.println(exc); System.exit(1); } } } } } } } protected void setBooleanTo(SootClass c, SootField f, boolean value) { if (!value && f.getType() instanceof IntegerType && Rand.getInt() % 2 > 0) return; Body b = null; boolean newInit = false; if (!c.declaresMethodByName("<clinit>")) { SootMethod m = new SootMethod("<clinit>", new ArrayList(),VoidType.v()); c.addMethod(m); b = Jimple.v().newBody(m); m.setActiveBody(b); newInit = true; } else { SootMethod m = c.getMethodByName("<clinit>"); b = m.getActiveBody(); } PatchingChain units = b.getUnits(); if (f.getType() instanceof IntegerType) { units.addFirst( Jimple.v().newAssignStmt( Jimple.v().newStaticFieldRef(f.makeRef()), IntConstant.v(value ? 1 : 0))); } else { Local bool = Jimple.v().newLocal("boolLcl",boolRef); b.getLocals().add(bool); SootMethod boolInit = boolRef.getSootClass().getMethod("void <init>(boolean)"); units.addFirst(Jimple.v().newAssignStmt( Jimple.v().newStaticFieldRef(f.makeRef()), bool)); units.addFirst(Jimple.v().newInvokeStmt( Jimple.v().newSpecialInvokeExpr(bool, boolInit.makeRef(), IntConstant.v(value ? 1 : 0)))); units.addFirst(Jimple.v().newAssignStmt(bool, Jimple.v().newNewExpr(boolRef))); } if (newInit) units.addLast(Jimple.v().newReturnVoidStmt()); } protected void renameField(String cName, SootField f) { if (sootFieldsRenamed.contains(f)) return; String newName = oldToNewFieldNames.get(f.getName()); if (newName == null) { newName = getNewName(); oldToNewFieldNames.put(f.getName(), newName); } if (output) G.v().out.println("\t\tChanged " + f.getName() + " to " + newName); f.setName(newName); sootFieldsRenamed.add(f); } /* * @return String newly generated junk name that DOES NOT exist yet */ public static String getNewName() { int size = 3; int tries = 0; int index = Rand.getInt(stringChars.length); int length = stringChars[index].length; String result = null; char cNewName[] = new char[size]; do { if (tries == 10) { cNewName = new char[++size]; index = Rand.getInt(stringChars.length); length = stringChars[index].length; tries = 0; } if (size<12) { do { int rand = Rand.getInt(length); cNewName[0] = stringChars[index][rand]; } while (!Character.isJavaIdentifierStart(cNewName[0])); // generate random string for (int i = 1; i < cNewName.length; i++) { int rand = Rand.getInt(length); cNewName[i] = stringChars[index][rand]; } result = String.copyValueOf(cNewName); } else { cNewName = new char[size-6]; // size will always be at least 8 here // generate more random string while (true) { for (int i = 0; i < cNewName.length; i++) cNewName[i] = (char)Rand.getInt(); result = String.copyValueOf(cNewName); if (isJavaIdentifier(result)) break; } } tries++; } while (oldToNewFieldNames.containsValue(result) || BodyBuilder.nameList.contains(result)); BodyBuilder.nameList.add(result); return result; } public static void addOldAndNewName(String oldn, String newn) { oldToNewFieldNames.put(oldn,newn); } public static boolean isJavaIdentifier(String s) { if (s == null || s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) { return false; } for (int i=1; i<s.length(); i++) { if (!Character.isJavaIdentifierPart(s.charAt(i))) { return false; } } return true; } public static SootField[] getRandomOpaques() { if (handedOutPairs == null) { handedOutPairs = new int[opaquePairs.length]; } int lowValue = 99999; ArrayList<Integer> available = new ArrayList<Integer>(); for (int element : handedOutPairs) if (lowValue>element) lowValue = element; for (int i = 0; i < handedOutPairs.length; i++) if (handedOutPairs[i] == lowValue) available.add(new Integer(i)); Integer index = available.get(Rand.getInt(available.size())); handedOutPairs[index.intValue()]++; return opaquePairs[index.intValue()]; } public static int getRandomOpaquesForRunnable() { if (handedOutRunPairs == null) { handedOutRunPairs = new int[opaquePairs.length]; } int lowValue = 99999; ArrayList<Integer> available = new ArrayList<Integer>(); for (int element : handedOutRunPairs) if (lowValue>element) lowValue = element; if (lowValue>2) return -1; for (int i = 0; i < handedOutRunPairs.length; i++) if (handedOutRunPairs[i] == lowValue) available.add(new Integer(i)); Integer index = available.get(Rand.getInt(available.size())); return index.intValue(); } public static void updateOpaqueRunnableCount(int i) { handedOutRunPairs[i]++; } private void buildOpaquePairings() { Object fields1[] = opaquePreds1ByClass.values().toArray(); Object fields2[] = opaquePreds2ByClass.values().toArray(); int leng = fields1.length; if (leng>1) { int i = leng * 2; while (i>1) { int rand1 = Rand.getInt(leng); int rand2 = Rand.getInt(leng); int rand3 = Rand.getInt(leng); int rand4 = Rand.getInt(leng); while (rand1 == rand2) rand2 = Rand.getInt(leng); while (rand3 == rand4) rand4 = Rand.getInt(leng); Object value = fields1[rand1]; fields1[rand1] = fields1[rand2]; fields1[rand2] = value; value = fields2[rand3]; fields2[rand3] = fields2[rand4]; fields2[rand4] = value; i--; } } opaquePairs = new SootField[leng][2]; for (int i = 0; i < leng; i++) { opaquePairs[i] = new SootField[]{(SootField)fields1[i], (SootField)fields2[i]}; } } }