寫在開頭,引用很喜歡的一句話:要么學!要么不學!學和不學之間沒有中間值 不學就放棄,學就要去認真的學! --致選擇
1、 反編譯簡介
為了回溯編譯過程(或對程序進行逆向工程),我們使用各種工具來撤銷匯編和編譯過程,這些工具就叫反匯編器和反編譯器。反匯編器撤銷匯編過程,因此我們可以得到匯編語言形式的輸出結果。反編譯器則以匯編語言甚至是機器語言為輸入,其輸出結果為高級語言。
2、 Smali語法教程
2.1 原始類型
- B---byte
- C---char
- D---double
- F---float
- I---int
- J---long
- S---short
- V---void
- Z---boolean
- [XXX---array
- Lxxx/yyy---object
數組的表示方式是:在基本類型前加上前中括號“[”,例如int數組和float數組分別表示為:[I、[F;對象的表示則以L作為開頭,格式是LpackageName/objectName;
(注意必須有個分號跟在最后),例如String對象在smali中為:Ljava/lang/String;,其中java/lang 對應 java.lang包,String就是定義在該包中的一個對象。或許有人問,既然類是用LpackageName/objectName;來表示,那類里面的內部類又如何在smali中引用呢?
答案是:在LpackageName/objectName/subObjectName的subObjectName前加 $ 符號。
2.2 方法
方法的定義一般為:Func-Name (Para-Type1Para-Type2Para-Type3...)Return-Type
注意參數與參數之間沒有任何分隔符,同樣舉幾個例子就容易明白
-
hello ()V。沒錯,這就是void hello() -
hello (III)Z。這個則是boolean hello(int, int, int) -
hello (Z[I[ILjava/lang/String;J)Ljava/lang/String;
看出來這是String hello (boolean, int[], int[], String, long)了嗎?
2.3 Smali基本語法
無序列表的使用,在符號"-"后加空格使用。如下:
- field private isFlag:z 定義變量
- method 方法
- parameter 方法參數
- prologue 方法開始
- line 123 此方法位于第123行
- invoke-super 調用父函數
- const/high16 v0, 0x7fo3 把0x7fo3賦值給v0
- invoke-direct 調用函數
- return-void 函數返回void
- end method 函數結束
- new-instance 創(chuàng)建實例
- iput-object 對象賦值
- iget-object 調用對象
- invoke-static 調用靜態(tài)函數
2.4 條件跳轉分支
- "if-eq vA, vB, :cond_*" 如果vA等于vB則跳轉到:cond_*
- "if-ne vA, vB, :cond_*" 如果vA不等于vB則跳轉到:cond_*
- "if-lt vA, vB, :cond_*" 如果vA小于vB則跳轉到:cond_*
- "if-ge vA, vB, :cond_*" 如果vA大于等于vB則跳轉到:cond_*
- "if-gt vA, vB, :cond_*" 如果vA大于vB則跳轉到:cond_*
- "if-le vA, vB, :cond_*" 如果vA小于等于vB則跳轉到:cond_*
- "if-eqz vA, :cond_*" 如果vA等于0則跳轉到:cond_*
- "if-nez vA, :cond_*" 如果vA不等于0則跳轉到:cond_*
- "if-ltz vA, :cond_*" 如果vA小于0則跳轉到:cond_*
- "if-gez vA, :cond_*" 如果vA大于等于0則跳轉到:cond_*
- "if-gtz vA, :cond_*" 如果vA大于0則跳轉到:cond_*
- "if-lez vA, :cond_*" 如果vA小于等于0則跳轉到:cond_*
3、 Android Killer
3.1 反編譯失敗
http://www.itdecent.cn/p/1c54c1ccf5cc
https://www.cnblogs.com/onelikeone/p/7594177.html

3.2 .smali文件已丟失

解決:點擊進去jd-gui,刪除試一試。再不行換最新版本

3.3 Android Killer反編譯失敗:No resource identifier found for attribute 問題解決方法

解析結束后進行編譯報錯
解決方法:https://blog.csdn.net/fuchaosz/article/details/104800802

3.4 解決反編譯安裝失敗報Error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED的處理方式
Failed parse during installPackageLI: Targeting R+ (version 30 and above) requires the resources.arsc of installed APKs to be stored uncompress

解決方法:
- 使用安卓10及一下手機
-
手動降低該程序版本
降低gradle里版本,若出現
signatures do not match the previously installed version;,
使用adb install命令在手機上安裝app時,遇到這個報錯。原因是新裝的app和手機上現有的舊版app沖突了。
解決方法:刪除手機上原來的app,再重新安裝即可。
可是轉念一想如果反編譯的apk都是Version 30 R+以上,難道我解壓后挨個改一遍gradle?太徹淡了,一定有解決方法,所以有了下面探究出現這個問題的解決方法:既然報錯是資源文件高版本不支持,而且沒有4位對齊,那么不編譯資源文件就好了
- 不編譯資源文件,使用V2簽名(還是不能編譯資源文件,即使用了V2簽名s,在高版本)
4、 二次打包
APK簽名工具之jarsigner和apksigner:
利用apktool反編譯apk,并且重新簽名打包:
- 進入AndroidKiller_v1.3.1\bin\apktool目錄

驗證apktool能否使用

apktool -r d apk名字.apk,不反編譯資源文件,為什么這么做,先挖個坑

錯誤提示沒有4位對齊和不支持30版本以上的資源文件。所有嘗試不編譯資源文件
解決4位對齊的方法:


-
使用apktool進行反編譯(Android Killer有該工具)
- 解壓apk
- apktool d src.apk //解壓apk到當前文件夾
- apktool d src.apk -o temp //到temp文件夾
- apktool -r d apk名字.apk //不反編譯資源文件,到當前文件夾
- 直接用zip打開手動解壓

執(zhí)行反編譯命令,apktool d filename,這里我執(zhí)行的是apktool d app-debug.apk
其中d是decode的意思,表示要對這個apk進行解碼,除了這個基本用法,還有其他的附帶參數:
- -f 如果目標文件夾已存在,則強制刪除現有文件夾(默認如果目標文件夾已存在,則解碼失敗)。
- -o 指定解碼目標文件夾的名稱(默認使用APK文件的名字來命名目標文件夾)。
- -s 不反編譯dex文件,也就是說classes.dex文件會被保留(默認會將dex文件解碼成smali文件)。
- -r 不反編譯資源文件,也就是說resources.arsc文件會被保留(默認會將resources.arsc解碼成具體的資源文件)
2. 修改需要的內容,用AndroidKiller修改smali文件
3. 二次打包回apk,使用apktool進行編譯成apk安裝包
- apktool b src //src為解壓的文件夾名稱

查看src目錄,里面多了一個dist目錄,目錄里面有個 xxx.apk
- 簽名
使用keytool產生密鑰,生成abc.keystore
- keytool -genkey -alias abc.keystore -keyalg RSA -validity 20000 -keystore abc.keystore
//keytool -genkeypair -keystore 密鑰庫名 -alias 密鑰別名 -validity 天數 -keyalg RSA
查看當前目錄,生成了新文件:abc.keystor
使用JarSigner對apk進行簽名,命令如下
jarsigner -verbose -keystore abc.keystore -signedjar testx.apk src.apk abc.keystore

直接反編譯的apk產生上述錯誤

但是只編譯資源文件的apk安裝時

發(fā)現沒有使用V2進行簽名,這時候進行V2簽名,(apksigner,默認同時使用V1和V2簽名)
所以先對只編譯資源文件的apk進行V2嘗試看能否成功
重復1(進行apktool -r d apk名字.apk)-->2 -->3 -->4(不使用jarsigner而使用apksigner)
將生成的abc.keystore和打包回的apk(apktool\app-debug\dist里的app-debug.apk)放入C:\Users\taowei.lian\AppData\Local\Android\Sdk\build-tools\30.0.3下,因為Android studio的SDK下有apksigner.bat.

對jarsigner只是apk進行了V1簽名;在Android7.0引入了V2簽名,因此,當進入sdk\25.0.0及后續(xù)版本,會發(fā)現一個apksigner.bat執(zhí)行腳本。
我們可以通過apksigner進行V2簽名,當然,apksigner默認是同時支持V1與V2的,于是:
abc.keystore密鑰庫只有一個密鑰對
- apksigner sign --ks debug.keystore app-debug.apk
在debug.keystore密鑰庫中有多個密鑰對,所以必須指定密鑰別名
- apksigner sign --ks debug.keystore --ks-key-alias androiddebugkey app-debug.apk
5、 簽名驗證
5.1 簽名機制概述
學習了公鑰和密鑰的使用和區(qū)別,使用私鑰的加密算法稱為對稱加密算法,這種算法實現是接收方和發(fā)送方公用一道密鑰,優(yōu)點是效率高,缺點是安全性差,如果被第三人得知密鑰則信息泄露,由此衍生了公鑰加密算法,也就是非對稱加密算法,這個算法是接收方給發(fā)送方公鑰,發(fā)送方用公鑰加密后發(fā)給接收方,接受方再用私鑰解密。這樣即使所有人知道公鑰也不會造成信息泄露。缺點是效率非常低。
此外了解了RSA簽名的大致過程,發(fā)送方擁有公鑰和私鑰,對信息進行摘要然后把摘要通過密鑰進行簽名,然后把簽名和信息一起發(fā)出去,那么如何驗證該信息就是發(fā)送方發(fā)出的呢,這時候就使用到了公鑰驗證,通過公鑰對信息進行解簽,然后使用一樣的摘要算法得到摘要,如果得到的摘要和解簽后的內容一致則說明是發(fā)送方發(fā)出。
總結就是公鑰加密,私鑰解密。公鑰驗證,私鑰簽名
RSA 密碼體制是一種公鑰密碼體制,公鑰公開,私鑰保密,它的加密解密算法是公開的。由公鑰加密的內容可以并且只能由私鑰進行解密,而由私鑰加密的內容可以并且只能由公鑰進行解密。也就是說,RSA 的這一對公鑰、私鑰都可以用來加密和解密,并且一方加密的內容可以由并且只能由對方進行解密。
- 加密:公鑰加密,私鑰解密的過程,稱為「加密」。
因為公鑰是公開的,任何公鑰持有者都可以將想要發(fā)送給私鑰持有者的信息進行加密后發(fā)送,而這個信息只有私鑰持有者才能解密。
- 簽名:私鑰加密,公鑰解密的過程,稱為「簽名」。
它和加密有什么區(qū)別呢?因為公鑰是公開的,所以任何持有公鑰的人都能解密私鑰加密過的密文,所以這個過程并不能保證消息的安全性,但是它卻能保證消息來源的準確性和不可否認性,也就是說,如果使用公鑰能正常解密某一個密文,那么就能證明這段密文一定是由私鑰持有者發(fā)布的,而不是其他第三方發(fā)布的,并且私鑰持有者不能否認他曾經發(fā)布過該消息。故此將該過程稱為「簽名」。
5.2 方法一(keytool,只支持V1簽名校驗)
進入JDK/bin, 輸入命令
keytool -printcert -jarfile MyApp.apk (顯示簽名證書信息)
參數:
- -printcert 打印證書內容
- -jarfile 已簽名的jar文件 或apk文件
5.3 方法二(apksigner,支持V1和V2簽名校驗)
進入Android SDK/build-tools/SDK版本, 輸入命令
apksigner verify -v --print-certs xxx.apk
參數:
- -v, --verbose 顯示詳情(顯示是否使用V1和V2簽名)
- –print-certs 顯示簽名證書信息
例如:
apksigner verify -v MyApp.apk
最后安裝加 -t :
adb install -t app-debug-signed.apk

附上參考鏈接:
- 利用apktool反編譯apk,并且重新簽名打包
- Android中APK簽名工具之jarsigner和apksigner詳解
- Android apk反編譯、修改代碼、重新打包、簽名全過程(for windows)
6、 用AS編寫第一個so
https://blog.csdn.net/A807296772/article/details/102298970
配置NDK的時候如果按鈕是灰色的,手動配置
sdk.dir=C\:\\Users\\taowei.lian\\AppData\\Local\\Android\\Sdk
ndk.dir=C\:\\Users\\taowei.lian\\AppData\\Local\\Android\\Sdk\\ndk\\21.3.6528147
問題一、javac編譯 解決警告:編碼 GBK 的不可映射字符

直接在javac后面指定編碼是UTF-8就是了。
javac -encoding UTF-8 xxxx.java
問題二、找不到 'com.example.jni.myJNI' 的類文件。

javah -classpath . -jni com.example.pixiaozhi.jni.TestJni
需要注意的是要加上* -classpath .其中classpath后面的一個黑點是不能省略的。
編譯好后如何導入so庫
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}

成功運行后發(fā)現lib目錄下已經apk編進去so了
7、 破解so
https://www.52pojie.cn/thread-732298-1-1.html
本節(jié)所有到的工具和Demo
IDA
鏈接:https://pan.baidu.com/s/15uCX8o6tTSSelgG_RN7kBQ 密碼:ftie
Demo
鏈接:https://pan.baidu.com/s/1vKC1SevvHfeI7f0d2c6IqQ 密碼:u1an
7.1 使用IDA工具,注意:拖進去的so必須是解壓apk的so,不然修改無法生效!
找到so并打開它 因為我的機型是支持arm的所以我這里打開的是armeabi文件夾下的so 如果機型是x86模式的那么這里要打開x86模式下的libJniTest.so
- armeabiv-v7a: 第7代及以上的 ARM 處理器。2011年15月以后的生產的大部分Android設備都使用它.
- arm64-v8a: 第8代、64位ARM處理器,很少設備,三星 Galaxy S6是其中之一。
- armeabi: 第5代、第6代的ARM處理器,早期的手機用的比較多。
- x86: 平板、模擬器用得比較多。
- x86_64: 64位的平板。

編譯過程:

7.2 找到字符串hello 52pojie!并修改為hello world!
按住鍵盤組合鍵 shift + f12 打開字符串窗口 這個窗口將會列舉出so中所包含的所有字符串 因為上節(jié)課我們只編寫了一個字符串 所以這里只有一個hello 52pojie! 如果打開的是x86的so這里還會有一些.so 但是字符串只有這一個

7.3 雙擊進去將會找到hello 52pojie!的內存地址

7.4 修改內存地址
鼠標點在hello 52pojie!字符串上,打開 Hex dump窗口,修改hello 52pojie!對應內存地址的內容
關于字符對應的16進制可以在百度百科搜索ascii碼表 找到字符所對應的16進制
因為我要把hello 52pojie!修改成hello world! 是不是只要找到每個字符所對應的hex修改就好了
這里我看到 hello 52pojie!對應的hex是:68 65 6C 6C 6F 20 35 32 70 6F 6A 69 65 21
我在ascii碼表上找到world所對應的十六進制是:77 6F 72 6C 64
所以hello world! 對應的十六進制是:68 65 6C 6C 6F 20 77 6F 72 6C 64 21


7.5 找到所要修改的字符所對應的16進制 右鍵Edit

注意編輯的時候光標暫停的位置只有先輸入字母才能更改成功,修改好后 右鍵Apply changes應用
7.6 保存so。

退出后保存

此時已經so修改完畢

7.7 對原apk進行解壓,替換so,打包,簽名,安裝流程,運行看看

大功告成,hello 52pojie! --> hello world!
第一篇技術處女文,借鑒了許多大佬的學習資料,填上了學習過程中踩到的所有坑,文章段落結構方面還待改進,望共勵共勵! --后記
