安卓逆向_16 --- ARM 靜態分析( 使用 IDA Pro 分析 ARM 彙編 )

 

From:菜鳥總結so分析,arm 彙編,IDA靜態分析:https://www.52pojie.cn/thread-695063-1-1.html

 

ARM 靜態分析:

https://www.bilibili.com/video/BV1UE411A7rW?p=50
https://www.bilibili.com/video/BV1UE411A7rW?p=51
https://www.bilibili.com/video/BV1UE411A7rW?p=52
https://www.bilibili.com/video/BV1UE411A7rW?p=53

 

 

菜鳥總結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 動態調試
so 被加載之後最開始執行的是.init_array 段的代碼。然後纔會去執行 jni_onload那麼,在.init_array 處斷下來便是很有必要的
1.啓動 android_server

端口轉發
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 將此次讀取位置拷貝到目的緩衝區
脫殼
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等的解密。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章