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

歡迎訪問 生活随笔!

生活随笔

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

c/c++

crash工具解析_IDA反汇编静态调试Android平台C++的so文件Crash入门

發布時間:2024/9/27 c/c++ 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 crash工具解析_IDA反汇编静态调试Android平台C++的so文件Crash入门 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近剛接觸到Android的C++編譯出的.so文件崩潰,從后臺上報系統能得到ARM64平臺下發生crash時pc寄儲器地址和函數名稱,但沒有行號,以及當時棧幀對應的31個寄儲器的值,,第一次用IDA調試,邊用邊百度,不到2小時就上手,很就查出野指針問題了。本文引入兩個實戰,一個野指針Crash,一個空指針Crash。

由于以前做windows多,有C++反匯編基礎,也有STL略底層點知識,只是沒有用過IDA。win32指令簡單一些,先介紹一下win32反匯編的一些知識,只要簡單學習一下,就可以很快擴展到arm64平臺中,兩者很相通。

win32平臺下10個寄儲器,加粗的比較重要

  • EAX, EBX,ECX,EDX 通用寄儲器,
  • ESI,EDI ,索引寄儲器
  • EIP ,指令指針寄儲器
  • ESP,EBP ,棧寄儲器,ESP表標棧頂指針
  • EFL,標志寄存器

Win32常用指令:只取常用的,也不強記,不過用的多就能記住了,平時可以隨時查

  • mov 傳送指令
  • push 壓棧指令
  • pop 出棧指令
  • lea 取地址
  • jmp 無條件跳轉
  • je 相等跳轉
  • jle 小于跳轉
  • call 函數調用
  • ret 返回
  • cmp比較指令
  • add 相加
  • sub 相減
  • inc 加1
  • mul 相乘
  • div 相除
  • and 與運算
  • or 或運算
  • xor 異或運算
  • not 取反
  • test 測試
  • 其它他有很多指令,就單純跳轉就有很多,不再一一列舉了。

上面的隨意可以百度到,下面來說些對剛接觸的來說相對比較有價值點或者比較難搜索到的:

1.要深入理解點棧,棧對匯編來說非常非常重要,也是函數調用關系的基礎。一些高級用法,比如協程,比如自定義子程序調度,都離不開這個和跳轉指令。棧可以理解為一個固定大小的地址空間,win32沒有記錯是1M,當然也可以在VS編譯器設置。esp表示棧頂指針,最開始esp,ebp相同的,棧的內存增長和一般的不同,是向下生長,先進后出,就是push進去,地址越來越小,pop出來,地址變大。

2.EAX常用作函數返回值,非常重要

3.ECX常用作C++普通成員函數函數第一個參數,也就是this指針,C++代碼編寫時默認隱了this指針,編譯器生成代碼時,第一個參數是this*,這個參數是不能省掉的,這種也有人稱為this call。

4.如果寄儲器不夠傳參時,會用棧來傳參

//關于2,3,4驗證,VS2019 debug版本,Release會優化,不利于初始學習

5.EIP指向下一條指令代碼,不能直接修改,可以用call,jmp,ret等修改

6.理解Push ,Pop 指令的等效過程

push eax; // 等同兩條偽指令 1.mov esp,eax; // 將eax 放到esp中,偽指令,僅供理解 2.sub esp,4; // 將esp減4,因為棧向下生長的,偽指令,僅供理解pop eax; // 等同兩條偽指令 1.mov eax,esp; // 將esp 放到eax中,偽指令,僅供理解 2.add esp,4; // 將esp加4,因為棧向下生長的,偽指令,僅供理解

7.深入理解call,jmp,ret指令的等效過程

call 內存/立即數/寄儲器; //1.原下個EIP對應指令地址入棧;(esp-4) //2.修改EIP為新的; //3.跳轉EIP執行;jmp 內存/立即數/寄儲器; //1.修改EIP為新的; //2.跳轉EIP執行; 注:無條件執行,不改變棧ret; //1.修改EIP為新的棧頂數據; //2.esp +4;(恢復棧) //3.跳轉EIP執行;

8.為什么有時看到函數反匯編開頭會push ebp,push ebx,push esi等?

答:因為為了保護這些寄儲器的值在函數調用結束不被錯誤修改。如果研究的比較深,比如函數加上__declspec(naked)修飾 ,或者完全自己寫匯編,的確是可以不進行這樣操作,只要能維護好就行。下面給個簡單偽代碼,舉例為什么push edi

int a = fun1()+fun2(1,2);// 偽代碼,edi用來保存fun1()的結果,然后與fun2()返回值相加,所以edi最好要在fun2內部過程保護好 call fun1_address; // 調用fun1() mov edi eax; // fun1()返回值放到edi中 push 2; push 1;// 放入參數(1,2) call fun2_address; // 調用fun2(1,2) add edi eax; // 執行fun1()+fun2(1,2),并放到edi中 mov ptr [a] edi; // 放到a對應棧內存中

9.C++虛函數的與反匯編

我們知道如果一個class沒有虛函數時,開始的第一個字段為第一個成員,但有了虛函數,第一個字段就變成虛表指針的地址,如果有多種繼承與虛繼承,這個復雜些,加入各個vbptr以及各個的vfptr,平同編譯器策略還是不同的,限于篇幅與主題,這個不細討論了,有興趣可以自己寫個demo調試一下。這里先以VS2019平臺編譯的win32分析

mainclass

