Sunday, July 31, 2011

Android反組譯 - Reverse engineer

以下文章轉載自此部落格

先前聽說許多關於 Android 程式很容易被逆向工程,我也抱持這樣的觀點去戳 Android 開發者 XDD 直到今天開完會,前輩跟我說 Android 產生出的 dex file 很難閱讀,因此我就去找一下相關資料,找到一篇 2007/11/28 How to decompile .dex file on Android 文章,上頭也是敘述要去解讀 dex 有點不好讀,但隨後找到 2009/08/08 decompiling DEX into Java sourcecode 這篇文章,細看一下原來還有 dex2jar 的方式!這下可好了...
所以,咱們來試試吧!我在 Ubuntu 10.04 的環境下測試
準備工具:
  • dex2jar
    • A tool for converting Android's .dex format to Java's .class format
  • JD-GUI
    • JD-GUI is a standalone graphical utility that displays Java source codes of “.class” files. 
  • 一份編譯好的 apk 檔案
接著就把 dex2jar 解壓縮出來,並執行
$ cd dex2jar-0.0.7.7-SNAPSHOT
$ sh dex2jar.sh MyWidget.apk
然後就會看到一個對應的 jar 檔:MyWidget.apk.dex2jar.jar
解壓縮 JD-GUI 去開啟 MyWidget.apk.dex2jar.jar 檔案,不囉唆,程式碼就可以看了!整個過程不用 30 秒吧!打快一點可能 3 秒喔 XD

這...還真的叫我難以投入 Android 開發啊 *誤* 不過仔細一看,之前在 MyWidget 寫的程式,有些地方式用 R.layout.main 的地方,已經直接被換成數值了,但我想這樣也不會太難閱讀,畢竟整個 source code 都丟在你眼前了!更別提 dex2jar 仍然很活躍地進行維護及開發,真的開感謝一下對岸,第一次看到簡體中文有那種莫名的感動 XD 太多東西都嘛是英文先衝啦
至於 dex2jar 的原理,可以參考這邊:介绍dex2jar的核心思想,有原理才會有相對應的解法喔!
順便筆記一下 iPhone 的:
arm-elf-objcopy -I binary -O elf32-littlearm iPhoneApp out.elf && arm-elf-objdump -marm9 -D out.elf
但組語嘛,雖然學過,但我想我應該看不下去 XD
最後,關於逆向工程的事情,如果程式碼非常冗長,那一樣不好啃啊!所以,玩玩就好。

以上文章轉載自此部落格

關於程式碼混淆proguard

在了解程式碼混淆前(官方翻譯成模糊程式碼),
建議可以先去觀看漁郎的教學


在了解程式碼混淆前,
也許你會問我︰「為什麼我要學程式碼混淆?」
很簡單,答案如下︰
1.讓使用反編譯工具想看你程式碼的人,閱讀你的程式碼更困難
2.優化你的程式,節省空間

如果你的開發版本在Android2.3以前
那麼Proguard的使用方式是”手動”混亂你的程式碼
請在你的專案目錄底下,打上︰
android update project -p E:\_Eclipse-3.5.2\workspace\Wallpaper -t 8
或在那個專案目錄下鍵入android update project -p ./ -t android-8
其中-t 8指的是即將編譯的Android版本編號
8代表要編譯成Goole Map + Android2.2以上用的Application
android-8 代表要編譯成Android2.2以上用的Application
灰色字體取決於你是否在當下目錄,
若在當下目錄使用proguard,那麼灰色字體就是打上「./」
當完這行指令後,
你的螢幕會變成以下︰
意思是説,己經在這個專案底下加入build.xml檔和proguard.cfg檔了

回到Eclipse環境中,對你的專案按下F5重整,
你就會看到這2個檔案。

在Eclipse專案下default.properties裡面
應該要有這2行︰
target=android-8(代表在2.2執行)
proguard.config=proguard.cfg(指定混淆腳本是被放在這個專案的根目錄)

如果build.xml出錯,
回報我找不到help文檔,
那麼把xml標籤的defualt="help"刪了吧。

此時將application export出來,
應該會多一個proguard目錄才對。
如果做完apk後,
按了F5這個目錄都沒出來,
代表你並沒有混淆成功。

註︰
-t 8 不可能打成 -t 9,
為什麼呢?
Android在2.3後(版本代號9),
正式將ProGuard納入了SDK,
所以如果你是在2.3環境開發時,
不用再打上面那行指令。
現在這行指令只會在2.2(含)以下需要了。

更詳細的內容,
請參閱於Android2.3推出的Proguard文檔

下一篇︰

