2012-12-18 19 views
11

Sto usando ASM e vogliono riscrivere qualcosa di simile:Iniettando un metodo Java _prima_ un altro metodo è chiamato

someMethod().targetMethod(args...) 

a:

someMethod().injectedMethod(arg).targetMethod(args...) 

Il guaio è che io non so quale sia la metodo prima è, conosco solo il metodo di destinazione (quindi trovare someMethod() e iniettare dopo che non è un'opzione).

Ho anche molte versioni del metodo di destinazione, con diversi set di parametri con cui desidero lavorare.

utilizzando ASM posso facilmente trovare il metodo di destinazione invocazione, ma purtroppo lo stack operando in quel punto è:

[ argN, ..., arg1, instance, ... ] 

E mentre io riesco a capire quanto in basso l'istanza sarà, non c'è bytecode I può iniettare che lo leggerà. So che puoi farlo per un massimo di 4 parametri usando trucchi con comandi dup, ma ho bisogno di una soluzione generale.

Potrei aggiungere un po 'di variabili locali e copiare tutto dallo stack, duplicare l'istanza puntata e rimettere tutto su, ma questa è un'inefficienza di runtime che davvero non voglio.

Quello che penso potrebbe funzionare è se potessi tenere traccia delle istruzioni che sono state responsabili dell'inserimento del puntatore dell'istanza nello stack e quindi è stato possibile inserire la chiamata al metodo piuttosto che al richiamo del metodo di destinazione. Tuttavia non ho avuto fortuna nel trovare qualcosa che mi aiuti a farlo.

So che cose come AspectJ lo consentono, ma devono farlo per un sacco di classi mentre si caricano e AspectJ è troppo lento.

Qualcuno può indicarmi strumenti di analisi basati su ASM che potrebbero consentirmi di farlo o qualcuno potrebbe pensare ad un approccio migliore per l'iniezione di una chiamata di metodo prima di un'altra?

+1

Non capisco, hai davvero bisogno di usare ASM? perché sembra che tu stia cercando di hackerare un Jar esistente. Per questo sarebbe meglio decompilarlo ......... Se no, e stai scrivendo un'applicazione java, non puoi usare l'aggregazione per quello? – fredcrs

+0

Non riesco a decompilare le classi perché posso collegarlo solo come javaagent in fase di caricamento della classe. Non sto né "hackerando un jar esistente" o "scrivendo un'applicazione Java"; Sto iniettando bytecode in classi mentre vengono caricati. – David

+0

Ho lavorato con [Soot] (http://www.sable.mcgill.ca/soot/) mentre ero a scuola e facevo cose esattamente come questa (OK forse un po 'meno complicato). Guarda. Sfortunatamente, non so se è costruito su ASM. L'unica altra opzione che viene in mente è AspectJ. – arin

risposta

1

In generale è possibile scaricare i valori dallo stack in variabili locali temporanee. L'adattatore LocalVariableSorter dal pacchetto comune di ASM rende tutto più semplice. Ma in realtà i metodi con più di 4 argomenti sono un caso raro. In ogni caso è ancora molto più semplice e solido, quindi eseguire analisi del flusso di dati in piena esecuzione in fase di esecuzione.

ASM org.objectweb.asm.tree.analysis fornisce funzionalità per l'analisi del flusso di dati, ad es. è possibile utilizzare SourceInterpreter per tenere traccia di quali istruzioni hanno prodotto i valori su ciascuna variabile e impilare gli slot. Vedi ASM User Guide per maggiori dettagli.

+0

cosa intendi con un'analisi completa dei dati in fase di esecuzione? e come lo faresti? – vijay

+1

Ho aggiunto alcuni chiarimenti e collegamenti diretti alla documentazione. –

2

Se ho capito bene la tua domanda, ho raggiunto lo stesso risultato di quello che vuoi fare, ma in un modo diverso.

Utilizzando la modifica del codice byte guidata da evento ASM, ho dapprima ribattezzato someMethod (arg, arg, arg) per copiareOf_someMethod (arg, arg, arg). Poi ho creato un nuovo metodo chiamato someMethod (arg, arg, arg) che ha fatto qualche elaborazione e poi ha chiamato copyOf_someMethod (arg, arg, arg).

ho fatto il metodo rinominare nel metodo visitMethod (..) del ClassVisitor ho implementato:

MethodVisitor methodVisitor = 
    super.visitMethod(
     methodAccess, "copyOf_" + methodName, methodDesc, 
      methodSignature, methodExceptions); 

return methodVisitor; 

Nel visitMethod (..) Ho anche memorizzati tutti i dettagli metodo di firma in variabili di classe pronti a essere usato nel metodo visitEnd().

realtà ho memorizzato i dettagli del metodo in un oggetto MethodDetail e messo in una coda:

private Queue<MethodDetail> methodDetails = new LinkedList<MethodDetail>(); 

ho creato la nuova implementazione di someMethod (arg, arg, arg) utilizzando il metodo visitEnd() del ClassVisitor I implementato. Ho usato ASMFier per generare il codice da inserire nel metodo visitEnd(). L'implementazione ha fatto uso dei dettagli che ho archiviato precedentemente in visitMethod (..). La nuova implementazione ha fatto qualche elaborazione e poi ha chiamato copyOf_someMethod(). Nel metodo visitEnd() ho estratto tutti i MethodDetail della coda e per ogni MethodDetail chiamato codice ASM che avevo precedentemente generato da ASMFier.

Utilizzando questo disegno ho creato un proxy per un metodo che ha fatto un po 'di elaborazione e poi ha chiamato il metodo originale. Si noti che il metodo originale è stato rinominato copyOf_someMethod (..). Si noti inoltre che ho fornito una nuova implementazione per il metodo originale che ha funzionato come proxy.

Per supportare più argomenti, ho utilizzato ASMFier per generare codice diverso per 1 arg, 2 arg, 3 arg, e.t.c. Ho supportato fino a 7 argomenti e ho lanciato un'eccezione non supportata se il metodo da proxy aveva più di 7 argomenti. Nel metodo visitEnd (..) ho chiamato un codice diverso (che era stato generato da ASMFier) a seconda di quanti argomenti del metodo aveva il metodo originale.

Ho usato un javaagent per intercettare il caricamento della classe e modificare i byte.

Dato che sono nuovo di ASM, forse non ho capito correttamente la tua domanda - tuttavia se la tua domanda riguardava la creazione di un proxy che esegue qualche elaborazione e poi chiama il metodo originale, allora la mia soluzione funziona. Non sembrava essere lento. Il tempo di caricamento della classe per la classe con i suoi metodi proxy era non molto più lento che senza la modifica del codice byte. La velocità di runtime del codice introdotto non era lenta, il codice ASM generato da ASMFier sembra essere molto veloce.

Acclamazioni

0

concettualizzazione della org.objectweb.asm.tree.analysis. SourceIterpreter dovrebbe darti le istruzioni che mettono un valore nello stack.

Problemi correlati