是否有一个Java反编译器(无论是独立的还是Eclipse插件)能够反编译由AspectJ编织的代码?

mar*_*ark 11 java decompiling

我已经在互联网上扫描了各种相关问题(比如http://www.java-decompiler.com/),特别是SO.到目前为止,我只能找到两个声称是最新的Java反编译器--JD-GUI和DJ Java Decompiler.

所有其余的都无法下载或停止使用.

因此,我使用包含AspectJ weaven代码的.class文件,并使用两个可用的反编译器对其进行反编译.观察结果:

  1. JD-GUI:

    在此输入图像描述

  2. DJ Java Decompiler:

    在此输入图像描述

正如您所看到的,这两个工具都无法使用AspectJ反编译Java代码.

现在我不是太挑剔,我只是习惯了.NET Reflector并在Java反编译器中寻找相同的质量,无论是独立的还是Eclipse插件,免费或商业.

我正在寻找一个实际可行的,并且使用方便.

编辑

对我的问题的回答的总体趋势是这样的 - "嗯,你想要什么?虽然,AspectJ创建有效的JVM字节码,但这个字节码不能被翻译成有效的Java".我只能说我不赞成这种观点.

让我向您介绍更多背景信息,我希望您同意这些工具可以而且应该做得更好.

反编译的Java类使用以下方面是weaven:

public abstract aspect LoggingAspect {
  declare parents: (@LogMe *) implements ILoggable;

  public Logger ILoggable.getLogger() {
    LoggerHolderAspect holder = LoggerHolderAspect.aspectOf(this.getClass());
    return holder.getLogger();
  }

  abstract pointcut loggedMethods();

  before(ILoggable o): loggedMethods() && this(o) {
    logBefore(o.getLogger(), thisJoinPoint);
  }

  after(ILoggable o) returning (Object result): loggedMethods() && this(o) {
    logAfterReturning(o.getLogger(), thisJoinPoint, result);
  }

  after(ILoggable o) throwing (Exception e): loggedMethods() && this(o) {
    logAfterThrowing(o.getLogger(), thisJoinPoint, e);
  }

  protected void logBefore(Logger l, JoinPoint jp) { ... }
  protected void logAfterReturning(Logger l, JoinPoint jp, Object result) { ... }
  protected void logAfterThrowing(Logger l, JoinPoint jp, Exception e) { ... }
}
Run Code Online (Sandbox Code Playgroud)

现在,这堂课是这样的:

@Path("user")
public class UserHandler {
  ...    

  @GET
  @Path("{id}")
  @Produces({ "application/json", "application/xml" })
  public User getUser(@PathParam("id") int id) { ... }

  @DELETE
  @Path("{id}")
  public void deleteUser(@PathParam("id") int id) { ... }

  @PUT
  @Path("{id}")
  public void putUser(@PathParam("id") int id, User entity) { ... }

  @POST
  @Produces({ "application/json", "application/xml" })
  public Response postUser(User entity) { ... }
}
Run Code Online (Sandbox Code Playgroud)

现在,JD-GUI无法正确地反编译每个检测方法.生成的输出看起来像是一个糟糕的SVN合并.这是好奇的完整文件的链接 - http://pastebin.com/raw.php?i=WEmMNCPS

DJ Java Decompiler产生的输出略胜一筹.看起来像DJ有非空方法的问题.确实,观察它如何反编译一个void方法:

@DELETE
@Path(value="{id}")
public void deleteUser(@PathParam(value="id") int id)
{
    int i = id;
    org.aspectj.lang.JoinPoint joinpoint = Factory.makeJP(ajc$tjp_1, this, this, Conversions.intObject(i));
    try
    {
        ResourceHandlerLoggingAspect.aspectOf().ajc$before$com_shunra_poc_logging_LoggingAspect$1$51e061ae(this, joinpoint);
        if(!securityContext.isUserInRole(Enroler.ADMINISTRATOR.getName()))
        {
            throw new WebApplicationException(javax.ws.rs.core.Response.Status.UNAUTHORIZED);
        } else
        {
            m_userService.delete(id);
            Object obj = null;
            ResourceHandlerLoggingAspect.aspectOf().ajc$afterReturning$com_shunra_poc_logging_LoggingAspect$2$51e061ae(this, obj, joinpoint);
            return;
        }
    }
    catch(Exception exception)
    {
        ResourceHandlerLoggingAspect.aspectOf().ajc$afterThrowing$com_shunra_poc_logging_LoggingAspect$3$51e061ae(this, exception, joinpoint);
        throw exception;
    }
}
Run Code Online (Sandbox Code Playgroud)

这很好,但是如果一个方法返回一些东西,那么DJ就会失败,例如:

@POST
@Produces(value={"application/json", "application/xml"})
public Response postUser(User entity)
{
    org.aspectj.lang.JoinPoint joinpoint;
    User user = entity;
    joinpoint = Factory.makeJP(ajc$tjp_3, this, this, user);
    ResourceHandlerLoggingAspect.aspectOf().ajc$before$com_shunra_poc_logging_LoggingAspect$1$51e061ae(this, joinpoint);
    entity.Id = 0;
    m_userService.post(entity);
    Response response;
    Response response1 = response = Response.created(postedUserLocation(entity.Id)).entity(new EntityPostResult(entity.Id)).build();
    ResourceHandlerLoggingAspect.aspectOf().ajc$afterReturning$com_shunra_poc_logging_LoggingAspect$2$51e061ae(this, response1, joinpoint);
    return response;
    Exception exception;
    exception;
    ResourceHandlerLoggingAspect.aspectOf().ajc$afterThrowing$com_shunra_poc_logging_LoggingAspect$3$51e061ae(this, exception, joinpoint);
    throw exception;
}
Run Code Online (Sandbox Code Playgroud)

再次,这是好奇的完整输出 - http://pastebin.com/raw.php?i=Qnwjm16y

AspectJ究竟做了什么,这些反编译器无法应对?我想要的只是一个与.NET Reflector相同的Java反编译器,除了真正的特殊情况之外,它对反编译的C#代码完全没有问题,我们不在那里.

编辑2

根据Andy Clement的建议,我创建了一个小应用程序,仅用于测试AspectJ如何检测代码,将其与手动检测进行比较,并了解JD-GUI和DJ如何对其进行反编译.以下是我的发现:

Java源代码是:

public class Program {
  private static boolean doThrow;

  public static void main(String[] args) {
    run();
    doThrow = true;
    run();
  }

  private static void run() {
    System.out.println("===============================================");
    System.out.println("doThrow = " + doThrow);
    System.out.println("===============================================");
    System.out.println("autoInstrumented:");
    try {
      System.out.println(autoInstrumented());
    } catch (Exception e) {
      System.out.println(e.getMessage());
    }
    System.out.println("===============================================");
    System.out.println("manuallyInstrumented:");
    try {
      System.out.println(manuallyInstrumented());
    } catch (Exception e) {
      System.out.println(e.getMessage());
    }
    System.out.println("===============================================");
  }

  public static void before() {
    System.out.println("before(f)");
  }

  public static void afterReturning(int x) {
    System.out.println("afterReturning(f) = " + x);
  }

  public static void afterThrowing(Exception e) {
    System.out.println("afterThrowing(f) = " + e.getMessage());
  }

  public static int f() throws Exception {
    if (doThrow) {
      throw new Exception("*** EXCEPTION !!! ***");
    }
    return 10;
  }

  public static int autoInstrumented() throws Exception {
    return f();
  }

  public static int manuallyInstrumented() throws Exception {
    before();
    try {
      int result = f();
      afterReturning(result);
      return result;
    } catch (Exception e) {
      afterThrowing(e);
      throw e;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

方面代码是:

public aspect Weave {
  pointcut autoInstrumented() : execution(int Program.autoInstrumented());

  before() : autoInstrumented() {
    Program.before();
  }

  after() returning (int result) : autoInstrumented() {
    Program.afterReturning(result);
  }

  after() throwing (Exception e) : autoInstrumented() {
    Program.afterThrowing(e);
  }
}
Run Code Online (Sandbox Code Playgroud)

DJ产生的输出是:

public static int autoInstrumented()
    throws Exception
{
    Weave.aspectOf().ajc$before$Weave$1$be1609d6();
    int i;
    int j = i = f();
    Weave.aspectOf().ajc$afterReturning$Weave$2$be1609d6(j);
    return i;
    Exception exception;
    exception;
    Weave.aspectOf().ajc$afterThrowing$Weave$3$be1609d6(exception);
    throw exception;
}
Run Code Online (Sandbox Code Playgroud)

抛开AspectJ创建的方法的名称,生成的Java代码既错误又无效.这是错误的,因为没有try-catch语句.它无效,因为exception;它不是有效的Java语句.

接下来是JD-GUI:

public static int autoInstrumented() throws Exception {
  try {
    Weave.aspectOf().ajc$before$Weave$1$be1609d6();
    int i;
    int j = i = f(); Weave.aspectOf().ajc$afterReturning$Weave$2$be1609d6(j); return i; } catch (Exception localException) { Weave.aspectOf().ajc$afterThrowing$Weave$3$be1609d6(localException); } throw localException;
}
Run Code Online (Sandbox Code Playgroud)

我必须接受关于JD-GUI产生损坏的输出的话.碰巧代码或多或少是正确的,但所有方法尾部都在一行输出!在GUI内部查看时,看起来该方法被截断.只有在复制代码之后,在Eclipse中粘贴并重新格式化才能看到,它几乎可以:

public static int autoInstrumented() throws Exception {
  try {
    Weave.aspectOf().ajc$before$Weave$1$be1609d6();
    int i;
    int j = i = f();
    Weave.aspectOf().ajc$afterReturning$Weave$2$be1609d6(j);
    return i;
  } catch (Exception localException) {
    Weave.aspectOf().ajc$afterThrowing$Weave$3$be1609d6(localException);
  }
  throw localException;
}
Run Code Online (Sandbox Code Playgroud)

几乎,因为throw localException;声明如何发现自己在catch块之外?

现在,至于实际的JVM字节码.我使用了ByteCode Outline Eclipse扩展,结果如下:

手动检测的方法manuallyInstrumented:

public static manuallyInstrumented()I throws java/lang/Exception 
ATTRIBUTE org.aspectj.weaver.MethodDeclarationLineNumber : unknown
  TRYCATCHBLOCK L0 L1 L2 java/lang/Exception
 L3
  INVOKESTATIC Program.before()V
 L0
  INVOKESTATIC Program.f()I
  ISTORE 0
 L4
  ILOAD 0
  INVOKESTATIC Program.afterReturning(I)V
 L5
  ILOAD 0
 L1
  IRETURN
 L2
 FRAME SAME1 java/lang/Exception
  ASTORE 0
 L6
  ALOAD 0
  INVOKESTATIC Program.afterThrowing(Ljava/lang/Exception;)V
 L7
  ALOAD 0
  ATHROW
 L8
  LOCALVARIABLE result I L4 L2 0
  LOCALVARIABLE e Ljava/lang/Exception; L6 L8 0
  MAXSTACK = 1
  MAXLOCALS = 1
Run Code Online (Sandbox Code Playgroud)

自动检测方法autoInstrumented:

public static autoInstrumented()I throws java/lang/Exception 
ATTRIBUTE org.aspectj.weaver.MethodDeclarationLineNumber : unknown
  TRYCATCHBLOCK L0 L1 L1 java/lang/Exception
 L0
  INVOKESTATIC Weave.aspectOf()LWeave;
  INVOKEVIRTUAL Weave.ajc$before$Weave$1$be1609d6()V
  INVOKESTATIC Program.f()I
  DUP
  ISTORE 0
  DUP
  ISTORE 1
  INVOKESTATIC Weave.aspectOf()LWeave;
  ILOAD 1
  INVOKEVIRTUAL Weave.ajc$afterReturning$Weave$2$be1609d6(I)V
  ILOAD 0
  IRETURN
 L1
  ASTORE 2
  INVOKESTATIC Weave.aspectOf()LWeave;
  ALOAD 2
  INVOKEVIRTUAL Weave.ajc$afterThrowing$Weave$3$be1609d6(Ljava/lang/Exception;)V
  ALOAD 2
  ATHROW
  MAXSTACK = 3
  MAXLOCALS = 3
Run Code Online (Sandbox Code Playgroud)

我不是JVM大师(温和地说),所以我不知道JVM字节码autoInstrumented是否"坏".你能?

摘要:

  • DJ不好
  • JD-GUI几乎就在那里,但仍然会产生糟糕的Java,可用性很糟糕 - 需要在Eclipse中复制,粘贴和重新格式化以了解正在发生的事情.

底线

没爱.

Ern*_*ill 4

Java字节码可以表达字面上无法直接翻译成Java的东西,因此,直接生成字节码的工具创建的类文件不一定能够被反编译。人类有可能编写类似的Java 代码,但这是一个人工智能问题,与反编译完全不同。

  • 一般来说我会同意,但在这种情况下不会。由aspectj 创建的代码,至少在我正在谈论的情况下,应该很容易翻译为Java。我对 PostSharp 有一些经验,它对 C# 的作用就像 AspectJ 对 Java 的作用一样,.NET 反射器在反编译大多数编织代码时没有问题。 (2认同)