我們可以借助VS自帶工具查看類的內存分布,tools->command line->Developer Command Prompt打開,輸入cl -d1reportSingleClassLayoutXXXX XXXX.cpp便可得到類的layout信息,可以觀測到C是16字節,第一個4字節內存是虛函數表vftab,然后是繼成A的成員m_iA,再次之B的成員m_iB,最后是C的對象m_iC。其中虛表函數分布也是簡單易看。

class C size(16):+---0 | +--- (base class B)0 | | +--- (base class A)0 | | | {vfptr}4 | | | m_iA| | +---8 | | m_iB| +--- 12 | m_iC+---C::$vftable@:| &C_meta| 00 | &C::fun1 | &C::fun22 | &A::funA3 | &B::funB4 | &C::funCC::fun this adjustor: 0 C::fun2 this adjustor: 0 C::funC this adjustor: 0

我們用IDA打開這個exe進行查看,32位就用32位IDA,64位就用64位的

File->open->生成exe文件路徑

定位到main函數,查看反匯編代碼

我們來逐行解析IDA的反匯編

.text:

由上面分析可知,在匯編層不一定和書寫完全一致,有時會遇到編譯器很強大的優化。我們再解釋一下內存分布,以便更加強化理解,本例pkC在堆上分配,pkC的虛表放在只讀數據段,里面有5個函數地址,(0 | &C::fun 1 | &C::fun2 2 | &A::funA 3 | &B::funB 4 | &C::funC),代碼段當然就是各種函數的指令實體,這5個函數實體也放在這里。

10. STL的知識,最好看一下STL源碼解析那本書,沒看過也問題不大。以vector.size()反匯編解析。

std

我們進行IDA反匯編靜態分析

.text:

從上面反匯編可看出,std::vector<long> vNum對象分配在棧區,本例在ebp+Memory中,大小是12個字節,是3個指針占用_Myfirst,_Mylast,_Myend,這要看源碼才清楚;vector容器的元素對象實際在堆區分配,這里放了一個long型100,占用8個字節,由STL的Alloc分配器new出來,Alloc的代碼也是值得研讀。這里size()函數根本沒有調用,由內聯復制過來了,且中間還被取std::cout地址插了一腳。size計算兩個指針地址相減,然后除以4,這些略為背后點知識,可能都要深入了解一下STL。



好了,有了上面知識準備,開始進行Android Arm64調試,這里有幾個知識點

1.Arm64有31個通用整型寄存器,r0-r30。當使用64bits時候,命名x0-x30;使用32bits時,命名w0-w30

2.ESP變成sp,EIP變成pc

3.一般函數可以用r0到r7傳遞時不用棧,不夠時也用棧傳

4.返回值一般用r0,有時也用r8

5.常用指令,指令也是挺多的,記不住的也可以查。

  • MOV x1,x0; 傳送指令,x0->x1
  • ADD x0,x1,x2; 相加,x1+x2 ->x0
  • SUB x0,x1,x2; 相減,x1-x2->x0
  • AND x0,x0,#8; 相與,(x0&8)->x0
  • ORR x0,x0,#8; 相或,(x0|8)->x0
  • EOR x0,x0,#8; 相或,(x0^8)->x0
  • LDR x0,[x1,#8]; 取內存(x1+8)地址放到x0
  • STR x0, [sp, #8]; 取x0數據到SP+8內存地址
  • STP x3, x4, [sp, #8]; 入棧
  • LDP x3, x4, [sp, #8]; 出棧
  • CMP x3,x4;比較x3,x4值
  • CBZ x3, xxxxxx; x3為0,就跳到xxxxxx代碼段
  • CBNZ x3, xxxxxx; x3不為0就跳到xxxxxx代碼段
  • B/BL ; 絕對跳轉,返回地址保存到LR(x30)

實戰:1例野指針,一例沒有判空

1.打開IDA選擇New,打開要調試的so文件

2.打開主界面后,看崩潰的pc代碼段值,比如0x0000000014A3287C,按G鍵跳轉過到0x0000000014A3287C,然后手動找到函數開頭,也可以按空格鍵看調用關系圖。

3.對著代碼反推反匯編,這個需要一些經驗,對指令熟悉的越多,通常會越快,也和復雜度正相關,對于比較重要的地方,可以按;鍵加一些注釋幫助理解。

4.由于實際項目代碼非常復雜且不便公開,要推較長時間,我這里寫些假的代碼,只取最關鍵部分,模擬一下崩潰環境。

A:野指針,直接看這代小代碼,也問題不大,該判斷的也都判斷了

struct

我們崩潰在0000000000002C0C,用IDA進行Arm64反匯編,一行一行的分析

.text:

B:空指針

這種問題是比較簡單,但實際工程量大了,代碼量大了,比如1000萬行以上,要判斷的地方太多,或者有的地方理論上就不應該為空,都進行判斷有時也挺討厭,這種問題對工程大的相對有點惡心,但這種問題修復起來比較難,因為問題根源不在這,只是這里先發生而已。簡單示意一下,略去前面要推的代碼,只取最核心的。

struct

我們崩潰在0000000000002C20,用IDA進行Arm64反匯編,一行一行的分析

.text:

通過匯編分析我們可知pkA為空指針,要進行進一步修補。

文章就介紹到這里,希望和大家一起進步。

總結

以上是生活随笔為你收集整理的crash工具解析_IDA反汇编静态调试Android平台C++的so文件Crash入门的全部內容,希望文章能夠幫你解決所遇到的問題。

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