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、下載好題目,拖入夜神中,打開如下所示:

2.png

    2、查殼,發現無殼,用jeb反編譯后,找到關鍵字驗證失敗所在類,發現調用了so層的CheckString函數進行了驗證,傳進去的參數為我們在輸入框中輸入的字符串,如下圖所示:

1.png

3.png

4.png

    3、用IDA打開so文件(要提取x86文件夾下面那個so文件,兩個arm文件夾下面的so文件用ida打開有問題),找到該靜態函數,直接F5大法,靜態分析該函數可知:首先將傳入的字符串前16位與后16位互換,然后兩兩一組互換位置,最后將得到的字符串與字符串f72c5a36569418a20907b55be5bf95ad比較返回比較結果!!!

5.png

6.png

    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))

運行截圖:

8.png

7.png


題目:app1

    此題wp已經在個人博客寫過,不在詳細說明,詳情請看鏈接:https://www.cnblogs.com/aWxvdmVseXc0/p/11902184.html


題目:Ph0en1x-100

    1、拖進夜神中安裝運行,主界面只有一個輸入框和一個按鈕,隨便輸入信息,點擊按鈕后,彈出信息Failed!,如下圖所示:

1.png

    2、查殼后無殼直接使用JEB反編譯,查看MainActivity.java文件,發現要彈出信息Success邏輯如下:首先在so層注冊了兩個靜態函數--encrypt(String)getFlag()函數,然后在java層有個函數getSecret(String),將so層函數getFlag返回值經過getSecret函數加密后與我們在輸入框中輸入的字符串經過encrypt函數后在經過getSecret函數加密比較,如果一致,則返回Success,由於比較的兩個字符串最外層都經過getSecret函數加密,所有我們不需要在管getSecret函數,直接讓內部兩個字符串一直一致即可得到flag!!!

6.png

7.png

    3、使用IDA打開so文件,靜態分析一下encrypt函數,發現邏輯很簡單,就是將傳進來的字符串的每個字符的ASCII碼減一;對於getFlag函數,由於該函數沒有輸入只有輸出,直接用frida Hook該函數得到返回值即可,如下圖所示:

2.png

3.png

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即可,如下所示:

4.png

5.png

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、下載好題目后,查殼發現無殼,直接拖進夜神中,要求輸入用戶名和注冊碼,隨便輸入,彈出信息無效用戶名或注冊碼,如下圖所示:

1.PNG

2.PNG

    2、用JEB反編譯后,查看onCreate方法,發現將我們輸入的用戶名和注冊碼作為參數調用checkSN函數,並且當該函數返回false時不彈出信息無效用戶名或注冊碼,接着去看checkSN函數,該函數當用戶名和注冊碼為空時返回false,返回false后彈出了一串md5值,再看checkSN函數其他邏輯,發現將輸入的用戶名經過md5加密后返回16進制字符串,然后取該字符串的奇數位拼接成一個新的字符串,再然后與我們輸入的注冊碼進行比較,返回true,最后也沒有發現這個跟flag沒什么關系,想起之前還有一串md5值,進行解密,解密得出YOU_KNOW_,輸入,提示flag錯誤。。。看了一下其他大佬的wp,才發現在后面加上ANDROID就行了。。。。。因為壓縮包里面有個word文件,里面寫了不懂安卓。。。。。。。

5.PNG

6.PNG

7.PNG

8.PNG

4.PNG

3.PNG


題目:app2

    1、將下載好的題目拖進夜神中,發現要求登陸,隨便點擊登陸后,如下所示:

1.png

2.png

    2、將apk用jeb反編譯后,首先看一下MainActivity這個入口文件,發現沒什么,就是將我們輸入的用戶名和密碼傳入SecondActivity頁面中,然后跳轉到該頁面,再來看一下SecondActivity,發現調用了so層函數doRawData,使其返回值和字符串VEIzd/V2UPYNdn/bxH3Xig==進行比較,如下所示:

3.png

5.png

    3、用IDA查看一下該函數,發現至少將傳入的字符串進行了AES-128-ECB加密,並且發現密鑰thisisatestkey==,將開始我們發現的字符串VEIzd/V2UPYNdn/bxH3Xig==進行解密,得到的字符串輸入后發現不是flag,但是在FileDataActivity文件中發現另一串字符串9YuQ2dk8CSaCe7DTAmaqAA==,對其解密,得到flag:Cas3_0f_A_CAK3

4.png

6.png

7.png


題目:黑客精神

    1、將下載好的題目拖進夜神模擬器中,發現要求注冊,隨便點擊注冊后,彈出一個彈框,提示已注冊

1.png

    2、用JEB反編譯后,跟進MainActivity文件,發現點擊按鈕后就一個彈出彈框,點擊彈框確定后,跳轉到RegActivity界面去,在該界面點擊注冊后,調用了so層函數saveSN

2.png

9.png

    3、用IDA分析so文件發現在java層注冊的native函數都是動態注冊的,此時用Ctrl+S找到.data段進入,發現對應的函數n1n2n3

3.png

4.png

    4、先分析一些n1函數,F5大法后,可以很輕易看出該函數作用是創建了一個文件/sdcard/reg.dat,然后讀取該文件,與字符串EoPAoY62@ElRD進行比較;再來看一下n2函數,該函數對應這java層的saveSN函數,首先前面做了一大堆令人看不懂的操作,然后將我們輸入的注冊碼的每個字符與另一個字符進行異或操作(該字符我們不知道,所以無法得到最終異或結果);再來看一下n3函數,該函數首先調用了n1函數,若結果為真,則將一大串字符串賦值給v4,雙擊去看一些該字符串,使用A健將其轉為ascii后,發現提示輸入即是flag,格式為xman{……}!

5.png

6.png

7.png

8.png

    5、按照提示,我們將開始發現的字符串EoPAoY62@ElRD(只發現這一個字符串,不輸入這個輸入啥)作為字符串進行輸入,然后將文件/sdcard/reg.dat拷貝出來,打開文件一看,發現flag!!!

10.png

11.png


題目:easy-dex

    1、下載好題目后,拖進夜神中,發現直接是黑屏的,在JEB中反編譯后,發現在AndroidManifest.xml文件applicationactivity標簽中存在android:hasCode="false"android:name="android.app.NativeActivity",說明這是個純C++編寫的,並且不含java代碼,也就是Native Activity

13.png

2.png

    2、既然是Native Activity,直接找到so文件,拖進IDA中,尋找android_main方法(這是Native Activity的入口方法,關於Native Activity的一些基礎知識,我會在將一些我覺得寫得比較好的博客鏈接附在文末),發現存在一個'write'方法,結合包名'findmydex',大膽推測一波此處就是將dex寫入某個文件中,那么直接開啟動態調試,在關鍵地方下好斷點后,終於運行到了write函數處,結合傳進去的參數,直接dump下來整個內存,用010打開一看,全都是'00000.....',。。。。。卒!!!!。。。。。。。

1.png

    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代碼復制下來,替換一下就行了)。

3.png

4.png

5.png

11.png

6.png

 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。

7.png

8.png

9.png

10.png

12.png

 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM