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 #include "slicer/instrumentation.h"
18 #include "slicer/dex_ir_builder.h"
19 
20 namespace slicer {
21 
Apply(lir::CodeIr * code_ir)22 bool EntryHook::Apply(lir::CodeIr* code_ir) {
23   ir::Builder builder(code_ir->dex_ir);
24   const auto ir_method = code_ir->ir_method;
25 
26   // construct the hook method declaration
27   std::vector<ir::Type*> param_types;
28   if ((ir_method->access_flags & dex::kAccStatic) == 0) {
29     ir::Type* this_argument_type;
30     if (use_object_type_for_this_argument_) {
31       this_argument_type = builder.GetType("Ljava/lang/Object;");
32     } else {
33       this_argument_type = ir_method->decl->parent;
34     }
35     param_types.push_back(this_argument_type);
36   }
37   if (ir_method->decl->prototype->param_types != nullptr) {
38     const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
39     param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
40   }
41 
42   auto ir_proto = builder.GetProto(builder.GetType("V"),
43                                    builder.GetTypeList(param_types));
44 
45   auto ir_method_decl = builder.GetMethodDecl(
46       builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
47       builder.GetType(hook_method_id_.class_descriptor));
48 
49   auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
50 
51   // argument registers
52   auto regs = ir_method->code->registers;
53   auto args_count = ir_method->code->ins_count;
54   auto args = code_ir->Alloc<lir::VRegRange>(regs - args_count, args_count);
55 
56   // invoke hook bytecode
57   auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
58   hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
59   hook_invoke->operands.push_back(args);
60   hook_invoke->operands.push_back(hook_method);
61 
62   // insert the hook before the first bytecode in the method body
63   for (auto instr : code_ir->instructions) {
64     auto bytecode = dynamic_cast<lir::Bytecode*>(instr);
65     if (bytecode == nullptr) {
66       continue;
67     }
68     code_ir->instructions.InsertBefore(bytecode, hook_invoke);
69     break;
70   }
71 
72   return true;
73 }
74 
Apply(lir::CodeIr * code_ir)75 bool ExitHook::Apply(lir::CodeIr* code_ir) {
76   ir::Builder builder(code_ir->dex_ir);
77   const auto ir_method = code_ir->ir_method;
78   const auto return_type = ir_method->decl->prototype->return_type;
79 
80   // do we have a void-return method?
81   bool return_void = (::strcmp(return_type->descriptor->c_str(), "V") == 0);
82 
83   // construct the hook method declaration
84   std::vector<ir::Type*> param_types;
85   if (!return_void) {
86     param_types.push_back(return_type);
87   }
88 
89   auto ir_proto = builder.GetProto(return_type, builder.GetTypeList(param_types));
90 
91   auto ir_method_decl = builder.GetMethodDecl(
92       builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
93       builder.GetType(hook_method_id_.class_descriptor));
94 
95   auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
96 
97   // find and instrument all return instructions
98   for (auto instr : code_ir->instructions) {
99     auto bytecode = dynamic_cast<lir::Bytecode*>(instr);
100     if (bytecode == nullptr) {
101       continue;
102     }
103 
104     dex::Opcode move_result_opcode = dex::OP_NOP;
105     dex::u4 reg = 0;
106     int reg_count = 0;
107 
108     switch (bytecode->opcode) {
109       case dex::OP_RETURN_VOID:
110         SLICER_CHECK(return_void);
111         break;
112       case dex::OP_RETURN:
113         SLICER_CHECK(!return_void);
114         move_result_opcode = dex::OP_MOVE_RESULT;
115         reg = bytecode->CastOperand<lir::VReg>(0)->reg;
116         reg_count = 1;
117         break;
118       case dex::OP_RETURN_OBJECT:
119         SLICER_CHECK(!return_void);
120         move_result_opcode = dex::OP_MOVE_RESULT_OBJECT;
121         reg = bytecode->CastOperand<lir::VReg>(0)->reg;
122         reg_count = 1;
123         break;
124       case dex::OP_RETURN_WIDE:
125         SLICER_CHECK(!return_void);
126         move_result_opcode = dex::OP_MOVE_RESULT_WIDE;
127         reg = bytecode->CastOperand<lir::VRegPair>(0)->base_reg;
128         reg_count = 2;
129         break;
130       default:
131         // skip the bytecode...
132         continue;
133     }
134 
135     // invoke hook bytecode
136     auto args = code_ir->Alloc<lir::VRegRange>(reg, reg_count);
137     auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
138     hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
139     hook_invoke->operands.push_back(args);
140     hook_invoke->operands.push_back(hook_method);
141     code_ir->instructions.InsertBefore(bytecode, hook_invoke);
142 
143     // move result back to the right register
144     //
145     // NOTE: we're reusing the original return's operand,
146     //   which is valid and more efficient than allocating
147     //   a new LIR node, but it's also fragile: we need to be
148     //   very careful about mutating shared nodes.
149     //
150     if (move_result_opcode != dex::OP_NOP) {
151       auto move_result = code_ir->Alloc<lir::Bytecode>();
152       move_result->opcode = move_result_opcode;
153       move_result->operands.push_back(bytecode->operands[0]);
154       code_ir->instructions.InsertBefore(bytecode, move_result);
155     }
156   }
157 
158   return true;
159 }
160 
Apply(lir::CodeIr * code_ir)161 bool DetourVirtualInvoke::Apply(lir::CodeIr* code_ir) {
162   ir::Builder builder(code_ir->dex_ir);
163 
164   // search for matching invoke-virtual[/range] bytecodes
165   for (auto instr : code_ir->instructions) {
166     auto bytecode = dynamic_cast<lir::Bytecode*>(instr);
167     if (bytecode == nullptr) {
168       continue;
169     }
170 
171     dex::Opcode new_call_opcode = dex::OP_NOP;
172     switch (bytecode->opcode) {
173       case dex::OP_INVOKE_VIRTUAL:
174         new_call_opcode = dex::OP_INVOKE_STATIC;
175         break;
176       case dex::OP_INVOKE_VIRTUAL_RANGE:
177         new_call_opcode = dex::OP_INVOKE_STATIC_RANGE;
178         break;
179       default:
180         // skip instruction ...
181         continue;
182     }
183     assert(new_call_opcode != dex::OP_NOP);
184 
185     auto orig_method = bytecode->CastOperand<lir::Method>(1)->ir_method;
186     if (!orig_method_id_.Match(orig_method)) {
187       // this is not the method you're looking for...
188       continue;
189     }
190 
191     // construct the detour method declaration
192     // (matching the original method, plus an explicit "this" argument)
193     std::vector<ir::Type*> param_types;
194     param_types.push_back(orig_method->parent);
195     if (orig_method->prototype->param_types != nullptr) {
196       const auto& orig_param_types = orig_method->prototype->param_types->types;
197       param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
198     }
199 
200     auto ir_proto = builder.GetProto(orig_method->prototype->return_type,
201                                      builder.GetTypeList(param_types));
202 
203     auto ir_method_decl = builder.GetMethodDecl(
204         builder.GetAsciiString(detour_method_id_.method_name), ir_proto,
205         builder.GetType(detour_method_id_.class_descriptor));
206 
207     auto detour_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
208 
209     // We mutate the original invoke bytecode in-place: this is ok
210     // because lir::Instructions can't be shared (referenced multiple times)
211     // in the code IR. It's also simpler and more efficient than allocating a
212     // new IR invoke bytecode.
213     bytecode->opcode = new_call_opcode;
214     bytecode->operands[1] = detour_method;
215   }
216 
217   return true;
218 }
219 
220 // Register re-numbering visitor
221 // (renumbers vN to vN+shift)
222 class RegsRenumberVisitor : public lir::Visitor {
223  public:
RegsRenumberVisitor(int shift)224   RegsRenumberVisitor(int shift) : shift_(shift) {
225     SLICER_CHECK(shift > 0);
226   }
227 
228  private:
Visit(lir::Bytecode * bytecode)229   virtual bool Visit(lir::Bytecode* bytecode) override {
230     for (auto operand : bytecode->operands) {
231       operand->Accept(this);
232     }
233     return true;
234   }
235 
Visit(lir::DbgInfoAnnotation * dbg_annotation)236   virtual bool Visit(lir::DbgInfoAnnotation* dbg_annotation) override {
237     for (auto operand : dbg_annotation->operands) {
238       operand->Accept(this);
239     }
240     return true;
241   }
242 
Visit(lir::VReg * vreg)243   virtual bool Visit(lir::VReg* vreg) override {
244     vreg->reg += shift_;
245     return true;
246   }
247 
Visit(lir::VRegPair * vreg_pair)248   virtual bool Visit(lir::VRegPair* vreg_pair) override {
249     vreg_pair->base_reg += shift_;
250     return true;
251   }
252 
Visit(lir::VRegList * vreg_list)253   virtual bool Visit(lir::VRegList* vreg_list) override {
254     for (auto& reg : vreg_list->registers) {
255       reg += shift_;
256     }
257     return true;
258   }
259 
Visit(lir::VRegRange * vreg_range)260   virtual bool Visit(lir::VRegRange* vreg_range) override {
261     vreg_range->base_reg += shift_;
262     return true;
263   }
264 
265  private:
266   int shift_ = 0;
267 };
268 
269 // Try to allocate registers by renumbering the existing allocation
270 //
271 // NOTE: we can't bump the register count over 16 since it may
272 //  make existing bytecodes "unencodable" (if they have 4 bit reg fields)
273 //
RegsRenumbering(lir::CodeIr * code_ir)274 void AllocateScratchRegs::RegsRenumbering(lir::CodeIr* code_ir) {
275   SLICER_CHECK(left_to_allocate_ > 0);
276   int delta = std::min(left_to_allocate_,
277                        16 - static_cast<int>(code_ir->ir_method->code->registers));
278   if (delta < 1) {
279     // can't allocate any registers through renumbering
280     return;
281   }
282   assert(delta <= 16);
283 
284   // renumber existing registers
285   RegsRenumberVisitor visitor(delta);
286   for (auto instr : code_ir->instructions) {
287     instr->Accept(&visitor);
288   }
289 
290   // we just allocated "delta" registers (v0..vX)
291   Allocate(code_ir, 0, delta);
292 }
293 
294 // Allocates registers by generating prologue code to relocate params
295 // into their original registers (parameters are allocated in the last IN registers)
296 //
297 // There are three types of register moves depending on the value type:
298 // 1. vreg -> vreg
299 // 2. vreg/wide -> vreg/wide
300 // 3. vreg/obj -> vreg/obj
301 //
ShiftParams(lir::CodeIr * code_ir)302 void AllocateScratchRegs::ShiftParams(lir::CodeIr* code_ir) {
303   const auto ir_method = code_ir->ir_method;
304   SLICER_CHECK(ir_method->code->ins_count > 0);
305   SLICER_CHECK(left_to_allocate_ > 0);
306 
307   // build a param list with the explicit "this" argument for non-static methods
308   std::vector<ir::Type*> param_types;
309   if ((ir_method->access_flags & dex::kAccStatic) == 0) {
310     param_types.push_back(ir_method->decl->parent);
311   }
312   if (ir_method->decl->prototype->param_types != nullptr) {
313     const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
314     param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
315   }
316 
317   const dex::u4 shift = left_to_allocate_;
318 
319   Allocate(code_ir, ir_method->code->registers, left_to_allocate_);
320   assert(left_to_allocate_ == 0);
321 
322   const dex::u4 regs = ir_method->code->registers;
323   const dex::u4 ins_count = ir_method->code->ins_count;
324   SLICER_CHECK(regs >= ins_count);
325 
326   // generate the args "relocation" instructions
327   auto first_instr = code_ir->instructions.begin();
328   dex::u4 reg = regs - ins_count;
329   for (const auto& type : param_types) {
330     auto move = code_ir->Alloc<lir::Bytecode>();
331     switch (type->GetCategory()) {
332       case ir::Type::Category::Reference:
333         move->opcode = dex::OP_MOVE_OBJECT_16;
334         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
335         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
336         reg += 1;
337         break;
338       case ir::Type::Category::Scalar:
339         move->opcode = dex::OP_MOVE_16;
340         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
341         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
342         reg += 1;
343         break;
344       case ir::Type::Category::WideScalar:
345         move->opcode = dex::OP_MOVE_WIDE_16;
346         move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg - shift));
347         move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg));
348         reg += 2;
349         break;
350       case ir::Type::Category::Void:
351         SLICER_FATAL("void parameter type");
352     }
353     code_ir->instructions.insert(first_instr, move);
354   }
355 }
356 
357 // Mark [first_reg, first_reg + count) as scratch registers
Allocate(lir::CodeIr * code_ir,dex::u4 first_reg,int count)358 void AllocateScratchRegs::Allocate(lir::CodeIr* code_ir, dex::u4 first_reg, int count) {
359   SLICER_CHECK(count > 0 && count <= left_to_allocate_);
360   code_ir->ir_method->code->registers += count;
361   left_to_allocate_ -= count;
362   for (int i = 0; i < count; ++i) {
363     SLICER_CHECK(scratch_regs_.insert(first_reg + i).second);
364   }
365 }
366 
367 // Allocate scratch registers without doing a full register allocation:
368 //
369 // 1. if there are not params, increase the method regs count and we're done
370 // 2. if the method uses less than 16 registers, we can renumber the existing registers
371 // 3. if we still have registers to allocate, increase the method registers count,
372 //     and generate prologue code to shift the param regs into their original registers
373 //
Apply(lir::CodeIr * code_ir)374 bool AllocateScratchRegs::Apply(lir::CodeIr* code_ir) {
375   const auto code = code_ir->ir_method->code;
376   // .dex bytecode allows up to 64k vregs
377   SLICER_CHECK(code->registers + allocate_count_ <= (1 << 16));
378 
379   scratch_regs_.clear();
380   left_to_allocate_ = allocate_count_;
381 
382   // can we allocate by simply incrementing the method regs count?
383   if (code->ins_count == 0) {
384     Allocate(code_ir, code->registers, left_to_allocate_);
385     return true;
386   }
387 
388   // allocate as many registers as possible using renumbering
389   if (allow_renumbering_) {
390     RegsRenumbering(code_ir);
391   }
392 
393   // if we still have registers to allocate, generate prologue
394   // code to shift the params into their original registers
395   if (left_to_allocate_ > 0) {
396     ShiftParams(code_ir);
397   }
398 
399   assert(left_to_allocate_ == 0);
400   assert(scratch_regs_.size() == size_t(allocate_count_));
401   return true;
402 }
403 
InstrumentMethod(ir::EncodedMethod * ir_method)404 bool MethodInstrumenter::InstrumentMethod(ir::EncodedMethod* ir_method) {
405   SLICER_CHECK(ir_method != nullptr);
406   if (ir_method->code == nullptr) {
407     // can't instrument abstract methods
408     return false;
409   }
410 
411   // apply all the queued transformations
412   lir::CodeIr code_ir(ir_method, dex_ir_);
413   for (const auto& transformation : transformations_) {
414     if (!transformation->Apply(&code_ir)) {
415       // the transformation failed, bail out...
416       return false;
417     }
418   }
419   code_ir.Assemble();
420   return true;
421 }
422 
InstrumentMethod(const ir::MethodId & method_id)423 bool MethodInstrumenter::InstrumentMethod(const ir::MethodId& method_id) {
424   // locate the method to be instrumented
425   ir::Builder builder(dex_ir_);
426   auto ir_method = builder.FindMethod(method_id);
427   if (ir_method == nullptr) {
428     // we couldn't find the specified method
429     return false;
430   }
431   return InstrumentMethod(ir_method);
432 }
433 
434 }  // namespace slicer
435