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