Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Protecting Java Bytecode from Hackers with the InvokeDynamic Instruction Ivan Kinash, Mikhail Dudarev Licel Corporation JavaOne 2015 1 About us Licel Corporation Web: https://licelus.com 2 Java Bytecode • High level JVM instruction set • Stack oriented • Instruction format Example: opcode invokevirtual (0xb6) operand 1 index1 operand 2 index2 … 3 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"); } } 4 Native Code Dump vs ByteCode Dump _main pushq %rbp movq %rsp, %rbp subq $0x10, %rsp ## "Hello JavaOne'2015\n" leaq 0x37(%rip), %rdi movb $0x0, %al ## symbol stub for: _printf callq 0x100000f66 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 5 Java Bytecode Dump (structured) 0: getstatic #2 3: ldc #3 5: invokevirtual #4 8: return opcode // Field java/lang/System.out:Ljava/io/PrintStream; // String Hello JavaOne 2015 // Method java/io/PrintStream.println:(Ljava/lang/String;)V operands constant pool values 0 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 6 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 7 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(); } 8 Java Bytecode Attacks • Reverse engineering • Bypassing critical routines • Easy decompilation and modification 9 Popular Java Decompilers Decompiler Rating Java 8 support Anti-obfuscation techniques GUI Procyon + Luyten 8/10 Yes Yes Yes CFR 7/10 Yes Yes - JD 6/10 - - Yes Fernflower 5/10 - Yes (Minecraft support) - Krakatau 5/10 - Yes - Candle 4/10 - - 10 Java Bytecode Protection • Codeflow obfuscation • Name obfuscation • Call hiding 11 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. 12 Constant Pool & Bytecode Constant pool: #1 = Methodref #2 = Fieldref #3 = String #4 = Methodref #6.#15 #16.#17 #18 #19.#20 // java/lang/Object."<init>":()V // java/lang/System.out:Ljava/io/PrintStream; // Hello JavaOne 2015 // java/io/PrintStream.println:(Ljava/lang/String;)V public static void main(java.lang.String[]); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello JavaOne 2015 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return 13 Bytecode Execution stack Current instruction: 0: getstatic #2 0 1 Console: opcode java/lang/System.out operands constant pool values 0 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 14 Bytecode Execution stack Current instruction: 3: ldc #3 Console: opcode 0 “Hello JavaOne 2015” 1 java/lang/System.out operands constant pool values 0 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 Current instruction: 5: invokevirtual #4 Console: Hello JavaOne 2015 opcode 0 “Hello JavaOne 2015” 1 java/lang/System.out operands constant pool values 0 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 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 17 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"}) 18 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’ > 19 Call Hiding via Reflection Constant pool: #1 = Methodref #2 = Fieldref #3 = String #4 = Methodref #6.#15 #16.#17 #18 #19.#20 // java/lang/Object."<init>":()V // java/lang/System.out:Ljava/io/PrintStream; // Hello JavaOne 2015 // java/io/PrintStream.println:(Ljava/lang/String;)V 20 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 21 Classic invoke* Instructions Math.random(); // Call static method invokestatic java/lang/Math.random:()D No dispatch System.out.println("Hello JavaOne 2015"); // Call virtual method invokevirtual java/io/PrintStream.println:(Ljava/lang/String;)V Single dispatch via table it.hasNext(); // Call interface method invokeinterface java/util/Iterator.hasNext:()Z Single dispatch via search new StringBuilder(); // Call special method invokespecial java/lang/StringBuilder."<init>":()V No dispatch 22 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 http://cr.openjdk.java.net/~vlivanov/talks/2015-Indy_Deep_Dive.pdf Invokedynamic: Deep Dive. Vladimir Ivanov Hot Spot JVM Compiler, Oracle 23 bytecode + bootsrap method method handles invokedynamic 24 InvokeDynamic Execution Bytecode 0: invokedynamic #2,0 ConstantPool 1.Resolving the bootstrap method ….. 2 … 3.Linking CallSit e 2.Executing invokedynamic #0:#22 Bootstrap method with arguments 18 BoostrapSection 19 0 CallSite LambdaMetafactory.metafactory(..) 20 1 MethodHandle 21 2 MethodType 25 CallSite MethodHandles.Lookup String callerName MethodType callerType arg4 bootstrap method CallSite1 MutableCallSite arg255 ConstantCallSite MutableCallSite1 26 Call Hiding via InvokeDynamic Plan - Generate bootstrap method - Replace invokevirtual/invokeinterface/invokestatic instructions with invokedynamic https://github.com/licel/indyprotectordemo 27 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 28 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); 29 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); break; default: throw new BootstrapMethodError(); } 30 org.objectweb.asm • Library for low-level bytecode manipulation • Visitor API (same as SAX) • Tree API (same as DOM) 31 Visitor API ClassVisitor visitMethod MethodVisitor visitCode visit*Insn visitEnd visitField FieldVisitor visitAnnotation visitEnd visitAttribute 32 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(); 33 ASMifier in Action Source code: Bytecode: public class Test { public static void main(String[] args) { System.out.println("Hello JavaOne 2015"); } } javac 0: getstatic #2 3: ldc #3 5: invokevirtual #4 8: return org.objectweb.asm.util.ASMifier ASMifier output: compile & run 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 BootstrapMethodGenerator.java String bootstrapMethodName = "bootstrap$0"; String bootstrapMethodSignature = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/Meth odType;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); ….. 35 Call Hiding via InvokeDynamic Plan ✔ Generate bootstrap method - Replace invokevirtual/invokeinterface/invokestatic instructions with invokedynamic 36 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 } } 37 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); } } 38 Result – Call Hiding via InvokeDynamic JD-GUI: 39 Result – Call Hiding via InvokeDynamic $ java -jar procyon.jar indyp/HelloJavaOne.class public static void main(final String[] array) { } // invokedynamic(937623782:(Ljava/lang/Object;Ljava/lang/Object;)V, System.out, "Hello JavaOne 2015") 40 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"); } 41 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\uaf 0d\ub61a\ua5f7\uecc2\ub638\ue7fb\uf02c\u56df\u5333")) 42 Conclusion • Protection for all classic invoke* instructions • No performance impact (excellent JVM optimization for InvokeDynamic) • Battle tested in our products 43 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 44 Contact Email: [email protected] Twitter: @ivan_kinash Email: [email protected] Twitter: @MikhailDudarev Demo project: https://github.com/licel/indyprotectordemo Licel Corporation Web: https://licelus.com 45