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