BCEL vs ASM
BCEL 과 ASM 은 모두 Java 바이트 코드를 컨트롤 하기 위해 탄생했다.
덤으로 이 둘다 바이트 코드를 분석하는데 도구로 사용될 수도 있다.
다른점이라면 BCEL은 Apache 의 것이고 ASM은 Objectweb 것이라는 것이
다른점이다.
원래 목적은 메소드 안에서 시테틱한 클래스 메소드를 콜하고 그 메소드에 인자가
무엇인지 알아내는데 그 목적을 두고 테스트 하였다.
public Map a(Map a){
a.b.c.doCall("SSSS",map);
}
그렇다. 저 "a.b.c.doCall" 과 "SSSS" 를 찾는것이 목적이었다. 물론 이것은 그냥 정의될 수도 있고.
메소드 안에 로컬변수 혹은 멤버변수로 정의될 수도 있다.
이런 어떤경우라도 찾는것이 그 목적이었다.
결론적으로 BCEL과 ASM 은 모두 저 값을 찾을 수 있다. 그러나 정확하게 찾는것은 무리다.
API 수준에서는 찾기는 힘들고 더 바이트 수준으로 들어가야 한다.
즉 API 수준으로는 근사치를 찾을 수 있다는 말이다.
다른점은 BCEL은 API 수준으로 직접적으로 찾을 수 없는게 차이점이다.
목적을 정확하게 하는것이 중요할 듯 하다.
그 목적에 맞게 코드를 생성하기 위한 것이라면 이 둘중 어느것을 사용해도
무방할듯 하다.
그러나 코드 분석을 위해서라면 ASM 을 사용하기를 권장한다.
ASM이 일단은 빠르고, API 수준으로 본다면 좀더 상세하다고 할 수 있다.
또한 Annotation 을 사용한다면 ASM을 해야 한다. BCEL에는 Annoation 은 찾을 수 없었다.
중요한것은 코드 분석시 모든 상세한 수준까지 들어가는 것은 무리고 스키마 수준 과 Annotation을
분석하는 도구로는 꾀 괸찬다.
BCEL 5.2 , ASM 3.3.1 을해 봤을시 일단 ASM의 승리다.
출처 - http://blog.naver.com/junsu60?Redirect=Log&logNo=80123035668
=============================================================================================
ASM - 자바 바이트코드 분석하기
전부터 자바소스코드를 읽어들여 분석할 수 있는 방법이 없을까 하는 생각을 많이 했었다.
그러려면 거의 자바 컴파일러 수준이 되어야 할 것 같아서 엄두를 못 내다가 얼마전에 자바 바이트코드를 다룰 수 있는 라이브러리가 있다는 사실을 알게 되었다. 대표적인 것이 아파치의 BCEL과 지금 살펴 보려고 하는 ASM이다.
근데 이런 게 있다는 걸 알긴 했는데 도무지 사람들이 관심이 없어서인지 쓸만한 참고자료 찾기가 너무 힘들었다.
결국 ASM 사이트에서 이것저것 파일을 다운받아서 보다보니 내가 딱 원하는 기능의 예제까지 찾아낼 수가 있었다.
일단, BCEL(Byte Code Engineering Library)과 ASM은 자바소스파일을 컴파일해서 얻은 클래스파일(바이트코드)을 읽어 들여 변경하거나 분석하는 데 사용하는 라이브러리다. 자바소스파일 같은 경우는 코드상에 문제가 있을 수 있으니까 이걸 분석하는 건 문제가 있을 가능성이 있지만, 클래스파일은 자바소스코드상에 오류가 없어야 컴파일을 통해 만들 수 있는 것이므로 클래스파일을 분석하는 것이 보다 신뢰성 있는 분석결과를 얻을 수 있지 않겠는가? (나는 아직 클래스파일을 동적으로 변경하거나 이런 건 별로 관심이 없다 보니...)
여기저기 자료를 찾다 보니 BCEL보다 ASM이 속도가 빠르다고 해서 나는 ASM을 써 보기로 결정했다.
1. 라이브러리 준비
http://asm.objectweb.org/download/index.html에 가서 ASM 관련 파일을 다운받는다.
여기서는 asm관련 모든 패키지가 들어 있는 asm-all-3.1.jar를 사용하는데, 그러려면 asm-3.1-bin.zip을 다운받아서 압축을 풀면 그 안의 lib/all에 이 파일이 있다.
이 파일 외에도 다른 가이드나 예제 같은 것이 많으니 필요에 따라 함께 다운받으면 된다.
2. 분석프로그램 생성
※ 이 클래스가 컴파일되려면 컴파일할 때 클래스패스에 1에서 받은 asm-all-3.1.jar를 등록시켜야 한다.
[code java] import java.util.List; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; public class ClassInfo implements Opcodes { public static void main(final String[] args) throws Exception { ClassReader cr = new ClassReader("test.Test"); ClassNode cn = new ClassNode(); cr.accept(cn, ClassReader.SKIP_CODE); System.out.println("Class Name : " + cn.name + "\n"); System.out.println("Super Class : " + cn.superName + "\n"); System.out.println("Interfaces :"); List interfaces = cn.interfaces; for (int i = 0; i < interfaces.size(); i++) { System.out.println(interfaces.get(i)); } System.out.println("\nMethods :"); List methods = cn.methods; for (int i = 0; i < methods.size(); ++i) { MethodNode method = (MethodNode) methods.get(i); System.out.println(method.name + method.desc); } } } [/code]
ClassInfo.java
코드설명 :
9: 분석하려는 클래스의 패캐지를 포함한 full-name을 인자로 ClassReader 객체를 생성한다.
여기서 읽기 위해서는 분석하려는 클래스가 클래스패스 경로 안에 반드시 있어야 한다.
12: 아마 클래스파일을 파싱하는 작업이 아닐까 생각이 된다.(아직 잘 몰라서 그만...)
14: 클래스 이름 출력
16: 클래스가 상속한 클래스 출력
18~22: 클래스가 구현한 인터페이스 리스트 출력
24~29: 클래스 내의 메소드 정보 출력
3. 분석 대상 클래스를 생성 및 컴파일
위의 ClassInfo 클래스가 test.Test라는 클래스를 분석하려고 하는데 그러려면 아래와 같이 미리 Test를 만들어서 컴파일해서 클래스파일이 위의 클래스가 실행하는 클래스패스 안에 들어가 있어야 한다.
[code java] package test; import java.io.Serializable; import java.util.HashMap; public class Test extends HashMap implements Serializable { public static String str; static { str = "abced"; } public Test() { super(); } public static void main(String[] args) throws Exception { String str = "abc.dfd.efef.fjskdl"; System.out.println(str.replace(".", "/")); Test test = new Test(); String msg = "message to be printed"; int count = 5; double d = 1.3; float f = 1.3f; long l = 170343702; HashMap map = new HashMap(); test.printMsg(msg, count, d, f, l, map); } public void printMsg(String msg, int count, double d, float f, long l, HashMap map) { for (int i = 0; i < count; i++) System.out.println(msg); } public String getString(int i, String str) { return "abc"; } } [/code]Test.java
4. 분석 실행 및 결과 확인
이렇게 한 다음 ClassInfo 클래스를 실행하면 아래와 같은 결과가 나온다.
[code] Class Name : test/Test Super Class : java/util/HashMap Interfaces : java/io/Serializable Methods : <clinit>()V <init>()V main([Ljava/lang/String;)V printMsg(Ljava/lang/String;IDFJLjav getString(ILjava/lang/String;)Ljava [/code]
클래스나 수퍼클래스, 인터페이스는 무슨 내용인지 바로 알겠는데, 메소드 부분은 바로 안 들어온다.
짧은 지식으로 대강 설명을 드리자면,
9: <clinit>는 클래스의 static 블럭. ()는 인자가 없음. V는 void로 리턴타입이 없다는 뜻
10: <init>는 constructor. 나머지는 위와 동일
11: main은 main 메소드.[Ljava/lang/String;에서 [로 시작하니까 배열이고, L로 시작하니까 자바클래스일 것 같고, V니까 void
다 합치면 void main(String[]) 이런 형태가 되려나?
12: 인자에서 L로 시작하는 String과 HashMap은 감이 오는데, IDFJ는 뭔가?
Test.java 소스를 보면서 맞추면 I는 int, D는 double, F는 float, J는 long가 될 것 같네.
primitive type은 바이트코드에서 한글자로 줄여서 표현하는 모양이다.
13: 맨 마지막이 V가 아니고 Ljava/lang/String; 이니까 String을 리턴하는 메소드로군...
이렇게 해서 간단하게 클래스파일에서 간단한 정보를 뽑아 보는 방법을 알아 보았다.
여기서 Test.java를 클래스패스에 연결이 영 안 되는 분은 ClassInfo.java의 8라인에 "test.Test" 대신 "java.lang.String"을 입력해서 실행해 보면 String 클래스의 정보가 쭉 나오는 것을 확인할 수 있다.
클래스파일을 이용한 분석의 좋은 점은 JSP 파일을 분석할 수도 있다는 것이다.
JSP를 실행하려면 웹컨테이너가 java파일로 변환하고, 이걸 다시 클래스파일로 컴파일해서 실행하는데 JSP를 실행하고 나면 웹컨테이너 디렉토리 어딘가에 클래스파일이 남아 있다. 그러니까 이 경로만 찾아서 적당히 클래스패스로 잡아 주면 JSP도 분석이 가능한 것이다.
참고로 톰캣의 경우는 {Tomcat_Root}/work/Catalina/localhost 디렉토리 아래에 생기니 클래스패스 잘 잡아서 테스트해 보는 것도 재미있을 수 있겠다.
사실, 이 정도 정보 외에도 메소드 내의 각 코드 라인별로도 분석이 가능하다.
그런데 이 정도까지 가게 되면 거의 어셈블리 언어 수준까지 읽을 수 있는 능력이 필요하다.
(ASM이 무엇의 약자인지 아무리 해도 못 찾겠는데, 내 생각에는 ASM이 어셈블리의 약자가 아닌가 하는 생각도 든다.)
나도 학교 다닐 때 보던 기억을 더듬어 어느정도 읽어내긴 했지만 그런 걸 모르는 사람은 거의 암호 수준일 것 같다.
모 어려운 것도 있고, 호기심이 발동한 분들이 열심히 공부하길 바라는 마음도 있고, 이걸 보면서 머리 속에 굉장한 아이디어가 떠오른 것도 있고 해서 더 이상 심화된 내용은 다루지 않아야겠다.
출처 -
http://whiteship.tistory.com/2624
Developer(s) | OW2 Consortium |
---|---|
Stable release | 4.0 / October 29, 2011 |
Written in | Java |
Operating system | Cross-platform |
Type | bytecode Engineering Library |
License | BSD License |
Website | http://asm.objectweb.org/ |
The ASM library is a project of the ObjectWeb consortium. It provides a simple API for decomposing, modifying, and recomposing binary Java classes (i.e. bytecode). The project was originally conceived and developed by Eric Bruneton. ASM is Java-centric at present, and does not currently have a backend that exposes other bytecode implementations (such as .NET bytecode, Python bytecode, etc.).
Contents[hide] |
[edit]Uses
ASM provides a simple library that exposes the internal aggregate components of a given Java class through its visitororiented API. ASM also provides, on top of this visitor API, a tree API that represents classes as object constructs. Both APIs can be used for modifying the binary bytecode, as well as generating new bytecode (via injection of new code into the existing code, or through generation of new classes altogether.) The ASM library has been used in several diverse applications, such as:
- Performance and Profiling
- Instrumentation calls that capture performance metrics can be injected into Java class binaries to examine memory/coverage data. (For example, injecting instrumentation at entry/exit points.)
- Implementation of New Language Semantics
- For example, Groovy uses ASM to generate its bytecode. Also, Aspect-Oriented additions to the Java language have been implemented by using ASM to decompose class structures for point-cut identification, and then again when reconstituting the class by injecting aspect-related code back into the binary. (See: AspectWerkz)
[edit]Invokedynamic
Since version 3.2, ASM has added support for the new invokedynamic
code, which allows method invocation relying on dynamic type checking on the latest JDK 7 binaries, thus easing support for dynamically typed languages.[1][2]
[edit]See also
'Development > Java' 카테고리의 다른 글
java - slf4j (0) | 2012.03.24 |
---|---|
서블릿 load-on-startup (0) | 2012.03.21 |
패키지 이름 정의 (0) | 2012.03.18 |
인터페이스(interface) (0) | 2012.03.18 |
DAO, DTO, VO (2) | 2012.03.16 |