I have 2 other ways that could work. Either the JFrame-approach that only works to JAVA2D
or another way would be to find out the class of the main PApplet and then replace the old one with a new one. The one problem is that I can’t close JOGL windows as something like Window.getWindows() doesn’t work. So it would just open a second window with the old one frozen.
I found out about the created frame by looking at the class of the Frame wich was
processing.core.PApplet$7
The $7 being a telltale sign of a annonymous inner class.
So checking if the classnames match processing.core.PApplet\\$\\d+
should suffice in that regard.
Do you know if processing 4 will have all the system libraries especially thoes derived from tools.jar?
If so I would have access to byte-code manipulation wich would be a last resort…
I came up with another solution that uses another weakpoint specificy for when using JOGL. processing/PSurfaceJOGL.java at master · processing/processing · GitHub
At this point a new Thread is dispatched and by checking all active threads you can find out that it has the name “Thread-3” from there you can get the Runnable
Thread.target. This is a inner class of PSurfaceJOGL so you can use this$0 to get it. And PSurfaceJOGL has a field called sketch. This can be combined with the solution for JAVA2D to cover all possibilities.
This solution probably will work with processing 4 with a few adjustments as processing 4 also dispatches the same thread with the one difference that lambdas are used. processing4/PSurfaceJOGL.java at master · processing/processing · GitHub
Here is the code that worked in the end:
import processing.awt.*;
import processing.opengl.*;
import processing.core.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.lang.reflect.*;
/**This class finds the active PApplet (Processing program)*/
public class PAppletFinder {
/**This class takes the contents of the window and pieces together the PApplet
* @return PApplet of the current sketch.
* @param c contents of the Processing window (must be a SmoothCanvas)*/
public static PApplet get(Component c) {
PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
try {
PSurfaceAWT psa=(PSurfaceAWT) get(sc, "this$0", sc.getClass());
PApplet prg=(PApplet) get((PSurfaceNone)psa, "sketch", PSurfaceNone.class);
return prg;
}
catch(Exception e) {
e.printStackTrace();
}
return null;
}
public static PApplet foundPapplet=null;
/**The main method to be used when using the PAppletFinder*/
public static PApplet find() {
if (foundPapplet==null) foundPapplet=findFromWindow();
if (foundPapplet==null) foundPapplet=fromThreads();
return foundPapplet;
}
/**This looks out for windows and gives the contents of the right one to the get method
* @return PApplet of the current sketch.*/
public static PApplet findFromWindow() {
JFrame mainWindow=null;
java.awt.Window win[]=java.awt.Window.getWindows();
for (int i=0; i<win.length&&mainWindow==null; i++) if (win[i] instanceof JFrame) {
mainWindow=(JFrame) win[i];
Component c=mainWindow.getContentPane().getComponents()[0];
return get(c);
}
return null;
}
/**This is used to get the value of fields
* @return Object value of a field
* @param j the Object from which the value is taken
* @param target name of the field*/
public static Object get(Object j, String target, Class<?> ref) {
try {
Field f=ref.getDeclaredField(target);
f.setAccessible(true);
return f.get(j);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**When using P2D or P3D everything is built differently however there is a weakness to be found in PSurfaceJOGL as it dispatches a Thread using an annonymous inner class as Runnable*/
public static PApplet fromThreads() {
Set<Thread> threadSet=Thread.getAllStackTraces().keySet();
//println(Thread.currentThread().getThreadGroup());
for (Thread th : threadSet) if (th.getName().matches("Thread-3")) {
//println(" ");
//println("Thread: Class:"+th.getClass()+" Name:"+th.getName()+" Daemon:"+th.isDaemon());
{
try {
//for (Field f : th.getClass().getDeclaredFields()) println(f);
Field f=th.getClass().getDeclaredField("target");
f.setAccessible(true);
Object currinstance=null;
currinstance=f.get(th);
currinstance=getEnclosingInstance(currinstance);
f=PSurfaceJOGL.class.getDeclaredField("sketch");
f.setAccessible(true);
return (PApplet) (currinstance=f.get(currinstance));
}
catch(Exception e) {
System.err.println("An error occurred");
}
}
//innerClassesLoaded(th);
}
return null;
}
public static Object getEnclosingInstance(Object o) {
try {
Class<?> classused=o.getClass();
Field this0=classused.getDeclaredField("this$0");
this0.setAccessible(true);
return this0.get(o);
}
catch(Exception e) {
return null;
}
}
}
I had a friend run the code and it seems it doesn’t work for P2D and P3D in Processing 4.
The Problem is the following:
Java 17 made the .setAccessible methods for the java.lang.* classes not work anymore.
When I try to access a field of java.lang.Thread it won’t work anymore.
So I either try to find a new weak spot or try to understand the library for bypassing Project Jigsaw.
I’m currently analysing the permit-reflect library.
The new part is that since Java 17 the java.lang package is also not open anymore and I need to reflect a field from the thread object.
I don’t think it is possible to add more commandline arguments to an already running JVM.
Currently I’m experimenting with byte-code although on Java 12. I’m currently trying to load a custom class into java.lang. If that works maybe I can trick java into giving me access to the target Field of Thread. (Edit: Ok it doesn’t work)
This code might do the trick. It also works with processing 3 and this code is able to breach encapsulation. (As long as there is no SecurityManager in place)
PAppletFinder.java
import processing.awt.*;
import processing.opengl.*;
import processing.core.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.lang.reflect.*;
import sun.misc.*;
/**This class finds the active PApplet (Processing program)*/
public class PAppletFinder {
/**Unsafe instance used*/
public static Unsafe unsafe=null;
/**Unsafe Field offset*/
public static long fieldIndex=0;
/**Target field of the thread*/
public static Field threadTargField=null;
/**Here the three Fields unsafe,fieldIndex, threadTargField are initalized*/
static {
try {
Field f2=Unsafe.class.getDeclaredField("theUnsafe");
f2.setAccessible(true);
unsafe=(Unsafe) f2.get(null);
System.out.println("Unsafe instance: "+unsafe.toString());
threadTargField=Thread.class.getDeclaredField("target");
fieldIndex=unsafe.objectFieldOffset(threadTargField);
}
catch(Exception e) {
e.printStackTrace();
}
}
/**All results will be saved here*/
public static java.util.List<PApplet> allFoundPapplets=new LinkedList<PApplet>();
/**This class takes the contents of the window and pieces together the PApplet
* @return PApplet of the current sketch.
* @param c contents of the Processing window (must be a SmoothCanvas)*/
public static void get(Component c) {
System.out.println("Processing Window content");
PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
try {
PSurfaceAWT psa=(PSurfaceAWT) get(sc, "this$0", sc.getClass());
PApplet prg=(PApplet) get((PSurfaceNone)psa, "sketch", PSurfaceNone.class);
allFoundPapplets.add( prg);
}
catch(Exception e) {
System.err.println("Something went wrong");
e.printStackTrace();
}
}
public static PApplet foundPapplet=null;
/**The main method to be used when using the PAppletFinder*/
public static void findAll() {
findFromWindow();
fromThreads();
System.out.println("Detection done");
}
/**This looks out for windows and gives the contents of the right one to the get method
* @return PApplet of the current sketch.*/
public static void findFromWindow() {
System.out.println("Searching Windows for instances");
JFrame mainWindow=null;
java.awt.Window win[]=java.awt.Window.getWindows();
for (int i=0; i<win.length; i++) if (win[i] instanceof JFrame) {
mainWindow=(JFrame) win[i];
Component c=mainWindow.getContentPane().getComponents()[0];
if (c instanceof PSurfaceAWT.SmoothCanvas) get(c);
}
}
/**This is used to get the value of fields
* @return Object value of a field
* @param j the Object from which the value is taken
* @param target name of the field*/
public static Object get(Object j, String target, Class<?> ref) {
try {
Field f=ref.getDeclaredField(target);
f.setAccessible(true);
return f.get(j);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**When using P2D or P3D everything is built differently however there is a weakness to be found in PSurfaceJOGL as it dispatches a Thread using an annonymous inner class as Runnable*/
public static void fromThreads() {
System.out.println("Searching Threads for instances");
//This fetches all threads
Set<Thread> threadSet=Thread.getAllStackTraces().keySet();
//It iterates upon
for (Thread th : threadSet) {
iteration:
{
try {
//Field f=th.getClass().getDeclaredField("target");
//f.setAccessible(true);
Object currinstance=null;
currinstance=unsafe.getObject(th,fieldIndex);
if (!currinstance.getClass().toString().contains("PSurfaceJOGL$")) break iteration;
System.out.println("Weak spot found! "+th.getName());
currinstance=getEnclosingInstance(currinstance);
Field f=PSurfaceJOGL.class.getDeclaredField("sketch");
f.setAccessible(true);
allFoundPapplets.add((PApplet) (currinstance=f.get(currinstance)));
System.out.println("Detection successful");
}
catch(Exception e)
{
catchBlock:
{
if (e instanceof NoSuchFieldException||e instanceof NullPointerException) {
System.err.println("target not found "+th.getName());
break catchBlock;
}
System.err.println("Something went wrong!");
e.printStackTrace();
}
}
}
}
}
//This will try to get the enclosing instance of an object (this can also be a lambda)
public static Object getEnclosingInstance(Object o) {
//This code will work for Processing 3 (uses annonymous inner classes)
try {
Class<?> classused=o.getClass();
Field this0=classused.getDeclaredField("this$0");
this0.setAccessible(true);
return this0.get(o);
}
catch(Exception e) {
System.err.println("Can't find enclosing instance of Object trying to use Lambdas... (To be expected with Processing 4)");
}
//This code should work for Processing 4 (lambdas are used instead of inner classes, Lambdas use arg$1 instead this$0)
try {
Class<?> classused=o.getClass();
Field this0=classused.getDeclaredField("arg$1");
this0.setAccessible(true);
return this0.get(o);
}
catch(Exception e) {
System.err.println("Detection Failed!");
}
return null;
}
}
I did some minor changes to my code. Here is the version that is confirmed to work for P4:
PAppletFinder.java
import processing.awt.*;
import processing.opengl.*;
import processing.core.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.lang.reflect.*;
import sun.misc.*;
/**This class finds the active PApplet (Processing program)
@author NumericPrime*/
public class PAppletFinder {
/**Unsafe instance used*/
public static Unsafe unsafe=null;
/**Unsafe Field offset*/
public static long fieldIndex=0;
/**Target field of the thread*/
public static Field threadTargField=null;
/**Here the three Fields unsafe,fieldIndex, threadTargField are initalized*/
static {
try {
Field f2=Unsafe.class.getDeclaredField("theUnsafe");
f2.setAccessible(true);
unsafe=(Unsafe) f2.get(null);
//System.out.println("Unsafe instance: "+unsafe.toString());
threadTargField=Thread.class.getDeclaredField("target");
fieldIndex=unsafe.objectFieldOffset(threadTargField);
}
catch(Exception e) {
e.printStackTrace();
}
}
/**Getter-Method used to detect all PApplets. If the detection hasn't run yet this method will run it.
@param refresh if true: redetects all PApplets
@return all detected PApplets*/
public static PApplet[] getPApplets(boolean refresh) {
int allfpSize=0;
if (refresh) allFoundPapplets.clear();
if ((allfpSize=allFoundPapplets.size())==0) findAll();
return allFoundPapplets.toArray(new PApplet[allfpSize]);
}
/**All results will be saved here*/
public static java.util.List<PApplet> allFoundPapplets=new LinkedList<PApplet>();
/**This class takes the contents of the window and pieces together the PApplet
* @return PApplet of the current sketch.
* @param c contents of the Processing window (must be a SmoothCanvas)*/
public static void get(Component c) {
//System.out.println("Processing Window content");
PSurfaceAWT.SmoothCanvas sc=(PSurfaceAWT.SmoothCanvas) c;
try {
//gets the PSurface for the PApplet
PSurfaceAWT psa=(PSurfaceAWT) get(sc, "this$0", sc.getClass());
//gets the PApplet
PApplet prg=(PApplet) get((PSurfaceNone)psa, "sketch", PSurfaceNone.class);
allFoundPapplets.add(prg);
}
catch(Exception e) {
//System.err.println("Something went wrong");
e.printStackTrace();
}
}
/**This method tries to detect all PApplet used*/
public static void findAll() {
findFromWindow();
fromThreads();
//System.out.println("Detection done");
}
/**This looks out for processing-windows and gives the contents of the right one to the get method*/
public static void findFromWindow() {
//System.out.println("Searching Windows for instances");
JFrame mainWindow=null;
java.awt.Window win[]=java.awt.Window.getWindows();
for (int i=0; i<win.length; i++) if (win[i] instanceof JFrame) {
mainWindow=(JFrame) win[i];
Component c=mainWindow.getContentPane().getComponents()[0];
if (c instanceof PSurfaceAWT.SmoothCanvas) get(c);
}
}
/**This is used to get the value of fields
* @return Object value of a field
* @param j the Object from which the value is taken
* @param target name of the field
* @param ref class the field is taken from*/
public static Object get(Object j, String target, Class<?> ref) {
try {
Field f=ref.getDeclaredField(target);
f.setAccessible(true);
return f.get(j);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
/** When using P2D or P3D everything is built differently however there is a weakness to be found in
* PSurfaceJOGL as it dispatches a Thread using an annonymous inner class as Runnable*/
public static void fromThreads() {
//System.out.println("Searching Threads for instances");
//This fetches all threads
Set<Thread> threadSet=Thread.getAllStackTraces().keySet();
//It iterates upon the threads to find ones dispatched by PSurfaceJOGL
for (Thread th : threadSet) {
iteration:
{
try {
//Field f=th.getClass().getDeclaredField("target");
//f.setAccessible(true);
Object currinstance=null;
//Here Unsafe is used to breach encapsulation of java.lang
currinstance=unsafe.getObject(th, fieldIndex);
if (!currinstance.getClass().toString().contains("PSurfaceJOGL$")) break iteration;
//System.out.println("Weak spot found! "+th.getName());
//gets the PSurfaceJOGL
currinstance=getEnclosingInstance(currinstance);
//gets the PApplet
Field f=PSurfaceJOGL.class.getDeclaredField("sketch");
f.setAccessible(true);
allFoundPapplets.add((PApplet) (currinstance=f.get(currinstance)));
//System.out.println("Detection successful");
}
catch(Exception e)
{
catchBlock:
{
if (e instanceof NoSuchFieldException||e instanceof NullPointerException) {
//System.err.println("target not found "+th.getName());
//A NullPointerException may occur
break catchBlock;
}
//System.err.println("Something went wrong!");
e.printStackTrace();
}
}
}
}
}
//This will try to get the enclosing instance of an object (this can also be a lambda)
public static Object getEnclosingInstance(Object o) {
//This code will work for Processing 3 (uses annonymous inner classes)
try {
Class<?> classused=o.getClass();
Field this0=classused.getDeclaredField("this$0");
this0.setAccessible(true);
return this0.get(o);
}
catch(Exception e) {
//System.err.println("Can't find enclosing instance of Object, trying to use Lambdas... (To be expected with Processing 4)");
}
//This code should work for Processing 4 (lambdas are used instead of inner classes, Lambdas use arg$1 instead this$0)
try {
Class<?> classused=o.getClass();
Field this0=classused.getDeclaredField("arg$1");
this0.setAccessible(true);
return this0.get(o);
}
catch(Exception e) {
//System.err.println("Detection Failed!");
}
return null;
}
}