Download presentation
Presentation is loading. Please wait.
Published byMarjory Booker Modified over 7 years ago
1
Ivan Kinash, Mikhail Dudarev Licel Corporation JavaOne 2015
Protecting Java Bytecode from Hackers with the InvokeDynamic Instruction Ivan Kinash, Mikhail Dudarev Licel Corporation JavaOne 2015
2
About us Licel Corporation Web:
3
Java Bytecode High level JVM instruction set Stack oriented
Instruction format Example: opcode operand 1 operand 2 … invokevirtual (0xb6) index1 index2
4
Native Code vs Java Bytecode
C-code: main(){ printf("Hello JavaOne 2015\n"); } Java-code: public class Test { public static void main(String[] args) { System.out.println("Hello JavaOne 2015"); }
5
Native Code Dump vs ByteCode Dump _main pushq %rbp movq %rsp, %rbp
subq $0x10, %rsp ## "Hello JavaOne'2015\n" leaq x37(%rip), %rdi movb $0x0, %al ## symbol stub for: _printf callq x100000f66 movl $0x0, %ecx movl %eax, -0x4(%rbp) movl %ecx, %eax addq $0x10, %rsp popq %rbp retq public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V Flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=1, args_size=1 // Field java/lang/System.out:Ljava/io/PrintStream; 0: getstatic #2 // String Hello JavaOne 2015 3: ldc #3 // Method // java/io/PrintStream.println:(Ljava/lang/String;)V 5: invokevirtual #4 8: return
6
Java Bytecode Dump (structured)
0: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc # // String Hello JavaOne 2015 5: invokevirtual # // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return opcode operands constant pool values getstatic Field#2 java/lang/System.out:Ljava/io/PrintStream; 3 ldc String#3 Hello JavaOne 2015 5 invokevirtual Method#4 java/io/PrintStream.println:(Ljava/lang/String;)V 8 return
7
Java Bytecode Decompiler
Original code with lamdas: public static void main(String[] args) { Runnable r = () -> System.out.println("Hello JavaOne 2015"); r.run(); } Bytecode dump 0: invokedynamic #2,0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 5: astore_1 6: aload_1 7: invokeinterface #3, 1 // InterfaceMethod java/lang/Runnable.run:()V 12: return
8
Java Bytecode Decompiler
Launching Procyon: $ java –jar procyon.jar HelloJavaOneWithLamdas.class Procyon output: public static void main(String[] args) { Runnable r = () -> System.out.println("Hello JavaOne 2015"); r.run(); }
9
Java Bytecode Attacks Reverse engineering Bypassing critical routines
Easy decompilation and modification
10
Popular Java Decompilers
Rating Java 8 support Anti-obfuscation techniques GUI Procyon + Luyten 8/10 Yes CFR 7/10 - JD 6/10 Fernflower 5/10 Yes (Minecraft support) Krakatau Candle 4/10
11
Java Bytecode Protection
Codeflow obfuscation Name obfuscation Call hiding
12
Obfuscator/Decompiler Demo
TECHNIQUES SHOWN HERE ARE FOR EDUCATIONAL PURPOSES ONLY. WE DO NOT ASSUME ANY RESPONSIBILITY FOR ANY UNLAWFUL ACTIONS AND/OR DAMAGES RESULTING FROM THE USE OF THESE TECHNIQUES.
13
Constant Pool & Bytecode
#1 = Methodref #6.# // java/lang/Object."<init>":()V #2 = Fieldref #16.# // java/lang/System.out:Ljava/io/PrintStream; #3 = String # // Hello JavaOne 2015 #4 = Methodref #19.# // java/io/PrintStream.println:(Ljava/lang/String;)V public static void main(java.lang.String[]); Code: 0: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc # // String Hello JavaOne 2015 5: invokevirtual # // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return
14
Bytecode Execution stack java/lang/System.out 1 Current instruction:
java/lang/System.out 1 Current instruction: 0: getstatic #2 Console: opcode operands constant pool values getstatic Field#2 java/lang/System.out:Ljava/io/PrintStream; 3 ldc String#3 Hello JavaOne 2015 5 invokevirtual Method#4 java/io/PrintStream.println:(Ljava/lang/String;)V 8 return
15
Bytecode Execution stack “Hello JavaOne 2015” 1 java/lang/System.out
“Hello JavaOne 2015” 1 java/lang/System.out Current instruction: 3: ldc #3 Console: opcode operands constant pool values getstatic Field#2 java/lang/System.out:Ljava/io/PrintStream; 3 ldc String#3 Hello JavaOne 2015 5 invokevirtual Method#4 java/io/PrintStream.println:(Ljava/lang/String;)V 8 return
16
Bytecode Execution stack “Hello JavaOne 2015” 1 java/lang/System.out
“Hello JavaOne 2015” 1 java/lang/System.out Current instruction: 5: invokevirtual #4 Console: Hello JavaOne 2015 opcode operands constant pool values getstatic Field#2 java/lang/System.out:Ljava/io/PrintStream; 3 ldc String#3 Hello JavaOne 2015 5 invokevirtual Method#4 java/io/PrintStream.println:(Ljava/lang/String;)V 8 return
17
Notes #1 Constant pool contains all the symbolic information
invoke* instruction is used to call a method JVM requires the stack to be consistent before method execution
18
Call Hiding via Reflection
// Hide Fieldref #2 Object out = Class.forName("java.lang.System").getField("out").get(null); // Hide Methodref #4 Class.forName("java.io.PrintStream"). getMethod("println",new Class[]{String.class}). invoke(System.out, new Object[]{"Hello JavaOne 2015"})
19
Call Hiding via Reflection
$ javac HelloJavaOneWithReflection.java $ javap -c -v HelloJavaOne.class | grep 'Methodref\|Fieldref’ | grep ’System.out\|println’ >#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream; #4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V $ javap -c –v HelloJavaOneWithReflection.class | grep 'Methodref\|Fieldref’ | grep ’System.out\|println’ >
20
Call Hiding via Reflection
Constant pool: #1 = Methodref #6.# // java/lang/Object."<init>":()V #2 = Fieldref #16.# // java/lang/System.out:Ljava/io/PrintStream; #3 = String # // Hello JavaOne 2015 #4 = Methodref #19.# // java/io/PrintStream.println:(Ljava/lang/String;)V
21
Call Hiding via Reflection
Pros MethodRef and FieldRef are removed from ConstantPool Cons Performance Bytecode size overhead The need for boxing/unboxing args and return value Call super.superMethod(…) is not possible Security breach when calling/accessing private methods/fields
22
Classic invoke* Instructions
Math.random(); // Call static method No dispatch invokestatic java/lang/Math.random:()D System.out.println("Hello JavaOne 2015"); // Call virtual method Single dispatch via table invokevirtual java/io/PrintStream.println:(Ljava/lang/String;)V it.hasNext(); // Call interface method Single dispatch via search invokeinterface java/util/Iterator.hasNext:()Z new StringBuilder(); // Call special method No dispatch invokespecial java/lang/StringBuilder."<init>":()V
23
InvokeDynamic (JSR-292) Features of dynamic languages in Java Platform
Shipped in Java 7 The basic building block for a lot of new Java features, such as Lambdas Invokedynamic: Deep Dive. Vladimir Ivanov Hot Spot JVM Compiler, Oracle
24
bytecode + bootsrap method method handles invoke dynamic
25
InvokeDynamic Execution
Bytecode ConstantPool ….. 2 invokedynamic #0:#22 … 18 BoostrapSection 19 CallSite LambdaMetafactory.metafactory(..) 20 1 MethodHandle 21 MethodType 1.Resolving the bootstrap method 0: invokedynamic #2,0 3.Linking 2.Executing Bootstrap method with arguments CallSite
26
bootstrap method CallSite CallSite1 MutableCallSite ConstantCallSite
MethodHandles.Lookup String callerName CallSite1 MethodType callerType bootstrap method arg4 MutableCallSite arg255 ConstantCallSite MutableCallSite1
27
Call Hiding via InvokeDynamic
Plan Generate bootstrap method Replace invokevirtual/invokeinterface/invokestatic instructions with invokedynamic
28
Bootstrap Method - Signature
private static Object bootstrap$0( MethodHandles.Lookup lookup, String callerName, MethodType callerType, int originalOpcode, String originalClassName, String originalMethodName, String originalMethodSignature) User-defined params
29
Bootstrap Method - Structure
MethodHandle mh = null; try { // variables initialization Class clazz = Class.forName(originalClassName); ClassLoader currentClassLoader = BootstrapMethodTemplate.class.getClassLoader(); MethodType originalMethodType = MethodType.fromMethodDescriptorString(originalMethodSignature, currentClassLoader); // lookup method handle …… mh = mh.asType(callerType); } catch (Exception ex) { throw new BootstrapMethodError(); } return new ConstantCallSite(mh);
30
Lookup MethodHandle switch (originalOpcode) {
case 0xB8: // invokestatic opcode mh = lookup.findStatic(clazz, originalMethodName, originalMethodType); break; case 0xB6: // invokevirtual opcode case 0xB9: // invokeinterface opcode mh = lookup.findVirtual(clazz, originalMethodName, originalMethodType); default: throw new BootstrapMethodError(); }
31
org.objectweb.asm Library for low-level bytecode manipulation
Visitor API (same as SAX) Tree API (same as DOM)
32
Visitor API ClassVisitor MethodVisitor FieldVisitor visitMethod
visitField MethodVisitor FieldVisitor visitAnnotation visitEnd visitCode visit*Insn visitEnd visitAttribute
33
HelloWorld in ASM mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("Hello JavaOne 2015"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mv.visitInsn(RETURN); mv.visitMaxs(2, 1); mv.visitEnd();
34
ASMifier in Action Source code: Bytecode: javac
public class Test { public static void main(String[] args) { System.out.println("Hello JavaOne 2015"); } 0: getstatic #2 3: ldc #3 5: invokevirtual #4 8: return javac org.objectweb.asm.util.ASMifier compile & run ASMifier output: mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("Hello JavaOne 2015"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mv.visitInsn(RETURN); mv.visitMaxs(2, 1); mv.visitEnd();
35
BootstrapMethodGenerator.java String bootstrapMethodName = "bootstrap$0"; String bootstrapMethodSignature = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"; MethodVisitor mv = cv.visitMethod(ACC_PRIVATE + ACC_STATIC, bootstrapMethodName, bootstrapSignature, null, null, true, true); mv.visitCode(); Label l0 = new Label(); Label l1 = new Label(); mv.visitTryCatchBlock(l0, l1, l0, "java/lang/Exception"); mv.visitInsn(ICONST_0); mv.visitVarInsn(ISTORE, 13); mv.visitInsn(ACONST_NULL); …..
36
Call Hiding via InvokeDynamic
Plan ✔ Generate bootstrap method Replace invokevirtual/invokeinterface/invokestatic instructions with invokedynamic
37
MethodVisitor class MethodIndyProtector extends MethodVisitor implements Opcodes { Handle bootsrapMethodHandle = null; public MethodIndyProtector(MethodVisitor mv, String className) { super(ASM4, mv); bootsrapMethodHandle = new Handle(Opcodes.H_INVOKESTATIC, className, bootstrapMethodName, bootstrapMethodSignature); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { // replace invokestatice/invokevirtual, invokeinterface instructions
38
Replace invoke* Instructions
@Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf ) { // generate a newName and a generic newSig (String.class->Object.class, ..) // … switch(opcode){ case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKESINTERFACE: mv.visitInvokeDynamicInsn(newName, newSig, bootsrapMethodHandle, opcode, owner, name, desc); default: mv.visitMethodInsn(opcode, owner, name, desc, itf); }
39
Result – Call Hiding via InvokeDynamic
JD-GUI:
40
Result – Call Hiding via InvokeDynamic
$ java -jar procyon.jar indyp/HelloJavaOne.class public static void main(final String[] array) { } // invokedynamic( :(Ljava/lang/Object;Ljava/lang/Object;)V, System.out, "Hello JavaOne 2015")
41
Result – Call Hiding via InvokeDynamic
$ java -jar cfr.jar indyp/HelloJavaOne.class public static void main(String[] arrstring) { LHelloWorld;.bootstrap$0(182, "java.io.PrintStream", "println", "(Ljava/lang/String;)V", System.out, "Hello JavaOne 2015"); }
42
Result – Call Hiding via InvokeDynamic
Procyon output after Stringer Java Obfuscator: public static void main(final String[] array) { } // invokedynamic(qBtSrvLX:(Ljava/lang/Object;Ljava/lang/Object;)V, o, HelloJavaOne$1.F("\u21cc\u2058\u1a61\u351e\uff1d\u30d4\u34c0\u3539\ua8c3\uaf0d\ub61a\ua5f7\uecc2\ub638\ue7fb\uf02c\u56df\u5333"))
43
Conclusion Protection for all classic invoke* instructions
No performance impact (excellent JVM optimization for InvokeDynamic) Battle tested in our products
44
Our Products Stringer Java Obfuscator (java bytecode)
Used for large scale Java EE production systems financial institutions, payment systems End user thin Java clients - 3M+ users JavaFX support DexProtector (dalvik bytecode) Several Thousand licenses sold Thousands of protected applications Millions of end users using DexProtected apps
45
Contact Demo project: Licel Corporation Web:
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.