Proguard 사용부터 JD-GUI로 확인까지

Clean Code Big Poo·2023년 11월 22일
1

Android

목록 보기
1/2
post-thumbnail

Overview

아뿔싸
보안팀에서 보안진단 결과가 나왔다.. rooted되었는지 확인해주는 RootBeerNative가 entryPoint가 노출되어 루팅이 가능하다는 것이다.(이 내용과 동일)
progaurd를 적용해 보자..

보안의 중요성

앱을 빌드하면 apk가 생긴다. 안드로이드 스튜디오나 다른 IDE를 사용하면 apk 내부의 파일을 확인할 수 있다. .zip으로 확장자를 바꾸면 압축 해제도 가능하다.

이 상태로 앱을 출시한다면, 누군가 리버스 엔지니어링을 통해 앱을 완전 카피하여 출시할 가능성도 있다!

따라서 우리는 누군가 우리 앱을 들여다 보지 못하도록 조치를 취해야 할 필요성이 있다.

Progaurd 란?

안드로이드 공문서에 따르면 난독화 및 최적화를 해주는 무료 오픈소스 툴이다.
바이트 코드를 최적화하고 사용하지 않는 명령어를 감지해 제거하는 역할도 수행한다.

R8과 차이점

Android Gradle 플러그인 3.4.0 이상을 사용하여 프로젝트를 빌드하는 경우, 플러그인은 더 이상 ProGuard를 사용하여 컴파일 시간 코드 최적화 작업을 하지 않는다.
대신 플러그인은 R8 컴파일러를 이용하여 다음의 컴파일 시간 작업을 처리한다.

R8은 기존의 모든 ProGuard 규칙 파일과 호환되므로 R8을 사용하도록 Android Gradle 플러그인을 업데이트하면 기존 규칙을 변경할 필요가 없다.

Proguard 적용

build.gradle

프로젝트 수준의 build.gradle 파일에 다음을 포함한다.

buildTypes {
	release {
    		minifyEnabled true
        	proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
	debug {
    		minifyEnabled true
        	proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
}

Android Gradle 플러그인은 proguard-android-optimize.txt를 생성하는데, 이 파일은 대부분의 Android 프로젝트에 유용한 규칙(예를 들면 자주 사용되는 라이브러리에 대한 예외처리 Retrofit2, GSON, OkHttp3, RxJava, etc.)을 포함하고 @Keep* 주석을 사용한다.

debug 할때 proguard 설정을 다르게 하고자 한다면 minifyEnabled를 사용하지 않거나, 별도의 파일에 옵션을 지정하도록 하자.
e. proguard-rules.pro -> proguard-debug.pro

proguard-rules.pro

다음은 flutter에서 일반적으로 사용할수 있(다고 하)는 proguard-rules.pro의 내용이다. 내 프로젝트와 패키지에 따라 적용을 하도록하자.

# Flutter-specific ProGuard configuration

#-keep class io.flutter.app.** { *; }
#-keep class io.flutter.plugin.** { *; }
#-keep class io.flutter.util.** { *; }
#-keep class io.flutter.view.** { *; }
#-keep class io.flutter.** { *; }
#-keep class io.flutter.plugins.** { *; }

# Add any additional classes or packages that need to be preserved.
# 패키지에 proguard 설정에 대해 안내가 있다면 따르도록 하자.
# 앱 실행시 정상동작을 하지 못 할 수도?

# Keep the Application class
# 이 곳에 내 패키지 이름을 등록하도록 하자
-keep class your.package.name.MyApplicationClass { *; }

# Keep classes with the @Keep annotation
-keep @io.flutter.Keep class * { *; }

# Keep classes with specific annotations
-keepclasseswithmembers class * {
    @io.flutter.embedding.engine.plugins.FlutterPlugin <methods>;
}

# Preserve native methods
-keepclasseswithmembernames class * {
    native <methods>;
}

# Preserve enum classes
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# Keep methods for serialization/deserialization
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName <fields>;
}

# Keep the BuildConfig class
-keepclassmembers class your.package.name.BuildConfig {
    public *;
}

proguard-rules.pro 톺아보기

# By default, the flags in this file are appended to flags specified
# in /usr/share/android-studio/data/sdk/tools/proguard/proguard-android.txt

# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# FlutterDart 코드 보존:
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }

