吾愛破解2024春節解題領紅包活動,喜迎新春~

K哥爬虫發表於2024-02-27

(圖作者 | 吾愛破解@Aoemax)

前言

K哥在這裡,先祝各位小夥伴們新春快樂,財源廣進,闔家幸福!

吾愛破解每年都有個解題領紅包活動,今年也不例外,需要我們使出看家逆向本領來分析內容獲得口令紅包,根據難度等級不同會獲得不同數量的吾愛幣,活動持續到元宵節結束。活動一共有十個題,本文分享過年期間抽空做的幾個題的相關思路。文章很早就寫好了,不過遵循論壇的規則,延遲至元宵節之後釋出。

活動地址:https://www.52pojie.cn/thread-1889163-1-1.html

Windows 初級

直接使用 IDA 開啟,先執行一次,隨便輸入:

Please input password:
aaa
Error, please try again

搜尋對應字串:

01

主要邏輯就是下面部分,先判斷長度是否等於 36,再逐位元組判斷 v10 != v9,那麼直接動態除錯:

if ( v36 == 36 )
{
    sub_5B2490(&v27, Src);
    sub_5B1FE0(Block, -3, (char *)v27, v28);
    LOBYTE(v38) = 2;
    v9 = Block;
    v10 = v35;
    if ( v34 >= 0x10 )
      v9 = (char **)Block[0];
    if ( v6 >= 0x10 )
      v10 = v7;
    if ( Block[4] != (char *)36 )
      goto LABEL_19;
    v11 = 32;
    while ( 1 )
    {
      v12 = *v10;
      if ( *v10 != *v9 )
        break;
      ++v10;
      ++v9;
      v14 = v11 < 4;
      v11 -= 4;
      if ( v14 )
      {
        v13 = 0;
        goto LABEL_18;
      }
    }
    。。。。。。。。。。。。。。。。。。。。
    LABEL_18:
    v18 = "Success";
    if ( v13 )
    LABEL_19:
    v18 = "Wrong,please try again.";
    。。。。。。。。。。。。。。。。。。。。
    sub_5BA6EE("Pause");
}
else
{
    v8 = sub_5B27D0(v5, "Error, please try again");
    sub_5B2A80((int)v8);
    sub_5BA6EE("Pause");
}

if ( v10 != v9 ) 這裡下個斷點,輸入 "a"×36,很明顯是明文對比了:

02

然後修改下資料型別,檢視對應值,很明顯 flag 就是 fl@g{H@ppy_N3w_e@r!2o24!Fighting!!!}:

03

還原也很簡單就不詳細分析了,唯一需要注意 IDA 識別 \x80 成了字元 €,不要直接去複製了:

bytes([a - 3 for a in b"ioCj~KCss|bQ6zbhCu$5r57$Iljkwlqj$$$\x80"]).decode("utf-8")

安卓初級1

抓貓能手

小小貓咪竟敢班門弄斧,偷走我的 flag,好歹我也是貓咪獵手:

04

抓到貓咪過後會播放“原神啟動”,影片最後會出現 flag,千萬不要提前關閉:

05

JS 除錯

抓貓部分是 html,使用的 webview,參考這篇文章:

https://www.52pojie.cn/thread-967665-1-1.html

開啟除錯,建議使用 Edge。

直接搜尋失敗的提示 "汗流浹背了吧,老弟!",可以看到失敗和成功呼叫函式,在失敗處下個斷點,隨便點選:

06

JAVA 分析

從上面內容當抓到小貓過後,回撥了 onSolverReturnValue:

public void onSolverReturnValue(int i) {
    if (i == -1) {
        this.mContext.startActivity(new Intent(this.mContext, YSQDActivity.class));
    }
}

onSolverReturnValue 又載入 YSQDActivity:

String filePath = "/data/user/0/com.zj.wuaipojie2024_1/files/ys.mp4";
   
public void onCreate(Bundle bundle) {
    。。。。。。。。。。
    playVideo(this.filePath);
}

public void playVideo(String str) {
    .............................
    videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { 
        @Override 
        public void onPrepared(MediaPlayer mediaPlayer) {
            mediaPlayer.setVideoScalingMode(1);//播放影片
        }
    });
    videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override 
        public void onCompletion(MediaPlayer mediaPlayer) {//播放結束設定flag
            YSQDActivity.this.tv.setText(YSQDActivity.extractDataFromFile(YSQDActivity.this.filePath));
        }
    });
    .............
}

