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