Search in sources :

Example 1 with IPAnalysis

use of org.mapleir.deob.interproc.IPAnalysis in project maple-ir by LLVM-but-worse.

the class ConstantParameterPass method accept.

@Override
public int accept(AnalysisContext cxt, IPass prev, List<IPass> completed) {
    Map<MethodNode, Set<MethodNode>> chainMap = new HashMap<>();
    for (MethodNode mn : cxt.getIRCache().getActiveMethods()) {
        makeUpChain(cxt, mn, chainMap);
    }
    InvocationResolver resolver = cxt.getInvocationResolver();
    Map<MethodNode, List<Set<Object>>> rawConstantParameters = new HashMap<>();
    Map<MethodNode, boolean[]> chainedNonConstant = new HashMap<>();
    Map<MethodNode, boolean[]> specificNonConstant = new HashMap<>();
    IPAnalysisVisitor vis = new IPAnalysisVisitor() {

        @Override
        public void postVisitMethod(IPAnalysis analysis, MethodNode m) {
            int pCount = Type.getArgumentTypes(m.desc).length;
            /* init map entries */
            if (!chainedNonConstant.containsKey(m)) {
                for (MethodNode assoc : chainMap.get(m)) {
                    boolean[] arr = new boolean[pCount];
                    chainedNonConstant.put(assoc, arr);
                }
                for (MethodNode assoc : chainMap.get(m)) {
                    boolean[] arr = new boolean[pCount];
                    specificNonConstant.put(assoc, arr);
                }
            }
            if (Modifier.isStatic(m.access)) {
                if (!rawConstantParameters.containsKey(m)) {
                    List<Set<Object>> l = new ArrayList<>(pCount);
                    rawConstantParameters.put(m, l);
                    for (int i = 0; i < pCount; i++) {
                        l.add(new HashSet<>());
                    }
                }
            } else {
                // TODO: cache
                for (MethodNode site : resolver.resolveVirtualCalls(m, true)) {
                    if (!rawConstantParameters.containsKey(site)) {
                        List<Set<Object>> l = new ArrayList<>(pCount);
                        rawConstantParameters.put(site, l);
                        for (int i = 0; i < pCount; i++) {
                            l.add(new HashSet<>());
                        }
                    }
                }
            }
        }

        @Override
        public void postProcessedInvocation(IPAnalysis analysis, MethodNode caller, MethodNode callee, Invocation call) {
            Expr[] params = call.getParameterExprs();
            for (int i = 0; i < params.length; i++) {
                Expr e = params[i];
                if (e.getOpcode() == Opcode.CONST_LOAD) {
                    if (Modifier.isStatic(callee.access)) {
                        rawConstantParameters.get(callee).get(i).add(((ConstantExpr) e).getConstant());
                    } else {
                        /* only chain callsites *can* have this input */
                        for (MethodNode site : resolver.resolveVirtualCalls(callee, true)) {
                            rawConstantParameters.get(site).get(i).add(((ConstantExpr) e).getConstant());
                        }
                    }
                } else {
                    /* whole branch tainted */
                    for (MethodNode associated : chainMap.get(callee)) {
                        chainedNonConstant.get(associated)[i] = true;
                    }
                    /* callsites tainted */
                    if (Modifier.isStatic(callee.access)) {
                        specificNonConstant.get(callee)[i] = true;
                    } else {
                        /* only chain callsites *can* have this input */
                        for (MethodNode site : resolver.resolveVirtualCalls(callee, true)) {
                            specificNonConstant.get(site)[i] = true;
                        }
                    }
                }
            }
        }
    };
    IPAnalysis constAnalysis = IPAnalysis.create(cxt, vis);
    // ApplicationClassSource app = cxt.getApplication();
    // ClassTree structures = app.getStructures();
    /* remove all calls to library methods since we can't
		 * handle them. */
    /*Iterator<Entry<MethodNode, List<Set<Object>>>> it = rawConstantParameters.entrySet().iterator();
		while(it.hasNext()) {
			Entry<MethodNode, List<Set<Object>>> en = it.next();
			
			MethodNode m = en.getKey();

			if(app.isLibraryClass(m.owner.name)) {
				it.remove();
				continue;
			}
			
			// TODO: MUST BE CONVERTED TO ACCOUNT FOR DIRECT SUPERS, NOT ALL
			superFor: for(ClassNode cn : structures.getAllParents(m.owner)) {
				if(app.isLibraryClass(cn.name)) {
					for(MethodNode m1 : cn.methods) {
						if(resolver.areMethodsCongruent(m1, m, Modifier.isStatic(m.access))) {
							it.remove();
							break superFor;
						}
					}
				}
			}
		}*/
    /* aggregate constant parameters indices with their chained
		 * methods such that the map contains only constant parameter
		 * indices that we can actually remove while keeping a valid chain.
		 * 
		 * We do this as we can have methods from different branches that
		 * are cousin-related but have different constant parameter values.
		 * In these cases we can still inline the constants (different constants)
		 * and change the descriptions, keeping the chain. */
    Map<MethodNode, boolean[]> filteredConstantParameters = new HashMap<>();
    for (Entry<MethodNode, List<Set<Object>>> en : rawConstantParameters.entrySet()) {
        MethodNode m = en.getKey();
        List<Set<Object>> objParams = en.getValue();
        boolean[] tainted = chainedNonConstant.get(m);
        if (filteredConstantParameters.containsKey(m)) {
            /* note: if this method is contained in the
				 * map all of it's cousin-reachable methods
				 * must also be and furthermore the dead map
				 * for the entire chain is the same array. 
				 * 
				 * we need to now merge the current dead map
				 * with the one specifically for this method.*/
            boolean[] thisDeadMap = makeDeadMap(objParams, tainted);
            boolean[] prevDeadMap = filteredConstantParameters.get(m);
            if (thisDeadMap.length != prevDeadMap.length) {
                throw new IllegalStateException(String.format("m: %s, chain:%s, %d:%d", m, chainMap.get(m), thisDeadMap.length, prevDeadMap.length));
            }
            /* each dead map contains true values for an
				 * index if that index is a constant parameter. */
            for (int i = 0; i < prevDeadMap.length; i++) {
                prevDeadMap[i] &= thisDeadMap[i];
            }
        } else {
            boolean[] deadParams = makeDeadMap(objParams, tainted);
            for (MethodNode chm : chainMap.get(m)) {
                filteredConstantParameters.put(chm, deadParams);
            }
        }
        ControlFlowGraph cfg = cxt.getIRCache().getFor(m);
        // boolean b = false;
        boolean[] specificTaint = specificNonConstant.get(m);
        for (int i = 0; i < objParams.size(); i++) {
            Set<Object> set = objParams.get(i);
            if (!specificTaint[i] && set.size() == 1) {
                inlineConstant(cfg, constAnalysis.getLocalIndex(m, i), set.iterator().next());
            }
        }
    }
    Map<MethodNode, String> remap = new HashMap<>();
    Set<MethodNode> toRemove = new HashSet<>();
    Set<Set<MethodNode>> mustRename = new HashSet<>();
    for (Entry<MethodNode, boolean[]> en : filteredConstantParameters.entrySet()) {
        MethodNode m = en.getKey();
        if (!remap.containsKey(m) && !toRemove.contains(m)) {
            boolean[] deadMap = en.getValue();
            boolean notSame = false;
            for (boolean b : deadMap) {
                notSame |= b;
            }
            if (!notSame) {
                /* eliminate all branches (same congruence class) */
                for (MethodNode n : chainMap.get(m)) {
                    toRemove.add(n);
                }
                continue;
            }
            Type[] params = Type.getArgumentTypes(m.desc);
            Type ret = Type.getReturnType(m.desc);
            String desc = buildDesc(params, ret, deadMap);
            Set<MethodNode> conflicts = new HashSet<>();
            for (MethodNode chm : chainMap.get(m)) {
                remap.put(chm, desc);
                if (Modifier.isStatic(m.access)) {
                    MethodNode mm = resolver.resolveStaticCall(chm.owner.name, chm.name, desc);
                    if (mm != null) {
                        conflicts.add(mm);
                    }
                } else {
                    if (chm.name.equals("<init>")) {
                        conflicts.addAll(resolver.resolveVirtualCalls(chm.owner.name, "<init>", desc, false));
                    } else {
                        conflicts.addAll(resolver.getHierarchyMethodChain(m.owner, m.name, desc, true));
                    }
                }
            }
            if (conflicts.size() > 0) {
                Set<MethodNode> chain = chainMap.get(m);
                /* rename the smallest conflict set */
                // if(chain.size() < conflicts.size()) {
                // 
                // } else {
                // mustRename.add(conflicts);
                // }
                mustRename.add(chain);
            }
        }
    }
    remap.keySet().removeAll(toRemove);
    int k = RenamingUtil.numeric("aaaaa");
    Map<MethodNode, String> methodNameRemap = new HashMap<>();
    for (Set<MethodNode> set : mustRename) {
        // MethodNode first = set.iterator().next();
        // String newName = "rename_" + first.name;
        String newName = RenamingUtil.createName(k++);
        System.out.printf(" renaming %s to %s%n", set, newName);
        System.out.println("   recom " + computeChain(cxt, set.iterator().next()));
        Set<MethodNode> s2 = new HashSet<>();
        for (MethodNode m : set) {
            s2.addAll(chainMap.get(m));
        }
        if (!s2.equals(set)) {
            System.err.println(set);
            System.err.println(s2);
            throw new IllegalStateException();
        }
        for (MethodNode m : set) {
            methodNameRemap.put(m, newName);
        }
    }
    if (mustRename.size() > 0) {
        MethodRenamerPass.rename(cxt, methodNameRemap, false);
    }
    Set<MethodNode> visitedMethods = new HashSet<>();
    Set<Expr> visitedExprs = new HashSet<>();
    int killedTotal = 0;
    for (; ; ) {
        int killedBeforePass = killedTotal;
        for (Entry<MethodNode, String> en : remap.entrySet()) {
            MethodNode key = en.getKey();
            String newDesc = en.getValue();
            if (!visitedMethods.contains(key)) {
                Set<MethodNode> chain = chainMap.get(key);
                /*for(MethodNode n : chain) {
						if(visitedMethods.contains(n)) {
							throw new IllegalStateException(String.format("Invalid transistivityr: %s in %s but not %s", n, chain, key));
						}
					}*/
                boolean[] dead = filteredConstantParameters.get(key);
                for (MethodNode n : chain) {
                    n.desc = newDesc;
                    /* boolean[] dead = filteredConstantParameters.get(n);
						boolean[] deadM = filteredConstantParameters.get(key);
						
						if(!Arrays.equals(dead, deadM)) {
							throw new IllegalStateException(String.format("neq: %s vs %s for %s and %s", Arrays.toString(dead), Arrays.toString(deadM), n, key));
						} */
                    demoteDeadParamters(constAnalysis, cxt.getIRCache().getFor(n), n, dead);
                    for (Invocation call : constAnalysis.getCallsTo(n)) {
                        /* since the callgrapher finds all
							 * the methods in a hierarchy and considers
							 * it as a single invocation, a certain
							 * invocation may be considered multiple times. */
                        if (visitedExprs.contains(call)) {
                            continue;
                        }
                        /* the invocationexpr method desc is changed implicitly
							 * when the new expression is created in patchCall() */
                        visitedExprs.add(call);
                        patchCall(newDesc, call, dead);
                        killedTotal += chain.size();
                    }
                }
                visitedMethods.addAll(chain);
            }
        }
        if (killedBeforePass == killedTotal) {
            break;
        }
    }
    System.out.printf("  removed %d constant parameters.%n", killedTotal);
    return killedTotal;
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) Invocation(org.mapleir.ir.code.expr.invoke.Invocation) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) MethodNode(org.objectweb.asm.tree.MethodNode) ArrayList(java.util.ArrayList) List(java.util.List) HashSet(java.util.HashSet) IPAnalysisVisitor(org.mapleir.deob.interproc.IPAnalysisVisitor) Type(org.objectweb.asm.Type) ConstantExpr(org.mapleir.ir.code.expr.ConstantExpr) InitialisedObjectExpr(org.mapleir.ir.code.expr.invoke.InitialisedObjectExpr) InvocationExpr(org.mapleir.ir.code.expr.invoke.InvocationExpr) VarExpr(org.mapleir.ir.code.expr.VarExpr) Expr(org.mapleir.ir.code.Expr) ControlFlowGraph(org.mapleir.ir.cfg.ControlFlowGraph) InvocationResolver(org.mapleir.app.service.InvocationResolver) IPAnalysis(org.mapleir.deob.interproc.IPAnalysis)

Aggregations

ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 HashSet (java.util.HashSet)1 List (java.util.List)1 Set (java.util.Set)1 InvocationResolver (org.mapleir.app.service.InvocationResolver)1 IPAnalysis (org.mapleir.deob.interproc.IPAnalysis)1 IPAnalysisVisitor (org.mapleir.deob.interproc.IPAnalysisVisitor)1 ControlFlowGraph (org.mapleir.ir.cfg.ControlFlowGraph)1 Expr (org.mapleir.ir.code.Expr)1 ConstantExpr (org.mapleir.ir.code.expr.ConstantExpr)1 VarExpr (org.mapleir.ir.code.expr.VarExpr)1 InitialisedObjectExpr (org.mapleir.ir.code.expr.invoke.InitialisedObjectExpr)1 Invocation (org.mapleir.ir.code.expr.invoke.Invocation)1 InvocationExpr (org.mapleir.ir.code.expr.invoke.InvocationExpr)1 Type (org.objectweb.asm.Type)1 MethodNode (org.objectweb.asm.tree.MethodNode)1