播放結束時呼叫了 extractDataFromFile 獲取 flag,就是明文藏在影片中:

public static String extractDataFromFile(String str) {
    RandomAccessFile randomAccessFile = new RandomAccessFile(str, "r");
    long length = randomAccessFile.length();
    for (long max = Math.max(length - 30, 0L); max < length; max++) {
        if (new String(bArr, StandardCharsets.UTF_8).indexOf("flag{") != -1) {
            String str3 = str2.substring(indexOf).split("\\}")[0] + "}";
            randomAccessFile.close();
            return str3;
        }
    }
    randomAccessFile.close();
    return null;
}

07

安卓初級2

眾所周知原神是一個抽卡遊戲,原神啟動過後先來一發,抽中過後就會出現 flag:

08

充錢是不可能充錢的,這輩子都不可能的:

09

要充錢首先就要找到充錢入口,簡單搜尋一下:

public class WishActivity extends h {
    public int[] o = {10, 0, 0};
    public int[] p = {1, 2, 4, 8, 16, 32, 64, 128};
    public final void run() {
        textView.setText(iArr2[0] < 10 ? String.format(Locale.SIMPLIFIED_CHINESE, "當前已完成%d次祈願,擁有%d個糾纏之緣\n%d秒後將為你補充一個", Integer.valueOf(iArr2[1]), Integer.valueOf(wishActivity.o[0]), Integer.valueOf(wishActivity.o[2])) : String.format(Locale.SIMPLIFIED_CHINESE, "當前已完成%d次祈願,當前擁有%d個糾纏之緣\n糾纏之緣已滿,%d秒後將溢位一個,請儘快使用!", Integer.valueOf(iArr2[1]), Integer.valueOf(wishActivity.o[0]), Integer.valueOf(wishActivity.o[2])));
    }
};

可以看到充錢入口就在 wishActivity.o[0],直接用 Frida 充:

Java.perform(function x() {
    let WishActivity = Java.choose("com.kbtx.redpack_simple.WishActivity", {
        onMatch: function (instance) {
            console.log(`WishActivity instance found: ${instance}`);
            console.log(`WishActivity instance found: ${instance.o.value}`);
            instance.o.value[0] = 648*10;

        },
        onComplete: function () {
        }
    });
});

只能充一點點不能充多了,衝多了會封號:

10

充完錢就不用我教了吧。

下面是判斷是否抽中,抽中過後,又呼叫了 FlagActivity:

if (random < (iArr2[1] <= 80 ? 0.006d : (iArr2[1] - 80) * 0.1d)) {
    Toast.makeText(wishActivity, "恭喜你十連出金了,獎品為 flag 提示!", 1).show();
    wishActivity.startActivity(new Intent(wishActivity, FlagActivity.class));
    return;
}

在 FlagActivity 中,先獲取了簽名,再和 o 異或,就得到了 flag:

public static byte[] o = {86, -18, 98, 103, 75, -73, 51, -104, 104, 94, 73, 81, 125, 118, 112, 100, -29, 63, -33, -110, 108, 115, 51, 59, 55, 52, 77};

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
try {
    signatureArr = getPackageManager().getPackageInfo(getPackageName(), 64).signatures;
} catch (PackageManager.NameNotFoundException unused) {
    bArr = new byte[0];
}
if (signatureArr != null && signatureArr.length >= 1) {
    byte[] byteArray = signatureArr[0].toByteArray();
    ByteBuffer allocate = ByteBuffer.allocate(bArr2.length);
    for (int i = 0; i < bArr2.length; i++) {
        allocate.put((byte) (bArr2[i] ^ byteArray[i % byteArray.length]));
    }
    bArr = allocate.array();
    StringBuilder d = a.d("for honest players only: \n");
    d.append(new String(bArr));
    ((TextView) findViewById(R.id.tvFlagHint)).setText(d.toString());
}

直接利用 KeyStore Explorer,從 /META-INF/CERT.RSA 匯出秘鑰就行:

11

