知识点

  • 在java开发里,源代码是.java文件,然后经过编译后生成计算机识别的.class文件

Java逆向算是属于安卓逆向的。一般是apk文件,放到jadx里面进行反汇编,找到MainActivity源码再进行对其Java源代码分析,简单的就可以得到flag了。

工具

JD-GUI

  • 官方地址:http://java-decompiler.github.io/
  • JD-GUI不需要安装,直接点击运行,可以反编译jar,class文件,可将整个jar包直接保存为*.src.zip源代码包,反编译迅速,错误率极少。
  • JD分为JD-GUI、JD-Eclipse/JD-Intellij两种运行方式,JD-GUI是以单独的程序的方式运行,JD-Eclipse/JD-Intellji则是以一个Eclipse/IDEA插件的方式运行。

jadx

fernflower

  • 对于未做混淆的war包和jar包,使用fernflower是一个比较好的反编译软件,比jd-gui好用,把所用工程从class反编译成java可以很好的进行字符串搜索和匹配,非常适合纯代码审计(当无法进行动态调试的时候是一个不错的选择)

命令格式为:

java -jar java-decompiler.jar [-<option>=<value>]* [<source>]+ <destination>

source 表示jar包所在目录,可以填写单个jar包,也可以填写一个目录(将解压目录下所有jar包)destination 表示反编译的java源码生成目录

官方地址:https://github.com/fesh0r/fernflower

idea decompiler

  • fernflower是 IDEA 采用的反编译工具,在IDEA打开class文件时,就是通过该组件的反编译能力。java-decompiler 是IDEA中的插件名称,实际上来源于 fernflower 工具。
  • 直接使用IDEA打开我们需要分析的war包工程,IDEA会自动帮助我们将class进行反编译并展示出来,节约了我们好多工作。
    • 对于jar包,将jar所在的目录(lib)右键设置成library
  • 直接调用IDEA java-decompiler.jar进行反编译
    • IDEA中java-decompiler.jar默认是安装的,位于IDEA安装目录\plugins\java-decompiler\lib\
    • 创建一个目录,用来存储反编译后的文件,如test文件夹。我这里拿FrontDemoController.class来举例:
java -cp "D:\Program\IntelliJ IDEA 2018.1.6\plugins\java-decompiler\lib\java-decompiler.jar" org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler -dgs=true .\FrontDemoController.class .\test\

这样可以将FrontDemoController.class直接反编译成FrontDemoController.java。对于jar包,反编译结果也打包成jar包,这点儿需要注意,你直接使用压缩工具解压即可。这里拿dom4j这个jar来举例,以下截图是反编译以后使用压缩工具解压jar的结果,可以明显看出jar包中的class文件均被反编译成java。

bytecode-viewer

其实有很多大佬改jar包的时候都会使用smali和baksmali ,然后在dex2jar。这确实可以适应大部分情况,但有些情况dex2jar后的jar包出问题了,这时候你可能会用到这款工具。

例题

BUU-Java逆向解密

  • 程序员小张不小心弄丢了加密文件用的秘钥,已知还好小张曾经编写了一个秘钥验证算法,聪明的你能帮小张找到秘钥吗? 注意:得到的 flag 请包上 flag{} 提交

  • class文件用jd-gui打开

alt text

  • 还比较好理解,写脚本
  • 脑子不好

第一段(错误): i ^ 32 - ord(‘@’)
由于 - 优先级高于 ^,Python 会先计算 32 - 64(结果为 -32)。

a = [180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65]
c = []
for i in a:
c.append(chr((i^32)-ord('@')))
print(''.join(c))
  • 其实写的不好,但正好实践下前面join的用法所以就这么写了
  • 更好的学姐的版本:
KEY = [180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 
133, 191, 134, 140, 129, 135, 191, 65 ]
flag=''
for i in range(len(KEY)):
flag+=chr((KEY[i]^0x20)-64)
print(flag)

[SWPUCTF 2021 新生赛]easyapp

  • 太好笑了附件上来我还以为就是apk呢查包+010最后ai和我说apk不仅是504b开头改zip后缀看里面文件名巴拉巴拉解压一看一个apk。。。没招了有点蠢

alt text

  • 直接搜索mainactivity找到
棿棢棢棲棥棷棊棐棁棚棨棨棵棢棌
  • ok从声明和获取那一行都能看到加密函数,双击跟进
public class Encoder {
// 1. 定义一个私有变量 key,初始值为 123456789
private int key = 123456789;

public String encode(String str) {
StringBuilder sb = new StringBuilder();

// 2. 将输入的字符串转换为字符数组,进行遍历
for (char c : str.toCharArray()) {

// 3. 核心算法:将字符 c 与 key 进行“按位异或”运算 (XOR)
// 4. 然后强制转换回 char 类型并拼接到字符串生成器中
sb.append((char) (c ^ this.key));
}
return sb.toString();
}
}
  • ai
  • 为什么会出现“中文字符”?
  • 在 Java 中,char 是 16 位的。当你把一个普通的英文字符(如 ‘a’,值约为 97)与一个巨大的数字(如 123456789)进行异或运算时,结果会变成一个非常大的数值。这个数值超出了 ASCII 范围,落入了 Unicode 编码的中文字符区间,所以你看到了“棿棢棢…”这样的木字旁字符。

越界的错误:由于ASCII有效位最大位127,那么我们只用%128就好了

alt text

  • 就在前面代码下面的类里面可以看到key被修改了
public class MainActlvity {
public MainActlvity() throws ... {
try {
// 1. 获取 Encoder 类的名为 "key" 的成员变量(即使它是私有的)
Field declaredField = Encoder.class.getDeclaredField("key");

// 2. 暴力破解访问权限,允许修改私有变量(private 限制失效)
declaredField.setAccessible(true);

// 3. 将 MainActivity 中静态实例 encoder 的 key 值修改为 987654321
declaredField.set(MainActivity.encoder, 987654321);

} catch (Exception e) {
e.printStackTrace();
}
}
}
  • 也就是说在这一步key被修改了:

alt text

a = '棿棢棢棲棥棷棊棐棁棚棨棨棵棢棌'
b = ''
for i in a:
b += chr((ord(i)^987654321)%128)
print(b)
# NSSCTF{apkYYDS}