日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java导致native非法指令,Java代码引起的NATIVE野指针问题(上)

發布時間:2024/7/23 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java导致native非法指令,Java代码引起的NATIVE野指针问题(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

樸英敏,小米MIUI部門。從事嵌入式開發和調試工作8年多,擅長逆向分析方法,主要負責解決安卓系統穩定性問題。

上周音樂組同事反饋了一個必現Native Crash問題,tombstone如下: pid:?5028,?tid:?5028,?name:?com.miui.player??>>>?com.miui.player?<<?????#01??pc?0002302f??/system/lib/libhwui.so?(android::uirenderer::OpenGLRenderer::callDrawGLFunction(android::Functor*,?android::uirenderer::Rect&)+322)?????#02??pc?00015d91??/system/lib/libhwui.so?(android::uirenderer::DrawFunctorOp::applyDraw(android::uirenderer::OpenGLRenderer&,?android::uirenderer::Rect&)+28)?????#03??pc?00014527??/system/lib/libhwui.so?(android::uirenderer::DrawBatch::replay(android::uirenderer::OpenGLRenderer&,?android::uirenderer::Rect&,?int)+74)?????#04??pc?00014413??/system/lib/libhwui.so?(android::uirenderer::DeferredDisplayList::flush(android::uirenderer::OpenGLRenderer&,?android::uirenderer::Rect&)+218)?????#05??pc?0001d1cf??/system/lib/libhwui.so?(_ZN7android10uirenderer14OpenGLRenderer15drawDisplayListEPNS0_11DisplayListERNS0_4RectEi.part.47+230)?????#06??pc?0006820d??/system/lib/libandroid_runtime.so

崩潰的原因是pc指向了一個沒有可執行權限的內存地址上。

初步分析:

對應的代碼如下: status_t?OpenGLRenderer::callDrawGLFunction(Functor*?functor,?Rect&?dirty)?{??if?(mSnapshot->isIgnored())?return?DrawGlInfo::kStatusDone;??detachFunctor(functor);??...??interrupt();??=>?status_t?result?=?(*functor)(DrawGlInfo::kModeDraw,?&info);

其中,Functor類重載了()操作符: class?Functor?{??public:??Functor()?{}??virtual?~Functor()?{}??=>?virtual?status_t?operator?()(int?/*what*/,?void*?/*data*/)?{?return?NO_ERROR;?}??};

因此,()操作其實就是調用了Functor類的一個虛函數,它的具體實現目前還不清楚。

對應的匯編代碼如下: 23028:?aa0b?add?r2,?sp,?#44??2302a:?6803?ldr?r3,?[r0,?#0]?;?r0是functor,r3?=?[r0]?=?functor.vtlb??2302c:?689d?ldr?r5,?[r3,?#8]?;?r5?=?[r3?+?8]?=?[functor.vtlb?+?8]?=?Functor.operator()??2302e:?47a8?blx?r5?;?call?Functor.operator()

崩潰時的寄存器值如下: r0?7ac59c98?r1?00000000?r2?bea7b174?r3?400fc1b8??r4?774c4c88?r5?79801f28?r6?bea7b478?r7?40c12bb8??r8?7c1b68e8?r9?778781e8?sl?bea7b478?fp?bea7b414??ip?00000001?sp?bea7b148?lr?40c07031?pc?79801f28?cpsr?600f0010

可以看到,r5和pc值是相等的,可以知道,確定是崩潰在2302e這一行匯編代碼中。

而查看寄存器對應的內存值,發現有點問題: memory?near?r0:?????7ac59c78?00000018?0000001b?735a9b38?23831ef0???????7ac59c88?23831ef0?735a9b50?00000018?00000011???????7ac59c98?79822328?77768698?00000010?00000022???????7ac59ca8?00000000?00000000?00000000?00000003????memory?near?r3:?????400fc198?7c74c000?00200000?00000077?0d44acd8???????400fc1a8?00000000?00000000?400fc1a8?400fc1a8???????400fc1b8?400fc1b0?400fc1b0?7c04acb8?7c78f008???????400fc1c8?7c021d98?7c78ffc0?7983bbf0?7c04bfa8

[r0] = [7ac59c98] = 798223298,這個和r3值(400fc1b8)不一樣,

同樣

[r3+8] = [400fc1b8 + 8] = 7c04acb8,這個值也和r5值(79801f28)不一樣。

這在平時的tombstone里是非常少見的!

乍一看非常不可思議,但仔細想想tombstone的生成過程,就能發現其中的問題。

原來寄存器信息是錯位崩潰時的cpu context,保存在崩潰時的線程私有的信號棧和內核棧中,直到debuggerd去獲取這個值,它是不會被修改的。

而內存是進程中的各個線程共享的,所以在發生異常到debuggerd打印內存信息這段過程中(其實是相對很長的一個過程),別的線程是有可能修改內存值的。

為了證明別的線程在改這個內存值,在callDrawGLFunction()函數中的若干處打印了Functor和它的vtbl(虛函數表地址)值: status_t?OpenGLRenderer::callDrawGLFunction(Functor*?functor,?Rect&?dirty)?{??AOGI("functor=%p,vtbl=%p");??sleep(1);??if?(mSnapshot->isIgnored())?return?DrawGlInfo::kStatusDone;??AOGI("functor=%p,vtbl=%p");??sleep(1);??detachFunctor(functor);??...??AOGI("functor=%p,vtbl=%p");??sleep(1);??interrupt();??AOGI("functor=%p,vtbl=%p");??sleep(1);??status_t?result?=?(*functor)(DrawGlInfo::kModeDraw,?&info);

抓到的log如下: 10-27?21:19:45.794?8027?8027?I?OpenGLRenderer:?functor=0x7a7b8530,vtbl=0x73648de0??10-27?21:19:47.801?8027?8027?I?OpenGLRenderer:?functor=0x7a7b8530,vtbl=0x73648de0??10-27?21:19:48.801?8027?8027?I?OpenGLRenderer:?functor=0x7a7b8530,vtbl=0x73648de0??10-27?21:19:49.801?8027?8027?I?OpenGLRenderer:?functor=0x7a7b8530,vtbl=0x73648de0??10-27?21:19:50.804?8027?8027?I?OpenGLRenderer:?functor=0x7a7b8530,vtbl=0x73648de0??10-27?21:19:51.804?8027?8027?I?OpenGLRenderer:?functor=0x7a7b8530,vtbl=0x400fc1b8

可以確定確實有別的線程在修改這個值。

這里就存在兩個可能性了:

1、別的線程也持有functor指針,并修改內容

2、functor是野指針,對應的內存已經還回系統,其他模塊可任意使用。

而對象的vtbl一般是不會修改的,所以2的可能性更大一些。

為了查明是哪個線程在改,對functor指向的內存做了寫保護操作: static?int**?s_saved_vtbl?=?NULL;?static?void*?s_saved_functor?=?NULL;??static?void??mprotect_local(int**?p)?{?????//?一旦發現vtbl有變化就將對應內存設置為只讀?????if(p?!=?s_saved_vtbl)?{??????????mprotect((void*)((unsigned?int)s_saved_functor&0xfffff000),?4096,?PROT_READ);?????}?????sleep(1);?}??status_t?OpenGLRenderer::callDrawGLFunction(Functor*?functor,?Rect&?dirty)?{?????int*?ptr?=?(int*)functor;?????s_saved_functor?=?(void*)ptr;?????s_saved_vtbl?=?(int**)*ptr;??????if?(mSnapshot->isIgnored())?return?DrawGlInfo::kStatusDone;???????mprotect_local((int**)*ptr);?????detachFunctor(functor);?????mprotect_local((int**)*ptr);?????...?????mprotect_local((int**)*ptr);?????interrupt();???????status_t?result?=?(*functor)(DrawGlInfo::kModeDraw,?&info);

push到手機中復現問題,很容易抓到訪問權限引起的crash。

而每次的crash的線程和位置都不一樣,也就是不同的線程在不同的函數中讀寫這個地址。

這樣基本上就確定是野指針問題,進入下一階段的分析。

關于野指針:

所謂野指針就是一個對象被釋放后又被使用,可能是釋放的問題,也可能是使用的問題。

我們已經知道使用的位置,接下來要找出是從哪釋放的。

找到釋放對象的最笨的方法,是在free()函數里打印調用棧。

但這么做有兩個問題:

1、log太量多,一秒內可能會有成千上萬的malloc/free函數被調用。

2、打印調用棧的函數本身會調用free函數,這樣會陷入死循環。

為了解決上面兩個問題,需要用到hook技術。

關于hook技術:

要了解hook技術,得先了解外部函數的調用過程。

所謂外部函數就是外部模塊中定義的函數。比如,libhwui.so中的某個源文件中調用了malloc函數,而這個malloc函數是libc.so中定義的。

當編譯libhwui.so的這個源文件時,對應調用malloc的地方會生成如下的匯編代碼: blx?addr

這里blx是arm的跳轉指令,addr是目標地址,也就是malloc函數的地址,那這個malloc函數的地址如何確定?

這個編譯的階段是無法確定的,只有當運行時進程加載完libc.so以后,malloc函數的地址才能被確定。

所以編譯器在編譯的時候會在libbinder.so中留出一部分空間作為地址表,專門用于存放外部函數的地址,這個區域叫got表。

每一個本模塊調用到的外部函數都對應got表中的一項。

當然got表里面的內容是在進程啟動階段,加載動態庫時被連接器linker填充的。

而編譯階段我們只需要將代碼寫成:

1、從got表對應位置獲取外部函數地址

2、跳轉到這個外部函數的地址

這個動作需要由若干的指令來完成,所以跳轉指令blx addr中的addr其實指向本模塊的一組指令: blx?cb74?

這組指令所在的區域就是elf文件結構里的plt表,plt表中每一個外部函數都對應一個表項,如:

0000cb74 :

cb74: e28fc600 add ip, pc, #0, 12

cb78: e28cca29 add ip, ip, #167936 ;

cb7c: e5bcf1e8 ldr pc, [ip, #488]! ;

0000c8bc :

c8bc: e28fc600 add ip, pc, #0, 12

c8c0: e28cca29 add ip, ip, #167936 ;

c8c4: e5bcf3b8 ldr pc, [ip, #952]! ;

每一個plt表項都是做相同操作:

1、先獲取got表中外目標函數對應的地址(前兩行);

2、從got表中獲取地址目標函數的地址,并賦給pc寄存器(第三行)。

下面給出got表和plt表在so文件中的位置:

readelf -S libhwui.so

[Nr] Name Type Addr Off Size ES Flg Lk Inf Al

[ 0] NULL 00000000 000000 000000 00 0 0 0

[ 1] .interp PROGBITS 00000134 000134 000013 00 A 0 0 1

[ 2] .dynsym DYNSYM 00000148 000148 002420 10 A 3 1 4

[ 3] .dynstr STRTAB 00002568 002568 0056a4 00 A 0 0 1

[ 4] .hash HASH 00007c0c 007c0c 001134 04 A 2 0 4

[ 5] .rel.dyn REL 00008d40 008d40 002bc8 08 A 2 0 4

[ 6] .rel.plt REL 0000b908 00b908 000a78 08 A 2 7 4

=>[ 7] .plt PROGBITS 0000c380 00c380 000fc8 00 AX 0 0 4

[ 8] .text PROGBITS 0000d348 00d348 01ef30 00 AX 0 0 8

[ 9] .ARM.exidx ARM_EXIDX 0002c278 02c278 001fb8 08 AL 8 0 4

[10] .ARM.extab PROGBITS 0002e230 02e230 000930 00 A 0 0 4

[11] .rodata PROGBITS 0002eb60 02eb60 0036a4 00 A 0 0 4

[12] .fini_array FINI_ARRAY 00034010 033010 000004 00 WA 0 0 4

[13] .data.rel.ro PROGBITS 00034018 033018 001910 00 WA 0 0 8

[14] .init_array INIT_ARRAY 00035928 034928 00000c 00 WA 0 0 4

[15] .dynamic DYNAMIC 00035934 034934 000140 08 WA 3 0 4

=>[16] .got PROGBITS 00035a74 034a74 00058c 00 WA 0 0 4

[17] .data PROGBITS 00036000 035000 00025c 00 WA 0 0 4

[18] .bss NOBITS 0003625c 03525c 000068 00 WA 0 0 4

[19] .comment PROGBITS 00000000 03525c 000010 01 MS 0 0 1

[20] .note.gnu.gold-ve NOTE 00000000 03526c 00001c 00 0 0 4

[21] .ARM.attributes ARM_ATTRIBUTES 00000000 035288 00003e 00 0 0 1

[22] .gnu_debuglink PROGBITS 00000000 0352c6 000010 00 0 0 1

[23] .shstrtab STRTAB 00000000 0352d6 0000dc 00 0 0 1

我們的hook技術就是通過修改so的got表來截獲so中的某些外部函數調用。

so的代碼段是多個進程共享的,但它的數據段私有的,而got表就是數據段。

所以我們只修改music應用進程的libhwui.so的got表中free函數對應的項,影響范圍將大大減少。

那改成什么值呢?一般是我們自己定義的函數,比如: void?inject_free(void?*ptr)???{?????ALOGI("free?ptr=%p",ptr);?????dumpNativeStack();?????dumpJavaStack();?????free(ptr);?}

為了不影響原來的邏輯,打印完debug信息,還是要調用原來被hook的函數。

有了hook技術后能完美的解決野指針中的兩個問題,下面繼續分析問題。

點贊 0

總結

以上是生活随笔為你收集整理的java导致native非法指令,Java代码引起的NATIVE野指针问题(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。