소스코드 보기는 매우 간단하다.idea, eclipse 등 일반적으로 사용되는 일부 IDE에서 클래스 파일의 소스코드를 보는 기능을 제공하고 있다. 구현이 일관되고 런타임 시 소스 코드 상황보다 JVM에 더 가깝습니다.
때때로 우리는 사용 요구 사항을 충족시키기 위해 소스 코드를 수정해야 합니다.Java 코드에서 생성된 바이트 코드를 리소스로 사용하는 것은 비교적 간단합니다.한 가지 방법은 수정될 함수를 상속한 다음 다시 작성하는 것입니다.다른 방법은 직접 같은 이름의 클래스 파일을 생성하고 거기에 디컴파일된 소스 코드를 복사하고 수정 후 원래 jar 패키지의 클래스 파일을 새로 생성된 클래스 파일로 대체하지만 일부 바이트 코드는 다른 언어에서 생성되며 디컴파일된 파일은 Java 컴파일 구문을 충족하지 않으면 새 클래스 파일로 컴파일할 수 없습니다.이 경우 일반적인 방법은 바이트 코드를 직접 수정하여 이를 달성하는 것입니다.
인터넷에 바이트코드를 수정하는 글은 많이 있지만 대부분 상수 풀을 수정해서 다른 값을 출력하는 글입니다. 비교적 간단할 수 있지만 이 방법은 좀 더 복잡한 수업에서도 사용할 수 있으니, 필요시 의논할 메시지를 남겨주세요.
1. 자바 소스 코드
package com.zhanghao.test.jclasslib;
public class JclasslibTest {
public static void main(String[] args) {
int a = 1;
int b = 2;
printMin(a, b);
}
private static void printMin(int a, int b) {
int min = a <= b ? a : b;
System.out.println(min);
}
}
두 매개변수 중 더 작은 값 출력: 1
목표: 바이트코드를 수정하여 printMin 메서드 출력을 더 큰 값으로 만들기: 2
2. 클래스 파일의 디컴파일 결과
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.zhanghao.test.jclasslib;
public class JclasslibTest {
public JclasslibTest() {
}
public static void main(String[] args) {
int a = 1;
int b = 2;
printMin(a, b);
}
private static void printMin(int a, int b) {
int min = a <= b ? a : b;
System.out.println(min);
}
}
생성된 클래스 파일은 자바 소스 코드에 비해 기본 무인수 구성 방식만 추가된 것을 알 수 있다(데모가 비교적 간단하기 때문에 컴파일 최적화를 위한 다양한 전략을 반영하지 않음)
3. jclasslib 다운로드 및 설치
https://github.com/ingokegel/jclasslib/releases
4. jclasslib를 사용하여 클래스 파일 열기
바이트코드의 각 부분의 이름은 이미 명확하기 때문에 여기서는 너무 많은 설명을 하지 않겠습니다 수정해야 할 위치를 확인합니다 Methods->printMin printMin
메소드에 해당하는 바이트코드는 총 19줄입니다
0: 푸시 첫 번째 매개변수를 스택에 넣음(매개변수 a=1)
1: 두 번째 매개변수를 스택에 넣기(매개변수 b=2)
2: 스택 맨 위에 있는 두 int 값의 크기를 비교하고, 결과가 0보다 클 때 라인 9의 바이트코드 명령어(판정 조건이 해당 논리 블록에 들어감)
5: 첫 번째 매개변수를 스택에 푸시합니다(a 선택) 6: 무조건 라인 10
의 바이트코드 명령어로 점프합니다.
스택에 두 번째 매개변수(b 선택)
10: 스택의 맨 위에 int 값을 넣습니다 세 번째 로컬 변수에 저장(저장소 선택 결과 c)
11: 지정된 클래스의 정적 도메인을 획득하여 맨 위에 푸시합니다 스택(실행할 인스턴스 및 메서드 획득)
14: 세 번째 매개변수를 스택으로 푸시(put c
15: 인스턴스 메서드 호출
: 현재 메서드에서 void 반환. 자세한 바이트코드 지침은 가상 머신을
참조하십시오. 바이트 코드 명령 테이블
.가상 머신 바이트 코드 명령 테이블을 참조하십시오.pringMin 출력 매개 변수에서 더 큰 값을 얻으려면 세 번째 명령 if_icmpgt를 if_icmple로 변경하십시오.
니모닉 | if_icmpgt | if_icple |
---|---|---|
명령 의미 | 스택 상단에 있는 두 int 값의 크기를 비교하여 결과가 0보다 크면 점프 | 스택 상단에 있는 두 int 값의 크기를 비교하여 결과가 0보다 작거나 같으면 점프 |
바이트코드 | 0xa3 | 0xa4 |
부호 있는 십진수 | -93 | -92 |
5. 바이트코드 수정
package com.zhanghao.test.jclasslib;
import com.alibaba.fastjson.JSON;
import java.io.*;
import org.gjt.jclasslib.io.ClassFileWriter;
import org.gjt.jclasslib.structures.AttributeInfo;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.structures.MethodInfo;
import org.gjt.jclasslib.structures.attributes.CodeAttribute;
public class JclasslibModify {
public static void main(String[] args) throws Exception {
String filePath = "/Users/zhanghao/Desktop/jclasslib/JclasslibTest.class";
FileInputStream fis = new FileInputStream(filePath);
DataInput di = new DataInputStream(fis);
ClassFile cf = new ClassFile();
cf.read(di);
System.out.println(JSON.toJSONString(cf));
MethodInfo[] methodInfos = cf.getMethods();
MethodInfo methodInfo = methodInfos[2];
AttributeInfo[] attributeInfos = methodInfo.getAttributes();
CodeAttribute codeAttribute = (CodeAttribute) attributeInfos[0];
byte[] bytes = codeAttribute.getCode();
bytes[2] = -92;
fis.close();
File f = new File(filePath);
ClassFileWriter.writeToFile(f, cf);
}
}
클래스 파일을 읽으려면 디버그 또는 출력 json을 통해 ClassFile의 클래스 구조를 보고 클래스 구조에서 바이트 코드를 수정한 다음 파일을 다시 작성할 수 있습니다.
jclasslib.jar를 사용해야 합니다. 다운로드 링크: jclasslib.jar
6. 수정된 결과 보기
package com.zhanghao.test;
public class JclasslibTest {
public JclasslibTest() {
}
public static void main(String[] args) {
int a = 1;
int b = 2;
printMin(a, b);
}
private static void printMin(int a, int b) {
int min = a > b ? a : b;
System.out.println(min);
}
}
이 글은 바이트코드를 수정하는 방법에 대해서만 간략하게 설명한 것 뿐 실제 프로젝트에서 바이트코드를 수정해야 할 경우 상황이 더 복잡해지기 때문에 원래 바이트코드에서 해당 바이트코드 블록을 교체하고 다른 파일도 그에 따라 수정해야 합니다. , 상수 풀 등과 같은