##---------------Begin: proguard configuration common for all Android apps ----------
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-dump class_files.txt
-printseeds seeds.txt
-printusage unused.txt
-printmapping mapping.txt
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-allowaccessmodification
-keepattributes *Annotation*
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
-repackageclasses ''

-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
-dontnote com.android.vending.licensing.ILicensingService

# Explicitly preserve all serialization members. The Serializable interface
# is only a marker interface, so it wouldn't save them.
-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();
}

# Preserve all native method names and the names of their classes.
-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembernames class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

# Preserve static fields of inner classes of R classes that might be accessed
# through introspection.
-keepclassmembers class **.R$* {
  public static <fields>;
}

# Preserve the special static methods that are required in all enumeration classes.
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep public class * {
    public protected *;
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}
##---------------End: proguard configuration common for all Android apps ----------

#---------------Begin: proguard configuration for support library  ----------
-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class com.actionbarsherlock.** { *; }
-keep interface com.actionbarsherlock.** { *; }

# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**
-dontwarn com.google.ads.**
##---------------End: proguard configuration for Gson  ----------

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class eco.wayble.app.driver.** { *; }

##---------------End: proguard configuration for Gson  ----------

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}





## Flutter 및 Dart 코드 보존:
#-keep class io.flutter.app.** { *; }
#-keep class io.flutter.plugin.** { *; }
#-keep class io.flutter.util.** { *; }
#-keep class io.flutter.view.** { *; }
#-keep class io.flutter.** { *; }
#-keep class io.flutter.plugins.** { *; }
#
## 앱의 메인 클래스 및 액티비티 보존:
#-keep class eco.wayble.app.driver { *; }
#
## 클래스 이름 난독화:
#-keepparameternames
#-repackageclasses ''
#-allowaccessmodification
#
## Flutter 플러그인과 관련된 클래스 보존:
#-keep class io.flutter.embedding.engine.plugins.FlutterPlugin { *; }
#-keepclasseswithmembers class * {
#    @io.flutter.embedding.engine.plugins.FlutterPlugin <methods>;
#}
#
## Gson과 관련된 클래스 보존:
#-keepclassmembers class * {
#    @com.google.gson.annotations.SerializedName <fields>;
#}
#
## BuildConfig
#-keepclassmembers class eco.wayble.app.driver.BuildConfig {
#    public *;
#}
#
## rootbeer
## -keep class com.scottyab.rootbeer.** { *; } -dontwarn com.scottyab.rootbeer.**
#
#
#

리버스 엔지니어링

컴파일과는 반대로, 컴파일된 실행 파일을 소스코드로 되돌리는 작업. 디컴파일이라고도 한다.

통상적으로 컴파일된 바이너리를 디스어셈블러라는 도구를 이용하여 어셈블리 코드를 출력한 후, 동일한 동작을 하는 프로그램을 만드는 것이다

Hooking

루팅 우회 방법 : 루팅을 탐지하는 부분을 가로채 '루팅되지 않음'을 리턴하여 우회

리버스 엔지니어링을 통해 디컴파일된 소스를 분석하여 루팅을 우회한다. 주로 프리다 후킹(frida hooking)을 사용한다.

리버스 엔지니어링 방법

1. Mac에서 apk decompile tool 설치

brew install apktool
brew install dex2jar

위가 안되면 아래 명령어 실행(homebrew는 설치했겠쥬?)

arch -arm64 brew install apktool
arch -arm64 brew install dex2jar

2. build apk

디컴파일할 apk 빌드

apk가 제대로 빌드안되는 거 같으면 ./gradlew clean하고 다시 apk 를 빌드하자.

cd android
./gradlew clean
flutter build apk --release --target-platform=android-arm64

일반적으로 apk가 생성되는 곳은 build/app/outputs/flutter-apk/app-release.apk 이곳이다.

3. decompile apk

app-release.apk 가 있는 폴더 에서 아래 명령어 실행. 그럼 JD-GUI에서 확인이 가능한 classes-dex2jar.jar 파일이 생성된다.

