개요
Android 앱 난독화 시 사용되는 Proguard에 대해서 알아본다.
목표
Android에서 Proguard 난독화를 통해 얻어지는 이점을 설명할 수 있다.
Proguard란?
프로가드(ProGuard)는 자바 코드를 축소하고 최적화하고 난독화하는 오픈 소스 명령 줄 도구이다. 바이트코드를 최적화할뿐 아니라 사용하지 않는 명령어를 찾고 감지할 수 있다.
- Proguard는 Android SDK에 포함되어 배포되고 있으며, 많은 Android 개발자들이 프로가드를 이용하여 쉽게 최적화 및 난독화 기능을 사용할 수 있다.
- Android Gradle 플러그인 3.4.0 이상을 사용하여 프로젝트를 빌드하는 경우 플러그인은 더 이상 ProGuard를 사용하여 컴파일 시간 코드 최적화 작업을 하지 않는다. 대신 플러그인은 R8 컴파일러를 이용하여 다음의 컴파일 시간 작업을 처리한다.
Proguard 기능
- 코드 축소(또는 Tree Shaking): 앱 및 라이브러리 종속 항목에서 미사용 클래스, 필드, 메서드, 속성을 감지하여 안전하게 삭제합니다(64k 참조 제한을 해결하기 위한 유용한 도구). 예를 들어 라이브러리 종속 항목에서 몇 개의 API만 사용한다면 축소는 앱이 사용하지 않는 라이브러리 코드를 식별하고 앱에서 그 코드만 삭제할 수 있습니다. 자세히 알아보려면 코드 축소 방법에 관한 섹션을 참조하세요.
- 리소스 축소: 앱 라이브러리 종속 항목의 미사용 리소스를 포함하여 패키징된 앱에서 사용하지 않는 리소스를 삭제합니다. 리소스 축소는 코드 축소와 함께 사용하여 미사용 코드를 삭제하고 마찬가지로 더 이상 참조되지 않는 리소스도 안전하게 삭제할 수 있습니다. 자세히 알아보려면 리소스 축소 방법에 관한 섹션을 참조하세요.
- 난독화: 클래스와 멤버 이름을 줄여 DEX 파일 크기를 줄입니다. 자세히 알아보려면 코드 난독화 방법에 관한 섹션을 참조하세요.
- 최적화: 코드를 검사하고 다시 작성하여 앱 DEX 파일의 크기를 더 줄입니다. 예를 들어 주어진 if/else 구문의 else {} 분기가 전혀 사용되지 않음을 R8에서 감지한 경우 R8이 else {} 분기 코드를 삭제합니다. 자세히 알아보려면 코드 최적화 섹션을 참조하세요.
또한, 난독화 적용을 통해 소스코드를 디컴파일하여 노출 되었을 때 알아보기가 힘들어지므로, 보안성 측면에서도 이점이 있다고 생각한다.
Proguard 적용 방법
minifyEnabled를 true로 설정할 경우 shrinkResources, proguardFiles에 적용한 옵션이 실행되므로, Proguard 적용이 안되었다면 minifyEnabled를 잘 적용되었는지 확인해보자.
android {
buildTypes {
release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true
// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
shrinkResources true
// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
...
}
기본 Proguard 파일 확인해보기
Android Studio에서 프로젝트를 생성하면 기본적으로 proguard-android-optimize.txt 파일과 proguard-rules.pro 파일을 사용하게끔 기본적으로 코드가 생성되는데, 여기에는 어떤 룰이 적용되어 있는지 확인해보자.
proguard-android-optimize.txt 파일 확인해보기
아래의 코드가 proguard-android-optimize.txt 파일의 전문이다.
자세한 내용은 proguard 개발사인 guardSquare사의 공식 문서를 확인해보길 바란다. https://www.guardsquare.com/manual/configuration/usage
# This is a configuration file for ProGuard.
# <http://proguard.sourceforge.net/index.html#manual/usage.html>
#
# This file is no longer maintained and is not used by new (2.2+) versions of the
# Android plugin for Gradle. Instead, the Android plugin for Gradle generates the
# default rules at build time and stores them in the build directory.
# Optimizations: If you don't want to optimize, use the
# proguard-android.txt configuration file instead of this one, which
# turns off the optimization flags. Adding optimization introduces
# certain risks, since for example not all optimizations performed by
# ProGuard works on all versions of Dalvik. The following flags turn
# off various optimizations known to have issues, but the list may not
# be complete or up to date. (The "arithmetic" optimization can be
# used if you are only targeting Android 2.0 or later.) Make sure you
# test thoroughly if you go this route.
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify
# The remainder of this file is identical to the non-optimized version
# of the Proguard configuration file (except that the other file has
# flags to turn off optimization).
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
# For native methods, see <http://proguard.sourceforge.net/manual/examples.html#native>
-keepclasseswithmembernames class * {
native ;
}
# keep setters in Views so that animations can still work.
# see <http://proguard.sourceforge.net/manual/examples.html#beans>
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# For enumeration classes, see <http://proguard.sourceforge.net/manual/examples.html#enumerations>
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
-keepclassmembers class **.R$* {
public static ;
}
# 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.**
# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}
-keepclasseswithmembers class * {
@android.support.annotation.Keep ;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep ;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep (...);
}
proguard-rules.pro 파일 확인해보기
모든 내용이 주석처리 되어 있으며, 첫번째 줄을 확인해보면 프로젝트에 특정 프로가드 룰을 여기서 적용하라는 글을 볼 수 있다. 따라서, 기본적으로 적용된 proguard 룰이 아닌 특정 proguard rule을 직접 설정 할 경우 여기에 작성하면 될 것으로 보인다.
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# <http://developer.android.com/guide/developing/tools/proguard.html>
# 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 *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Proguard 적용 전, 후 비교해보기
위에서 Proguard를 적용했을때 어떤 이점이 있었는지 확인해볼 수 있었다. 그렇다면 실제 proguard가 적용되기 전, 후의 차이점을 실제로 비교해보자.
테스트 환경
- 필자의 개인 Android 프로젝트인 compose-coktail-recipes 앱을 대상으로 한다. 비교대상은 아래와 같다.
- proguard를 적용한 release 빌드 apk
- proguard를 적용하지 않은 release 빌드 apk
- 빌드한 apk를 dex2jar 툴을 사용하여 디컴파일 후 JD-GUI를 통해 소스코드 확인.
파일 크기 비교
위의 테스트 환경으로 빌드한 apk파일 크기를 비교해보니 아래와 같았다.
proguard를 적용한 빌드의 경우 2.5MB, 적용하지 않은 빌드의 경우 7.1MB로 크기가 약 3분의 1 가량 줄어든 것을 확인할 수 있었다.
디컴파일된 소스코드 비교
proguard 적용되지 않은 apk
proguard를 적용하지 않은경우에는 소스코드의 계층구조와 파일명, 변수명까지 모두 확인할 수 있었다. 만약 해당 데이터 클래스를 http 통신을 하는데에 사용한다면, 중요한 값이 이렇게 난독화 없이 노출된다면 보안성에 위협이 있을것으로 판단된다.
proguard 적용된 apk
패키지명이 모두 난독화되어 알아보기 힘들게 되었다. 또한 클래스 이름과 변수명도 모두 난독화되어 소스코드를 분석하기 굉장히 어려워졌다.
정리
Proguard를 통해 리소스 축소 및 난독화를 적용하고, 그 결과를 눈으로 직접 확인하여 난독화의 중요성을 알 수 있었다. 추가로 Proguard를 라이브러리 형태로 배포할 때에는 주의해야 할 사항들이 몇가지 있는데, 기본적으로 별다른 rule을 추가하지 않고 난독화를 적용할 경우 모든 파일명이 a,b,c.. 와 같은 알파벳으로 설정되는데, 만약 이렇게 패키지명이 기본 설정으로 된 라이브러리가 2개이상 있을경우, 패키지명 충돌로 인하여 라이브러리를 찾지 못하는 경우가 있으니 이점을 주의해줘야 한다.
Reference
https://developer.android.com/studio/build/shrink-code?hl=ko
'Android' 카테고리의 다른 글
Flow 재요청 버튼 활용하기 (0) | 2023.07.11 |
---|---|
A problem occurred configuring root project 'Unscramble'.> Could not resolve all files for configuration ':classpath'. 오류해결 (0) | 2023.06.08 |
Android AppBar(Toolbar) Menu에 Lottie 사용하기 (0) | 2022.12.19 |
kotlin data class redeclaration error 해결하기 (0) | 2022.12.10 |
manifestPlaceholders가 값을 찾지 못할 때 해결방법 (0) | 2022.11.27 |