静态分析反调试apk
轉(zhuǎn)載出處:《教我兄弟學(xué)Android逆向10 靜態(tài)分析反調(diào)試apk》 吾愛破解論壇
在進(jìn)入本節(jié)課之前我先問你一個(gè)問題
什么是反調(diào)試?
答:反調(diào)試技術(shù)是為了保護(hù)自己程序的代碼不被逆向。增加逆向分析的難度 防止程序被破解, 針對動(dòng)態(tài)分析。
要么學(xué)!要么不學(xué)!學(xué)和不學(xué)之間沒有中間值 不學(xué)就放棄,學(xué)就要去認(rèn)真的學(xué)! --致選擇
分析環(huán)境:JEB2.2.7+IDA7.0 測試手機(jī):nexus
IDA7.0下載鏈接
鏈接:https://pan.baidu.com/s/1xEtjnTVZFuLiMpHKwMOY2Q 密碼:g3v9
前言
測試一:首先拿到反調(diào)試挑戰(zhàn).apk 安裝后直接運(yùn)行,這里因?yàn)槲覜]有動(dòng)態(tài)調(diào)試程序,所以彈出框恭喜你,挑戰(zhàn)成功!
測試二:用第9課講的調(diào)試方法 先運(yùn)行程序等程序彈框后再掛起IDA調(diào)試 發(fā)現(xiàn)IDA直接退出 猜測程序做了反調(diào)試。
一 用jeb反編譯反調(diào)試挑戰(zhàn).apk
1打開Manifest查看android:debuggable=“true” 說明此程序是可以被動(dòng)態(tài)調(diào)試的。
2找到程序入口MainActivity 分析可知程序運(yùn)行的時(shí)候調(diào)用了myJNI類中的Native函數(shù)checkport并彈框 所以checkport是彈框的內(nèi)容 看函數(shù)名可知里面也有可能做了反調(diào)試
3分析代碼可知程序彈框后說明checkport函數(shù)已經(jīng)執(zhí)行完了,但是IDA掛起來后程序還是退出,說明還是被反調(diào)試了 猜想有兩種情況:
猜想一: checkport函數(shù)中開啟了一個(gè)線程循環(huán)來做反調(diào)試 就算函數(shù)執(zhí)行完 這個(gè)線程中的反調(diào)試還是運(yùn)行的 (如果不開啟線程 循環(huán)檢測反調(diào)試程序主線程將會(huì)堵塞 )
猜想二: 我們知道init_array和JNI_OnLoad會(huì)在so加載的時(shí)候就開始執(zhí)行,所以程序也有可能會(huì)在這里開啟線程進(jìn)行反調(diào)試。
4.帶著上面的猜想 我們下面來分析下so
二 驗(yàn)證猜想一
IDA分析so
解壓反調(diào)試挑戰(zhàn).apk進(jìn)入lib文件夾下找到libsix.so并用ida打開 由于我手機(jī)支持v7所以這里我打開的是armeabi-v7a下的so
注意:這里一定要打開手機(jī)對應(yīng)的so 如果手機(jī)是arm的就打開armeabi文件夾下的so,支持v7則打開armeabi-v7a下的so 同理x86的要打開x86文件夾下的so進(jìn)行分析 這里很重要 不然會(huì)和動(dòng)態(tài)調(diào)試的內(nèi)存地址不對應(yīng)
1.我們先來找到 checkport函數(shù) 看看代碼里面到底有什么 按照以前教程中找Native函數(shù)的方法 我們打開函數(shù)導(dǎo)出表發(fā)現(xiàn)并沒有checkport函數(shù) 在String窗口搜索恭喜你,挑戰(zhàn)成功!也是搜索不到的說明此字符串被隱藏了 但是發(fā)現(xiàn)了JNI_OnLoad函數(shù) 說明函數(shù)checkport是動(dòng)態(tài)注冊的。
3點(diǎn)進(jìn)dword_1140發(fā)現(xiàn)這部分代碼IDA沒有解析好 這里需要手動(dòng)解析一下 點(diǎn)住dword_1140右鍵Data轉(zhuǎn)化成數(shù)據(jù) 然后鼠標(biāo)放在__unwind按住P鍵就轉(zhuǎn)換成函數(shù)了 這個(gè)函數(shù)就是checkport對應(yīng)的函數(shù)
4.分析checkport函數(shù) F5轉(zhuǎn)換成C偽代碼 導(dǎo)入Jni.h (不會(huì)導(dǎo)入的看前面的課程) 然后手動(dòng)解析代碼 提高代碼可讀性 解析完成后Esc返回到C代碼 再按下F5刷新一下代碼 解析的字符串就出來了
5 通過以上操作可以看到 恭喜你,挑戰(zhàn)成功字符串已經(jīng)被我們解析出來了 接下來分析這個(gè)函數(shù) 這個(gè)函數(shù)會(huì) 讀取/proc/net/tcp,查找23946端口,也就是IDA動(dòng)態(tài)調(diào)試的端口 如果查找到了說明程序正在被動(dòng)態(tài)調(diào)試 然后退出程序 從而達(dá)到了反調(diào)試的目的。
但是這里我們看到這個(gè)函數(shù)的反調(diào)試并沒有放到線程中做 說明只執(zhí)行一次就結(jié)束了 而且測試一驗(yàn)證的時(shí)候這里的代碼已經(jīng)執(zhí)行完了 所以其他地方一定還有反調(diào)試 并且只能在這個(gè)函數(shù)之前執(zhí)行。
三 驗(yàn)證猜想二
init_array介紹
init_array段是在so加載的時(shí)候執(zhí)行的 執(zhí)行順序要優(yōu)先于 JNI_OnLoad 所以這里是最早被執(zhí)行的函數(shù) 把反調(diào)試和so的解密放到這里是比較好的選擇。
1 ctrl+s打開segment表找到.init_array段點(diǎn)進(jìn)去 發(fā)現(xiàn)init_array段里面有一個(gè)thread_create函數(shù) 點(diǎn)進(jìn)去F5轉(zhuǎn)換成C偽代碼 并分析函數(shù)
2.我們打開命令行窗口來驗(yàn)證一下 查看當(dāng)前程序的status文件 ps命令用來列出系統(tǒng)中當(dāng)前運(yùn)行的那些進(jìn)程
3.通過上面分析可知thread_create函數(shù)是創(chuàng)建線程循環(huán)讀取當(dāng)前程序的Tracepid的值 如果值大于0說明程序當(dāng)前正在被動(dòng)態(tài)調(diào)試并退出程序 那么現(xiàn)在就知道為什么在程序彈出恭喜你,挑戰(zhàn)成功框后我們進(jìn)行動(dòng)態(tài)調(diào)試 程序還是會(huì)退出了 因?yàn)檫@里開啟了一個(gè)線程進(jìn)行循環(huán)反調(diào)試。
4.那么到這里程序的反調(diào)試是不是就找完了呢?剛剛我們也說了除了init_array還有一個(gè)地方JNI_OnLoad函數(shù)也會(huì)在so剛加載的時(shí)候運(yùn)行 那么出于習(xí)慣 我們還是來看一下JNI_OnLoad函數(shù)
四 JNI_OnLoad函數(shù)
1.在函數(shù)窗口中搜索并找到JNI_OnLoad函數(shù) F5反編譯成C偽代碼(有點(diǎn)時(shí)候?qū)С霰砝锩媸菦]有JNI_OnLoad函數(shù)的 所以這里在函數(shù)窗口中搜索)
2.經(jīng)過上面分析知道 JNI_OnLoad函數(shù)中調(diào)用SearchObjProcess函數(shù)進(jìn)行反調(diào)試 這個(gè)函數(shù)通過ps列出當(dāng)前手機(jī)的所有進(jìn)程 然后如果進(jìn)程名中包含android_server,gdbserver,gdb等名稱 則認(rèn)為程序當(dāng)前被動(dòng)態(tài)調(diào)試 退出程序
五 小結(jié)
1.首先我們通過JEB工具靜態(tài)分析了反調(diào)試挑戰(zhàn).apk 發(fā)現(xiàn)在MainActivity類中調(diào)用了Native函數(shù)checkprot 因?yàn)檫@個(gè)函數(shù)是動(dòng)態(tài)注冊的 所以我們在data.rel.ro.local段中找到了這個(gè)函數(shù) 分析知道這個(gè)函數(shù)會(huì)讀取/proc/net/tcp,查找23946端口 如果找到則認(rèn)為程序當(dāng)前被動(dòng)態(tài)調(diào)試 退出程序
2 在init_array段里面發(fā)現(xiàn)了thread_create函數(shù) 這個(gè)函數(shù)創(chuàng)建了一個(gè)線程循環(huán)來讀取/proc/pid/status文件下的TracePid的值 如果大于0說明程序正在被調(diào)試 退出程序
3.JNI_OnLoad函數(shù)中發(fā)現(xiàn)了SearchObjProcess函數(shù)通過搜索指定進(jìn)程名來判斷程序有沒有被調(diào)試
六 解決方案一
1.對于checkprot我們可以通過-p將IDA調(diào)試端口改為23947或者其他端口 注意端口轉(zhuǎn)發(fā)和IDA調(diào)試的端口號都要改成23947
2.對于thread_create函數(shù)我們可以刷機(jī)改內(nèi)核讓TracePid的值永遠(yuǎn)為0
參考文章《逆向修改內(nèi)核,繞過TracerPID反調(diào)試 》
3.對抗SearchObjProcess函數(shù)可以將android_server改成其他名字然后運(yùn)行 比如zs ls ww(張三,李四,王5)參考文章
《教我兄弟學(xué)Android逆向番外03 Android逆向必會(huì)命令》
七 解決方案二
1.也可以通過exit函數(shù)定位到反調(diào)試位置并patch掉當(dāng)前函數(shù) 這里以thread_create函數(shù)為例子 因?yàn)榇撕瘮?shù)在init_array段里面 所以是沒有調(diào)用的地方的 這里直接把第二條指令改成pop直接出棧 改Hex指令這里我就不為你演示了 前面教程都有教過。
2.對于SearchObjProcess函數(shù) 直接找到調(diào)用此函數(shù)的位置 然后nop掉 或者進(jìn)函數(shù)里面把exit給nop掉都行 最后一處反調(diào)試這里也不演示了 方法相同
八 另一種patch方法
以前我們都是用IDA插件modifyfile.plw來patch 其實(shí)還有一種patch的方法 直接用IDA Patch Program插件來Patch也是可以的 點(diǎn)菜單Edit->Patch Progra
最后將patch后的so替換原包的so 重打包簽名 運(yùn)行 即可過反調(diào)試。
九總結(jié)
本節(jié)課我?guī)阌肑EB+IDA工具靜態(tài)分析了反調(diào)試APK 通過本節(jié)課的學(xué)習(xí) 你了解了init_array段和JNI_OnLoad函數(shù)的執(zhí)行順序和這三處反調(diào)試的找尋方法 對于反調(diào)試 當(dāng)然也可以通過搜尋特征的方式定位位置 比如TracePid反調(diào)試可以在字符串窗口搜索/proc/%d/status 檢測進(jìn)程名可以搜索android_server 檢測端口號搜索5D8A 然后我又帶你重溫了一遍函數(shù)Patch的方法 以及第二種保存修改so的方法 通過本節(jié)課我想你對反調(diào)試技術(shù)已經(jīng)有了初步的了解 教程附件里面前人總結(jié)出來的反調(diào)試大全 你可以看一看并且學(xué)習(xí)一下。學(xué)習(xí)完之后 下節(jié)課我將會(huì)為你講解怎么動(dòng)態(tài)調(diào)試這個(gè)apk。
十 課后作業(yè)
1.按照本節(jié)課教程把附件里面的反調(diào)試.apk so里面的反調(diào)試函數(shù)給patch掉 并重打包
2.了解并熟悉附件里面常見的反調(diào)試
總結(jié)
以上是生活随笔為你收集整理的静态分析反调试apk的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决IE浏览器下导出Excel文件乱码和
- 下一篇: Revit剪贴板用法复制一样的楼层及构件