Closed kotori2 closed 3 years ago
@lianglixin 你也是谜语人:有更好的解决方法。 所以方法呢?
@lianglixin 你也是谜语人:有更好的解决方法。 所以方法呢?
💴
@lianglixin 在人家开源项目底下说钱,真有你的呢
@yujincheng08 把ClassLinker::ShouldUseInterpreterEntrypoint 这个函数inline hook掉,对于被hook过的方法让其返回false就能解决debug不生效的问题;另,yahfa作者明确表示过不想再引入一套完整的native inline hook框架,所以我没发pr。
@canyie 这个其实edxp有个接口只是没实现。我个人不是很喜欢这种native hook的方法。
@yujincheng08 把ClassLinker::ShouldUseInterpreterEntrypoint 这个函数inline hook掉,对于被hook过的方法让其返回false就能解决debug不生效的问题;另,yahfa作者明确表示过不想再引入一套完整的native inline hook框架,所以我没发pr。
看起来我遇到的并不是这个问题;我试了下直接拿yahfa的示例app在debug build的系统跑是没问题的,但是edxp在hook了这个函数后,甚至没有遇到过被hook的method检查ShouldUseInterpreterEntrypoint
,不过楼上用模拟器测试的时候并没有遇到这个问题。
谢谢 @kotori2 @yujincheng08
某谜语壬测试下来,发现有个问题,不知道是谜语壬的问题,还是你的问题,、会调到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!!!
hook的目标target
方法原本的入口是什么?backup
的trampoline会最终调用target
原本的入口(如果是要解析的话可能就会根据native access flag错误解析到jni?)
2020-09-28 11:28:25.546 28866-28866/lab.galaxy.yahfa.demoApp I/YAHFA-Native: replace entry point from 0x791f808fa0 to 0x7bb37b93a0 2020-09-28 11:28:25.546 28866-28866/lab.galaxy.yahfa.demoApp I/YAHFA-Native: change access flags from 0x12080009 to 0x12080009 2020-09-28 11:28:25.546 28866-28866/lab.galaxy.yahfa.demoApp I/YAHFA-Native: replace method from 0x71233098 to 0x790472a1d8 2020-09-28 11:28:25.546 28866-28866/lab.galaxy.yahfa.demoApp I/YAHFA-Native: replace entry point from 0x791f808fa0 to 0x7bb37b93c0 2020-09-28 11:28:25.546 28866-28866/lab.galaxy.yahfa.demoApp I/YAHFA-Native: change access flags from 0x12080001 to 0x12080001 这里是 access_flags &= ~kAccFastInterpreterToInterpreterInvoke; 但执行前后access_flags是一样 2020-09-28 11:28:25.546 28866-28866/lab.galaxy.yahfa.demoApp I/YAHFA-Native: hook and backup done 2020-09-28 11:28:25.641 28866-28866/lab.galaxy.yahfa.demoApp E/origin: call Log.e() 这里永远都是调用原函数,不会进hook函数
大佬还是亲测一下吧
------------------ 原始邮件 ------------------ 发件人: "Ruikai Liu"<notifications@github.com>; 发送时间: 2020年11月25日(星期三) 中午11:20 收件人: "PAGalaxyLab/YAHFA"<YAHFA@noreply.github.com>; 抄送: "1281579248"<1281579248@qq.com>; "Comment"<comment@noreply.github.com>; 主题: Re: [PAGalaxyLab/YAHFA] Fix crash on Android 11 (#133)
hook的目标target方法原本的入口是什么?backup的trampoline会最终调用target原本的入口(如果是要解析的话可能就会根据native access flag错误解析到jni?)
— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.
@rk700 没记错的话是backup解析成了JNI。的确我这边用edxposed的话会因为没有quick_code所以走解释,但是用YAHFA应用是有quick_code的,可能是跟系统是debug build有关系。
我这里debug或者release都是一样的情况。好吧,那我也说下自己的解决方案吧,我使用了Handler.sendMessageAtFrontOfQueue完美修复11.0闪退问题(完美不闪退,包括一加8),但是这样会导致hook时机会偏慢一点点,后面我开了一个线程去等待,可以解决时机问题,但是现在在一加8,上会发现部分游戏有崩溃现象,也是那个队列的问题(但是是因为这个线程引起的)。另外建议作者可以测试时候直接在hook完马上调用dowork(),不要放在button里去测试。这样即使测试正常,真正使用还是会发现有很多问题
------------------ 原始邮件 ------------------ 发件人: "双草酸酯"<notifications@github.com>; 发送时间: 2020年11月25日(星期三) 中午1:55 收件人: "PAGalaxyLab/YAHFA"<YAHFA@noreply.github.com>; 抄送: "1281579248"<1281579248@qq.com>; "Comment"<comment@noreply.github.com>; 主题: Re: [PAGalaxyLab/YAHFA] Fix crash on Android 11 (#133)
@rk700 没记错的话是backup解析成了JNI。的确我这边用edxposed的话会因为没有quick_code所以走解释,但是用YAHFA应用是有quick_code的,可能是跟系统是debug build有关系。
— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.
跟native这个flag没关系,而且R系统也是必须加这个flag。
native flag记得是很早之前引入的,主要为了防止hook不生效
现在好像是backup添加native flag,解析会变成jni stub?这几个方法的原本entry都是什么?R我只有模拟器显示几个方法的entry都是interp bridge,所以没有复现出来jni的情况
R系统在AS上的模拟器是正常的,并且以前做反射在AS上的模拟器都不需要pass,,所以作者,不要在模拟器上测试,会浪费时间还得不到准确答案。
Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 14181 (.bigbull.tdsgtw), pid 14181 (.bigbull.tdsgtw),我是觉得跟队列有关系,SI_QUEUE,所以我用我的方案是成功修复,不会闪退,只是一个时机问题。开一个线程等待就可以解决,然而跟libil2cpp会有冲突,需要attach(domain)那个
没有R真机
可以参考下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"));
}
}
是在link阶段,应该不影响后面的运行时吧。这里的检查
是在link阶段,应该不影响后面的运行时吧。这里的检查
我提交的更改已经提示比较明显了,其实并不是所属什么Native Flag问题。Debug下实际表现跟安卓10之前一样,问题实际在GC,目前不通过钩子无法较好解决这个问题。具体在ArtMethod的invoke函数里面,走到执行分支,进入汇编函数闪退。汇编函数检测异常,进入处理异常流程,根本原因是Moving GC。
是在link阶段,应该不影响后面的运行时吧。这里的检查
目前有个比较复杂的解决办法,在 汇编shellcode处 加入结构体成员信息恢复流程,每次调用都恢复backup的成员信息,但是这样做影响性能。另一个方案不支持某些函数,兼容性不好,实现更复杂,容易引起崩溃,其实是更改hook框架的全部成员属性。
是在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就直接崩掉
汇编函数检测异常,进入处理异常流程
你仔细看看看就会知道实际上他崩的原因是Check failed: exception != nullptr
,他出错是在artQuickGenericJniTrampoline
这个函数,直接called->GetEntryPointFromJni();
返回了个nullptr,然后汇编认为他出错了才跳到异常处理的,实际上是Android的bug导致他返回nullptr的时候没有设置exception
130 的trace里面,确实有art_quick_generic_jni_trampoline,这个是有问题,不知道是怎么根据native flag解析到jni trampoline的
确实,LinkCode发生得比hook早,我之前改AOSP打了下log,那个方法被LinkCode的时候的确是没有quick_code的,不过既然这么说的话当时这里应该是走interpreter的。
结果感觉像是SetEntryPointsToInterpreter
的问题
好了查明白了
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方法是怎么走的。
程序员都是这么晚睡的吗。头发都还好吗 ------------------ 原始邮件 ------------------ 发件人: "双草酸酯"<notifications@github.com> 发送时间: 2020年11月26日(星期四) 凌晨2:21 收件人: "PAGalaxyLab/YAHFA"<YAHFA@noreply.github.com>; 抄送: "shuajinanhai"<1281579248@qq.com>;"Comment"<comment@noreply.github.com>; 主题: Re: [PAGalaxyLab/YAHFA] Fix crash on Android 11 (#133)
好了查明白了
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方法是怎么走的。
从trace看是在class初始化的时候调用fixup
但反射拿方法时,方法所在的类应该已经初始化过了
好了查明白了
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方法是怎么走的。从trace看是在class初始化的时候调用fixup
但反射拿方法时,方法所在的类应该已经初始化过了
我hook过InitializeClass,很多类都会初始化若干次,不太懂原理,但是solo在EdXp这里做的workaround就是把FixupStaticTrampolines
给hook了,然后如果是hook过的类的话就把native flag去掉,走完再重新hook一遍这个static函数。
应该还不涉及jit,因为hook方式是通过解释器执行,不进行jit编译
可以看下hook之前几个方法的entry指向的是什么,在trampoline下断点看下运行时是否会访问到
如果class初始化fixup在hook之前执行,那时还没有native flag,应该可以把static方法的entry从resolve更新为interpreter bridge
如果反射获取方法时没有对类做初始化,那么可能会有问题 https://github.com/PAGalaxyLab/YAHFA/issues/126#issuecomment-607109896
因为backup方法也是static的,所以backup方法所在的类也需要初始化或者类似上面说的在fixup之后再添加native flag进行hook
@kotori2 那个workaround翻了下commit记录,好像在2019年5月加的,时间对不上所以应该不是解决R这个问题的
我有一个思路,不知道可不可行
native flag还是继续设置,entrypoint也和之前一样,不过对ArtMethod
里面jni地址也指向一段新的的代码
如果没有触发类初始化,那么entry不变正常;如果触发了类初始化,由于native flag的原因会进入到我们的jni地址,这里还是我们可控的,在这段伪jni代码中再把方法的entry替换回来,并回到栈起始处重新调用
@rk700 感觉可行,但是实现上似乎比较难。因为JNI的栈是跳了好几层的。如果是这样,不如直接上libffi。
当然,在我给edxp最新提交的PR上提供了几种解决方案,不过都需要inline hook:https://github.com/ElderDrivers/EdXposed/pull/717
jni trampoline在调用jni方法之前会备份寄存器和栈顶
jni方法代码是否可以直接从x28恢复栈和寄存器?
@rk700 后面那里不太理解,不过我想到了一种方案,不知道和你说的是不是同一个 还是同样改jni地址,这段伪jni代码里把method entry改回来,完成之后直接通过jni调用自己这个java method,由于此时entry已经改回来了,就会进入原来的执行流程
嗯,感觉都需要我们先在jni里找到ArtMethod *
恢复其entry为hook trampoline。改完entry后调用,我觉得是否可以在恢复栈和寄存器的情况下,直接跳转到改后的entry?
我的想法是,我们的jni data指向另一段trampoline,在那里先通过x28
恢复栈到这里 https://cs.android.com/android/platform/superproject/+/master:art/runtime/arch/arm64/quick_entrypoints_arm64.S;l=1785
然后从栈顶拿ArtMethod *
到x0
,再pop其他寄存器,恢复栈到这里 https://cs.android.com/android/platform/superproject/+/master:art/runtime/arch/arm64/quick_entrypoints_arm64.S;l=1756 (此时的栈和寄存器应该相当于刚进入entry时的状态?)我们再改掉ArtMethod
的entry,直接跳到那个entry执行hook trampoline
yahfa又没有延迟hook,完全可以利用 MakeInitializedClassesVisiblyInitialized
的,这个不需要inline hook,直接 dlopen
拿到sym就行。但是edxp由于有延时hook,所以会死锁。
如果想动jni那边,我建议直接上libffi,在closure里面再次替换entrypoint然后调用自己,如此便不需要写汇编搞寄存器。
由于entrypoint被替换了,我建议用 unordered_map
来存起来。如此效率上和实现上都有不错的平衡。
谢谢建议哈
我的想法,一个是尽量减少代码的副作用让使用者遇到未曾设想的情况,我可能更愿意的方式是检测类是否初始化,如果没有则报错让用户自己初始化。二是我个人想尽量保持项目的简单,不到万不得已不太愿意再引入一个复杂的依赖
R上面的类初始化已经不能由用户完成了。所以最符合你想法的方案应该就是 MakeInitializedClassesVisiblyInitialized
鉴于你没有实机测试,如果方案可以,我这边可以提PR (符号提取和offset是用回我在edxp使用的方案)。
谢谢,我再看看jni的方式是否能到达效果,如果不行那还是需要主动调用初始化了
我的想法,一个是尽量减少代码的副作用让使用者遇到未曾设想的情况,我可能更愿意的方式是检测类是否初始化,如果没有则报错让用户自己初始化。二是我个人想尽量保持项目的简单,不到万不得已不太愿意再引入一个复杂的依赖 我非常赞同你这点, 尽可能少代码量,保持简单又功能强大 尽量不使用hook 理解起来也会比较容易上手 其他框架写得太臃肿------------------ 原始邮件 ------------------ 发件人: "Ruikai Liu"<notifications@github.com> 发送时间: 2020年12月12日(星期六) 下午5:57 收件人: "PAGalaxyLab/YAHFA"<YAHFA@noreply.github.com>; 抄送: "shuajinanhai"<1281579248@qq.com>;"Comment"<comment@noreply.github.com>; 主题: Re: [PAGalaxyLab/YAHFA] Fix crash on Android 11 (#133)
直接跳转到改后的entry?
指的是让他从jni的入口直接跳到quick_code的入口?听起来感觉这种实现有点脏。 另外是不是可以换一种思路,把hook和backup的static去掉?
去掉static,可能会改变参数布局,可能需要再重新调整
感觉除了backup是static,hook的目标是static也可能会造成类似的fixupstatictrampoline的调用问题,这种情况的话直接jni中汇编恢复栈可能会有问题
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
andart_quick_generic_jni_trampoline
, which will readdata_
fromArtMethod
instead ofentry_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