-
1. Re: Trigger Load/Unload of Rules?
damcelligott Sep 29, 2014 5:08 PM (in response to damcelligott)I worked around this problem by copying the source from Main.java, importing some stock Byteman classes, and having it run my custom class based off of TransformListener.java that I can use to load new rules in anyway that I want to.
AgentMain.java
...... Imports omitted import org.jboss.byteman.agent.Retransformer; public class AgentMain { private static ScriptReload scriptReload; ...... lines omitted public static void premain(String args, Instrumentation inst) { ...... lines omitted if (allowRedefine && isRedefine) { scriptReload.initialize((Retransformer)transformer); /* * transformerClazz = loader.loadClass("org.jboss.byteman.agent.Retransformer"); * Method method = transformerClazz.getMethod("initialize", Retransformer.class); * method.invoke((Retransformer)transformer); */ } ...... lines omitted }
ScriptReload.java
import org.jboss.byteman.agent.Retransformer; import org.jboss.byteman.agent.Transformer; public class ScriptReload extends Thread { private static ScriptReload scriptReload = null; private Retransformer retransformer; private static List<String> scriptTexts; private static List<String> scriptNames; ScriptReload(Retransformer retransformer) { this.retransformer = retransformer; setDaemon(true); } public void run() { scriptTexts = Arrays.asList("RULE jabber\nCLASS java.lang.Thread\nMETHOD start()\nIF true\nDO traceln(\"*** start for thread: \"+ $0.getName())\nENDRULE\n"); scriptNames = Arrays.asList("newRules.btm"); PrintStream stdout = System.out; System.setOut(stdout); PrintWriter printWriter = new PrintWriter(stdout); while (true) { try { Thread.sleep(5000); /* * Get new script however you want */ System.out.printf("Installing Scripts %s\n", scriptNames.toString()); retransformer.installScript(scriptTexts, scriptNames, printWriter); } catch(InterruptedException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } public static synchronized boolean initialize(Retransformer retransformer) { if(scriptReload == null) { scriptReload = new ScriptReload(retransformer); scriptReload.start(); } return true; }
This is a rudementry example where the new rule to add is hardcoded, but I plan to add logic to add rules, purge rules, add jars, and other logic such as in the TransformListener.
This example is just to show that you can write your own way to reload rules without having to fork Byteman.
-
2. Re: Trigger Load/Unload of Rules?
adinn Sep 29, 2014 7:07 AM (in response to damcelligott)1 of 1 people found this helpfulHi David,
Nice work!
Yeah, I deliberately coded Byteman so that the listener socket was the only point of entry to the dynamic capabilities of the Byteman agent. Even if you use class Submit in the JVM actually running the agent they still talk through the socket (there are no visible Java methods providing access to the agent load/unload capabilities or the Transformer/Instrumentation instances). That single access mechanism is good for some apps and not good for others. Your solution is a very nice way to provide an alternative access mechanism.
There is one thing you might need to consider. Your agent will get loaded into the system classpath (that's how the JVM does it). So, this means your current definition of Main will load the imported Byteman classes into the system loader. This means you cannot then add the Byteman jar to the bootstrap classpath in order to instrument classes in java.lang etc. (the version of Transformer in the system classpath will clash with the version linked via the bootstrap classpath). If you look at my agent Main you will see that I only reference and invoke behaviour from other Byteman classes via reflection and only make the reflective calls after adding the byteman jar to the bootstrap path. You might want to think about doing the same.
-
3. Re: Trigger Load/Unload of Rules?
damcelligott Sep 29, 2014 3:49 PM (in response to adinn)Andrew, Thanks for the reply!
I will try and changed it to only use reflection in the Main class and post my changes,
hopefully you can take a look and let me know if you see any other problem areas.
-
4. Re: Re: Trigger Load/Unload of Rules?
damcelligott Sep 29, 2014 5:06 PM (in response to adinn)Alright, I managed to remove all of the imports of Byteman Classes from the Main class.
The way I decided to do it was to reuse most of the code from Retranformer class, changing only the addTransformListener method to use my ScriptReload class.
I also created a Gist with the omitted lines: https://gist.github.com/4be0097ed91ca23b315a.git.
Feedback is always appreciated!
AgentMain.java
package com.example.proj.agent; ... imports omitted public class AgentMain { ... lines omitted public static void premain(String args, Instrumentation inst) { ... lines omitted if (allowRedefine && isRedefine) { transformerClazz = loader.loadClass("com.example.proj.agent.AgentRetransformer"); Constructor constructor.newInstance(new Object[] { inst, scriptPaths, scripts, isRedefine}); } else { transformerClazz = loader.loadClass("org.jboss.byteman.agent.Transformer"); Constructor constructor = transformerClazz.getConstructor(Instrumentation.class, List.class, List.class, boolean.class); transformer = (ClassFileTransformer)constructor.newInstance(new Object[] { inst, scriptPaths, scripts, isRedefine}); } inst.addTransformer(transformer, true); if (allowRedefine && isRedefine) { Method method = transformerClazz.getMethod("addTransformListener"); method.invoke(transformer); } ... lines omitted } ... lines omitted } }
AgentRetransformer.java
package com.example.proj.agent; ... imports omitted import org.jboss.byteman.agent.RuleScript; import org.jboss.byteman.agent.ScriptRepository; import org.jboss.byteman.agent.Transform; import org.jboss.byteman.agent.Transformer; public class AgentRetransformer extends Transformer { ... lines omitted public AgentRetransformer(Instrumentation inst, List<String> scriptPaths, List<String> scriptTexts, boolean isRedefine) throws Exception { super(inst, scriptPaths, scriptTexts, isRedefine); } ... lines omitted public void addTransformListener() { ScriptReload.initialize(this); } ... lines omitted }
ScriptReload.java
package com.example.proj.agent; import org.jboss.byteman.agent.Retransformer; import org.jboss.byteman.agent.Transformer; public class ScriptReload extends Thread { private static ScriptReload scriptReload = null; private Retransformer retransformer; private static List<String> scriptTexts; private static List<String> scriptNames; ScriptReload(Retransformer retransformer) { this.retransformer = retransformer; setDaemon(true); } public void run() { scriptTexts = Arrays.asList("RULE jabber\nCLASS java.lang.Thread\nMETHOD start()\nIF true\nDO traceln(\"*** start for thread: \"+ $0.getName())\nENDRULE\n"); scriptNames = Arrays.asList("newRules.btm"); PrintStream stdout = System.out; System.setOut(stdout); PrintWriter printWriter = new PrintWriter(stdout); while (true) { try { Thread.sleep(5000); /* * Get new script however you want */ System.out.printf("Installing Scripts %s\n", scriptNames.toString()); retransformer.installScript(scriptTexts, scriptNames, printWriter); } catch(InterruptedException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } public static synchronized boolean initialize(Retransformer retransformer) { if(scriptReload == null) { scriptReload = new ScriptReload(retransformer); scriptReload.start(); } return true; }
-
5. Re: Trigger Load/Unload of Rules?
adinn Sep 30, 2014 5:37 AM (in response to damcelligott)Hi David,
That looks like a very neat way to do what you want. However, I am not sure that all the details are right as you present them above. You have
class AgentRetransformer extends Transformer
but
ScriptReload(Retransformer retransformer) {
. . .
Is your AgentRetransformer actually meant to extend Retransformer? Or is your ScriptReload class supposed to accept an AgentRetransformer. In the latter case I guess that means that you will have to cut and past quite a lot of the functionality of Retransformer into your own class. Clearly, it would be better if you could could adopt the former approach and just inherit the useful behaviour from my Retransformer, overriding the things you need to redefine.
If you find that is difficult to achieve then let me know what is problematic. I might be able to make changes which simplify the job e.g. I could make certain members protected rather than private or I could even factor out the retransform functionality into an abstract superclass decoupling it from the socket-listener specific functions that I use to do load, unload and agent state management. I'm not promising I will do this but if your use case makes it clear that this is a useful and sensible factorization of functions then I would be very keen to implement it so that others can reuse my agent in the same way as you want to.
Anyway, assuming some resolution of the issue above I think your design should now allow you to load both agents into the bootstrap loader so as to be able to instrument bootstrap classes. You will have to append both your agent jar and the byteman agent jar to the bootstrap loader path. If your premain still includes the args processing code from by my Main class (which searches args for "boot:/path/to/jar" and adds the corresponding jars to the bootstrap path) then you coudl just add them on the java command line by specifying the agent options:
-javaagent:/path/to/byteman.jar=boot:/path/to/byteman.jar,boot:/path/to/youragent.jar
Thanks very much for coming up with this nice solution and don't hesitate to let me know if there is something I can do to help make it work better.
regards,
Andrew Dinn
-
6. Re: Trigger Load/Unload of Rules?
damcelligott Oct 1, 2014 9:10 AM (in response to adinn)Hey Andrew,
I made the changes you suggested, inheriting the behavior from Retransformer is an obvious improvement.
Do you have any suggestions on the best way for me to add the new code snippets to maximize the usefulness of this discussion for other community members?
I am glad you brought up the decoupling of the retransformer and socket-listener specific functions because that was my next question.
I would volunteer to work on it now, but I need to talk to my companies law department before I can take on any work in an official capacity.
Ideally, I would much rather contribute then spend my time on a work-around!
My suggestion would be to keep your socket-listener as a default from the Java agent listener param (e.g. listener=true ), but possibly allowing the path to a jar in the Java agent listener param as a replacement for your socket-listener (e.g. listener=%CUSTOM_LISTENER_HOME%/File-Listener.jar ) that can be parsed out.
Some use cases off the top of my head.
- Rules can be loaded from any source
- Custom security implementations can be used to fit individual needs
- No longer relies on ports
- Makes it possible to negate the threat of a malicious insider
Hopefully by letting developers code their own listeners you can spend more time on the Byteman core functionality we all love so much!
Let me know your thoughts on the matter and I will update you when I hear back from the law department about contributing to the project.
Thanks for all your great feedback!
David McElligott