171226 逆向-JarvisOJ(APK_500)

1625-5 王子昂 总结《2017年12月26日》 【连续第452天总结】
A. JarvisOJ-APK_500
B.
跟之前的APK300-DebugMe如出一辙
一大堆反调试,字符串都进行了加密,通过异或动态解密来操作

DebugMe主要的坑点在于IDA把函数的返回值识别错误了,ARM并没看懂,其他还好

这个500的坑点就比较多了..
首先用Apktool和改之理都报AndroidManifest.xml解码失败,于是整个文件都反编译不出来
拖入安卓系统安装是正常的,因此可以直接解压缩出classes.dex,用dex2jar得到jar文件,然后用jd-gui之类的工具看到java

不过为了学习,还是研究了一波
直接解压出来的xml是二进制数据,不能直接查看
但是可以通过010Editor的模板来识别
首先可以发现MagicNumber错误,修改为00030800即可
解包发现还是错误,继续对比正常的xml分析
在这里https://justanapplication.wordpress.com/category/android/android-binary-xml/
有对二进制AndroidManifest.xml的格式解析
可以看到0x20处的StylePoolOffset应该为0,此处为一个巨大的值,试着将其修改
解包就正常了

(修改完了通过压缩软件添加入apk即可)

然而动态调试还是不行,估计是反调试在作祟
直接附加会显示权限不够,通过adb start时Waitting for the debugger不会消失,于是就一直卡着无法进展

继续静态分析
lib中只有libeasy.so一个库,java代码中显示核心方法为native函数:helloword
查看函数列表,没有发现相关的内容

找了一圈,最后在JNI Onload函数中发现了东西
171226 逆向-JarvisOJ(APK_500)_第1张图片
这个熟悉的解密字符串函数没有变,还是它们
下面第二个字符串是函数类型的声明,继续往下看
171226 逆向-JarvisOJ(APK_500)_第2张图片
调用了JNI的RegisterNatives这个方法就很明显了,在动态注册native函数
那么函数名、函数类型都有了,函数体在哪儿?
肯定就是上面那个sub_1198咯

点进去看,果然是
171226 逆向-JarvisOJ(APK_500)_第3张图片
抛开前面的反调试不管,这里进行了一次bytes转码,连接到一个变量中
继续往下读,发现一共有4次变换

  • 第一次
    input[i] ^= i
  • 第二次
    input[i] += 1, i<4
  • 第三次
    前7个字符移到最后,其余字符顺序向前移动
  • 第四次
    异或硬编码数组

最后转16进制与一个结果字符串比较
结果字符串解出来是

ddedd4ea2e7bef168491a6cae2bc660
脚本调用bytes.fromhex的时候发现它长度不对,只有31位
转十六进制的时候是sprintf(buff, “%x”, input),注意格式化不是%02x
因此有一个0被消掉了,需要爆破

写出脚本进行逆解,还是不对
偶然看硬编码数组的时候发现有一个坑
171226 逆向-JarvisOJ(APK_500)_第4张图片
这里进行了标识,说明有地方调用了它
查看交叉引用,发现果然有重写
171226 逆向-JarvisOJ(APK_500)_第5张图片
写了两次,观察发现第二次和原值相同,那说明真正正确的应该是前一个值

修改硬编码脚本后即可得到flag

s = "ddedd4ea2e7bef168491a6cae2bc660"
n = []
for i in range(32):
    p = s[:i] + "0" + s[i:]
    n.append(bytes.fromhex(p))

b = [133, 139, 236, 131, 236, 20, 131, 141, 12, 1, 117, 95, 198, 69, 243, 80]
b[3] = 0x83
b[4] = 0x6c
b[5] = 0x9c
b[6] = 0x83


for a in n:
    flag = []
    flag1 = ""
    for i in range(16):
        p = a[i]^b[i]
        flag.append(bytes((p, )))
    flag = flag[-7:] + flag[:-7]
    for i in range(4):
        flag[i] = bytes((flag[i][0] - 1, ))
    for i in range(16):
        p = flag[i][0] ^ i
        if(p<=32 or p>=127):
            break
        flag1 += chr(p)
    else:
        print(flag1)

得到两个全为可见字符串的flag,很明显发现其中一个为有意义的词组,提交完成

C. 明日计划
JarvisOJ Shell

你可能感兴趣的:(CTF,Android)