|
|
# jad
|
|
|
|
|
|
[`jad`在线教程](https://arthas.aliyun.com/doc/arthas-tutorials?language=cn&id=command-jad)
|
|
|
|
|
|
::: tip
|
|
|
反编译指定已加载类的源码
|
|
|
:::
|
|
|
|
|
|
`jad` 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于你理解业务逻辑;如需批量下载指定包的目录的 class 字节码可以参考 [dump](/doc/dump.md)。
|
|
|
|
|
|
- 在 Arthas Console 上,反编译出来的源码是带语法高亮的,阅读更方便
|
|
|
- 当然,反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解
|
|
|
|
|
|
## 参数说明
|
|
|
|
|
|
| 参数名称 | 参数说明 |
|
|
|
| --------------------: | :----------------------------------------- |
|
|
|
| _class-pattern_ | 类名表达式匹配 |
|
|
|
| `[c:]` | 类所属 ClassLoader 的 hashcode |
|
|
|
| `[classLoaderClass:]` | 指定执行表达式的 ClassLoader 的 class name |
|
|
|
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
|
|
|
|
|
|
## 使用参考
|
|
|
|
|
|
### 反编译`java.lang.String`
|
|
|
|
|
|
```java
|
|
|
$ jad java.lang.String
|
|
|
|
|
|
ClassLoader:
|
|
|
|
|
|
Location:
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Decompiled with CFR.
|
|
|
*/
|
|
|
package java.lang;
|
|
|
|
|
|
import java.io.ObjectStreamField;
|
|
|
import java.io.Serializable;
|
|
|
...
|
|
|
public final class String
|
|
|
implements Serializable,
|
|
|
Comparable<String>,
|
|
|
CharSequence {
|
|
|
private final char[] value;
|
|
|
private int hash;
|
|
|
private static final long serialVersionUID = -6849794470754667710L;
|
|
|
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
|
|
|
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
|
|
|
...
|
|
|
public String(byte[] byArray, int n, int n2, Charset charset) {
|
|
|
/*460*/ if (charset == null) {
|
|
|
throw new NullPointerException("charset");
|
|
|
}
|
|
|
/*462*/ String.checkBounds(byArray, n, n2);
|
|
|
/*463*/ this.value = StringCoding.decode(charset, byArray, n, n2);
|
|
|
}
|
|
|
...
|
|
|
```
|
|
|
|
|
|
### 反编译时只显示源代码
|
|
|
|
|
|
默认情况下,反编译结果里会带有`ClassLoader`信息,通过`--source-only`选项,可以只打印源代码。方便和[mc](mc.md)/[retransform](retransform.md)命令结合使用。
|
|
|
|
|
|
```java
|
|
|
$ jad --source-only demo.MathGame
|
|
|
/*
|
|
|
* Decompiled with CFR 0_132.
|
|
|
*/
|
|
|
package demo;
|
|
|
|
|
|
import java.io.PrintStream;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
import java.util.Random;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
public class MathGame {
|
|
|
private static Random random = new Random();
|
|
|
public int illegalArgumentCount = 0;
|
|
|
...
|
|
|
```
|
|
|
|
|
|
### 反编译指定的函数
|
|
|
|
|
|
```java
|
|
|
$ jad demo.MathGame main
|
|
|
|
|
|
ClassLoader:
|
|
|
+-sun.misc.Launcher$AppClassLoader@232204a1
|
|
|
+-sun.misc.Launcher$ExtClassLoader@7f31245a
|
|
|
|
|
|
Location:
|
|
|
/private/tmp/math-game.jar
|
|
|
|
|
|
public static void main(String[] args) throws InterruptedException {
|
|
|
MathGame game = new MathGame();
|
|
|
while (true) {
|
|
|
/*16*/ game.run();
|
|
|
/*17*/ TimeUnit.SECONDS.sleep(1L);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 反编译时不显示行号
|
|
|
|
|
|
`--lineNumber` 参数默认值为 true,显示指定为 false 则不打印行号。
|
|
|
|
|
|
```java
|
|
|
$ jad demo.MathGame main --lineNumber false
|
|
|
|
|
|
ClassLoader:
|
|
|
+-sun.misc.Launcher$AppClassLoader@232204a1
|
|
|
+-sun.misc.Launcher$ExtClassLoader@7f31245a
|
|
|
|
|
|
Location:
|
|
|
/private/tmp/math-game.jar
|
|
|
|
|
|
public static void main(String[] args) throws InterruptedException {
|
|
|
MathGame game = new MathGame();
|
|
|
while (true) {
|
|
|
game.run();
|
|
|
TimeUnit.SECONDS.sleep(1L);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 反编译时指定 ClassLoader
|
|
|
|
|
|
::: tip
|
|
|
当有多个 `ClassLoader` 都加载了这个类时,`jad` 命令会输出对应 `ClassLoader` 实例的 `hashcode`,然后你只需要重新执行 `jad` 命令,并使用参数 `-c <hashcode>` 就可以反编译指定 ClassLoader 加载的那个类了;
|
|
|
:::
|
|
|
|
|
|
```java
|
|
|
$ jad org.apache.log4j.Logger
|
|
|
|
|
|
Found more than one class for: org.apache.log4j.Logger, Please use jad -c hashcode org.apache.log4j.Logger
|
|
|
HASHCODE CLASSLOADER
|
|
|
69dcaba4 +-monitor's ModuleClassLoader
|
|
|
6e51ad67 +-java.net.URLClassLoader@6e51ad67
|
|
|
+-sun.misc.Launcher$AppClassLoader@6951a712
|
|
|
+-sun.misc.Launcher$ExtClassLoader@6fafc4c2
|
|
|
2bdd9114 +-pandora-qos-service's ModuleClassLoader
|
|
|
4c0df5f8 +-pandora-framework's ModuleClassLoader
|
|
|
|
|
|
Affect(row-cnt:0) cost in 38 ms.
|
|
|
$ jad org.apache.log4j.Logger -c 69dcaba4
|
|
|
|
|
|
ClassLoader:
|
|
|
+-monitor's ModuleClassLoader
|
|
|
|
|
|
Location:
|
|
|
/Users/admin/app/log4j-1.2.14.jar
|
|
|
|
|
|
package org.apache.log4j;
|
|
|
|
|
|
import org.apache.log4j.spi.*;
|
|
|
|
|
|
public class Logger extends Category
|
|
|
{
|
|
|
private static final String FQCN;
|
|
|
|
|
|
protected Logger(String name)
|
|
|
{
|
|
|
super(name);
|
|
|
}
|
|
|
|
|
|
...
|
|
|
|
|
|
Affect(row-cnt:1) cost in 190 ms.
|
|
|
```
|
|
|
|
|
|
对于只有唯一实例的 ClassLoader 还可以通过`--classLoaderClass`指定 class name,使用起来更加方便:
|
|
|
|
|
|
`--classLoaderClass` 的值是 ClassLoader 的类名,只有匹配到唯一的 ClassLoader 实例时才能工作,目的是方便输入通用命令,而`-c <hashcode>`是动态变化的。
|
|
|
|
|
|
### 反编译时指定dump class文件目录
|
|
|
|
|
|
`jad`反编译需要将class dump到文件,默认会dump到logback.xml中配置的log目录下,使用`-d/--directory`可以将文件dump到指定目录。
|
|
|
|
|
|
```java
|
|
|
$ jad demo.MathGame -d /tmp/jad/dump
|
|
|
```
|