Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

android7.0和android7.1的模拟器出现java.lang.IncompatibleClassChangeError #9

Closed
yunshouhu opened this issue May 24, 2017 · 7 comments

Comments

@yunshouhu
Copy link

测试环境:
android7.0 armeabi模拟器/android7.1 x86模拟器。
多次执行Toast.show方法后出现如下java.lang.IncompatibleClassChangeError异常,然后程序退出

05-24 04:13:43.362: W/YAHFA(1439): hook end
05-24 04:13:43.366: W/YAHFA(1439): hook
05-24 04:13:43.367: W/YAHFA(1439): hook end
05-24 04:13:43.367: I/Choreographer(1439): Skipped 42 frames! The application may be doing too much work on its main thread.
05-24 04:13:44.462: W/YAHFA(1439): hook
05-24 04:13:44.467: D/AndroidRuntime(1439): Shutting down VM
05-24 04:13:44.472: E/AndroidRuntime(1439): FATAL EXCEPTION: main
05-24 04:13:44.472: E/AndroidRuntime(1439): Process: lab.galaxy.yahfa.demoApp, PID: 1439
05-24 04:13:44.472: E/AndroidRuntime(1439): java.lang.IncompatibleClassChangeError: The method 'void android.widget.Toast.show()' was expected to be of type static but instead was found to be of type virtual (declaration of 'com.yunshouhu.hookitem.Hook_Toast_show' appears in /data/app/lab.galaxy.yahfa.demoApp-1/base.apk)
05-24 04:13:44.472: E/AndroidRuntime(1439): at com.yunshouhu.hookitem.Hook_Toast_show.hook(Hook_Toast_show.java:19)
05-24 04:13:44.472: E/AndroidRuntime(1439): at com.yunshouhu.MainActivity$4.onClick(MainActivity.java:88)
05-24 04:13:44.472: E/AndroidRuntime(1439): at android.view.View.performClick(View.java:5610)
05-24 04:13:44.472: E/AndroidRuntime(1439): at android.view.View$PerformClick.run(View.java:22260)
05-24 04:13:44.472: E/AndroidRuntime(1439): at android.os.Handler.handleCallback(Handler.java:751)
05-24 04:13:44.472: E/AndroidRuntime(1439): at android.os.Handler.dispatchMessage(Handler.java:95)
05-24 04:13:44.472: E/AndroidRuntime(1439): at android.os.Looper.loop(Looper.java:154)
05-24 04:13:44.472: E/AndroidRuntime(1439): at android.app.ActivityThread.main(ActivityThread.java:6077)
05-24 04:13:44.472: E/AndroidRuntime(1439): at java.lang.reflect.Method.invoke(Native Method)
05-24 04:13:44.472: E/AndroidRuntime(1439): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
05-24 04:13:44.472: E/AndroidRuntime(1439): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
05-24 04:13:44.515: W/ActivityManager(378): Force finishing activity lab.galaxy.yahfa.demoApp/com.yunshouhu.MainActivity

@rk700
Copy link
Member

rk700 commented May 24, 2017

Android N下面有比较多的坑,所以还只是试验性质

@yunshouhu
Copy link
Author

目前通过对比android6.0和android7.0的源码:
http://androidxref.com/7.0.0_r1/xref/art/runtime/class_linker.cc#7380
定位到是runtime->class_linker->ResolveMethod->CheckIncompatibleClassChange引起的。
android7.0之后ClassLinker开启了kForceICCECheck 对调用者进行了强检查。

以下是android7.0 runtime的class_linker.cc源码
template <ClassLinker::ResolveMode kResolveMode>
ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
uint32_t method_idx,
Handlemirror::DexCache dex_cache,
Handlemirror::ClassLoader class_loader,
ArtMethod* referrer,
InvokeType type) {
DCHECK(dex_cache.Get() != nullptr);
// Check for hit in the dex cache.
ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
if (kResolveMode == ClassLinker::kForceICCECheck) {
if (resolved->CheckIncompatibleClassChange(type)) {
ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
return nullptr;
}
}
return resolved;
}
// Fail, get the declaring class.
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
if (klass == nullptr) {
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
..........
}
本来想通过hook绕过检查,但是他们都是inline函数,无法HooK。
您看下有什么办法绕过CheckIncompatibleClassChange检查?问题可能还是在dex_cache这块。

@rk700
Copy link
Member

rk700 commented May 25, 2017

看了下Android 6.0上的ResolveMethod方法,也是有检查方法的属性的:
http://androidxref.com/6.0.1_r10/xref/art/runtime/class_linker.cc#5409

那么问题应该就是Android N的混合编译造成的。Android N的应用在安装时使用的是解释模式,运行过程中可能还会有有编译的过程。所以在应用运行一段时间后,也会有调用到ResolveMethod。但是Android N之前就不存在这个问题,因为应用在安装时就已经编译完成,运行后不会再调用ResolveMethod了。

hook绕过检查感觉确实不太好做,正如所说,是inline的

