1. 程式人生 > >apktool,dex2jar,jd-gui簡單使用與實戰

apktool,dex2jar,jd-gui簡單使用與實戰

前言

最近專案裡要實現一個TimePicker,類似於這樣的

我一看,瞬間想起了手機上的某APP,他上面就剛好有類似的效果

本來專案時間就有點趕,自己慢慢寫這個控制元件時間上也有點來不及了,而且他又那麼的像,於是,嘿嘿嘿…

正文

下載安裝apktool,dex2jar以及jd-gui

這三個軟體都是免費的,下載地址Google一下也能輕易的搜到,這裡還是簡單都是匯一下總。

dex2jar在sourceforge上面,建議代理開全域性訪問。

OK,下載好了之後就開始安裝。
首先是apktool。執行apktool是需要Java環境的,不過我相信需要用到這個軟體的人電腦裡肯定是有JDK的,這個就不贅敘了。

  1. 在下載apktool壓縮包的同一個資料夾裡新建一個文字檔案,寫入
@echo off
if "%PATH_BASE%" == "" set PATH_BASE=%PATH%
set PATH=%CD%;%PATH_BASE%;
java -jar -Duser.language=en "%~dp0\apktool.jar" %1 %2 %3 %4 %5 %6 %7 %8 %9

並將這個檔案命名為apktool.bat。
2. 將剛剛下載的apktool的壓縮包更名為apktool.jar(剛下載的時候名字裡應該有版本號,幹掉他)。
3. 成功。

dex2jar和jd-gui都是綠色版的,不需要安裝,下載之後直接解壓就好了。

使用apktool得到Android apk的資原始檔

一般來說拿到一個apk檔案之後想要得到自己想要的部分的程式碼一個很重要的突破口就是圖片和xml檔案,尤其是在你想要得到的部分是一個自定義控制元件的時候。因為圖片和xml檔案的名字是不會被混淆的,如果編寫這個程式的人秉承了良好的程式碼規範的話,你就可以很容易的猜出你想要的介面使用的那些圖片的名字,甚至猜出那個介面或者控制元件的xml檔案的名字,繼而找到控制元件的包名——幸運的是,包名也是不會被混淆的。繼而就可以找到相應的Java檔案——這是像我這樣的小偷的幸運也是像我這樣的開發者的悲哀。

ok,接下來該怎麼用apktool得到apk的資原始檔呢?我們知道,想要看到apk裡面的圖片是很簡單的,直接用WinRAR之類的解壓器直接開啟apk檔案就可以得到圖片——也可以看到佈局檔案或者Manifest檔案等,但是他們是被混淆過的,開啟是一堆亂碼。這個時候就要用到apktool了。

  1. 將想要反編譯的apk檔案,假設apk檔案的名字是base.apk,放到apktool的資料夾下。
  2. 開啟控制檯,進入apktool所在的資料夾。
  3. 輸入apktool d base.apk,敲回車。

然後等一會兒你就會發現在apktool所在的資料夾裡面多了一個叫做base的資料夾,進去之後是這樣的

這個時候res裡面的xml檔案就都是可讀的了。可以看到,還有一些奇奇怪怪的資料夾,比如smali和unknown,其中smail是apk中的Java檔案轉換過去的,Android採用的是java語言進行開發,但是Android系統有自己的虛擬機器Dalvik,程式碼編譯最終不是採用的java的class,而是使用的smali。我們反編譯得到的程式碼,jar的話可能很多地方無法正確的解釋出來,如果我們反編譯的是smali則可以正確的理解程式的意思。但是smali有自己的一套語法,而且比較的晦澀難懂,所以雖然他要比反編譯出來的jar檔案準確一些,但是在大多數情況下我還是更傾向於看反編譯出的jar檔案。

