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

Fix crash on Android 11 #133

Merged
merged 1 commit into from
Nov 23, 2020
Merged

Fix crash on Android 11 #133

merged 1 commit into from
Nov 23, 2020

Conversation

kotori2
Copy link
Contributor

@kotori2 kotori2 commented Nov 21, 2020

Fix #132 #130 (probably)
鉴于某谜语壬宁愿在issue里面讲故事也不发PR,那我自己研究下好了
Since someone figured out but won't send a PR, I'll do it.

From the crash trace, it tries to call artQuickGenericJniTrampoline and art_quick_generic_jni_trampoline, which will read data_ from ArtMethod instead of entry_point_from_quick_compiled_code_, which will get null pointer and it won't properly throw a Exception. But according to the Changelog, it might cause hook fail on Android O+, but it seems working for me with even debug build.

Credits: @yujincheng08

@yujincheng08
Copy link

@lianglixin 你也是谜语人:有更好的解决方法。
所以方法呢?

@ghost
Copy link

ghost commented Nov 22, 2020

@lianglixin 你也是谜语人:有更好的解决方法。
所以方法呢?

💴

@yujincheng08
Copy link

@lianglixin 在人家开源项目底下说钱,真有你的呢

@canyie
Copy link

canyie commented Nov 22, 2020

@yujincheng08 把ClassLinker::ShouldUseInterpreterEntrypoint 这个函数inline hook掉,对于被hook过的方法让其返回false就能解决debug不生效的问题;另,yahfa作者明确表示过不想再引入一套完整的native inline hook框架,所以我没发pr。

@yujincheng08
Copy link

yujincheng08 commented Nov 22, 2020

@canyie 这个其实edxp有个接口只是没实现。我个人不是很喜欢这种native hook的方法。

@kotori2
Copy link
Contributor Author

kotori2 commented Nov 22, 2020

@yujincheng08 把ClassLinker::ShouldUseInterpreterEntrypoint 这个函数inline hook掉,对于被hook过的方法让其返回false就能解决debug不生效的问题;另,yahfa作者明确表示过不想再引入一套完整的native inline hook框架,所以我没发pr。

看起来我遇到的并不是这个问题;我试了下直接拿yahfa的示例app在debug build的系统跑是没问题的,但是edxp在hook了这个函数后,甚至没有遇到过被hook的method检查ShouldUseInterpreterEntrypoint,不过楼上用模拟器测试的时候并没有遇到这个问题。

@rk700
Copy link
Member

rk700 commented Nov 23, 2020

谢谢 @kotori2 @yujincheng08