import base64
key = '''MIIDADCCAegCAQEwDQYJKoZIhvcNAQELBQAwRjEQMA4GA1UEAwwHa2J0eHdlcjEQ
MA4GA1UECwwHNTJwb2ppZTEQMA4GA1UECgwHNTJwb2ppZTEOMAwGA1UEBwwFQ2hp
bmEwHhcNMjQwMTE2MDYzMzIzWhcNNDkwMTA5MDYzMzIzWjBGMRAwDgYDVQQDDAdr
YnR4d2VyMRAwDgYDVQQLDAc1MnBvamllMRAwDgYDVQQKDAc1MnBvamllMQ4wDAYD
VQQHDAVDaGluYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIBIBBNf
V8FTmAmp9ikd0NqDxfn8V8rxmaSM/je5oMxGoQUhMqY0TjCaMbgO5xXf/L0gf4Sw
fmIMi8MjKwkwUEc/gp7LdKVF7o/UKf6uhIDkKEw1vGncQ9PBMOv3sKFsbRCFdhPC
JCAq53Em/P3JZCFEFYKH/noZaWO8UqR7uULw916wWSNr+mTFJxjHNUekw2LxF07G
QrmKMaTXy+jpkd+ifbcANdRRyHm13vEtu32xn9WrIREQJWxBVs0L5z0i0sBgMUTe
oY5lehLAwBRrpcXrprlzoie4FfyO/tTEonVHcYVL08BEaG7L5lBaVA56+qCZkzlB
C1qf64JkB0UsKIsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAXhAk7ZWZLNjYgzTq
82D9VpntfSMzY03e1l6c2mIiu1rmgYnbavtYmMqfNDeVnbLlDObRn8O5gu3n6e1d
2SSI3tZpKK1ZOf3zGLF7SpXwIFu22iej3k97aXANlKJegHZ9JWtjABTiVGSLKjfW
iZWe9HKTp3LBUJ2zGw3e03eWT+kzZtjvgI4gfRsji7vVG2odODMODCm+4a/dBnTl
ADtM0lVdJaDPUj8ReR0ql/99EyNUMv7wtE+3o0xpCrUd5NVLp4doEusfaRnSvS35
fDfp6SfODQ9BqE9TPgEPyOGn+iA8HHw+XQhzsrn8bNdNnlOBMsbXJcMFvF92Cw+4
cQGoog=='''
sign = base64.b64decode(key.replace('\n', ''))
bArr2 = [86, -18, 98, 103, 75, -73, 51, -104, 104, 94, 73, 81, 125, 118, 112, 100, -29, 63, -33, -110, 108, 115, 51, 59,
         55, 52, 77];
bArr2 = [(b & 0xFF ^ sign[i % len(sign)]) for i, b in enumerate(bArr2)]
print(bytes(bArr2).decode('utf-8'))

安卓中級

玄天帝重生

2006 年正月初五 2/2 10:00 玄天帝重生,18 年後,偶遇前世一個寶藏:

12

13

一代玄天帝還沒有發育起來就被消滅了,預知後事如何,歡迎吃席。

一線生機

在刺客聯盟追捕下,少年玄天帝不幸墜落懸崖,按照劇情發展應該又有奇遇了。在墜落懸崖時不幸碰到了頭,他突然想起 Android Studio 可以檢視部分日誌:

14

發現 checksum 不匹配,檢視對應程式碼,dex 位於 /assets/classes.dex 然後釋放到根目錄改名為 1.dex 了,那麼直接用 np 修復檔案頭,在替換回去:

