攻防世界 mobile 安卓移动端简单题练习区 答题(1-12题解)

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了攻防世界 mobile 安卓移动端简单题练习区 答题(1-12题解)相关的知识,希望对你有一定的参考价值。


1、easyjni

题目描述:无
题目思路:

  • 获得一个apk,网易mumu模拟器 安装打开。
  • 丢到JEB里面,先看一下配置文件
    找标签name属性,看看主函数。

    进主函数,解析一下看源代码。找到点击的oncreate方法。看到它调用了 MainActivity.a 方法,我们继续跟进去看看这个 a 方法干了什么。

    可以看到它有调用了另外一个 a 方法,去分析它。在第二个 a 方法中可以看到有一个关键语句。
    this.ncheck(new a().a(arg3.getBytes()));
    先实例化了一个 a 类的对象,然后调用了 ncheck ,这个ncheck 是我们的 libnative.so 文件里的一个函数。

    我们先来看这个 a 类的java代码,先对它分析一下,我们再去看 so 文件。

    这里我们知道了 是我们输入的字符 经过变形的base64加密码表为 然后传进了 so 里面的方法 然后我们去so里面找函数。
  • 用 ida 打开 so 文件,找到 ncheck 函数。
    发现将传进来的字符串首先将前 16 个字符和后 16 个字符交换位置,然后将这个新字符串两两一组互相交换位置,最后和一个字符串比较,相等则返回TRUE

  • 这里返回值是与一个字符串比较的结果,那我们就将该字符串逆推回去,写出逆向脚本。
    import base64
    import string
    
    # 注意py3将string.maketrans修改为 str.maketrans
    string1 = str.maketrans("i5jLW7S0GX6uf1cv3ny4q8es2Q+bdkYgKOIT/tAxUrFlVPzhmow9BHCMDpEaJRZN","ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
    strEnBase64 = "QAoOQMPFks1BsB7cbM3TQsXg30i9g3==".translate(string1)
    print(strEnBase64)
    
    strFlag = base64.b64decode(strEnBase64)
    print(strFlag)
    
  • 所以最后的flag。
    flagjust_ANot#er_@p3
    

2、Ph0en1x-100

题目描述:无
题目思路:

  • 安装是一样的输入flag界面
  • 附,安卓分层

    android的分4层,java应用程序,java框架,本地框架Native和java运行环境,Linux内核空间


    Java应用程序无需过多解释,基本可以理解为各个App,由Java语言实现。
    Java框架层(系统服务)就是常说的Framework,这层里东西很多也很复杂,比如说主要的一些系统服务如ActivityManagerService、PackageManagerService等,我们编写的Android代码之所以能够正常识别和动作 ,都要依赖这一层的支持。这一层也是由Java语言实现。
    Native层(本地服务) 这部分常见一些本地服务和一些链接库等。这一层的一个特点就是通过C和C++语言实现 。比如我们现在要执行一个复杂运算,如果通过java代码去实现,那么效率会非常低,此时可以选择通过C或C++代码去实现,然后和我们上层的Java代码通信(这部分在android中称为jni机制)。又比如我们的设备需要运行,那么必然要和底层的硬件驱动交互,也要通过Native层。
    Linux内核空间就不过多解释了

  • 丢到JEB里面。
    很清晰就能看见是两个结果字符串做比对。先看看getFlag和encrypt,因为这两个函数都在native层
  • 用IDA加载so文件分析。
    encrypt函数,对字符串的每个字符的ascii值减一。

    getFlag,没有外部输入参数,就是如果我们能拿到这个方法的运行结果就OK了
  • 所以思路:
    通过getFlag方法得到的字符串然后每个字符的ascii值+1,程序就可以运行成功。
    关键就是getFlag方法的执行结果。
    有两个思路:
    1.动态调试。
    2.修改smali源码,让app显示getFlag方法的执行结果。
  • 通过APKIDE修改smali源码,本来显示 Failed的地方,让其执行getFlag方法 ,将执行结果存入v1寄存器。

    得到getFlag返回值: ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|
    最后每个字符的ascii值+1,即可得到flag:
    flag = "ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|"
    result = ''
    for i in flag:
        result += chr(ord(i) + 1)
    print(result)
    
  • 最后flag。
    flagAr3_y0u_go1nG_70_scarborough_Fair
    

3、RememberOther

题目描述:无
题目思路:

  • 下载后解压可以得到一个apk文件和word文件。查看word文件,里面写了比较简单的一句话,未获有有效信息。
  • 运行apk文件,是一个登录界面。
  • 丢到JEB反编译
    checkSN返回为true,就注册成功了。

    进checkSN。修改smali源码(java编译为安卓并不是class,而是.dex格式,汇编后源代码的语言为smali),让什么都不输的情况下能返回true,然后进程序,输出一串MD5。


    MD5如下:md5:b3241668ecbeb19921fdac5ac1aafa69
    (或者资源字符串里,也可以找得到)


    md5解密是YOU_KNOW_,加上word文档的提示后面加个ANDROID。
  • 所以最后的flag是:
    YOU_KNOW_ANDROID
    

4、app1

题目描述:无
题目思路:

  • apk丢进JEB反编译一下。
    如果输入字符串==内置str异或i,那么输出flag。

    找一下内置字符串。
  • 所以py一下
    s="X<cP[?PHNB<P?aj"
    flag=""
    for i in s:
        flag+=chr(ord(i)^15)
        print (flag)
     
    
    flag:
    W3l_T0_GAM3_0ne
    

5、app2

题目思路:

  • 在JEB中打开

    进入SecondActivity类中,寻找关键方法onCreate()
  • 使用IDA打开Lib库中的.so文件,找到关键函数doRawData的实现

    大概的意思就是使用AES对咱们的字符串进行加密,使用的密钥是thisisatestkey==,恰好使得结果等于VEIzd/V2UPYNdn/bxH3Xig==就可以了。
    输入aimage/tencent结果发现还是腾讯等你哟 Waiting for you,被戏耍了,并不是正确的flag
  • 回到JEB,发现第一个FileDataActivity类,里面也有一个奇怪的加密字符串
    将上面的加密结果改成9YuQ2dk8CSaCe7DTAmaqAA== ,再次运行,成功拿到flag


    flag:
    Cas3_0f_A_CAK3
    

6、app3

题目思路:

  • 拿到的文件是app3.ab,是android的备份数据格式。ab文件一般分两种
    未加密,文件前面有24字节文件头,文件头包含none标志,文件头之后就是数据
    加密,文件头包含AES-256标志
    用010editor打开app3.ab,可以看出文件是没有加密的。
  • 然后直接使用Android backup extractor进行解析,转换为tar压缩包,Android backup extractor是一个开源项目,github上可以下载到。
    对于未加密的ab文件,命令格式为:java -jar abe.jar unpack backup.ab backup.tar。
  • 解压后获得apk文件,丢进jeb,看主程序。
    大致就是加载了SQLiteData,调用了一个a(),然后对字符串Stranger和Interger.valueof(0x1E240)做了给v1.a()操作。
  • 查看SQLHelper的代码,发现创建了一个包含F_l_a_g字段的表,意味着flag应该在.db文件,猜测大概率在Encryto.db中。
    继续查看getWritableDatebase()函数可知。如果数据库不存在,就用传进来的参数,作为密码创建一个。在IntelliJ IDEA中复现代码,计算得密码为ae56f99
    搞懂了之后,尝试打开Encrypto.db和demo.db,这里我用的是SQLiteStudio,使用对应密码打开Encyto.db后得到flag字段,使用base64解码,得到flag。
    sqlite> PRAGMA key = "ae56f99";
    sqlite> ATTACH DATABASE "ailx10.db" AS plaintext KEY "";
    sqlite> SELECT sqlcipher_export("plaintext");
    sqlite> DETACH DATABASE plaintext;
    
  • 最后flag
    TctfH3ll0_Do_Y0u_Lov3_Tenc3nt!
    

7、easy-apk

题目思路:

  • 开局丢进JEB,很明显的一个字符串比较操作。

    将输入的字符串进行base64 加密之后与“5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=” 进行比较!
    所以我们需要对这个字符串进行base64解密,但是我们去解密会发现错误:于是我们看一下前面的 base64new,加了一个 new 一定有变化。

    代码表不同,所以需要使用特定的 base64 解密脚本。
    # coding:utf-8
     
    #s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    s = "vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/"
     
    def My_base64_encode(inputs):
    	# 将字符串转化为2进制
    	bin_str = []
    	for i in inputs:
    		x = str(bin(ord(i))).replace('0b', '')
    		bin_str.append(':0>8'.format(x))
    	#print(bin_str)
    	# 输出的字符串
    	outputs = ""
    	# 不够三倍数,需补齐的次数
    	nums = 0
    	while bin_str:
    		#每次取三个字符的二进制
    		temp_list = bin_str[:3]
    		if(len(temp_list) != 3):
    			nums = 3 - len(temp_list)
    			while len(temp_list) < 3:
    				temp_list += ['0' * 8]
    		temp_str = "".join(temp_list)
    		#print(temp_str)
    		# 将三个8字节的二进制转换为4个十进制
    		temp_str_list = []
    		for i in range(0,4):
    			temp_str_list.append(int(temp_str[i*6:(i+1)*6],2))
    		#print(temp_str_list)
    		if nums:
    			temp_str_list = temp_str_list[0:4 - nums]
    			
    		for i in temp_str_list:
    			outputs += s[i]
    		bin_str = bin_str[3:]
    	outputs += nums * '='
    	print("Encrypted String:\\n%s "%outputs)
    	
    def My_base64_decode(inputs):
    	# 将字符串转化为2进制
    	bin_str = []
    	for i in inputs:
    		if i != '=':
    			x = str(bin(s.index(i))).replace('0b', '')
    			bin_str.append(':0>6'.format(x))
    	#print(bin_str)
    	# 输出的字符串
    	outputs = ""
    	nums = inputs.count('=')
    	while bin_str:
    		temp_list = bin_str[:4]
    		temp_str = "".join(temp_list)
    		#print(temp_str)
    		# 补足8位字节
    		if(len(temp_str) % 8 != 0):
    			temp_str = temp_str[0:-1 * nums * 2]
    		# 将四个6字节的二进制转换为三个字符
    		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)
    	
    print()
    print("     *************************************")
    print("     *    (1)encode         (2)decode    *")	
    print("     *************************************")
    print()
     
     
    num = input("Please select the operation you want to perform:\\n")
    if(num == "1"):
    	input_str = input("Please enter a string that needs to be encrypted: \\n")
    	My_base64_encode(input_str)
    else:
    	input_str = input("Please enter a string that needs to be decrypted: \\n")
    	My_base64_decode(input_str)
    
  • 所以最后的flag,外面套个壳
    flag05397c42f9b6da593a3644162d36eb01
    

