internal static List <ClassDefinition> Hierarchicalize(List <ClassDefinition> container) { var result = new List <ClassDefinition>(); foreach (var cdef in container) { if (cdef.Fullname.Contains(DexConsts.InnerClassMarker)) { var items = cdef.Fullname.Split(DexConsts.InnerClassMarker); var fullname = items[0]; var name = items[1]; var owner = Dex.GetClass(fullname, container); if (owner != null) { owner.InnerClasses.Add(cdef); cdef.Owner = owner; } } else { result.Add(cdef); } } return(result); }
/// <summary> /// Transform the given body. /// </summary> public void Transform(Dex target, MethodBody body) { foreach (var ins in body.Instructions) { switch (ins.Code) { case RCode.Invoke_direct: case RCode.Invoke_virtual: case RCode.Invoke_interface: MethodDefinition method; if (((MethodReference)ins.Operand).TryResolve(target, out method)) { if (method.Owner.IsInterface) { ins.Code = RCode.Invoke_interface; } else if (method.IsDirect) { ins.Code = RCode.Invoke_direct; } else { ins.Code = RCode.Invoke_virtual; } } break; } } }
/// <summary> /// Verify all methods /// </summary> private void VerifyMethods(Dex dex) { foreach (var @class in dex.GetClasses()) { VerifyClass(@class); } }
private static IEnumerable<IRLTransformation> GetTransformations(Dex target, MethodBody body) { foreach (var transformation in optimizations1) { transformation.Transform(target, body); yield return transformation; } var noopRemove = new NopRemoveTransformation(); noopRemove.Transform(target, body); yield return noopRemove; bool hasChanges = true; while (hasChanges) { hasChanges = false; foreach (var transformation in incrementalOptimizations) { bool changed = transformation.Transform(target, body); if (changed) noopRemove.Transform(target, body); hasChanges = changed || hasChanges; yield return transformation; } } foreach (var transformation in optimizations2) { transformation.Transform(target, body); yield return transformation; } }
public bool Transform(Dex target, MethodBody body) { #if DEBUG //return; #endif return DoOptimization(body); }
/// <summary> /// Default ctor /// </summary> public DelegateType(AssemblyCompiler compiler, XTypeDefinition delegateType, ClassDefinition interfaceClass, Dex target, NameConverter nsConverter) { this.compiler = compiler; this.delegateType = delegateType; this.interfaceClass = interfaceClass; // Build invoke prototype invokeMethod = delegateType.Methods.First(x => x.EqualsName("Invoke")); }
/// <summary> /// Transform the given body. /// </summary> public void Transform(Dex target, MethodBody body) { var basicBlocks = BasicBlock.Find(body); var registerCount = body.Registers.Count(); if (registerCount > MaxRegisters) return; Dictionary<Instruction, ConstantKey> allConstInstructions; CollectReadOnlyConstInstructions(body, out allConstInstructions); RegisterUsageMap registerUsageMap = null; foreach (var block in basicBlocks) { // Select all const instructions that have a next instruction in the block. var list = block.Instructions.ToList(); var isLargeBlock = list.Count > LargeBlockSize; if (isLargeBlock) continue; var constInstructionKeys = list.Where(x => allConstInstructions.ContainsKey(x)).Select(x => allConstInstructions[x]).ToList(); // Select all const instructions where the next instruction is a move. while (constInstructionKeys.Count > 0) { // Take the first instruction var insKey = constInstructionKeys[0]; constInstructionKeys.RemoveAt(0); // Get the register var reg = insKey.Instruction.Registers[0]; // Are there other const instructions of the same type and the same operand? var sameConstInstructions = constInstructionKeys.Where(x => x.Equals(insKey)).ToList(); if (sameConstInstructions.Count == 0) continue; if (sameConstInstructions.Count < 4) continue; // It is now save to use the register for all const operations foreach (var other in sameConstInstructions) { // Replace all register uses var otherReg = other.Instruction.Registers[0]; // Find usages registerUsageMap = registerUsageMap ?? new RegisterUsageMap(body.Instructions); registerUsageMap.ReplaceRegisterWith(otherReg, reg, body); // Remove const constInstructionKeys.Remove(other); allConstInstructions.Remove(other.Instruction); // Change const to nop which will be removed later other.Instruction.ConvertToNop(); } } } }
public bool Transform(Dex target, MethodBody body) { bool hasChanges = false; var registerUsage = new RegisterUsageMap2(body); hasChanges = InlineIntConstsIntoBinOp(registerUsage); return hasChanges; }
/// <summary> /// Transform the given body. /// </summary> public bool Transform(Dex target, MethodBody body) { bool hasChanges = false; // Find all "const" instructions and record register usage var allConstInstructions = new List<Instruction>(); var registerUsage = new Dictionary<Register, List<Instruction>>(); CollectInstructionInformation(body, allConstInstructions, registerUsage); // Find all basic blocks var basicBlocks = BasicBlock.Find(body); // Go over each block foreach (var iterator in basicBlocks) { var block = iterator; // Select all const instructions where the next instruction is a move. foreach (var ins in allConstInstructions) { var r = ins.Registers[0]; // Only optimize const instructions to temp registers if (r.Category != RCategory.Temp) continue; // Get all instructions using this register var all = registerUsage[r]; if ((all.Count != 2) || (all[0] != ins)) continue; var next = all[1]; // Opcode must match if (next.Code != ConstToMove(ins.Code)) continue; // Register must match if (next.Registers[1] != r) continue; // The following are the most expensive, so only test them if we have to. // Only optimize is instruction is in current block if (!block.Contains(ins)) continue; // Next must be in same basic block if (!block.Contains(next)) continue; // We found a replacement r = next.Registers[0]; ins.Registers[0] = r; next.ConvertToNop(); hasChanges = true; } } return hasChanges; }
/// <summary> /// Create all child nodes /// </summary> protected override void CreateChildNodes() { var data = Load(); dexSize = data.Length; dex = Dex.Read(new MemoryStream(data)); foreach (var classDef in dex.Classes) { var parentNodes = Nodes.GetParentForFile(classDef.Namespace, 7, new[] { '.' }); parentNodes.Add(new DexClassDefinitionNode(classDef)); } }
public bool Transform(Dex target, MethodBody body) { bool hasChanges = false; var graph = new ControlFlowGraph2(body); var registerUsage = new RegisterUsageMap2(graph); hasChanges = EliminateRegisterAssigments(registerUsage); return hasChanges; }
public bool Transform(Dex target, MethodBody body) { bool hasChanges = false; #if DEBUG //return; #endif var instructions = body.Instructions; var hasNops = instructions.Any(x => x.Code == RCode.Nop); if (!hasNops) return false; var rerouter = new BranchReRouter(body); var i = 0; while (i < instructions.Count) { var inst = instructions[i]; if (inst.Code != RCode.Nop) { i++; continue; } if (body.Exceptions.Count > 0) { foreach (var ex in body.Exceptions.Where(x => x.TryEnd == inst).ToList()) { var exTryEnd = ex.TryEnd; if (exTryEnd.Index > ex.TryStart.Index) exTryEnd = exTryEnd.Previous; if (exTryEnd == ex.TryStart) { // empty exception handler -- remove. body.Exceptions.Remove(ex); } else { ex.TryEnd = exTryEnd; } } } if (i < instructions.Count - 1) { var next = instructions[i + 1]; rerouter.Reroute(inst, next); } instructions.RemoveAt(i); hasChanges = true; } return hasChanges; }
/// <summary> /// Transform the given body. /// </summary> public void Transform(Dex target, MethodBody body) { // Find all basic blocks var basicBlocks = BasicBlock.Find(body); // Replace temp with variable registers with possible. TransformTempToVariable(body, basicBlocks); // Share registers in block only TransformInBlock(body, basicBlocks); // Share register across blocks TransformCrossBlock(body, basicBlocks); }
public bool Transform(Dex target, MethodBody body) { foreach (var ins in body.Instructions) { if (ins.Code == RCode.Check_cast) { var typeReference = (TypeReference) ins.Operand; if(typeReference.Descriptor == "Ljava/lang/Object;") ins.ConvertToNop(); } } return false; }
/// <summary> /// Transform the given body towards Dex compilation. /// <returns> /// Returns the name of the last applied tranformation, or null on full processing. /// </returns> /// </summary> internal static string Transform(Dex target, MethodBody body, int stopAfterSteps = int.MaxValue) { if (stopAfterSteps == 0) return "(no proccessing)"; int stepCount = 0; foreach (var applied in GetTransformations(target, body)) { if (++stepCount > stopAfterSteps) { return applied.GetType().Name; } } return null; }
/// <summary> /// Default ctor /// </summary> public DelegateType(AssemblyCompiler compiler, XTypeDefinition delegateType, ClassDefinition interfaceClass, Dex target, NameConverter nsConverter) { this.compiler = compiler; this.delegateType = delegateType; this.interfaceClass = interfaceClass; // Build invoke prototype invokeMethod = delegateType.Methods.First(x => x.EqualsName("Invoke")); XTypeDefinition baseType = delegateType; while ((null != baseType) && !baseType.Methods.Any(x => x.EqualsName("Equals"))) { baseType = baseType.BaseType as XTypeDefinition; } if (null != baseType) { equalsMethod = baseType.Methods.First(x => x.EqualsName("Equals")); } }
/// <summary> /// Transform the given body. /// </summary> public bool Transform(Dex target, MethodBody body) { foreach (var ins in body.Instructions) { switch (ins.Code) { case RCode.Invoke_direct: case RCode.Invoke_virtual: case RCode.Invoke_interface: MethodDefinition method; if (((MethodReference)ins.Operand).TryResolve(target, out method)) { if (method.Owner.IsInterface) { ins.Code = RCode.Invoke_interface; } else if (method.IsStatic) // This happens for android extension methods. // Is this a hack. why was the correct invoke code not // used in the first place? // (in AstCompilerVisitor.Expression.VisitCallExpression?) { ins.Code = RCode.Invoke_static; } else if (method.IsDirect) { ins.Code = RCode.Invoke_direct; } else { ins.Code = RCode.Invoke_virtual; } } break; } } return false; }
public bool Transform(Dex target, MethodBody body) { bool hasChanges = false; #if DEBUG //return; #endif var instructions = body.Instructions; bool[] reachable = new bool[instructions.Count]; MarkReachable(0, instructions, reachable, body); for (int i = instructions.Count - 1; i > 0 ; --i) { // the nop-remover will also correcly remove exception handlers. if (!reachable[i] && instructions[i].Code != RCode.Nop) { instructions[i].ConvertToNop(); hasChanges = true; } } return hasChanges; }
/// <summary> /// Set the super class of the class definition. /// </summary> protected void ImplementSuperClass(Dex target, NameConverter nsConverter) { // Set base type var baseType = typeDef.SuperClass; if (baseType != null) { classDef.SuperClass = new ClassReference(baseType.ClassName); } else if (typeDef.IsInterface) { classDef.SuperClass = new ClassReference("java/lang/Object"); } else { throw new ArgumentException(string.Format("Type {0} has no base type", typeDef.ClassName)); } }
/// <summary> /// Implement make minor fixes after the implementation phase. /// </summary> public void FixUp(Dex target, NameConverter nsConverter) { FixUpInnerClasses(target, nsConverter); FixUpMethods(target, nsConverter); }
/// <summary> /// FixUp all nested types. /// </summary> protected virtual void FixUpInnerClasses(Dex target, NameConverter nsConverter) { // FixUp nested type nestedBuilders.ForEach(x => x.FixUp(target, nsConverter)); }
/// <summary> /// Add references to all implemented interfaces. /// </summary> protected virtual void ImplementInterfaces(Dex target, NameConverter nsConverter) { // Implement interfaces classDef.Interfaces.AddRange(typeDef.Interfaces.Select(x => new ClassReference(x.ClassName))); }
public DexLookup(Dex dex) : this(dex.Classes, true) { }
/// <summary> /// Optimize the RL structure. /// </summary> private void OptimizeRL(Dex target) { if (rlIsOptimized) return; var rlBody = RLBody; if (rlBody == null) throw new ArgumentException("No RL body set"); // Optimize RL code RLTransformations.Transform(target, rlBody); rlIsOptimized = true; }
/// <summary> /// Transform the given body. /// </summary> public bool Transform(Dex target, MethodBody body) { // Build the control flow graph var cfg = new ControlFlowGraph(body); // Go over each block to find registers that are initialized and may need initialization foreach (var iterator in cfg) { var block = iterator; var data = new BasicBlockData(block); block.Tag = data; // Go over all instructions in the block, finding source/destination registers foreach (var ins in block.Instructions) { var info = OpCodeInfo.Get(ins.Code.ToDex()); var count = ins.Registers.Count; for (var i = 0; i < count; i++) { var reg = ins.Registers[i]; if (reg.Category != RCategory.Argument) { var flags = info.GetUsage(i); if (flags.HasFlag(RegisterFlags.Source)) { // Must be initialize if (!data.Initialized.Contains(reg)) { data.MayNeedInitialization.Add(reg); } } if (flags.HasFlag(RegisterFlags.Destination)) { // Destination data.Initialized.Add(reg); } } } } } // Go over all blocks to collect the register that really need initialization var needInitialization = new HashSet<Register>(); foreach (var iterator in cfg) { var block = iterator; var data = (BasicBlockData)block.Tag; foreach (var regIterator in data.MayNeedInitialization) { // Short cut var reg = regIterator; if (needInitialization.Contains(reg)) continue; // If the register is initialized in all entry blocks, we do not need to initialize it if (block.EntryBlocks.Select(x => (BasicBlockData) x.Tag).Any(x => !x.IsInitialized(reg))) { // There is an entry block that does not initialize the register, so we have to initialize it needInitialization.Add(reg); } } } var index = 0; Register valueReg = null; Register objectReg = null; Register wideReg = null; var firstSourceLocation = body.Instructions[0].SequencePoint; foreach (var reg in needInitialization.OrderBy(x => x.Index)) { switch (reg.Type) { case RType.Value: if (valueReg == null) { body.Instructions.Insert(index++, new Instruction(RCode.Const, 0, new[] {reg}) { SequencePoint = firstSourceLocation }); valueReg = reg; } else if (valueReg != reg) { body.Instructions.Insert(index++, new Instruction(RCode.Move, reg, valueReg) { SequencePoint = firstSourceLocation }); } break; case RType.Object: if (objectReg == null) { body.Instructions.Insert(index++, new Instruction(RCode.Const, 0, new[] { reg }) { SequencePoint = firstSourceLocation }); objectReg = reg; } else if (objectReg != reg) { body.Instructions.Insert(index++, new Instruction(RCode.Move_object, reg, objectReg) { SequencePoint = firstSourceLocation }); } break; case RType.Wide: if (wideReg == null) { body.Instructions.Insert(index++, new Instruction(RCode.Const_wide, 0, new[] { reg }) { SequencePoint = firstSourceLocation }); wideReg = reg; } else if (wideReg != reg) { body.Instructions.Insert(index++, new Instruction(RCode.Move_wide, reg, wideReg) { SequencePoint = firstSourceLocation }); } break; } } return false; }
/// <summary> /// Verify the given dex structure. /// </summary> internal static void Verify(Dex dex, Action<string> onError) { var v = new Verifier(onError); v.VerifyMethods(dex); }
internal void RegisterDex(Dex dex) { belongsToDex.Add(new WeakReference(dex)); }
public bool Transform(Dex target, MethodBody body) { var cfg = new ControlFlowGraph2(body); return OptimizeBranches(cfg); }
/// <summary> /// FixUp all methods. /// </summary> protected virtual void FixUpMethods(Dex target, NameConverter nsConverter) { // FixUp methods methodBuilders.ForEach(x => x.FixUp(target, nsConverter)); }
/// <summary> /// Create the current type as class definition. /// </summary> public void Create(Dex target, NameConverter nsConverter) { Create(target, nsConverter, null, null, null); }
public string Disassemble(ClassDefinition def) { if (string.IsNullOrEmpty(_settings.BaksmaliCommand)) return "#ERROR: command to run not set."; StringBuilder ret = new StringBuilder(); try { // get a temporary directoy string tempPath = Path.GetTempFileName(); File.Delete(tempPath); Directory.CreateDirectory(tempPath); string dexFileName = Path.Combine(tempPath, "classes.dex"); // create dex with one class definition // NOTE: it would be better if we could get hold of // the actual binary representation. // or use the original apk/dex and limit what baksmali // processes. var dex = new Dex(); dex.AddClass(def); dex.Write(dexFileName); // run baksmali var cmd = GetBacksmaliCommand(_settings, dexFileName, tempPath); ret.AppendLine("# processed with: " + cmd); ret.AppendLine("#"); int retCode; List<string> output = new List<string>(); if ((retCode = Run.System(output, "{0}", cmd)) != 0) ret.AppendLine("# return code: " + retCode); // process eventual errors output.ForEach(p=>ret.AppendFormat("# output: {0}\n", p)); // find .smali file var filenames = Directory.EnumerateFiles(tempPath, "*.smali", SearchOption.AllDirectories).ToList(); if (!filenames.Any()) { ret.AppendLine("# ERROR: baksmali did not produce any output files."); } if (filenames.Count > 1) ret.AppendLine("# NOTE: baksmali produced multiple files"); IEnumerable<string> contents = filenames.Select(n => File.ReadAllText(Path.Combine(tempPath, n))); ret.Append(string.Join("\n\n\n\n-----------\n\n\n\n", contents)); // clean up. (don't clean up on exception, to let the user inspect what happened) Directory.Delete(tempPath, true); return ret.ToString(); } catch (Exception ex) { return "# ERROR: " + ex; } }
/// <summary> /// Create the current type as class definition. /// </summary> protected void Create(Dex target, NameConverter nsConverter, ClassDefinition parent, ClassFile parentClass, XTypeDefinition parentXType) { xType = new XBuilder.JavaTypeDefinition(compiler.Module, parentXType, typeDef); CreateClassDefinition(target, nsConverter, parent, parentClass); CreateNestedClasses(target, nsConverter, parent); }