Closed kotori2 closed 3 years ago
@rk700 进入jni方法时会切换线程状态到kNative,从jni直接跳转到method entry感觉会因为线程状态不对有问题 (能想到的场景就是moving gc时需要除native外的所有线程挂起,不会等这个看起来在native其实在running的线程,可能会造成各种奇奇怪怪的问题)
嗯,感觉还是比想象的要复杂的多,如果方法flag改成fast native是否可以,这样会调用JniMethodFastStart
而不是JniMethodStart
,不知道是不是你说的那里。
我看FixupStaticTrampoline
的时候,如果方法有oat code则更新入口为oat code,这种情况下也不会再走jni了,想通过jni还原的方式也就触发不了
如果主动调用MakeInitializedClassesVisiblyInitialized
,是否就是从libart.so拿到符号就行,有什么坑需要注意吗
没有延迟hook的话,MakeInitializedClassesVisiblyInitialized
没有啥坑。
此外,最近yahfa把备份方案改为 只修改entrypoint,会导致 GetOatQuickMethodHeader
崩溃,@canyie 说这和 https://github.com/PAGalaxyLab/YAHFA/wiki/%E6%9B%B4%E6%96%B0%E8%AF%B4%E6%98%8E#a929fce-%E6%9B%B4%E6%96%B0%E8%AF%B4%E6%98%8E 这里有关系。看了一下确实类似。不知道还有没有什么其他思路?
@google-mirror https://github.com/PAGalaxyLab/YAHFA/pull/133#issuecomment-733874546 这里也说了是Fixup的问题,这都理解不了?
因为不是visiblyinitialized,所以R上会在调用若干次后,重新fixup这个static方法,导致entrypoint被reset。如果还设置了native,会导致reset成jni的trampoline然后读了一个null的data_然后crash。所以要手动调用makevisiblyinitialzed。懂了吗?
这里是技术讨论,不是让你问问题的地方。
还在edxp的issue里面点踩。。。
@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 其实你连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, 我错了, 大佬
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
@rk700 我也是这个思路。在trampoline前面多放allocate一点空间,使得 OatQuickMethodHeader
指针正确并且能读出一个 0 的code_size。目前正在测试中
在trampoline前面多放allocate一点空间
其实可以构造一个mov r0, #0
这样的东西吧,不过我现在是直接偷懒把GetCodeSize
给hook掉了
在trampoline前面多放allocate一点空间
其实可以构造一个
mov r0, #0
这样的东西吧,不过我现在是直接偷懒把GetCodeSize
给hook掉了
mov r0, #0
是什么原理呢?
在trampoline前面多放allocate一点空间
其实可以构造一个
mov r0, #0
这样的东西吧,不过我现在是直接偷懒把GetCodeSize
给hook掉了
mov r0, #0
是什么原理呢?
我意思是,这样就可以在不炸掉程序的前提下获得几个字节的0,可以让GetCodeSize
拿到0
Edit:是我理解错了,没注意到要-offset才是header
我觉得不如entrypoint前面多放几个0,这样还能保证 vmap_table_offset_
是正确的,header的指针也正确
嗯,感觉可以试下,好像前面就只有2个uint32,放8个0应该就可以?
@rk700 可以多放个,因为他还有 &~0x1
建议把整个class PACKED(4) OatQuickMethodHeader结构抄过来
@google-mirror 我就是这么做的。抄谷歌代码反算entrypoint的实际offset,保证最小的allocate
这个会有跨系统版本的问题,过两年你就要准备一大堆struct在代码里
Deleted user notifications@github.com 于 2020年12月18日周五 18:05写道:
建议把整个class PACKED(4) OatQuickMethodHeader结构抄过来
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PAGalaxyLab/YAHFA/pull/133#issuecomment-747999100, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEGYNSNEGEY7M76IBAOMPLDSVMSPVANCNFSM4T54C2JA .
其实我感觉放9个0,然后抄个 GetEntryPoint
拿到真正的entrypoint地址放trampoline就行。
首先, 我没细看, 只是抛个问题, 假设带上了字段, 返回了真正的OatQuickMethodHeader, 在DoGetCalleeSaveMethodCaller里可能会调用NativeQuickPcOffset(caller_pc), 不是照样崩溃么
@google-mirror current_code->IsOptimized() == false
@yujincheng08 current_code是真实的header, 不一定为false
@google-mirror current_code是我们自己造的
那么我们是否可以在 hookTrampoline 前构造一个假的 OatQuickMethodHeader?让其 code_size 为0,使得Contains(pc) 不会挂掉且返回 false,继续从原始的 oat file 获取真正的 entry 再计算 dexpc
@yujincheng08 current_code是GetOatQuickMethodHeader返回的, 意思是返回假的? 如果返回假的, 问题更多吧, 就拿DoGetCalleeSaveMethodCaller来说, 原本应该走GetResolvedMethod的逻辑就永远不走了
那么我们是否可以在 hookTrampoline 前构造一个假的 OatQuickMethodHeader?让其 code_size 为0,使得Contains(pc) 不会挂掉且返回 false,继续从原始的 oat file 获取真正的 entry 再计算 dexpc
按上面的说法, GetOatQuickMethodHeader返回的难道不是原始的header?
@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);
}
其实又想了一下,即使把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()'
相关代码:
这个报错信息是说没有找到匹配的method index?
是否有测试例子?
这个报错信息是说没有找到匹配的method index?
是否有测试例子?
按理来讲是任意oat的系统库里面丢异常就可以复现,但是我写了几个test case测复现不来。比较容易复现貌似的是氢OS的一加8P。 另外他跑到Hook的类里面去找这个方法,当然搜不到method index...
我看它是从method的declaring class里遍历方法对比的,method的declaring class应该是没有修改的
他是用栈顶的方法去拿art_method指针的,所以调用backup时候会拿到target方法的art_method,然后用它的entrypoint去拿header,但是target的entrypoint变成了trampoline,自然会崩。
我这里有一台android 9的一加6,是否可以用于复现?
可以尝试: hook一个系统函数,然后使得系统函数抛出异常,不catch,看看dump出来的stack有没有问题。
实际上edxp最近修复之后,已经比较稳定了,只有一个用户报问题( https://github.com/ElderDrivers/EdXposed/issues/740#issuecomment-748865758 ),而且是非hook函数下出问题,头大。
不catch
可能是因为我catch了所以才没复现。
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: #01 pc 00000000001ad92c /apex/com.android.art/lib64/libart.so (art::Mutex::ExclusiveLock(art::Thread)+412) native: #02 pc 00000000001bf66c /apex/com.android.art/lib64/libart.so (art::ClassLinker::MakeInitializedClassesVisiblyInitialized(art::Thread, bool)+104)
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