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