Applet和Java Web Start应用程序默认配置运行在严格的沙箱中。显然内部类和私有域的修改通常是不被允许的。这意味着我们必须将安全性“关闭”。JavaSoop中提供的startup.bat文件实现了这个需求,我们需要使用startup.bat来运行JavaSnoop。
该批处理文件将实现以下功能: 1.检查环境变量JAVA_HOME的值是否包含JDK1.6的路径 2.关闭JavaSnoop的安全性 3.启动JavaSnoop工具 4.退出工具后,再次将Java安全性恢复为安全浏览
将JavaSnoop”附加”到进程中
JavaSnoop工具提供了两种注入进程的方式。
附加到现有进程
我们可以通过从可用的正在运行的进程列表中选择一个已经运行的进程来注入JavaSnoop.
创建新的进程
我们也可以通过选择要拦截的JAR文件来启动一个新进程。
工具主界面介绍
JavaSnoop工具的主界面分为四部分,如下图所示:
第一部分
在这部分中,我们选择需要hook的类或方法。界面「Add New Hook」按钮来添加一个新的hook。然后我们可以从列表中提供的类里添加一个方法,如下图所示:
第二部分
本部分提供了用于设置截取方法调用的各种选项的功能。 我们可以设置正则表达式条件来匹配和拦截来方法调用的内容。如下图所示:
第三部分
这一部分针对Hook提供了各种选项提供了以下各种选项: 将parameters / stacktrace打印到控制台或特定文件 运行自定义脚本 篡改参数 修改返回值 暂停程序
第四部分
来自目标应用程序的Hook修改内容和反编译类的输出显示在此区域中。
使用JavaSnoop拦截来自基于JAVA的应用程序的数据
在本文中,我们将介绍两个基于Java的示例应用程序,并学习如何拦截JavaSnoop工具中的数据: 1.拦截浏览器中运行的applet的数据。 2.拦截基于JAVA的胖客户端的数据。
1.拦截浏览器中运行的Applet的流量。
Java小程序是以Java字节码的形式传递给用户的小程序。Java小程序可以是Web页面的一部分,并且由Java虚拟机(JVM)在与Web浏览器分开的过程中执行,也可以在Sun的AppletViewer中运行(AppletViewer是用于测试applet小程序的独立工具)。
很难拦截作为网页一部分的applet小程序的数据。普通代理工具(如Burpsuite和Paros)无法拦截/修改来自这些applet程序的流量。下面我们演示一个使用JavaSnoop工具来拦截applet小程序的例子。
步骤1:我们将一个登录小程序嵌入到Web浏览器中,小程序接[……]
「JavaSnoop使用入门」
「ODB的简介」
ODB的截图如下:
当程序调用exit()或按下Debugger Controller窗口中的“Stop Recording”按钮时,主调试器窗口(上图)将弹出。你随后可以浏览时间以找到感兴趣的事件或可疑的参数值。
不同的窗格显示了在运行该程序时不同方面的信息。Method Tracks窗格显示程序运行期间所做的所有方法调用(每个线程)。 “Stack”窗格显示当前方法的堆栈,局部变量位于“Locals”窗格中,下方是“this”对象等。当ODB恢复到不同时间时,所有窗格都会相应更新。因此,在事件532(1273以外)中,数组中第二个元素的值为237。
Navigation
导航的主要模式是:在Method Traces窗格、Code窗格、Stack窗格、Threads窗格之一中选择一行;或按 first/last/previous/next其中一个按钮移动到第一个/最后一个/上一个/下一个/等。上下文切换,方法调用,代码行,变量值更改等等。这些都会将调试器恢复到与选择的时间相适合的状态。
Data Display
所有对象都以打印字符串显示,如“<Person_123 Jimmy>”,其中“Person”是类名称,“123”是Person对象的任意唯一标识,“Jimmy”是可选字段的值。任何地方显示的任何对象都可以通过双击将其复制到“Objects”窗格中。当程序员在不同的时间导航时,实例变量值将被更新。尚未设置的值(例如,对象创建之前的实例变量)将显示为“ — ”。
Performance
尽管可以绝对记录所有内容,但它并不是非常有用。人们可能会跳过记录受信任的类(如JCF类,库类和您自己经过良好测试的类)或者不感兴趣的类。事实上,默认情况下只是在选定的软件包中记录和记录这些类。用户可以使用Debugger Controller窗口开始/停止录制。性能尚未证明是一个普遍问题。 Ant重新编译本身运行速度慢了7倍,并且在100万个事件下生成。
Starting
ODB使用了GPL,并且jar文件中包含了完整的源码和手册。如果要调试你的程序,在jar包中有一个aliase文件(SHELL脚本),在执行source aliase后,执行如下命令即可:
# debug YourProgram arg0 arg1 …
参考文献
One page description of the ODB[……]
「4.Debugging Backwards in Time」
Debugging Backwardsin Time Bil Lewis Lambda Computer Science 555 Bryant St. 194, Palo Alto, CA, 94301, USA Bil. Lewis@LambdaCs.com March 2003 Abstract By recording every state change in the run of a program, it is possible to present the programmer everything that might be desired. Essentially, it becomes possible to debug the program by going “backwardsin time,” vastly simplifying the process of debugging. An implementation of this idea, the “Omniscient Debugger,” is used to demonstrate its viability and has been used successfully on a numberof large programs. Integration with an event analysis engine for searching and control is presented. Finally performance issues and implementation are discussed along with possible optimizations. This paper makes three contributions of interest: the concept and technique of “going backwards in time,” the GUI which presents a global view of the program state and has a formal notion of “navigation through time,” and the integration with an event analyzer.
1 Introduction
Over the past forty years there has been little change in way commercial program debuggers work. In 1961 a debugger called “DDT”[8] existed on Digital machines which allowe[……]
「5.User Manual」
ODB是通过保持状态变化进行调试的概念,并及时向后“导航”以找到问题。ODB是概念验证实现,是用Java编写的。它既没有优化也没有整合。无论如何,我相信你会发现它是你使用过的最有效的调试器。
ODB基于在程序中的每个“兴趣点”收集“时间戳”的想法,然后允许程序员使用这些时间戳来浏览程序运行的历史。“兴趣点”包括:设置变量值(本地,实例,数组元素,静态)、进行方法调用、抛出/捕获异常、创建新对象。调试器将代码插入到类文件中以收集这些“时间戳”。当程序运行时,时间戳将被记录下来。这写功能是通过BCEL库实现的。
ODB不需要任何预处理,或任何特殊的存储库。对正在调试的程序没有限制,甚至没有必要提供源代码。安装后(见下文,安装包括一个Unix脚本),程序员通过调用以下命令运行调试器:
# debug TargetProgram arg1 arg2 arg3
调试器将显示一个小小的”控制窗口“(我们称之为”Debugger Controller“)并启动程序。当程序调用exit()或程序员在控制窗口中按下“Stop Recording”按钮时,”调试窗口“将弹出。程序员将能够浏览时间戳以找到感兴趣的事件。下面是控制窗口的截图: (图一)
一旦调试窗口启动,即使程序仍在运行,所有记录都将被关闭。你可以使用控制窗口继续录制。下面是调试窗口的截图: (图二)
导航(Navigation)
在传统的调试器中,导航功能从来都不是问题,因为没有太多想法。你点击继续,程序继续运行,直到它到达下一个断点,就是这样。ODB需要更正式一点,因为有很多方法可以从这里到那里,然后再回来。
ODB提供了多种用于浏览程序运行的方法。它们都旨在使程序员尽可能简单地在程序中获得“感兴趣”的点,并查看它处于什么状态。DODB实现了最“明显”的方法,具有足够的空间添加其他。
主要的导航模式如下:
在「Method Traces」窗口中选择一行:这将使调试器恢复到该方法记录的第一个时间戳的时间。如果该方法未被检测,则调试器将恢复到它所调用的行。你可以通过关闭Trace->Go to first line in method来使调试器始终到达调用行。
在「Code」窗口中选择一行:这会将调试器恢复为该行的时间戳(见下文)。
按下其中一个按钮:这将根据当前选中的对象或该窗格中的行来将调试器恢复到对应的时间戳。(请参阅下面的导航按钮章节。)
在「Stack」窗格中选择一行:这不会更改当前时间戳的行,但会显示堆栈中的方法从何处调用(恢复「Code」窗格)以及本地变量的值。(请参阅「Stack」窗格。)
选择一个线程:该线程将恢复到该线程中最接近的时间戳,早于当前首选的时间[……]
「使用配置控制ODB的行为」
通过命令行指定属性(-D)
下面的这些属性,用于控制ODB的行为、样式等等。通常与被调试项目无关,被调试项目的配置是通过.debuggerDefaults配置文件进行指定的,参考「配置.debuggerDefaults文件」部分。
这些属性通过java(1)命令-D选项传入。例如,可以在启动时使用命令java -DFONT_SIZE=15 …的形式来修改字体的大小为15。
属性分为两类:布尔类型;参数类型。布尔类型的属性没有值,只要通过-D选项指定该属性,该属性所附带的效果即会生效。参数类型的属性需要指定具体的参数值,否则会被忽略。
字体相关
FONT 指定要使用的字体。
FONT_SIZE 设置字体大小。在启动时,在命令行参数中追加-DFONT_SIZE=15
窗口相关
由于Java与窗口管理器之间对协议的实现存在差异,导致了窗口管理器的某些功能无法使用。所以通过如下的环境变量进行处理。
注意,由于窗口大小设置方法(setSize)在某些窗口管理器中不起作用,因此强制设置窗口的最大(setMaximumSize)和最小值(setMinimumSize)来使窗口固定大小。因为我们假设:既然你通过命令行指定了大小,则此大小是一个令你满意的大小,因此固定大小。所以下面的属性值应该是一个令你满意的值。
ODBCW_WIDTH 控制窗口的宽度。如果未指定,则采用窗口渲染的默认大小,这个默认大校视情况而定。
ODBCW_HEIGHT 控制窗口的高度。如果未指定,则采用窗口渲染的默认大小,这个默认大校视情况而定。
ODBCW_POSX 控制窗口在X方向上的起始位置。如果未指定,则默认为0,即从左上角开始。
ODBCW_POSY 控制窗口在Y方向上的起始位置。如果未指定,则默认为0,即从左上角开始。
ODBDW_WIDTH 调试窗口的宽度。如果未指定,则采用窗口渲染的默认大小,这个默认大校视情况而定。
ODBDW_HEIGHT 调试窗口的高度。如果未指定,则采用窗口渲染的默认大小,这个默认大校视情况而定。
ODBDW_POSX 调试窗口在X方向上的起始位置。如果未指定,则默认为0,即从左上角开始。
ODBDW_POSY 调试窗口在Y方向上的起始位置。如果未指定,则默认为0,即从左上角开始。
其他属性
DONT_START 如果调试器使用DONT_START标志启动,则ODB不会立即启动程序,ODB会显示控制器窗口来让你选择以下所有的选项。
DONT_SHOW 通常,如果main()方法运行结束后会停止录制,并弹出调试窗口。但有些程序在main()返回后,会继续运行在其中产生的新线程(例如,窗口化程序)。在这种情况下,应该设置[……]
「数据显示格式」
ODB需要打印字符串,所以它提供了自己的格式来显示类名和一个索引号:<MyObject_75>,去掉了包名。你可以添加一个额外的字符串:<MyObject_75 square>(见.debuggerDefaults文件)。
方法跟踪与对象(或静态方法的类对象)一起显示,后跟方法名称,前三个参数,最后是一个箭头和返回值。没有返回值的方法只显示****。抛出异常或传递异常的方法则显示**** <Exception>。它们会被缩进地适当,因此在图二中,sort()调用了average()一次,new()两次,然后是start(),最后是递归sort()。递归调用sort()调用average()等。
标志着方法结束的返回显示在的单独行中,形如sort -> void。如果返回行位于调用行的右侧,则不会在单独行显示。如果程序从多个深度方法调用中捕获异常,则显示将出现“锯齿”,因为不会有任何介入的“返回行”。
各种类型的显示格式
类对象只显示为类名,例如Person;
布尔值、数字、字符类型按预期显示:true,false,123,45.678,’X’(88);
字节也像字符一样同时在括号中显示ASCII字符和十进制字符。
构造函数会像静态方法一样显示,形如MyObject.new() -> <MyObject_1>,父类方法也一样的。
长度处理
在”Method Traces“中,较长的名称和字符串(超过20个字符)将被隐藏,并打印为<TestLongName..0>的形式。
在“Objects”中提供了更多空间,有500个字符。
非常长的字符串和大对象可以通过菜单项中的Objects->Print按钮打印到终端里。
参考文献
用户手册中的「Data Display Formats」部分[……]
「界面中的窗格」
ODB尝试一次性在屏幕上显示所有感兴趣的数据。弹出对话框仅用于查找源文件和选择值,这些很少用的地方。所用的窗格里的内容都是同一个时间戳的。
「Threads」
线程以<Thread-3>一样的格式显示,其中“Thread-3”是由getName()方法返回的内容。如果当前选定的时间戳在线程创建之前(在线程的第一个时间戳之前),则线程显示为– <Thread-3> –。如果选定的时间戳在线程退出之后,则显示为<Thread-3> Dead (在最后时间戳之后的线程的isAlive()方法返回false)。如果线程被阻塞、等待一个锁、在一个调用wait()或join()之中时,它将显示为<Thread-10> <MyObj_2>。将线程添加到「Objects」窗格将显示为伪实例变量_blockedOn。
「Stack」
这里显示堆栈中的所有方法。单击「Stack」窗格中的一行将更新「Code」窗格、参数、局部变量。当前的时间戳不会被改变。推动任何按钮都会影响实际时间,而不是在「Code」窗格中显示的内容。(目前还不清楚这里最“直观的”是什么。)菜单命令Code->Goto Stack Frame将恢复为该时间戳。
「Locals」
所有参数和局部变量将显示在这里。如果程序编译时没有-g标志,局部变量名称将显示为var1等等。
「this」
该窗格显示当前的’this’对象(或静态方法的类对象)。
「Trace」
对于每个线程,这显示了所有进行的方法调用的跟踪。只显示三个参数。只记录十个。
「Code」
这显示了当前的代码行。按钮向前或向后移动都是从这条线开始。这两个例外是:当选择了一个更高的堆栈帧(请参阅「Stack」窗格)并且在无法找到的源文件中出现时间戳时,「Code」窗格将为空。
「TTY Output」/「I/O」
这显示了由System.out.println()和System.err.println()写入的所有行。(很显然,只有那些来自被检测代码的调用。)如果行前面有“–”,表示当前时间戳在该行被写入的时间之前。(最终,这个窗格将显示调用print(),write()等去任何I/O流。)
「Objects」
这显示了用户通过双击其他窗口中的对象复制的任何对象。双击实例变量会将该对象添加到「Objects」窗格。双击「Objects」窗格中的对象将“关闭”该对象,隐藏其所有实例变量。再次双击将打开它。单个实例变量也可以选择性地删除(请参阅Objects->Remove和Objects->Retain)。
双击一个实例变量将展开该实例变量的值(如图2中的数组int[2[……]
「Known Bugs」
软件的“Heisenbug Principal”(观察程序会改变其行为)将始终适用,但在传统调试器下运行应该没有什么特别的区别。一个例外是当一个程序遭受高速缓存异常时。如果未能正确锁定共享变量,则CPU 1可能会向该变量写入一个值,并且CPU 2可能会在一段时间内看不到该新值。ODB将防止发生此错误。有静态锁分析器。使用一个。替代集合类(MyArrayList,Vector,Hashtable)不会通过枚举器记录更改的条目。菜单栏上显示的时钟非常有价值。
Java在所有使用锁的代码周围都有一个“catch”。通常你不会注意到这一点。它将在ODB中显示为“extra”捕获。
调用“super”方法看起来像调用静态方法。(目前还不清楚什么样的显示最“直观”。):
<MySubObj_4>.frob() -> void
MySuperObj.frob() -> void
frob -> void
某些char和boolean值会被记录成int值。
一些Swing的bug和错误消息出现在JDK 1.3中,你可以忽略它(再次按下控制器中的“Stop”将强制重新显示 – 在JDK 1.3中有用)。数组只显示前1000个元素。「TTY Output」窗格仅显示对println()写入标准输出的调用。
为了更快速地启动大型项目(超过100个文件),大多数文件不经常重新编译,你可以手动监测文件。
RMI与序列化的问题(RMI and Serialization Problems)
如果序列化一个对象,那么对它进行反序列化的程序期望与序列化它的程序具有相同的类定义。但是,如果两个程序中的一个程序被检测,而另一个程序不被检测,则可能会获得各种异常,如下所示。一种解决方案是直接对文件进行监测,并在关闭收集的情况下简单地运行一端。
java.lang.LinkageError: duplicate class definition: ClientImpl_Skel
可能验证错误(Possible Verifier Bugs)
因为ODB是通过插入类代码来工作的,所以你的javac编译器可能会以ODB无法使用的形式生成代码。(它适用于我生成的所有代码,但我之前感到很惊讶。)你唯一可以做的就是:(a)将该类放入“don’t instrument”列表中;(b)手动调试所有其他文件,并在关闭监测下运行ODB。并向我汇报错误。
典型的验证错误(和相关的链接器错误)如下所示:
java.lang.VerifyError: (class: BadClass, method: bar signature: ()V[……]
「CFR :: Class File Reader」
CFR,Java反编译器。使用Java语言开发。
安装程序
直接下载JAR文件 Homepage: http://www.benf.org/other/cfr 122 Download: http://www.benf.org/other/cfr/cfr_0_122.jar
目前暂未开源,无法使用源码编译
可执行程序文件
cfr-0.122.jar 主程序文件。用于反编译。
cfr wrapper
#!/bin/bash
script_path=”$0″
script_realpath=$(test -L $script_path && readlink $script_path || echo $script_path)
dirname=$(dirname $script_realpath)
java -jar “$dirname/cfr.jar” $@
参考文献
Homepage: http://www.benf.org/other/cfr/[……]
「cfr-0.122.jar」
cfr.jar,反编译.class文件、JAR文件等等。
本手册适用于CFR 0_122版本。
命令支持的选项及含义
详细的选项介绍可以使用’–help optionname’进行查看,比如查看–pullcodecase选项的详细介绍:
–help pullcodecase
–aexagg 如果它们不会改变语义,删除嵌套的异常处理程序。 取值:boolean
–aggressivesizethreshold 触发积极减少的操作码计数。 (int >= 0) 默认值:15000
–allowcorrecting 允许修改错误的转换,可能改变代码行为。 比如:删除不可能异常处理,如果这有任何效果,将发出警告。 取值:boolean 默认值:true
–analyseas 指出被分析的文件的类型。 取值范围:[JAR, WAR, CLASS]
–arrayiter 基于重组array的迭代。 取值:boolean 默认值:true,如果class文件版本49.0+ (Java 5)
–caseinsensitivefs 通过重命名冲突类,来处理不区分大小写的文件系统。 取值:boolean 默认值:false
–clobber 使用outputpath时,覆盖文件。 取值:boolean
–collectioniter 基于重组collection的迭代。 取值:boolean 默认值:true,如果class文件版本49.0+ (Java 5)
–commentmonitors 用注释替换监视锁 – 如果十分困惑时,很有用。 取值:boolean 默认值:false
–comments 输出描述反编译器状态、后备标志等的注释。 取值:boolean 默认值:true
–decodeenumswitch 解码使用Enum的switch,更其更可读。 参考:http://www.benf.org/other/cfr/switch-on-enum.html 取值:boolean 默认值:true,如果class文件版本49.0+ (Java 5)
–decodefinally 使finally语句更可读。 取值:boolean 默认值:true
–decodelambdas 重建lambda函数。 取值:boolean 默认值:true,如果class文件版本52.0+ (Java 8)
–decodestringswitch 解码使用String的switch,更其更可读。 取值:boolean 默认值:true,如果class文件版本51.0+ (Java 7)[……]
「Java」- Class File Format
major version number of the class file format being used
Java SE 10 = 54 (0x36 hex),[3] Java SE 9 = 53 (0x35 hex),[4] Java SE 8 = 52 (0x34 hex), Java SE 7 = 51 (0x33 hex), Java SE 6.0 = 50 (0x32 hex), Java SE 5.0 = 49 (0x31 hex), JDK 1.4 = 48 (0x30 hex), JDK 1.3 = 47 (0x2F hex), JDK 1.2 = 46 (0x2E hex), JDK 1.1 = 45 (0x2D hex).[……]
「Java」- Class File Editor
Java Class File Editor
https://sourceforge.net/projects/classeditor/
JBE – Java Bytecode Editor
http://set.ee/jbe/ 整个也算是可以的了。
Java Overall Editor
http://dirty-joe.com 这东西貌似是给Windows用的。 ├── dirtyjoe.chm ├── dirtyJOE.exe ├── jvmspec.dat ├── PyJOE25.dll ├── PyJOE26.dll ├── PyJOE27.dll └── scripts ├── allatori_decrypt.py ├── pyjoe.py └── zkm_decrypt.py
Class File Editor
http://www.drgarbage.com/bytecode-visualizer/class-file-editor/ Eclipse插件。这个使比较好用的啦,有些BUG。大概使是这个样子的:
ByteCode-Viewer
Github Repo: https://github.com/Konloch/bytecode-viewer[……]
「FernFlower」
Fernflower是第一个实际工作的分析反编译器,可以用于Java和一般的高级编程语言。
获取源码
该源码位于:https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine 及一个克隆出的源码:https://github.com/fesh0r/fernflower
如何构建
参考:https://github.com/MinecraftForge/FernFlower/issues/1
使用gradle进行构建,build.gradle文件参考:https://gist.github.com/LexManos/abf02179db7b5b367ee33639e266b34b
将build.gradle文件放入/intellij-community/plugins/java-decompiler/engine目录后,执行:gradle build
生成的fernflower-x.x.x.jar位于build/libs/目录下。
直接下载
Download Page: http://files.minecraftforge.net/maven/net/minecraftforge/fernflower/
安装的程序文件
fernflower-x.x.x.jar,用于反编译.class文件
fernflower wapper:
#!/bin/bash
# 不适用于将该脚本加入环境变量的情况
script_name=”$0″
jar_path=$(test -L $script_name && readlink $script_name || echo $script_name)
jar_path=$(dirname $jar_path)
java -jar “${jar_path}”/fernflower.jar $@
参考文献
Homepage: https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine
TODO 完善FernFlower内容[……]
「fernflower-x.x.x.jar」
如何在命令行中使用(Running from command line)
java -jar fernflower.jar [-<option>=<value>]* [<source>]+ <destination>
星号(*):0次或者多次。 加号(+):1次或者多次。
<option>, <value> 具有相应值的命令行选项(详见”Command-line options”部分)
<source> 需要反编译的文件或者目录。目录会进行递归扫描。允许的文件扩展格式为:class、zip、jar。 <source>以-e=表示“库”不会被反编译,但是会被用于分析方法和类之间的关系。特别是重命名标识符(选项’ren’),可以从有关外部类的信息中受益。
<destination> 输出目录。
简单示例(Examples)
java -jar fernflower.jar -hes=0 -hdc=0 c:\Temp\binary\ -e=c:\Java\rt.jar c:\Temp\source\
java -jar fernflower.jar -dgs=1 c:\Temp\binary\library.jar c:\Temp\binary\Boot.class c:\Temp\source\
命令行选项(Command-line options)
除了mpm和urc之外,在括号中给出的是默认值(如果有),1:表示选项被激活;0:被禁用。
通常,以下选项将由用户更改,hes,hdc,dgs,mpm,ren,urc选项可以保留原样,它们针对专业的逆向工程师。
rbr (1) 隐藏桥接方法。
rsy (0) 隐藏合成的类成员。
din (1) 反编译内部类。
dc4 (1) 折叠1.4类引用
das (1) 反编译断言。
hes (1) 隐藏空super调用
hdc (1) 隐藏空的默认构造器。
dgs (0) 反编译通用签名。
ner (1) 假设return不抛出异常。
den (1) 反编译枚举。
rgn (1) 当getClass()是合格的new语句的一部分时,移除getClass()调用。
lit (0) 输出数字文字“原样”
asc (0) 将字符串和字符文字中的非ASCII字符编码为Unicode转义。
bto (1) 将int 1解释为布尔值true(编译器BUG的解决方法)
nns (1) 允许未设置的合成的属性(编译器错误的解决方法)[……]
「JADX」
JADX简介
Dex到Java的反编译器
JADX的安装
下载源码并进入源码目录
#!/bin/bash
git clone https://github.com/skylot/jadx.git
cd jadx
执行gradlew命令执行构建编译
#!/bin/bash
./gradlew dist
运行jadx命令
#!/bin/bash
# 编译结束后,生成的可执行程序位于源码目录下的buid目录中
./build/jadx/bin/jadx-gui
安装的可执行程序
jadx 命令行程序.
jadx-gui jadx命令的GUI接口。
参考文献
JADX在GitHub中的项目地址
完善JADX文章内容[……]
「JODE – Java Optimize and Decompile Environment」
参考文献
Homepage: http://jode.sourceforge.net/[……]
「Java」- 虚拟机指令
简述
每个指令的长度为:1BYTE、0~255、0x00~0xFF
一、未归类系列
此系列暂未归类。
二、const系列
该系列命令主要:负责把简单的数值类型送到栈顶。该系列命令不带参数。注意:只把简单的数值类型送到栈顶时,才使用如下的命令。 比如:对应int型,只能把-1,0,1,2,3,4,5送到栈顶(分别采用iconst_m1,iconst_0, iconst_1, iconst_2, iconst_3, iconst_4, iconst_5) 对于int型及其他的数值,使用push系列命令(比如bipush)。
三、push系列
该系列命令负责把一个整形数字(长度比较小)送到到栈顶。该系列命令有一个参数,用于指定要送到栈顶的数字。 注意该系列命令只能操作一定范围内的整形数值,超出该范围的使用将使用ldc命令系列。
四、ldc系列
该系列命令:负责把数值常量或String常量从常量池中推至栈顶。该命令后面需要给一个表示常量在常量池中位置(编号)的索引值, 哪些常量是放在常量池呢?比如:
final static int id = 32768;
final static float double = 6.5;
除了const系列命令和push系列命令操作范围之外的数值类型常量,都放在常量池中. 另外,所有不是通过new创建的String都是放在常量池中的。
五、load系列
5.1、load系列A 该系列命令负责把本地变量的送到栈顶。这里的本地变量不仅可以是数值类型,还可以是引用类型。 对于前四个本地变量可以采用iload_0,iload_1,iload_2,iload_3(它们分别表示第0,1,2,3个整形变量)这种不到参数的简化命令形式。 对于第4以上的本地变量将使用iload命令这种形式,在它后面给一参数,以表示是对第几个(从0开始)本类型的本地变量进行操作。 对本地变量所进行的编号,是对所有类型的本地变量进行的(并不按照类型分类)。 对于非静态函数,第一变量是this,即其对于的操作是aload_0. 还有函数传入参数也算本地变量,在进行编号时,它是先于函数体的本地变量的。
5.2、load系列B 该系列命令负责把数组的某项送到栈顶。该命令根据栈里内容来确定对哪个数组的哪项进行操作。 比如,如果有成员变量:final String names[]={“robin”,”hb”}; 那么这句话:String str=names[0];对应的指令为 17: aload_0 //将this引用推至栈顶,即压入栈。 18: getfield #5; Field names:[Ljava/lang/String;将栈顶的指定的对象的第5个[……]
「Java」- 虚拟机指令
astore 0x3a
将「栈顶引用型数值」存入指定的「本地变量」
astore_<n>
将「栈顶引用型数值」存入「索引为n的本地变量」
Operand Stack …, objectref → …
Forms astore_0 = 75 (0x4b) astore_1 = 76 (0x4c) astore_2 = 77 (0x4d) astore_3 = 78 (0x4e)
objectref必须是returnAddress或者reference类型。
aload 0x19
将指定的「引用类型本地变量」推至「栈顶」
aload_<n>
将「第n+1个引用类型本地变量」推至「栈顶」
Forms aload_0 = 42 (0x2a) aload_1 = 43 (0x2b) aload_2 = 44 (0x2c) aload_3 = 45 (0x2d)
Operand Stack … → …, objectref
aaload 0x32
将「引用型数组」中「索引指定的值」推至栈顶
Operand Stack …, arrayref, index → …, value
athrow 0xbf
将栈顶的异常抛出
arraylength 0xbe
获得数组的长度值,并压入栈顶
Operand Stack …, arrayref → …, length
areturn 0xb0
从当前方法返回对象引用
aastore 0x53
将栈顶引用型数值存入指定数组的指定索引位置
Operand Stack …, arrayref, index, value → …
aconst_null 0x01
将「null对象引用」推至栈顶
Operand Stack … → …, null
anewarray 0xbd
创建一个引用类型(如类,接口,数组)的数组,并将其引用值压入栈顶。
Operand Stack …, count → …, arrayref
引用值占用两个字节,该引用值为指向运行时常量池中某个项的索引值,该项的内容为一个类、接口或数组。[……]
「Java」- 虚拟机指令
bipush 0x10
将一个单字节的常量值(-128~127)推至栈顶。单字节。
Operand Stack
… → …, value
baload 0x33
将boolean或byte型数组指定索引的值推至栈顶
bastore 0x54
将栈顶boolean或byte型数值存入指定数组的指定索引位置[……]
「Java」- 虚拟机指令
checkcast 0xc0
检验类型转换,检验未通过将抛出ClassCastException
castore 0x55
将栈顶char型数值存入指定数组的指定索引位置
Operand Stack
…, arrayref, index, value → …
caload 0x34
将char型数组指定索引的值推至栈顶
Operand Stack
…, arrayref, index → …, value[……]
「Java」- 虚拟机指令
dload 0x18
将指定的double型本地变量推至栈顶
dload_<n>
将第n+1个double型本地变量推至栈顶
Forms
dload_0 = 38 (0x26) dload_1 = 39 (0x27) dload_2 = 40 (0x28) dload_3 = 41 (0x29)
Operand Stack
… → …, value
dup 0x59
复制栈顶数值(数值不能是long或double类型的),并将复制值压入栈顶
dup_x1 0x5a
复制栈顶数值(数值不能是long或double类型的),并将复制值压入栈顶的两个值下面
Operand Stack
…, value2, value1 → …, value1, value2, value1
dup_x2 0x5b
复制栈顶数值(数值不能是long或double类型的)并将三个(或两个)复制值压入栈顶
dup2 0x5c
复制栈顶一个(long或double类型的)或两个(其它)数值并将复制值压入栈顶
dup2_x1 0x5d
复制栈顶数值(long或double类型的)并将两个复制值压入栈顶
dup2_x2 0x5e
复制栈顶数值(long或double类型的)并将三个(或两个)复制值压入栈顶
d2i 0x8e
将栈顶double型数值强制转换成int型数值并将结果压入栈顶
d2l 0x8f
将栈顶double型数值强制转换成long型数值并将结果压入栈顶
d2f 0x90
将栈顶double型数值强制转换成float型数值并将结果压入栈顶
daload 0x31
将double型数组指定索引的值推至栈顶
dstore 0x39
将栈顶double型数值存入指定本地变量
dstore_<n> 0x47
将栈顶double型数值存入第一个本地变量
Forms
dstore_0 = 71 (0x47) dstore_1 = 72 (0x48) dstore_2 = 73 (0x49) dstore_3 = 74 (0x4a)
Operand Stack
…, value → …
dastore 0x52
将栈顶double型数值存入指定数组的指定索引位置
dreturn 0xaf
从当前方法返回double
dcmpl 0x97
比较栈顶两double型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
dcmpg 0x98
比较栈顶两d[……]
「Java」- 虚拟机指令
fload 0x17
将指定的float型本地变量推至栈顶
fload_<n>
将第n+1个float型本地变量推至栈顶
Forms
fload_0 = 34 (0x22) fload_1 = 35 (0x23) fload_2 = 36 (0x24) fload_3 = 37 (0x25)
Operand Stack
… → …, value
f2i 0x8b
将栈顶float型数值强制转换成int型数值并将结果压入栈顶
f2l 0x8c
将栈顶float型数值强制转换成long型数值并将结果压入栈顶
f2d 0x8d
将栈顶float型数值强制转换成double型数值并将结果压入栈顶
faload 0x30
将float型数组指定索引的值推至栈顶
fstore 0x38
将栈顶float型数值存入指定本地变量
fstore_<n>
将栈顶float型数值存入第一个本地变量
Forms
fstore_0 = 67 (0x43) fstore_1 = 68 (0x44) fstore_2 = 69 (0x45) fstore_3 = 70 (0x46)
Operand Stack
…, value → …
freturn 0xae
从当前方法返回float
fcmpl 0x95
比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
fcmpg 0x96
比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
frem 0x72
将栈顶两float型数值作取模运算并将结果压入栈顶
fneg 0x76
将栈顶float型数值取负并将结果压入栈顶
fdiv 0x6e
将栈顶两float型数值相除并将结果压入栈顶
fmul 0x6a
将栈顶两float型数值相乘并将结果压入栈顶
fsub 0x66
将栈顶两float型数值相减并将结果压入栈顶
fadd 0x62
将栈顶两float型数值相加并将结果压入栈顶
fastore 0x51
将栈顶float型数值存入指定数组的指定索引位置
fconst_<f>
将float型(f)推至栈顶
Forms
fconst_0 = 11 (0xb) fconst_1 = 12 (0xc) fconst_2 = 13 (0xd)
Operand Stack
… → …, <f&[……]
「Java」- 虚拟机指令
goto 0xa7
无条件跳转
goto_w 0xc8
无条件跳转(宽索引)
getstatic 0xb2
将指定类的「静态域的值」压入「栈顶」
Format getstatic indexbyte1 indexbyte2
Operand Stack …, → …, value
getfield 0xb4
获取指定类的实例域,并将其值压入栈顶[……]
「Java」- 虚拟机指令
iconst_<i>
将「int型值」推至「栈顶」
Forms iconst_m1 = 2 (0x2) iconst_0 = 3 (0x3) iconst_1 = 4 (0x4) iconst_2 = 5 (0x5) iconst_3 = 6 (0x6) iconst_4 = 7 (0x7) iconst_5 = 8 (0x8)
Operand Stack … → …, <i>
istore 0x36
将「栈顶int型值」存入指定「本地变量」
Format istore index
Operand Stack …, value → …
istore_<n>
将「栈顶int型值」存入「第n+1个本地变量」
Forms istore_0 = 59 (0x3b) istore_1 = 60 (0x3c) istore_2 = 61 (0x3d) istore_3 = 62 (0x3e)
Operand Stack …, value → …
iload 0x15
将指定的「int类型本地变量」推至「栈顶」
iload_<n>
将索引为「n」的「int型本地变量」推至「栈顶」
Forms iload_0 = 26 (0x1a) iload_1 = 27 (0x1b) iload_2 = 28 (0x1c) iload_3 = 29 (0x1d)
Operand Stack … → …, value
ixor 0x82
将栈顶两int型数值作“按位异或”(value1 ~ value2),然后将结果压入栈顶
Operand Stack …, value1, value2 → …, result
iinc 0x84
格式:iinc index const 将指定int型变量增加指定值(i++, i–, i+=2)
Operand Stack No change
i2b 0x91
将栈顶int型数值强制转换成byte型数值并将结果压入栈顶
i2c 0x92
将栈顶int型数值强制转换成char型数值并将结果压入栈顶
Operand Stack …, value → …, result
i2s 0x93
将栈顶int型数值强制转换成short型数值并将结果压入栈顶
instanceof 0xc1
检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶
i2l 0x85
将栈顶int型数值强制转换成long型数值并将结果压入栈顶
i2f 0x[……]
「Java」- 虚拟机指令
if<cond>
当栈顶int型数值与0进行比较,满足条件时跳转
Forms ifeq = 153 (0x99) ifne = 154 (0x9a) iflt = 155 (0x9b) ifge = 156 (0x9c) ifgt = 157 (0x9d) ifle = 158 (0x9e)
Operand Stack …, value → …
if_icmp<cond>
比较栈顶两个int型数值大小,靠近栈底的值为左值,满足条件时进行跳转。
Forms if_icmpeq = 159 (0x9f) if_icmpne = 160 (0xa0) if_icmplt = 161 (0xa1) if_icmpge = 162 (0xa2) if_icmpgt = 163 (0xa3) if_icmple = 164 (0xa4)
Operand Stack …, value1, value2 → …
if_acmp<cond>
比较栈顶两个引用型数值,满足条件时进行跳转。
Forms if_acmpeq = 165 (0xa5) if_acmpne = 166 (0xa6)
Operand Stack …, value1, value2 → …
ifnull 0xc6
为null时跳转
ifnonnull 0xc7
不为null时跳转[……]
「Java」- 虚拟机指令
invokevirtual 0xb6
调用「实例的方法」,函数的返回值位于栈顶。
Format invokevirtual indexbyte1 indexbyte2
Operand Stack …, objectref, [arg1, [arg2 …]] → …
invokespecial 0xb7
调用超类构造方法,实例初始化方法,私有方法
Format invokespecial indexbyte1 indexbyte2
Operand Stack …, objectref, [arg1, [arg2 …]] → …
invokestatic 0xb8
调用静态方法
Format invokestatic indexbyte1 indexbyte2
Operand Stack …, [arg1, [arg2 …]] → …
invokeinterface 0xb9
调用接口方法
Format invokeinterface indexbyte1 indexbyte2 count 0
Operand Stack …, objectref, [arg1, [arg2 …]] → …
invokedynamic 0xba[……]
「Java」- 虚拟机指令
jsr 0xa8
跳转至指定位置,该位置使用jsr命令后的2BTYE的offset指定的,offset=(branchbyte1 << 8) | branchbyte2, 并将jsr的下一条指令地址压入栈顶,用作返回地址。跳转的目标地址必须在包含jsr指令的方法的内部。
Format
jsr branchbyte1 branchbyte2
Operand Stack
… → …, address
address为紧跟在jsr后的那条指令的地址。
jsr_w 0xc9
跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶[……]
「Java」- 虚拟机指令
ldc 0x12
将int, float或String型常量值,从常量池中推至栈顶。
Operand Stack … → …, value
ldc_w 0x13
将int, float或String型常量值从常量池中推至栈顶(宽索引)
ldc2_w 0x14
将long或double型常量值从常量池中推至栈顶(宽索引)
lstore 0x37
将栈顶long型数值存入指定本地变量
lstore_<n>
将栈顶long型数值存入第一个本地变量
Forms
lstore_0 = 63 (0x3f) lstore_1 = 64 (0x40) lstore_2 = 65 (0x41) lstore_3 = 66 (0x42)
Operand Stack
…, value → …
lconst_<l>
将long型(0)推至栈顶
Forms
lconst_0 = 9 (0x9) lconst_1 = 10 (0xa)
Operand Stack
… → …, <l>
lload 0x16
将指定的long型本地变量推至栈顶
lload_<n>
将第一个long型本地变量推至栈顶
Forms
lload_0 = 30 (0x1e) lload_1 = 31 (0x1f) lload_2 = 32 (0x20) lload_3 = 33 (0x21)
Operand Stack
… → …, value
l2i 0x88
将栈顶long型数值强制转换成int型数值并将结果压入栈顶
l2f 0x89
将栈顶long型数值强制转换成float型数值并将结果压入栈顶
l2d 0x8a
将栈顶long型数值强制转换成double型数值并将结果压入栈顶
laload 0x2f
将long型数组指定索引的值推至栈顶
lastore 0x50
将栈顶long型数值存入指定数组的指定索引位置
lreturn 0xad
从当前方法返回long
lookupswitch 0xab
用于switch条件跳转,case值不连续(可变长度指令)
lcmp 0x94
比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶
lor 0x81
将栈顶两long型数值作“按位或”并将结果压入栈顶
lxor 0x83
将栈顶两long型数值作“按位异或”并将结果压入栈顶
land 0x7f
将栈顶两long型数值作“按位与”[……]
「Java」- 虚拟机指令
nop 0x00
什么都不做
new 0xbb
创建一个对象,并将其引用值压入栈顶
Format
new indexbyte1 indexbyte2
Operand Stack
… → …, objectref
newarray 0xbc
创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶[……]
「Java」- 虚拟机指令
putstatic 0xb3
将「栈顶的值」为赋给指定的「类的静态域」
Operand Stack …, value → …
putfield 0xb5
用「栈顶的值」为指定的「类的实例域」赋值
pop 0x57
将栈顶数值弹出 (数值不能是long或double类型的)
Operand Stack …, value → …
pop2 0x58
将栈顶的一个(long或double类型的)或两个数值弹出(其它)[……]