코딩마을방범대

[java] ProGuard를 이용하여 war파일 난독화하기 본문

💡 백엔드/Java

[java] ProGuard를 이용하여 war파일 난독화하기

신짱구 5세 2023. 11. 23. 14:11

 

war 파일을 디컴파일 할 경우 모든 소스가 오픈되어 코드 탈취의 위험이 있다.

중요한 파일의 경우 디컴파일을 해도 알아볼 수 없게 난독화 하는 것이 권고된다.

 

디컴파일 프로그램 중에 ProGuard라는 프로그램이 있는데,

진짜 수많은 오류들을 겪었다..

참고로 나는 1번 방법인 gui로는 실패해서 gradle로 성공했다.

 

 

 

 


 

 

 

 

 

 

사용하기

 

 

1. gui 로 사용하기

 

1. ProGuard 다운받기

 

아래 사이트로 접속해서 'Download ProGuard' 를 클릭한다.

 

Java Obfuscator and Android App Optimizer | ProGuard

ProGuard is the most popular optimizer and obfuscator for Java bytecode and Android apps. Reduce your Java and Android apps up to 90% with ProGuard.

www.guardsquare.com

 

 

클릭 시 Git 페이지로 넘어가는데 README.md를 살짝 내려보면 

Quick Start 세션에 'GitHub releases' 링크를 클릭하면 된다.

 

 

 

최신 버전으로 다운로드하여 압축 해제 후 해당 폴더에서 터미널을 열어준다.

 

 

 


 

 

 

2. ProGuard 실행

 

다운로드 받았던 폴더에서 lib로 들어가면 jar 파일들이 쭈르륵 나온다.

여기서 gui 파일을 실행시켜주면 된다.

java -jar proguardgui.jar

 

 

 

 


 

 

 

3. jar 파일 생성

 

아래 화면에서 Load configuration.. 버튼을 클릭해서 직접 config 파일을 설정해줄 수도 있다.

config 파일은 'proguard.conf' 파일명으로 해서 적용시켜주면 된다.

 

 

 

 


 

 

 

Next

 

 

next를 눌러보면 rt.jar를 찾을 수 없다는 오류가 발생한다.

(안 뜰 수도 있지만, 실행시켜보면 오류가 발생할 수도 있다.)

그럴 경우 Edit..을 눌러 실제 경로로 제대로 지정해주면 된다.

 

자바 9이상 부터는 rt.jar가 사라져서 지원이 되지 않는다고 한다.
그럴 경우 아래 옵션을 추가해주면 된다고 하는데, 난 8버전을 사용해서 직접 실험해보진 않았다..

-libraryjars <java.home>/jmods/java.base.jmod(!**.jar;!module-info.class)

 

 

 

설정 설명
Add input.. 난독화할 war 파일
Add output.. 난독화한 파일 저장 위치
Filter... 파일명 jar,war,ear,zip 등의 이름을 필터로 걸러 난독 처리를 피함

 

 

 


 

 

 

Next

 

 

정확한 옵션들은 하기 참고

 

ProGuard Manual: Java/Kotlin Gradle | Guardsquare

How to set up ProGuard with Gradle for Java or Kotlin.

www.guardsquare.com

 

 

설정 설명
Shrink 사용하지 않는 클래스 및 메소드들을 삭제해주는 기능
(난 이 옵션을 적용할 경우 클래스가 모조리 날아가서 제외했다)
Keep - Libraries The output jar is empty. Did you specify the proper '-keep' options?
위 오류가 발생할 확률이 높으므로 체크
Also keep - Bean Classses 체크 해제 시 변수이름만 변경된다.

 

 


 

 

 

이후 계속 next를 눌러 Process! 를 클릭해주면 된다.

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

2. gradle로 사용하기

 

gradle은 git에 나와있는 그대로하면 IOException이 나거나 Mainfest를 찾을 수 없다는 둥의 오류가 발생한다..

진짜 오랜 테스트를 끝으로 아래와 같이 난독화하는 것을 성공했다. 

 

 

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.guardsquare:proguard-gradle:7.4.1'
    }
}

task copyClasses(type: Copy) {
    from "${buildDir}/classes/java/main"
    into "${buildDir}/obfuscated"
}

task obfuscate(type: proguard.gradle.ProGuardTask, dependsOn: ['compileJava', 'copyClasses']) {
    configuration 'proguard.conf'

    injars "${buildDir}/obfuscated"
    outjars "${buildDir}/obfuscated-out"
    libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
    libraryjars "${System.getProperty('java.home')}/lib/jsse.jar"
    libraryjars "${System.getProperty('java.home')}/lib/jce.jar"

    libraryjars configurations.runtimeClasspath
}

bootWar {
    dependsOn obfuscate
    archiveFileName = "${project.name}.war"
    mainClassName = 'com.fnsvalue.api.GccsApiApplication'
    classpath = classpath - files(sourceSets.main.output.classesDirs)
    from("${buildDir}/obfuscated-out") {
        into 'WEB-INF/classes'
    }
}

 

 

 


 

 

 

이후에 conf 파일을 build.gralde과 같은 위치에 저장해주면 된다.

(꼭 conf가 아니라 proguard.pro 여도 상관 없음!)

 

 

proguard.conf

-dontusemixedcaseclassnames
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,Synthetic,EnclosingMethod
-renamesourcefileattribute SourceFile
-dontnote
-overloadaggressively
-allowaccessmodification
-adaptclassstrings
-dontshrink