@rk700 rk700 merged commit 1fbe52b into PAGalaxyLab:master Nov 23, 2020
if (SDKVersion >= __ANDROID_API_Q__) {
// On API 29 whether to use the fast path or not is cached in the ART method structure
access_flags &= ~kAccFastInterpreterToInterpreterInvoke;
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是不是把Q漏了

@shuajinanhai
Copy link

某谜语壬测试下来,发现有个问题,不知道是谜语壬的问题,还是你的问题,、会调到java层的backup里。
public static Toast backup(Context context, CharSequence text, int duration) {
try{
Log.e("yahfa", "this is cannot delete, come in is error!!!");
return null;
}catch (Exception e){
return null;
}
}

2020-09-26 19:29:34.779 9988-9988/lab.galaxy.yahfa E/yahfa: this is cannot delete, come in is error!!!

@rk700
Copy link
Member

rk700 commented Nov 25, 2020

hook的目标target方法原本的入口是什么?backup的trampoline会最终调用target原本的入口(如果是要解析的话可能就会根据native access flag错误解析到jni?)

@shuajinanhai
Copy link

shuajinanhai commented Nov 25, 2020 via email

@kotori2
Copy link
Contributor Author

kotori2 commented Nov 25, 2020

@rk700 没记错的话是backup解析成了JNI。的确我这边用edxposed的话会因为没有quick_code所以走解释,但是用YAHFA应用是有quick_code的,可能是跟系统是debug build有关系。

@shuajinanhai
Copy link

shuajinanhai commented Nov 25, 2020 via email

@shuajinanhai
Copy link

跟native这个flag没关系,而且R系统也是必须加这个flag。

@rk700
Copy link
Member

rk700 commented Nov 25, 2020

native flag记得是很早之前引入的,主要为了防止hook不生效

现在好像是backup添加native flag,解析会变成jni stub?这几个方法的原本entry都是什么?R我只有模拟器显示几个方法的entry都是interp bridge,所以没有复现出来jni的情况

@shuajinanhai
Copy link

R系统在AS上的模拟器是正常的,并且以前做反射在AS上的模拟器都不需要pass,,所以作者,不要在模拟器上测试,会浪费时间还得不到准确答案。

@shuajinanhai
Copy link

Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 14181 (.bigbull.tdsgtw), pid 14181 (.bigbull.tdsgtw),我是觉得跟队列有关系,SI_QUEUE,所以我用我的方案是成功修复,不会闪退,只是一个时机问题。开一个线程等待就可以解决,然而跟libil2cpp会有冲突,需要attach(domain)那个

@rk700
Copy link
Member

rk700 commented Nov 25, 2020

没有R真机

#130 的trace里面,确实有art_quick_generic_jni_trampoline,这个是有问题,不知道是怎么根据native flag解析到jni trampoline的

@kotori2
Copy link
Contributor Author

kotori2 commented Nov 25, 2020

@shuajinanhai
Copy link

可以参考下sandhook这个
void* getInterpreterBridge(bool isNative) {
SandHook::ElfImg libart(art_lib_path);
if (isNative) {
return reinterpret_cast<void *>(libart.getSymbAddress("art_quick_generic_jni_trampoline"));
} else {
return reinterpret_cast<void *>(libart.getSymbAddress("art_quick_to_interpreter_bridge"));
}
}

@rk700
Copy link
Member

rk700 commented Nov 25, 2020

是在link阶段,应该不影响后面的运行时吧。这里的检查

https://cs.android.com/android/platform/superproject/+/master:art/runtime/class_linker.cc;l=3727;drc=master

@ghost
Copy link

ghost commented Nov 25, 2020

是在link阶段,应该不影响后面的运行时吧。这里的检查

https://cs.android.com/android/platform/superproject/+/master:art/runtime/class_linker.cc;l=3727;drc=master

我提交的更改已经提示比较明显了,其实并不是所属什么Native Flag问题。Debug下实际表现跟安卓10之前一样,问题实际在GC,目前不通过钩子无法较好解决这个问题。具体在ArtMethod的invoke函数里面,走到执行分支,进入汇编函数闪退。汇编函数检测异常,进入处理异常流程,根本原因是Moving GC。

@ghost
Copy link

ghost commented Nov 25, 2020

是在link阶段,应该不影响后面的运行时吧。这里的检查

https://cs.android.com/android/platform/superproject/+/master:art/runtime/class_linker.cc;l=3727;drc=master

目前有个比较复杂的解决办法,在 汇编shellcode处 加入结构体成员信息恢复流程,每次调用都恢复backup的成员信息,但是这样做影响性能。另一个方案不支持某些函数,兼容性不好,实现更复杂,容易引起崩溃,其实是更改hook框架的全部成员属性。

@kotori2
Copy link
Contributor Author

kotori2 commented Nov 25, 2020

是在link阶段,应该不影响后面的运行时吧。这里的检查
https://cs.android.com/android/platform/superproject/+/master:art/runtime/class_linker.cc;l=3727;drc=master

我提交的更改已经提示比较明显了,其实并不是所属什么Native Flag问题。Debug下实际表现跟安卓10之前一样,问题实际在GC,目前不通过钩子无法较好解决这个问题。具体在ArtMethod的invoke函数里面,走到执行分支,进入汇编函数闪退。汇编函数检测异常,进入处理异常流程,根本原因是Moving GC。

你提交的更改=检测到debug就直接崩掉

@kotori2
Copy link
Contributor Author

kotori2 commented Nov 25, 2020

汇编函数检测异常,进入处理异常流程

你仔细看看看就会知道实际上他崩的原因是Check failed: exception != nullptr,他出错是在artQuickGenericJniTrampoline这个函数,直接called->GetEntryPointFromJni();返回了个nullptr,然后汇编认为他出错了才跳到异常处理的,实际上是Android的bug导致他返回nullptr的时候没有设置exception

@kotori2
Copy link
Contributor Author

kotori2 commented Nov 25, 2020

#130 的trace里面,确实有art_quick_generic_jni_trampoline,这个是有问题,不知道是怎么根据native flag解析到jni trampoline的

确实,LinkCode发生得比hook早,我之前改AOSP打了下log,那个方法被LinkCode的时候的确是没有quick_code的,不过既然这么说的话当时这里应该是走interpreter的。
结果感觉像是SetEntryPointsToInterpreter的问题

@kotori2
Copy link
Contributor Author

kotori2 commented Nov 25, 2020

好了查明白了

11-25 22:54:27.846  8638  8638 D EdXposed:   # 0: 0x792f048a08  edxp::captureBacktrace(void**, unsigned long)
11-25 22:54:27.846  8638  8638 D EdXposed:   # 1: 0x792f0462d0  art::ClassLinker::ShouldUseInterpreterEntrypointReplace(void*, void const*)
11-25 22:54:27.846  8638  8638 D EdXposed:   # 2: 0x7936c12098  art::ClassLinker::FixupStaticTrampolines(art::ObjPtr<art::mirror::Class>)
11-25 22:54:27.846  8638  8638 D EdXposed:   # 3: 0x792f0461f0  art::ClassLinker::FixupStaticTrampolinesReplace(void*, void*)
11-25 22:54:27.846  8638  8638 D EdXposed:   # 4: 0x7936c41a50  
11-25 22:54:27.846  8638  8638 D EdXposed:   # 5: 0x7936c2e7cc  art::ClassLinker::InitializeClass(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)
11-25 22:54:27.846  8638  8638 D EdXposed:   # 6: 0x7936c0ada8  art::ClassLinker::EnsureInitialized(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)
11-25 22:54:27.846  8638  8638 D EdXposed:   # 7: 0x79370d5898  MterpNewInstance
11-25 22:54:27.846  8638  8638 D EdXposed:   # 8: 0x7936b7d214  
11-25 22:54:27.846  8638  8638 D EdXposed:   # 9: 0x79370cfa38  MterpInvokeDirect
11-25 22:54:27.846  8638  8638 D EdXposed:   #10: 0x7936b7f918  
11-25 22:54:27.846  8638  8638 D EdXposed:   #11: 0x79370cd3bc  MterpInvokeVirtual
11-25 22:54:27.846  8638  8638 D EdXposed:   #12: 0x7936b7f818  
11-25 22:54:27.846  8638  8638 D EdXposed:   #13: 0x7936d57c38  
11-25 22:54:27.846  8638  8638 D EdXposed:   #14: 0x79370bbe24  artQuickToInterpreterBridge
11-25 22:54:27.846  8638  8638 D EdXposed:   #15: 0x7936b8effc  
11-25 22:54:27.846  8638  8638 D EdXposed:   #16: 0x7936b857ec  
11-25 22:54:27.846  8638  8638 D EdXposed:   #17: 0x7936bfaa98  art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, ar

这里FixupStaticTrampolines说是会"Link the code of methods skipped by LinkCode.",实际上是把所有static方法都重新设置了一遍。不过不知道非static方法是怎么走的。

@shuajinanhai
Copy link

shuajinanhai commented Nov 25, 2020 via email

@kotori2
Copy link
Contributor Author

kotori2 commented Dec 18, 2020

@google-mirror 其实你连epic的备注也没看

    /**
     * the static method is lazy resolved, when not resolved, the entry point is a trampoline of
     * a bridge, we can not hook these entry. this method force the static method to be resolved.
     */

@google-mirror
Copy link

@google-mirror 其实你连epic的备注也没看

    /**
     * the static method is lazy resolved, when not resolved, the entry point is a trampoline of
     * a bridge, we can not hook these entry. this method force the static method to be resolved.
     */

是的, 我就看了这个issue, 我错了, 大佬

@rk700
Copy link
Member

rk700 commented Dec 18, 2020

GetOatQuickMethodHeader的那个问题,我看下来似乎是这里造成的?
https://cs.android.com/android/platform/superproject/+/master:art/runtime/art_method.cc;l=592

我们把 target 的 entry 替换成 hookTrampoline 了,从这个新的 entry 直接往前计算偏移得到 OatQuickMethodHeader
https://cs.android.com/android/platform/superproject/+/master:art/runtime/oat_quick_method_header.h;l=47;drc=master

这个所谓的OatQuickMethodHeader肯定是不对的,导致计算 Contains(pc) 出错

那么我们是否可以在 hookTrampoline 前构造一个假的 OatQuickMethodHeader?让其 code_size 为0,使得Contains(pc) 不会挂掉且返回 false,继续从原始的 oat file 获取真正的 entry 再计算 dexpc
https://cs.android.com/android/platform/superproject/+/master:art/runtime/art_method.cc;l=620

@yujincheng08
Copy link

yujincheng08 commented Dec 18, 2020

@rk700 我也是这个思路。在trampoline前面多放allocate一点空间,使得 OatQuickMethodHeader 指针正确并且能读出一个 0 的code_size。目前正在测试中

@kotori2
Copy link
Contributor Author

kotori2 commented Dec 18, 2020

在trampoline前面多放allocate一点空间

其实可以构造一个mov r0, #0 这样的东西吧,不过我现在是直接偷懒把GetCodeSize给hook掉了

@rk700
Copy link
Member

rk700 commented Dec 18, 2020

在trampoline前面多放allocate一点空间

其实可以构造一个mov r0, #0 这样的东西吧,不过我现在是直接偷懒把GetCodeSize给hook掉了

mov r0, #0 是什么原理呢?

@kotori2
Copy link
Contributor Author

kotori2 commented Dec 18, 2020

在trampoline前面多放allocate一点空间

其实可以构造一个mov r0, #0 这样的东西吧,不过我现在是直接偷懒把GetCodeSize给hook掉了

mov r0, #0 是什么原理呢?

我意思是,这样就可以在不炸掉程序的前提下获得几个字节的0,可以让GetCodeSize拿到0
Edit:是我理解错了,没注意到要-offset才是header

@yujincheng08
Copy link

我觉得不如entrypoint前面多放几个0,这样还能保证 vmap_table_offset_ 是正确的,header的指针也正确

@rk700
Copy link
Member

rk700 commented Dec 18, 2020

嗯,感觉可以试下,好像前面就只有2个uint32,放8个0应该就可以?

@yujincheng08
Copy link

yujincheng08 commented Dec 18, 2020

@rk700 可以多放个,因为他还有 &~0x1

@google-mirror
Copy link

建议把整个class PACKED(4) OatQuickMethodHeader结构抄过来

@yujincheng08
Copy link

@google-mirror 我就是这么做的。抄谷歌代码反算entrypoint的实际offset,保证最小的allocate

@kotori2
Copy link
Contributor Author

kotori2 commented Dec 18, 2020 via email

@yujincheng08
Copy link

yujincheng08 commented Dec 18, 2020

其实我感觉放9个0,然后抄个 GetEntryPoint 拿到真正的entrypoint地址放trampoline就行。

@google-mirror
Copy link

首先, 我没细看, 只是抛个问题, 假设带上了字段, 返回了真正的OatQuickMethodHeader, 在DoGetCalleeSaveMethodCaller里可能会调用NativeQuickPcOffset(caller_pc), 不是照样崩溃么

@yujincheng08
Copy link

@google-mirror current_code->IsOptimized() == false

@google-mirror
Copy link

@yujincheng08 current_code是真实的header, 不一定为false

@yujincheng08
Copy link

@google-mirror current_code是我们自己造的

那么我们是否可以在 hookTrampoline 前构造一个假的 OatQuickMethodHeader?让其 code_size 为0,使得Contains(pc) 不会挂掉且返回 false,继续从原始的 oat file 获取真正的 entry 再计算 dexpc

@google-mirror
Copy link

google-mirror commented Dec 18, 2020

@yujincheng08
current_code是GetOatQuickMethodHeader返回的, 意思是返回假的?
如果返回假的, 问题更多吧, 就拿DoGetCalleeSaveMethodCaller来说, 原本应该走GetResolvedMethod的逻辑就永远不走了

那么我们是否可以在 hookTrampoline 前构造一个假的 OatQuickMethodHeader?让其 code_size 为0,使得Contains(pc) 不会挂掉且返回 false,继续从原始的 oat file 获取真正的 entry 再计算 dexpc

按上面的说法, GetOatQuickMethodHeader返回的难道不是原始的header?

@canyie
Copy link

canyie commented Dec 18, 2020

@google-mirror

  static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) {
    uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr);
    uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_);
    return reinterpret_cast<OatQuickMethodHeader*>(header);
  }

