1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include "code_ir.h" 20 #include "common.h" 21 #include "dex_ir.h" 22 #include "dex_ir_builder.h" 23 24 #include <memory> 25 #include <vector> 26 #include <utility> 27 #include <set> 28 29 namespace slicer { 30 31 // Interface for a single transformation operation 32 class Transformation { 33 public: 34 virtual ~Transformation() = default; 35 virtual bool Apply(lir::CodeIr* code_ir) = 0; 36 }; 37 38 // Insert a call to the "entry hook" at the start of the instrumented method: 39 // The "entry hook" will be forwarded the original incoming arguments plus 40 // an explicit "this" argument for non-static methods. 41 class EntryHook : public Transformation { 42 public: 43 explicit EntryHook( 44 const ir::MethodId& hook_method_id, 45 bool use_object_type_for_this_argument = false) hook_method_id_(hook_method_id)46 : hook_method_id_(hook_method_id), 47 use_object_type_for_this_argument_(use_object_type_for_this_argument) { 48 // hook method signature is generated automatically 49 SLICER_CHECK(hook_method_id_.signature == nullptr); 50 } 51 52 virtual bool Apply(lir::CodeIr* code_ir) override; 53 54 private: 55 ir::MethodId hook_method_id_; 56 // If true, "this" argument of non-static methods is forwarded as Object type. 57 // For example "this" argument of OkHttpClient type is forwared as Object and 58 // is used to get OkHttp class loader. 59 bool use_object_type_for_this_argument_; 60 }; 61 62 // Insert a call to the "exit hook" method before every return 63 // in the instrumented method. The "exit hook" will be passed the 64 // original return value and it may return a new return value. 65 class ExitHook : public Transformation { 66 public: ExitHook(const ir::MethodId & hook_method_id)67 explicit ExitHook(const ir::MethodId& hook_method_id) : hook_method_id_(hook_method_id) { 68 // hook method signature is generated automatically 69 SLICER_CHECK(hook_method_id_.signature == nullptr); 70 } 71 72 virtual bool Apply(lir::CodeIr* code_ir) override; 73 74 private: 75 ir::MethodId hook_method_id_; 76 }; 77 78 // Replace every invoke-virtual[/range] to the a specified method with 79 // a invoke-static[/range] to the detour method. The detour is a static 80 // method which takes the same arguments as the original method plus 81 // an explicit "this" argument, and returns the same type as the original method 82 class DetourVirtualInvoke : public Transformation { 83 public: DetourVirtualInvoke(const ir::MethodId & orig_method_id,const ir::MethodId & detour_method_id)84 DetourVirtualInvoke(const ir::MethodId& orig_method_id, const ir::MethodId& detour_method_id) 85 : orig_method_id_(orig_method_id), detour_method_id_(detour_method_id) { 86 // detour method signature is automatically created 87 // to match the original method and must not be explicitly specified 88 SLICER_CHECK(detour_method_id_.signature == nullptr); 89 } 90 91 virtual bool Apply(lir::CodeIr* code_ir) override; 92 93 private: 94 ir::MethodId orig_method_id_; 95 ir::MethodId detour_method_id_; 96 }; 97 98 // Allocates scratch registers without doing a full register allocation 99 class AllocateScratchRegs : public Transformation { 100 public: 101 explicit AllocateScratchRegs(int allocate_count, bool allow_renumbering = true) allocate_count_(allocate_count)102 : allocate_count_(allocate_count), allow_renumbering_(allow_renumbering) { 103 SLICER_CHECK(allocate_count > 0); 104 } 105 106 virtual bool Apply(lir::CodeIr* code_ir) override; 107 ScratchRegs()108 const std::set<dex::u4>& ScratchRegs() const { 109 SLICER_CHECK(scratch_regs_.size() == static_cast<size_t>(allocate_count_)); 110 return scratch_regs_; 111 } 112 113 private: 114 void RegsRenumbering(lir::CodeIr* code_ir); 115 void ShiftParams(lir::CodeIr* code_ir); 116 void Allocate(lir::CodeIr* code_ir, dex::u4 first_reg, int count); 117 118 private: 119 const int allocate_count_; 120 const bool allow_renumbering_; 121 int left_to_allocate_ = 0; 122 std::set<dex::u4> scratch_regs_; 123 }; 124 125 // A friendly helper for instrumenting existing methods: it allows batching 126 // a set of transformations to be applied to method (the batching allow it 127 // to build and encode the code IR once per method regardless of how many 128 // transformation are applied) 129 // 130 // For example, if we want to add both entry and exit hooks to a 131 // Hello.Test(int) method, the code would look like this: 132 // 133 // ... 134 // slicer::MethodInstrumenter mi(dex_ir); 135 // mi.AddTransformation<slicer::EntryHook>(ir::MethodId("LTracer;", "OnEntry")); 136 // mi.AddTransformation<slicer::ExitHook>(ir::MethodId("LTracer;", "OnExit")); 137 // SLICER_CHECK(mi.InstrumentMethod(ir::MethodId("LHello;", "Test", "(I)I"))); 138 // ... 139 // 140 class MethodInstrumenter { 141 public: MethodInstrumenter(std::shared_ptr<ir::DexFile> dex_ir)142 explicit MethodInstrumenter(std::shared_ptr<ir::DexFile> dex_ir) : dex_ir_(dex_ir) {} 143 144 // No copy/move semantics 145 MethodInstrumenter(const MethodInstrumenter&) = delete; 146 MethodInstrumenter& operator=(const MethodInstrumenter&) = delete; 147 148 // Queue a transformation 149 // (T is a class derived from Transformation) 150 template<class T, class... Args> AddTransformation(Args &&...args)151 T* AddTransformation(Args&&... args) { 152 T* transformation = new T(std::forward<Args>(args)...); 153 transformations_.emplace_back(transformation); 154 return transformation; 155 } 156 157 // Apply all the queued transformations to the specified method 158 bool InstrumentMethod(ir::EncodedMethod* ir_method); 159 bool InstrumentMethod(const ir::MethodId& method_id); 160 161 private: 162 std::shared_ptr<ir::DexFile> dex_ir_; 163 std::vector<std::unique_ptr<Transformation>> transformations_; 164 }; 165 166 } // namespace slicer 167