| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- 운영체제
- 객체지향
- spring security
- Python
- 알고리즘
- 스프링 시큐리티
- BOJ
- 최소 신장 트리
- java
- CS
- 파이썬
- Reflection
- Spring
- test
- 리플렉션
- MST
- 스프링
- 다이나믹 프록시
- 자바
- Junit5
- 백준
- 모던 자바 인 액션
- 약수
- 프록시
- 문자열
- proxy
- Deadlock
- 모던자바
- OS
- redis
- Today
- Total
Dev 달팽이 @_''
Java - 바이트코드 조작 본문
Java - 바이트코드 조작
모자에서 토끼를 꺼내는 마술
Moja.java
public class Moja {
public String pullOut(){
return "";
}
}
Masulsa.java
public class Masulsa {
public static void main(String[] args) {
System.out.println(new Moja().pullOut());
}
}
Masulsa를 실행하면 아무것도 없는 빈 문자열이 출력될 것이다. 하지만 여기서 우리는 Rabbit 이라는 문자열을 출력하도록 바이트코드를 조작하려고 한다.
바이트코드 조작 라이브러리
- ASM
- Javassist
- ByteBuddy
여기서 우리는 ByteBuddy를 사용한다.
바이트 버디를 사용하기 위하여 pom.xml에 dependency를 추가한다.
ByteBuddy : https://bytebuddy.net/#/
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>LATEST</version>
</dependency>
다음은 ByteBuddy를 이용하여 Moja 클래스를 재정의 해준다.
public class Masulsa {
public static void main(String[] args) throws IOException {
new ByteBuddy().redefine(Moja.class)
.method(ElementMatchers.named("pullOut")).intercept(FixedValue.value("Rabbit!"))
.make().saveIn(new File("C:\\Users\\Jang\\Desktop\\더자바_코드를 조작하는 다양한 방법\\target\\classes\\"));
// System.out.println(new Moja().pullOut());
}
}
이 때, 기존에 있던 프린트문은 주석처리를 해준다. 그 이유는 클래스가 재정의 되기 전에 프린트문에 의해 Moja 클래스가 클래스 로더에 의해 로드되기 때문이다.
다음으로 이를 실행시키고 재정의 부분을 주석처리, 프린트문의 주석을 제거하여 실행시키면 토끼를 꺼낼 수 있다.

위에 코드는 클래스를 재정의하고 다시 실행시켜야 토끼를 꺼낼 수 있었다. 이번 방법은 javaagent를 이용하여 한번에 토끼를 꺼내는 방법이다.
Javaagent JAR 파일 만들기
새로운 프로젝트를 생성하여 MasulsaAgent.java클래스를 생성한다.
다음은 위와 같이 ByteBuddy를 pom.xml에 추가하고 build에 maven jar plugin을 추가한다.
maven jar plugin : https://maven.apache.org/plugins/maven-jar-plugin/examples/manifest-customization.html
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.10.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<index>true</index>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<mode>development</mode>
<url>${project.url}</url>
<key>value</key>
<Premain-Class>me.jdb4497.MasulsaAgent</Premain-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
premain 방식은 시작시 붙이는 방식, agentmain은 런타임 중 동적으로 붙이는 방식이다.
Javaagent 붙여서 사용하기
클래스 로더가 클래스를 읽어올 때 javaagent를 거쳐서 변경된 바이트 코드를 읽어드려 사용한다.
MasulsaAgent.java
public class MasulsaAgent {
public static void premain(String agentArgs, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.any())
.transform((builder, typeDescription, classLoader, javaModule) ->
builder.method(named("pullOut")).intercept(FixedValue.value("Rabbit!"))).installOn(inst);
}
}
이를 패키징해준다

Javaagent 적용
이제 기존의 Masulsa 코드로 돌아와서 Javaagent를 추가해준다.

프로젝트 configuration에 들어가서 VM options 에 jar 파일의 경로를 넣어준다.
-javaagent : C:\Users\Jang\Desktop\me.jdb4497\target\me.jdb4497-1.0-SNAPSHOT.jar
이 후 Masulsa를 실행하면 토끼가 꺼내지는 것을 볼 수 있다.

첫번째 방법과 두번째 방법의 차이
- 첫번째 방법은 클래스 파일 자체를 건드림
- 두번째 방법은 클래스 로딩할 때 일어나서 클래스 파일이 변경되지 않음.
- 읽어 올 때 일어나므로 메모리 내부가 바뀌어있음
바이트 코드 조작 예
- 프로그램 분석
- 코드에서 버그 찾는 툴
- 코드 복잡도 계산
- 클래스 파일 생성
- 프록시
- 특정 API 호출 접근 제한
- 스칼라 같은 언어의 컴파일러
- 그 밖에도 자바 소스 코드 건드리지 않고 코드 변경이 필요한 여러 경우에 사용할 수 있다.
- 프로파일러
- 최적화
- 로킹
- ...
- 스프링이 컴포넌트 스캔을 하는 방법(ASM)
- 컴포넌트 스캔으로 빈으로 등록할 후보 클래스 정보를 찾는데 사용
- ClassPathScanningCandidateComponentProvider -> SimpleMetadataReader
- ClassReader와 Visitor 사용해서 클래스에 있는 메타 정보를 읽어옴
'Java&Spring > 더 자바, 코드를 조작하는 다양한 방법' 카테고리의 다른 글
| Java - 리플렉션(클래스 정보 수정 또는 실행) (0) | 2022.01.09 |
|---|---|
| Java - 리플렉션 API(클래스 정보 조회) (0) | 2022.01.09 |
| Java - 코드 커버리지 (0) | 2022.01.07 |
| Java - 클래스 로더 (0) | 2022.01.07 |
| Java - JVM 구조 (0) | 2022.01.06 |