18/09/26-2-BugKu-逆向-easy100(LCTF)
Posted fingerprint
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了18/09/26-2-BugKu-逆向-easy100(LCTF)相关的知识,希望对你有一定的参考价值。
0x00
题目链接:https://pan.baidu.com/s/1dIYmMqp73379xRo_6D3fFQ 密码:trg6
0x01
下载压缩包安装apk,打开app。
尝试输入,点击sure按钮,会弹出“oh,no”消息。
0x02
使用jeb2反编译apk,分析Java源码,因为多文件,以及类名和许多变量名、方法都使用a,b,c,d等,导致反编译的源码不容易分析。
0x03
分析算法
1)分析类d
类d实现OnCliListener监听接口,有按钮按下事件的处理方法。if-else语句完成输入flag成功与失败的弹出消息。
分析if语句的判断条件:
1 MainActivity.a(this.a, MainActivity.a(this.a), this.a.findViewById(2131427414).getText().toString())
调用MainActivity中的a方法,返回真或假,传入三个参数,this.a为类d中成员,MainActivity.a(this.a)为MainActivity中a方法的返回值,this.a.findViewById(2131427414).getText().toString()则为app中输入转化为字符串。
接着分析类MainActivity中的a方法。
2)MainActivity中的a方法分析
a.进入反编译的MainActivity,找到a方法,发现为重载。
1 static String a(MainActivity arg1) { 2 return arg1.v; 3 4 static boolean a(MainActivity arg1, String arg2, String arg3) { 5 return arg1.a(arg2, arg3); 6 } 7 8 private boolean a(String arg4, String arg5) { 9 return new c().a(arg4, arg5).equals(new String(new byte[]{21, -93, -68, -94, 86, 117, -19, -68, -92, 33, 50, 118, 16, 13, 1, -15, -13, 3, 4, 103, -18, 81, 30, 68, 54, -93, 44, -23, 93, 98, 5, 59})); 10 }
可以看到private boolean a(String arg4, String arg5)方法中调用了equals方法进行比较返回布尔值,重点分析此方法和其参数。
b.args4是类d中传入的MainActivity.a(this.a),为MainActivity中static String a(MainActivity arg1)方法的返回值,可以看到返回的是arg1.v,而v是MainActivity的String类型的数据成员以及有相应的方法进行赋值。
1 private String v; 2 3 private void p() { 4 try { 5 InputStream v0_1 = this.getResources().getAssets().open("url.png"); 6 int v1 = v0_1.available(); 7 byte[] v2 = new byte[v1]; 8 v0_1.read(v2, 0, v1); 9 byte[] v0_2 = new byte[16]; 10 System.arraycopy(v2, 144, v0_2, 0, 16); 11 this.v = new String(v0_2, "utf-8"); 12 } 13 catch(Exception v0) { 14 v0.printStackTrace(); 15 } 16 }
c.args5是类d中传入this.a.findViewById(2131427414).getText().toString(),即为app中的输入的字符串转化。
d.分析比较,new c().a(args4,args5)和一byte数组转化的String比较,说明ew c().a(args4,args5)返回的为String类型。
e.接着分析new c().a(args4,args5),类c中的a方法。
3) 类c中a方法的分析
1 public String a(String arg5, String arg6) { 2 String v0 = this.a(arg5); 3 String v1 = ""; 4 a v2 = new a(); 5 v2.a(v0.getBytes()); 6 try { 7 v0 = new String(v2.b(arg6.getBytes()), "utf-8"); 8 } 9 catch(Exception v0_1) { 10 v0_1.printStackTrace(); 11 v0 = v1; 12 } 13 14 return v0; 15 } 16 17 private String a(String arg4) { 18 String v0_2; 19 try { 20 arg4.getBytes("utf-8"); 21 StringBuilder v1 = new StringBuilder(); 22 int v0_1; 23 for(v0_1 = 0; v0_1 < arg4.length(); v0_1 += 2) { 24 v1.append(arg4.charAt(v0_1 + 1)); 25 v1.append(arg4.charAt(v0_1)); 26 } 27 28 v0_2 = v1.toString(); 29 } 30 catch(UnsupportedEncodingException v0) { 31 v0.printStackTrace(); 32 v0_2 = null; 33 } 34 35 return v0_2; 36 }
a方法重载,先分析两个参数a方法,调用了一个参数的a方法并传入字符串arg5然后将返回值赋值给v0。
一个参数的a方法比较简单,把传入的字符串每两个字符为一组然后交换这两个字符的位置最后返回改变后的字符串。
接着分析两个参数的a方法。
1 a v2 = new a(); 2 v2.a(v0.getBytes()); 3 try { 4 v0 = new String(v2.b(arg6.getBytes()), "utf-8"); 5 }
发现和类a有关。
4) 类a的分析
1 public class a { 2 private SecretKeySpec a; 3 private Cipher b; 4 5 public a() { 6 super(); 7 } 8 9 protected void a(byte[] arg4) { 10 if(arg4 != null) { 11 goto label_15; 12 } 13 14 try { 15 this.a = new SecretKeySpec(MessageDigest.getInstance("MD5").digest("".getBytes("utf-8")), "AES"); 16 this.b = Cipher.getInstance("AES/ECB/PKCS5Padding"); 17 return; 18 label_15: 19 this.a = new SecretKeySpec(arg4, "AES"); 20 this.b = Cipher.getInstance("AES/ECB/PKCS5Padding"); 21 } 22 catch(UnsupportedEncodingException v0) { 23 v0.printStackTrace(); 24 } 25 catch(NoSuchAlgorithmException v0_1) { 26 v0_1.printStackTrace(); 27 } 28 catch(NoSuchPaddingException v0_2) { 29 v0_2.printStackTrace(); 30 } 31 } 32 33 protected byte[] b(byte[] arg4) { 34 this.b.init(1, this.a); 35 return this.b.doFinal(arg4); 36 } 37 }
可以轻松猜测到这是AES加密,ECB模式,PKCS5Padding填充。
1 protected byte[] b(byte[] arg4) { 2 this.b.init(1, this.a); 3 return this.b.doFinal(arg4); 4 } 5 6 v0 = new String(v2.b(arg6.getBytes()), "utf-8");//来自类c
此段代码即是返回AES加密后的结果,再看类c发现要加密的明文是arg6,传入的arg6即是this.a.findViewById(2131427414).getText().toString(),app中输入的内容的字符串转化。
然后找找密钥。
1 private SecretKeySpec a; 2 3 this.a = new SecretKeySpec(arg4, "AES");
发现arg4作为了密钥,arg4则是在类c中传入的v0.getBytes(),而v0是经过类c中方法处理的字符串,最初是来自类MainActivity的v成员。也就是说字符串成员v在类c中进行变化,然后作为了AES加密的密钥。关于v成员在上文已分析。
0x04
总结算法
题目主要考察AES加密,ECB模式,PKCS5Padding填充,密钥为MainActivity中成员v的变化,明文为在app中输入的内容。然后AES加密结果和byte数组转化的String比较。若相同则弹出“Congratulation”,说明输入的即为flag。
1 //AES加密结果要比较的String 2 new String(new byte[]{21, -93, -68, -94, 86, 117, -19, -68, -92, 33, 50, 118, 16, 13, 1, -15, -13, 3, 4, 103, -18, 81, 30, 68, 54, -93, 44, -23, 93, 98, 5, 59})
0x05
获得flag的操作
首先要找到密钥,使用jeb2进行动态调试,在很多地方设下断点都能获得密钥的内容。操作后得到成员v为“this_is_the_key.”,而变换即为密钥"htsii__sht_eek.y"。
现在已经知道加密方式,密钥,和密文了,解密就很简单了,一个比较麻烦的地方就是解密不太好操作,因为加密结果要比较是的String类型,并且为乱码。如果直接搜索和修改网上的AES源码,也可以得到密文,不过比较麻烦。
一个比较简单的操作是将写脚本将比较的String进行Base64加密(因为在线解密要求输入密文的格式为Base64格式),然后网上搜索在线AES解密。然后选择相应AES解密模式(ECB,PKCS5Padding),输入密钥key,即可进行解密得到flag的Base64格式,接着进行Base64解密即可得到flag。
以上是关于18/09/26-2-BugKu-逆向-easy100(LCTF)的主要内容,如果未能解决你的问题,请参考以下文章