@kotori2
Copy link
Contributor Author

kotori2 commented Dec 19, 2020

其实又想了一下,即使把code_size正确设置了也没有什么用
这东西是为了在崩溃的时候找崩溃点的,如果崩溃点本身是被hook的函数的话,那么这里的code_已经是跳板了,在跳板所在区域搜索没有什么意义,那样依旧是不知道PC在不在原函数里。
然后会获得一个这样的崩溃:

Abort message: 'Check failed: found_virtual Didn't find oat method index for virtual method: java.lang.String[] android.icu.impl.ICUResourceBundle.getStringArray()'

相关代码:
image

@rk700
Copy link
Member

rk700 commented Dec 21, 2020

这个报错信息是说没有找到匹配的method index?

是否有测试例子?

@kotori2
Copy link
Contributor Author

kotori2 commented Dec 21, 2020

这个报错信息是说没有找到匹配的method index?

是否有测试例子?

按理来讲是任意oat的系统库里面丢异常就可以复现,但是我写了几个test case测复现不来。比较容易复现貌似的是氢OS的一加8P。
另外他跑到Hook的类里面去找这个方法,当然搜不到method index...

@rk700
Copy link
Member

rk700 commented Dec 21, 2020

我看它是从method的declaring class里遍历方法对比的,method的declaring class应该是没有修改的

