版權聲明:本文爲博主原創文章,未經博主容許不得轉載。javascript
今天咱們開始apk破解的另一種方式:動態代碼調試破解,以前其實已經在一篇文章中說到如何破解apk了:android
Android中使用靜態方式破解Apk 主要採用的是靜態方式,步驟也很簡單,首先使用apktool來反編譯apk,獲得smail源碼,而後分析smail代碼,採用代碼注入技術來跟蹤代碼,而後找到關鍵方法進行修改,進而破解,同時還可使用一些開源的hook框架,好比:Xposed和Cydia Substrate,來進行關鍵方法的hook。因此這裏咱們能夠看到咱們破解的第一步是使用apktool來進行成功的反編譯,而後是須要了解smali語法,不過關於smali語法其實很簡單,網上有不少教程。算法
那麼今天咱們就用另一種方式來破解apk:動態方式,關於動態方式其實很廣義的,由於動態方式相對於靜態方式來講,難度大一點,可是他比靜態方式高效點,可以針對更過的破解範圍。固然動態方式不少,因此這裏就分爲三篇文章來說解這塊:ubuntu
一、動態方式破解apk前奏篇(Eclipse動態調試smail源碼)api
二、動態方式破解apk升級篇(IDA動態調試so源碼)數組
三、動態方式破解apk終極篇(應對加固的apk破解方法)安全
從這三篇文章可以讓咱們破解通常的apk沒有任何問題,不過不能表明可以破解全部的apk,由於沒有絕對的安全,也是沒有絕對的破解,兩方都在進步,咱們只能具體問題具體分析。好了,下面咱們就來看第一篇文章,也是今天的重點:Eclipse動態調試smali源碼服務器
首先須要解釋一下,這裏爲何說是調試smali源碼,不是Java源碼,由於咱們弄過反編譯的人知道,使用apktool反編譯apk以後,會有一個smali文件夾,這裏就存放了apk對應的smali源碼,關於smali源碼這裏不解釋了,網上有介紹。微信
由於這一篇是一個教程篇,因此不能光說,那樣會很枯燥的,因此這裏用一個例子來介紹一下:
咱們就用阿里2014年安全挑戰賽的第一題:AliCrack_one.apk
看到這張圖了,阿里還挺會製造氛圍的,那麼其實很簡單,咱們輸入密碼就能夠破解了,下面咱們就來看看如何獲取這個密碼。
java -jar apktool_2.0.0rc4.jar d -d AliCraceme_1.apk -o out
這裏的命令不作解釋了,可是有一個參數必須帶上,那就是:-d
由於這個參數表明咱們反編譯獲得的smali是java文件,這裏說的文件是後綴名是java,若是不帶這個參數的話,後綴名是smali的,可是Eclipse中是不會識別smali的,而是識別java文件的,因此這裏必定要記得加上這個參數。
反編譯成功以後,咱們獲得了一個out目錄,以下:
源碼都放在smali文件夾中,咱們進入查看一下文件:
看到了,這裏全是Java文件的,其實只是後綴名爲java了,內容仍是smali的:
上面咱們反編譯成功了,下面咱們爲了後續的調試工做,因此仍是須要作兩件事:
1》修改AndroidManifest.xml中的Android:debuggable="true"
關於這個屬性,咱們前面介紹run-as命令的時候,也提到了,他標識這個應用是不是debug版本,這個將會影響到這個應用是否能夠被調試,因此這裏必須設置成true。
2》在入口處添加waitForDebugger代碼進行調試等待。
這裏說的入口處,就是程序啓動的地方,就是咱們通常的入口Activity,查找這個Activity的話,方法太多了,好比咱們這裏直接從上面獲得的AndroidManifest.xml中找到,由於入口Activity的action和category是固定的。
固然還有其餘方式,好比aapt查看apk的內容方式,或者是安裝apk以後用adb dumpsys activity top命令查看都是能夠的。
找到入口Activity以後,咱們直接在他的onCreate方法的第一行加上waitForDebugger代碼便可,找到對應的MainActivity的smali源碼:
而後添加一行代碼:
invoke-static {}, Landroid/os/Debug;->waitForDebugger()V
這個是smali語法的,其實對應的Java代碼就是:android.os.Debug.waitForDebugger();
這裏把Java語言翻譯成smali語法的,不難,網上有smali的語法解析,這裏不想再解釋了。
java -jar apktool_2.0.0rc4.jar b -d out -o debug.apk
仍是使用apktool進行回編譯
編譯完成以後,將獲得debug.apk文件,可是這個apk是沒有簽名的,因此是不能安裝的,那麼下面咱們須要在進行簽名,這裏咱們使用Android中的測試程序的簽名文件和sign.jar工具進行簽名:
關於簽名的相關知識,能夠看這篇文章:Android中的簽名機制詳解
java -jar .\sign\signapk.jar .\sign\testkey.x509.pem .\sign\testkey.pk8 debug.apk debug.sig.apk
簽名以後,咱們就能夠進行安裝了。
這裏咱們新建一個Java工程,記住不是Android工程,由於咱們最後調試實際上是藉助於Java的調試器,而後勾選掉Use default location選項,選擇咱們的smali源碼目錄,也就是咱們上面反編譯以後的out目錄,點擊完成
咱們導入源碼以後的項目工程結構:
主要看MainActivity類:
這一步咱們看到,其實說的比較廣義了,這個要具體問題具體分析了,好比這個例子中,咱們知道當咱們輸入密碼以後,確定要點擊按鈕,而後觸發密碼的校驗過程,那麼這裏咱們知道找到這個button的定義的地方,而後進入他的點擊事件中就能夠了。這裏分爲三步走:
1》使用Eclipse自帶的View分析工具找到Button的ResId
點擊以後,須要等待一會,分析View以後的結果:
看到了,這裏咱們可以看到整個當前的頁面的所有佈局,已經每一個控件的屬性值,咱們須要找到button的resource-id
這裏咱們看到定義是@+id/button這個值。
2》咱們獲得這個resId以後,可否在smali工程中全局搜索這個值,就能夠定位到這個button的定義的地方呢?
而後咱們看看搜到的結果:
這時候咱們實際上是在資源文件中搜到了這個id的定義,這個id值對應的是0x7F05003E。
固然除了這種方式,咱們還有一種方式能快速找到這個id對應的整型值,那就是在反編譯以後的values/public.xml文件中:
這個文件頗有用的,他是真個apk中全部資源文件定義的映射內容,好比drawable/string/anim/attr/id 等這些資源文件定義的值,名字和整型值對應的地方:
這個文件很重要,是咱們在尋找突破口的重要關鍵,好比咱們有時候須要經過字符串內容來定位到關鍵點,這裏就能夠經過string的定義來找到對應的整型值便可。
當咱們找到了button對應的id值了以後,咱們就能夠用這個id值在一次全局搜索一下,由於咱們知道,Android中編譯以後的apk,在代碼中用到的resId都是用一個整型值代替的,這個整型值就是在R文件中作了定義,將資源的id和一個值對應起來,而後代碼裏面通常使用R.id.button這樣的值,在編譯出apk的時候,這個值就會被替換成對應的整型值,因此在全局搜索0x7F05003E
搜索的結果以下:
看到了,這裏就定位到了代碼中用到的這個button,咱們進入代碼看看:
在這裏,看到了,使用了findViewById的方式定義Button,咱們在往下面簡單分析一下smali語法,下面是給button添加一個按鈕事件,這裏用的是內部類MainActivity$1,咱們到這個類看看,他確定實現了OnClickListener接口,那麼直接搜onClick方法:
在這裏咱們就能夠下個斷點了,這裏就是觸發密碼校驗過程。
在第五步中,咱們找到了關鍵點,而後打上斷點,下面咱們就來運行程序,而後在Eclipse中設置遠程調試的工程
首先咱們運行程序,由於咱們加入了waitForDebug的代碼,因此啓動的時候會出現一個Wait debug的對話框。不過,我測試的時候,個人手機沒有出現這個對話框,而是一個白屏,不過這個不影響,程序運行起來以後,咱們看看如何在Eclipse中設置遠程調試工程,首先咱們找到須要調試的程序對應遠程調試服務端對應的端口:
這裏咱們看到有幾個點:
1》在程序等待遠程調試服務器的時候,前面會出現一個紅色的小蜘蛛
2》在調試服務端這裏咱們會看到兩個端口號:8600/8700,這裏須要解釋一下,爲何會有兩個端口號呢?
首先在這裏的端口號,表明的是,遠程調試服務器端的端口,下面在簡單來看一下,Java中的調試系統:
這裏咱們看到,這裏有三個角色:
111》JDB Client端(被調試的客戶端),這裏咱們能夠認爲咱們須要破解的程序就是客戶端,若是一個程序能夠被調試,當啓動的時候,會有一個jdwp線程用來和遠程調試服務端進行通訊
這裏咱們看到,咱們須要破解的程序啓動了JDWP線程,注意這個線程也只有當程序是debug模式下才有的,也就是AndroidManifest.xml中的debug屬性值必須是true的時候,也就是一開始爲何咱們要修改這個值的緣由。
222》JDWP協議(用於傳輸調試信息的,好比調試的行號,當前的局部變量的信息等),這個就能夠說明,爲何咱們在一開始的時候,反編譯成java文件,由於爲了Eclipse導入可以識別的Java文件,而後爲何可以調試呢?由於smali文件中有代碼的行號和局部變量等信息,因此能夠進行調試的。
333》JDB Server端(遠程調試的服務端,通常是有JVM端),就是開啓一個JVM程序來監聽調試端,這裏就能夠認爲是本地的PC機,固然這裏必須有端口用來監聽,那麼上面的8600端口就是這個做用,並且這裏端口是從8600開始,後續的程序端口後都是依次加1的,好比其餘調試程序:
那麼有了8600端口,爲何還有一個8700端口呢?他是幹什麼的?
其實他的做用就是遠程調試端備用的基本端口,也就是說好比這裏的破解程序,咱們用8600端口能夠鏈接調試,8700也是能夠的,可是其餘程序,好比demo.systemapi他的8607端口能夠鏈接調試,8700也是能夠的:
因此呀,能夠把8700端口想象成你們均可以用於鏈接調試的一個端口,不過,在實際過程當中,仍是建議使用程序獨有的端口號8600,咱們能夠查看8600和8700端口在遠程調試端(本地pc機)的佔用狀況:
看到了,這裏的8600端口和8700端口號都是對應的javaw程序,其實javaw程序就是啓動一個JVM來進行監聽的。
好了,到這裏咱們就弄清楚了,Java中的調試系統以及遠程調試的端口號。
注意:
其實咱們可使用adb jdwp命令查看,當前設備中能夠被調試的程序的進程號信息:
下面繼續,咱們知道了遠程調試服務端的端口:8600,以及ip地址,這裏就是本地ip:localhost/127.0.0.1
咱們能夠在Eclipse中新建一個遠程調試項目,將咱們的smali源碼工程和設備中須要調試的程序關聯起來:
右擊被調試的項目=》選擇Debug Configurations:
而後開始設置調試項目
選擇Romote Java Application,在Project中選擇被調試的smali項目,在Connection Type中選擇SocketAttach方式,其實還有一種方式是Listener的,關於這兩種方式其實很好理解:
Listner方式:是調試客戶端啓動就準備好一個端口,當調試服務端準備好了,就鏈接這個端口進行調試
Attach方式:是調試服務端開始就啓動一個端口,等待調試端來鏈接這個端口
咱們通常都是選擇Attach方式來進行操做的。
好了,咱們設置完遠程調試的工程以後,開始運行,擦發現,設備上的程序仍是白屏,這是爲何呢?看看DDMS中調試程序的狀態:
擦,關聯到了這個進程,緣由也很簡單,咱們是上面使用的是8700端口號,這時候咱們選中了這個進程,因此就把smali調試工程關聯到了這個進程,因此破解的進程沒反應了,咱們立馬改一下,用8600端口:
好了,這下成功了,咱們看到紅色的小蜘蛛變成綠色的了,說明調試端已經鏈接上遠程調試服務端了。
注意:
咱們在設置遠程調試項目的時候,必定要注意端口號的設置,否則沒有將調試項目源碼和調試程序關聯起來,是沒有任何效果的
下面咱們就開始操做了,在程序的文本框中輸入:gggg內容,點擊開始:
好了,到這裏咱們看到期待已久的調試界面出來了,到了咱們開始的時候加的斷點處,這時候咱們就能夠開始調試了,使用F6單步調試,F5單步跳入,F7單步跳出進行操做:
看到了,這裏使用v3變量保存了咱們輸入的內容
這裏有一個關鍵的地方,就是調用MainActivity的getTableFromPic方法,獲取一個String字符串,從變量的值來看,貌似不是規則的字符串內容,這裏先不用管了,繼續往下走:
這裏又遇到一個重要的方法:getPwdFromPic,從字面意義上看,應該是獲取正確的密碼,用於後面的密碼字符串比對。
查看一下密碼的內容,貌似也是一個不規則的字符串,可是咱們能夠看到和上面獲取的table字符串內容格式很像,接着往下走:
這裏還有一個信息就是,調用了系統的Log打印,log的tag就是v6保存的值:lil
這時候,咱們看到v3是保存的咱們輸入的密碼內容,這裏使用utf-8獲取他的字節數組,而後傳遞給access$0方法,咱們使用F5進入這個方法:
在這個方法中,還有一個bytesToAliSmsCode方法,使用F5進入:
那麼這個方法其實看上去仍是很簡單的,就是把傳遞進來的字節數組,循環遍歷,取出字節值,而後轉化成int類型,而後在調用上面獲取到的table字符串的chatAt來獲取指定的字符,使用StringBuilder進行拼接,而後返回便可。
按F7跳出,查看,咱們返回來加密的內容是:日日日日,也就是說gggg=>日日日日
最後再往下走,能夠看到是進行代碼比對的工做了。
那麼上面咱們就分析完了全部的代碼邏輯,還不算複雜,咱們來梳理一下流程:
A>調用MainActivity中的getTableFromPic方法,獲取一個table字符串
咱們能夠進入看看這個方法的實現:
這裏能夠大致瞭解了,他是讀取asset目錄下的一個logo.png圖片,而後獲取圖片的字節碼,在進行操做,獲得一個字符串,那麼咱們從上面的分析能夠知道,其實這裏的table字符串相似於一個密鑰庫。
B>經過MainActivity中的getPwdFromPic方法,獲取正確的密碼內容
C>獲取咱們輸入內容的utf-8的字節碼,而後調用access$0方法,獲取加密以後的內容
D>access$0方法中在調用bytesToAliSmsCode方法,獲取加密以後的內容
這個方法是最核心的,咱們經過分析知道,他的邏輯是,經過傳遞進來的字節數組,循環遍歷數組,拿到字節轉化成int類型,而後在調用密鑰庫字符串table的charAt獲得字符,使用StringBuilder進行拼接。
經過上面的分析以後,咱們知道獲取加密以後的輸入內容和正確的密碼內容作比較,那麼咱們如今有的資源是:
密鑰庫字符串和正確的加密以後的密碼,以及加密的邏輯
那麼咱們的破解思路其實很簡單了,至關於,咱們知道了密鑰庫字符串,也知道了,加密以後的字符組成的字符串,那麼能夠經過遍歷加密以後的字符串,循環遍歷,獲取字符,而後再去密鑰庫找到指定的index,而後在轉成byte,保存到字節數組,而後用utf-8獲取一個字符串,那麼這個字符串就是咱們要的密碼。
下面咱們就用代碼來實現這個功能:
代碼邏輯,很簡單吧,其實這個函數至關於上面加密函數的bytesToAliSmsCode的反向實現,運行結果:
OK,獲得了正確的密碼,下面來驗證一下:
哈哈,不要太激動,成功啦啦~~。破解成功。
補充:
剛剛咱們在斷點調試的時候,看到了代碼中用了Log來打印日誌,tag是lil,那麼咱們能夠打印這個log看看結果:
看到了,這裏table是密鑰庫,pw是正確的加密以後的密碼,enPassword是咱們輸入以後加密的密碼。
因此從這裏能夠看到,這個例子,其實咱們在破解apk的時候,有時候日誌也是一個很是重要的信息。
破解須要的資料,我已經上傳了,下載地址:http://download.csdn.net/detail/jiangwei0910410003/9526113
一、咱們經過apktool工具進行apk的反編譯,獲得smali源碼和AndroidManifest.xml,而後修改AndroidManifest.xml中的debug屬性爲true,同時在入口處加上waitForDebug代碼,進行debug等待,通常入口都是先找到入口Activity,而後在onCreate方法中的第一行這裏須要注意的是,apktool工具必定要加上-d參數,這樣反編譯獲得的文件是java文件,這樣纔可以被Eclipse識別,進行調試。
二、修改完成AndroidManifest.xml和添加waitForDebug以後,咱們須要在使用apktool進行回編譯,回編譯以後獲得的是一個沒有簽名的apk,咱們還須要使用signapk.jar來進行簽名,簽名文件直接使用測試程序的簽名文件就能夠,最後在進行安裝。
三、而後咱們將反編譯以後的smali源碼導入到Eclipse工程中,找到關鍵點,進行下斷點,這裏的關鍵點,通常是咱們先大體瞭解程序運行的結構,而後找到咱們須要破解的地方,使用View分析工具,或者是使用jd-gui工具直接查看apk源碼(使用dex2jar將dex文件轉化成jar文件,而後用jd-gui進行查看),找到代碼的大致位置。而後下斷點,這裏咱們能夠藉助Eclipse的DDMS自帶的View分析工具找到對應控件的resid,而後在全局搜索這個控件的resid,或者直接在values/public.xml中查找,最終定位到這個控件位置,在查看他的點擊事件便可。
四、設置遠程調試工程,首先運行須要調試程序,而後在DDMS中找到對應的調試服務端的端口號,而後在Debug Configurations中設置遠程調試項目,設置對應的調試端口和ip地址(通常都是本機pc,那就是localhost),而後紅色小蜘蛛變成綠色的,表示咱們的遠程調試項目鏈接關聯上了調試程序,這裏須要注意的是,必定須要關聯正確,否則是沒有任何效果的,關聯成功以後,就能夠進行操做。
五、操做的過程當中,會進入到關鍵的斷點處,經過F6單步,F5單步進入,F7單步跳出,來進行調試,找到關鍵方法,而後經過分析smali語法,瞭解邏輯,若是邏輯複雜的,能夠經過查看具體的環境變量的值來觀察,這裏也是最重要的,也是最複雜的,同時這裏也是沒有規章可尋的,這個和每一個人的邏輯思惟以及破解能力有關係,分析關鍵的加密方法是須要功底的,固然這裏還須要注意一個信息,就是Log日誌,有時候也是很重要的一個信息。
六、最後通常當咱們知道了核心方法的邏輯,要想獲得正確的密碼,仍是須要本身用語言去實現邏輯的,好比本文中的加密方法,咱們須要手動的code一下加密的逆向方法,才能獲得正確的密碼。
一、使用apktool工具進行反編譯有時候並非那麼順利,好比像這樣的報錯:
這個通常都是apktool中解析出現了錯誤,其實這個都是如今apk爲了抵抗apktool,作的apk加固策略,這個後面會寫一篇文章如何應對這些加固策略,如何進行apk修復,其實原理就是分析apktool源碼,找到指定的報錯位置,進行apktool代碼修復便可。
二、本文中說到了Java的調試系統,可是爲了篇幅限制,沒有詳細的講解了整個內容,後面會寫一篇文章具體介紹Java中的調試系統以及Android的調試系統。
三、有時候咱們還會遇到回編譯成功了,而後遇到運行不起來的錯誤,這個就須要使用靜態方式先去分析程序啓動的邏輯,看看是否是程序作了什麼運行限制,好比咱們在靜態分析那篇文章中,提到了應用爲了防止反編譯在回編譯運行,在程序的入口處做了簽名校驗,若是校驗失敗,直接kill掉本身的進程,退出程序了,因此這時候咱們仍是須要使用靜態方式去分析apk。
四、如何作到不修改AndroidManifest.xml中的debug屬性就能夠進行調試:
1》 修改boot.img,從而打開系統調試,這樣就能夠省去給app添加android:debuggable="true",再重打包的步驟了。
2.》直接修改系統屬性,使用setpropex工具在已經root的設備上修改只讀的系統屬性。使用此工具來修改ro.secure和ro.debuggable的值。
這個也會在後面詳細介紹這兩種方法
這篇文章咱們就介紹瞭如何使用Eclipse去動態調試反編譯以後的smali源碼,這種方式比靜態方式高效不少的,好比本文中的這個例子,其實咱們也可使用靜態方式進行破解的,可是確定效率沒有動態方式高效,因此之後咱們又學會了一個技能,就是動態的調試smali源碼來跟蹤程序的核心點,可是如今市場上的大部分應用沒有這麼簡單就破解了,好比核心的加密算法放到了native層去作,那麼這時候就須要咱們去動態調試so文件跟蹤,這個是咱們下一篇文章的內容,也有的時候,apk進行加固了,直接在apktool進行反編譯就失敗了,這時候咱們就須要先進行apk修復,而後才能後續的操做,這個是咱們下下篇的文章,如何應對apk的加固策略。經過這篇文章咱們能夠看到動態方式破解比靜態方式高效的多,可是有時候咱們還須要使用靜態方式先作一些準備工做,因此在破解apk的時候,動靜結合,才能作到完美的破解。
更多內容:點擊這裏
關注微信公衆號,最新Android技術實時推送