XCTF mobile新手区解题记录以及一些总结和思考

前言

    疫情当下,都已经耍了好几个月了,都不知道干啥了,好不容易等到我四川宣布开学时间了,结果四川高校一点动静都没有,无聊中,那就写一下解题记录吧,有些题先前已经在我个人博客上发过了,就不再重复写了,贴个链接就行了!!!


题目:app3

    此题wp已经在个人博客写过,不在详细说明,详情请看链接:https://www.52pojie.cn/thread-1082706-1-1.html


题目:easy-apk

    此题wp已经在个人博客写过,不在详细说明,详情请看链接:https://www.cnblogs.com/aWxvdmVseXc0/p/11955006.html


题目:easy-java

    此题wp已经在个人博客写过,不在详细说明,详情请看链接:https://www.cnblogs.com/aWxvdmVseXc0/p/12207697.html


题目:easy-jni

    此题wp已经在个人博客写过,不在详细说明,详情请看链接:https://www.cnblogs.com/aWxvdmVseXc0/p/12198459.html


题目:easy-so

    1、下载好题目,拖入夜神中,打开如下所示:

XCTF mobile新手区解题记录以及一些总结和思考

    2、查壳,发现无壳,用jeb反编译后,找到关键字验证失败所在类,发现调用了so层的CheckString函数进行了验证,传进去的参数为我们在输入框中输入的字符串,如下图所示:

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    3、用IDA打开so文件(要提取x86文件夹下面那个so文件,两个arm文件夹下面的so文件用ida打开有问题),找到该静态函数,直接F5大法,静态分析该函数可知:首先将传入的字符串前16位与后16位互换,然后两两一组互换位置,最后将得到的字符串与字符串f72c5a36569418a20907b55be5bf95ad比较返回比较结果!!!

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    4、写了一个python小脚本跑出flag,如下所示:

脚本代码:

string = 'f72c5a36569418a20907b55be5bf95ad'
strlist = list(string)

k = 0

for i in range(16):
    ch = strlist[1 + k]
    strlist[1 + k] = strlist[0 + k]
    strlist[0 + k] = ch
    k = k + 2

k = 0

for i in range(16):
    ch = strlist[0 + k]
    strlist[0 + k] = strlist[16 + k]
    strlist[16 + k] = ch
    k = k + 1

print(''.join(strlist))

运行截图:

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考


题目:app1

    此题wp已经在个人博客写过,不在详细说明,详情请看链接:https://www.cnblogs.com/aWxvdmVseXc0/p/11902184.html


题目:Ph0en1x-100

    1、拖进夜神中安装运行,主界面只有一个输入框和一个按钮,随便输入信息,点击按钮后,弹出信息Failed!,如下图所示:

XCTF mobile新手区解题记录以及一些总结和思考

    2、查壳后无壳直接使用JEB反编译,查看MainActivity.java文件,发现要弹出信息Success逻辑如下:首先在so层注册了两个静态函数--encrypt(String)getFlag()函数,然后在java层有个函数getSecret(String),将so层函数getFlag返回值经过getSecret函数加密后与我们在输入框中输入的字符串经过encrypt函数后在经过getSecret函数加密比较,如果一致,则返回Success,由于比较的两个字符串最外层都经过getSecret函数加密,所有我们不需要在管getSecret函数,直接让内部两个字符串一直一致即可得到flag!!!

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    3、使用IDA打开so文件,静态分析一下encrypt函数,发现逻辑很简单,就是将传进来的字符串的每个字符的ASCII码减一;对于getFlag函数,由于该函数没有输入只有输出,直接用frida Hook该函数得到返回值即可,如下图所示:

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

Frida代码:

import frida
import sys