無法混淆的相關問題

相關文章︰

作品 - 管理學決策模式

花了3個星期,
這隻程式終於在蹺課數次,
下班後熬夜數天,
沒日沒夜的趕出來了。

謝謝Camangi Janis當初在公司時,
不藏私的教同仁管理學的決策模式。

這讓我在人生的幾個重要決策中,
因為這些管理學者的技術,
我的每一個決定,
都不再是隨便的做出來,
而是經過條理分析。

話不多說,
請參考教學影片

請至Android Market下載︰
管理學決策模式

或使用QRCode搜尋

Saturday, July 30, 2011

分享才是王道︰從你的APK發佈Facebook訊息!

這個部落格的存在,
主要是因為小鰻本人記憶力非常不好,
我必須要有一個工具記住我以前學過的東西,
哈,離題。
今天要講如何讓你的APK
擁有發佈Facebook訊息的功能


底下列了大概要做哪些事的步驟︰

一、Facebook網站端
1.在Facebook註冊開發者身份
2.在Facebook開發者頁面新增應用程式
3.輸入Key Hash


二、Eclipse端
1.下載Facebook源碼
2.創建Facebook的基本SDK


三、Code端
1.將Facebook Library套入到自己的專案
2.將manifest.xml加上Internet permission
3.攢寫並使用Facebook Code




==========正文開始===============


一、Facebook網站端


1.在Facebook註冊開發者身份
我們必須跟 Facebook註冊成為開發者的身份,
之後才有權利跟Facebook伺服器做溝通。


2.在Facebook開發者頁面新增應用程式
要利用Facebook API跟Facebook伺服器溝通前,
我們必須先創立專屬的應用程式
因為等等我們寫Code呼叫API時,
會需要我們這裡申請到的應用程式ID




3.輸入Key Hash
官方文件這麼說明︰
We now need to export the signature for your app so that Facebook can use to ensure users are only communicating with your app on the Android. This is done by running the keytool.
原來Key Hash是Facebook和我們的應用程式做溝通的橋樑。



怎麼生成這個Key Hash呢?
首先,我們必須搞懂Android Signature這個東西,
由於篇幅問題,這裡跳過,
你可以去參考Android Guide裡的資料
但我可以跟你說,
一般我們的開發過程,
會有2種Signature,
一種是Debug用的Signature,
另一種是真正要發佈程式至Android Market用的正式Signature。

在這裡,我先示範抓出Debug Signature的方法,
如果之後要用發佈的Signature,
記得底下這步要做啊!


(1)開啟Terminal終端機,輸入


註︰如果你是用Windows,請在Mysgit產生的Git Bash視窗裡輸入上面這行指令

要怎麼查你的debug.keystore在哪裡呢?
在Eclipse中,點選[Windows]-->[Preferences]
出現底下的視窗
沒有Keytool指令?
Keytools在C:\Program Files\Java\jdk1.6.0_26\bin\裡
拜託環境變數設好再來。


沒有openssl?
我在Ubuntu和Mac下,因為是Linux環境,
只要apt install openssl就好了,很快
但Windows下openssl要怎麼匯入,
 請知道的人提供一下,謝謝
註︰Android真的不適合在Windows環境開發,
你會發現很多東西都要用到Linux


總之,剛才那段keytool指令,
在terminal裡應該要能生成出Key Hash。
示意圖如下︰




(2)將生成的值複製並貼入Facebook的Key Hash



二、Eclipse端

1.下載Facebook源碼
請使用Git的方式,將Facebook源碼下載下來
Windows用msygit
指令為
git clone http://github.com/facebook/facebook-android-sdk.git


2.創建Facebook的基本SDK
我們現在要將剛才git clone下來的源碼新增進Eclipse裡。

在Eclipse工具列[File]-->[New]-->[Android Project]裡,
從一個已存在的目錄去新建專案,
而路徑請導到剛才clone下來的目錄底下的facebook目錄


 此外,你可以按照官方文件的教學,
同樣方式導入另一個資料夾的Sample Application,
去看看官方教你的API使用方式。
當然,我接下來的教學裡也會說明。
This library includes two sample applications to guide you in development.
  • simple: A bare-bones app that demonstrates authorization, making API calls, and invoking a dialog.
  • stream: This slightly beefier application lets you view your news feed.
To install a sample application into Eclipse (3.5):
  • Create the sample application in your workspace:
  • Select File -> New -> Project, choose Android Project, and then click Next.
    1. Select "Create project from existing source".
    2. Choose either examples/simple or examples/stream. You should see the project properties populated.
    3. Click Finish to continue.
  • Build the project: from the Project menu, select "Build Project".    

