看雪3w班7月第二题,主要考察对ART解释器源码熟悉程度。
interpreter.cc
ps:Android7.0有三种解释模式
如果想查看函数的调用,可以通过hook如下函数,并通过ShadowFrame参数使用shadow_frame.GetMethod()->PrettyMethod()获取函数信息
interpreter模式:由ART下的解释器解释执行。对于任何一个运行在Interpreter模式下的java函数,最终都会走到ART下的解释器中运行。
流程:
从ArtMethod类中的EnterInterpreterFromInvoke开始分析Execute->ExecuteMterImpl或者ExecuteSwitchImpl。
interpreter.cc#Execute
interpreter_switch_impl.cc
interpreter_common.h
Instruction类有一个DumpString函数,可以输出当前指令的解析信息
dex_instruction.h
dex_instruction.cc
1、解释器改为switch模式
interpreter.cc
2、修改TraceExecution函数,针对感兴趣的函数筛选trace
interpreter_common.h
1、使用解释模式
runtime.cc里面有个Runtime::Init函数, 搜GetInstrumentation()->ForceInterpretOnly();, 在Runtime初始化的时候,调用一下ForceInterpretOnly就走解释模式了
runtime.cc
2、hook TraceExecution函数
interpreter_common.h
231 enum InterpreterImplKind {
232 kSwitchImplKind, // Switch-based interpreter implementation.
233 kMterpImplKind // Assembly interpreter
234 };
235
236 static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; //解释器的实现模式由该全局变量控制,默认汇编实现
237
231 enum InterpreterImplKind {
232 kSwitchImplKind, // Switch-based interpreter implementation.
233 kMterpImplKind // Assembly interpreter
234 };
235
236 static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; //解释器的实现模式由该全局变量控制,默认汇编实现
237
229 enum InterpreterImplKind {
230 kSwitchImplKind, // Switch-based interpreter implementation.
231 kComputedGotoImplKind, //Computed-goto-based interpreter implementation. goto跳转表模式
232 kMterpImplKind // Assembly interpreter
233 };
234 static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs) {
235 os << ((rhs == kSwitchImplKind)
236 ? "Switch-based interpreter"
237 : (rhs == kComputedGotoImplKind)
238 ? "Computed-goto-based interpreter"
239 : "Asm interpreter");
240 return os;
241 }
229 enum InterpreterImplKind {
230 kSwitchImplKind, // Switch-based interpreter implementation.
231 kComputedGotoImplKind, //Computed-goto-based interpreter implementation. goto跳转表模式
232 kMterpImplKind // Assembly interpreter
233 };
234 static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs) {
235 os << ((rhs == kSwitchImplKind)
236 ? "Switch-based interpreter"
237 : (rhs == kComputedGotoImplKind)
238 ? "Computed-goto-based interpreter"
239 : "Asm interpreter");
240 return os;
241 }
template<bool do_access_check, bool transaction_active>
extern JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
bool interpret_one_instruction);
template<bool do_access_check, bool transaction_active>
extern JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
// Mterp does not support transactions or access check, thus no templated versions.
extern "C" bool ExecuteMterpImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame, JValue* result_register);
template<bool do_access_check, bool transaction_active>
extern JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
bool interpret_one_instruction);
template<bool do_access_check, bool transaction_active>
extern JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register);
// Mterp does not support transactions or access check, thus no templated versions.
extern "C" bool ExecuteMterpImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame, JValue* result_register);
function hook_Impl() {
var module_libart = Process.findModuleByName("libart.so");
var symbols = module_libart.enumerateSymbols();
var ArtMethod_ExecuteSwitchImpltt = null;
var ArtMethod_ExecuteSwitchImpltf = null;
var ArtMethod_ExecuteSwitchImplff = null;
var ArtMethod_ExecuteMterpImpl = null;
//_ZN3art11interpreter17ExecuteSwitchImplILb1ELb1EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2_b ; art::interpreter::ExecuteSwitchImpl<true,true>(art::Thread *,art::DexFile::CodeItem const*,art::ShadowFrame &,art::JValue,bool)
//_ZN3art11interpreter17ExecuteSwitchImplILb1ELb0EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2_b ; art::interpreter::ExecuteSwitchImpl<true,false>(art::Thread *,art::DexFile::CodeItem const*,art::ShadowFrame &,art::JValue,bool)
//_ZN3art11interpreter17ExecuteSwitchImplILb0ELb0EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2_b ; art::interpreter::ExecuteSwitchImpl<false,false>(art::Thread *,art::DexFile::CodeItem const*,art::ShadowFrame &,art::JValue,bool)
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
var address = symbol.address;
var name = symbol.name;
var indexinterpreter = name.indexOf("interpreter");
var indexExecuteSwitchImpl = name.indexOf("ExecuteSwitchImpl");
var indexExecuteMterpImpl = name.indexOf("ExecuteMterpImpl");
var indexShadowFrame = name.indexOf("ShadowFrame");
var indextt = name.indexOf("b1ELb1");
var indextf = name.indexOf("b1ELb0");
var indexff = name.indexOf("b0ELb0");
var ArtMethod_PrettyMethod = null;
if (name.indexOf("ArtMethod") >= 0 && name.indexOf("PrettyMethod") >= 0 && name.indexOf("Eb") >= 0 ) {
console.log(name);
ArtMethod_PrettyMethod = address;
}
if (indexinterpreter >= 0
&& indexExecuteSwitchImpl >= 0
&& indexShadowFrame >= 0
&& indextt >= 0) {
console.log(name);
ArtMethod_ExecuteSwitchImpltt = address;
}
if (indexinterpreter >= 0
&& indexExecuteSwitchImpl >= 0
&& indexShadowFrame >= 0
&& indextf >= 0) {
console.log(name);
ArtMethod_ExecuteSwitchImpltf = address;
}
if (indexinterpreter >= 0
&& indexExecuteSwitchImpl >= 0
&& indexShadowFrame >= 0
&& indexff >= 0) {
console.log(name);
ArtMethod_ExecuteSwitchImplff = address;
}
if(indexExecuteMterpImpl >= 0) {
console.log(name);
ArtMethod_ExecuteMterpImpl = address;
}
}
var module_libext = null;
if (Process.arch === "arm64") {
module_libext = Module.load("/data/app/libext64.so");
} else if (Process.arch === "arm") {
module_libext = Module.load("/data/app/libext.so");
}
if (ArtMethod_ExecuteSwitchImpltt != null) {
Interceptor.attach(ArtMethod_ExecuteSwitchImpltt, {
onEnter: function (args) {
}, onLeave: function (retval) {
}
});
}
if (ArtMethod_ExecuteSwitchImpltf != null) {
Interceptor.attach(ArtMethod_ExecuteSwitchImpltf, {
onEnter: function (args) {
}, onLeave: function (retval) {
}
});
}
if (ArtMethod_ExecuteSwitchImplff != null) {
Interceptor.attach(ArtMethod_ExecuteSwitchImplff, {
onEnter: function (args) {
var shadow_frame = args[2];
var artMethodObj = ptr(shadow_frame).add(Process.pointerSize);
if (module_libext != null) {
var addr_PrettyMethod = module_libext.findExportByName("PrettyMethod");
var PrettyMethod = new NativeFunction(addr_PrettyMethod, "void", ["pointer", "pointer", "pointer", "int"])
var result = Memory.alloc(0x100);
try {
PrettyMethod(ArtMethod_PrettyMethod, artMethodObj, result, 0x100);
//console.log(result.readCString());
console.log("ArtMethod_ExecuteSwitchImplff java_class:", result.readCString());
} catch (error) {
console.log("ArtMethod_ExecuteSwitchImplff" + error);
}
}
}, onLeave: function (retval) {
}
});
}
if (ArtMethod_ExecuteMterpImpl != null) {
Interceptor.attach(ArtMethod_ExecuteMterpImpl, {
onEnter: function (args) {
}, onLeave: function (retval) {
}
});
}
}
function hook_Impl() {
var module_libart = Process.findModuleByName("libart.so");
var symbols = module_libart.enumerateSymbols();
var ArtMethod_ExecuteSwitchImpltt = null;
var ArtMethod_ExecuteSwitchImpltf = null;
var ArtMethod_ExecuteSwitchImplff = null;
var ArtMethod_ExecuteMterpImpl = null;
//_ZN3art11interpreter17ExecuteSwitchImplILb1ELb1EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2_b ; art::interpreter::ExecuteSwitchImpl<true,true>(art::Thread *,art::DexFile::CodeItem const*,art::ShadowFrame &,art::JValue,bool)
//_ZN3art11interpreter17ExecuteSwitchImplILb1ELb0EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2_b ; art::interpreter::ExecuteSwitchImpl<true,false>(art::Thread *,art::DexFile::CodeItem const*,art::ShadowFrame &,art::JValue,bool)
//_ZN3art11interpreter17ExecuteSwitchImplILb0ELb0EEENS_6JValueEPNS_6ThreadEPKNS_7DexFile8CodeItemERNS_11ShadowFrameES2_b ; art::interpreter::ExecuteSwitchImpl<false,false>(art::Thread *,art::DexFile::CodeItem const*,art::ShadowFrame &,art::JValue,bool)
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
var address = symbol.address;
var name = symbol.name;
var indexinterpreter = name.indexOf("interpreter");
var indexExecuteSwitchImpl = name.indexOf("ExecuteSwitchImpl");
var indexExecuteMterpImpl = name.indexOf("ExecuteMterpImpl");
var indexShadowFrame = name.indexOf("ShadowFrame");
var indextt = name.indexOf("b1ELb1");
var indextf = name.indexOf("b1ELb0");
var indexff = name.indexOf("b0ELb0");
var ArtMethod_PrettyMethod = null;
if (name.indexOf("ArtMethod") >= 0 && name.indexOf("PrettyMethod") >= 0 && name.indexOf("Eb") >= 0 ) {
console.log(name);
ArtMethod_PrettyMethod = address;
}
if (indexinterpreter >= 0
&& indexExecuteSwitchImpl >= 0
&& indexShadowFrame >= 0
&& indextt >= 0) {
console.log(name);
ArtMethod_ExecuteSwitchImpltt = address;
}
if (indexinterpreter >= 0
&& indexExecuteSwitchImpl >= 0
&& indexShadowFrame >= 0
&& indextf >= 0) {
console.log(name);
ArtMethod_ExecuteSwitchImpltf = address;
}
if (indexinterpreter >= 0
&& indexExecuteSwitchImpl >= 0
&& indexShadowFrame >= 0
&& indexff >= 0) {
console.log(name);
ArtMethod_ExecuteSwitchImplff = address;
}
if(indexExecuteMterpImpl >= 0) {
console.log(name);
ArtMethod_ExecuteMterpImpl = address;
}
}
var module_libext = null;
if (Process.arch === "arm64") {
module_libext = Module.load("/data/app/libext64.so");
} else if (Process.arch === "arm") {
module_libext = Module.load("/data/app/libext.so");
}
if (ArtMethod_ExecuteSwitchImpltt != null) {
Interceptor.attach(ArtMethod_ExecuteSwitchImpltt, {
onEnter: function (args) {
}, onLeave: function (retval) {
}
});
}
if (ArtMethod_ExecuteSwitchImpltf != null) {
Interceptor.attach(ArtMethod_ExecuteSwitchImpltf, {
onEnter: function (args) {
}, onLeave: function (retval) {
}
});
}
if (ArtMethod_ExecuteSwitchImplff != null) {
Interceptor.attach(ArtMethod_ExecuteSwitchImplff, {
onEnter: function (args) {
var shadow_frame = args[2];
var artMethodObj = ptr(shadow_frame).add(Process.pointerSize);
if (module_libext != null) {
var addr_PrettyMethod = module_libext.findExportByName("PrettyMethod");
var PrettyMethod = new NativeFunction(addr_PrettyMethod, "void", ["pointer", "pointer", "pointer", "int"])
var result = Memory.alloc(0x100);
try {
PrettyMethod(ArtMethod_PrettyMethod, artMethodObj, result, 0x100);
//console.log(result.readCString());
console.log("ArtMethod_ExecuteSwitchImplff java_class:", result.readCString());
} catch (error) {
console.log("ArtMethod_ExecuteSwitchImplff" + error);
}
}
}, onLeave: function (retval) {
}
});
}
if (ArtMethod_ExecuteMterpImpl != null) {
Interceptor.attach(ArtMethod_ExecuteMterpImpl, {
onEnter: function (args) {
}, onLeave: function (retval) {
}
});
}
}
238 static inline JValue Execute(
239 Thread* self,
240 const DexFile::CodeItem* code_item,
241 ShadowFrame& shadow_frame,
242 JValue result_register,
243 bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) {
244 DCHECK(!shadow_frame.GetMethod()->IsAbstract());
245 DCHECK(!shadow_frame.GetMethod()->IsNative());
246 if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization.
247 if (kIsDebugBuild) {
248 self->AssertNoPendingException();
249 }
250 instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
251 ArtMethod *method = shadow_frame.GetMethod();
252
253 if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
254 instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
255 method, 0);
256 }
257
258 if (!stay_in_interpreter) {
259 jit::Jit* jit = Runtime::Current()->GetJit();
260 if (jit != nullptr) {
261 jit->MethodEntered(self, shadow_frame.GetMethod());
262 if (jit->CanInvokeCompiledCode(method)) {
263 JValue result;
264
265 // Pop the shadow frame before calling into compiled code.
266 self->PopShadowFrame();
267 ArtInterpreterToCompiledCodeBridge(self, nullptr, code_item, &shadow_frame, &result);
268 // Push the shadow frame back as the caller will expect it.
269 self->PushShadowFrame(&shadow_frame);
270
271 return result;
272 }
273 }
274 }
275 }
276
277 shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
278
279 // Lock counting is a special version of accessibility checks, and for simplicity and
280 // reduction of template parameters, we gate it behind access-checks mode.
281 ArtMethod* method = shadow_frame.GetMethod();
282 DCHECK(!method->SkipAccessChecks() || !method->MustCountLocks());
283
284 bool transaction_active = Runtime::Current()->IsActiveTransaction();
285 if (LIKELY(method->SkipAccessChecks())) {
286 // Enter the "without access check" interpreter.
287 if (kInterpreterImplKind == kMterpImplKind) {
288 if (transaction_active) {
289 // No Mterp variant - just use the switch interpreter.
290 return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
291 false);
292 } else if (UNLIKELY(!Runtime::Current()->IsStarted())) {
293 return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
294 false);
295 } else {
296 while (true) {
297 // Mterp does not support all instrumentation/debugging.
298 if (MterpShouldSwitchInterpreters() != 0) {
299 return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
300 false);
301 }
302 bool returned = ExecuteMterpImpl(self, code_item, &shadow_frame, &result_register);
303 if (returned) {
304 return result_register;
305 } else {
306 // Mterp didn't like that instruction. Single-step it with the reference interpreter.
307 result_register = ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame,
308 result_register, true);
309 if (shadow_frame.GetDexPC() == DexFile::kDexNoIndex) {
310 // Single-stepped a return or an exception not handled locally. Return to caller.
311 return result_register;
312 }
313 }
314 }
315 }
316 } else {
317 DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
318 if (transaction_active) {
319 return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
320 false);
321 } else {
322 return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
323 false);
324 }
325 }
326 } else {
327 // Enter the "with access check" interpreter.
328 if (kInterpreterImplKind == kMterpImplKind) {
329 // No access check variants for Mterp. Just use the switch version.
330 if (transaction_active) {
331 return ExecuteSwitchImpl<true, true>(self, code_item, shadow_frame, result_register,
332 false);
333 } else {
334 return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register,
335 false);
336 }
337 } else {
338 DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
339 if (transaction_active) {
340 return ExecuteSwitchImpl<true, true>(self, code_item, shadow_frame, result_register,
341 false);
342 } else {
343 return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register,
344 false);
345 }
346 }
347 }
348}
238 static inline JValue Execute(
239 Thread* self,
240 const DexFile::CodeItem* code_item,
241 ShadowFrame& shadow_frame,
242 JValue result_register,
243 bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) {
244 DCHECK(!shadow_frame.GetMethod()->IsAbstract());
245 DCHECK(!shadow_frame.GetMethod()->IsNative());
246 if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization.
247 if (kIsDebugBuild) {
248 self->AssertNoPendingException();
249 }
250 instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
251 ArtMethod *method = shadow_frame.GetMethod();
252
253 if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
254 instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
255 method, 0);
256 }
257
258 if (!stay_in_interpreter) {
259 jit::Jit* jit = Runtime::Current()->GetJit();
260 if (jit != nullptr) {
261 jit->MethodEntered(self, shadow_frame.GetMethod());
262 if (jit->CanInvokeCompiledCode(method)) {
263 JValue result;
264
265 // Pop the shadow frame before calling into compiled code.
266 self->PopShadowFrame();
267 ArtInterpreterToCompiledCodeBridge(self, nullptr, code_item, &shadow_frame, &result);
268 // Push the shadow frame back as the caller will expect it.
269 self->PushShadowFrame(&shadow_frame);
270
271 return result;
272 }
273 }
274 }
275 }
276
277 shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
278
279 // Lock counting is a special version of accessibility checks, and for simplicity and
280 // reduction of template parameters, we gate it behind access-checks mode.
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!