8、easyjava

题目描述:无
题目思路:

  • 拿到apk,丢进jeb反编译。

    主类MainActivity,发现super调用了自身onCreate方法,点击事件MainActivity.a(((EditText)((MainActivity)jdField_this).findViewById(2131427445)).getText().toString()).booleanValue()为真时成功。
    MainActivity调用了方法a,加密函数有两层最后调用,a b 加密逻辑大致一样,处理对象都是单字符,进行混淆后,对密钥进行更新。
    b 加密函数主要如下:(忽略处理空格和字符不存在情况)加密操作为:检索字符在keylisit的下标,记为index,然后检索index在a_ArrayList的下标,记为v1,检索成功就调用Mb.a()生成新的密钥keylist和a_ArrayList,然后返回v1。
    a_ArrayList不是一个静态变量,是经由public Mb(Integer arg9)生成的,一开始以为动态生成的,然后动态调试了一下,发现是第一次调用时生成好了,后续字符加密沿用上一字符密钥。生成的偏移为 2 。
    Mb.a()处理逻辑就是:每一轮加密完成后,将两个密钥的首元素放置到最后一位。
    a.a(b.a(string)) b 函数加密完成后,返回值作为参数传入 a ,加密方式与 b 相近,较大不同点是每轮加密不会随机化密钥,密钥初始偏移为 3 。因为从最后判断函数可知 flag 中间字符为 12 位,不足以触发 a 随机化密钥函数要求。

  • 最后写exp解密

    from collections import deque # 双端队列简化随机密钥实现
    alpha = deque("abcdefghijklmnopqrstuvwxyz") 
    #key_b = deque([8, 25, 17, 23, 7, 22, 1, 16, 6, 9, 21, 0, 15, 5, 10, 18, 2, 24, 4, 11, 3, 14, 19, 12, 20, 13]) 
    #key_a = deque([7, 14, 16, 21, 4, 24, 25, 20, 5, 15, 9, 17, 6, 13, 3, 18, 12, 10, 19, 0, 22, 2, 11, 23, 1, 8])
    key_b = deque([17, 23, 7, 22, 1, 16, 6, 9, 21, 0, 15, 5, 10, 18, 2, 24, 4, 11, 3, 14, 19, 12, 20, 13, 8, 25])
    key_a = deque([21, 4, 24, 25, 20, 5, 15, 9, 17, 6, 13, 3, 18, 12, 10, 19, 0, 22, 2, 11, 23, 1, 8, 7, 14, 16])
    c = 'wigwrkaugala'
    
    def decode(s): 
        i = key_a[(ord(s) - ord('a'))]
        i = key_b[(i)] 
        print(alpha[i], end='')
        key_b.append(key_b.popleft()) 
        alpha.append(alpha.popleft())
    
    print("flag",end='')
    for s in c: decode(s)
    print("")
    
  • 最后flag:

    flagvenividivkcr
    