三、Code端
    1. 將Facebook Library套入到自己的專案
    (1)點擊[Project]-->[Propertites]


    底下那裡應該有打勾

    (2)看一下Package Explore,Facebook是不是順利匯進來了呢?




    2.將manifest.xml加上Internet permission


    耶!可以開始寫Code了


    3.攢寫並使用Facebook Code
    將FB專案成功的匯入我們的正在開發的專案夾後,
    剩下的就是如何攢寫Code去呼叫Facebook API了。
    Facebook的API有新(Graph API )舊(Legacy REST API:)兩種版本,
    官方已經不建議我們用舊的了。


    底下會以Graph API做示範。


    因為Facebok支援Single-Sign-On(SSO),
    所以如果使用者在用我們APK的當下有登入Facebok的話,
    基本上就不需要重複登入了。


    在攢寫程式碼的一開始,
    還記得我們教學一開始要求在Facebook Developer裡新增應用程式裡的應用程式ID嗎?
    這裡就要開始用到了。
    package com.greatap;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import com.facebook.android.*;
    import com.facebook.android.Facebook.*;
    
    public class MyGreatActivity extends Activity {
    
        Facebook facebook = new Facebook("你的應用程式ID");
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
               //跟Facebook伺服器要求User可以透過我們的程式跟Facebook做
               //發文的動作。
            facebook.authorize(this, new String[] { "email", "publish_stream" },
               //跟伺服器申請我們要能發文和用e-mail,
               //這裡可以任我們新增修改
            new DialogListener() {
                 @Override
                  public void onComplete(Bundle values) {
             //當認證成功後我們當然是要發文,
             //發文有2種模式,
             //一種用post或get,
             //另一種用Dialog的模式,
             //Dialog模式會呼叫"即將會發什麼文章至Facebook"的視窗給使用者預覽
              try {     
                   Bundle parameters = new Bundle();
    
                  //使用者想說的話 
                   parameters.putString("message", "可以預寫使用者要發的黑體文字");
                   //連結所想呈現的文字
                   parameters.putString("name", "連結裡所以呈現的文字");
                   //連結網址
                   parameters.putString("link","https://market.android.com/details?id=com.hardworking.managingdecision_lite&feature=search_result");
                  //灰色註解
                   parameters.putString("description","在灰色註解區塊想寫些什麼?");
                //使用Dialog的方式發文
               facebook.dialog(Result.this, "feed", parameters, new DialogListener(){
    
           @Override
           public void onComplete(Bundle values) {
            // TODO Auto-generated method stub
            
           }
    
           @Override
           public void onFacebookError(FacebookError e) {
            // TODO Auto-generated method stub
            
           }
    
           @Override
           public void onError(DialogError e) {
            // TODO Auto-generated method stub
            
           }
    
           @Override
           public void onCancel() {
            // TODO Auto-generated method stub
            
           }
                    
                   });
                  
    }
    
               @Override
               public void onFacebookError(FacebookError error) {}
    
               @Override
               public void onError(DialogError e) {}
    
               @Override
               public void onCancel() {}
          }
    );
          
    
        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
    
            facebook.authorizeCallback(requestCode, resultCode, data);
        }
    }
     


    認證那段的程式碼facebook.authorize會讓使用者出現底下的畫面


    寫到這裡,這樣子你會了嗎?
    對了,更多的Facebook API指令,
    都可以在這裡查到哦!

    Thursday, July 21, 2011

    ListView滑動時背景變黑

    當我們將ListView背景設定成一個我們想要的圖案時,
    在上下滑動時,往往背景會變成黑色,
    顯然這不是我們想要的畫面。 

    解決的方法︰
    在Layout的ListView標籤裡加入
    android:cacheColorHint="#00000000" 
    的屬性。

    Wednesday, July 20, 2011

    Android in-app Purchase 應用程式內結帳(筆記暫停更新)

    2011-08
    本然想既然台灣目前不能上架付費版apk,
    那麼就來改走in-app purchase路線,
    結果⋯

    Before you get started with in-app billing, be sure to review the following requirements and limitations.
    在你開始使用內部app金流時,請確定已經看過以下的限制和需求
    • In-app billing can be implemented only in applications that you publish through Android Market.內部app金流只能透過Android Market實作並且發佈
    • You must have a Google Checkout Merchant account to use Android Market In-app Billing.你必須有一組Google Checkout帳號
    • If your device is running Android 3.0, in-app billing requires version 5.0.12 (or higher) of the MyApps application. If your device is running any other version of Android, in-app billing requires version 2.3.4 (or higher) of the Android Market application.如果你的裝置是運行Android 3.0的話,內部app金流需要MyApps這隻程式版本5.0.12(或更高)。如果你的裝置是運行其它版本,那麼需要Android Market版本在2.3.4(或更高)
    • An application can use in-app billing only if the device is running Android 1.6 (API level 4) or higher.能使用內部app金流機制的程式必須是在Android1.6(或更高)。
    • You can use in-app billing to sell only digital content. You cannot use in-app billing to sell physical goods, personal services, or anything that requires physical delivery.
    • Android Market does not provide any form of content delivery. You are responsible for delivering the digital content that you sell in your applications.
    • You cannot implement in-app billing on a device that never connects to the network. To complete in-app purchase requests, a device must be able to access the Android Market server over the network. 
    研究了一下手上幾臺測試機的Android Market版本號︰
    手機型號 系統版本 Android Market版本
    Motorola X720 2.1 2002306
    Nexus One 2.3.4 2.3.6
    Nexus S 2.3.4 2.3.6
    HTC Hero 2.1 2002306
    HTC Incredible 2.3.3 2.3.4
    HTC Legend 2.1 2002306

    有發現什麼事嗎?XD

    =====稍微做一下筆記(底下會持續更新)======
    1.必須在permission加入com.android.vending.BILLING
    如果沒加入,publish頁面會無法新增in app product

    2.需要申請測試帳號
    3. 應用程式不需要被發佈,但in app product一定要發佈才能測
    4. 發佈時請用Release版的signature
    5. 測試程式的版本號需與market這個測試apk版本一致
    6.一定要用實機測試billing,不能用模擬器
    7.實機的帳戶請設成測試帳戶
    設定方法︰
    1. Open Settings on your device.
    2. Touch Privacy.
    3. Touch Factory data reset.
    4. Touch Reset phone.
    5. After the phone resets, be sure to sign in with one of your test accounts during the device setup process.
    8.確認實機的Android Market版本編號是否在2.3.4以上;
    如果是平板Android 3.0以上請確認是否有MyApps這隻程式且版本在5.0.12以上。
    =========================
    In-app Billing Overview頁面重點︰
    1. In-app purchase使用asynchronous message loop
    2. 我們的應用程式不會和Android Market直接互動,而是透過發送請求的方式。


    一個典型的in-app purchase仰賴以下三大元件︰
    1. Service - (範例程式裡的BillingService),負責處理從程式裡發送出來到Android Market in-app billing Service的購買訊息、
    2. BroadcastReceiver - (範例程式裡的為BillingReceiver),負責接收從Android Market來的所有非同步(asynchronous)交易回應。
    3. Security - (範例程式裡的為Security),負責執行安全相關的工作任務。像是數位簽章驗證(signature verification)以及nonce generation(?)

    也許你還會併入另外兩個元件來處理in-app purchase︰
    1. Handler - (範例程式裡的為ResponseHandler),處理購買通買通知訊息、錯誤、或其它的狀態訊息
    2. observer - (範例程式裡的為PurchaseObserver),負責發送callback到我們的應用程式,因此我們能夠更新我們的程式內的購買資訊畫面。
    商品型態分為二種︰
    1. 經由使用者帳戶管理 - 若商品選擇該方式,那麼購買資訊等⋯,將會被儲存於Android Market後臺。這種性質的商品,若使用者換裝置,並重新安裝你的軟體,該購買都會保存。
    2. 不管理 - 不會將購買資訊儲存於Android Market後臺。舉例來說︰遊戲裡的魔法藥水。

    處理複雜的in-app pruchase交易流程
     上圖由上至下是整個應用程式內結帳的流程順序。
    一旦你使用到了該機制,
    整體的程式碼互動性就和這個架構開始息息相關。
    官方文件提到,即使你沒有呼叫REQUEST PURCHASE去呼叫出購買視窗,
    我們都一定要處理IN_APP_NOTIFY這個broadcast receiver(參閱In-app Billing Overview下方文章)。
    為什麼呢?
    1. 因為Android Market可能會傳來讓程式內部交易的記錄,我們必須要處理
    2. 使用者可能多機一帳號,我們要將該資訊連結至新裝置的程式裡

    Android Market也會藉由IN_APP_NOTIFY通知程式該in-app purchase已退費的事情。
    退費狀態會尾隨著PURCHASE_STATE_CHANGED這個BroadCast Receiver以JSON的方式傳進來︰purchaseState值回傳2(表示已退費)


    JASON資料被綁在PURCHASE_STATE_CHANGED裡傳進來

    詳細內文請看Android Document