Java ASM de pseudo-code binaire Modification-Modification de la méthode d'organes
J'ai une méthode d'une classe dans un bocal dont le corps, je veux échanger avec mes propres. Dans ce cas, je veux juste avoir de la méthode print "GOT IT" de la console et return true;
Je suis en utilisant le système de chargeur pour charger les classes de la jarre. Je suis l'aide de la réflexion pour rendre le système de classloader être en mesure de charger des classes en bytecode. Cette partie semble fonctionner correctement.
Je suis la suite de la méthode de remplacement exemple ici: asm.ow2.org/current/asm-transformations.pdf.
Mon code est comme suit:
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();
}
}
Le problème semble être à mv.visitMethodInsn(access, cname, newName, desc);
dans generateMethodBody
à l'intérieur de MethodReplacer
.
- Je obtenir un "Illégal Type en constante piscine d'erreur".
Je ne suis pas sûr de ce que je suis en manque...mais après lecture et des tests pour environ 3 jours, je ne suis toujours pas arriver n'importe où.
[Modifier]
Dans le cas où vous vous le demandiez, tmcore
est un seul joueur "Objection" de jeu pour les avocats. Je fais cela pour le plaisir. Le programme lance le jeu et tout ce qui est de l'amende, retrait de la modification de MethodReplacer
rend le jeu se comportent comme prévu. Donc le problème semble être isolé du mauvais bytecode/modifications par moi à l'intérieur de la méthode de remplacement.
[EDIT2]
CheckClassAdapter.verify(cr, true, pw);
renvoie exactement la même bytecode que la fonction est censé avoir avant l'édition. C'est comme si les modifications ne sont pas effectuées.
[EDIT3]
copie de classtoload
commenté que par les commentaires
OriginalL'auteur emist | 2012-08-02
Vous devez vous connecter pour publier un commentaire.
Si vous utilisez Eclipse, vous devez installer Bytecode Contour - il est indispensable.
J'ai construit un petit test pour ce que vous voulez atteindre (ce qui devrait correspondre à la signature de votre méthode d'essai, vous devrez changer de forfait et classname):
requiert les éléments suivants bytecode pour construire la méthode:
Appels à
visitLineNumber
peut être omis. Donc, apparemment, vous êtes absent de toutes les étiquettes, j'ai oublié de charger les paramètres de la méthode, ne pas ignorer la valeur de retour, définissez les valeurs erronées pourvisitMaxs
(ce n'est pas forcément nécessaire, cela dépend de votre ClassWriter drapeaux si je me souviens bien) et n'a pas de visiter les variables (ou paramètres dans ce cas).En outre, votre classloading semble être un peu confus /foiré.
Je n'ai pas le pot (donc je ne peux pas dire si ces travaux), mais peut-être que vous pourriez remplacer Principale et Chargeur:
Principal:
Chargeur:
Si je omettre: mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(accès, cname, newName, desc); je n'obtiens pas d'erreur, mais la méthode fonctionne aussi avec son corps d'origine.
Aussi, depuis mon ClassWriter est construit avec COMPUTE_FRAMES, la valeur de visitMaxx n'a pas d'importance, c'est exact?
Vous avez raison, visitMaxs ne compte pas. Je vais modifier le code ci-dessus.
les changements apportés, pas d'erreur mais pas de changement dans la codeflow(il est en cours d'exécution comme si la méthode n'a pas changé). Est-il possible que je manque quelque chose à la façon dont mes classes sont en cours de chargement?
OriginalL'auteur Arne
ASKER LA RÉPONSE DE DÉPLACÉS À PARTIR DE LA QUESTION
Le bytecode java n'a jamais été le problème. C'est ainsi que j'était en train de charger le pot, ce qui rend impossible l'instrument le code.
Grâce à l'Ame pour m'aider à y faire face.
Le code suivant fonctionne:
PRINCIPAL
CHARGEUR:
MethodReplacer reste le même.
OriginalL'auteur