jscode = """
Java.perform(function(){
    Interceptor.attach(Module.findExportByName("libphcm.so","Java_com_ph0en1x_android_1crackme_MainActivity_getFlag"),{
        onEnter: function(args) {
        },
        onLeave: function(retval){
            var String_java = Java.use('java.lang.String');
            var args_4 = Java.cast(retval, String_java);
            send("getFlag()==>"+args_4);
        }
    });
});
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print('[*] {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach('com.ph0en1x.android_crackme')
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()

    4、得到以上信息后,使用python脚本跑出falg即可,如下所示:

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

python脚本:

Flag = 'ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|'

flaglist = list(Flag)

stringlist = []

for ch in flaglist:
    stringlist.append(chr(ord(ch) + 1))
    
print(''.join(stringlist))

题目:RememberOther

    PS:此题为脑洞题,披了一个安卓的皮而已!!!

    1、下载好题目后,查壳发现无壳,直接拖进夜神中,要求输入用户名和注册码,随便输入,弹出信息无效用户名或注册码,如下图所示:

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    2、用JEB反编译后,查看onCreate方法,发现将我们输入的用户名和注册码作为参数调用checkSN函数,并且当该函数返回false时不弹出信息无效用户名或注册码,接着去看checkSN函数,该函数当用户名和注册码为空时返回false,返回false后弹出了一串md5值,再看checkSN函数其他逻辑,发现将输入的用户名经过md5加密后返回16进制字符串,然后取该字符串的奇数位拼接成一个新的字符串,再然后与我们输入的注册码进行比较,返回true,最后也没有发现这个跟flag没什么关系,想起之前还有一串md5值,进行解密,解密得出YOU_KNOW_,输入,提示flag错误。。。看了一下其他大佬的wp,才发现在后面加上ANDROID就行了。。。。。因为压缩包里面有个word文件,里面写了不懂安卓。。。。。。。

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考


题目:app2

    1、将下载好的题目拖进夜神中,发现要求登陆,随便点击登陆后,如下所示:

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    2、将apk用jeb反编译后,首先看一下MainActivity这个入口文件,发现没什么,就是将我们输入的用户名和密码传入SecondActivity页面中,然后跳转到该页面,再来看一下SecondActivity,发现调用了so层函数doRawData,使其返回值和字符串VEIzd/V2UPYNdn/bxH3Xig==进行比较,如下所示:

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    3、用IDA查看一下该函数,发现至少将传入的字符串进行了AES-128-ECB加密,并且发现密钥thisisatestkey==,将开始我们发现的字符串VEIzd/V2UPYNdn/bxH3Xig==进行解密,得到的字符串输入后发现不是flag,但是在FileDataActivity文件中发现另一串字符串9YuQ2dk8CSaCe7DTAmaqAA==,对其解密,得到flag:Cas3_0f_A_CAK3

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考


题目:黑客精神

    1、将下载好的题目拖进夜神模拟器中,发现要求注册,随便点击注册后,弹出一个弹框,提示已注册

XCTF mobile新手区解题记录以及一些总结和思考

    2、用JEB反编译后,跟进MainActivity文件,发现点击按钮后就一个弹出弹框,点击弹框确定后,跳转到RegActivity界面去,在该界面点击注册后,调用了so层函数saveSN

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    3、用IDA分析so文件发现在java层注册的native函数都是动态注册的,此时用Ctrl+S找到.data段进入,发现对应的函数n1n2n3

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    4、先分析一些n1函数,F5大法后,可以很轻易看出该函数作用是创建了一个文件/sdcard/reg.dat,然后读取该文件,与字符串EoPAoY62@ElRD进行比较;再来看一下n2函数,该函数对应这java层的saveSN函数,首先前面做了一大堆令人看不懂的操作,然后将我们输入的注册码的每个字符与另一个字符进行异或操作(该字符我们不知道,所以无法得到最终异或结果);再来看一下n3函数,该函数首先调用了n1函数,若结果为真,则将一大串字符串赋值给v4,双击去看一些该字符串,使用A健将其转为ascii后,发现提示输入即是flag,格式为xman{……}!

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    5、按照提示,我们将开始发现的字符串EoPAoY62@ElRD(只发现这一个字符串,不输入这个输入啥)作为字符串进行输入,然后将文件/sdcard/reg.dat拷贝出来,打开文件一看,发现flag!!!

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考


题目:easy-dex

    1、下载好题目后,拖进夜神中,发现直接是黑屏的,在JEB中反编译后,发现在AndroidManifest.xml文件applicationactivity标签中存在android:hasCode="false"android:name="android.app.NativeActivity",说明这是个纯C++编写的,并且不含java代码,也就是Native Activity

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

    2、既然是Native Activity,直接找到so文件,拖进IDA中,寻找android_main方法(这是Native Activity的入口方法,关于Native Activity的一些基础知识,我会在将一些我觉得写得比较好的博客链接附在文末),发现存在一个'write'方法,结合包名'findmydex',大胆推测一波此处就是将dex写入某个文件中,那么直接开启动态调试,在关键地方下好断点后,终于运行到了write函数处,结合传进去的参数,直接dump下来整个内存,用010打开一看,全都是'00000.....',。。。。。卒!!!!。。。。。。。

XCTF mobile新手区解题记录以及一些总结和思考

    3、好吧,开玩笑的!!!动态调试dump下来的内存有问题,一看就不是dex文件,开始还以为调试的时候哪里出来问题,导致dump出来的有问题,然后接着动态了一整个下午,发现好像dump下来的加密后的dex,去看了一下大佬们的wp,发现都是dump下加密后的dex,然后直接解密,好吧,再来看android_main函数,发现一开始就通过_aeabi_memcpy函数将加密后的dex文件加载进来了,我们可以轻松看到加密后的dex文件首地址为0x7004(ida使用F5后,要使用那一块内存空间地址直接是以&unk_地址命名的,所以首地址可以轻松看出来是0x7004),大小为0x3ca10,那么直接在静态下执行dump脚本即可,至于解密,把想应的c语言转换为python即可,至于最后为啥要进行一次zip的解压操作,是因为在android_main函数中解密完成后调用了uncompress函数进行了解压缩(更偷懒的可以直接把F5后的c代码复制下来,替换一下就行了)。

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

 IDA dump脚本:

import idaapi

addr = 0x7004
size = 0x3ca10

with open('dump','wb') as f:
    f.write(get_bytes(addr,size))
    
print('[+] dump end')

 python解密dex脚本(我电脑上运行环境为3.6):

import zlib

with open('dump','rb') as f:
    data1 = f.read()
    
    data = list(data1)
    
    count = 0
    
    while True:
        if count <= 0x59:
            count_tmp = (int)(count / 10)
            if count % 10 == 9:
                size = 0x3ca10
                size_tmp = (int)(size / 10)
                xor = (count_tmp + 1) * size_tmp
                if (size_tmp * count_tmp) < xor:
                    index = size_tmp * count_tmp
                    while size_tmp:
                        data[index] = data[index] ^ count
                        index = index + 1
                        size_tmp = size_tmp - 1
                if count == 89:
                    while xor < size:
                        data[xor] = data[xor] ^ 0x59
                        xor = xor + 1
        else:
            break
        count = count + 1

filebytes = bytes(data)
with open('easy-dex.dex','wb') as f1:
    f1.write(zlib.decompress(filebytes))
print('[+] decrypt end')

    4、将解密后的dex文件拖进jeb中反编译,首先看一下MainActivity.java文件的onCreate函数,发现有一个按钮监听事件,触发后调用了a.java里面的onClick函数,那么去看一下onclick函数,发现调用了MainActivity里面的a函数,并且传入了两个字符串参数,第一个字符串是输入框中的值,第二个参数是string.xml资源文件中的字符串,然后与一个字节数组进行比较,结合题目反编译后的public.xmlstring.xml文件,该字符串为I have a male fish and a female fish.,并且资源ID为two_fish,好吧,都已经是明示了,这里是TwoFish加密(关于TwoFish加密我会将一下我觉得写得还行的博客链接贴在最后面),并且该字符串就是密钥,与之比较字节数组就是加密后的结果。那么首先将字节数组转为字符串再说吧,一看字符串里面还有负数,那就直接与一下0xff咯(关于为啥要与0xff,是因为数字在java中是以补码形式表示的,与0xff相当于将一个有符号数转为了无符号数,看来我确实没有写博客的天赋,感觉说得不明不白的,老规矩,就把我觉得写得可以的博客链接贴在文末),然后与完后,直接转为ascii拼接成字符串,发现有些根本无法显示出具体字符来。。。。。又卡了,突然发现里面有个/符号,一下就想到了base64,尝试一下base4解码,结果解出来又是啥都不是。。。。。好气哦这个题。。。。。那就在来一个base64加密吧,得到字符串iE3y2hEF1izgbVUfGKWQrUCtgFQFop7iEkbmRwWdwsZ1HdQGcPxRVAkWzV/eDC9N(好吧,我承认我有赌的成分。。。。),随便找了个在线解密的网站,解密即得到flag。

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

XCTF mobile新手区解题记录以及一些总结和思考

 python获取twofish加密结果脚本(运行环境同上):

import base64

i = [-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 k in i:
    data.append(k&0xff)
print(base64.b64encode(bytes(data)))

题目:我是谁

    55555555555,我太菜了,这道题还没有做出来,感觉对不起党,对不起人民。。。。。。。。。。。。。。。。。等做出来在补上!!!


一些总结

    1、以往反编译后的第一步是看MainActivity文件,很少看AnroidMainifes.xml,结果在很多地方吃了大亏,比如easy-dex这个题,android_main这个函数还是撞进去的,后来看见那两个标签觉得没见过,才去百度了一下,才知道是Native Activity,感觉这个文件看似不起眼,结果能少走一些不必要的地方!!!

    2、思想上的牛角尖比技术上的牛角尖更难受,不然也不会去傻乎乎动态调试了一下午了!!!


一些博客链接

 关于Native Activity的:
  https://blog.csdn.net/qq_19683651/article/details/82623717
  https://blog.csdn.net/qq_21071977/article/details/77878252

 关于TwoFish加密的:
  https://blog.csdn.net/l540538550/article/details/5642435

 关于与0xff的:
  https://blog.csdn.net/csdn_ds/article/details/79106006?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

上一篇:前端固定table表头方法之纯css实现


下一篇:int型参数的SQL注入