不过Android N上在应用运行时加载的dex,是编译而非解释的,所以同样的操作放到VirtualHook中应该就不会出现,可以试验下

@yunshouhu
Copy link
Author

yunshouhu commented Jun 3, 2017

hook了ThrowIncompatibleClassChangeError方法强制绕过后,后面还是存在调用匹配检查,
从原方法句柄在jit-code-cache中,可以看出如果应用运行时加载的dex,经过dex2oat编译后,应该可以绕过ThrowIncompatibleClassChangeError异常。
http://androidxref.com/7.0.0_r1/xref/art/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc#2063
06-02 09:38:39.414: W/YAHFA(2674): Hook_Toast_show
06-02 09:38:39.415: E/YAHFA(2674): Hook_Toast_show yahfahook origin
06-02 09:38:39.415: I/YAHFA-Native(2674): ThrowIncompatibleClassChangeError expected_type=0,found_type=2 //这里hook了ThrowIncompatibleClassChangeError方法强制绕过后
06-02 09:38:39.415: A/art(2674): art/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc:2063] Check failed: self->IsExceptionPending()
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] Runtime aborting...
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] Aborting thread:
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] "main" prio=5 tid=1 Runnable
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] | group="" sCount=0 dsCount=0 obj=0x73c3f950 self=0xa3c8b400
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] | sysTid=2674 nice=0 cgrp=default sched=0/0 handle=0xa7fbf534
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] | state=R schedstat=( 0 0 0 ) utm=7 stm=28 core=1 HZ=100
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] | stack=0xbf69d000-0xbf69f000 stackSize=8MB
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] | held mutexes= "abort lock" "mutator lock"(shared held)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #00 pc 003cb8de /system/lib/libart.so (???)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #1 pc 00397f6e /system/lib/libart.so (???)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #2 pc 00394f6b /system/lib/libart.so (???)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #3 pc 00380d43 /system/lib/libart.so (???)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #4 pc 003809fa /system/lib/libart.so (???)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #5 pc 0037127b /system/lib/libart.so (???)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #6 pc 00119572 /system/lib/libart.so (_ZN3art10LogMessageD1Ev+1298)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #7 pc 004cc093 /system/lib/libart.so (???)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #8 pc 0010801d /system/lib/libart.so (art_quick_invoke_static_trampoline_with_access_check+77)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] native: #9 pc 000003ac /dev/ashmem/dalvik-jit-code-cache (deleted)
(Java_com_yunshouhu_hookitem_Hook_1Toast_1show_hook_1dvm__Lcom_yunshouhu_dalvik_XC_1MethodHook_00024MethodHookParam_2Landroid_widget_Toast_2+396)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at com.yunshouhu.hookitem.Hook_Toast_show.hook_dvm(Hook_Toast_show.java:65)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at com.yunshouhu.hookitem.Hook_Toast_show.hook(Hook_Toast_show.java:39)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at com.yunshouhu.MainActivity$4.onClick(MainActivity.java:83)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at android.view.View.performClick(View.java:5610)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at android.view.View$PerformClick.run(View.java:22260)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at android.os.Handler.handleCallback(Handler.java:751)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at android.os.Handler.dispatchMessage(Handler.java:95)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at android.os.Looper.loop(Looper.java:154)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at android.app.ActivityThread.main(ActivityThread.java:6077)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at java.lang.reflect.Method.invoke!(Native method)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] Dumping all threads without appropriate locks held: thread list lock
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] All threads:
06-02 09:38:39.434: A/art(2674): art/runtime/runtime.cc:403] All threads:

@yunshouhu
Copy link
Author

1、初步测试了通过DexClassLoader动态加载apk/dex的方式去hook后:
dex执行了dex2oat流程,不经过jit-code-cache,就没有出现java.lang.IncompatibleClassChangeError异常。
即像博主说的:Android N上在应用运行时加载的dex,是编译而非解释的,
但是多次点击循环执行后,出现了gc的Tried to mark 0xa4d85060 not contained by any spaces异常。
06-03 01:51:52.563: A/art(3245): art/runtime/gc/collector/mark_sweep.cc:413] Tried to mark 0xa4d85060 not contained by any spaces
06-03 01:51:52.563: A/art(3245): art/runtime/utils.cc:184] 12c00000-12e08000 rw-p 00000000 00:04 5615 /dev/ashmem/dalvik-main space (deleted)

2、目前初步估计使用public static void origin(Toast toast) { }方式来保存原方法的句柄在android7.0以上的系统可能会导致:
a、声明的调用类型和运行时的调用类型不匹配异常,即强检查了java.lang.IncompatibleClassChangeError。
b、gc的mark_sweep算法无法标记回收,即Tried to mark 0xa4d85060 not contained by any spaces。
也许在android7.0之后使用legend的方法来保存原来的artMethod方法句柄可能兼容性会更好些。

@rk700
Copy link
Member

rk700 commented Jun 6, 2017

没有试过用legend的方式在android 7.0上做hook,可以试验一下

@rk700
Copy link
Member

rk700 commented Jun 30, 2017

#8 重复

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants