[Java] 바이트코드 조작 ②

홍정완·2022년 7월 28일
0

Java

목록 보기
19/25
post-thumbnail

바이트코드 조작 ①에서는 바이트코드 조작 코드와 출력 코드를 같이 사용하게 되면 Rabbit 문자열이 출력되지 않았다.


  • 바이트코드 조작 코드가 실행되기 전에 Moja 클래스를 읽었기 때문이다.

이번 시간에는 바이트코드 조작 코드와 출력 코드를 동시에 실행시켜도 Rabbit 문자열이 출력 가능하게 해보자




문자열에서부터 참조


public class Masulsa {

    public static void main(String[] args) {
        ClassLoader classLoader = Masulsa.class.getClassLoader();
        TypePool typePool = TypePool.Default.of(classLoader);

        try {
            new ByteBuddy().redefine(
                            typePool.describe("me.whiteship.moja.Moja").resolve(),
                            ClassFileLocator.ForClassLoader.of(classLoader))
                    .method(named("pullOut")).intercept(FixedValue.value("Rabbit"))
                    .make().saveIn(new File("/Users/Hong/Desktop/java 8/HongJungWan-Java8-Java/target/classes/"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println(new Moja().pullOut());
    }
}

// 출력 Rabbit

바이트코드 조작 코드와 출력 코드를 동시에 실행시켜도 Rabbit 문자열이 출력 가능하다.


하지만 위 방법은 클래스 로딩 순서에 너무 의존적이라 다른 코드에서 Moja Class를 먼저 읽을 경우 의도대로 동작하지 않을 수 있다.




JavaAgent


클래스 로더가 클래스를 읽어올 때 javaagent를 거쳐서,
변경된 바이트코드를 읽어서 사용하는 방법으로 접근해 보자



1. 프로젝트 새로 생성



2. 의존성 추가

	<dependency>
        <groupId>net.bytebuddy</groupId>
        <artifactId>byte-buddy</artifactId>
        <version>1.10.1</version>
    </dependency>

Byte Buddy 의존성을 추가하자.



3. 플러그인 추가

	<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.2</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.whiteship.MasulsaAgent</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

Agent 클래스를 사용하기 위해 jar 파일 안에다가 특정한 값들을 넣어줘야 한다.
그러기 위해 Manifest를 조작할 수 있는 maven-jar-plugin을 플러그인에 추가하자.



4. MasulsaAgent Class

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);

    }
}

  • premain

    • 애플리케이션을 실행할 때 옵션으로 agent를 붙인다.



5. Package

mvn clean package



6. JVM Option 추가



7. 기존 Masulsa 실행

public class Masulsa {

    public static void main(String[] args) {

        System.out.println(new Moja().pullOut());
    }
}


클래스 로더가 클래스를 읽어올 때 javaagent를 거쳐 변경된 바이트코드를 읽어온 결과 Rabbit 문자열이 정상 출력된다.

profile
습관이 전부다.

0개의 댓글