@yujincheng08
Copy link

他是用栈顶的方法去拿art_method指针的,所以调用backup时候会拿到target方法的art_method,然后用它的entrypoint去拿header,但是target的entrypoint变成了trampoline,自然会崩。

@rk700
Copy link
Member

rk700 commented Dec 21, 2020

我这里有一台android 9的一加6,是否可以用于复现?

@yujincheng08
Copy link

yujincheng08 commented Dec 21, 2020

可以尝试: hook一个系统函数,然后使得系统函数抛出异常,不catch,看看dump出来的stack有没有问题。

实际上edxp最近修复之后,已经比较稳定了,只有一个用户报问题( ElderDrivers/EdXposed#740 (comment) ),而且是非hook函数下出问题,头大。

@kotori2
Copy link
Contributor Author

kotori2 commented Dec 21, 2020

不catch

可能是因为我catch了所以才没复现。

@zhoujunyou
Copy link

yahfa又没有延迟hook,完全可以利用 MakeInitializedClassesVisiblyInitialized 的,这个不需要inline hook,直接 dlopen 拿到sym就行。但是edxp由于有延时hook,所以会死锁。

大佬 请教一下为什么会死锁呢。 我这边在MEIZU 18 Android 11 机型上必现。

"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x71ae1cc8 self=0xb4000072682b5010
| sysTid=32680 nice=-10 cgrp=top-app sched=0/0 handle=0x73eee8f4f8
| state=S schedstat=( 230325048 13994022 307 ) utm=19 stm=3 core=6 HZ=100
| stack=0x7fc22ae000-0x7fc22b0000 stackSize=8192KB
| held mutexes=
native: #00 pc 000000000004b48c /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28)
native: #1 pc 00000000001ad92c /apex/com.android.art/lib64/libart.so (art::Mutex::ExclusiveLock(art::Thread*)+412)
native: #2 pc 00000000001bf66c /apex/com.android.art/lib64/libart.so (art::ClassLinker::MakeInitializedClassesVisiblyInitialized(art::Thread*, bool)+104)

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

Successfully merging this pull request may close these issues.

安卓R崩溃的一些问题
8 participants