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