逆向Fibonacci-WP

前言

分析

在这里插入图片描述

  • 64位未知的可执行程序,用IDA打开可以看到很多很复杂的代码。看不太懂,于是单步跟了几次。发现程序会解密一段代码,然后开启一个线程去执行解密出来的代码。但是线程里边函数下断似乎没什么好的效果。
  • 然后在字符串中看到很多java代码的身影
    在这里插入图片描述
  • 莫非刚才解密的是java的代码?由此想起了我们课设的时候,有同学用java写项目,然后用某种工具生成EXE文件,那么应该有某种方法可以提取出原来的代码数据。
  • 上了度娘之后,看到了夜影师傅的题解,此题才得以比较顺利的解决。
  • 原来使用了Jar2Exejava代码和jvm一起打包成exe,可以使得在没有java虚拟机的机器上运行该java程序。原理则是通过JNI接口,创建JVM来执行封装的java代码。
  • 通过前面的单步得知,这是在内部执行代码,我们没有现成的文件可以反编译,只能是通过dump内存来获取java代码数据
  • 有个自动化工具 e2j-agent.jar,此工具能够dump出被执行过的方法,而没有被执行的方法是不能dump的。
  • 现在解包一下程序
    在这里插入图片描述
  • 然后在该工具目录下会出现一个dump文件,用jd-gui打开
    在这里插入图片描述
  • 发现还有不完整的地方,例如找不到b这个类。如果说这个程序的flag是求a[100000000000000],很不现实;flag很有可能就是变量m
  • 要得到b类,还得dump内存。java数据存放在RCDATA段,我使用的是ResouceHack来找到相关的段落
    在这里插入图片描述
  • 这是unicode编码,将其转为hex,然后用十六进制编辑器打开源程序,找到对应数据的偏移
    在这里插入图片描述
    在这里插入图片描述
  • 得到文件偏移是0x71D98,然后用x64dbg加载源文件,快捷键ctr+shift+g,填入文件偏移来到java原始数据处
    在这里插入图片描述
  • 硬件-访问8字节断点,运行程序,最后确定断在的解密行数位置在
    在这里插入图片描述
  • 可以联想脱壳的过程,这个jb跳转实际上就是循环解密,我们在他的下一个位置下断。运行程序,直到断下,此时观察寄存器状态。
    在这里插入图片描述
    在这里插入图片描述
  • 可以确定r10指向的内存就是解密后的java代码。用Scylla dump内存,大小是猜测的,无法确定,我测试过后填的是1900,再大点就dump不出来了
    在这里插入图片描述
  • 文件名后缀是jar,然后又一个坑点就是在这个文件必须用win rar解压,我习惯用360解压,但他总是给我报错——压缩文件损坏。导致我一度认为dump内存那个步骤错了,很烦。最后解压出来得到
    在这里插入图片描述
  • 用十六进制编辑器依次打开,可以知道1ab0208dc7dce9e9是class文件,用jd-gui打开得到完整的java代码
    在这里插入图片描述

脚本

package 逆向脚本;

public class Test {
	public static String hello(String aaa, String bbb)
	  {
	    int[] iS = new int[256];
	    byte[] iK = new byte[256];
	    for (int i = 0; i < 256; i++) {
	      iS[i] = i;
	    }
	    int j = 1;
	    for (short i = 0; i < 256; i = (short)(i + 1)) {
	      iK[i] = ((byte)bbb.charAt(i % bbb.length()));
	    }
	    j = 0;
	    for (int i = 0; i < 255; i++)
	    {
	      j = (j + iS[i] + iK[i]) % 256;
	      int temp = iS[i];
	      iS[i] = iS[j];
	      iS[j] = temp;
	    }
	    int i = 0;
	    j = 0;
	    char[] iInputChar = aaa.toCharArray();
	    char[] iOutputChar = new char[iInputChar.length];
	    for (short x = 0; x < iInputChar.length; x = (short)(x + 1))
	    {
	      i = (i + 1) % 256;
	      j = (j + iS[i]) % 256;
	      int temp = iS[i];
	      iS[i] = iS[j];
	      iS[j] = temp;
	      int t = (iS[i] + iS[j] % 256) % 256;
	      int iY = iS[t];
	      char iCY = (char)iY;
	      iOutputChar[x] = ((char)(iInputChar[x] ^ iCY));
	    }
	    return new String(iOutputChar);
	  }
	public static void main(String[] args) {
		 char[] x = { '}', '\020', 'ý', 'É', '\013', '\026', '9', 'D', '7', ',', ' ', 'Í' };
		 char[] y = {
		    't', 
		    '–', 
		    '®', 
		    'D', 
		    '´', 
		    'Z', 
		    'Ö', 
		    '½', 
		    'O', 
		    '5', 
		    '
', 
		    '\n', 
		    '+', 
		    '+', 
		    '½', 
		    'Ù', 
		    'O', 
		    '`', 
		    '\023', 
		    'Š', 
		    'Ç', 
		    '€', 
		    '@', 
		    'Ü', 
		    'Þ', 
		    'ê', 
		    '\013', 
		    '¯', 
		    'ä', 
		    '' };
		  
		 System.out.println(hello(new String(y),new String(x)));
	}
}

PCTF{1ts_not_5c2ipt_Chall3nge}

补充

  • 写脚本的时候注意 tostring与new string的区别

toString()与new String ()用法区别
str.toString是调用了b这个object对象的类的toString方法。一般是返回这么一个String:[class name]@[hashCode]。
new String(str)是根据parameter是一个字节数组,使用java虚拟机默认的编码格式,将这个字节数组decode为对应的字符。若虚拟机默认的编码格式是ISO-8859-1,按照ascii编码表即可得到字节对应的字符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值