public boolean checkPassword(String str) {
    try {
        InputStream open = getAssets().open("classes.dex");
        byte[] bArr = new byte[open.available()];
        open.read(bArr);
        File file = new File(getDir("data", 0), "1.dex");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        fileOutputStream.write(bArr);
        fileOutputStream.close();
        open.close();
        String str2 = (String) new DexClassLoader(file.getAbsolutePath(), getDir("dex", 0).getAbsolutePath(), null, getClass().getClassLoader()).loadClass("com.zj.wuaipojie2024_2.C").getDeclaredMethod("isValidate", Context.class, String.class, int[].class).invoke(null, this, str, getResources().getIntArray(R.array.A_offset));
        if (str2 == null || !str2.startsWith("唉!")) {
            return false;
        }
        this.tvText.setText(str2);
        this.myunlock.setVisibility(8);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}
// frida

var fileOutputStream = Java.use("java.io.FileOutputStream");
var a = 1;
fileOutputStream.write.overload('[B').implementation = function (bArr) {
    console.log("write: ");
    if (a % 2) {
        bArr = [100, 101, 120。。。。。]// 修復後的位元組,也可也只替換頭
    }
    a++;
    var ret = fileOutputStream.write.overload('[B').call(this, bArr);
    return ret;
};

然後執行,又報錯了,檢視對應程式碼:

15

public static HashMap<String, Integer> getClassDefData(ByteBuffer byteBuffer, int i) {
    if (byteBuffer == null) {
        throw new IllegalArgumentException("Buffer cannot be null");
    }
}

byteBuffer 讀取是在 fix(read(context), iArr[0], iArr[1], iArr[2], context),按提示在 fix dex 而這裡缺讀取的 decode.dex,前面儲存的又是 1.dex,那麼直接把 1.dex 改個名字/data/user/0/com.zj.wuaipojie2024_2/app_data/decode.dex:

private static ByteBuffer read(Context context) {
    File file = new File(context.getDir("data", 0), "decode.dex");
    if (file.exists()) {
        FileInputStream fileInputStream = new FileInputStream(file);
        byte[] bArr = new byte[fileInputStream.available()];
        fileInputStream.read(bArr);
        ByteBuffer wrap = ByteBuffer.wrap(bArr);
        fileInputStream.close();
        return wrap;
    }
    return null;
}

還注意到在獲取了對應方法後,刪掉了修復後的 dex,那麼直接 hook 刪除函式,或者 hook 寫入函式傳到電腦裡:

16

var deleteFile = Java.use("java.io.File").delete;
deleteFile.implementation = function () {
    console.log("delete file: " + this);
};

檢視修復成功後的函式,很容易得到密碼 048531267,也沒啥用,還有個提示:

17

結果發現沒有任何有用資訊,參考之前 A.d 的修復,再加上之前的傳入了一個引數 A_offset,附近還有個 B_offset:

18

那麼 hook 一下,修改為 B 的偏移,先把之前修復好了 2.dex 的改為 decode.dex,這樣 A 就是修復好了的:

let MainActivity = Java.use("com.zj.wuaipojie2024_2.MainActivity");
MainActivity["checkPassword"].implementation = function (str) {
    str = "048531267"
    console.log(`MainActivity.checkPassword is called: str=${str}`);
    let result = this["checkPassword"](str);
    return result;
};
let AppCompatActivity = Java.use("androidx.appcompat.app.AppCompatActivity");
AppCompatActivity["getResources"].implementation = function () {
    let result = this["getResources"]();
    console.log(result.getIntArray(0x7f030001));
    console.log(result);
    return result;
};

然後執行,首先密碼正確:

19

然後函式也修復出來了:

20

防止魔改主動呼叫一下:

Java.enumerateClassLoaders({
    "onMatch": function (loader) {
        if (loader.toString().indexOf("dalvik.system.DexClassLoader") !== -1) {
            Java.classFactory.loader = loader;
            console.log(loader);
        }
    },
    "onComplete": function () {
        console.log("success");
    }
});
let Utils = Java.use("com.zj.wuaipojie2024_2.Utils");
let password_uid="048531267"
let str = Java.use("java.lang.String").$new(password_uid);
let bArr = str.getBytes();
let sha1 = Utils.getSha1(bArr);
let md5 = Utils.md5(sha1);
console.log(`機緣是${md5}`);

Web

flag3{GRsgk2} 影片開頭變化的。

flag1{52pj2024} 2-3s 左右變化的。

flag2{xHOpRP} 掃描二維碼 直接訪問 https://2024challenge.52pojie.cn/ 會重定向,在響應頭裡面 X-Flag2: flag2{xHOpRP}:

21

flag4{YvJZNS} 網站會載入一張圖片 flag4_flag10.png 裡面直接顯示 4。

flagA ,登陸時後臺返回了加密 flagA 以及 UID,參考每次會請求 https://2024challenge.52pojie.cn/auth/uid 這個地址去解密 uid,直接修改 ck:

cookies = {
    "uid": "Uu6S/LKGcHP....ahI9KitSRsMFLDNu7ecW2TqkIcWBA==",//修改為flagA=****裡面的值
}
url = "https://2024challenge.52pojie.cn/auth/uid"
response = requests.get(url, cookies=cookies)
flagA{ea239d69}

flag5{P3prqF} 網頁中有提示 <!-- flag5 flag9 --> 以-.. 換行,可以看出另一個 flag,微調一下,再縮放:

22

flag9{KHTALK}:

23

flag6{20240217} 計算 md5(*)==1c450bbafad15ad87c32831fa1a616fc,直接讓網頁跑一會就行,或者在 https://www.cmd5.com/ 這裡查詢。

flag7{Djl9NQ} 影片中的 Git 地址裡面,利用歷史記錄檢視 https://github.com/ganlvtech/52pojie-2024-challenge/commit/6bbac038c4813fbc5d129a8d605471ea2e374786

flag8{OaOjIK} flagB 玩遊戲就行,如果你買了v50,會給你提示溢位,直接買 2**62 =4611686018427387904 個,不出意外的話可以購買成功。

flag10{6BxMkW} flag4_flag10.png 裡面,沒看懂隨便改了一個二進位制就有了:

24

實際上應該是兩個圖層,預設是黑色的,flag4 是白色可以直接看,flag10 和背景一樣導致顯示不出來

直接在這個網站 https://www.georgeom.net/StegOnline/image,選擇 lnverse (RGBA) 甚至你還可以直接改字尾為 mp4:

25

26

flag11{HPQfVF} 拼圖,網站給了提示:

:root {
    --var1: 0; /* 在 0 ~ 100 範圍內找到一個合適的值 */
    --var2: 0; /* 在 0 ~ 100 範圍內找到一個合適的值 */
}

#a000 {
    position: absolute;
    left: 0;
    top: 0;
    width: 30px;
    height: 30px;
    background: url(flag11.png) 0px 0px;
    transform: translate(calc(942.5135817416999px + 1.0215884355337748px * var(--var1) + 0.24768196677010001px * var(--var2)), calc(224.16483995058888px + 2.9293942195858147px * var(--var1) + 0.8924085229409133px * var(--var2)));
}

