• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 namespace {
23 
24 struct BytecodeConvertingVisitor : public lir::Visitor {
25   lir::Bytecode* out = nullptr;
Visitslicer::__anonf1c200fa0111::BytecodeConvertingVisitor26   bool Visit(lir::Bytecode* bytecode) {
27     out = bytecode;
28     return true;
29   }
30 };
31 
BoxValue(lir::Bytecode * bytecode,lir::CodeIr * code_ir,ir::Type * type,dex::u4 src_reg,dex::u4 dst_reg)32 void BoxValue(lir::Bytecode* bytecode,
33               lir::CodeIr* code_ir,
34               ir::Type* type,
35               dex::u4 src_reg,
36               dex::u4 dst_reg) {
37   bool is_wide = false;
38   const char* boxed_type_name = nullptr;
39   switch (*(type->descriptor)->c_str()) {
40     case 'Z':
41       boxed_type_name = "Ljava/lang/Boolean;";
42       break;
43     case 'B':
44       boxed_type_name = "Ljava/lang/Byte;";
45       break;
46     case 'C':
47       boxed_type_name = "Ljava/lang/Character;";
48       break;
49     case 'S':
50       boxed_type_name = "Ljava/lang/Short;";
51       break;
52     case 'I':
53       boxed_type_name = "Ljava/lang/Integer;";
54       break;
55     case 'J':
56       is_wide = true;
57       boxed_type_name = "Ljava/lang/Long;";
58       break;
59     case 'F':
60       boxed_type_name = "Ljava/lang/Float;";
61       break;
62     case 'D':
63       is_wide = true;
64       boxed_type_name = "Ljava/lang/Double;";
65       break;
66   }
67   SLICER_CHECK(boxed_type_name != nullptr);
68 
69   ir::Builder builder(code_ir->dex_ir);
70   std::vector<ir::Type*> param_types;
71   param_types.push_back(type);
72 
73   auto boxed_type = builder.GetType(boxed_type_name);
74   auto ir_proto = builder.GetProto(boxed_type, builder.GetTypeList(param_types));
75 
76   auto ir_method_decl = builder.GetMethodDecl(
77       builder.GetAsciiString("valueOf"), ir_proto, boxed_type);
78 
79   auto boxing_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
80 
81   auto args = code_ir->Alloc<lir::VRegRange>(src_reg, 1 + is_wide);
82   auto boxing_invoke = code_ir->Alloc<lir::Bytecode>();
83   boxing_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
84   boxing_invoke->operands.push_back(args);
85   boxing_invoke->operands.push_back(boxing_method);
86   code_ir->instructions.InsertBefore(bytecode, boxing_invoke);
87 
88   auto move_result = code_ir->Alloc<lir::Bytecode>();
89   move_result->opcode = dex::OP_MOVE_RESULT_OBJECT;
90   move_result->operands.push_back(code_ir->Alloc<lir::VReg>(dst_reg));
91   code_ir->instructions.InsertBefore(bytecode, move_result);
92 }
93 
MethodLabel(ir::EncodedMethod * ir_method)94 std::string MethodLabel(ir::EncodedMethod* ir_method) {
95   auto signature_str = ir_method->decl->prototype->Signature();
96   return ir_method->decl->parent->Decl() + "->" + ir_method->decl->name->c_str() + signature_str;
97 }
98 
99 }  // namespace
100 
Apply(lir::CodeIr * code_ir)101 bool EntryHook::Apply(lir::CodeIr* code_ir) {
102   lir::Bytecode* bytecode = nullptr;
103   // find the first bytecode in the method body to insert the hook before it
104   for (auto instr : code_ir->instructions) {
105     BytecodeConvertingVisitor visitor;
106     instr->Accept(&visitor);
107     bytecode = visitor.out;
108     if (bytecode != nullptr) {
109       break;
110     }
111   }
112   if (bytecode == nullptr) {
113     return false;
114   }
115   if (tweak_ == Tweak::ArrayParams) {
116     return InjectArrayParamsHook(code_ir, bytecode);
117   }
118 
119   ir::Builder builder(code_ir->dex_ir);
120   const auto ir_method = code_ir->ir_method;
121 
122   // construct the hook method declaration
123   std::vector<ir::Type*> param_types;
124   if ((ir_method->access_flags & dex::kAccStatic) == 0) {
125     ir::Type* this_argument_type;
126     switch (tweak_) {
127       case Tweak::ThisAsObject:
128         this_argument_type = builder.GetType("Ljava/lang/Object;");
129         break;
130       default:
131         this_argument_type = ir_method->decl->parent;
132         break;
133     }
134     param_types.push_back(this_argument_type);
135   }
136   if (ir_method->decl->prototype->param_types != nullptr) {
137     const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
138     param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
139   }
140 
141   auto ir_proto = builder.GetProto(builder.GetType("V"),
142                                    builder.GetTypeList(param_types));
143 
144   auto ir_method_decl = builder.GetMethodDecl(
145       builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
146       builder.GetType(hook_method_id_.class_descriptor));
147 
148   auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
149 
150   // argument registers
151   auto regs = ir_method->code->registers;
152   auto args_count = ir_method->code->ins_count;
153   auto args = code_ir->Alloc<lir::VRegRange>(regs - args_count, args_count);
154 
155   // invoke hook bytecode
156   auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
157   hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
158   hook_invoke->operands.push_back(args);
159   hook_invoke->operands.push_back(hook_method);
160 
161   // insert the hook before the first bytecode in the method body
162   code_ir->instructions.InsertBefore(bytecode, hook_invoke);
163   return true;
164 }
165 
GenerateShiftParamsCode(lir::CodeIr * code_ir,lir::Instruction * position,dex::u4 shift)166 void GenerateShiftParamsCode(lir::CodeIr* code_ir, lir::Instruction* position, dex::u4 shift) {
167   const auto ir_method = code_ir->ir_method;
168   SLICER_CHECK(ir_method->code->ins_count > 0);
169 
170   // build a param list with the explicit "this" argument for non-static methods
171   std::vector<ir::Type*> param_types;
172   if ((ir_method->access_flags & dex::kAccStatic) == 0) {
173     param_types.push_back(ir_method->decl->parent);
174   }
175   if (ir_method->decl->prototype->param_types != nullptr) {
176     const auto& orig_param_types = ir_method->decl->prototype->param_types->types;
177     param_types.insert(param_types.end(), orig_param_types.begin(), orig_param_types.end());
178   }
179 
180   const dex::u4 regs = ir_method->code->registers;
181   const dex::u4 ins_count = ir_method->code->ins_count;
182   SLICER_CHECK(regs >= ins_count);
183 
184   // generate the args "relocation" instructions
185   dex::u4 reg = regs - ins_count;
186   for (const auto& type : param_types) {
187     auto move = code_ir->Alloc<lir::Bytecode>();
188     switch (type->GetCategory()) {
189       case ir::Type::Category::Reference:
190         move->opcode = dex::OP_MOVE_OBJECT_16;
191         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
192         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
193         reg += 1;
194         break;
195       case ir::Type::Category::Scalar:
196         move->opcode = dex::OP_MOVE_16;
197         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg - shift));
198         move->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
199         reg += 1;
200         break;
201       case ir::Type::Category::WideScalar:
202         move->opcode = dex::OP_MOVE_WIDE_16;
203         move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg - shift));
204         move->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg));
205         reg += 2;
206         break;
207       case ir::Type::Category::Void:
208         SLICER_FATAL("void parameter type");
209     }
210     code_ir->instructions.InsertBefore(position, move);
211   }
212 }
213 
InjectArrayParamsHook(lir::CodeIr * code_ir,lir::Bytecode * bytecode)214 bool EntryHook::InjectArrayParamsHook(lir::CodeIr* code_ir, lir::Bytecode* bytecode) {
215   ir::Builder builder(code_ir->dex_ir);
216   const auto ir_method = code_ir->ir_method;
217   auto param_types_list = ir_method->decl->prototype->param_types;
218   auto param_types = param_types_list != nullptr ? param_types_list->types : std::vector<ir::Type*>();
219   bool is_static = (ir_method->access_flags & dex::kAccStatic) != 0;
220 
221   // number of registers that we need to operate
222   dex::u2 regs_count = 3;
223   auto non_param_regs = ir_method->code->registers - ir_method->code->ins_count;
224 
225   // do we have enough registers to operate?
226   bool needsExtraRegs = non_param_regs < regs_count;
227   if (needsExtraRegs) {
228     // we don't have enough registers, so we allocate more, we will shift
229     // params to their original registers later.
230     code_ir->ir_method->code->registers += regs_count - non_param_regs;
231   }
232 
233   // use three first registers:
234   // all three are needed when we "aput" a string/boxed-value (1) into an array (2) at an index (3)
235 
236   // register that will store size of during allocation
237   // later will be reused to store index when do "aput"
238   dex::u4 array_size_reg = 0;
239   // register that will store an array that will be passed
240   // as a parameter in entry hook
241   dex::u4 array_reg = 1;
242   // stores result of boxing (if it's needed); also stores the method signature string
243   dex::u4 value_reg = 2;
244   // array size bytecode
245   auto const_size_op = code_ir->Alloc<lir::Bytecode>();
246   const_size_op->opcode = dex::OP_CONST;
247   const_size_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_size_reg));
248   const_size_op->operands.push_back(code_ir->Alloc<lir::Const32>(
249       2 + param_types.size())); // method signature + params + "this" object
250   code_ir->instructions.InsertBefore(bytecode, const_size_op);
251 
252   // allocate array
253   const auto obj_array_type = builder.GetType("[Ljava/lang/Object;");
254   auto allocate_array_op = code_ir->Alloc<lir::Bytecode>();
255   allocate_array_op->opcode = dex::OP_NEW_ARRAY;
256   allocate_array_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_reg));
257   allocate_array_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_size_reg));
258   allocate_array_op->operands.push_back(
259       code_ir->Alloc<lir::Type>(obj_array_type, obj_array_type->orig_index));
260   code_ir->instructions.InsertBefore(bytecode, allocate_array_op);
261 
262   // fill the array with parameters passed into function
263 
264   std::vector<ir::Type*> types;
265   types.push_back(builder.GetType("Ljava/lang/String;")); // method signature string
266   if (!is_static) {
267     types.push_back(ir_method->decl->parent); // "this" object
268   }
269 
270   types.insert(types.end(), param_types.begin(), param_types.end()); // parameters
271 
272   // register where params start
273   dex::u4 current_reg = ir_method->code->registers - ir_method->code->ins_count;
274   // reuse not needed anymore register to store indexes
275   dex::u4 array_index_reg = array_size_reg;
276   int i = 0;
277   for (auto type: types) {
278     dex::u4 src_reg = 0;
279     if (i == 0) { // method signature string
280       // e.g. const-string v2, "(I[Ljava/lang/String;)Ljava/lang/String;"
281       // for (int, String[]) -> String
282       auto const_str_op = code_ir->Alloc<lir::Bytecode>();
283       const_str_op->opcode = dex::OP_CONST_STRING;
284       const_str_op->operands.push_back(code_ir->Alloc<lir::VReg>(value_reg)); // dst
285       auto method_label = builder.GetAsciiString(MethodLabel(ir_method).c_str());
286       const_str_op->operands.push_back(
287           code_ir->Alloc<lir::String>(method_label, method_label->orig_index)); // src
288       code_ir->instructions.InsertBefore(bytecode, const_str_op);
289       src_reg = value_reg;
290     } else if (type->GetCategory() != ir::Type::Category::Reference) {
291       BoxValue(bytecode, code_ir, type, current_reg, value_reg);
292       src_reg = value_reg;
293       current_reg += 1 + (type->GetCategory() == ir::Type::Category::WideScalar);
294     } else {
295       src_reg = current_reg;
296       current_reg++;
297     }
298 
299     auto index_const_op = code_ir->Alloc<lir::Bytecode>();
300     index_const_op->opcode = dex::OP_CONST;
301     index_const_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_index_reg));
302     index_const_op->operands.push_back(code_ir->Alloc<lir::Const32>(i++));
303     code_ir->instructions.InsertBefore(bytecode, index_const_op);
304 
305     auto aput_op = code_ir->Alloc<lir::Bytecode>();
306     aput_op->opcode = dex::OP_APUT_OBJECT;
307     aput_op->operands.push_back(code_ir->Alloc<lir::VReg>(src_reg));
308     aput_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_reg));
309     aput_op->operands.push_back(code_ir->Alloc<lir::VReg>(array_index_reg));
310     code_ir->instructions.InsertBefore(bytecode, aput_op);
311 
312     // if function is static, then jumping over index 1
313     //  since null should be be passed in this case
314     if (i == 1 && is_static) i++;
315   }
316 
317   std::vector<ir::Type*> hook_param_types;
318   hook_param_types.push_back(obj_array_type);
319 
320   auto ir_proto = builder.GetProto(builder.GetType("V"),
321                                    builder.GetTypeList(hook_param_types));
322 
323   auto ir_method_decl = builder.GetMethodDecl(
324       builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
325       builder.GetType(hook_method_id_.class_descriptor));
326 
327   auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
328   auto args = code_ir->Alloc<lir::VRegRange>(array_reg, 1);
329   auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
330   hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
331   hook_invoke->operands.push_back(args);
332   hook_invoke->operands.push_back(hook_method);
333   code_ir->instructions.InsertBefore(bytecode, hook_invoke);
334 
335   // clean up registries used by us
336   // registers are assigned to a marker value 0xFE_FE_FE_FE (decimal
337   // value: -16843010) to help identify use of uninitialized registers.
338   for (dex::u2 i = 0; i < regs_count; ++i) {
339     auto cleanup = code_ir->Alloc<lir::Bytecode>();
340     cleanup->opcode = dex::OP_CONST;
341     cleanup->operands.push_back(code_ir->Alloc<lir::VReg>(i));
342     cleanup->operands.push_back(code_ir->Alloc<lir::Const32>(0xFEFEFEFE));
343     code_ir->instructions.InsertBefore(bytecode, cleanup);
344   }
345 
346   // now we have to shift params to their original registers
347   if (needsExtraRegs) {
348     GenerateShiftParamsCode(code_ir, bytecode, regs_count - non_param_regs);
349   }
350   return true;
351 }
352 
Apply(lir::CodeIr * code_ir)353 bool ExitHook::Apply(lir::CodeIr* code_ir) {
354   ir::Builder builder(code_ir->dex_ir);
355   const auto ir_method = code_ir->ir_method;
356   const auto declared_return_type = ir_method->decl->prototype->return_type;
357   bool return_as_object = (tweak_ & Tweak::ReturnAsObject) != 0;
358   // do we have a void-return method?
359   bool return_void = (::strcmp(declared_return_type->descriptor->c_str(), "V") == 0);
360   // returnAsObject supports only object return type;
361   SLICER_CHECK(!return_as_object ||
362       (declared_return_type->GetCategory() == ir::Type::Category::Reference));
363   const auto return_type = return_as_object ? builder.GetType("Ljava/lang/Object;")
364       : declared_return_type;
365 
366   bool pass_method_signature = (tweak_ & Tweak::PassMethodSignature) != 0;
367   // construct the hook method declaration
368   std::vector<ir::Type*> param_types;
369   if (pass_method_signature) {
370     param_types.push_back(builder.GetType("Ljava/lang/String;"));
371   }
372   if (!return_void) {
373     param_types.push_back(return_type);
374   }
375 
376   auto ir_proto = builder.GetProto(return_type, builder.GetTypeList(param_types));
377 
378   auto ir_method_decl = builder.GetMethodDecl(
379       builder.GetAsciiString(hook_method_id_.method_name), ir_proto,
380       builder.GetType(hook_method_id_.class_descriptor));
381 
382   auto hook_method = code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
383 
384   // find and instrument all return instructions
385   for (auto instr : code_ir->instructions) {
386     BytecodeConvertingVisitor visitor;
387     instr->Accept(&visitor);
388     auto bytecode = visitor.out;
389     if (bytecode == nullptr) {
390       continue;
391     }
392 
393     dex::Opcode move_result_opcode = dex::OP_NOP;
394     dex::u4 reg = 0;
395     int reg_count = 0;
396     switch (bytecode->opcode) {
397       case dex::OP_RETURN_VOID:
398         SLICER_CHECK(return_void);
399         break;
400       case dex::OP_RETURN:
401         SLICER_CHECK(!return_void);
402         move_result_opcode = dex::OP_MOVE_RESULT;
403         reg = bytecode->CastOperand<lir::VReg>(0)->reg;
404         reg_count = 1;
405         break;
406       case dex::OP_RETURN_OBJECT:
407         SLICER_CHECK(!return_void);
408         move_result_opcode = dex::OP_MOVE_RESULT_OBJECT;
409         reg = bytecode->CastOperand<lir::VReg>(0)->reg;
410         reg_count = 1;
411         break;
412       case dex::OP_RETURN_WIDE:
413         SLICER_CHECK(!return_void);
414         move_result_opcode = dex::OP_MOVE_RESULT_WIDE;
415         reg = bytecode->CastOperand<lir::VRegPair>(0)->base_reg;
416         reg_count = 2;
417         break;
418       default:
419         // skip the bytecode...
420         continue;
421     }
422 
423     dex::u4 scratch_reg = 0;
424     // load method signature into scratch_reg
425     if (pass_method_signature) {
426       // is there a register that can be overtaken
427       bool needsScratchReg = ir_method->code->registers < reg_count + 1;
428       if (needsScratchReg) {
429         // don't renumber registers underneath us
430         slicer::AllocateScratchRegs alloc_regs(1, false);
431         alloc_regs.Apply(code_ir);
432       }
433 
434       // we need use one register before results to put signature there
435       // however result starts in register 0, thefore it is shifted
436       // to register 1
437       if (reg == 0 && bytecode->opcode != dex::OP_RETURN_VOID) {
438         auto move_op = code_ir->Alloc<lir::Bytecode>();
439         switch (bytecode->opcode) {
440           case dex::OP_RETURN_OBJECT:
441             move_op->opcode = dex::OP_MOVE_OBJECT_16;
442             move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg + 1));
443             move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
444             break;
445           case dex::OP_RETURN:
446             move_op->opcode = dex::OP_MOVE_16;
447             move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg + 1));
448             move_op->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
449             break;
450           case dex::OP_RETURN_WIDE:
451             move_op->opcode = dex::OP_MOVE_WIDE_16;
452             move_op->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg + 1));
453             move_op->operands.push_back(code_ir->Alloc<lir::VRegPair>(reg));
454             break;
455           default:
456             SLICER_FATAL("Unexpected opcode %d", bytecode->opcode);
457         }
458         code_ir->instructions.InsertBefore(bytecode, move_op);
459         // return is the last call, return is shifted to one, so taking over 0 registry
460         scratch_reg = 0;
461       } else {
462         // return is the last call, so we're taking over previous registry
463         scratch_reg = bytecode->opcode == dex::OP_RETURN_VOID ? 0 : reg - 1;
464       }
465 
466 
467       // return is the last call, so we're taking over previous registry
468       auto method_label = builder.GetAsciiString(MethodLabel(ir_method).c_str());
469       auto const_str_op = code_ir->Alloc<lir::Bytecode>();
470       const_str_op->opcode = dex::OP_CONST_STRING;
471       const_str_op->operands.push_back(code_ir->Alloc<lir::VReg>(scratch_reg)); // dst
472       const_str_op->operands.push_back(code_ir->Alloc<lir::String>(method_label, method_label->orig_index)); // src
473       code_ir->instructions.InsertBefore(bytecode, const_str_op);
474     }
475 
476     auto args = pass_method_signature
477         ? code_ir->Alloc<lir::VRegRange>(scratch_reg, reg_count + 1)
478         : code_ir->Alloc<lir::VRegRange>(reg, reg_count);
479     auto hook_invoke = code_ir->Alloc<lir::Bytecode>();
480     hook_invoke->opcode = dex::OP_INVOKE_STATIC_RANGE;
481     hook_invoke->operands.push_back(args);
482     hook_invoke->operands.push_back(hook_method);
483     code_ir->instructions.InsertBefore(bytecode, hook_invoke);
484 
485     // move result back to the right register
486     //
487     // NOTE: we're reusing the original return's operand,
488     //   which is valid and more efficient than allocating
489     //   a new LIR node, but it's also fragile: we need to be
490     //   very careful about mutating shared nodes.
491     //
492     if (move_result_opcode != dex::OP_NOP) {
493       auto move_result = code_ir->Alloc<lir::Bytecode>();
494       move_result->opcode = move_result_opcode;
495       move_result->operands.push_back(bytecode->operands[0]);
496       code_ir->instructions.InsertBefore(bytecode, move_result);
497 
498       if ((tweak_ & Tweak::ReturnAsObject) != 0) {
499         auto check_cast = code_ir->Alloc<lir::Bytecode>();
500         check_cast->opcode = dex::OP_CHECK_CAST;
501         check_cast->operands.push_back(code_ir->Alloc<lir::VReg>(reg));
502         check_cast->operands.push_back(
503             code_ir->Alloc<lir::Type>(declared_return_type, declared_return_type->orig_index));
504         code_ir->instructions.InsertBefore(bytecode, check_cast);
505       }
506     }
507   }
508 
509   return true;
510 }
511 
Apply(lir::CodeIr * code_ir)512 bool DetourHook::Apply(lir::CodeIr* code_ir) {
513   ir::Builder builder(code_ir->dex_ir);
514 
515   // search for matching invoke-virtual[/range] bytecodes
516   for (auto instr : code_ir->instructions) {
517     BytecodeConvertingVisitor visitor;
518     instr->Accept(&visitor);
519     auto bytecode = visitor.out;
520     if (bytecode == nullptr) {
521       continue;
522     }
523 
524     dex::Opcode new_call_opcode = GetNewOpcode(bytecode->opcode);
525     if (new_call_opcode == dex::OP_NOP) {
526       continue;
527     }
528 
529     auto orig_method = bytecode->CastOperand<lir::Method>(1)->ir_method;
530     if (!orig_method_id_.Match(orig_method)) {
531       // this is not the method you're looking for...
532       continue;
533     }
534 
535     // construct the detour method declaration
536     // (matching the original method, plus an explicit "this" argument)
537     std::vector<ir::Type*> param_types;
538     param_types.push_back(orig_method->parent);
539     if (orig_method->prototype->param_types != nullptr) {
540       const auto& orig_param_types = orig_method->prototype->param_types->types;
541       param_types.insert(param_types.end(), orig_param_types.begin(),
542                          orig_param_types.end());
543     }
544 
545     auto ir_proto = builder.GetProto(orig_method->prototype->return_type,
546                                      builder.GetTypeList(param_types));
547 
548     auto ir_method_decl = builder.GetMethodDecl(
549         builder.GetAsciiString(detour_method_id_.method_name), ir_proto,
550         builder.GetType(detour_method_id_.class_descriptor));
551 
552     auto detour_method =
553         code_ir->Alloc<lir::Method>(ir_method_decl, ir_method_decl->orig_index);
554 
555     // We mutate the original invoke bytecode in-place: this is ok
556     // because lir::Instructions can't be shared (referenced multiple times)
557     // in the code IR. It's also simpler and more efficient than allocating a
558     // new IR invoke bytecode.
559     bytecode->opcode = new_call_opcode;
560     bytecode->operands[1] = detour_method;
561   }
562 
563   return true;
564 }
565 
GetNewOpcode(dex::Opcode opcode)566 dex::Opcode DetourVirtualInvoke::GetNewOpcode(dex::Opcode opcode) {
567   switch (opcode) {
568     case dex::OP_INVOKE_VIRTUAL:
569       return dex::OP_INVOKE_STATIC;
570     case dex::OP_INVOKE_VIRTUAL_RANGE:
571       return dex::OP_INVOKE_STATIC_RANGE;
572     default:
573       // skip instruction ...
574       return dex::OP_NOP;
575   }
576 }
577 
GetNewOpcode(dex::Opcode opcode)578 dex::Opcode DetourInterfaceInvoke::GetNewOpcode(dex::Opcode opcode) {
579   switch (opcode) {
580     case dex::OP_INVOKE_INTERFACE:
581       return dex::OP_INVOKE_STATIC;
582     case dex::OP_INVOKE_INTERFACE_RANGE:
583       return dex::OP_INVOKE_STATIC_RANGE;
584     default:
585       // skip instruction ...
586       return dex::OP_NOP;
587   }
588 }
589 
590 // Register re-numbering visitor
591 // (renumbers vN to vN+shift)
592 class RegsRenumberVisitor : public lir::Visitor {
593  public:
RegsRenumberVisitor(int shift)594   explicit RegsRenumberVisitor(int shift) : shift_(shift) {
595     SLICER_CHECK(shift > 0);
596   }
597 
598  private:
Visit(lir::Bytecode * bytecode)599   virtual bool Visit(lir::Bytecode* bytecode) override {
600     for (auto operand : bytecode->operands) {
601       operand->Accept(this);
602     }
603     return true;
604   }
605 
Visit(lir::DbgInfoAnnotation * dbg_annotation)606   virtual bool Visit(lir::DbgInfoAnnotation* dbg_annotation) override {
607     for (auto operand : dbg_annotation->operands) {
608       operand->Accept(this);
609     }
610     return true;
611   }
612 
Visit(lir::VReg * vreg)613   virtual bool Visit(lir::VReg* vreg) override {
614     vreg->reg += shift_;
615     return true;
616   }
617 
Visit(lir::VRegPair * vreg_pair)618   virtual bool Visit(lir::VRegPair* vreg_pair) override {
619     vreg_pair->base_reg += shift_;
620     return true;
621   }
622 
Visit(lir::VRegList * vreg_list)623   virtual bool Visit(lir::VRegList* vreg_list) override {
624     for (auto& reg : vreg_list->registers) {
625       reg += shift_;
626     }
627     return true;
628   }
629 
Visit(lir::VRegRange * vreg_range)630   virtual bool Visit(lir::VRegRange* vreg_range) override {
631     vreg_range->base_reg += shift_;
632     return true;
633   }
634 
635  private:
636   int shift_ = 0;
637 };
638 
639 // Try to allocate registers by renumbering the existing allocation
640 //
641 // NOTE: we can't bump the register count over 16 since it may
642 //  make existing bytecodes "unencodable" (if they have 4 bit reg fields)
643 //
RegsRenumbering(lir::CodeIr * code_ir)644 void AllocateScratchRegs::RegsRenumbering(lir::CodeIr* code_ir) {
645   SLICER_CHECK(left_to_allocate_ > 0);
646   int delta = std::min(left_to_allocate_,
647                        16 - static_cast<int>(code_ir->ir_method->code->registers));
648   if (delta < 1) {
649     // can't allocate any registers through renumbering
650     return;
651   }
652   assert(delta <= 16);
653 
654   // renumber existing registers
655   RegsRenumberVisitor visitor(delta);
656   for (auto instr : code_ir->instructions) {
657     instr->Accept(&visitor);
658   }
659 
660   // we just allocated "delta" registers (v0..vX)
661   Allocate(code_ir, 0, delta);
662 }
663 
664 // Allocates registers by generating prologue code to relocate params
665 // into their original registers (parameters are allocated in the last IN registers)
666 //
667 // There are three types of register moves depending on the value type:
668 // 1. vreg -> vreg
669 // 2. vreg/wide -> vreg/wide
670 // 3. vreg/obj -> vreg/obj
671 //
ShiftParams(lir::CodeIr * code_ir)672 void AllocateScratchRegs::ShiftParams(lir::CodeIr* code_ir) {
673   const auto ir_method = code_ir->ir_method;
674   SLICER_CHECK(left_to_allocate_ > 0);
675 
676   const dex::u4 shift = left_to_allocate_;
677   Allocate(code_ir, ir_method->code->registers, left_to_allocate_);
678   assert(left_to_allocate_ == 0);
679 
680   // generate the args "relocation" instructions
681   auto first_instr = *(code_ir->instructions.begin());
682   GenerateShiftParamsCode(code_ir, first_instr, shift);
683 }
684 
685 // Mark [first_reg, first_reg + count) as scratch registers
Allocate(lir::CodeIr * code_ir,dex::u4 first_reg,int count)686 void AllocateScratchRegs::Allocate(lir::CodeIr* code_ir, dex::u4 first_reg, int count) {
687   SLICER_CHECK(count > 0 && count <= left_to_allocate_);
688   code_ir->ir_method->code->registers += count;
689   left_to_allocate_ -= count;
690   for (int i = 0; i < count; ++i) {
691     SLICER_CHECK(scratch_regs_.insert(first_reg + i).second);
692   }
693 }
694 
695 // Allocate scratch registers without doing a full register allocation:
696 //
697 // 1. if there are not params, increase the method regs count and we're done
698 // 2. if the method uses less than 16 registers, we can renumber the existing registers
699 // 3. if we still have registers to allocate, increase the method registers count,
700 //     and generate prologue code to shift the param regs into their original registers
701 //
Apply(lir::CodeIr * code_ir)702 bool AllocateScratchRegs::Apply(lir::CodeIr* code_ir) {
703   const auto code = code_ir->ir_method->code;
704   // .dex bytecode allows up to 64k vregs
705   SLICER_CHECK(code->registers + allocate_count_ <= (1 << 16));
706 
707   scratch_regs_.clear();
708   left_to_allocate_ = allocate_count_;
709 
710   // can we allocate by simply incrementing the method regs count?
711   if (code->ins_count == 0) {
712     Allocate(code_ir, code->registers, left_to_allocate_);
713     return true;
714   }
715 
716   // allocate as many registers as possible using renumbering
717   if (allow_renumbering_) {
718     RegsRenumbering(code_ir);
719   }
720 
721   // if we still have registers to allocate, generate prologue
722   // code to shift the params into their original registers
723   if (left_to_allocate_ > 0) {
724     ShiftParams(code_ir);
725   }
726 
727   assert(left_to_allocate_ == 0);
728   assert(scratch_regs_.size() == size_t(allocate_count_));
729   return true;
730 }
731 
InstrumentMethod(ir::EncodedMethod * ir_method)732 bool MethodInstrumenter::InstrumentMethod(ir::EncodedMethod* ir_method) {
733   SLICER_CHECK(ir_method != nullptr);
734   if (ir_method->code == nullptr) {
735     // can't instrument abstract methods
736     return false;
737   }
738 
739   // apply all the queued transformations
740   lir::CodeIr code_ir(ir_method, dex_ir_);
741   for (const auto& transformation : transformations_) {
742     if (!transformation->Apply(&code_ir)) {
743       // the transformation failed, bail out...
744       return false;
745     }
746   }
747   code_ir.Assemble();
748   return true;
749 }
750 
InstrumentMethod(const ir::MethodId & method_id)751 bool MethodInstrumenter::InstrumentMethod(const ir::MethodId& method_id) {
752   // locate the method to be instrumented
753   ir::Builder builder(dex_ir_);
754   auto ir_method = builder.FindMethod(method_id);
755   if (ir_method == nullptr) {
756     // we couldn't find the specified method
757     return false;
758   }
759   return InstrumentMethod(ir_method);
760 }
761 
762 }  // namespace slicer
763