Geek-10h-安卓-wp

geek-10h-wp-apk

这是Syclover组办的成信校内第十届geek大赛的安卓部分wp,面向新生,主要是两个方向的题四道题,这里记录了自己的wp,前面加%%表示是相关的知识点,里面可能有的理解不正确,希望各位发现的及时评论指正。
链接:⬇⬇⬇⬇
链接:https://pan.baidu.com/s/1HvQh0tHPCJcsk_sd0GjmwQ
提取码:tq9o
复制这段内容后打开百度网盘手机App,操作更方便哦

其实安卓基础部分网上已经有很多,可以自行搜索,这里只是出现的在两类题目之间简单说明,
具体关于各层和使用工具的更深层次细节可以自行了解。

%%apk文件分析

最主要一点是安卓文件,即apk文件,内部存在两个资源区,smali储存为代码部分,res储存为数据,
我们的 Android Killer 可以分析整个apk,但是由于这个工具真的比较老了,一般会在分析反汇编卡死,我们这时候直接退出,然后重新打开ak,载入apk文件就好,然后它可以加载到res区u,但是无法加载smali区,我目前就是用它当作查看res层的工具,
smali层通过jdb或者gda来看,平时更喜欢gda的界面一些。然后一般可以看到对应文件目录下出现的MainActivity及后缀$的一系列都是apk的主要运行程序,我们只分析这些即可。

Sign_in

