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