Android APP破解利器Frida之反调试对抗
·?OWASP Level2的CrackMe APK
·?Android SDK和模擬器(我使用的是Android 7.1 x64映像)
·?Frida安裝(以及frida-server二進(jìn)制文件)
·?ByteCode viewer
·?radare2(或你喜歡使用的其他一些反匯編程序)
·?apktool
如果你需要知道如何安裝Frida,請查看Frida的相關(guān)?文檔。對于Frida的使用,你還可以查看本教程系列的第I部分。我想你在繼續(xù)本文的操作之前已經(jīng)擁有所有需要的東西,并且基本上熟悉了Frida的使用。另外,確保Frida可以連接到你的設(shè)備或模擬器(例如使用frida -ps -U 命令)。
警告:本教程不僅僅是一個(gè)快速破解Crackme的練習(xí)。相反,我將向你展示各種方法來克服所遇到的具體問題。如果你只是尋找一個(gè)快速的破解方案,請?jiān)诒窘坛棠┪膊榭聪嚓P(guān)的Frida腳本。
注意:如果你遇到了下面這個(gè)錯(cuò)誤:
Error:?access?violation?accessing?0xebad8082或者使用Frida時(shí)會出現(xiàn)了類似的錯(cuò)誤,再模擬器中擦除用戶數(shù)據(jù),重新啟動并重新安裝該apk可能有助于解決此類錯(cuò)誤問題。
做好多次嘗試的準(zhǔn)備。該應(yīng)用程序可能會崩潰,模擬器也可能會重新啟動,一切也可能會搞砸,但是,它依舊可以正常工作。
先把APP跑起來
在開始時(shí),我們做的事情和之前破解UnCrackable1是一樣的,先運(yùn)行應(yīng)用程序:接下來,當(dāng)你在模擬器中運(yùn)行它時(shí),它會檢測到它是在有root權(quán)限的設(shè)備上運(yùn)行的。
我們可能會嘗試像之前破解UnCrackable 1一樣Hook OnClickListener。但首先我們來看看我們是否可以連接到Frida來進(jìn)行篡改:
michael@sixtyseven:~/Development$?frida?-U?sg.vantagepoint.uncrackable2____/?_??|???Frida?9.1.27?-?A?world-class?dynamic?instrumentation?framework|?(_|?|>?_??|???Commands:/_/?|_|???????help??????->?Displays?the?help?system.?.?.?.???????object????->?Display?information?about?'object'.?.?.?.???????exit/quit?->?Exit.?.?.?..?.?.?.???More?info?at?http://www.frida.re/docs/home/ Failed?to?attach:?ambiguous?name;?it?matches:?sg.vantagepoint.uncrackable2?(pid:?5184),?sg.vantagepoint.uncrackable2?(pid:?5201)這是什么東西?有兩個(gè)名稱相同的進(jìn)程?我們可以使用frida -ps -U命令來驗(yàn)證一下:
5184??sg.vantagepoint.uncrackable2 5201??sg.vantagepoint.uncrackable2奇怪。我們試著將Frida注入到父進(jìn)程:
michael@sixtyseven:~/Development$?frida?-U?5184____/?_??|???Frida?9.1.27?-?A?world-class?dynamic?instrumentation?framework|?(_|?|>?_??|???Commands:/_/?|_|???????help??????->?Displays?the?help?system.?.?.?.???????object????->?Display?information?about?'object'.?.?.?.???????exit/quit?->?Exit.?.?.?..?.?.?.???More?info?at?http://www.frida.re/docs/home/ Failed?to?attach:?unable?to?access?process?with?pid?5184?due?to?system?restrictions;?try?`sudo?sysctl?kernel.yama.ptrace_scope=0`,?or?run?Frida?as?root看起來它并不起作用,因?yàn)楫?dāng)我們使用root權(quán)限運(yùn)行Frida時(shí),我們得到了相同的結(jié)果,所以提出的解決方案并沒有什么幫助。這里到底發(fā)生了什么?我們來看看應(yīng)用程序吧。解壓縮apk并使用ByteCodeViewer 查看classes.dex并進(jìn)行反編譯(例如CFR-Decompiler):
package?sg.vantagepoint.uncrackable2; import?android.app.AlertDialog; import?android.content.Context; import?android.content.DialogInterface; import?android.os.AsyncTask; import?android.os.Bundle; import?android.support.v7.app.c; import?android.text.Editable; import?android.view.View; import?android.widget.EditText; import?sg.vantagepoint.a.a; import?sg.vantagepoint.a.b; import?sg.vantagepoint.uncrackable2.CodeCheck; import?sg.vantagepoint.uncrackable2.MainActivity; public?class?MainActivity extends?c?{private?CodeCheck?m;static?{System.loadLibrary("foo");?//[1]}private?void?a(String?string)?{AlertDialog?alertDialog?=?new?AlertDialog.Builder((Context)this).create();alertDialog.setTitle((CharSequence)string);alertDialog.setMessage((CharSequence)"This?in?unacceptable.?The?app?is?now?going?to?exit.");alertDialog.setButton(-3,?(CharSequence)"OK",?(DialogInterface.OnClickListener)new?/*?Unavailable?Anonymous?Inner?Class!!?*/);alertDialog.setCancelable(false);alertDialog.show();}static?/*?synthetic?*/?void?a(MainActivity?mainActivity,?String?string)?{mainActivity.a(string);}private?native?void?init();?//[2]protected?void?onCreate(Bundle?bundle)?{this.init();?//[3]if?(b.a()?||?b.b()?||?b.c())?{this.a("Root?detected!");}if?(a.a((Context)this.getApplicationContext()))?{this.a("App?is?debuggable!");}new?/*?Unavailable?Anonymous?Inner?Class!!?*/.execute((Object[])new?Void[]{null,?null,?null});this.m?=?new?CodeCheck();super.onCreate(bundle);this.setContentView(2130968603);}public?void?verify(View?view)?{String?string?=?((EditText)this.findViewById(2131427422)).getText().toString();AlertDialog?alertDialog?=?new?AlertDialog.Builder((Context)this).create();if?(this.m.a(string))?{alertDialog.setTitle((CharSequence)"Success!");alertDialog.setMessage((CharSequence)"This?is?the?correct?secret.");}?else?{alertDialog.setTitle((CharSequence)"Nope...");alertDialog.setMessage((CharSequence)"That's?not?it.?Try?again.");}alertDialog.setButton(-3,?(CharSequence)"OK",?(DialogInterface.OnClickListener)new?/*?Unavailable?Anonymous?Inner?Class!!?*/);alertDialog.show();} }我們注意到一個(gè)調(diào)用static的靜態(tài)調(diào)用System.load加載了foo庫(參見[1])。該應(yīng)用程序還在其onCreate方法的第一行調(diào)用了this.init()(參見[3]),該方法被聲明為一種native方法(參見[2]),因此它可能是foo的一部分。
現(xiàn)在讓我們來看看foo庫。在radare2中,打開這個(gè)庫文件(你可以在lib文件夾中找到各種架構(gòu)的多個(gè)庫文件,我在這里使用的是lib/x86_64),分析并列出其導(dǎo)出函數(shù):
michael@sixtyseven:~/Development/UnCrackable2/lib/x86_64$?r2?libfoo.so--?Don't?look?at?the?code.?Don't?look. [0x000007a0]>?aaa [x]?Analyze?all?flags?starting?with?sym.?and?entry0?(aa) [x]?Analyze?len?bytes?of?instructions?for?references?(aar) [x]?Analyze?function?calls?(aac) [?]?[*]?Use?-AA?or?aaaa?to?perform?additional?experimental?analysis. [x]?Constructing?a?function?name?for?fcn.*?and?sym.func.*?functions?(aan)) [0x000007a0]>?iE [Exports] vaddr=0x00001060?paddr=0x00001060?ord=004?fwd=NONE?sz=183?bind=GLOBAL?type=FUNC?name=Java_sg_vantagepoint_uncrackable2_CodeCheck_bar vaddr=0x00001050?paddr=0x00001050?ord=006?fwd=NONE?sz=15?bind=GLOBAL?type=FUNC?name=Java_sg_vantagepoint_uncrackable2_MainActivity_init vaddr=0x00004008?paddr=0x00003008?ord=014?fwd=NONE?sz=0?bind=GLOBAL?type=NOTYPE?name=__bss_start vaddr=0x00004008?paddr=0x00003008?ord=015?fwd=NONE?sz=0?bind=GLOBAL?type=NOTYPE?name=__bss_start vaddr=0x0000400d?paddr=0x0000400d?ord=016?fwd=NONE?sz=0?bind=GLOBAL?type=NOTYPE?name=_end 5?exports [0x000007a0]>我們注意到,該庫出口2個(gè)有趣的導(dǎo)出函數(shù):
Java_sg_vantagepoint_uncrackable2_MainActivity_init和Java_sg_vantagepoint_uncrackable2_CodeCheck_bar
(對這些方法的具體命名可以查看Java接口NATIV JNI)。
我們來看看Java_sg_vantagepoint_uncrackable2_MainActivity_init:
[0x000007a0]>?s?0x00001050 [0x00001050]>?V這是一個(gè)相當(dāng)短的函數(shù):
[0x00001050?29%?848?libfoo.so]>?pd?$r?@ sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init???????????????????????????????????????????????????????????????????????????????????????????????????? /?(fcn)?sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init?15?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init?();????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00001050??????50?????????????push?rax???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00001051??????e8caf7ffff?????call?sub.fork_820???????????;[1]???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00001056??????c605af2f0000.??mov?byte?[0x0000400c],?1????;?[0x400c:1]=58?;?":?(GNU)?4.9.x?20150123?(prerelease)"????????????????????????????????????????????????????????????????????? |???????????0x0000105d??????58?????????????pop?rax??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0x0000105e??????c3?????????????ret?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0x0000105f??????90?????????????nop它調(diào)用了另一個(gè)函數(shù)sub.fork_820,在這個(gè)函數(shù)中還有更多的函數(shù)調(diào)用:
[0x00000820?14%?265?libfoo.so]>?pd?$r?@?sub.fork_820???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? /?(fcn)?sub.fork_820?242??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???sub.fork_820?();??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????;?var?int?local_8h?@?rsp+0x8???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????;?var?int?local_10h?@?rsp+0x10????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????????????;?CALL?XREF?from?0x00001051?(sym.Java_sg_vantagepoint_uncrackable2_MainActivity_init)??????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00000820??????4156???????????push?r14????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00000822??????53?????????????push?rbx????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00000823??????4883ec18???????sub?rsp,?0x18??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00000827??????64488b042528.??mov?rax,?qword?fs:[0x28]????;?[0x28:8]=0x3180?;?'('????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00000830??????4889442410?????mov?qword?[local_10h],?rax??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x00000835??????e806ffffff?????call?sym.imp.fork???????????;[1]????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????????0x0000083a??????8905c8370000???mov?dword?loc.__bss_start,?eax?;?[0x4008:4]=0x43434700?;?loc.__bss_start???????????????????????????????????????????????????????????????????????????????? |???????????0x00000840??????85c0???????????test?eax,?eax??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????,=<?0x00000842??????741a???????????je?0x85e????????????????????;[2]????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????|???0x00000844??????488d15a5ffff.??lea?rdx,?0x000007f0?????????;?0x7f0????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????|???0x0000084b??????488d7c2408?????lea?rdi,?[local_8h]?????????;?0x8??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????|???0x00000850??????31f6???????????xor?esi,?esi????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????|???0x00000852??????31c9???????????xor?ecx,?ecx????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |???????|???0x00000854??????e8f7feffff?????call?sym.imp.pthread_create?;[3];?ssize_t?read(int?fildes,?void?*buf,?size_t?nbyte)????????????????????????????????????????????????????????????????????? |??????,==<?0x00000859??????e990000000?????jmp?0x8ee???????????????????;[4]???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????||??????;?JMP?XREF?from?0x00000842?(sub.fork_820)???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|`->?0x0000085e??????e8fdfeffff?????call?sym.imp.getppid????????;[5]????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x00000863??????89c3???????????mov?ebx,?eax???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x00000865??????bf10000000?????mov?edi,?0x10??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x0000086a??????31d2???????????xor?edx,?edx????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x0000086c??????31c9???????????xor?ecx,?ecx????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x0000086e??????31c0???????????xor?eax,?eax???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x00000870??????89de???????????mov?esi,?ebx???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x00000872??????e8f9feffff?????call?sym.imp.ptrace?????????;[6]???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|????0x00000877??????4885c0?????????test?rax,?rax???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????|,=<?0x0000087a??????7572???????????jne?0x8ee???????????????????;[4]????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????||???0x0000087c??????4c8d742408?????lea?r14,?[local_8h]?????????;?0x8??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????||???0x00000881??????31d2???????????xor?edx,?edx???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????||???0x00000883??????89df???????????mov?edi,?ebx????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????||???0x00000885??????4c89f6?????????mov?rsi,?r14????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? |??????||???0x00000888??????e883feffff?????call?sym.imp.waitpid????????;[7]我們看到有調(diào)用fork,pthread_create,getppid,ptrace和waitpid。不需要花費(fèi)太多的時(shí)間來進(jìn)行反匯編,我們就可以猜測出,主進(jìn)程使用ptrace作為調(diào)試器來fork了一個(gè)附加到它的子進(jìn)程。這是一個(gè)基本的常見的反調(diào)試技術(shù),你可以在這里閱讀到更多信息。
由于Frida 使用?ptrace用于初始的注入操作,所以這就解釋了為什么我們沒有成果連接到父進(jìn)程:附加調(diào)試進(jìn)程被阻止,因?yàn)橐呀?jīng)有其他的進(jìn)程作為調(diào)試器進(jìn)行了連接。
繞過反調(diào)試解決方案1:Frida
Frida來解決問題的目的不是將Frida注入正在運(yùn)行的進(jìn)程,而是我們可以讓它為我們生成進(jìn)程。使用-f選項(xiàng),我們告訴Frida注入Zygote并開始啟動應(yīng)用程序。關(guān)閉設(shè)備上的應(yīng)用程序,看看當(dāng)我們啟動Frida時(shí)會發(fā)生什么:
frida?-U?-f?sg.vantagepoint.uncrackable2我們得到如下輸出:
michael@sixtyseven:~/Development/UnCrackable2/lib/x86_64$?frida?-U?-f?sg.vantagepoint.uncrackable2?--no-pause____/?_??|???Frida?9.1.27?-?A?world-class?dynamic?instrumentation?framework|?(_|?|>?_??|???Commands:/_/?|_|???????help??????->?Displays?the?help?system.?.?.?.???????object????->?Display?information?about?'object'.?.?.?.???????exit/quit?->?Exit.?.?.?..?.?.?.???More?info?at?http://www.frida.re/docs/home/ Spawned?`sg.vantagepoint.uncrackable2`.?Resuming?main?thread!?????????? [USB::Android?Emulator?5554::['sg.vantagepoint.uncrackable2']]->好吧!Frida注入到了Zygote,產(chǎn)生了我們的進(jìn)程并等待輸入。(我承認(rèn),告訴你在啟動Frida時(shí)添加-f選項(xiàng)是一個(gè)很長的介紹,不過你已被警告過…)
我們現(xiàn)在準(zhǔn)備繼續(xù)破解,?但是在我們繼續(xù)之前,我們將要檢查另一個(gè)解決方案來克服這個(gè)破解的調(diào)試保護(hù)。
繞過反調(diào)試解決方案2:補(bǔ)丁
除了讓Frida生成進(jìn)程之外,我們還可以通過對應(yīng)用打補(bǔ)丁來解決我們遇到的問題。這意味著我們需要反匯編應(yīng)用程序,對修改的apk進(jìn)行重建和簽名。但是,在這種情況下,這將會給我們麻煩帶來麻煩。不過我還是會告訴你如何解決這個(gè)麻煩,我們稍后會注意到這個(gè)問題。
我們可以通過apktool來實(shí)現(xiàn)補(bǔ)丁:
michael@sixtyseven:~/Disassembly$?/opt/apktool/apktool.sh?-r?d?UnCrackable-Level2.apk I:?Using?Apktool?2.2.0?on?UnCrackable-Level2.apk I:?Copying?raw?resources... I:?Baksmaling?classes.dex... I:?Copying?assets?and?libs... I:?Copying?unknown?files... I:?Copying?original?files...(我跳過了資源的提取, 因?yàn)?r參數(shù)在重新編譯apk時(shí)引起了新的問題,我們在這里不需要APP的資源。)
看一下smali/sg/vantagepoint/uncrackable2/MainActivity.smali 文件里的smali代碼。你可以在82行處找到調(diào)用init的代碼,并且可以將其注釋掉:
#?virtual?methods .method?protected?onCreate(Landroid/os/Bundle;)V.locals?4const/4?v3,?0x0 #????invoke-direct?{p0},?Lsg/vantagepoint/uncrackable2/MainActivity;->init()Vinvoke-static?{},?Lsg/vantagepoint/a/b;->a()Z重新編譯apk(忽略致命錯(cuò)誤…):
michael@sixtyseven:~/Disassembly/UnCrackable-Level2$?/opt/apktool/apktool.sh?b I:?Using?Apktool?2.2.0 I:?Checking?whether?sources?has?changed... I:?Smaling?smali?folder?into?classes.dex... [Fatal?Error]?AndroidManifest.xml:1:1:?Content?ist?nicht?zul?ssig?in?Prolog. I:?Checking?whether?resources?has?changed... I:?Copying?raw?resources... I:?Copying?libs...?(/lib) I:?Building?apk?file... I:?Copying?unknown?files/dir...對齊:
michael@sixtyseven:~/Disassembly/UnCrackable-Level2$?zipalign?-v?4?dist/UnCrackable-Level2.apk??UnCrackable2.recompiled.aligned.apk Verifying?alignment?of?UnCrackable2.recompiled.aligned.apk?(4)...49?AndroidManifest.xml?(OK?-?compressed)914?classes.dex?(OK?-?compressed)269899?lib/arm64-v8a/libfoo.so?(OK?-?compressed)273297?lib/armeabi-v7a/libfoo.so?(OK?-?compressed)279346?lib/armeabi/libfoo.so?(OK?-?compressed)簽名(注意:你需要有一個(gè)密鑰和密鑰庫):
michael@sixtyseven:~/Disassembly/UnCrackable-Level2$?jarsigner?-verbose?-keystore?~/.android/debug.keystore??UnCrackable2.recompiled.aligned.apk?signkey Enter?Passphrase?for?keystore:adding:?META-INF/MANIFEST.MFadding:?META-INF/SIGNKEY.SFadding:?META-INF/SIGNKEY.RSAsigning:?AndroidManifest.xmlsigning:?classes.dexsigning:?lib/arm64-v8a/libfoo.sosigning:?lib/armeabi-v7a/libfoo.sosigning:?lib/armeabi/libfoo.sosigning:?lib/mips/libfoo.so [...]你可以在“?OWASP移動安全測試指南”中找到更廣泛的描述。卸載掉原始的apk并安裝打過補(bǔ)丁的apk:
adb?uninstall?sg.vantagepoint.uncrackable2 adb?install?UnCrackable2.recompiled.aligned.apk再次啟動該應(yīng)用程序。運(yùn)行frida-ps后我們發(fā)現(xiàn)現(xiàn)在只有一個(gè)進(jìn)程:
29996?sg.vantagepoint.uncrackable2Frida連接也沒有問題:
michael@sixtyseven:~/Disassembly/UnCrackable-Level2$?frida?-U?sg.vantagepoint.uncrackable2____/?_??|???Frida?9.1.27?-?A?world-class?dynamic?instrumentation?framework|?(_|?|>?_??|???Commands:/_/?|_|???????help??????->?Displays?the?help?system.?.?.?.???????object????->?Display?information?about?'object'.?.?.?.???????exit/quit?->?Exit.?.?.?..?.?.?.???More?info?at?http://www.frida.re/docs/home/ [USB::Android?Emulator?5554::sg.vantagepoint.uncrackable2]->比起給Frida增加 -r選項(xiàng)更為繁瑣一點(diǎn),但也更普遍的適用。
如前所述,當(dāng)我們使用補(bǔ)丁版本后(我會告訴你如何克服這個(gè),所以不要把它丟棄),我們就不能輕易地提取這個(gè)秘密字符串了。但是現(xiàn)在我們繼續(xù)使用原來的apk。確保你按照正確的方式安裝了原始的apk。
繼續(xù)解惑
在我們找到了一些擺脫反調(diào)試的可能性之后,我們來看看我們接下來該如何進(jìn)行。一旦我們按下OK按鈕,應(yīng)用程序就會在模擬器的運(yùn)行時(shí)中進(jìn)行root權(quán)限的檢測并退出。我們已經(jīng)知道UnCrackable1的一些行為。另外,我們可以對這種檢測行為打補(bǔ)丁,刪除System.exit的調(diào)用代碼,但是我們試圖用Frida來解決這個(gè)問題。再看看反編譯的代碼,我們看到?jīng)]有OnClickListener類,只是一個(gè)匿名的內(nèi)部類。由于onClickListener的實(shí)現(xiàn)中調(diào)用了System.exit所以我們簡單地Hook住該函數(shù)并無卵用。
這是Frida腳本:
setImmediate(function()?{console.log("[*]?Starting?script");Java.perform(function()?{exitClass?=?Java.use("java.lang.System");exitClass.exit.implementation?=?function()?{console.log("[*]?System.exit?called");}console.log("[*]?Hooking?calls?to?System.exit");}); });再次關(guān)閉任何正在運(yùn)行的UnCrackable2實(shí)例,并再次在Frida的幫助下啟動它:
frida?-U?-f?sg.vantagepoint.uncrackable2?-l?uncrackable2.js?--no-pause等到App啟動后,Frida?在控制臺中顯示了Hooking calls…的消息。然后按“確定”。你應(yīng)該會得到這樣的輸出信息:
michael@sixtyseven:~/Development/frida$?frida?-U?-f?sg.vantagepoint.uncrackable2?--no-pause?-l?uncrackable2.js____/?_??|???Frida?9.1.27?-?A?world-class?dynamic?instrumentation?framework|?(_|?|>?_??|???Commands:/_/?|_|???????help??????->?Displays?the?help?system.?.?.?.???????object????->?Display?information?about?'object'.?.?.?.???????exit/quit?->?Exit.?.?.?..?.?.?.???More?info?at?http://www.frida.re/docs/home/ Spawned?`sg.vantagepoint.uncrackable2`.?Resuming?main?thread!?????????? [USB::Android?Emulator?5554::['sg.vantagepoint.uncrackable2']]->?[*]?Hooking?calls?to?System.exit [*]?System.exit?called該應(yīng)用程序不再退出。我們可以輸入一個(gè)秘密字符串試試:
但是我們應(yīng)該在這里輸入什么呢?看看MainActivity的Android代碼,檢查正確的輸入邏輯如下:
this.m?=?new?CodeCheck(); [...] //in?method:?public?void?verify if?(this.m.a(string))?{alertDialog.setTitle((CharSequence)"Success!");alertDialog.setMessage((CharSequence)"This?is?the?correct?secret."); }這是CodeCheck類代碼:
package?sg.vantagepoint.uncrackable2; public?class?CodeCheck?{private?native?boolean?bar(byte[]?var1);public?boolean?a(String?string)?{return?this.bar(string.getBytes());?//Call?to?a?native?function} }我們注意到,我們的文本字段的輸入也就是我們的“秘密字符串” 被傳遞給一個(gè)native函數(shù)bar。我們可以在libfoo.so庫中找到這個(gè)函數(shù)。搜索函數(shù)地址(如我們之前使用的init函數(shù)),并用radare2進(jìn)行反匯編:
看看反匯編代碼,我們注意到有一些字符串比較的邏輯,另外我們還注意到一個(gè)有趣的明文字符串Thanks for all t.測試這個(gè)字符串作為我們的crackme的解決方案后,我們發(fā)現(xiàn)它并不起作用。我們必須繼續(xù)向前。
看看0x000010d8這個(gè)地址處的反匯編代碼:
0x000010d8??????83f817?????????cmp?eax,?0x17??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? 0x000010db??????7519???????????jne?0x10f6??????????????????;[1]這里有一個(gè)eax寄存器與0x17的比較,其十進(jìn)制格式為23。如果這個(gè)比較不成功,strncmp在這里就不會調(diào)用。我們也注意到了0x17已作為參數(shù)傳遞給了在
0x000010e1 ba17000000 mov edx, 0x17 處的strncmp。
記住,根據(jù)linux 64位的調(diào)用約定,函數(shù)參數(shù)在寄存器中傳遞至少1到6個(gè)參數(shù)。尤其是前3個(gè)參數(shù)要按照這個(gè)順序在RDI,RSI和RDX中傳遞(參見[PDF],第20頁)。Strncmp函數(shù)聲明如下:
int?strncmp?(?const?char?*?str1,?const?char?*?str2,?size_t?num?);所以我們的strncmp函數(shù)將比較0x17 = 23個(gè)字符。我們可以推斷,我們的秘密字符串的長度應(yīng)該是23個(gè)字符。
讓我們最后嘗試Hook住strncmp函數(shù),并簡單地打印出它的參數(shù)。我們可以期望這里給我們直接輸出解密的輸入字符串。我們必須
1.??? 查找strncmp函數(shù)在libfoo.so中的內(nèi)存地址
2.??? 使用 Interceptor.attach Hook libfoo.so的strncmp函數(shù)和轉(zhuǎn)儲參數(shù)
如果你這樣做了,你會發(fā)現(xiàn)有很多地方調(diào)用了strncmp,我們將進(jìn)一步限制輸出。這是Frida代碼片段:
var?strncmp?=?undefined; imports?=?Module.enumerateImportsSync("libfoo.so"); for(i?=?0;?i?<?imports.length;?i++)?{ if(imports[i].name?==?"strncmp")?{strncmp?=?imports[i].address;break;} } Interceptor.attach(strncmp,?{onEnter:?function?(args)?{if(args[2].toInt32()?==?23?&&?Memory.readUtf8String(args[0],23)?==?"01234567890123456789012")?{console.log("[*]?Secret?string?at?"?+?args[1]?+?":?"?+?Memory.readUtf8String(args[1],23));}} });對這個(gè)腳本的一些說明:
1.??該腳本調(diào)用Module.enumerateImportsSync從libfoo.so(查閱文檔)中獲取有關(guān)導(dǎo)入信息的對象數(shù)組。我們遍歷這個(gè)數(shù)組,直到找到strncmp并檢索其地址。然后我們將攔截器附加到它上面。
2. ?Java中的字符串不會已空字符終止。當(dāng)我們使用Frida的Memory.readUtf8String方法訪問strncmp字符串指針的內(nèi)存位置,并且不提供長度時(shí),Frida會期望一個(gè)終止符,否則會輸出一些內(nèi)存垃圾。因?yàn)樗恢雷址谀睦锝Y(jié)束。如果我們指定要讀取的字符數(shù)量作為第二個(gè)參數(shù),我們就可以解決掉這個(gè)警告。
3.??如果我們沒有限制我們轉(zhuǎn)儲strncmp參數(shù)的條件,我們會得到很多輸出。所以我們只輸出strncmp中當(dāng)?shù)谌齻€(gè)參數(shù)的參數(shù)size_t為23且第一參數(shù)的字符串指針指向我們在輸入框輸入的01234567890123456789012這個(gè)字符串作為過濾條件
我是如何知道args[0]也就是我們的輸入和args[1]也就是秘密字符串的?我其實(shí)并不知道,我只是在測試它,并將大量的輸出轉(zhuǎn)儲到屏幕上,以找到我的輸入。如果你不想跳過,你可以在上述腳本中移除if語句,并使用Frida的hexdump輸出:
buf?=?Memory.readByteArray(args[0],32); console.log(hexdump(buf,?{offset:?0,length:?32,header:?true,ansi:?true })); buf?=?Memory.readByteArray(args[1],32); console.log(hexdump(buf,?{offset:?0,length:?32,header:?true,ansi:?true }));每次調(diào)用strncmp時(shí)都會輸出很多hexdumps,所以要提醒你一下。
以下是完整版本的腳本,可以更好地輸出參數(shù):
setImmediate(function()?{Java.perform(function()?{console.log("[*]?Hooking?calls?to?System.exit");exitClass?=?Java.use("java.lang.System");exitClass.exit.implementation?=?function()?{console.log("[*]?System.exit?called");}var?strncmp?=?undefined;imports?=?Module.enumerateImportsSync("libfoo.so");for(i?=?0;?i?<?imports.length;?i++)?{if(imports[i].name?==?"strncmp")?{strncmp?=?imports[i].address;break;}}Interceptor.attach(strncmp,?{onEnter:?function?(args)?{if(args[2].toInt32()?==?23?&&?Memory.readUtf8String(args[0],23)?==?"01234567890123456789012")?{console.log("[*]?Secret?string?at?"?+?args[1]?+?":?"?+?Memory.readUtf8String(args[1],23));}},});console.log("[*]?Intercepting?strncmp");}); });現(xiàn)在,啟動Frida并加載腳本:
frida?-U?-f?sg.vantagepoint.uncrackable2?--no-pause?-l?uncrackable2.js輸入字符串并按驗(yàn)證:
在控制臺,你會得到:
michael@sixtyseven:~/Development/frida$?frida?-U?-f?sg.vantagepoint.uncrackable2?--no-pause?-l?uncrackable2.js____/?_??|???Frida?9.1.27?-?A?world-class?dynamic?instrumentation?framework|?(_|?|>?_??|???Commands:/_/?|_|???????help??????->?Displays?the?help?system.?.?.?.???????object????->?Display?information?about?'object'.?.?.?.???????exit/quit?->?Exit.?.?.?..?.?.?.???More?info?at?http://www.frida.re/docs/home/ Spawned?`sg.vantagepoint.uncrackable2`.?Resuming?main?thread!?????????? [USB::Android?Emulator?5554::['sg.vantagepoint.uncrackable2']]->?[*]?Hooking?calls?to?System.exit [*]?Intercepting?strncmp [*]?System.exit?called [*]?Secret?string?at?0x7fffa628f010:?Thanks?for?all?the?fish沒錯(cuò),輸出內(nèi)容很好很簡單:完整的秘密字符串。將其輸入到輸入框中并享受成功吧
修復(fù)補(bǔ)丁的解決方案
最后,是有一些關(guān)于補(bǔ)丁修復(fù)的說明,以及為什么我們在使用打過補(bǔ)丁的apk時(shí)不會得到秘密字符串。Libfoo中的init函數(shù)包含一些初始化邏輯,阻止應(yīng)用程序根據(jù)我們的輸入檢查或解碼秘密字符串。
如果我們再次看看init函數(shù)的反匯編,我們會看到有趣的一行代碼:
0x00001056?c605af2f0000.?mov?byte?[0x0000400c],?1在bar函數(shù)的后面會檢查相同的變量libfoo,如果未設(shè)置,則代碼會跳過strncmp:
0x0000107d??????803d882f0000.??cmp?byte?[0x0000400c],?1????;?[0x1:1]=69???????????????????????????????????????????????????????????????????????????????????????????????????????????????? 0x00001084??????7570???????????jne?0x10f6??????????????????;[1]所以在后面它可能是一個(gè)布爾變量,如果init函數(shù)運(yùn)行,它將被設(shè)置。如果我們想要在我們的apk的修補(bǔ)版本中調(diào)用strncmp那么我們必須設(shè)置此變量,或者至少阻止它跳過實(shí)際的strncmp調(diào)用。
我們現(xiàn)在可以再次進(jìn)行修補(bǔ),反編譯apk,覆蓋jmp指令,并重新編譯一次。的確有些餓笨重。?由于這是Frida教程,我們將使用Frida來動態(tài)更改內(nèi)存。
因此,我們需要:
1.??獲取加載foo庫的基址
2.??找到相對于庫基地址的變量(通過反匯編中,我們知道從基地址的偏移量是0x400C字節(jié))
3.??將變量設(shè)置為1
所以,在Frida中的相關(guān)代碼如下:
//Get?base?address?of?library var?libfoo?=?Module.findBaseAddress("libfoo.so"); //Calculate?address?of?variable var?initialized?=?libfoo.add(ptr("0x400C")); //Write?1?to?the?variable Memory.writeInt(initialized,1);以下是該修補(bǔ)程序版本的完整腳本:
setImmediate(function()?{Java.perform(function()?{console.log("[*]?Hooking?calls?to?System.exit");exitClass?=?Java.use("java.lang.System");exitClass.exit.implementation?=?function()?{console.log("[*]?System.exit?called");}var?strncmp?=?undefined;imports?=?Module.enumerateImportsSync("libfoo.so");for(i?=?0;?i?<?imports.length;?i++)?{if(imports[i].name?==?"strncmp")?{strncmp?=?imports[i].address;break;}}//Get?base?address?of?libraryvar?libfoo?=?Module.findBaseAddress("libfoo.so");//Calculate?address?of?variablevar?initialized?=?libfoo.add(ptr("0x400C"));//Write?1?to?the?variableMemory.writeInt(initialized,1);Interceptor.attach(strncmp,?{onEnter:?function?(args)?{if(args[2].toInt32()?==?23?&&?Memory.readUtf8String(args[0],23)?==?"01234567890123456789012")?{console.log("[*]?Secret?string?at?"?+?args[1]?+?":?"?+?Memory.readUtf8String(args[1],23));}},});console.log("[*]?Intercepting?strncmp");}); });現(xiàn)在運(yùn)行應(yīng)用程序,通過frida加載腳本,再次輸入01234567890123456789012。按驗(yàn)證按鈕。應(yīng)用程序會調(diào)用strncmp并且會記錄秘密字符串:
root@sixtyseven:/home/michael/Development/frida#?frida?-U?sg.vantagepoint.uncrackable2?-l?uncrackable2-final.js____/?_??|???Frida?9.1.27?-?A?world-class?dynamic?instrumentation?framework|?(_|?|>?_??|???Commands:/_/?|_|???????help??????->?Displays?the?help?system.?.?.?.???????object????->?Display?information?about?'object'.?.?.?.???????exit/quit?->?Exit.?.?.?..?.?.?.???More?info?at?http://www.frida.re/docs/home/ [USB::Android?Emulator?5554::sg.vantagepoint.uncrackable2]->?[*]?Hooking?calls?to?System.exit [*]?Intercepting?strncmp [*]?System.exit?called [*]?Secret?string?at?0x7fffd52c6570:?Thanks?for?all?the?fish希望你在使用Frida后能體驗(yàn)到更多的樂趣。
原文發(fā)布時(shí)間為:2017年5月11日 本文作者:李白 本文來自云棲社區(qū)合作伙伴嘶吼,了解相關(guān)信息可以關(guān)注嘶吼網(wǎng)站。 原文鏈接
總結(jié)
以上是生活随笔為你收集整理的Android APP破解利器Frida之反调试对抗的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你的Java代码是否符合规范,来测测便知
- 下一篇: nginx lua 小项目:根据 use