-keepclasseswithmembers public class * {
    public static void main(java.lang.String[]);
}

-keep @org.springframework.boot.autoconfigure.SpringBootApplication class *

 

 

 

 

그럼 이렇게 난독화된 war 파일을 만날 수 있다!!

 

 

 

 

 


 

 

 

그런데!!!

 

또 오류 발생!!!!!!!!!

 

1. 난독화 시 주의할 점은 클래스명이 겹쳐서 Bean 충돌이 나는 것이다.

 

[23-11-29 15:20:37.763][main][ERROR][o.s.b.SpringApplication.reportFailure()] Application run failed (SpringApplication.java:858) org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.project .api.GccsApiApplication]; nested exception is org.springframework.context.annotation.ConflictingBeanDefinitionException : Annotation-specified bean name 'a' for bean class [com.project .api.b.a.a.a] conflicts with existing, non-compatible bean definition of same name and class [com.project.api.a.a]

 

 

bean 이름이 겹쳐서 오류가 발생한다는거다...

그에 대한 해결책으로 bean 마다 이름을 정의해주는 방법이 있는데...

 

 

이렇게..

 

 

 

 

그래서 하나하나 정의해봤더니 이번엔 아래 오류가 발생한다...

 

Bean 생성 메소드가 private이나 final로 선언되어 있으면 안되고 public 또는 protected로 선언되어야 한다고 한다...

난독화 전에는 잘만 구동되는게 난독화 시에만 이렇게 오류가 나는게 말이 안된다!

 

[23-11-29 15:58:44.513][main][ERROR][o.s.b.SpringApplication.reportFailure()] Application run failed (SpringApplication.java:858)
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: @Bean method 'a' must not be private or final; change the method's modifiers to continue
Offending resource: class path resource [com/project/api/c/c/a.class]

 

 

 

그래서 conf파일에 아래 옵션을 추가해주었다.

 

-keep,allowobfuscation class ** { *; }

 

 

 


 

 

 

2. 난독화 시 주의할 점은 String 안에 들어간 패키지, 메소드명 등이다.

 

난독화 후 실행 시 매핑이 되지 않아 오류가 발생한다.

예를 들어 아래와 같은 코드가 있다고 가정할 때 수정 방법을 예시로 들어보았다. 

@Pointcut("within(com.project.api.controller.*..*)")
    public void onRequest() {
}

 

이게 기존 코드고, 아래와 같이 변경했다.

 

@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void onRequest() {
}

 

 

만일, 메소드명이 변경되지 않게 유지시키고 싶은 경우 아래 옵션을 추가하면 된다!

 

-keep class com.project.api.aop.AOP {
    void onRequest(...);
    void accessrole(...);
}

 

 

 

 

 


 

 

 

3. 난독화 시 주의할 점은  ConfigurationProperties 세팅이다.

 

yml에 있는 값들을 가져올 때 @Value가 아닌 Class로 생성하여 주입받는 방법이 있다.

예를 들어 아래 같은 경우다.

 

아래를 keep하지 않고 난독화 되게 둘 경우 변수명이 a로 바뀌어서 yml 변수명과 매칭이 되지 않는다.

따라서 null 값이 들어가서 오류가 발생한다.

@Data
@ToString
@Component("TESTSettings")
@ConfigurationProperties(prefix = "test")
public class TESTSettings {
    private String name;
}

 

 

conf 파일에 아래 옵션을 추가해준다.

 

-keep @org.springframework.boot.context.properties.ConfigurationProperties class * { *; }

 

{*;} 는 해당 클래스의 메소드와 필드들을 보호해준다.

 

 

 

 

 


 

 

 

 

 

 

 

 

 

💡 Proguard Conf TIPS!

 

 

1. 클래스의 전체 필드 또는 메소드를 난독화에서 제외하고 싶은 경우

 

-keep class com.project.api.common.TEST {
      <fields>;
      <methods>;
}

 

만약 class로 지정하는 것이 아닌, 어노테이션으로 지정하고 싶다면 아래와 같이 설정해주면 된다.

해당 패키지 내의 전체 클래스에 적용 시키고 싶은 경우는 아래처럼 ** 해주면 됨.

(class com.project.api.common.**)

-keep @org.springframework.boot.context.properties.ConfigurationProperties class * {
      <fields>;
      <methods>;
}

 

 

 


 

 

 

2. 클래스 내의 클래스를 난독화에서 제외하고 싶은 경우

 

package com.project.api.common;
public class Test(){
	public static class testClass(){}
}

 

위와 같은 클래스가 있다고 가정 하에 아래처럼 $ 표시 후 class 명을 적어주면 해당 클래스 내의 클래스만 난독화에서 제외할 수 있다.

-keep class com.project.api.common.TEST$testClass {
      <fields>;
      <methods>;
}

 

 

 


 

 

 

3. 클래스의 특정 필드 또는 메소드를 난독화에서 제외하고 싶은 경우

 

# 특정 클래스의 특정 필드만 난독화에서 제외
-keepclassmembers class com.example.MyClass {
      java.lang.String myField;
}

# 특정 클래스의 특정 메소드만 난독화에서 제외
-keepclassmembers class com.example.MyClass {
      # void myMethod(...);  ::  ...으로 매개변수 타입을 패스해도됨.
      void myMethod(java.lang.String);
}

 

 

 

 

 


참고사이트

Java 난독화 프로그램 - ProGuard(설치 및 사용법)

 

 

SMALL