Overriding a static method on java.lang.System?

1,095 views
Skip to first unread message

Will Sargent

unread,
Jan 15, 2016, 9:29:08 PM1/15/16
to Byte Buddy
Hi all,

I'd like to instrument some methods on java.lang.System, notably System.setSecurityManager, so that I can prevent it from being reset after it is first called:

public class FixSecurityManagerTest {

static {
SecurityFixerAgent.initialize();
}

public static void main(String[] args) throws Exception {
System.setSecurityManager(new SecurityManager());

System.out.println("Security manager is set!");
try {
System.setSecurityManager(null);
} catch (SecurityException e) {
System.out.println("Security manager cannot be reset!");
}
}

}


The idea is that I can use a bootstrap injection to read in the System bytecodes and then rewrite the setSecurityManager bytecode implementation, and have the program read in the new bytes.  It seems like this should be possible using ASM, but given that ByteBuddy seems to have a more straight forward interface, I'd prefer to use it instead.  

I get that I should be using an AgentBuilder and the premain method so that I can replace the method before it is first loaded, but my example doesn't seem to change anything when run:

    static AgentBuilder.Identified.Extendable createExtendable(Instrumentation inst) {
        final File folder = new File("bootstrap");
        folder.mkdir();

        final ElementMatcher.Junction<NamedElement> systemType = ElementMatchers.is(named("java.lang.System"));

        final AgentBuilder.Identified identified = new AgentBuilder.Default()
                .enableBootstrapInjection(folder, inst)
                .type(systemType);

        final Implementation definition = MethodDelegation.to(MySystemInterceptor.class);


        return identified.transform((builder, type) ->
                builder.method(named("setSecurityManager"))
                        .intercept(definition));
    }


Full example is here, I am running it with -javaagent enabled:


It seems like this may be not be possible since it is static, final, and a Java internal class all at the same time.  I've looked through the documentation and Stack Overflow, and there is an example of someone overriding System.currentTimeMillis using bootclasspath:


But I don't know if instrumentation works along the same lines.

Will.

Rafael Winterhalter

unread,
Jan 17, 2016, 5:01:52 PM1/17/16
to Will Sargent, Byte Buddy
Hi Will.

