Yes , so what im trying to do is getting the class bytes of every loaded class loaded by the jvm during run time . Instrumentation wont work for this case because the program im trying to load has encrypted his classes files and load it with its own class loader.
Here's my attempt : https://gist.github.com/MalikDz/944cae9c168fa05fbd0a
here the output (error) : https://gist.github.com/MalikDz/fdf20df16b951d41cb78
Thanks a lot !
You can use a Java Agent to do this trick:
The Agent is very straightforward: It registers a class transformer, which can get access to the byte-code:
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassFileTransformer;
public class ClassDumpAgent
{
/**
* This method is called before the application’s main-method is called, when
* this agent is specified to the Java VM.
**/
public static void premain(String agentArgs, Instrumentation inst)
{
ClassFileTransformer trans = new ClassDumpTransformer();
inst.addTransformer(trans);
}
}
The ClassFileTransformer that is used simply dumps the byte-array with byte-code to the file system:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class ClassDumpTransformer implements ClassFileTransformer
{
private File rootFolder = new File("C:\\temp\\dump");
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException
{
File dumpFile = new File(rootFolder,className+".class");
dumpFile.getParentFile().mkdirs();
try {
FileOutputStream out = new FileOutputStream(dumpFile);
try {
out.write(classfileBuffer);
} finally {
out.close();
}
} catch (IOException e) {
throw new IllegalClassFormatException(e.getMessage());
}
return classfileBuffer;
}
}
To package this class dumping agent, you need to JAR the two classes and include a MANIFEST.MF for this agent:
Manifest-Version: 1.0
PreMain-Class: ClassDumpAgent
To run the application with this agent, use a command-line like this:
java -javaagent:cldumpagent.jar MyApplication
Some remarks about the solution:
The classes are dumped in a hardcoded folder (C:\TEMP\DUMP), you
might want to change this.
The transformer will dump all classes, including the JDK Runtime. You might want to filter which packages are dumped.
Be careful with reverse-engineering, in some countries this might be considered illegal.
Barry
Related
I've found a somehow unexpected behaviour using JVM Security Manager custom policies.
repo: https://github.com/pedrorijo91/jvm-sec-manager
in branch master, go into the /code folder:
custom policy file grants File read permission for file ../allow/allow.txt
no permission for the file ../deny/deny.txt
the code in the HelloWorld.java tries to read both files
There's a run.sh script to run the command
Now everything works as expected: the allowed file reads, but the other throws a security exception: java.security.AccessControlException: access denied ("java.io.FilePermission" "../deny/deny.txt" "read")
But if I move both files (../allow/allow.txt and ../deny/deny.txt) to the code folder (changing the custom policy and the java code to use those files), I get no exception. (branch 'unexpected')
Is the current directory a special case or something else is happening?
Brief explanation
This behaviour is documented in a number of places:
FilePermission's overview;
the Permissions in the JDK document; and
the URLClassLoader#getPermissions(CodeSource) method.
The latter two reiterate the closing note of the first one, which states that:
Code can always read a file from the same directory it's in (or a subdirectory of that directory); it does not need explicit permission to do so.
In other words, if
(HelloWorld.class.getProtectionDomain().getCodeSource().implies(
new CodeSource(new URL("file:" + codeDir),
(Certificate[]) null)) == true)
then HelloWorld will by default be granted read access to the denoted directory and its descendants. Particularly for the code directory itself this should make some intuitive sense, as otherwise the class would be unable to even access public-access classes within its very package.
The full story
It is basically up to the ClassLoader: If it statically assigned any Permissions to the ProtectionDomain to which it mapped the class--which applies to both java.net.URLClassLoader and sun.misc.Launcher$AppClassLoader (the OpenJDK-specific default system class loader)--these permissions will always be accorded to the domain, regardless of the Policy in effect.
Workarounds
The typical "quick-n'-dirty" workaround to anything authorization-related is to extend SecurityManager and override the methods irking you; i.e. in this case the checkRead group of methods.
For a more thorough solution that doesn't reduce the flexibility of AccessController and friends, on the other hand, you would have to write a class loader that at the very least overrides URLClassLoader#getPermissions(CodeSource) and/or restricts loaded classes' domains' CodeSources down to the file level (code sources of domains assigned by default by URLClassLoader and AppClassLoader imply (recursively) the .class file's classpath entry (JAR or directory)). For further granularity, your loader might as well assign instances of your own domain subclass, and/or domains encapsulating code sources of your own subclass, overriding respectively ProtectionDomain#implies(Permission) and/or CodeSource#implies(CodeSource); the former could for example be made to support "negative permission" semantics, and the latter could base code source implication on arbitrary logic, potentially decoupled from physical code location (think e.g. "trust levels").
Clarification as per the comments
To prove that under a different class loader these permissions actually matter, consider the following example: There are two classes, A and B; A has the main method, which simply calls a method on B. Additionally, the application is launched using a different system class loader, which a) assigns domains on a per-class basis (rather than on a per-classpath-entry basis, as is the default) to classes it loads, without b) assigning any permissions to these domains.
Loader:
package com.example.q45897574;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
public class RestrictiveClassLoader extends URLClassLoader {
private static final Pattern COMMON_SYSTEM_RESOURCE_NAMES = Pattern
.compile("(((net\\.)?java)|(java(x)?)|(sun|oracle))\\.[a-zA-Z0-9\\.\\-_\\$\\.]+");
private static final String OWN_CLASS_NAME = RestrictiveClassLoader.class.getName();
private static final URL[] EMPTY_URL_ARRAY = new URL[0], CLASSPATH_ENTRY_URLS;
private static final PermissionCollection NO_PERMS = new Permissions();
static {
String[] classpathEntries = AccessController.doPrivileged(new PrivilegedAction<String>() {
#Override
public String run() {
return System.getProperty("java.class.path");
}
}).split(File.pathSeparator);
Set<URL> classpathEntryUrls = new LinkedHashSet<>(classpathEntries.length, 1);
for (String classpathEntry : classpathEntries) {
try {
URL classpathEntryUrl;
if (classpathEntry.endsWith(".jar")) {
classpathEntryUrl = new URL("file:jar:".concat(classpathEntry));
}
else {
if (!classpathEntry.endsWith("/")) {
classpathEntry = classpathEntry.concat("/");
}
classpathEntryUrl = new URL("file:".concat(classpathEntry));
}
classpathEntryUrls.add(classpathEntryUrl);
}
catch (MalformedURLException mue) {
}
}
CLASSPATH_ENTRY_URLS = classpathEntryUrls.toArray(EMPTY_URL_ARRAY);
}
private static byte[] readClassData(URL classResource) throws IOException {
try (InputStream in = new BufferedInputStream(classResource.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
while (in.available() > 0) {
out.write(in.read());
}
return out.toByteArray();
}
}
public RestrictiveClassLoader(ClassLoader parent) {
super(EMPTY_URL_ARRAY, parent);
for (URL classpathEntryUrl : CLASSPATH_ENTRY_URLS) {
addURL(classpathEntryUrl);
}
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name == null) {
throw new ClassNotFoundException("< null >", new NullPointerException("name argument must not be null."));
}
if (OWN_CLASS_NAME.equals(name)) {
return RestrictiveClassLoader.class;
}
if (COMMON_SYSTEM_RESOURCE_NAMES.matcher(name).matches()) {
return getParent().loadClass(name);
}
Class<?> ret = findLoadedClass(name);
if (ret != null) {
return ret;
}
return findClass(name);
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String modifiedClassName = name.replace(".", "/").concat(".class");
URL classResource = findResource(modifiedClassName);
if (classResource == null) {
throw new ClassNotFoundException(name);
}
byte[] classData;
try {
classData = readClassData(classResource);
}
catch (IOException ioe) {
throw new ClassNotFoundException(name, ioe);
}
return defineClass(name, classData, 0, classData.length, constructClassDomain(classResource));
}
#Override
protected PermissionCollection getPermissions(CodeSource codesource) {
return NO_PERMS;
}
private ProtectionDomain constructClassDomain(URL codeSourceLocation) {
CodeSource cs = new CodeSource(codeSourceLocation, (Certificate[]) null);
return new ProtectionDomain(cs, getPermissions(cs), this, null);
}
}
A:
package com.example.q45897574;
public class A {
public static void main(String... args) {
/*
* Note:
* > Can't we set the security manager via launch argument?
* No, it has to be set here, or bootstrapping will fail.
* > Why?
* Because our class loader's domain is unprivileged.
* > Can't it be privileged?
* Yes, but then everything under the same classpath entry becomes
* privileged too, because our loader's domain's code source--which
* _its own_ loader creates, thus escaping our control--implies _the
* entire_ classpath entry. There are various workarounds, which
* however fall outside of this example's scope.
*/
System.setSecurityManager(new SecurityManager());
B.b();
}
}
B:
package com.example.q45897574;
public class B {
public static void b() {
System.out.println("success!");
}
}
Unprivileged test:
Make sure nothing is granted at the policy level; then run (assuming a Linux-based OS--modify classpath as appropriate):
java -cp "/home/your_user/classpath/" \
-Djava.system.class.loader=com.example.q45897574.RestrictiveClassLoader \
-Djava.security.debug=access=failure com.example.q45897574.A
You should get a NoClassDefFoundError, along with a failed FilePermission for com.example.q45897574.A.
Privileged test:
Now grant the necessary permission to A (again make sure to correct both the codeBase (code source URL) and permission target name):
grant codeBase "file:/home/your_user/classpath/com/example/q45897574/A.class" {
permission java.io.FilePermission "/home/your_user/classpath/com/example/q45897574/B.class", "read";
};
...and re-run. This time execution should complete successfully.
We have few classes which extends a base class. We noticed that we use quit a few sleeps method and we wanted to log when a sleep occurs. Is there a way to override the Thread.sleep method in which I can add some custom logic ( ie logging) and then just call the actual Thread.sleep()? This way I wouldn't have to change all the places where Thread.sleep is being used in my bases classes. I'm open to other options as well.
You cannot override Thread.sleep method, you cannot instrument or transform it either as it's a native method. One way is to automatically add logging to all places which call Thread.sleep using a Java Agent.
While the following approach works and is quite fun, in your case it's probably much better to refactor all calls to the Thread.sleep into a separate method and add the logging there.
You can find an introduction to Java Agents here. In a nutshell it's a special mechanism which allows (among other) transformation of loaded Java byte code. The following example of an Java Agent class automatically enhances all calls to the Thread.sleep with System.out logging and measure time spent in the method:
package fi.schafer.agent;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class LoggingAgent {
public static void premain(String agentArgument, Instrumentation instrumentation) throws Exception {
instrumentation.addTransformer(new ClassFileTransformer() {
#Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
return doClass(className, classBeingRedefined, classfileBuffer);
}
});
}
/**
* Method enhances calls to Thread.sleep with logging.
*/
private static byte[] doClass(String name, Class clazz, byte[] b) {
ClassPool pool = ClassPool.getDefault();
CtClass cl = null;
try {
cl = pool.makeClass(new java.io.ByteArrayInputStream(b));
final CtMethod[] targetMethods = cl.getDeclaredMethods();
for (CtMethod targetMethod : targetMethods) {
targetMethod.instrument(new ExprEditor() {
public void edit(final MethodCall m) throws CannotCompileException {
if ("java.lang.Thread".equals(m.getClassName()) && "sleep".equals(m.getMethodName())) {
m.replace("{long startMs = System.currentTimeMillis(); " +
"$_ = $proceed($$); " +
"long endMs = System.currentTimeMillis();" +
"System.out.println(\"Logging Thread.sleep call execution, ms: \" + (endMs-startMs));}");
}
}
});
return cl.toBytecode();
}
} catch (Exception e) {
System.err.println("Could not instrument " + name
+ ", exception : " + e.getMessage());
} finally {
if (cl != null) {
cl.detach();
}
}
return b;
}
}
You will need to compile it into a loggerAgent.jar file and include the following META-INF/MANIFEST.MF in it:
Manifest-Version: 1.0
Premain-Class: fi.schafer.agent.LoggingAgent
Boot-Class-Path: javassist.jar
Download JavaAssist and put it into same folder as your jar with compiled Agent. Run your application with parameter -javaagent:loggerAgent.jar.
You can download a full example. Just extract it, open folder release and run the application with java -cp loggerAgent.jar -javaagent:loggerAgent.jar Test
More information and more examples can be found in this excellent article.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Execute another jar in a java program
Basically I want to run an external .jar from the one I'm working on now.
I.e. I want to run foo.jar from bar.jar
I've tried using Runtime and Process to execute "java -jar foo.jar", but it opens foo.jar and then it closes immediately. Any tips?
The easiest solution (as Thorn pointed out) would be to have the jar as a build-time dependency and invoke it statically from your code:
ExternalJarMainClass.main(new String[]{"arguments", "to", "main"});
But if that is not possible, you can use a URLClassLoader to load the jar dynamically. If the jar is indeed runnable, then you can read the main class from META-INF/MANIFEST.MF and invoke main via reflection.
This is a different approach from creating a separate process, as the external code will run in the same process as your application. Perhaps this is desirable, perhaps not - that depends on the situation.
Below's a (hastily written and flawed) sample helper class that does just that.
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JarRunner {
private final Method entryPoint;
public JarRunner(File jarFile) throws
ClassNotFoundException,
IOException,
NoSuchMethodException {
URL jarUrl = jarFile.toURI().toURL();
URLClassLoader loader = URLClassLoader.newInstance(
new URL[]{jarUrl});
URL manifestUrl = loader.findResource("META-INF/MANIFEST.MF");
String manifest = resourceToString(manifestUrl);
Class<?> clazz = loader.loadClass(findMainClassName(manifest));
entryPoint = clazz.getMethod("main", String[].class);
}
public void run(String[] argsToMain) throws
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException {
entryPoint.invoke(null, (Object) argsToMain);
}
private static String resourceToString(URL url) throws IOException {
InputStream contentStream = url.openStream();
try {
BufferedReader r = new BufferedReader(
new InputStreamReader(contentStream));
StringBuilder sb = new StringBuilder();
String line = null;
do {
line = r.readLine();
if (line != null) {
sb.append(line).append('\n');
}
} while (line != null);
return sb.toString();
} finally {
contentStream.close();
}
}
private static String findMainClassName(String manifest) {
Matcher m = MAIN_CLASS_PATTERN.matcher(manifest);
if (m.find()) {
return m.group(1);
}
return null;
}
private static final Pattern MAIN_CLASS_PATTERN =
Pattern.compile("Main-Class: (.+)");
}
Sample usage:
JarRunner jr = new JarRunner(new File("path/to/MyJar.jar"));
jr.run(new String[]{"arg1", "arg2"});
Can you run foo.jar directly? Does it have a manifest with a main method?
I am guessing that you can. So you want to launch the main method inside of a class like foo.Main
Option 1: Include foo.jar in the classpath. If you are using an IDE, then this just means adding foo.jar as a library. Now you are free to import the package (lets call the package foo) and launch your second java program from a single line of Java code:
foo.Main.main(null);
Most likely you would want to do this in a separate thread:
class FooRunner extends Thread {
public void run() {
foo.Main.main(null);
}
}
and then you would launch with this:
FooRunner secondaryApp = new FooRunner();
secondaryApp.start();
Option 2
You can load the classes in the Foo package at runtime using a class loader.
See the Javadocs for java.lang.ClassLoader and this example of a CustomClassLoader
Check java -jar foo.jar runs correctly from command line. Also ensure java is there in the path. It may be better to provide absolute path to java.exe in the arguments.
Please consider using ProcessBuilder instead of Runtime.
Our java application relies on some resources which are available on a network share. This network share is located on the classpath, and the resources are read at runtime using MyClass.class.getResourceAsStream("/myfile.jpg").
java -Djava.class.path=\\myserver\myshare:C:\myjar.jar MainClass
When the share is available at startup, everything runs smoothly. Image and properties files which are located in the share can be read using getResourceAsStream(). However, if the share is not online when the application starts, even if the share comes online before any resources are read, they cannot be read using getResourceAsStream().
Doing some digging using eclispse + decompiler, I noticed one difference. The default classloader inherits from URLClassLoader, and its ucp member (URLClassPath) contains a list of URLClassPath.Loader instances. In the first scenario, it contains a URLClassPath.FileLoader and a URLClassPath.JarLoader. In the second scenario, it only contains a jar loader.
It's like java determines that the classpath entry is invalid and completely discards it.
Why is this? How can I avoid it?
Update
I am unable to change the mechanism by which we are loading resources because of a few reasons:
There are far too many areas which currently load files this way for me change at the moment
There are situations where by the resource is actually being loaded by a third party component
I have no problem creating a custom class loader, I just need some guidance on how to do it.
I tried with this, but was unable to get expected results:
import java.net.URL;
import java.net.URLClassLoader;
public class MyUrlClassLoader extends URLClassLoader {
public MyUrlClassLoader(ClassLoader parent) {
super(new URL[0], parent);
System.out.println("MyUrlClassLoader ctor");
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("url find class " + name);
return super.findClass(name);
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("url load class " + name);
return super.loadClass(name);
}
#Override
public URL getResource(String name) {
System.out.println("url get resource " + name);
return super.getResource(name);
}
}
import java.net.URL;
public class ClassLoaderMain {
public static void main(String[] args) throws ClassNotFoundException {
URL url = ClassLoaderMain.class.getResource("/myfile.txt");
System.out.print("Loaded? ");
System.out.println(url != null);
System.out.println(ClassLoaderMain.class.getClassLoader().toString());
System.out.println(MyUrlClassLoader.class.getClassLoader().toString());
System.out.println(FakeClass.class.getClassLoader().toString());
}
}
When I run java -cp . -Djava.system.class.loader=MyUrlClassLoader ClassLoaderMain
This outputs:
MyUrlClassLoader ctor
url load class java.lang.System
url load class java.nio.charset.Charset
url load class java.lang.String
url load class ClassLoaderMain
Loaded? true
sun.misc.Launcher$AppClassLoader#923e30
sun.misc.Launcher$AppClassLoader#923e30
sun.misc.Launcher$AppClassLoader#923e30
So my class loader is being created, and load class is being called, but it doesn't appear to be the class loader for the classes it is loading?
I ended up resolving this by creating my own ClassLoader, deriving from URLClassLoader.
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
public class CustomClassLoader extends URLClassLoader {
public CustomClassLoader(ClassLoader parent) {
// System classloader will filter inaccessible URLs.
// Force null parent to avoid using system classloader.
super(createURLReferences(), null);
}
/**
* Build an array of URLs based on the java.class.path property.
* #return An array of urls to search for classes.
*/
private static URL[] createURLReferences() {
String classpath = System.getProperty("java.class.path");
String[] classpathEntries = classpath.split(System.getProperty("path.separator"));
List<URL> urls = new ArrayList<URL>();
for (String classpathEntry : classpathEntries) {
File classpathFile = new File(classpathEntry);
URI uri = classpathFile.toURI();
try {
URL url = uri.toURL();
urls.add(url);
} catch (MalformedURLException e) {
System.out.println("Ignoring classpath entry: " + classpathEntry);
}
}
return urls.toArray(new URL[urls.size()]);
}
}
Using a sample from xSocket which will run xSocketHandler as a new process, I want to customize and moving all of these code into other java file, can I copy public class xSocketDataHandler implements IDataHandler and paste into different filename say main.java?
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import org.xsocket.*;
import org.xsocket.connection.*;
public class xSocketDataHandler implements IDataHandler
{
public boolean onData(INonBlockingConnection nbc) throws IOException, BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException
{
try
{
String data = nbc.readStringByDelimiter("\0");
//nbc.write("Reply" + data + "\0");
nbc.write("+A4\0");
if(data.equalsIgnoreCase("SHUTDOWN"))
xSocketServer.shutdownServer();
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
}
return true;
}
}
No, you can't do that without reducing the visibility of xSocketDataHandler to default. If you don't want to do that, your file name should be xSocketDataHandler.java
You must be having class xSocketDataHandler in a file of the same name already since it is public. You could move other non public classes in this file to Main.java instead.
A public class will need to be in a file named according to the class, so in this case it would be xSocketDataHandler.java.
Convention is also to name java classes starting with an upper-case letter, so it would be public class XSocketDataHandler and file XSocketDataHandler.java. This isn't required, though.