原文出處: 郭霖java
以前一直有猶豫過要不要寫這篇文章,畢竟去反編譯人家的程序並非什麼值得驕傲的事情。不過單純從技術角度上來說,掌握反編譯功能確實是一項很是有用的技能,可能日常不太會用獲得,可是一旦真的須要用到的了,而你卻不會的話,那就很是頭疼了。另外既然別人能夠反編譯程序,咱們固然有理由應該對程序進行必定程度的保護,所以代碼混淆也是咱們必需要掌握的一項技術。那麼最近的兩篇文章咱們就圍繞反編譯和混淆這兩個主題來進行一次徹底解析。linux
咱們都知道,Android程序打完包以後獲得的是一個APK文件,這個文件是能夠直接安裝到任何Android手機上的,咱們反編譯其實也就是對這個APK文件進行反編譯。Android的反編譯主要又分爲兩個部分,一個是對代碼的反編譯,一個是對資源的反編譯,咱們立刻來逐個學習一下。android
在開始學習以前,首先咱們須要準備一個APK文件,爲了尊重全部開發者,我就不拿任何一個市面上的軟件來演示了,而是本身寫一個Demo用來測試。git
這裏我但願代碼越簡單越好,所以咱們創建一個新項目,在Activity里加入一個按鈕,當點擊按鈕時彈出一個Toast,就這麼簡單,代碼以下所示:github
Java編程
1緩存 2架構 3app 4ide 5 6 7 8 9 10 11 12 13 14 15 16 |
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "you clicked button", Toast.LENGTH_SHORT).show(); } }); } } |
activity_main.xml中的資源以下所示:
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button"/> RelativeLayout> |
而後咱們將代碼打成一個APK包,並命名成Demo.apk,再把它安裝到手機上,結果以下所示:
好的,到這裏準備工做就已經基本完成了,接下來就讓咱們開始對這個Demo程序進行反編譯吧。
要想將APK文件中的代碼反編譯出來,咱們須要用到如下兩款工具:
dex2jar 這個工具用於將dex文件轉換成jar文件下載地址:http://sourceforge.net/projects/dex2jar/files/
jd-gui 這個工具用於將jar文件轉換成java代碼下載地址:http://jd.benow.ca/
將這兩個工具都下載好並解壓,而後咱們就開始對Demo程序進行反編譯。解壓dex2jar壓縮包後,你會發現有不少個文件,以下圖所示:
其中咱們要用到的是d2j-dex2jar.bat這個文件,固然若是你是linux或mac系統的話就要用d2j-dex2jar.sh這個文件。
而後咱們將Demo.apk文件也進行解壓,若是不知道怎麼直接解壓的能夠先將文件重命名成Demo.zip,而後用解壓軟件打開。解壓以後你會發現裏面有一個classes.dex文件,以下圖所示:
這個classes.dex文件就是存放全部java代碼的地方了,咱們將它拷貝到dex2jar解壓後的目錄下,並在cmd中也進入到一樣的目錄,而後執行:
Java
1 |
d2j-dex2jar classes.dex |
執行結果以下圖所示:
沒有報任何錯誤,這就說明咱們已經轉換成功了。如今觀察dex2jar目錄,你會發現多了一個文件,以下圖所示:
能夠看到,classes-dex2jar.jar這個文件就是咱們藉助工具以後成功轉換出來的jar文件了。可是對於咱們而言,jar文件也不是可讀的,所以這裏還須要再借助一下jd-gui這個工具來將jar文件轉換成java代碼。
下面就很簡單了,使用jd-gui工具打開classes-dex2jar.jar這個文件,結果以下圖所示:
OK,因而可知,咱們的代碼反編譯工做已經成功了,MainActivity中的代碼很是清晰,基本已經作到了90%以上的還原工做。可是若是想要作到100%的代碼還原仍是很是有難度的,由於像setContentView()方法傳入的參數,其實就是一個資源的id值而已,那麼這裏反編譯也就只能將相應的id值進行還原,而沒法變成像R.layout.activity_main這樣直觀的代碼展現。
另外,除了MainActivity以外,還有不少其它的代碼也被反編譯出來了,由於當前項目有引用support-v4和support-v7的包,這些引用的library也會做爲代碼的一部分被打包到classes.dex文件當中,所以反編譯的時候這些代碼也會一塊兒被還原。
好的,學完了反編譯代碼,接下來咱們看一下如何反編譯資源。
其實細心的朋友可能已經觀察到了,剛纔Demo.apk的解壓目錄當中不是已經有資源文件了嗎,有AndroidManifest.xml文件,也有res目錄。進入res目錄當中,內容以下圖所示:
這不是全部資源文件都在這裏了麼?其實這些資源文件都是在打包的時候被編譯過了,咱們直接打開的話是看不到明文的,不信的話咱們打開AndroidManifest.xml文件來瞧一瞧,內容以下圖所示:
能夠看到,這代碼是徹底無法閱讀的。固然若是你去打開activity_main.xml看看,結果也不會好到哪兒去:
因而可知,直接對APK包進行解壓是沒法獲得它的原始資源文件的,所以咱們還須要對資源進行反編譯才行。
要想將APK文件中的資源反編譯出來,又要用到另一個工具了:
apktool 這個工具用於最大幅度地還原APK文件中的9-patch圖片、佈局、字符串等等一系列的資源。下載地址:http://ibotpeaches.github.io/Apktool/install/
關於這個工具的下載我還要再補充幾句,咱們須要的就是apktool.bat和apktool.jar這兩個文件。目前apktool.jar的最新版本是2.0.3,這裏我就下載最新的了,而後將apktool_2.0.3.jar重命名成apktool.jar,並將它們放到同一個文件夾下就能夠了,以下圖所示:
接下來的工做就很簡單了,咱們將Demo.apk拷貝到和這兩個文件一樣的目錄當中,而後cmd也進入到這個目錄下,並在cmd中執行以下命令:
Java
1 |
apktool d Demo.apk |
其中d是decode的意思,表示咱們要對Demo.apk這個文件進行解碼。那除了這個基本用法以外,咱們還能夠再加上一些附加參數來控制decode的更多行爲:
-f 若是目標文件夾已存在,則強制刪除現有文件夾(默認若是目標文件夾已存在,則解碼失敗)。
-o 指定解碼目標文件夾的名稱(默認使用APK文件的名字來命名目標文件夾)。
-s 不反編譯dex文件,也就是說classes.dex文件會被保留(默認會將dex文件解碼成smali文件)。
-r 不反編譯資源文件,也就是說resources.arsc文件會被保留(默認會將resources.arsc解碼成具體的資源文件)。
經常使用用法就這麼多了,那麼上述命令的執行結果以下圖所示:
這就說明反編譯資源已經成功了。
固然即便你在和我執行如出一轍的操做,也有可能會在這裏反編譯失敗,好比說會報以下錯誤:
出現這個錯誤的緣由頗有多是你以前使用過apktool的老版本進行過反編譯操做,而後apktool就會在你係統的C:UsersAdministratorapktoolframework
這個目錄下生成一個名字爲1.apk
的緩存文件,將這個緩存文件刪除掉,而後再從新執行反編譯命令應該就能夠成功了。
如今你會發如今當前目錄下多了一個Demo文件夾,這個文件夾中存放的就是反編譯的結果了。咱們能夠打開AndroidManifest.xml來瞧一瞧,以下圖所示:
怎麼樣?這樣就徹底能看得懂了吧,而後能夠再到res/layout中看一下activity_main.xml文件,以下圖所示:
能夠看到,activity_main.xml中的內容基本和源代碼中的內容是一致的,外層是一個RelativeLayout,裏面則是一個Button。你能夠再到其它目錄中去看一看別的資源,基本上都是能夠正常還原的,這樣咱們就把反編譯資源的方法也已經掌握了。
那麼對於反編譯出來的文件夾,咱們能不能從新把它打包成APK文件呢?答案是確定的,只不過我實在想不出有什麼義正言辭的理由可讓咱們這麼作。有的人會說漢化,沒錯,漢化的方式確實就是將一個APK進行反編譯,而後翻譯其中的資源再從新打包,可是無論怎麼說這仍然是將別人的程序進行破解,因此我並不認爲這是什麼光榮的事情。那麼咱們就不去討論自己這件事情的對或錯,這裏只是站在技術的角度來學習一下從新打包的相關知識。
首先咱們來看一下經過apktool反編譯後的包目錄狀況,以下圖所示:
其中,original文件夾下存放的是未經反編譯過、原始的AndroidManifest.xml文件,res文件夾下存放的是反編譯出來的全部資源,smali文件夾下存放的是反編譯出來的全部代碼,AndroidManifest.xml則是通過反編譯還原後的manifest文件。這裏值得一提的是smali文件夾,若是你進入到這個文件夾中你會發現它的目錄結構和咱們源碼中src的目錄結構是幾乎同樣的,主要的區別就是全部的java文件都變成了smali文件。smali文件其實也是真正的源代碼,只不過它的語法和java徹底不一樣,它有點相似於彙編的語法,是Android虛擬機所使用的寄存器語言,語法結構大概以下所示:
看上去有點暈頭轉向是嗎?可是若是你一旦可以看得懂smali文件的話,那麼你就能夠作很恐怖的事情了——你能夠隨意修改應用程序內的邏輯,將其進行破解!
不過我對這種黑技術並無什麼太大的興趣,所以我也沒有去作具體研究,但即便是這樣,也已經能夠對程序的邏輯作必定程度的修改了。好比說當咱們點擊按鈕時會彈出you clicked button
這樣一句Toast,邏輯是寫在MainActivity按鈕點擊事件的匿名類當中的,所以這段代碼反編譯以後必定就會在MainActivity$1.smali這個文件當中,讓咱們打開瞧一瞧,部分代碼以下所示:
雖然說多數的代碼我是看不懂的,但其中第47行實在太明顯了,Toast顯示的內容不就是在這裏定義的麼,那麼若是咱們想把Demo程序hack掉,就能夠將這段字符串給改掉,好比說我把它改爲Your app is been hacked
。
關於smali的語法,網上的資料也很是多,若是你對這門技術十分感興趣的話能夠直接上網去搜,這裏我只是簡單介紹一下,就再也不深刻講解相關知識了。
改了一處代碼後咱們再來改一處資源吧,好比這裏想要把Demo的應用圖標給換掉,那麼首先咱們要準備好一張新的圖片,以下圖所示:
而後從AndroidManifest.xml文件中能夠看出,應用圖標使用的是ic_launcher.png這張圖片,那麼咱們將上面籃球這張圖片命名成ic_launcher.png,而後拷貝到全部以res/mipmap開頭的文件夾當中完成替換操做。
在作了兩處改動以後,咱們如今來把反編譯後的Demo文件夾從新打包成APK吧,其實很是簡單,只須要在cmd中執行以下命令:
Java
1 |
apktool b Demo -o New_Demo.apk |
其中b是build的意思,表示咱們要將Demo文件夾打包成APK文件,-o用於指定新生成的APK文件名,這裏新的文件叫做New_Demo.apk。執行結果以下圖所示:
如今你會發如今同級目錄下面生成了一個新的APK文件:
不過不要高興得太早了,目前這個New_Demo.apk仍是不能安裝的,由於它尚未進行簽名。那麼若是這是別人的程序的話,咱們從哪兒能拿到它原來的簽名文件呢?很顯然,這是根本沒有辦法拿到的,所以咱們只能拿本身的簽名文件來對這個APK文件從新進行簽名,但同時也代表咱們從新打包出來的軟件就是個十足的盜版軟件。這裏你們學學技術就行了,但願不要有任何人去作什麼壞事情。
那麼這裏我就用一個以前生成好的簽名文件了,使用Android Studio或者Eclipse均可以很是簡單地生成一個簽名文件。
有了簽名文件以後在cmd中執行簽名命令就能夠進行簽名了,命令格式以下:
Java
1 |
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore 簽名文件名 -storepass 簽名密碼 待簽名的APK文件名 簽名的別名 |
其中jarsigner命令文件是存放在jdk的bin目錄下的,須要將bin目錄配置在系統的環境變量當中才能夠在任何位置執行此命令。
簽名以後的APK文件如今已經能夠安裝到手機上了,不過在此以前Android還極度建議咱們對簽名後的APK文件進行一次對齊操做,由於這樣可使得咱們的程序在Android系統中運行得更快。對齊操做使用的是zipalign工具,該工具存放於/build-tools/
目錄下,將這個目錄配置到系統環境變量當中就能夠在任何位置執行此命令了。命令格式以下:
Java
1 |
zipalign 4 New_Demo.apk New_Demo_aligned.apk |
其中4是固定值不能改變,後面指定待對齊的APK文件名和對齊後的APK文件名。運行這段命令以後就會生成一個New_Demo_aligned.apk文件,以下所示:
這個New_Demo_aligned.apk就是咱們從新打包簽名對齊後的文件了,如今把它安裝到手機上,效果以下圖所示:
能夠看到,應用圖標已經成功改爲了籃球,另外點擊按鈕後彈出的Toast的提示也變成了咱們修改後的文字,說明從新打包操做確實已經成功了。
問啊-定製化IT教育平臺,牛人一對一服務,有問必答,開發編程社交頭條 官方網站:www.wenaaa.com 下載問啊APP,參與官方懸賞,賺百元現金。
QQ羣290551701 彙集不少互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志於從事IT行業人員進入!