攻防世界 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一下
flag:s="X<cP[?PHNB<P?aj" flag="" for i in s: flag+=chr(ord(i)^15) print (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
题目思路:
- 题目提示了,就是分析so文件,丢进JEB提取一下丢到IDA里。
- 操作思路就是:对输入的字符串取前一半和后一半进行交换,然后再每两位位置互换,然后和明文比较。
所以exp:s = list('f72c5a36569418a20907b55be5bf95ad') for i in range(0, len(s), 2): s1 = s[i] s[i] = s[i+1] s[i+1] = s1 for i in range(len(s)//2): s2 = s[i] s[i] = s[i+16] s[i+16re学习笔记(91)攻防世界 - mobile进阶区 - Android2.0