1. 程式人生 > >Android 逆向工程

Android 逆向工程

1. Apk簽名

1). 建立簽名檔案

新建專案 -> build -> Generate Signed Bundle/APK... -> Create new...


3110861-5431ab3e68873d86.png 圖1.png
2). 填寫簽名檔案相關資訊

點選確認會生成signed.jks檔案,將signed.jks檔案拷貝至<專案路徑>/app/

3110861-33662ec441f1e736.png 圖2.png

3). 在app/build.gradle檔案中配置

在android節點下配置signingConfigs節點,並在其節點下配置別名,密碼,檔案,檔案密碼屬性,最後在buildTypes下的release結點下配置signingConfig

android {    
    // 簽名配置
    signingConfigs {
        release {
            keyAlias 'test'
            keyPassword 'password'
            storeFile file('signed.jks')
            storePassword 'password'
        }
    }
    
    buildTypes {
        release {
            minifyEnabled false
            // 配置簽名
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
4). 打包

<專案路徑>下執行gradlew aR命令打包,完成後提示資訊如圖3所示,並在<專案路徑>\app\build\outputs\apk\release\路徑下檢視app--release.apk檔案。

3110861-a71e2316a394e73a.png 圖3.png

5). 反編譯

工具:dex2jar
下載完成後,將dex2jar的路徑配置到環境變數中D:\Program\android\android_reverse\dex2jar;

3110861-057d79d2c1722786.png
圖4.png

6). 反編譯

將第4)步生成的app-release.apk解壓並進入該資料夾,並使用d2j-dex2jar classes.dex命令將classes.dex檔案轉換為jar檔案

3110861-b860890e9a61d42c.png 圖5.png

解壓完成後,會在當前目錄生成一個classes-dex2jar.jar檔案,我這裡還生成了classes-error.zip檔案,是由於轉換過程中出現錯誤了,由於未影響之後的操作,所以這裡我忽略了這個錯誤。

3110861-4ab93b783492a94f.png 圖6.png

7). 檢視classes-dex2jar.jar檔案

工具:jd-gui
使用jd-gui開啟classes-dex2jar.jar檔案

3110861-c0138d925199c4a9.png 圖7.png

2. 混淆

1). 打包混淆

修改app/build.gradle檔案中的android-buildTypes-release中的minifyEnabled屬性,設定為true,此時相關的混淆檔案有proguard-android.txt和proguard-rules.pro。

3110861-617b9922051e21b7.png 圖8.png

2). proguard-android.txt

位置:sdk\tools\proguard\proguard-android.txt

3). proguard-rules.pro

位置:<專案路徑>\app\proguard-rules.pro

4). 打包並讀取檔案

簽名 -> 打包 -> 反編譯, 後classes.dex中的內容如下,


3110861-a680178ba8eb12ed.png 圖9.png
5). 問題

此時的問題是,混淆了一些本不該混淆的檔案,那麼我們就需要自己去配置混淆規則,我們必須要自己修改proguard-rules.pro檔案

6). 新增混淆規則模板
-ignorewarnings # 忽略警告
-optimizationpasses 5 # 指定程式碼的壓縮級別
-dontusemixedcaseclassnames # 是否使用大小寫混合
-dontpreverify # 混淆時是否做預校驗
-verbose # 混淆時是否記錄日誌
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆時所採用的演算法

# 保持子類不被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
   native <methods>;
}
# 保持自定義控制元件類不被混淆
-keepclasseswithmembers class * {
   public <init>(android.content.Context, android.util.AttributeSet);
}
# 保持自定義控制元件類不被混淆
-keepclasseswithmembers class * {
   public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保持自定義控制元件類不被混淆
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}
# 保持列舉 enum 類不被混淆
-keepclassmembers enum * {
   public static **[] values();
   public static ** valueOf(java.lang.String);
}
#保持Parcelable不被混淆
-keep class * implements android.os.Parcelable {
   public static final android.os.Parcelable$Creator *;
}
# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
# 保持序列號類不被混淆
-keep public class * implements java.io.Serializable {*;}
-keepclassmembers class * implements java.io.Serializable {
   static final long serialVersionUID;
   private static final java.io.ObjectStreamField[]   serialPersistentFields;
   private void writeObject(java.io.ObjectOutputStream);
   private void readObject(java.io.ObjectInputStream);
   java.lang.Object writeReplace();
   java.lang.Object readResolve();
}

3.

1). 獲取簽名信息
  public int getSignature() {
    PackageManager packageManager = this.getPackageManager();
    PackageInfo pi;
    int sign = 0;
    try {
      pi = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
      Signature[] signingInfo = pi.signatures;
      sign = signingInfo[0].hashCode();
    } catch (Exception ignored) {}
    return sign;
  }

工具下載

程式碼下載