安卓逆向_16 --- ARM 静态分析( 使用 IDA Pro 分析 ARM 汇编【java_ 和 JNI_OnLoad】 )
?
菜鳥總結 so 分析,arm 匯編,IDA 靜態分析:https://www.52pojie.cn/thread-695063-1-1.html
JNI 靜態注冊 so 和 IDA 導入的 JNI.h 文件.zip:https://download.csdn.net/download/freeking101/12571373
?
ARM?靜態分析:
代碼 和 數據 的 切換:C??=>??代碼D??=>??數據???A??=>??ascii字符串?U??=>??解析成未定義的內容p ?=> ?識別成一個函數 X ? => ?查看交叉應用 F5 => ?查看偽代碼 Alt+T ? => ?搜索文本 Alt+B ? => ?搜索十六進制?
- https://www.bilibili.com/video/BV1vE411c7Zj?p=49? ? ? ? 15' 30''【識別成函數 ,按p】? ? 15' 50''【識別成字符串,按 a】
?
- https://www.bilibili.com/video/BV1UE411A7rW?p=50
JNI_OnLoad 函數 的 ARM 匯編代碼的簡單分析
- R0 --- R3:這 4 個寄存器,用來傳遞函數調用的 第1 到 第4 個參數,超出的參數?通過 堆棧 來傳遞。
- R0 :R0寄存器 同時用來存放 "函數的返回值",類似 x86 匯編中 ax 寄存器,存放 call 之后的返回值。
- 被調用的函數在返回前無需恢復這些寄存器的內容
關于 ADD PC 寄存器
?
?
- https://www.bilibili.com/video/BV1UE411A7rW?p=51
getText 函數分析:
IDA Pro 反編譯后,參數類型反編譯錯誤,修改參數類型 和 重命名參數。。。。。
修改變量類型( 快捷鍵 :按 Y? ):
重命名變量名 和 修改變量類型 差不多,這個是右鍵之后選擇 Rename 選項( 快捷鍵:按 N ),這里 a1 重命名為 env,如圖:
同理,參數 int a2 修改為 jobject obj?
hide casts ,是代碼看起來更清晰
導入 jni.h 頭文件。? ? 12'
添加 jni 的結構體。 12' 50''
選擇和 jni 相關的結構體進行添加
還原 ARM匯編代碼中的方法名:
?
- https://www.bilibili.com/video/BV1UE411A7rW?p=52
靜態函數 getText2 函數的ARM代碼分析
ARM 代碼
關于 add pc 寄存器:? ? 8' 30''
?
?
- https://www.bilibili.com/video/BV1UE411A7rW?p=53
JNI_OnLoad 正向開發:
JNI_OnLoad 的 ARM 代碼分析:
C++ 函數定位? ? 13'
C++函數參數對應分析
?
Cadd 函數、Csub函數 對應 ARM 代碼:(思考:怎么通過修改 arm 代碼,把 加法 變成 減法???)
?
?
菜鳥總結 so 分析,arm 匯編,IDA 靜態分析
?
So?靜態 (arm?匯編,IDA 靜態分析等)
- R7:棧幀指針(Frame Pointer)。指向前一個保存的棧幀(stack frame)和鏈接寄存器(link register,?lr)在棧上的地址
- R13:又叫SP(stack pointer),是棧頂指針
- R14:又叫LR(link register),存放函數的返回地址。
- R15:又叫PC(program counter),指向當前指令地址
- bl:這條指令對函數進行調用。請記住被調用函數需要的參數已經存儲到相關的寄存器中了(r0和r1)。這條指令的執行一般被當做一個分支(branch)。可以理解為執行帶鏈接的分支,也就是說,在跳轉到分支之前,會將lr(link register)的值設置為當前函數中將要執行的下一條指令,當從分支(被調函數)中返回時,通過lr中的值可以知道當前函數執行到哪里了。
- blx 中的 x 標示交換 “exchange”,意思是如果有必要,處理器將對指令集模式進行切換。
- 返回值 (存儲在 r0 中)
- mov r0, r1? ? ? ? ? ? ?=>????r0 = r1
- mov r0, #10 ? ? ? ? ?=> ? ?r0 = 10
- ldr r0, [sp]? ? ? ? ? ? ?=> ? ?r0 = *sp
- str r0, [sp]? ? ? ? ? ? ?=> ? ?*sp = r0
- str? ? ? ? ? ? ? ? ? ? ? ? ?把寄存器內容存到棧上去
- ldr? ? ? ? ? ? ? ? ? ? ? ? ?把棧上內容載入一寄存器中
- add r0, r1, r2? ? ? ? ?=> ? ?r0 = r1 + r2
- add r0, r1? ? ? ? ? ? ? => ? ?r0 = r0 + r1
- push {r0, r1, r2}? ? ?=>?? ?將?r0, r1?和?r2push 到棧中.
- pop {r0, r1, r2}? ? ? ?=>?? ?將3個值從棧中pop出來,并存放到r0, r1?和?r2中.
- b_label? ? ? ? ? ? ? ? ? => ? ?pc = _label
- bl _label? ? ? ? ? ? ? ? ?=> ? ?lr = pc + 4; pc = _label
- self 和 _cmd 占用了 r0 和 r1 寄存器。它存儲著當前執行方法的 selector
- 每次調用 Objective-C 方法時,都由 objc_msgSend 方法處理消息的派送。該方法根據傳遞的消息類型在類的方法列表中查找被調用方法的實現。objc_msgSend方法:id objc_msgSend(id self, SEL _cmd, ...)
- MOV R1, #0 的 機器碼計算:這里將?5?位?opcode?分成了兩部分 ----- 前?3?位?001?是固定的,后?2?位用于標識?4?中不同的操作:?mov, cmp, add, sub。所以?mov?指令的?opcode?二進制表示為?00100;這里?Rd?為?R1,所以?8~10?位為?001;同理,?0~7?為就?0000 0000。所以?MOVS R1, #0?的?2?進制表示為:?0010 0001 0000 0000 = 0x 21 00。
http://blog.csdn.net/zolovegd/article/details/1826192
http://blog.csdn.net/gooogleman/article/details/3758555(opcode學習帖子)
?
.so 文件(shared object)?linux 的動態鏈接庫,
顯示調用則是在主程序里使用 dlopen、dlsym、dlerror、dlclose 等系統函數。
1.IDA計算出了成員變量的偏移地址并把 symbol 直接顯示出來
IDA:__text:000026C4? ?? ?? ?? ?? ???
mov??ebx, ds:(_OBJC_IVAR_$_TestButton_m_model - 26C3h)[esi]
2.函數參數在IDA中被賦予名稱,ebp+8為arg_0,ebp+12為arg_1。?arg即為argument的縮寫,第n個參數在+號后面的偏移量不是絕對的
。在函數開頭和代碼中,名稱都會直接替換掉實際偏移量。基本上arg_0都是self。
3.常數值型偏移地址被賦予名稱,以loc_為前綴。
IDA:jmp? ???short loc_2732
4.局部變量,即?ebp-xxx?會被命名為?var_xxx
搜索特征字符串。具體操作為:①快捷鍵Ctrl+S,打開搜索類型選擇對話框-->雙擊Strings,跳到字符串段-->菜單項“Search-->Text”;
②快捷鍵Alt+T,打開文本搜索對話框,在String文本框中輸入要搜索的字符串點擊OK即可;
(C的函數?,抹掉了符號表)
?
?
So?動態調試 (?.init_array 下斷?)
?
System.loadLibrary()加載so文件流程
- 先讀取 so 文件的 .init_array段。
- 再執行 JNI_OnLoad 函數,JNI_ONLoad是.so文件的初始函數
- 然后調用具體的 native 方法
so?被加載之后最開始執行的是 .init_array?段的代碼,然后才去執行?jni_onload,那么在 .init_array?處斷下來便是很有必要的
?
- 1. 啟動?android_server
- 2. 端口轉發:adb forward tcp:23946 tcp:23946
- 3. 調試啟動:adb shell am start -D -n?com.scottgames.fnaf4/com.putaolab.ptsdk.activity.PTMainActivity
- 4.鏈接,下斷點
?
Shift+F12?打開 字符串窗口,搜索字符串:?dlopen,找到?dlopen?函數的偏移?0xF30
動態調試的?IDA?中,?G?跳轉到:?400D3000+F30=400D3F30?處,下好斷點
搜索字符串:?calling
按?F9?運行
然后打開?Eclipse?或者?ddms,執行?jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
程序就會斷在第一個斷點處,?F9?幾次就段在?blx R4?處
F7?跟進就來到?init?段代碼處:
1.IDA用32位,
2 ./android_server??要su
3? 重啟平板
4. <application android:allowBackup="true" android:debuggable="false" android:icon="@drawable/app_icon"
Dump
dvmLoadNativeCode?函數是加載和初始化?so?的函數,?dvmDexFileOpenPartial?函數是對緩存?Dex?文件,該函數第一個參數就是解密后?dex?文件頭內存地址,而第二個參數是該?dex?大小。
跳到?dvmDexFileOpenPartial?函數或?inflate?函數去下斷,
int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex);
第一個參數就是?dex?內存起始地址,第二個參數就是?dex?大小。所以在這個函數下斷點可以直接?dump?出明文?dex
static main(void)
{
auto fp, dex_addr, end_addr;
fp = fopen("D:\\dump.dex", "wb");
end_addr = r0 + r1;
for ( dex_addr = r0; dex_addr < end_addr; dex_addr ++ )
fputc(Byte(dex_addr), fp);
}
?
遇到反調試
先查 Pid,ps -aux,然后 cat /proc/xxxxx/status 就可以看 tracerPid, 如果不為零及被調試過,當程序打開進程成功后使用?fgets?獲得信息,當獲得如下信息進我們將其修改為?0,原因:底層會調用?libc?庫中的?fopen?函數打開?so?文件句柄,然后通過?fgets?函數讀取進程狀態值,就可以通過修改讀取到的狀態值繞過調試進程檢測。
修改成 0( Thumb 00 20?,?70 47 即?Mov R0,#0)
1.fopen—/proc/self/cmdline.debug.atrace.app_cmdlines
2.fgets—-包名
3.LoadNativeCode–加載?libexec.so
4.LoadNativeCode–加載?libexecmain.so
5.建立反調試線程(通過檢查是否存在調試進程)
6.調用?fopen 一一打開/proc/pid/status
7.調用?fgets —讀取調試進程?pid
在?fgets?內部會調用?memchr?函數,和?memcpy?函數,?memchr?函數完成以換行為分隔符,?memcpy?將此次讀取位置拷貝到目的緩沖區
?
?
脫殼:https://www.52pojie.cn/forum-5-1.html
nop: 0xc046
INIT_ARRAY,JNI_OnLoad
殼入口?---> INIT_ARRAY ---> 解密第二層殼(JNI_OnLoad) ---> 解密原始so文件 ---> 解壓縮原始so的代碼節。
http://www.52pojie.cn/thread-356096-1-1.html
__gnu_armfini_26?此函數是 ELF 的入口函數,此函數就完成了 jni_onload 和 verify 等的解密。
?
?
?
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的安卓逆向_16 --- ARM 静态分析( 使用 IDA Pro 分析 ARM 汇编【java_ 和 JNI_OnLoad】 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络层(学习笔记)
- 下一篇: 王爽 汇编语言第三版 第8章( 寻址方式