既然都已經得到了佈局檔案,那麼現在就來找目標介面所在的xml。很顯然我的目標名字裡含有time或者picker這類的單詞,我很快就找到了我的目標——一個叫做time_picker.xml的檔案——也許原來的APP裡面這個控制元件在多個地方都有出現,所以原作者把它單獨抽取出來了。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:gravity="center"
    android:orientation="horizontal">

    <net.simonvt.calendarview.CalendarView
        android:id="@id/calendar_view"
        android:layout_width="245.0dip"
        android:layout_height="280.0dip"
        android:layout_marginLeft="16.0dip"
        android:layout_marginRight="16.0dip"
        android:layout_weight="1.0"
        android:focusable="true"
        android:focusableInTouchMode="true"/>

    <LinearLayout
        android:id="@id/pickers"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1.0"
        android:gravity="center"
        android:orientation="horizontal">

        <net.simonvt.numberpicker.NumberPicker
            android:id="@id/month"
            android:layout_width="48.0dip"
            android:layout_height="wrap_content"
            android:layout_marginLeft="6.0dip"
            android:layout_marginRight="6.0dip"
            android:focusable="true"
            android:focusableInTouchMode="true"/>

        <net.simonvt.numberpicker.NumberPicker
            android:id="@id/day"
            android:layout_width="48.0dip"
            android:layout_height="wrap_content"
            android:layout_marginLeft="6.0dip"
            android:layout_marginRight="6.0dip"
            android:focusable="true"
            android:focusableInTouchMode="true"/>

        <net.simonvt.numberpicker.NumberPicker
            android:id="@id/year"
            android:layout_width="42.0dip"
            android:layout_height="wrap_content"
            android:layout_marginLeft="6.0dip"
            android:layout_marginRight="6.0dip"
            android:focusable="true"
            android:focusableInTouchMode="true"/>

        <net.simonvt.numberpicker.NumberPicker
            android:id="@id/hour"
            android:layout_width="48.0dip"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16.0dip"
            android:layout_marginRight="5.0dip"
            android:focusable="true"
            android:focusableInTouchMode="true"/>

        <TextView
            android:id="@id/colon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=":"
            android:textColor="#ff000000"
            android:textSize="16.0sp"
            android:textStyle="bold"/>

        <net.simonvt.numberpicker.NumberPicker
            android:id="@id/min"
            android:layout_width="48.0dip"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5.0dip"
            android:layout_marginRight="10.0dip"
            android:focusable="true"
            android:focusableInTouchMode="true"/>
    </LinearLayout>
</LinearLayout>

我們可以很清晰的看到,我最終希望的效果是五個NumberPicker拼湊出來的,而且我還知道了他的包名是net.simonvt.numberpicker,那麼接下來幾乎可以直接動手粗暴的獲取apk反編譯之後的jar檔案了。

使用dex2jar獲取jar檔案

在用dex2jar的時候我還出了一點問題,試了好幾次都不能成功的搞定,等我將正確的方式說了之後一會兒再分享坑爹的經歷。

  1. 首先將目標apk檔案的字尾改為.rar,比如base.apk改為base.rar,方便解壓。
  2. 解壓剛剛得到base.rar檔案。可以看到解壓出來的檔案裡面有一個的字尾名是.dex,名字一般來說是classes.dex,反正他是什麼一會兒就輸入什麼。沒錯,dex2jar就是對他起作用的。
  3. 進入dex2jar的那一堆bat命令所在的資料夾,找到那個名字裡面帶有dex2jar.bat的bat命令,我的這個版本是這個

  4. 開啟控制檯,進入dex2jar所在的資料夾,注意,是那個有一大堆bat命令的地方,那才是終點。

  5. 輸入命令 d2j-dex2jar.bat classes.dex的全路徑(比如:E:\lypeer\classes.dex),敲回車。

然後稍等一會兒就可以看到在dex2jar的資料夾裡多了一個jar包,像這樣的

這是正確的流程。然而一開始我用dex2jar的時候踩到了好多的地雷:(。
有可能是dex2jar的版本不一樣的原因,有好多地方都和網上搜到的教程不一樣。比如我看到的教程他輸入的命令是 dex2jar.bat %class.dex的地址%class.dex 簡直坑啊…這命令一共就兩部分,兩部分都是錯的。第一部分根本就沒有那個命令,人家包裡的命令是d2j-dex2jar.bat,第二部分明明是classes.dex他寫成了class.dex……不過這有可能是由於版本的原因,大家要善於自己找這些命令的正確書寫方式。

使用jd-gui檢視得到的jar包

然後怎麼看這個jar包呢?這個時候jd-gui就起作用了。
先前說過將下載來的jd-gui解壓就好了,解壓之後會有一個叫做jd-gui.exe的可執行檔案,雙擊點開,然後根據目錄開啟剛剛得到的jar包或者直接將jar包拖進去,就可以看到反編譯之後的結果了。

可以看到,jar包裡面的檔案是根據包結構排列的像這樣

而我之前已經知道了我的目標的包名,所以很容易我就找到了我的目標

點進去就是目的碼了,當然具體的程式碼肯定是經過混淆的,比如引數都是aa,bb,cc啦,方法名都是a,b,c,d啦,都很常見,這個時候就取不得巧了,慢慢研讀吧,讀得多了掌握了一些技巧之後其實還是很快的。

後記

到最後,其實我發現我的目標是一個開源庫:)