overriding methods of the System class is a bit tricky as classes can only be redefined. You have to activate redefinition explicitly (see http://bytebuddy.net/javadoc/1.0.0/net/bytebuddy/agent/builder/AgentBuilder.RedefinitionStrategy.html). Otherwise, Byte Buddy tries to preserve the original code what results in adding additional methods. Also, you should probably disable the implementation context of your Byte Buddy instance. This makes sure, you do not add any fields or methods as a side effect.

When your agent is executed, the System class is furthermore already loaded as it is an internal class. You have to trigger a redefinition. Byte Buddy supports this by enabling redefinition during building the agent. To further debug this, please try registering a AgentBuilder.Listener on your agent, this will probably inform you about other problems.

On a side note. You know that it is possible to set a SecurityManager policy that forbis changing the security manager? This would normally be a more straight-forward solution.

Please ask, if you have any further questions.
Cheers, Rafael

--
You received this message because you are subscribed to the Google Groups "Byte Buddy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to byte-buddy+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Will Sargent

unread,
Jan 17, 2016, 11:19:04 PM1/17/16
to Rafael Winterhalter, Byte Buddy
So for "activate redefinition explicitly" I am putting:

.withRedefinitionStrategy(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.withTypeStrategy(AgentBuilder.TypeStrategy.Default.REDEFINE)
and for "disable the implementation context of your Byte Buddy instance" I have:

ByteBuddy byteBuddy = new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE);
agentBuilder.withByteBuddy(byteBuddy)
For "try registering a AgentBuilder.Listener on your agent" I have:
agentBuilder.withListener(new AgentBuilder.Listener.StreamWriting(System.out))
So the final result is:
private static AgentBuilder createAgentBuilder(Instrumentation inst) {

final File folder = new File("bootstrap");
folder.mkdir();

    final ElementMatcher.Junction<NamedElement> systemType = ElementMatchers.named("java.lang.System");
AgentBuilder.Transformer transformer = (builder, typeDescription) -> {
return builder.method(ElementMatchers.named("setSecurityManager"))
.intercept(MethodDelegation.to(MySystemInterceptor.class));
};

ByteBuddy byteBuddy = new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE);
final AgentBuilder agentBuilder = new AgentBuilder.Default()
//.enableBootstrapInjection(folder, inst)
.withByteBuddy(byteBuddy)
.withRedefinitionStrategy(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.withTypeStrategy(AgentBuilder.TypeStrategy.Default.REDEFINE)
.withListener(new AgentBuilder.Listener.StreamWriting(System.out))
.type(systemType)
.transform(transformer);

return agentBuilder;
}

This gets me to the point where I can see that "java.lang.System" is being picked out, but I get an exception:

java.lang.IllegalStateException: Cannot define type initializer which was explicitly disabled: TypeInitializer.Simple{byteCodeAppender=AgentBuilder.InitializationStrategy.SelfInjection.NexusAccessor.InitializationAppender{identification=-589254895}}
at net.bytebuddy.implementation.Implementation$Context$Disabled$Factory.make(Implementation.java:611)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1572)
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:179)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:2790)
at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRedefinition$Entry.resolve(AgentBuilder.java:1786)
at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRedefinition.apply(AgentBuilder.java:1705)
at net.bytebuddy.agent.builder.AgentBuilder$Default.installOn(AgentBuilder.java:2234)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Matched.installOn(AgentBuilder.java:3249)
at com.tersesystems.securityfixer.SecurityFixerAgent.install(SecurityFixerAgent.java:35)
at com.tersesystems.securityfixer.SecurityFixerAgent.premain(SecurityFixerAgent.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
[Byte Buddy] COMPLETE java.lang.System

So I am not sure that MethodDelegation is allowed here for this usage -- am I supposed to be using a different type initializer?.

> On a side note. You know that it is possible to set a SecurityManager policy that forbis changing the security manager? This would normally be a more straight-forward solution.

I do, but there are some ways around it.  Wrote a blog post here: 


Will.

Rafael Winterhalter

unread,
Jan 18, 2016, 3:01:44 AM1/18/16
to Will Sargent, Byte Buddy
Hi Will,
thanks for the background information.

All you did is correct but I forgot to mention that you also need to disable self-initialization. This is another configuration of the AgentBuilder. Self initialization makes sure that for example fields are set before a class is in use. Since you delegate to a static class rather than to an instance field, your code should work fine even without self-initialization.

Cheers, Rafael

Rafael Winterhalter

unread,
Jan 18, 2016, 3:02:09 AM1/18/16
to Will Sargent, Byte Buddy
PS: Choose, InitializationStrategy.Disabled.INSTANCE.

Will Sargent

unread,
Jan 18, 2016, 3:24:34 PM1/18/16
to Rafael Winterhalter, Byte Buddy

> PS: Choose, InitializationStrategy.Disabled.INSTANCE.

There isn’t a Disabled.INSTANCE, but so I went with NoOp:

   .withInitializationStrategy(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)

This gets me to:

java.lang.IllegalArgumentException: None of [] allows for delegation from public static void java.lang.System.setSecurityManager(java.lang.SecurityManager)
at net.bytebuddy.implementation.bind.MethodDelegationBinder$Processor.process(MethodDelegationBinder.java:881)
at net.bytebuddy.implementation.MethodDelegation$Appender.apply(MethodDelegation.java:1224)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:564)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RedefinitionClassVisitor$CodePreservingMethodVisitor.visitCode(TypeWriter.java:3221)
at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source)
at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.doCreate(TypeWriter.java:2659)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2643)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1577)
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:179)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2472)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:2790)
at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRedefinition$Entry.resolve(AgentBuilder.java:1786)
at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRedefinition.apply(AgentBuilder.java:1705)
at net.bytebuddy.agent.builder.AgentBuilder$Default.installOn(AgentBuilder.java:2234)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Matched.installOn(AgentBuilder.java:3249)
at com.tersesystems.securityfixer.SecurityFixerAgent.install(SecurityFixerAgent.java:27)
at com.tersesystems.securityfixer.SecurityFixerAgent.premain(SecurityFixerAgent.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
[Byte Buddy] COMPLETE java.lang.System 

Which leads me to believe that my intercept binding isn’t working quite right:

        final ElementMatcher.Junction<NamedElement> systemType = ElementMatchers.named("java.lang.System");
        AgentBuilder.Transformer transformer =
                (b, typeDescription) -> b.method(ElementMatchers.named("setSecurityManager"))
                .intercept(MethodDelegation.to(MySystemInterceptor.class));


MySystemInterceptor is defined as:

class MySystemInterceptor {
    @RuntimeType
    public static void setSecurityManager(@Argument(0) SecurityManager s) {
        throw new IllegalStateException("hello");
    }
}

Which sure looks right — is “final” significant when it comes to arguments?

Will.


-----Original Message-----
From: Rafael Winterhalter <rafae...@gmail.com>
Reply: Rafael Winterhalter <rafae...@gmail.com>
Date: January 18, 2016 at 12:02:09 AM
To: Will Sargent <will.s...@gmail.com>
CC: Byte Buddy <byte-...@googlegroups.com>
Subject:  Re: Overriding a static method on java.lang.System?

>
> 2016-01-18 9:01 GMT+01:00 Rafael Winterhalter :
>
> > Hi Will,
> > thanks for the background information.
> >
> > All you did is correct but I forgot to mention that you also need to
> > disable self-initialization. This is another configuration of the
> > AgentBuilder. Self initialization makes sure that for example fields are
> > set before a class is in use. Since you delegate to a static class rather
> > than to an instance field, your code should work fine even without
> > self-initialization.
> >
> > Cheers, Rafael
> >
> > 2016-01-18 5:18 GMT+01:00 Will Sargent :
> >
> >> So for "activate redefinition explicitly" I am putting:
> >>
> >> .withRedefinitionStrategy(AgentBuilder.RedefinitionStrategy.REDEFINITION)
> >> .withTypeStrategy(AgentBuilder.TypeStrategy.Default.REDEFINE)
> >>
> >> and for "disable the implementation context of your Byte Buddy instance"
> >> I have:
> >>
> >> ByteBuddy byteBuddy = new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE);
> >>
> >> agentBuilder.withByteBuddy(byteBuddy)
> >>
> >> For "try registering a AgentBuilder.Listener on your agent" I have:
> >>
> >> agentBuilder.withListener(new AgentBuilder.Listener.StreamWriting(System.out))
> >>
> >> So the final result is:
> >>
> >> private static AgentBuilder createAgentBuilder(Instrumentation inst) {
> >> final File folder = new File("bootstrap");
> >> folder.mkdir();
> >>
> >> final ElementMatcher.Junction systemType = ElementMatchers.named("java.lang.System");
> >>>> final ElementMatcher.Junction systemType = ElementMatchers.is(named("java.lang.System"));

Rafael Winterhalter

unread,
Jan 18, 2016, 3:43:17 PM1/18/16
to Will Sargent, Byte Buddy

No, it is not. Is your interceptor package-private? Seems like the class is not visible.

Will Sargent

unread,
Jan 18, 2016, 5:06:01 PM1/18/16
to Rafael Winterhalter, Byte Buddy
Yeah, it was package private -- didn't realize that was a factor.  But I got it working!

I needed to set up the interceptor so it was packaged in a different JAR than the Agent, and then specify the JAR file as an argument so I could append the intercepter to the bootstrap path:
static void install(String arg, Instrumentation inst) {
appendInterceptorToBootstrap(arg, inst);
AgentBuilder agentBuilder = createAgentBuilder(inst);
agentBuilder.installOn(inst);
}

private static void appendInterceptorToBootstrap(String arg, Instrumentation inst) {
try {
File file = new File(arg).getCanonicalFile();
if (!file.exists()) {
throw new IllegalStateException(arg + " does not exist");
}

if (!file.isFile()) {
throw new IllegalStateException(arg + " is not a file");

}
if (!file.canRead()) {
throw new IllegalStateException(arg + " cannot be read");

}
JarFile jarFile = new JarFile(file);
inst.appendToBootstrapClassLoaderSearch(jarFile);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
Then I could point the agent at the bootstrap jar with:

java -javaagent:agent/target/securityfixer-agent-1.0-SNAPSHOT.jar=bootstrap/target/securityfixer-bootstrap-1.0-SNAPSHOT.jar securityfixer.Main

And then setting a non-null security manager resulted in IllegalStateException!

Exception in thread "main" Security manager is set!
java.lang.IllegalStateException: SecurityManager cannot be reset!
at com.tersesystems.securityfixer.MySystemInterceptor.setSecurityManager(MySystemInterceptor.java:9)
at java.lang.System.setSecurityManager(System.java)
at securityfixer.Main.main(Main.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Whew.  Thanks for all your help!

Will.

Rafael Winterhalter

unread,
Jan 18, 2016, 5:14:17 PM1/18/16
to Will Sargent, Byte Buddy

You are welcome. You choose a very special use case for a first attempt, I am glad you got it working! Cheers, Rafael

Will Sargent

unread,
Jan 19, 2016, 2:45:58 PM1/19/16
to Rafael Winterhalter, Byte Buddy
Added a blog post: https://tersesystems.com/2016/01/19/redefining-java-dot-lang-dot-system/ 


-----Original Message-----
From: Rafael Winterhalter <rafae...@gmail.com>
Reply: Rafael Winterhalter <rafae...@gmail.com>
Date: January 18, 2016 at 2:14:17 PM
To: Will Sargent <will.s...@gmail.com>
CC: Byte Buddy <byte-...@googlegroups.com>
Subject:  Re: Overriding a static method on java.lang.System?

> You are welcome. You choose a very special use case for a first attempt, I
> am glad you got it working! Cheers, Rafael
Message has been deleted

sen...@gmail.com

unread,
Nov 24, 2016, 5:35:25 PM11/24/16
to Byte Buddy, rafae...@gmail.com
Hi all,

I'm trying to recreate the behavior described in Will's blog post but getting the following exception on an attempt to run it via:

$ java -javaagent:agent/target/securityfixer-agent-1.0-SNAPSHOT.jar=bootstrap/target/securityf ixer-bootstrap-1.0-SNAPSHOT.jar -jar example/target/securi
tyfixer-example-1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: net/bytebuddy/implementation/Implementation$Context$Factory
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
at java.lang.Class.getDeclaredMethod(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: net.bytebuddy.implementation.Implementation$Context$Factory
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 5 more
FATAL ERROR in native method: processing of -javaagent failed

The structure is as described in Will's blog - 3 separate jars, one with the agent, one with the interceptor and one with Main class.

I also tried to run it as an executable jar by adding the mainClass stanza into the securityfixer-example's manifest, but that seems to be bypassing instrumentation altogether:

$ java -jar example/target/securityfixer-example-1.0-SNAPSHOT.jar -javaagent:agent/target/securityfixer-agent-1.0-SNAPSHOT.jar=bootstrap/target/securityf ixer-bootstrap-1.0-SNAPSHOT.jar
Security manager is set!
ATTACK SUCCEEDED: Security manager was reset!

What could I be missing here? Thanks in advance.

Simeon

Rafael Winterhalter

unread,
Nov 25, 2016, 4:38:05 AM11/25/16
to sen...@gmail.com, Byte Buddy
I assume that you are missing Byte Buddy on the class path. I think Will built his agent as a fat jar where the Byte Buddy dependency was packaged together with the agent in the security-fixer-agent.
May this be the problem?
Best regards, Rafael

Simeon Leyzerzon

unread,
Nov 25, 2016, 5:34:29 PM11/25/16
to Rafael Winterhalter, Byte Buddy
Hi Rafael,

Yes, as a fat jar it will certainly work, however it's not so in the code mentioned in the blog (https://github.com/wsargent/securityfixer) and hence the question.  Thanks.

Regards,
Simeon

Will Sargent

unread,
Oct 6, 2018, 6:41:45 PM10/6/18
to Byte Buddy
The code in https://github.com/wsargent/securityfixer has been updated to 1.9.0 and streamlined a bit.
Reply all
Reply to author
Forward
0 new messages