2016-05-31 146 views
2

我試圖修改其打包jar文件不在類路徑中的幾個類的字節碼 - 它們在運行時通過自定義ClassLoader加載給定URL。我試圖用java agentClassFileTransformer希望攔截這些類,但失敗。 classloader是遺留項目的一部分,因此我無法直接對其進行更改。如何儀器類加載自定義類加載器?

該代理對AppClassLoader「本地」加載的類正常工作,但只是忽略由自定義類加載器加載的類。

CustomClassLoader

public class CustomClassLoader extends URLClassLoader { 

    public CustomClassLoader(URL[] urls) { 
     super(urls, CustomClassLoader.class.getClassLoader()); 
    } 

    // violates parent-delegation pattern 
    @Override 
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 
     synchronized (getClassLoadingLock(name)) { 
      Class<?> clazz = findLoadedClass(name); 
      if (clazz == null) { 
       try { 
        clazz = findClass(name); 
       } catch (ClassNotFoundException e) { 
       } 

       if (clazz == null) { 
        clazz = getParent().loadClass(name); 
       } 
      } 
      if (resolve) { 
       resolveClass(clazz); 
      } 
      return clazz; 
     } 
    } 
} 

ClassFileTransformer在我的經紀人使用(用Javassist):

public class MyTransformer implements ClassFileTransformer 
{ 
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
      ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException 
    { 
     byte[] byteCode = null; 

     if (className.replace("/", ".").equals("com.example.services.TargetService")) 
     { 
      ClassPool cp = ClassPool.getDefault(); 
      CtClass cc; 
      try 
      { 
       cc = cp.get("com.example.services.TargetService"); 
       CtMethod verifyMethod = cc.getDeclaredMethod("verify"); 
       //invalidate the verification process of method : verify 
       verifyMethod.insertBefore("{return true;}"); 
       byteCode = cc.toBytecode(); 
       cc.detach(); 
       return byteCode; 
      } 
      catch (Exception e) 
      { 
       e.printStackTrace(); 
      } 
     } 

     return byteCode; 
    } 
} 

代理

public class Agent 
{ 
    public static void premain(String agentArgs, Instrumentation inst) 
    { 
     inst.addTransformer(new MyTransformer()); 
    } 

} 

我想出了一個解決方法,通過調用CustomClassLoader本身,調用instrumentation.redifineClasses(),但不知道如何將檢測實例傳遞到CustomClassLoader實例中;我不熟悉儀器/類加載,但仍然不清楚其機制。 有什麼幫助嗎?謝謝。

爲了使簡單:

  1. 內的應用程序創建而在其他地方,你在運行時的文件系統加載某些jar文件自定義的URLClassLoader。
  2. 實現一個java代理程序,用於轉換類加載器加載的類,替換其中一個方法的主體或其他東西。
  3. 在應用程序內部爲其接口分配一個類的實例並調用它的檢測方法。
  4. 使用-javaagent運行應用程序進行檢查。
+0

你有沒有依賴的簡單代理嘗試過嗎?例如嘗試簡單地調用System.out.println(className);在你的方法轉換中,然後返回classfileBuffer。我試圖用你的CustomClassLoader加載一個類,我在控制檯上看到這個類的名字 –

+0

@NicolasFilotto問題解決了,謝謝,你讓我想起了我用於代理的工具。代理本身得到了類加載事件的通知,但如果外部URL未添加到其類路徑中,則檢測工具無法獲取字節碼。 –

回答

1

我認爲你的班級沒有正確安裝,因爲你打電話給ClassPool.getDefault(),它不包括對你的自定義類加載器可見的類文件,但只對系統類加載器可見。您永遠不會註冊classfileBuffer類文件。

作爲替代方案,你可以嘗試Byte Buddy它提供的儀表API更容易獲得:

new AgentBuilder.Default() 
    .type(named("com.example.services.TargetService")) 
    .transform((builder, type, loader) -> { 
    builder.method(named("verify")).intercept(FixedValue.of(true)); 
    }).installOn(instrumentation); 

上述代理可以從agentmainpremain方法被調用。您也可以禁用類文件格式更改並重新定義現有類,以防在附件期間該類已被(可能)加載。

+1

這是類池問題,添加'cp.appendClassPath(新的LoaderClassPath(加載程序));'和工作,真的很感謝你,字節夥伴看起來很酷。如果我有足夠的代表,我會很高興。 –