I. Lời nói đầu
Trong chain CC2, Javasisst được sử dụng để xây dựng gadget chain. Do đó, trước khi debug chain CC2 tôi muốn hiểu Javasisst thật sự làm gì.
II. Giới thiệu về Javasisst
2.1. Javassist là gì?
Bytecode Java được lưu trữ dưới dạng nhị phân trong các file .class. Mỗi file .class chứa một class hoặc một interface Java.
Javassist (Java Programming Assistant) là thư viện Java dùng để đọc, tạo và chỉnh sửa bytecode của class Java ngay trong lúc chạy hoặc trước khi chạy ứng dụng.
Tóm gọn: thay vì sửa file .java rồi compile lại bằng javac, Javassist cho phép bạn can thiệp trực tiếp vào file .class.
Javasisst được ứng dụng trong:
- AOP, proxy và framework runtime.
- Instrumentation / Java Agent.
- Hook method để logging, profiling hoặc tracing.
- Sinh class động.
Trang chủ của Javasisst: https://www.javassist.org/
2.2. Một số concept quan trọng trong Javasisst
Javassist làm việc với các đối tượng biểu diễn cấu trúc class:
| Thành phần | Ý nghĩa |
|---|---|
ClassPool |
Kho chứa metadata của các class |
CtClass |
Đại diện cho một class |
CtMethod |
Đại diện cho một method |
CtField |
Đại diện cho một field |
CtConstructor |
Đại diện cho constructor |
Ct là viết tắt của Compile Time.
III. Cách sử dụng Javasisst
Ở đây tôi đi sâu hơn 1 chút về các concept quan trọng trong Javasisst
3.1. Một số concept quan trọng
a. ClassPool
ClassPool: Là một container chứa các đối tượng CtClass, được cài đặt bằng Hashtable.
- Key: tên class
- Value: đối tượng
CtClassđại diện cho class đó HashtablevàHashMapđều là các implementation củaMap.HashMapcó thể chứa giá trịnull, trong khiHashtablethì không
Các method thường dùng:
static ClassPool getDefault(): Trả về class pool mặc định.ClassPath insertClassPath(java.lang.String pathname): Thêm một thư mục hoặc file JAR (hoặc ZIP) vào đầu classpath tìm kiếm.ClassPath insertClassPath(ClassPath cp): Thêm một đối tượngClassPathvào đầu classpath.java.lang.ClassLoader getClassLoader(): LấyClassLoaderđược sử dụng cho các thao tác nhưtoClass(),getAnnotations()trongCtClass, v.v.CtClass get(java.lang.String classname): Đọc file class từ nguồn và trả về một đối tượngCtClassđại diện cho class đó.ClassPath appendClassPath(ClassPath cp): Thêm mộtClassPathvào cuối classpath.CtClass makeClass(java.lang.String classname): Tạo một class public mới.
b. CtClass
CtClass đại diện cho một class.
- Một đối tượng
CtClass(compile-time class) có thể thao tác trực tiếp trên file.class - Các đối tượng
CtClassnày được lấy thông qua các method củaClassPool.
Các method thường dùng:
void setSuperclass(CtClass clazz): Thay đổi superclass của class này (trừ khi nó là interface).java.lang.Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup): ChuyểnCtClassthành một đối tượngjava.lang.Class.byte[] toBytecode(): ChuyểnCtClassthành mảng bytecode của file class.void writeFile(): Ghi file class doCtClassđại diện ra thư mục hiện tại.void writeFile(java.lang.String directoryName): Ghi file class ra thư mục được chỉ định.CtConstructor makeClassInitializer(): Tạo một class initializer rỗng (static constructor).
c. CtMethod
CtMethod: Đại diện cho một method trong class.
d. CtConstructor
Một instance của CtConstructor đại diện cho một constructor. Nó cũng có thể đại diện cho static constructor (class initializer).
Các method thường dùng:
void setBody(java.lang.String src): Thiết lập phần thân (body) của constructor.void setBody(CtConstructor src, ClassMap map): Sao chép phần thân của một constructor khác.CtMethod toMethod(java.lang.String name, CtClass declaring): Sao chép constructor này và chuyển nó thành một method.
e. ClassClassPath
Class này được sử dụng như một search path để lấy file class thông qua getResourceAsStream() trong java.lang.Class.
Constructor của nó:
ClassClassPath(java.lang.Class<?> c)
Tạo một search path.
Các method thường dùng:
java.net.URL find(java.lang.String classname)
Lấy URL của file class được chỉ định.java.io.InputStream openClassfile(java.lang.String classname)
Lấy file class thông quagetResourceAsStream().
3.2. Ví dụ
1 | ClassPool pool = ClassPool.getDefault(); |
Lấy một đối tượng ClassPool bằng system search path mặc định.
Nếu cần chỉnh sửa class search path, sử dụng insertClassPath().
1 | pool.insertClassPath(new ClassClassPath(this.getClass())); |
Chèn đường dẫn chứa class hiện tại vào class search path.
toByCode
toBytecode() dùng khi muốn lấy raw bytecode,
1 | package com.demo; |
toClass
toClass dùng khi muốn load class đã bị chỉnh sửa vào JVM và thực thi luôn.
Class Hello:
1 | public class Hello { |
Test class:
1 | public class Test { |