在这里插入图片描述
我们看到关键字符,找到了判定是否正确的位置:
在这里插入图片描述
然后发现这就是输入的字符串经过base64加密,然后和一串字符对比,这串字符在以0x7f0b0029表示,但我们是找不到的,不管此时使用的是jdb还是gda,因为这串字符 保存在res区,这两个工具只是分析smali区,
我们使用ak中的搜索,我们搜索“0x7f0b0029”然后定位到“sign_in”,再继续搜索就可以找到密文,
我们直接将flag前几位“Syc{”base64加密,取前几位,然后ak中搜索也可以。
然后直接在线工具或者python2中的字符串解密方法的以得到flag:

 #python2
 st1 = 'U3lje1NpOW5fMW5fSTNfRTRzeSF9'
 print(st1.decode("base64"))
 #python3
 import base64
 st1 = 'U3lje1NpOW5fMW5fSTNfRTRzeSF9'
 print(base64.b64decode(st1))
 #Syc{Si9n_1n_I3_E4sy!}

蒋学姐的秘密

将apk文件载入gda,翻一翻找到关键判定函数:
在这里插入图片描述
然后然后发现程序逻辑,p0判定,然后p1是flag,判定时括号内的部分是由p0再函数中生成的。我们先看p0的值,一般运用到的程序参量都会再buildconfig中找到:
在这里插入图片描述
然后我们在ak中搜索:
在这里插入图片描述
然后得到p0的值为:“Syclover"
然后我们分析生成的函数:
在这里插入图片描述这个程序首先将参数转化为对应十六进制的编码,然后使用md5加密,然后判定是否小于0和大于16,分别执行不同的操作,最后得到一个字符串返回。
我们先得到p0的对应编码;

p2 = 'Syclover'
 p3 = '' 
 for i in range(len(p2)):
     p3 += str(hex(ord(p2[i]))).replace('0x','')
 print (p3)
 #5379636c6f766572

然后我们使用在线工具进行md5加密,得到:73BC1836984E834458C8061527944AEE
然后套上flag格式即可。

%%so层-jni

后面两个安卓题会涉及到so层,我们需要了解一些关于jni的知识:

jni,Java Native Interface;
我们可以简单理解为是java和c/c++之间的转换器
apk的主函数部分是java编写,而可调用的so层是c/c++,两者之间的数据传递和处理函数调用等,我们就需要了解jni

另外一点,apk文件其实是一种压缩包的形式,我们可以使用普通的解压工具解压开,其内部我们可以看到res层,so层在对应的lib文件夹下,注意要打开arm系列的文件夹内的so文件,其中的arm后的后缀表示的是86位/64位,使用对应的ida打开分析即可。在这里插入图片描述
遇到这种代码,即为载入so层文件的意思,某些调用的函数就不会在smali区分析出来,而是需要ida查看so层对应文件。
在ida中查看的函数以Java_xxx开头,如下:
在这里插入图片描述
关于jni的逆向,下面是一些小注意点

  • 载入函数会有一个参数a1,那并不是参数,而是接口指针,
  • 在函数调用时使用的参数a2,是对某个对象的引用,或是对java类的引用,
  • 我们将a1设置为对应类型即可,将光标选中a1,单击Y,然后改变类型为:JNIEnv*
  • 在a3开始才是函数真正的参数。

另注函数整理
这里写出几个在以下题目出现的函数或调用的方法:

  • CallMethod:从本地方法中调用java实例,中间的类型标识返回值类型,参数a1还是接口指针,参数a2会确定该调用的处理方式,其后就是参数,
    • CallMethod (JNIEnv*en v, jobject obj , …)
  • GetStringUTFChars:返回指向字符串的utf-8字符数组指针。
    • GetStringUTFChars (JNIEnv*env, jstring string, jboolean *isCopy)
  • NewObject:构造新的java对象。
    • NewObject (JNIEnv *env , jclass clazz, jmethodID methodID, …

正在尝试重新连接

载入gda,翻看MainActivity及相关函数:
在这里插入图片描述
这里是判定对错的关键函数,我们看到是将一个获取到的数据和另一串数据一起传入check函数,这个函数返回1为正确。
获取到的数据,一般来说这应该就是我们输入的数据,即flag。
在这里插入图片描述
这里是在在未连接到网络时,会出现的话。
这里出现了一个网址,我们们进入后得到一个字典:

 {"key":"Syclover",
 "data":["33","28","21","20","93","13","13","96","0","11","66","27","51","42","10","12","1","85","41","38","34","23","60","33","23","86","2","94","2","90","4"]}

然后继续分析并没有其他很有意义的代码,我们去寻找那个判定对错的关键函数,然后发现:
在这里插入图片描述
但我们可以看到在此之前:
在这里插入图片描述
这是调用了一个so层,其名为native-lib,而我们的关键的这个函数就在这个so层文件内,要打开arm系列的文件然后v8a或v7a是代表64位/86位,我这里分析的是v7a,其实内部的函数都是一致的,我们使用ida打开。
首先定位到,这个函数应该要返回ture,我们找到应该要返回1,则看到了关键的判定位置。
在这里插入图片描述
我们点击某个参量高亮后查看之前的赋值,或是单击X查看交叉调用。可以确定判定的两个数组,其中一个为a3异或运算后生成的,一个为a4直接生成的数组,但这个位置有些奇怪:
在这里插入图片描述
两个变量分别得到了参数a4的两部分,或者说a4像一个列表或是字典一样a4[0],a4[1]分别转化为object类型赋值给两个变量,
其实到这里就差不多思路清晰了,a3是原本传入的第一个参数,即为我们的flag,第二个参数我们可以想到之前我们得到了一个字典,由两部分,所以是由flag异或后和字典内的列表 (即a4[1]) 一致。
我们在关注异或部分:
在这里插入图片描述
其中的v20即为flag,我们查看v19:
在这里插入图片描述
其实v19就和a4[0]一致,然后通过函数处理了,我们查看:
在这里插入图片描述
这个函数代表了一个倒序处理,将a4[0]进行倒序生成了一个新的数组。即,a4[0]倒序后的数组,和flag异或得到的值,和a4[1]的列表相等,我们可以写出脚本:

arr = [33,28,21,20,93,13,13,96,0,11,66,27,51,42,10,12,1,85,41,38,34,23,60,33,23,86,2,94,2,90,4]
 st1 = 'Syclover'[::-1]
 for i in range(31):
     print(chr(ord(st1[i%8]) ^ arr[i]),end='')
 #Syc{1nt3rn4t_Is_s0_INtEre3t1n9}

其实这个算法不难,我们只是需要一些关于jni的知识,
师傅的hint是关于动调so,动态应该是会更加深一些理解,但是过程配置有些复杂,我使用的静态分析的方式。

little case

载入gdb,先查找关键位置:
在这里插入图片描述
这个程序流程,
首先是获取输入的字符串,然后载入第一层判定,这里直接输出的是“Wrong",这里应该要返回turn,然后取反是fault,之后将字符串剪切只保留5–20位字符(因为前包后不包应是4–19,而且索引从0开始字符应该是5–20位),然后再载入另一个函数判定,返回值来决定是否打印出“you have sloved it !”,所以这个值应该要返回turn。
我们先分析第一个函数:
在这里插入图片描述
首先确定了flag长度为21,然后限制了flag格式为”Syc{“最后为”}“,
然后最为关键的位置是判定flag中间部分,要为数字或者在97–102之间,其实就是为数字或者a–f之间,
然后下一个判定,我们发现在smali区显示为null:
在这里插入图片描述
然后我们就考虑是在so层内:
在这里插入图片描述
而且我们回看这个函数的参数,是flag的5–20位,即为括号内的部分
在这里插入图片描述
首先前面这些都是预先设置,但是要注意func1和func3的调用位置,这两个调用分别代表了加法和乘法。
在这里插入图片描述
然后将flag的中间段分隔为4*4,我们为了记住他们的顺序简记为A1–A4,另外要注意一点,这里的更新,不是得到字符串对应的编码,而是直接转化为数,类似int(‘f’,16)这种操作,所以我们的flag也在限制要求在16进制数中,
在这里插入图片描述
然后我们就看到了这样一个解密过程,首先定义数字2的位置和我们定义A1–A4位置是一样的,所以那个参数就是2。
在这里插入图片描述
而且数组453后面对应的选值,453[0]=4,453[1]=5,453[2] = 3,
然后参数调用的时候参数:

  • a1:接口指针
  • a2:对类对象或者java库调用,
  • a3:设置函数的运算处理
  • a4,a5:参数,

想在后面注释的一样,得到后面的运算过程,然后进入下层的判定。
得到四个方程式,我们解出来就可以得到A1–A4,然后接起来就是flag了。有以下脚本:(z3解方程框架:

 from z3 import * 
 a1, a2, a3, a4 = Ints('a1, a2, a3, a4')
 solver = Solver()
 solver.add()if solver.check() == sat:
     print(solver.model())
 else:
     print('unsat')

写出脚本得到四个值:

 from z3 import * 
 a1, a2, a3, a4 = Ints('a1, a2, a3, a4')
 solver = Solver()
 ​
 solver.add(a1 + 2 * a2 * a3 - a4 == 0xFA52560D)
 solver.add(a1 * a3 + 4 * a2 - 5 * a4 == 0x134C22B5)
 solver.add(a1 * a2 + 4 * a4 - a3 == 0x117164E1)
 solver.add(a4 * a3 + 3 * a1 - 2 * a2  == 0x36AD4421)if solver.check() == sat:
     print(solver.model())
 else:
     print('unsat')

然后得到他们对应的十六进制,套上flag格式即可:

a = [6716,43570,48195,19035]
 for i in range(4):
     print(str(hex(a[i])).replace('0x',''),end='')
 #Syc{1a3caa32bc434a5b}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值