transform 裡面對應的應該是整數甚至可以說是 30 整數倍數才行,不然幾乎不可能還原圖片:

a = 942.5135817416999
b = 1.0215884355337748
c = 0.24768196677010001
d = 224.16483995058888
e = 2.9293942195858147
f = 0.8924085229409133
for i in range(0, 100):
    for j in range(0, 100):
        dd= a+b*i+c*j
        ww= d+e*i+f*j
        print(dd, ww)
        print(i, j)
//1020.0 450.00000000000006
71 20

27

flag12{HOXI} 很簡單 wasm,修改一下 num 就行:

let num = instance.exports.get_flag12(secret);//1213159497   (int32)(secret* 1103515245)!= 1 ? 0: 1213159497
let str = '';
while (num > 0) {
    str = String.fromCodePoint(num & 0xff) + str;
    num >>= 8;
}
return `flag12{${str}}`;

flagC 沒看懂考的什麼直接用他給案例圖片,根據返回提示修改,一開始提示物體太多,刪減 classes 到 4 個後就行了:

{"hint":"物體太多了","labels":["car 種類錯誤","bus 種類錯誤","truck 種類錯誤","train 種類錯誤","fire hydrant 種類錯誤","motorcycle 種類正確 位置錯誤","traffic light 種類正確 位置錯誤","traffic light 種類正確 位置錯誤","cat 種類錯誤","bicycle 種類錯誤","person 種類正確 位置正確","boat 種類錯誤","traffic light 種類正確 位置錯誤","airplane 種類正確 位置正確"],"colors":["ff9999","ff9999","ff9999","ff9999","ff9999","ffff99","ffff99","ffff99","ff9999","ff9999","99ff99","ff9999","ffff99","99ff99"]}

我這運氣好,剛好有四種 traffic light person airplane motorcycle 隨便填按提示修改就行,最後提交的內容:

data = {
    "boxes": [
        0.0071830302476882935,
        0.5186262726783752,
        0.4009798765182495,
        0.6479262709617615,
        0.4077116847038269,
        0.5121312141418457,
        0.7820706963539124,
        0.776945948600769,
        0.31250375509262085,
        0.2294374704360962,
        0.7281658053398132,
        0.4627001881599426,
        0.002122640609741211,
        0.8341933488845825,
        0.3802390992641449,
        0.9994925260543823
    ],
    "scores": [
        0.8933814167976379,
        0.8905049562454224,
        0.884631872177124,
        0.8726911544799805
    ],
    "classes": [
        0,
        9,
        3,
        4
    ]
}

{"hint":"flagC{b8ff9fbc} 過期時間: 2024-02-17 22:50:00","labels":["person 種類正確 位置正確","traffic light 種類正確 位置正確","motorcycle 種類正確 位置正確","airplane 種類正確 位置正確"],"colors":["99ff99","99ff99","99ff99","99ff99"]}

相關文章