apktool d -s -o decompile app-release.apk
cd decompile
d2j-dex2jar classes.dex

혹은 아래 명령어를 실행.
app-release 폴더내의 smali, smali_classes2 폴더를 확인하면 동일하다

apktool d -f app-release.apk

smali, smali_classes2 차이?

앱을 디컴파일하면 생성되는 smali와 smali_classes2 폴더는 둘 다 안드로이드 앱의 Dalvik Executable (DEX) 파일을 나타낸다. 그러나 이 둘 간에는 몇 가지 차이가 있다.

  • smali 폴더:
    smali 폴더에는 원본 소스 코드를 Dalvik 바이트 코드로 변환한 것이 포함되어 있다.
    각 Java 클래스는 해당하는 smali 파일로 변환되어 있다.
    이 폴더는 주로 디컴파일된 코드를 읽고 이해하기 쉽게 하기 위한 용도로 사용된다.
  • smali_classes2 폴더:
    smali_classes2 폴더는 안드로이드 Gradle 플러그인 버전 3.0 이상에서 사용되는 빌드 시스템 변경과 관련이 있다.
    이 폴더는 smali 폴더보다 더 나은 멀티덱스 지원을 제공하고, 더 효율적인 빌드를 가능케 합니다.
    smali_classes2 폴더는 DEX 파일을 최적화하는 데 사용되며, 여러 DEX 파일이 있는 경우 각각의 DEX 파일은 해당하는 smali 폴더에서 가져온 클래스를 포함합니다.

일반적으로 개발자나 보안 전문가들이 앱을 분석할 때는 smali 폴더를 주로 살펴보게 된다. 여기에는 디컴파일된 코드가 포함되어 있어 앱의 동작 및 보안 측면에서의 취약점을 식별하는 데 도움이 된다.(라고 하는데 smali_classes2는 안보는 걸까..?)

4. JD-GUI 설치

이 곳에서 자신의 OS에 맞는 파일 다운로드 및 압축해제

JD-GUI 의 Download 탭에서 다운로드한다.

압축해제 하면 이렇게 보인다.

5. JD-GUI.app 로 확인

JD-GUI.app가 있는 폴더에서 아래 명령어 실행.
터미널 닫으면 종료된다! 주의.

java -jar JD-GUI.app/Contents/Resources/Java/jd-gui-1.6.6-min.jar

내 맥이 이상한건지 모르겠지만... 다른 파일을 열면 적용이 제대로 안된다. JD-GUI.app을 껏다가 다시 켜서 오픈을 해야 다른 파일이 정상적으로 열렸다. 아무래도 위치와 이름은 같지만 지워졌다 다시 생성된 파일이라 그런것 같기도 하다.


File - Open File 에서 d2j-dex2jar classes.dex 로 생성한 classes-dex2jar.jar 파일를 열어주자

Proguard를 적용한 안드로이드 APK파일을 디컴파일하면 아래처럼 모든 이름들이 a,b,c 형태로 변경된것을 볼 수 있다.
해독하는게 불가능하지는 않지면 매우 어렵게 만들어 어느정도의 보안을 적용할 수 있다.

주의사항

minifyEnabled true 를 주석해도 proguard-rules.pro 에 내용이 있으면 뭔가 읽히는 것 같다.. 내용을 지우고 진행하자.

적용되기 전을 확인해보고 싶어 minifyEnabled false로 놓고 확인해보았는데... 뭔가 생각대로 결과가 안나온다.
proguard-rules.pro의 내용을 완전 주석하고 나니 원하는대로 적용전 내용을 확인할 수 있었다.
뭔가 꼬인걸까?

## this is 'build.gradle'
buildTypes {
        release {
            signingConfig signingConfigs.release

             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            signingConfig signingConfigs.debug

             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
## this is 'proguard-rules.pro'

## Flutter wrapper
#-keep class io.flutter.app.** { *; }
#-keep class io.flutter.plugin.**  { *; }
#-keep class io.flutter.util.**  { *; }
#-keep class io.flutter.view.**  { *; }
#-keep class io.flutter.**  { *; }
#-keep class io.flutter.plugins.**  { *; }
-keep class com.** { *; }
#-dontwarn io.flutter.embedding.**

참고

0개의 댓글