1 4 package com.tc.object.bytecode.hook; 5 6 import com.tc.asm.ClassAdapter; 7 import com.tc.asm.ClassReader; 8 import com.tc.asm.ClassVisitor; 9 import com.tc.asm.ClassWriter; 10 import com.tc.asm.Label; 11 import com.tc.asm.MethodAdapter; 12 import com.tc.asm.MethodVisitor; 13 import com.tc.asm.Opcodes; 14 import com.tc.asm.Type; 15 16 import java.util.HashMap ; 17 import java.util.Map ; 18 19 24 public class ClassLoaderPreProcessorImpl { 25 26 private final static String CLASSLOADER_CLASS_NAME = "java/lang/ClassLoader"; 27 private final static String DEFINECLASS0_METHOD_NAME = "defineClass0"; 28 29 private final static String DEFINECLASS1_METHOD_NAME = "defineClass1"; 31 private final static String DEFINECLASS2_METHOD_NAME = "defineClass2"; 32 33 private static final String DESC_CORE = "Ljava/lang/String;[BIILjava/security/ProtectionDomain;"; 34 private static final String DESC_PREFIX = "(" + DESC_CORE; 35 private static final String DESC_HELPER = "(Ljava/lang/ClassLoader;" + DESC_CORE + ")[B"; 36 37 private static final String DESC_BYTEBUFFER_CORE = "Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;"; 38 private static final String DESC_BYTEBUFFER_PREFIX = "(" + DESC_BYTEBUFFER_CORE; 39 private static final String DESC_BYTEBUFFER_HELPER = "(Ljava/lang/ClassLoader;" + DESC_BYTEBUFFER_CORE 40 + ")Ljava/nio/ByteBuffer;"; 41 42 public ClassLoaderPreProcessorImpl() { 43 } 45 46 53 public byte[] preProcess(byte[] classLoaderBytecode) { 54 try { 55 ClassWriter cw = new ClassWriter(true); 56 ClassLoaderVisitor cv = new ClassLoaderVisitor(cw); 57 ClassReader cr = new ClassReader(classLoaderBytecode); 58 cr.accept(cv, false); 59 return cw.toByteArray(); 60 } catch (Exception e) { 61 System.err.println("failed to patch ClassLoader:"); 62 e.printStackTrace(); 63 return classLoaderBytecode; 64 } 65 } 66 67 private static class ClassLoaderVisitor extends ClassAdapter { 68 private String className; 69 70 public ClassLoaderVisitor(ClassVisitor cv) { 71 super(cv); 72 } 73 74 public void visit(int version, int access, String name, String signature, String superName, String [] interfaces) { 75 super.visit(version, access, name, signature, superName, interfaces); 76 this.className = name; 77 } 78 79 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String [] exceptions) { 80 MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 81 if (CLASSLOADER_CLASS_NAME.equals(className) && "loadClassInternal".equals(name) 82 && "(Ljava/lang/String;)Ljava/lang/Class;".equals(desc)) { 83 return new LoadClassVisitor(mv); 84 } else if (CLASSLOADER_CLASS_NAME.equals(className) && "getResource".equals(name) 85 && "(Ljava/lang/String;)Ljava/net/URL;".equals(desc)) { 86 return new GetResourceVisitor(mv); 87 88 } else { 89 Type[] args = Type.getArgumentTypes(desc); 90 return new ProcessingVisitor(mv, access, args); 91 } 92 } 93 } 94 95 99 public static class GetResourceVisitor extends MethodAdapter implements Opcodes { 100 private boolean isInstrumented = false; 101 102 public GetResourceVisitor(MethodVisitor mv) { 103 super(mv); 104 } 105 106 public void visitLineNumber(int line, Label start) { 107 super.visitLineNumber(line, start); 108 if (!isInstrumented) instrument(); 109 } 110 111 public void visitVarInsn(int opcode, int var) { 112 if (!isInstrumented) instrument(); 113 super.visitVarInsn(opcode, var); 114 } 115 116 private void instrument() { 117 Label l = new Label(); 118 119 mv.visitVarInsn(ALOAD, 1); 120 mv.visitVarInsn(ALOAD, 0); 121 mv.visitMethodInsn(INVOKESTATIC, "com/tc/object/bytecode/hook/impl/ClassProcessorHelper", "getTCResource", 122 "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/net/URL;"); 123 mv.visitVarInsn(ASTORE, 2); 124 125 mv.visitVarInsn(ALOAD, 2); 126 mv.visitJumpInsn(IFNULL, l); 127 128 mv.visitVarInsn(ALOAD, 2); 129 mv.visitInsn(ARETURN); 130 131 mv.visitLabel(l); 132 133 this.isInstrumented = true; 134 } 135 136 } 137 138 156 public static class LoadClassVisitor extends MethodAdapter implements Opcodes { 157 private boolean isInstrumented = false; 158 159 public LoadClassVisitor(MethodVisitor mv) { 160 super(mv); 161 } 162 163 public void visitLineNumber(int line, Label start) { 164 super.visitLineNumber(line, start); 165 if (!isInstrumented) instrument(); 166 } 167 168 public void visitVarInsn(int opcode, int var) { 169 if (!isInstrumented) instrument(); 170 super.visitVarInsn(opcode, var); 171 } 172 173 private void instrument() { 174 Label l = new Label(); 175 176 mv.visitVarInsn(ALOAD, 1); 177 mv.visitVarInsn(ALOAD, 0); 178 mv.visitMethodInsn(INVOKESTATIC, "com/tc/object/bytecode/hook/impl/ClassProcessorHelper", "getTCClass", 179 "(Ljava/lang/String;Ljava/lang/ClassLoader;)[B"); 180 mv.visitVarInsn(ASTORE, 2); 181 182 mv.visitVarInsn(ALOAD, 2); 183 mv.visitJumpInsn(IFNULL, l); 184 185 mv.visitVarInsn(ALOAD, 0); 186 mv.visitVarInsn(ALOAD, 1); 187 mv.visitVarInsn(ALOAD, 2); 188 mv.visitInsn(ICONST_0); 189 mv.visitVarInsn(ALOAD, 2); 190 mv.visitInsn(ARRAYLENGTH); 191 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassLoader", "defineClass", 192 "(Ljava/lang/String;[BII)Ljava/lang/Class;"); 193 mv.visitInsn(ARETURN); 194 195 mv.visitLabel(l); 196 197 this.isInstrumented = true; 198 } 199 200 } 201 202 private static class ProcessingVisitor extends RemappingMethodVisitor { 203 public ProcessingVisitor(MethodVisitor cv, int access, Type[] args) { 204 super(cv, access, args); 205 } 206 207 public void visitMethodInsn(int opcode, String owner, String name, String desc) { 208 boolean insertPostCall = false; 209 210 if ((DEFINECLASS0_METHOD_NAME.equals(name) || (DEFINECLASS1_METHOD_NAME.equals(name))) 211 && CLASSLOADER_CLASS_NAME.equals(owner)) { 212 insertPostCall = true; 213 Type[] args = Type.getArgumentTypes(desc); 214 if (args.length < 5 || !desc.startsWith(DESC_PREFIX)) { throw new Error ( 215 "non supported JDK, native call not supported: " 216 + desc); } 217 int[] locals = new int[args.length]; 219 for (int i = args.length - 1; i >= 0; i--) { 220 mv.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), locals[i] = nextLocal(args[i].getSize())); 221 } 222 for (int i = 0; i < 5; i++) { 223 mv.visitVarInsn(args[i].getOpcode(Opcodes.ILOAD), locals[i]); 224 } 225 super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/tc/object/bytecode/hook/impl/ClassProcessorHelper", 226 "defineClass0Pre", DESC_HELPER); 227 int returnLocalByteArray = nextLocal(args[1].getSize()); 228 mv.visitVarInsn(Opcodes.ASTORE, returnLocalByteArray); 229 mv.visitVarInsn(Opcodes.ALOAD, locals[1]); mv.visitVarInsn(Opcodes.ALOAD, returnLocalByteArray); Label l1 = new Label(); 232 mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1); 233 237 mv.visitVarInsn(Opcodes.ALOAD, 0); 238 mv.visitVarInsn(Opcodes.ALOAD, locals[0]); mv.visitVarInsn(Opcodes.ALOAD, returnLocalByteArray); mv.visitInsn(Opcodes.ICONST_0); mv.visitVarInsn(Opcodes.ALOAD, returnLocalByteArray); 242 mv.visitInsn(Opcodes.ARRAYLENGTH); mv.visitVarInsn(Opcodes.ALOAD, locals[4]); for (int i = 5; i < args.length; i++) { 245 mv.visitVarInsn(args[i].getOpcode(Opcodes.ILOAD), locals[i]); 246 } 247 super.visitMethodInsn(opcode, owner, name, desc); 248 Label l2 = new Label(); 249 mv.visitJumpInsn(Opcodes.GOTO, l2); 250 mv.visitLabel(l1); 251 mv.visitVarInsn(Opcodes.ALOAD, 0); 252 for (int i = 0; i < args.length; i++) { 253 mv.visitVarInsn(args[i].getOpcode(Opcodes.ILOAD), locals[i]); 254 } 255 super.visitMethodInsn(opcode, owner, name, desc); 256 mv.visitLabel(l2); 257 } else if (DEFINECLASS2_METHOD_NAME.equals(name) && CLASSLOADER_CLASS_NAME.equals(owner)) { 258 insertPostCall = true; 259 Type[] args = Type.getArgumentTypes(desc); 260 if (args.length < 5 || !desc.startsWith(DESC_BYTEBUFFER_PREFIX)) { throw new Error ( 261 "non supported JDK, bytebuffer native call not supported: " 262 + desc); } 263 int[] locals = new int[args.length]; 265 for (int i = args.length - 1; i >= 0; i--) { 266 mv.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), locals[i] = nextLocal(args[i].getSize())); 267 } 268 for (int i = 0; i < 5; i++) { 269 mv.visitVarInsn(args[i].getOpcode(Opcodes.ILOAD), locals[i]); 270 } 271 super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/tc/object/bytecode/hook/impl/ClassProcessorHelperJDK15", 272 "defineClass0Pre", DESC_BYTEBUFFER_HELPER); 273 mv.visitVarInsn(Opcodes.ASTORE, locals[1]); 274 mv.visitVarInsn(Opcodes.ALOAD, 0); 275 mv.visitVarInsn(Opcodes.ALOAD, locals[0]); mv.visitVarInsn(Opcodes.ALOAD, locals[1]); mv.visitInsn(Opcodes.ICONST_0); mv.visitVarInsn(Opcodes.ALOAD, locals[1]); 279 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Ljava/nio/Buffer;", "remaining", "()I"); 280 mv.visitVarInsn(Opcodes.ALOAD, locals[4]); for (int i = 5; i < args.length; i++) { 282 mv.visitVarInsn(args[i].getOpcode(Opcodes.ILOAD), locals[i]); 283 } 284 super.visitMethodInsn(opcode, owner, name, desc); 285 } else { 286 super.visitMethodInsn(opcode, owner, name, desc); 287 } 288 289 if (insertPostCall) { 290 super.visitInsn(Opcodes.DUP); 291 super.visitVarInsn(Opcodes.ALOAD, 0); 292 super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/tc/object/bytecode/hook/impl/ClassProcessorHelper", 294 "defineClass0Post", "(Ljava/lang/Class;Ljava/lang/ClassLoader;)V"); 295 } 296 } 297 } 298 299 private static class State { 300 Map locals = new HashMap (); 301 int firstLocal; 302 int nextLocal; 303 304 State(int access, Type[] args) { 305 nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1; 306 for (int i = 0; i < args.length; i++) { 307 nextLocal += args[i].getSize(); 308 } 309 firstLocal = nextLocal; 310 } 311 } 312 313 private static class IntRef { 314 int key; 315 316 public boolean equals(Object o) { 317 return key == ((IntRef) o).key; 318 } 319 320 public int hashCode() { 321 return key; 322 } 323 } 324 325 private static class RemappingMethodVisitor extends MethodAdapter { 326 private State state; 327 private IntRef check = new IntRef(); 328 329 public RemappingMethodVisitor(MethodVisitor v, int access, Type[] args) { 330 super(v); 331 state = new State(access, args); 332 } 333 334 public RemappingMethodVisitor(RemappingMethodVisitor wrap) { 335 super(wrap.mv); 336 this.state = wrap.state; 337 } 338 339 protected int nextLocal(int size) { 340 int var = state.nextLocal; 341 state.nextLocal += size; 342 return var; 343 } 344 345 private int remap(int var, int size) { 346 if (var < state.firstLocal) { return var; } 347 check.key = (size == 2) ? ~var : var; 348 Integer value = (Integer ) state.locals.get(check); 349 if (value == null) { 350 IntRef ref = new IntRef(); 351 ref.key = check.key; 352 state.locals.put(ref, value = new Integer (nextLocal(size))); 353 } 354 return value.intValue(); 355 } 356 357 public void visitIincInsn(int var, int increment) { 358 mv.visitIincInsn(remap(var, 1), increment); 359 } 360 361 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { 362 mv.visitLocalVariable(name, desc, signature, start, end, remap(index, 0)); 363 } 364 365 public void visitVarInsn(int opcode, int var) { 366 int size; 367 switch (opcode) { 368 case Opcodes.LLOAD: 369 case Opcodes.LSTORE: 370 case Opcodes.DLOAD: 371 case Opcodes.DSTORE: 372 size = 2; 373 break; 374 default: 375 size = 1; 376 } 377 mv.visitVarInsn(opcode, remap(var, size)); 378 } 379 380 public void visitMaxs(int maxStack, int maxLocals) { 381 mv.visitMaxs(0, 0); 382 } 383 } 384 385 } 386 | Popular Tags |