मेरे पास एक जार में एक कक्षा का एक तरीका है जिसका शरीर मैं अपने साथ बदलना चाहता हूं। इस मामले में मैं सिर्फ कंसोल पर "इसे प्राप्त करें" विधि प्रिंट करना चाहता हूं और सत्य वापस करना चाहता हूं;जावा एएसएम बाइटकोड संशोधन-बदलती विधि निकायों
मैं जार के वर्गों को लोड करने के लिए सिस्टम लोडर का उपयोग कर रहा हूं। मैं सिस्टम क्लासलोडर को बाइटकोड द्वारा कक्षाएं लोड करने में सक्षम होने के लिए प्रतिबिंब का उपयोग कर रहा हूं। यह हिस्सा सही ढंग से काम कर रहा प्रतीत होता है।
मैं यहां पाए गए विधि प्रतिस्थापन उदाहरण का पालन कर रहा हूं: asm.ow2.org/current/asm-transformations.pdf।
मेरे कोड इस प्रकार है:
public class Main
{
public static void main(String[] args)
{
URL[] url = new URL[1];
try
{
url[0] = new URL("file:////C://Users//emist//workspace//tmloader//bin//runtime//tmgames.jar");
verifyValidPath(url[0]);
}
catch (Exception ex)
{
System.out.println("URL error");
}
Loader l = new Loader();
l.loadobjection(url);
}
public static void verifyValidPath(URL url) throws FileNotFoundException
{
File filePath = new File(url.getFile());
if (!filePath.exists())
{
throw new FileNotFoundException(filePath.getPath());
}
}
}
class Loader
{
private static final Class[] parameters = new Class[] {URL.class};
public static void addURL(URL u) throws IOException
{
URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class sysclass = URLClassLoader.class;
try
{
Method method = sysclass.getDeclaredMethod("addURL", parameters);
method.setAccessible(true);
method.invoke(sysloader, new Object[] {u});
}
catch (Throwable t)
{
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}
}
private Class loadClass(byte[] b, String name)
{
//override classDefine (as it is protected) and define the class.
Class clazz = null;
try
{
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class cls = Class.forName("java.lang.ClassLoader");
java.lang.reflect.Method method =
cls.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
// protected method invocaton
method.setAccessible(true);
try
{
Object[] args = new Object[] {name, b, new Integer(0), new Integer(b.length)};
clazz = (Class) method.invoke(loader, args);
}
finally
{
method.setAccessible(false);
}
}
catch (Exception e)
{
e.printStackTrace();
System.exit(1);
}
return clazz;
}
public void loadobjection(URL[] myJar)
{
try
{
Loader.addURL(myJar[0]);
//tmcore.game is the class that holds the main method in the jar
/*
Class<?> classToLoad = Class.forName("tmcore.game", true, this.getClass().getClassLoader());
if(classToLoad == null)
{
System.out.println("No tmcore.game");
return;
}
*/
MethodReplacer mr = null;
ClassReader cr = new ClassReader("tmcore.objwin");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
MethodVisitor mv = null;
try
{
mr = new MethodReplacer(cw, "Test", "(Ljava/lang/String;ZLjava/lang/String;)Z");
}
catch (Exception e)
{
System.out.println("Method Replacer Exception");
}
cr.accept(mr, ClassReader.EXPAND_FRAMES);
PrintWriter pw = new PrintWriter(System.out);
loadClass(cw.toByteArray(), "tmcore.objwin");
Class<?> classToLoad = Class.forName("tmcore.game", true, this.getClass().getClassLoader());
if(classToLoad == null)
{
System.out.println("No tmcore.game");
return;
}
//game doesn't have a default constructor, so we need to get the reference to public game(String[] args)
Constructor ctor = classToLoad.getDeclaredConstructor(String[].class);
if(ctor == null)
{
System.out.println("can't find constructor");
return;
}
//Instantiate the class by calling the constructor
String[] args = {"tmgames.jar"};
Object instance = ctor.newInstance(new Object[]{args});
if(instance == null)
{
System.out.println("Can't instantiate constructor");
}
//get reference to main(String[] args)
Method method = classToLoad.getDeclaredMethod("main", String[].class);
//call the main method
method.invoke(instance);
}
catch (Exception ex)
{
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
}
public class MethodReplacer extends ClassVisitor implements Opcodes
{
private String mname;
private String mdesc;
private String cname;
public MethodReplacer(ClassVisitor cv, String mname, String mdesc)
{
super(Opcodes.ASM4, cv);
this.mname = mname;
this.mdesc = mdesc;
}
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces)
{
this.cname = name;
cv.visit(version, access, name, signature, superName, interfaces);
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions)
{
String newName = name;
if(name.equals(mname) && desc.equals(mdesc))
{
newName = "orig$" + name;
generateNewBody(access, desc, signature, exceptions, name, newName);
System.out.println("Replacing");
}
return super.visitMethod(access, newName, desc, signature, exceptions);
}
private void generateNewBody(int access, String desc, String signature, String[] exceptions,
String name, String newName)
{
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(access, cname, newName, desc);
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("GOTit!");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
mv.visitInsn(ICONST_0);
mv.visitInsn(IRETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
समस्या MethodReplacer
अंदर generateMethodBody
में mv.visitMethodInsn(access, cname, newName, desc);
पर हो रहा है।
मुझे "निरंतर पूल में अवैध प्रकार" त्रुटि मिलती है।
मुझे यकीन नहीं है कि मैं क्या खो रहा हूं ... लेकिन पढ़ने और परीक्षण के बाद लगभग 3 दिनों में मुझे अभी भी कहीं भी नहीं मिल रहा है।
[संपादित करें]
यदि आप सोच रहे थे, tmcore
एक एकल खिलाड़ी "अनापत्ति" वकीलों के लिए खेल है। मैं इसे मजाक के लिए कर रहा हूं। कार्यक्रम ने सफलतापूर्वक गेम लॉन्च किया है और सब कुछ ठीक है, MethodReplacer
से संशोधनों को हटाने से गेम को डिज़ाइन किया गया है। तो यह तरीका विधि प्रतिस्थापन के अंदर मेरे द्वारा खराब बाइटकोड/संशोधनों के लिए अलग किया गया प्रतीत होता है।
[EDIT2]
CheckClassAdapter.verify(cr, true, pw);
ठीक उसी बाईटकोड कि समारोह संपादन करने से पहले है करने के लिए माना जाता है देता है। ऐसा लगता है कि बदलाव नहीं किए जा रहे हैं।
[EDIT3]classtoload
की
प्रति टिप्पणी
हाय एमे, मेरे पास प्लगइन स्थापित है। आपको इसके बारे में सही विचार है कि मैं क्या करने की कोशिश कर रहा हूं इसके अलावा: सार्वजनिक बूलियन टेस्ट (स्ट्रिंग ए, बूलियन बी, स्ट्रिंग सी) { टेस्ट (ए, बी, सी); System.out.println ("GOTIT"); झूठी वापसी; } मैं बस ऐसा करने के लिए देख रहा हूं: सार्वजनिक बूलियन टेस्ट (स्ट्रिंग ए, बूलियन बी, स्ट्रिंग सी) { System.out.println ("GOTIT"); झूठी वापसी; } बाइटकोड जो प्लगइन जेनरेट करता है साथ ही ASMifer मेरे जेनरोडी विधि के अंदर बाइटकोड से मेल खाता है। – emist
यदि मैं छोड़ देता हूं: mv.visitVarInsn (Opcodes.ALOAD, 0); mv.visitMethodInsn (एक्सेस, cname, newName, desc); मुझे कोई त्रुटि नहीं है लेकिन विधि भी इसके मूल शरीर के साथ चलती है। – emist
इसके अलावा, चूंकि मेरा क्लासवाइटर COMPUTE_FRAMES के साथ बनाया गया है, विज़िटमैक्स का मान कोई फर्क नहीं पड़ता, सही? – emist