0x01 打开题目
目标apk没有加壳,直接显示是输入flag后check即可。这么一来就可以直接分析输入的代码就行。
0x02 java层代码分析
看到代码执行到a函数,a函数为布尔型函数,其中返回ncheck(newa().a(str.getBytes()));中ncheck为navite方法,而ncheck参数是a类中的a方法,str为输入的值,其中传入为输入的值。
跳入a类中可以看到,这里如果是分析习惯的话很快可以看出,这里为魔改table后的base64,因此,这里可以直接写出魔改后的base加解密,那么接下来就是navite方法的处理,以及源数据的对比。
0x03 so层代码分析
进入so代码,快速定位到ncheck中的Java_com_a_easyjni_MainActivity_ncheck,这里不难确认s1就是输入的值,所以只需要将该流程分析清楚就好。
这里直接看汇编代码,汇编代码中这一个模块有两个关键的数据CMPR0, #0x10与CMPR0, #0x1E 转换成10进制如下图,关键就是对比的数据为16与30,那么先看第一个循环,第一个循环的数据cmp前面是ADDSR0, #1,不难猜出就是地址递增,数组中的字符变化,指向下一个字符,从cmp下一个推断STRBR1, [R2,#16] 得出,数据跳到第16位,再从LDRBR1, [R1,#16]中推断出来,数据是与自身16位递增做替换,因此,这里是做了简单的以第15与16位做中间对称形成了数据对称偏移。
0x04 解题
对比ida,ghidra的优势就是伪代码层面相对比较容易看懂,ida分析的时候许多时候还是要结合汇编进行分析,这点上,ghidra用于校验是最好的选择。
这里流程是要把数据中间对称对换,然后两两调换,逆回来的顺序便是先两两调换再中间对称对换,当然,这里可以直接写个程序就可以把对换后的数据写出来。
if __name__ == '__main__': data = "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7" dd = [] for i in data: dd.append(i) for i in range(0,len(dd),2): tmp = dd[i] dd[i] = dd[i 1] dd[i 1] = tmp for i in range(0,len(dd)/2): tmp = dd[i] dd[i] = dd[i 16] dd[i 16] = tmp print ''.join(dd) |
---|
这里有个软件可以直接通过Editor010修改base64的table值,修改到前面java层分析获取到的table数据,然后这里就可以直接进行解密了,如果在比赛中这种快速完成解密操作当然是最好的,但是下面为了凑文章字数,我还是继续编写解密base64直接可以改掉base64的table的代码。
这里解题结果如下:
那么直接再python中base64的encode与decode控制base64的值就是知识点。
通过修改base64的Table,然后算出加密后的值。
s = "i5jLW7S0GX6uf1cv3ny4q8es2Q bdkYgKOIT/tAxUrFlVPzhmow9BHCMDpEaJRZN"def decode_str(inputs): bin_str = [] for i in inputs: if i != '=': x = str(bin(s.index(i))).replace('0b', '') bin_str.append('{:0>6}'.format(x)) outputs = "" nums = inputs.count('=') while bin_str: temp_list = bin_str[:4] temp_str = "".join(temp_list) if (len(temp_str) % 8 != 0): temp_str = temp_str[0:-1 * nums * 2] for i in range(0, int(len(temp_str) / 8)): outputs = chr(int(temp_str[i * 8:(i 1) * 8], 2)) bin_str = bin_str[4:] print("Decrypted String:n%s " % outputs)if __name__ == '__main__': decode_str("QAoOQMPFks1BsB7cbM3TQsXg30i9g3==") |
---|
最后在公众号后台回复关键词 “ctf素材”,即可获得上述实验APK。