9、easy-dex

题目描述:无
题目思路:

  • 拿到apk,先丢进JEB。
    看一下入口,native层,直接去so分析

  • 扔进IDA看源码

    要求10秒内晃动100次,并逐渐解密dex文件,解密算法使用异或操作。加密的dex文件存放位置:
    qmemcpy(v3, &unk_7004, (size_t)v2);//v2为3CA10
    地址:0x7004 大小:3CA10
    加密算法是将这部分内容分为10分,并且分别和9、19、29、39、49、59、69、79、89
    进行异或。
    IDA pro中直接运行python脚本得到dex文件,得dump地址文件

    import idaapi
    addr = 0x7004
    size = 0x3CA10
    
    with open('C:/Users/gwj11/Desktop/dump', 'wb') as f:
        f.write(idaapi.get_bytes(addr,size))
    
    print('dump finished')
    

    再用py对dump解密:
    得到easy-dex.dex

    import zlib
    
    with open('dump', 'rb') as f:
        data1 = f.read()
        data = list(data1)
        v14 = 0
        while True:
            if (v14 - 1) <= 0x58:
                v15 = v14 // 10
                if v14 % 10 == 9:
                    v16 = 0x3CA10
                    v17 = 0x3CA10 // 10
                    v18 = (v15 + 1) * (0x3CA10 // 10)
                    if 0x3CA10 // 10 * v15 < v18:
                        v19 = v17 * v15
                        v17 -= 1
                        data[v19] ^= v14
                        v19 += 1
                        while v17:
                            v17 -= 1
                            data[v19] ^= v14
                            v19 += 1
                    if v14 == 89:
                        while v18 < v16:
                            data[v18] ^= 0x59
                            v18 += 1
            else:
                break
            v14 += 1
    
    filebytes = bytes(data)
    with open('easy-dex.dex', 'wb') as w:
        w.write(zlib.decompress(filebytes))
    print('decrypt finished')
    

    jadx打开easy-dex.dex

    在资源文件中找到:twofish
    python还原加密字符串

    import base64
    m = [-120, 77, -14, -38, 17, 5, -42, 44, -32, 109, 85, 31, 24, -91, -112, -83,
    64, -83, -128, 84, 5, -94, -98, -30, 18, 70, -26, 71, 5, -99, -62, -58, 117, 29,
    -44, 6, 112, -4, 81, 84, 9, 22, -51, 95, -34, 12, 47, 77]
    data = []
    for i in m:
        data.append(i&0xFF)
    print(base64.b64encode(bytes(data)))
    

    得到加密字符串:iE3y2hEF1izgbVUfGKWQrUCtgFQFop7iEkbmRwWdwsZ1HdQGcPxRVAkWzV/eDC9N

    加密密钥为:I have a male fish and a female fish.
    在线解密:http://tool.chacuo.net/crypttwofish

  • 最后flag:

    qwbTH3y_Io<e_EACh_OTh3r_FOrEUER
    

10、easy-so

题目描述:flag格式为flag
题目思路: