Download Hello JavaOne 2015

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
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
Related documents