Advertisement
Guest User

Time machine

a guest
Jun 20th, 2012
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.67 KB | None | 0 0
  1. package paci.samples.bce;
  2.  
  3. import static org.objectweb.asm.Opcodes.INVOKESTATIC;
  4. import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
  5. import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
  6. import static org.objectweb.asm.Opcodes.LADD;
  7.  
  8. import java.lang.instrument.ClassFileTransformer;
  9. import java.lang.instrument.IllegalClassFormatException;
  10. import java.lang.instrument.Instrumentation;
  11. import java.security.ProtectionDomain;
  12. import java.util.HashSet;
  13. import java.util.Set;
  14. import java.util.concurrent.TimeUnit;
  15. import java.util.logging.Logger;
  16. import java.util.regex.Matcher;
  17. import java.util.regex.Pattern;
  18.  
  19. import org.objectweb.asm.ClassAdapter;
  20. import org.objectweb.asm.ClassReader;
  21. import org.objectweb.asm.ClassWriter;
  22. import org.objectweb.asm.MethodVisitor;
  23. import org.objectweb.asm.commons.GeneratorAdapter;
  24.  
  25. /*  
  26.  * @author paci.salminen@gmail.com
  27.  */
  28. public class TimeMachineAgent {
  29.     public static void premain( String agentArgs,Instrumentation instr ) throws Exception {    
  30.         String targets = "paci.*:org/joda.*";
  31.         long timeOffset = TimeUnit.HOURS.toMillis( 6 );
  32.         if( agentArgs != null )
  33.             targets = agentArgs;
  34.  
  35.         Set<String> targetSet = new HashSet<String>( );
  36.         for( String str : targets.split( ":" ) )
  37.             targetSet.add( str );
  38.         instr.addTransformer( new TransformerImpl2( targetSet,timeOffset ) );
  39.     }
  40. }
  41.  
  42. class TransformerImpl2 implements ClassFileTransformer {
  43.     private static final Logger LOGGER = Logger.getLogger( TransformerImpl.class.getPackage( ).getName( ) );
  44.     private final Set<Pattern> _targets;
  45.     private final long timeOffset;
  46.  
  47.     TransformerImpl2( Set<String> targets,long timeOffset ) {
  48.         _targets = new HashSet<Pattern>( );
  49.         for( String target : targets )
  50.             _targets.add( Pattern.compile( target ) );
  51.         this.timeOffset = timeOffset;
  52.     }
  53.  
  54.     public byte[ ] transform(   ClassLoader loader,
  55.                                 final String className,
  56.                                 Class<?> classBeingRedefined,
  57.                                 ProtectionDomain protectionDomain,
  58.                                 byte[ ] classfileBuffer )
  59.         throws IllegalClassFormatException {
  60.         boolean match = false;
  61.        
  62.         if( className.startsWith( "java/lang" ) || className.startsWith( "java/util" ) || className.startsWith( "com/ibm" ) || className.startsWith( "sun/util" ) )
  63.             return null;
  64.        
  65.         if( _targets.size( ) > 0 )
  66.         {
  67.             for( Pattern target : _targets )
  68.             {
  69.                 Matcher matcher = target.matcher( className );
  70.                 if( matcher.matches( ) )
  71.                 {
  72.                     match = true;
  73.                     break;
  74.                 }
  75.             }
  76.         }
  77.         else
  78.             match = true;
  79.        
  80.         if( ! match )
  81.             return null;
  82.  
  83.         //System.out.println( "Codesource: " + protectionDomain.getCodeSource( ).getLocation( ) );
  84.        
  85.         try {
  86.             ClassReader cr = new ClassReader( classfileBuffer );
  87.             ClassWriter cw = new ClassWriter( 0 );
  88.             cr.accept(
  89.                     new ClassAdapter( cw ) {
  90.                         public MethodVisitor visitMethod( int access,String name,String desc,String signature,String[ ] exceptions ) {
  91.                             MethodVisitor mv = super.visitMethod( access,name,desc,signature,exceptions );                         
  92.                             return new TimeMachineAdapter( access,name,desc,mv,timeOffset );
  93.                         }
  94.                     },ClassReader.EXPAND_FRAMES );
  95.    
  96.             return cw.toByteArray( );
  97.         }
  98.         catch( Exception e ) {
  99.             // If anything goes wrong, return the class as is and log this fact
  100.             LOGGER.severe( "Instrumentation of class " + className + " failed. Message is " + e.getMessage( ) + "." );
  101.            
  102.             return null;
  103.         }
  104.     }
  105. }
  106.  
  107. class TimeMachineAdapter extends GeneratorAdapter {
  108.     private long timeOffset;
  109.    
  110.     public TimeMachineAdapter( int access,String name,String desc,MethodVisitor mv,long offset ) {
  111.         super( mv,access,name,desc );
  112.         timeOffset = offset;
  113.     }
  114.    
  115.     /**
  116.      * Modifies calls to methods which may be used get current time.
  117.      * Modification is accomplished using bytecode manipulation, so that e.g
  118.      * an instantiation of java.util.Date with a default constructor is intercepted
  119.      * and offset (positive or negative) is added to current time. This method is not used
  120.      * in any way during execution of the code; bytecode manipulation happens during
  121.      * class loading time.
  122.      */
  123.     @Override
  124.     public void visitMethodInsn( int opcode,String owner,String name,String desc ) {
  125.         // Slot is an allocation unit on the stack. One slot equals a word (32 bit).
  126.         // An object reference and 32-bit (or shorter) primitives take one slot,
  127.         // 64-bit primitives like long and double take two slots.
  128.        
  129.         // Intercept java.util.Date default constructor call
  130.         if( opcode == INVOKESPECIAL && owner.equals( "java/util/Date" ) &&
  131.             name.equals( "<init>" ) && desc.equals( "()V" ) ) {
  132.             // Bytecode NEW is already executed
  133.             /*
  134.              *  Duplicate reference before constructor call
  135.              *  FIXES BUG when no assignment is done
  136.              *  private static void foo( ) {
  137.              *      new Date( );
  138.              *  }
  139.             */
  140.             // Stack contains now a reference to java.util.Date (slots reserved 1)
  141.             // Note that constructor call is not made yet.
  142.             // Dup the reference (slots used +1)
  143.             dup( );        
  144.            
  145.             // Now generate a constructor call (consumes one slot so slots used is 1 after the call)
  146.             super.visitMethodInsn( opcode,owner,name,desc );
  147.             // Dup the reference (slots used is 2) ...
  148.             dup( );
  149.             // Invoke getTime method (slots used after this call is 3)
  150.             visitMethodInsn( INVOKEVIRTUAL,"java/util/Date","getTime", "()J");
  151.             // Push offset to the stack (slots used after this call is 5)          
  152.             visitLdcInsn( new Long( timeOffset ) );
  153.             // Add the operands (slots used after this call is 3)
  154.             visitInsn( LADD );
  155.             // And finally call setTime method (slots used after this call is 1)
  156.             visitMethodInsn( INVOKEVIRTUAL,"java/util/Date","setTime", "(J)V");
  157.             // One reference to java.util.Date remains on the stack
  158.         }
  159.         //Intercept java.lang.System.currentTimeMills call
  160.         else if(    opcode == INVOKESTATIC &&
  161.                     owner.equals( "java/lang/System") &&
  162.                     name.equals( "currentTimeMillis" ) &&
  163.                     desc.equals( "()J" ) ) {
  164.             // Generate a currentTimeMillis call (after this call slots used is 2)
  165.             super.visitMethodInsn( opcode,owner,name,desc );
  166.             // Push offset to the stack (slots used after this call is 4)
  167.             visitLdcInsn( new Long( timeOffset ) );
  168.             // Add the operands (slots used after this call is 2)
  169.             visitInsn( LADD );
  170.             // Only current time remains on the stack
  171.         }
  172.         //Intercept java.util.Calendar getInstance call
  173.         else if( opcode == INVOKESTATIC &&
  174.                 owner.equals( "java/util/Calendar" ) &&
  175.                 name.equals( "getInstance" ) &&
  176.                 desc.equals( "()Ljava/util/Calendar;" ) ) {
  177.             // Generate a getInstance call (after this call slots used is 1)
  178.             super.visitMethodInsn( opcode,owner,name,desc );
  179.             // Dup the reference (slots used after this is 2)
  180.             dup( );
  181.             // Dup the reference (slots used after this is 3)
  182.             dup( );
  183.             // Call getTimeInMillis method (slots used after this call is 4)
  184.             visitMethodInsn( INVOKEVIRTUAL,"java/util/Calendar","getTimeInMillis", "()J");
  185.             // Push offset to the stack (slots used after this call is 6)
  186.             visitLdcInsn( new Long( timeOffset ) );
  187.             // Add the operands (slots used after this call is 4)
  188.             visitInsn( LADD );
  189.             // And finally call setTimeInMillis method (slots used after this call is 1)
  190.             visitMethodInsn( INVOKEVIRTUAL,"java/util/Calendar","setTimeInMillis", "(J)V");
  191.             // One reference to java.util.Calendar remains on the stack
  192.         }
  193.         else
  194.             // Otherwise generate whatever instruction was encountered
  195.             super.visitMethodInsn( opcode,owner,name,desc );
  196.     }
  197.    
  198.     /**
  199.      * Reports the maximum consumption of stack and local variables in slots.
  200.      */
  201.     @Override
  202.     public void visitMaxs( int maxStack,int maxLocals ) {
  203.         // Parameters are given in slots, not in bytes
  204.         // Our date manipulation logic added 5 slots and no local variables
  205.         super.visitMaxs( maxStack + 4,maxLocals );
  206.     }
  207. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement