简介 :
简单到不行
100
*39 solves*
这是一道送分题,不要白不要~
[附件下载](http://iscc.isclab.org.cn/static/uploads/c9f684fd3f5f416eaaa34355263e287a/crackone.apk)
分析 :
首先需要找到自己顺手的工具先反编译安卓的APK
搞清楚APK内部的逻辑
这里使用 jeb 进行 APK 的反编译
直接将 APK 拖入窗口左边的工程栏目 , 就会自动进行反编译
可以看到这个工程有两个程序员写的类 , MainActivity 以及 Digest 类
分别反编译 :
根据对代码的分析可以知道 , 这个安卓应用有两个控件 :
一个 EditText 用于接收用户输入 , 另一个 Button 用于检验用户输入是否合法
需要我们重点关注 Button 的 onClick 事件
这里调用了一个 JNI 层的函数 : checkFlag , 我们只使用 jeb 是不能直接反汇编出 JNI 层的函数的
需要将 APK 中的动态链接库 .so 文件单独提取出来进行分析
这里根据字符串 : "ABCD...0123456789+/" 以及下面的 append("=")
可以很容易判断出是 Base64 编码
APK 中打包了两种架构的动态链接库 , 我们这里使用 x86 架构的.so 文件
使用 IDA_pro 载入这个 so 文件
找到 checkFlag 函数
f5 反编译成 c 代码
不过这里反编译成的 c 代码在有的地方似乎存在一点问题 , 如果发现某些地方比较诡异的话
再回去看看汇编基本上就可以明白了 , 可能是编译器优化的问题 , 导致 IDA 不能正确识别出 C
在 checkFlag 函数中 , 重点有这么两段
将传入的字符串使用了 malloc 重新复制了一份
这里算法的意思是 :
将用户输入的字符串按照长度分割成两半
把前一半字符串中的字符按照从左到右的顺序取出来 , ASCII码减去 5 , 然后与后半个字符串与之对应的位置进行交换
举个例子 :
用户输入的字符串 str = "9876543210"
分成两半 :
前一半为 : "98765"
后一半为 : "43210"
对前一半的所有字符 , 从左向右取 , 第一个取到的是 9 , ASCII - 5 , 变成字符 4 , 然后与后半个字符串的对应位置 (也就是 '0' ) 的位置进行交换
那么这一次变换得到的结果就是 :
str = "0876543214"
那么这个算法总结一下的话 , 其实可以这样理解 :
首先将整个字符串逆序 , 然后将后半个字符串的每一个字符ASCII都减去 5
这里在逆向过程中的一些小技巧 :
1. f5 将汇编代码反编译成 c 代码
2. 光标在某一个符号上的时候 , n 可以修改这个符号的名称
3. *((_BYTE *)new_str + i) 类似这种结构 , 对应到 C 语言里面其实就是 new_str[i]
当处理完用户输入的字符串的时候 , 进行的操作是 :
将用户处理完的结果和程序中已经存在的一段数据逐字符进行对比 , 对比如果完全一致就返回 1 , 反之就 return 0
程序中的这个字符串是 :
=0HWYl1SE5UQWFfN?I+PEo.UcshU
这里注意到第一个字符是 "=" , 根据之前的分析 , 最终明文的字符串最后一个字符是 ' = '
这也就基本上对应了 java 里面的 Digest 类中的 Base64 算法
那么解密算法就是将这个密文逆序 , 前半部分的ASCII码全部加上 5
得到一个 Base64 解这个 Base64 即可
cipher = "=0HWYl1SE5UQWFfN?I+PEo.UcshU"
def decrypt(cipher):
plain = list(cipher[::-1])
for i in range(0,(len(plain) / 2)):
plain[i] = chr(ord(plain[i]) + 5)
return ''.join(i for i in plain)
print decrypt(cipher).decode("base64")
参考资料 :
征服C指针
汇编语言
安卓开发
IDA_pro指南
Android逆向
Android安卓破解之逆向分析SO常用的IDA分析技巧*