首页
社区
课程
招聘
[原创][原创]frida跟踪应用中所有运行在解释模式的java函数
发表于: 2020-11-4 11:22 17208

[原创][原创]frida跟踪应用中所有运行在解释模式的java函数

2020-11-4 11:22
17208

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.

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 5
支持
分享
最新回复 (5)
雪    币: 8285
活跃值: (4710)
能力值: ( LV8,RANK:134 )
在线值:
发帖
回帖
粉丝
2
Dalvik时代的解释器可以说是驱动Android每一个app运行的核心,到了ART时代,虽然有了dex2oat的编译,ART中的解释器依然发挥着至关重要的作用。而Google在对ART的解释器的实现过程中也出现了三种不同类型的实现,分别是switch类型、goto跳转表类型以及汇编类型。在不同的Android版本中,往往又是不同的组合。可以说,只要搞懂了ART当中的解释器,就很容易能够理解APP运行过程中诸如jni函数的注册绑定、java函数的调用流程。
2020-11-11 18:18
1
雪    币: 977
活跃值: (435)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
认真到让人肃然起敬
2020-11-12 10:12
0
雪    币: 1759
活跃值: (2334)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
有点复杂,我还是用官方的跟踪工具吧
2020-11-16 16:40
0
雪    币: 116
活跃值: (1012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2021-10-31 15:10
0
雪    币: 562
活跃值: (4347)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
学习,思路不错
2023-3-23 20:36
0
游客
登录 | 注册 方可回帖
返回
//