一道XCTF安卓逆向之easyapk的变种题分析

0x00

下载下来是一个apk,拖进模拟器运行一下,是一个输入框,输入flag然后检查flag是否正确。

改后缀为zip后使用dex2jar反编译得到一个classes-dex2jar.jar。

0x01

将这个jar拖进jd-gui查看一波反编译出来的java代码,得到如下核心代码:

protected static boolean checkflag(String paramString) {
	    String[] arrayOfString = paramString.split("_");
	    System.out.println("xxx1"+":"+String.format("%d", new Object[] { Integer.valueOf(arrayOfString.length) }));
	    if (arrayOfString.length == 4 && paramString.length() == 27) {
	      StringBuilder stringBuilder = new StringBuilder();
	      for (int i = 0; i < arrayOfString.length; i++) {
	        String str2 = reChange(arrayOfString[i]);
	        System.out.println(str2);
	        String str1 = str2.substring(0, i + 1);
	        str2 = str2.substring(i + 1);
	        str1 = reChange(str1);
	        str2 = reChange(str2);
	        stringBuilder.append(str1);
	        stringBuilder.append(str2);
	      } 
	      BigInteger bigInteger1 = new BigInteger(stringBuilder.toString(), 16);
	      BigInteger bigInteger2 = new BigInteger("A3332C65844CC6F5E5DABE8DD42FDE39", 16);
	      if (bigInteger1.pow(65537).mod(bigInteger2).toString(16).compareTo("6d14c92d4ae244b583227f1e870f57bc") == 0)
	        return true; 
	    } 
	    return false;
	  }
public static String reChange(String paramString) {
	    char[] arrayOfChar = paramString.toCharArray();
	    int j = arrayOfChar.length;
	    for (int i = 0; i < j / 2; i++) {
	      char c = arrayOfChar[i];
	      int k = j - 1 - i;
	      arrayOfChar[i] = arrayOfChar[k];
	      arrayOfChar[k] = c;
	    } 
	    return String.valueOf(arrayOfChar);
	  }

0x02逻辑分析:

首先是一系列的字符变换,这个先不管,看最后一句

bigInteger1.pow(65537).mod(bigInteger2).toString(16).compareTo("6d14c92d4ae244b583227f1e870f57bc"

RSA实锤了。大体就是将所给的字符串经过字符变换之后转换为16进制大整数通过RSA加密。

所以需要先将RSA密文还原为明文,再根据字符变换逆回flag。

0x03攻击RSA

在这里,我开始的思路是猜测私钥不会太大,使用已知明文攻击找私钥,但跑了一个小时还是没出来,所以我估计私钥不会太小。

此处RSA的n:"A3332C65844CC6F5E5DABE8DD42FDE39"和e:"65537"已经给出,故尝试分解n得到p,q,再根据p,q和e得到私钥d。

0x04暴力分解N

此处可以使用在线站点,也可以使用RSAtools:

此处使用RSA-tool2:在N处输入模数N,如果是16进制,右上角base选择16,然后FactorN,得到p:12004517743563056963

q:18070686016358995667

0x05根据p,q,e计算私钥d

此处给出已知p,q,e求d的python3代码:

#已知p,q,e,求解密密钥d
def egcd(a, b):
    if a == 0:
      return (b, 0, 1)
    else:
      g, y, x = egcd(b % a, a)
      return (g, x - (b // a) * y, y)
def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
      raise Exception('模数分解失败')
    else:
      return x % m
def main():
    e=65537
    p=12004517743563056963
    q=18070686016358995667
    d = modinv(e,(p-1)*(q-1))
    print(d) 
if __name__ == '__main__':
    main()

运行之后得到:d=72585793360129405920426096641020292037

0x06根据d与密文c计算明文m

RSA的解密数学公式为:

明文m=c^d (mod N)

这里^指求幂。

其他博客用的是大整数计算工具big Integer Calculator,这个工具我没找到,直接使用python3的pow函数:

pow(c,d,N):

计算得到明文m=62823249404463802755091465448,转为16进制:cafe2c86588df9d0798304e8

0x07回到java,根据字符变换逆回flag。

在checkflag处打断点,输入"abcd_ef_ghijk",使用F5步进查看变量变化情况以观察其逻辑,得到如下逻辑:

1.根据下划线将字符串分为3组

2.分别对每个分组根据组号作为长度做字符串反转

3.分别对每个分组根据组号作为长度做字符串切割

4.拼接操作得到结果:

d abc ef ijk gh

0x08根据字符变换逻辑写出逆向算法:

根据上述逻辑,该字符串分为4组,且第 i 组长度至少为 i  , 否则会发生数组下标越界。

且明文长度为24,推测每组6个字符。如果每一组的长度不等,或许还有其他flag也能通过验证。

根据上述逻辑,将cafe2c86588df9d0798304e8分为4组,第一组取第一个字符拼接到第一组末尾,第二组取前两个字符拼接到第二组末尾,3,4同理。最终得到flag:afe2cc_588d86_079f9d_e88304

发布了109 篇原创文章 · 获赞 34 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/shuaicenglou3032/article/details/103752299
今日推荐