0xGame2025Week3WriteUp

0xGame2025 Week3 Reverse方向全解&详解

easyApp

考点:apk逆向、工具应用、base64编码解码、z3求解

  • 附件程序是个apk,在模拟器中运行如图,推测按钮会触发flag检验逻辑:

    模拟器运行

  • 用JEB打开apk,找到MainActivity并右键解析后,得到JAVA代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    package com.example.easyapp;

    import android.content.Context;
    import android.os.Bundle;
    import android.util.Base64;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;
    import androidx.activity.ComponentActivity;
    import dalvik.system.DexClassLoader;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.lang.reflect.Method;
    import kotlin.Metadata;
    import kotlin.collections.ArraysKt;
    import kotlin.io.ByteStreamsKt;
    import kotlin.jvm.functions.Function1;
    import kotlin.jvm.internal.Intrinsics;
    import kotlin.text.Charsets;
    import kotlin.text.StringsKt;

    @Metadata(d1 = {"\u0000\"\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000E\n\u0002\u0010\u0012\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001A\u00020\u00042\b\u0010\u0005\u001A\u0004\u0018\u00010\u0006H\u0014J\f\u0010\u0007\u001A\u00020\b*\u00020\tH\u0002¨\u0006\n"}, d2 = {"Lcom/example/easyapp/MainActivity;", "Landroidx/activity/ComponentActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "toHexString", "", "", "app_release"}, k = 1, mv = {1, 9, 0}, xi = 0x30)
    public final class MainActivity extends ComponentActivity {
    public static final int $stable;

    static {
    }

    @Override // androidx.activity.ComponentActivity
    protected void onCreate(Bundle arg3) {
    super.onCreate(arg3);
    this.setContentView(layout.activity_main);
    EditText v3 = (EditText)this.findViewById(id.flagInput);
    ((Button)this.findViewById(id.button)).setOnClickListener((View arg2) -> MainActivity.onCreate$lambda$0(v3, this, arg2)); // 这里将Click和onCreate$lambda$0绑定起来了
    }

    private static final void onCreate$lambda$0(EditText arg6, MainActivity arg7, View arg8) {
    boolean v6_4; // v8_1的返回值流向v6_4
    Intrinsics.checkNotNullParameter(arg7, "this$0");
    String v6 = arg6.getText().toString(); // v6存放了输入的flag
    File v8 = new File(arg7.getCacheDir(), "dex.zip");
    InputStream v0 = arg7.getAssets().open("dex.zip"); // 打开了asset/dex.zip
    Intrinsics.checkNotNullExpressionValue(v0, "open(...)");
    ByteStreamsKt.copyTo$default(v0, ((OutputStream)new FileOutputStream(v8)), 0, 2, null);
    if(v6.length() == 42) {
    Method v8_1 = new DexClassLoader(v8.getAbsolutePath(), arg7.getCacheDir().getAbsolutePath(), null, arg7.getClassLoader()).loadClass("com.example.easyapp.Secret").getDeclaredMethod("check", String.class); // v8指向了dex中的com.example.easyapp.Secret的check方法
    String v1 = v6.substring(0, 26); // flag被截断,截断的部分流向v1
    Intrinsics.checkNotNullExpressionValue(v1, "this as java.lang.String…ing(startIndex, endIndex)");
    byte[] v1_1 = v1.getBytes(Charsets.UTF_8); // flag截断部分流向v1_1
    Intrinsics.checkNotNullExpressionValue(((Object)v1_1), "this as java.lang.String).getBytes(charset)");
    String v1_2 = Base64.encodeToString(v1_1, 0); // flag截断部分被进行了base编码,流向v1_2
    Intrinsics.checkNotNullExpressionValue(v1_2, "encodeToString(...)");
    String v1_3 = StringsKt.trim(((CharSequence)v1_2)).toString(); // 编码后的flag截断部分流向v1_3
    String v6_1 = v6.substring(26); // flag的剩余部分流向了v6_1
    Intrinsics.checkNotNullExpressionValue(v6_1, "this as java.lang.String).substring(startIndex)");
    byte[] v6_2 = v6_1.getBytes(Charsets.UTF_8); // flag的剩余部分从v6_1流向了v6_2
    Intrinsics.checkNotNullExpressionValue(((Object)v6_2), "this as java.lang.String).getBytes(charset)");
    if(v1_3.equals("MHhHYW1le0RvX3kwdV9sMHYzX2FuZHIwMWQ=")) { // 此处为v1_3的检验逻辑
    Object v6_3 = v8_1.invoke(null, arg7.toHexString(v6_2)); // v6_2经过toHexString处理后作为v8_1,即dex:com.example.easyapp.Secret.check的参数
    Intrinsics.checkNotNull(v6_3, "null cannot be cast to non-null type kotlin.Boolean");
    v6_4 = ((Boolean)v6_3).booleanValue(); // v8_1的返回值流向v6_4
    }
    else {
    v6_4 = false;
    }

    if(v6_4) { // v6_4决定了最后的输出:Right/Oh no no
    Toast.makeText(((Context)arg7), "Right!", 0).show();
    return;
    }

    Toast.makeText(((Context)arg7), "Oh no no", 0).show();
    return;
    }

    Toast.makeText(((Context)arg7), "Length wrong!", 0).show();
    }

    private final String toHexString(byte[] arg11) {
    return ArraysKt.joinToString$default(arg11, "", null, null, 0, null, ((Function1)MainActivity.toHexString.1.INSTANCE), 30, null);
    }
    }

    可以看到,flag被拆分成两部分进行检验,前26个字符经过base64编码后与”MHhHYW1le0RvX3kwdV9sMHYzX2FuZHIwMWQ=”比对,剩下部分由dex中的com.example.easyapp.Secret.check方法检验。第一部分可以借助CyberChef解密:

    第一部分flag

  • 现在来分析flag第二部分的检验。在JEB左上角工程浏览器中找到asset/dex.zip/dex.bin

    找到dex.bin

    Secret类解析后得到:

    Secret类

    其中check的逻辑等价为一个三元一次大数方程组,三个元从flag的第二部分截取:

    v0 = substring(flagPart2[0:16])
    v3 = substring(flagPart2[8:24])
    v4 = substring(flagPart2:[16:32])

    v3 + 3 * v0 - 27454419028250566601 = 0
    2 * v4 - 5 * v3 + 20616666104378640363 = 0
    v0 + 4 * v4 - 0x1dce62be9f0fa2f6c = 0

    可以使用z3求解。解密脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    from z3 import *

    """
    v0 v3 v4分别存放[0:16],[8:24],[16:32]下标的字符,BigInteger将这些字符转成十六进制数,每个十六进制数4位,所以每个变量是64位
    需要关注两两变量之间有32位的重叠
    所以可以将三个64位变量分成4个32位变量来处理
    """
    # 定义4个32位变量
    u=BitVec('u',32) # v0的高32位
    v=BitVec('v',32) # v0的低32位,也是v3的高32位
    w=BitVec('w',32) # v3的低32位,也是v4的高32位
    t=BitVec('t',32) # v4的低32位

    # 通过拼接32位变量构造64位变量
    v0=Concat(u,v) #v0=u<<32|v
    v3=Concat(v,w) #v3=v<<32|w
    v4=Concat(w,t) #v4=w<<32|t

    #定义常量
    A=BitVecVal(27454419028250566601,64)
    B=BitVecVal(-20616666104378640363,64)
    C=BitVecVal(0x1dce62be9f0fa2f6c,64)

    #建立约束
    s=Solver()
    s.add(v3+3*v0==A)
    s.add(2*v4-5*v3==B)
    s.add(v0+4*v4==C)

    if s.check()==unsat:
    print("无解")
    while s.check()==sat:
    model=s.model()
    u_val=model[u].as_long()
    v_val=model[v].as_long()
    w_val=model[w].as_long()
    t_val=model[t].as_long()
    print(f"found solution:")
    print(f"u: {u_val:08x}")
    print(f"v: {v_val:08x}")
    print(f"w: {w_val:08x}")
    print(f"t: {t_val:08x}")

    # 将32位变量还原成字符
    result=(u_val<<96)|(v_val<<64)|(w_val<<32)|t_val

    hex_str=f"{result:032x}"
    flag_bytes=bytes.fromhex(hex_str)
    flag=flag_bytes.decode('utf-8')
    print(f"flag: {flag}")

    #排除已有的解
    s.add(Or(u!=u_val,v!=v_val,w!=w_val,t!=t_val))

    """
    found solution:
    u: 5f346e64
    v: 5f646578
    w: 5f6c6f61
    t: 6465727d
    flag: _4nd_dex_loader}
    """

flag:0xGame{Do_y0u_l0v3_andr01d_4nd_dex_loader}

Minesweepr

考点:web逆向、js逆向

  • 附件是html文件和js文件,推测是一个web逆向题。html打开是一个扫雷游戏网页。因为js主要用来处理网页交互,所以重点关注js文件的内容

    扫雷游戏

  • 网页按F12打开开发人员工具,可以在工具栏源代码模块看js脚本。搜索”flag”可以找到一个可疑的函数:

    js脚本与可疑函数

    这里函数名和变量名都不是有意义的,不好阅读,可以双击高亮来追踪数据流。

    分析可以发现,0x55ea57被调用了很多次,追踪这个值可以到达0x183c,这个函数负责从0xca50中加载值返回,这算是一个全局的混淆手段了

    0x55ea57被调用了多次

    0x183c函数

    根据0x183c的代码可以找到0xca50的字符串表0x1193d5,表中就可以索引出密钥winModal:message2:”WebIsInteresting”了,以及密文”g\x1d%(\x1e,\x15@SA\x5cFD\x0fWJn]P}^}\x0c\x12\x07_]AGYC^o\x04\x00yA-ZGT\x16U\x0e”(0x183c的索引算法有点奇怪,好像索引不到正确的位置上,没搞懂)

    而0x172ca4的解密逻辑可以等价如下:

    1
    2
    for i in range(len(enc)):
    flag+=enc[i]^key[i%len(key)]

    因此可以得到解密脚本:

    1
    2
    3
    4
    5
    6
    enc="g\x1d%(\x1e,\x15@SA\x5cFD\x0fWJn]P}^}\x0c\x12\x07_]AGYC^o\x04\x00yA-ZGT\x16U\x0e"
    key="WebIsInteresting"
    flag=""
    for i in range(len(enc)):
    flag+=chr(ord(enc[i])^ord(key[i%len(key)]))
    print(flag)

flag:0xGame{463950f9-9824-4bfb-8230-98ab02d431d0}

ezVBS

考点:VBS逆向、VBS去混淆

  • 附件程序是个VBS文件,运行显示:

    运行VBS

  • 用记事本打开VBS会发现是一堆奇怪的字符,查了一下说是加了混淆

    记事本打开

    参考VBS代码混淆,可以使用去混淆脚本进行去混淆,使用方法见链接:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    Option Explicit

    Function Defuscator(vbs)
    Dim t
    t = InStr(1, vbs, "Execute", 1)
    t = Mid(vbs, t + Len("Execute"))
    t = Eval(t)
    Defuscator = t
    End Function

    Dim fso, i
    Const ForReading = 1
    Set fso = CreateObject("Scripting.FileSystemObject")
    For i = 0 To WScript.Arguments.Count - 1
    Dim FileName
    FileName = WScript.Arguments(i)
    Dim MyFile
    Set MyFile = fso.OpenTextFile(FileName, ForReading)
    Dim vbs
    vbs = MyFile.ReadAll
    WScript.Echo Defuscator(vbs)
    MyFile.Close
    Next

    Set fso = Nothing

    然后可以得到如下的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Microsoft (R) Windows Script Host Version 5.812
    版权所有(C) Microsoft Corporation。保留所有权利。

    code = "Function l(str):Dim i,j,k,r:j=Len(str):r="""":For i=1 to j:k=Asc(Mid(str,i,1)):If k>=33 And k<=126 Then:r=r&Chr(33+((k+14)Mod 94)):Else:r=r&Chr(k):End If:Next:l=r:End Function:Execute l(""DEC l x?AFEq@IWQt?E6C J@FC >6DD286i QX i q2D6ec%23=6 l Q7Ie{&*d2Eh=?H>5b%3BKF#JZp:A(w!s@)+z|uvr'ax^"""";$C6tD9`g}y<8_Gfc~4qQ i 7=28 l QH2+2pJ}vsyhrH{7}5K*r?J&DpyE$>{&_HB}z>{*u?J%g:J#|:d&tp|w_52glQ i 6?4 l QQ i u@C : l ` %@ {6?WDECX $E6A b i :7 : Z a kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade Z pD4W|:5WDEC[ : Z a[ `XX i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 4c l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4<X |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U 4c i t=D6 i x7 : Z ` kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U QlQ i t=D6 i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U QlQ U QlQ i t?5 x7 i t?5 x7 i }6IE i xu 6?4 l 7=28 %96? i |D8q@I Q*@FC 7=28 :D 4@CC64EPQ i t=D6 i |D8q@I Q*@FC 7=28 :D :?4@CC64EPQ i t?5 x7"")"
    Execute code
    code = "Function l(str):Dim i,j,k,r:j=Len(str):r="""":For i=1 to j:k=Asc(Mid(str,i,1)):If k>=33 And k<=126 Then:r=r&Chr(33+((k+14)Mod 94)):Else:r=r&Chr(k):End If:Next:l=r:End Function:Execute l(""DEC l x?AFEq@IWQt?E6C J@FC 7=28i QX i q2D6ec%23=6 l QeHB)IvGyC%c~"""">}twp^h@fg'qZ`=+#s4EdAFa2(_5Du{Jr$86;z79&:x|*!<Kb?3Q i 7=28 l Q^^H5q'Z2CGvJ+(fdZyaE#:vz=(faCy28#^H$=xwE#GKE+_f$CvZB@zHa`'%2qxpJs^6Esgb&+AHF=:&6#'p2+AHD+zHJ`gr2=yaE#GKEq(@Eq'p9qg>{ZgwEq(fFq'f7Z^H8ZAH9`G27~BHu#'>9CGv7Cy28#'CEZ(;dZzH5q'""""Eq(f2=AH8#(fz#x%D#yp2=EllQ i 6?4 l QQ i u@C : l ` %@ {6?WDECX $E6A b i :7 : Z a kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade Z pD4W|:5WDEC[ : Z a[ `XX i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 4c l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4<X |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U 4c i t=D6 i x7 : Z ` kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U QlQ i t=D6 i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U QlQ U QlQ i t?5 x7 i t?5 x7 i }6IE i xu 6?4 l 7=28 %96? i |D8q@I DEC i t=D6 i |D8q@I Q*@FC 7=28 :D :?4@CC64EPQ i |D8q@I 6?4 i t?5 x7"")"
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    strScriptPath = WScript.ScriptFullName
    strTextToWrite = code
    objFSO.OpenTextFile(strScriptPath, 2, True).WriteLine(strTextToWrite)

    可以看到code中有一些可读明文,后面又是一大串奇怪的字符。推测还有一层混淆,可读明文即为解密函数。让AI给出解密脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    def l(encrypted_str):
    """VBScript l 函数的 Python 实现"""
    result = ""
    for i in range(len(encrypted_str)):
    k = ord(encrypted_str[i])
    if 33 <= k <= 126:
    # 与 VBScript 相同的算法
    result += chr(33 + ((k + 14) % 94))
    else:
    result += chr(k)
    return result


    # 你的加密字符串
    encrypted_strings = [
    "DEC l x?AFEq@IWQt?E6C J@FC >6DD286i QX i q2D6ec%23=6 l Q7Ie{&*d2Eh=?H>5b%3BKF#JZp:A(w!s@)+z|uvr'ax^\"\"\"\";$C6tD9`g}y<8_Gfc~4qQ i 7=28 l QH2+2pJ}vsyhrH{7}5K*r?J&DpyE$>{&_HB}z>{*u?J%g:J#|:d&tp|w_52glQ i 6?4 l QQ i u@C : l ` %@ {6?WDECX $E6A b i :7 : Z a kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade Z pD4W|:5WDEC[ : Z a[ `XX i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 4c l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4<X |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U 4c i t=D6 i x7 : Z ` kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U QlQ i t=D6 i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U QlQ U QlQ i t?5 x7 i t?5 x7 i }6IE i xu 6?4 l 7=28 %96? i |D8q@I Q*@FC 7=28 :D 4@CC64EPQ i t=D6 i |D8q@I Q*@FC 7=28 :D :?4@CC64EPQ i t?5 x7",

    "DEC l x?AFEq@IWQt?E6C J@FC 7=28i QX i q2D6ec%23=6 l QeHB)IvGyC%c~\"\"\"\">}twp^h@fg'qZ`=+#s4EdAFa2(_5Du{Jr$86;z79&:x|*!<Kb?3Q i 7=28 l Q^^H5q'Z2CGvJ+(fdZyaE#:vz=(faCy28#^H$=xwE#GKE+_f$CvZB@zHa`'%2qxpJs^6Esgb&+AHF=:&6#'p2+AHD+zHJ`gr2=yaE#GKEq(@Eq'p9qg>{ZgwEq(fFq'f7Z^H8ZAH9`G27~BHu#'>9CGv7Cy28#'CEZ(;dZzH5q'\"\"\"\"Eq(f2=AH8#(fz#x%D#yp2=EllQ i 6?4 l QQ i u@C : l ` %@ {6?WDECX $E6A b i :7 : Z a kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade Z pD4W|:5WDEC[ : Z a[ `XX i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 4c l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4<X |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U 4c i t=D6 i x7 : Z ` kl {6?WDECX %96? i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade Z pD4W|:5WDEC[ : Z `[ `XX Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 4b l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - ecX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U 4b U QlQ i t=D6 i 3:Eq=@4< l pD4W|:5WDEC[ :[ `XX Y ade Y ade i 4` l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ec Y ecXX |@5 ec Z `[ `X i 4a l |:5Wq2D6ec%23=6[ x?EW3:Eq=@4< - Wec Y ecXX |@5 ec Z `[ `X i 6?4 l 6?4 U 4` U 4a U QlQ U QlQ i t?5 x7 i t?5 x7 i }6IE i xu 6?4 l 7=28 %96? i |D8q@I DEC i t=D6 i |D8q@I Q*@FC 7=28 :D :?4@CC64EPQ i |D8q@I 6?4 i t?5 x7"
    ]

    # 解密并显示结果
    for i, encrypted in enumerate(encrypted_strings, 1):
    print(f"解密第 {i} 个字符串:")
    decrypted = l(encrypted)
    print(decrypted)
    print("\n" + "=" * 80 + "\n")

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    解密第 1 个字符串:
    str = InputBox("Enter your message: ") : Base64Table = "fx6LUY5at9lnwmd3TbqzuRy+AipWHPDoXZKMFGCV2I/QQQQjSreEsh18NJkg0v74OcB" : flag = "waZaAyNGDJ9CwLfNdzYCnyUsAJtSmLU0wqNKmLYFnyT8iyRMi5UEAMH0da8=" : enc = "" : For i = 1 To Len(str) Step 3 : if i + 2 <= Len(str) Then : bitBlock = Asc(Mid(str, i, 1)) * 256 * 256 + Asc(Mid(str, i + 1, 1)) * 256 + Asc(Mid(str, i + 2, 1)) : c1 = Mid(Base64Table, Int(bitBlock \ (64 * 64 * 64)) Mod 64 + 1, 1) : c2 = Mid(Base64Table, Int(bitBlock \ (64 * 64)) Mod 64 + 1, 1) : c3 = Mid(Base64Table, Int(bitBlock \ 64) Mod 64 + 1, 1) : c4 = Mid(Base64Table, Int(bitBlock) Mod 64 + 1, 1) : enc = enc & c1 & c2 & c3 & c4 : Else : If i + 1 <= Len(str) Then : bitBlock = Asc(Mid(str, i, 1)) * 256 * 256 + Asc(Mid(str, i + 1, 1)) * 256 : c1 = Mid(Base64Table, Int(bitBlock \ (64 * 64 * 64)) Mod 64 + 1, 1) : c2 = Mid(Base64Table, Int(bitBlock \ (64 * 64)) Mod 64 + 1, 1) : c3 = Mid(Base64Table, Int(bitBlock \ 64) Mod 64 + 1, 1) : enc = enc & c1 & c2 & c3 & "=" : Else : bitBlock = Asc(Mid(str, i, 1)) * 256 * 256 : c1 = Mid(Base64Table, Int(bitBlock \ (64 * 64 * 64)) Mod 64 + 1, 1) : c2 = Mid(Base64Table, Int(bitBlock \ (64 * 64)) Mod 64 + 1, 1) : enc = enc & c1 & c2 & "=" & "=" : End If : End If : Next : IF enc = flag Then : MsgBox "Your flag is correct!" : Else : MsgBox "Your flag is incorrect!" : End If

    ================================================================================

    解密第 2 个字符串:
    str = InputBox("Enter your flag: ") : Base64Table = "6wqXxGvJrT4OQQQQmNEHA/9o78VB+1lZRDct5pu2aW0dsFLyCSgejKfhUiIMYPkz3nb" : flag = "//wdBV+arvGyZW75+J2tRiGKlW72rJagR/wSlIHtRvztZ07SrG+qoKw21VTaBIAyD/etD83UZpwuliUeRVAaZpwsZKwy18CalJ2tRvztBWotBVAhB8mL+8HtBW7uBV7f+/wg+pwh1vafOqwFRVmhrvGfrJagRVrt+Wj5+KwdBVQQQQtBW7alpwgRW7KRITsRJAalt==" : enc = "" : For i = 1 To Len(str) Step 3 : if i + 2 <= Len(str) Then : bitBlock = Asc(Mid(str, i, 1)) * 256 * 256 + Asc(Mid(str, i + 1, 1)) * 256 + Asc(Mid(str, i + 2, 1)) : c1 = Mid(Base64Table, Int(bitBlock \ (64 * 64 * 64)) Mod 64 + 1, 1) : c2 = Mid(Base64Table, Int(bitBlock \ (64 * 64)) Mod 64 + 1, 1) : c3 = Mid(Base64Table, Int(bitBlock \ 64) Mod 64 + 1, 1) : c4 = Mid(Base64Table, Int(bitBlock) Mod 64 + 1, 1) : enc = enc & c1 & c2 & c3 & c4 : Else : If i + 1 <= Len(str) Then : bitBlock = Asc(Mid(str, i, 1)) * 256 * 256 + Asc(Mid(str, i + 1, 1)) * 256 : c1 = Mid(Base64Table, Int(bitBlock \ (64 * 64 * 64)) Mod 64 + 1, 1) : c2 = Mid(Base64Table, Int(bitBlock \ (64 * 64)) Mod 64 + 1, 1) : c3 = Mid(Base64Table, Int(bitBlock \ 64) Mod 64 + 1, 1) : enc = enc & c1 & c2 & c3 & "=" : Else : bitBlock = Asc(Mid(str, i, 1)) * 256 * 256 : c1 = Mid(Base64Table, Int(bitBlock \ (64 * 64 * 64)) Mod 64 + 1, 1) : c2 = Mid(Base64Table, Int(bitBlock \ (64 * 64)) Mod 64 + 1, 1) : enc = enc & c1 & c2 & "=" & "=" : End If : End If : Next : IF enc = flag Then : MsgBox str : Else : MsgBox "Your flag is incorrect!" : MsgBox enc : End If

    ================================================================================

    解密出来是两段代码。但是代码给的base表不是很对,第2段代码中删除base表中间连续QQQQ的几个可以解密出一些类似提示的东西

    类似提示的东西

    第1段代码的编码表进行同样的修改就得到了flag:

    得到flag

flag:0xGame{bf00591f-a1cb-4191-b41d-d4eecda0b798}

World’s_end_BlackBox

考点:动态调试、魔改RC4算法

  • 附件程序运行如图:

    附件程序运行

  • 用IDA打开,main函数由于类的影响不好分析,先进行简单的变量名优化:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    int __fastcall main(int argc, const char **argv, const char **envp)
    {
    __int64 v3; // rax
    __int64 v4; // rax
    __int64 v5; // rax
    __int64 v6; // rax
    __int64 v7; // rax
    __int64 v8; // rax
    __int64 v9; // rax
    __int64 v10; // rax
    __int64 v11; // rax
    std::string::iterator __for_end; // [rsp+20h] [rbp-60h] BYREF
    std::string::iterator __for_begin; // [rsp+28h] [rbp-58h] BYREF
    std::vector<int> EndFlag; // [rsp+30h] [rbp-50h] BYREF
    std::string encrypted_message; // [rsp+50h] [rbp-30h] BYREF
    std::string key; // [rsp+70h] [rbp-10h] BYREF
    std::string flag; // [rsp+90h] [rbp+10h] BYREF
    std::string v19; // [rsp+B0h] [rbp+30h] BYREF
    std::vector<int>::value_type __x; // [rsp+D0h] [rbp+50h] BYREF
    char c; // [rsp+D7h] [rbp+57h]
    std::string *__for_range; // [rsp+D8h] [rbp+58h]

    _main(argc, argv, envp);
    SetConsoleOutputCP(0xFDE9u);
    basicString((__int64)&flag);
    basicString((__int64)&key);
    v3 = operateOut(pOut, &unk_408078);
    stdOut(v3, out);
    v4 = operateOut(pOut, &unk_408160);
    stdOut(v4, out);
    getLine(pIn, &key); // 获取密码
    if ( getLength(&key) != 12 ) // 长度判断
    {
    v5 = operateOut(pOut, "Length Error!");
    stdOut(v5, out);
    system("pause");
    exit(0);
    }
    KeyGenerate(&v19, &KeyEnc[abi:cxx11], &DeKey[abi:cxx11]);// key的生成算法
    std::string::operator=(&TrueKey[abi:cxx11], &v19);
    std::string::~string(&v19);
    if ( std::operator!=<char>(&key, &TrueKey[abi:cxx11]) )// 出现了检验逻辑,可以从这里直接获取到真正的key
    {
    v6 = operateOut(pOut, &unk_4081A2); // key错误时的输出
    stdOut(v6, out);
    system("pause");
    exit(0);
    }
    v7 = operateOut(pOut, &Congratulation); // key正确时的输出
    stdOut(v7, out);
    v8 = operateOut(pOut, &unk_408212);
    stdOut(v8, out);
    getLine(pIn, &flag); // 获取flag的输入
    if ( getLength(&flag) != 51 )
    {
    v9 = operateOut(pOut, "Length Error!");
    stdOut(v9, out);
    system("pause");
    exit(0);
    }
    encrypt(&encrypted_message, &flag, &key); // 加密算法,密钥即为前文的key
    std::vector<int>::vector(&EndFlag);
    __for_range = &encrypted_message;
    __for_begin._M_current = (char *)std::string::begin(&encrypted_message);
    __for_end._M_current = (char *)std::string::end(__for_range);
    while ( __gnu_cxx::operator!=<char *,std::string>(&__for_begin, &__for_end) )
    {
    c = *__gnu_cxx::__normal_iterator<char *,std::string>::operator*(&__for_begin);
    __x = (unsigned __int8)c;
    std::vector<int>::push_back(&EndFlag, &__x);
    __gnu_cxx::__normal_iterator<char *,std::string>::operator++(&__for_begin);
    }
    if ( std::operator==<int,std::allocator<int>>(&KALEIDXSCOPE, &EndFlag) )// 检验加密结果
    {
    v10 = operateOut(pOut, "All Perfect!");
    stdOut(v10, out);
    system("pause");
    exit(0);
    }
    v11 = operateOut(pOut, "Try again!");
    stdOut(v11, out);
    std::vector<int>::~vector(&EndFlag);
    std::string::~string(&encrypted_message);
    std::string::~string(&key);
    std::string::~string(&flag);
    return 0;
    }

    //加密函数
    std::string *__cdecl modifiedRC4(std::string *__return_ptr retstr, const std::string *plaintext, const std::string *key)
    {
    size_t v3; // rax
    size_t length; // rax
    char v5; // bl
    std::vector<int>::reference List; // rax
    __int64 v8; // [rsp+0h] [rbp-80h] BYREF
    std::vector<int> k; // [rsp+20h] [rbp-60h] BYREF
    std::vector<int> s; // [rsp+40h] [rbp-40h] BYREF
    std::allocator<int> __a; // [rsp+66h] [rbp-1Ah] BYREF
    char v12; // [rsp+67h] [rbp-19h] BYREF
    size_t i; // [rsp+68h] [rbp-18h]

    std::allocator<int>::allocator((std::allocator<int> *const)&v8 + 102);
    std::vector<int>::vector(&s, 0x100uLL, &__a);
    std::allocator<int>::~allocator(&__a);
    rc4_ksa(&s, key); // RC4密钥调度算法
    v3 = std::string::size(plaintext);
    rc4_prga(&k, &s, v3); // RC4伪随机数生成算法
    std::allocator<char>::allocator(&v12);
    std::string::basic_string(retstr, &unk_408037, &v12);
    std::allocator<char>::~allocator(&v12);
    for ( i = 0LL; ; ++i )
    {
    length = std::string::size(plaintext);
    if ( i >= length )
    break;
    v5 = *(_BYTE *)StrMakeList(plaintext, i);
    List = IntMakeList(&k, i);
    std::string::operator+=(retstr, (unsigned int)(char)(v5 ^ *(_BYTE *)List ^ 7));// 魔改的地方在这
    }
    std::vector<int>::~vector(&k);
    std::vector<int>::~vector(&s);
    return retstr;
    }

    //密钥调度算法
    void __cdecl rc4_ksa(std::vector<int> *s, const std::string *key)
    {
    int *M_current; // rbx
    std::vector<int>::iterator v3; // rax
    int v4; // ebx
    unsigned __int64 v5; // rdx
    _BYTE *List; // rax
    char v7; // dl
    int *s_j; // rbx
    int *s_i; // rax
    size_t i; // [rsp+20h] [rbp-60h]
    int j; // [rsp+2Ch] [rbp-54h]

    M_current = std::vector<int>::end(s)._M_current;
    v3._M_current = std::vector<int>::begin(s)._M_current;
    std::iota<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,int>(
    v3,
    (__gnu_cxx::__normal_iterator<int*,std::vector<int> >)M_current,
    0);
    j = 0;
    for ( i = 0LL; i <= 255; ++i )
    {
    v4 = *IntMakeList(s, i) + j;
    v5 = i % getLength(key);
    List = (_BYTE *)StrMakeList(key, v5);
    v7 = v4 + *List;
    LODWORD(List) = (unsigned int)((v4 + (char)*List) >> 31) >> 24;
    j = (unsigned __int8)((_BYTE)List + v7) - (_DWORD)List;
    s_j = IntMakeList(s, j);
    s_i = IntMakeList(s, i);
    std::swap<int>(s_i, s_j);
    }
    }

    //伪随机数生成算法
    std::vector<int> *__cdecl rc4_prga(std::vector<int> *__return_ptr retstr, std::vector<int> *s, size_t length)
    {
    std::vector<int>::reference v3; // rax
    int *v4; // rbx
    int *v5; // rax
    std::vector<int>::reference v6; // rax
    const std::vector<int>::value_type *v7; // rax
    size_t r; // [rsp+30h] [rbp-50h]
    int j; // [rsp+38h] [rbp-48h]
    int i; // [rsp+3Ch] [rbp-44h]

    std::vector<int>::vector(retstr);
    i = 0;
    j = 0;
    for ( r = 0LL; r < length; ++r )
    {
    i = (i + 1) % 256;
    v3 = IntMakeList(s, i);
    j = (unsigned __int8)(((unsigned int)((j + *v3) >> 31) >> 24) + j + *(_BYTE *)v3)
    - ((unsigned int)((j + *v3) >> 31) >> 24);
    v4 = IntMakeList(s, j);
    v5 = IntMakeList(s, i);
    std::swap<int>(v5, v4);
    LODWORD(v4) = *IntMakeList(s, i);
    v6 = IntMakeList(s, j);
    v7 = IntMakeList(
    s,
    (int)((unsigned __int8)(((unsigned int)(((int)v4 + *v6) >> 31) >> 24) + (_BYTE)v4 + *(_BYTE *)v6)
    - ((unsigned int)(((int)v4 + *v6) >> 31) >> 24)));
    std::vector<int>::push_back(retstr, v7);
    }
    return retstr;
    }

    可以看到函数对输入的数据进行了魔改的RC4加密,但实际上没有改变RC4加解密同一个函数的性质,所以解密只需要复现这个代码即可

    下面先获取密钥和加密后的flag数据。在main函数第一个getline处下断点,开始动态调试

    第一个getline处下断点

    随便输入一个字符后会到长度检测,要求输入的密码长度要等于12,可以输入12个字符也可以后续到这个cmp的时候改jz绕过

    长度检测

    过了长度检测后就可以一直运行到key检验处,双击生成的密钥的变量名(TrueKey)就可以得到真正的key了(从

    hex窗口可以看到key即为”XaleidscopiX”)

    运行到key检验处

    真正的key

    然后还需要获得真正flag的加密数据。往下绕过key检验,运行到jz的时候再改一下ZF标志位(此处应当跳转)

    绕过key检验(已绕过)

    绕过后终端还有提示

    提示

    接下来运行到第二个getline,随便输几个数据(有长度检验,最好是输入51个字符,省的绕过长度检验)。然后继续运行到加密结果的检验处

    运行到加密结果检验处

    其中KALEIDXSCOPE的第一个offset就是正确的flag加密后的数据

    KALEIDXSCOPE第一个offset(unk_2692A10)

    正确flag加密后的数据

    解密脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    def rc4_decrypt(data, key):
    S = list(range(256))
    j = 0
    out = []

    # Key-scheduling algorithm (KSA)
    for i in range(256):
    j = (j + S[i] + key[i % len(key)]) % 256
    S[i], S[j] = S[j], S[i]

    # Pseudo-random generation algorithm (PRGA)
    i = 0
    j = 0
    for char in data:
    i = (i + 1) % 256
    j = (j + S[i]) % 256
    S[i], S[j] = S[j], S[i]
    K = S[(S[i] + S[j]) % 256]
    out.append(K)

    return out

    enc=[0x000000FC, 0x000000EA, 0x00000015, 0x0000002C, 0x00000086, 0x00000038, 0x0000003F, 0x000000F3, 0x00000092, 0x000000CE, 0x000000DA, 0x0000008E, 0x00000048, 0x000000D3, 0x00000007, 0x0000009F, 0x000000D9, 0x00000057, 0x000000B1, 0x000000EE, 0x00000041, 0x0000009A, 0x0000004D, 0x000000C5, 0x00000065, 0x0000006A, 0x000000FF, 0x000000C9, 0x0000005D, 0x00000034, 0x000000AD, 0x000000EA, 0x000000B1, 0x00000020, 0x0000004B, 0x000000DC, 0x000000BD, 0x000000D2, 0x00000035, 0x00000002, 0x00000084, 0x00000035, 0x00000071, 0x000000EC, 0x000000E0, 0x00000048, 0x0000008E, 0x000000EA, 0x0000007B, 0x000000AA, 0x000000CF]
    _key="XaleidscopiX"
    key=[]
    flag=[]
    for i in _key:
    key.append(ord(i))
    #print(key)

    keystream=rc4_decrypt(enc, key)
    for i in range(len(enc)):
    flag.append(enc[i]^keystream[i]^7)
    print(''.join([chr(i) for i in flag]))

flag:0xGame{RC4_15_4_b4s1c&fl3x1bl3_3ncrYp710n4lg0r17hm}

Q(≧▽≦)T

考点:QT程序逆向、crackme、动态调试、RC4、哈希校验

  • 附件程序运行如图,是个序列号生成器:

    运行

  • 用IDA打开,出现的是start函数, 看不出什么东西来

    start函数

    shift+F12看看字符串表,可以发现两串可疑的字符串

    可疑的字符串

    跟踪过去可以找到这样一个函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    void __fastcall sub_140001890(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
    {
    volatile signed __int32 *v5; // rax
    volatile signed __int32 *v6; // [rsp+80h] [rbp-1F8h] BYREF
    volatile signed __int32 *v7; // [rsp+88h] [rbp-1F0h]
    volatile signed __int32 *v8; // [rsp+90h] [rbp-1E8h] BYREF
    volatile signed __int32 *v9; // [rsp+98h] [rbp-1E0h]
    __int64 Block; // [rsp+A0h] [rbp-1D8h] BYREF
    const char *v11; // [rsp+A8h] [rbp-1D0h]
    volatile signed __int32 *v12[4]; // [rsp+B0h] [rbp-1C8h] BYREF
    volatile signed __int32 *v13[4]; // [rsp+D0h] [rbp-1A8h] BYREF
    volatile signed __int32 *v14[4]; // [rsp+F0h] [rbp-188h] BYREF
    volatile signed __int32 *v15; // [rsp+110h] [rbp-168h] BYREF
    volatile signed __int32 *v16[4]; // [rsp+130h] [rbp-148h] BYREF
    volatile signed __int32 *v17[2]; // [rsp+150h] [rbp-128h] BYREF
    volatile signed __int32 *v18; // [rsp+160h] [rbp-118h]
    __m128i v19[2]; // [rsp+170h] [rbp-108h] BYREF
    __m128i v20; // [rsp+190h] [rbp-E8h] BYREF
    volatile signed __int32 *v21[4]; // [rsp+1B0h] [rbp-C8h] BYREF
    volatile signed __int32 *v22[2]; // [rsp+1D0h] [rbp-A8h] BYREF
    volatile signed __int32 *v23; // [rsp+1E0h] [rbp-98h]
    volatile signed __int32 *v24[4]; // [rsp+1F0h] [rbp-88h] BYREF
    volatile signed __int32 *v25[13]; // [rsp+210h] [rbp-68h] BYREF

    ((void (__fastcall *)(void *, volatile signed __int32 **, _QWORD, volatile signed __int32 **))QLineEdit::text)(
    QLineEdit::text,
    v25,
    *(_QWORD *)(*(_QWORD *)(a4 + 40) + 16LL),
    v25);
    QString::trimmed_helper(QLineEdit::text, v25, v25, v12);
    if ( v25[0] && !_InterlockedSub(v25[0], 1u) )
    free(QLineEdit::text);
    ((void (__fastcall *)(void *, volatile signed __int32 **, _QWORD, volatile signed __int32 **))QLineEdit::text)(
    QLineEdit::text,
    v25,
    *(_QWORD *)(*(_QWORD *)(a4 + 40) + 24LL),
    v13);
    if ( v12[2] == (volatile signed __int32 *)4 )
    {
    QString::toUtf8_helper(QLineEdit::text, v25, v12, v14);
    Block = (__int64)v14[2];
    v11 = (const char *)v14[1];
    QCryptographicHash::hash(&Block, v25, &Block, &v15, 4LL);
    sub_140001620((__int64)&Block, (__int64)v25, a4, (__int64)v16);
    Block = 64LL;
    v11 = "c94201919ec7463313c747d0a27fabcabf1400fa1e9a64d36a6b1a7e7b12ae68";
    QString::fromUtf8(&Block, v25, &Block, v17);
    if ( v18 == v16[2]
    && (v8 = v18, v6 = v18, v9 = v16[1], v7 = v17[1], (unsigned __int8)QtPrivate::equalStrings(&Block, v25, &v6, &v8)) )
    {
    QString::toUtf8_helper(&Block, v25, v13, v19);
    sub_1400016F0((__int64)&Block, (__int64)v25, a4, &v20, v19, (__int64)v14);
    sub_140001620((__int64)&Block, (__int64)v25, a4, (__int64)v21);
    Block = 72LL;
    v11 = "af33da5e152c15863b3a03c87601899a37d51b8b8168f65aca65352d3669e91959300ccb";
    QString::fromUtf8(&Block, v25, &Block, v22);
    if ( v23 == v21[2]
    && (v8 = v23, v9 = v21[1],
    v6 = v23,
    v7 = v22[1],
    (unsigned __int8)QtPrivate::equalStrings(&Block, v25, &v6, &v8)) )
    {
    Block = 21LL;
    v11 = (const char *)&unk_1400061D8;
    QString::fromUtf8(&Block, v25, &Block, v25);
    Block = 12LL;
    v11 = (const char *)&unk_1400061EE;
    QString::fromUtf8(&Block, v25, &Block, v24);
    QMessageBox::information(&Block, v25, v24, a4, v25, 1024LL);
    }
    else
    {
    Block = 22LL;
    v11 = (const char *)&unk_1400061C1;
    QString::fromUtf8(&Block, v25, &Block, v25);
    Block = 12LL;
    v11 = (const char *)&unk_140006104;
    QString::fromUtf8(&Block, v25, &Block, v24);
    QMessageBox::warning(&Block, v25, v24, a4, v25, 1024LL);
    }
    if ( v24[0] && !_InterlockedSub(v24[0], 1u) )
    free(&Block);
    if ( v25[0] && !_InterlockedSub(v25[0], 1u) )
    free(&Block);
    if ( v22[0] && !_InterlockedSub(v22[0], 1u) )
    free(&Block);
    if ( v21[0] && !_InterlockedSub(v21[0], 1u) )
    free(&Block);
    if ( v20.m128i_i64[0] && !_InterlockedSub((volatile signed __int32 *)v20.m128i_i64[0], 1u) )
    free(&Block);
    if ( !v19[0].m128i_i64[0] || _InterlockedSub((volatile signed __int32 *)v19[0].m128i_i64[0], 1u) )
    goto LABEL_45;
    }
    else
    {
    Block = 25LL;
    v11 = (const char *)&unk_140006159;
    QString::fromUtf8(&Block, v25, &Block, v25);
    Block = 12LL;
    v11 = (const char *)&unk_140006104;
    QString::fromUtf8(&Block, v25, &Block, v24);
    QMessageBox::warning(&Block, v25, v24, a4, v25, 1024LL);
    if ( v24[0] && !_InterlockedSub(v24[0], 1u) )
    free(&Block);
    if ( !v25[0] || _InterlockedSub(v25[0], 1u) )
    goto LABEL_45;
    }
    free(&Block);
    LABEL_45:
    if ( v17[0] && !_InterlockedSub(v17[0], 1u) )
    free(&Block);
    if ( v16[0] && !_InterlockedSub(v16[0], 1u) )
    free(&Block);
    if ( v15 && !_InterlockedSub(v15, 1u) )
    free(&Block);
    if ( v14[0] && !_InterlockedSub(v14[0], 1u) )
    free(&Block);
    goto LABEL_10;
    }
    Block = 35LL;
    v11 = (const char *)&unk_1400060E0;
    QString::fromUtf8(&Block, v25, &Block, v25);
    Block = 12LL;
    v11 = (const char *)&unk_140006104;
    QString::fromUtf8(&Block, v25, &Block, v24);
    QMessageBox::warning(&Block, v25, v24, a4, v25, 1024LL);
    if ( v24[0] && !_InterlockedSub(v24[0], 1u) )
    {
    free(&Block);
    v5 = v25[0];
    if ( !v25[0] )
    goto LABEL_10;
    }
    else
    {
    v5 = v25[0];
    if ( !v25[0] )
    goto LABEL_10;
    }
    if ( !_InterlockedSub(v5, 1u) )
    free(&Block);
    LABEL_10:
    if ( v13[0] && !_InterlockedSub(v13[0], 1u) )
    free(&Block);
    if ( v12[0] )
    {
    if ( !_InterlockedSub(v12[0], 1u) )
    free(&Block);
    }
    }

    程序的图形化界面是QT实现的,引入了QT的很多类,所以阅读难度还是有点大的。借助AI优化一下以及一点点动态调试辅助分析可以得到:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    void __fastcall check(__int64 a1, __int64 a2, __int64 a3, __int64 mainWindows)
    {
    volatile signed __int32 *v5; // rax
    volatile signed __int32 *v6; // [rsp+80h] [rbp-1F8h] BYREF
    volatile signed __int32 *v7; // [rsp+88h] [rbp-1F0h]
    volatile signed __int32 *v8; // [rsp+90h] [rbp-1E8h] BYREF
    volatile signed __int32 *v9; // [rsp+98h] [rbp-1E0h]
    __int64 Block; // [rsp+A0h] [rbp-1D8h] BYREF
    const char *stringData; // [rsp+A8h] [rbp-1D0h]
    volatile signed __int32 *usernameUtf8[4]; // [rsp+B0h] [rbp-1C8h] BYREF
    volatile signed __int32 *passwordUtf8[4]; // [rsp+D0h] [rbp-1A8h] BYREF
    volatile signed __int32 *usernameBytes[4]; // [rsp+F0h] [rbp-188h] BYREF
    volatile signed __int32 *usernameHash; // [rsp+110h] [rbp-168h] BYREF
    volatile signed __int32 *usernameHashHex[4]; // [rsp+130h] [rbp-148h] BYREF
    volatile signed __int32 *v17[2]; // [rsp+150h] [rbp-128h] BYREF
    volatile signed __int32 *expectedUserHashSize; // [rsp+160h] [rbp-118h]
    __m128i passwordBytes[2]; // [rsp+170h] [rbp-108h] BYREF
    __m128i v20; // [rsp+190h] [rbp-E8h] BYREF
    volatile signed __int32 *v21[4]; // [rsp+1B0h] [rbp-C8h] BYREF
    volatile signed __int32 *v22[2]; // [rsp+1D0h] [rbp-A8h] BYREF
    volatile signed __int32 *v23; // [rsp+1E0h] [rbp-98h]
    volatile signed __int32 *v24[4]; // [rsp+1F0h] [rbp-88h] BYREF
    volatile signed __int32 *tempBuffer[13]; // [rsp+210h] [rbp-68h] BYREF

    (QLineEdit::text)(QLineEdit::text, tempBuffer, *(*(mainWindows + 40) + 16LL), tempBuffer);
    QString::trimmed_helper(QLineEdit::text, tempBuffer, tempBuffer, usernameUtf8);
    if ( tempBuffer[0] && !_InterlockedSub(tempBuffer[0], 1u) )
    free(QLineEdit::text);
    (QLineEdit::text)(QLineEdit::text, tempBuffer, *(*(mainWindows + 40) + 24LL), passwordUtf8);
    if ( usernameUtf8[2] == 4 ) // 长度检验
    {
    QString::toUtf8_helper(QLineEdit::text, tempBuffer, usernameUtf8, usernameBytes);
    Block = usernameBytes[2];
    stringData = usernameBytes[1];
    QCryptographicHash::hash(&Block, tempBuffer, &Block, &usernameHash, 4LL);// 求输入的用户名hash
    toHex(&Block, tempBuffer, mainWindows, usernameHashHex);
    Block = 64LL;
    stringData = "c94201919ec7463313c747d0a27fabcabf1400fa1e9a64d36a6b1a7e7b12ae68";// 预设哈希值
    QString::fromUtf8(&Block, tempBuffer, &Block, v17);
    if ( expectedUserHashSize == usernameHashHex[2]
    && (v8 = expectedUserHashSize,
    v6 = expectedUserHashSize,
    v9 = usernameHashHex[1],
    v7 = v17[1],
    QtPrivate::equalStrings(&Block, tempBuffer, &v6, &v8)) )// 用户名检验
    {
    QString::toUtf8_helper(&Block, tempBuffer, passwordUtf8, passwordBytes);
    RC4(&Block, tempBuffer, mainWindows, &v20, passwordBytes, usernameBytes);
    toHex(&Block, tempBuffer, mainWindows, v21);
    Block = 72LL;
    stringData = "af33da5e152c15863b3a03c87601899a37d51b8b8168f65aca65352d3669e91959300ccb";
    QString::fromUtf8(&Block, tempBuffer, &Block, v22);
    if ( v23 == v21[2]
    && (v8 = v23, v9 = v21[1], v6 = v23, v7 = v22[1], QtPrivate::equalStrings(&Block, tempBuffer, &v6, &v8)) )// 验证通过
    {
    Block = 21LL;
    stringData = &unk_7FF7F2EB61D8;
    QString::fromUtf8(&Block, tempBuffer, &Block, tempBuffer);
    Block = 12LL;
    stringData = &unk_7FF7F2EB61EE;
    QString::fromUtf8(&Block, tempBuffer, &Block, v24);
    QMessageBox::information(&Block, tempBuffer, v24, mainWindows, tempBuffer, 1024LL);
    }
    else // 密码验证不通过
    {
    Block = 22LL;
    stringData = &unk_7FF7F2EB61C1;
    QString::fromUtf8(&Block, tempBuffer, &Block, tempBuffer);
    Block = 12LL;
    stringData = &unk_7FF7F2EB6104;
    QString::fromUtf8(&Block, tempBuffer, &Block, v24);
    QMessageBox::warning(&Block, tempBuffer, v24, mainWindows, tempBuffer, 1024LL);
    }
    if ( v24[0] && !_InterlockedSub(v24[0], 1u) )
    free(&Block);
    if ( tempBuffer[0] && !_InterlockedSub(tempBuffer[0], 1u) )
    free(&Block);
    if ( v22[0] && !_InterlockedSub(v22[0], 1u) )
    free(&Block);
    if ( v21[0] && !_InterlockedSub(v21[0], 1u) )
    free(&Block);
    if ( v20.m128i_i64[0] && !_InterlockedSub(v20.m128i_i64[0], 1u) )
    free(&Block);
    if ( !passwordBytes[0].m128i_i64[0] || _InterlockedSub(passwordBytes[0].m128i_i64[0], 1u) )
    goto LABEL_45;
    }
    else // 用户名验证不通过
    {
    Block = 25LL;
    stringData = &unk_7FF7F2EB6159;
    QString::fromUtf8(&Block, tempBuffer, &Block, tempBuffer);
    Block = 12LL;
    stringData = &unk_7FF7F2EB6104;
    QString::fromUtf8(&Block, tempBuffer, &Block, v24);
    QMessageBox::warning(&Block, tempBuffer, v24, mainWindows, tempBuffer, 1024LL);
    if ( v24[0] && !_InterlockedSub(v24[0], 1u) )
    free(&Block);
    if ( !tempBuffer[0] || _InterlockedSub(tempBuffer[0], 1u) )
    goto LABEL_45;
    }
    free(&Block);
    LABEL_45:
    if ( v17[0] && !_InterlockedSub(v17[0], 1u) )
    free(&Block);
    if ( usernameHashHex[0] && !_InterlockedSub(usernameHashHex[0], 1u) )
    free(&Block);
    if ( usernameHash && !_InterlockedSub(usernameHash, 1u) )
    free(&Block);
    if ( usernameBytes[0] && !_InterlockedSub(usernameBytes[0], 1u) )
    free(&Block);
    goto LABEL_10;
    }
    Block = 35LL;
    stringData = &unk_7FF7F2EB60E0;
    QString::fromUtf8(&Block, tempBuffer, &Block, tempBuffer);
    Block = 12LL;
    stringData = &unk_7FF7F2EB6104;
    QString::fromUtf8(&Block, tempBuffer, &Block, v24);
    QMessageBox::warning(&Block, tempBuffer, v24, mainWindows, tempBuffer, 1024LL);
    if ( v24[0] && !_InterlockedSub(v24[0], 1u) )
    {
    free(&Block);
    v5 = tempBuffer[0];
    if ( !tempBuffer[0] )
    goto LABEL_10;
    }
    else
    {
    v5 = tempBuffer[0];
    if ( !tempBuffer[0] )
    goto LABEL_10;
    }
    if ( !_InterlockedSub(v5, 1u) )
    free(&Block);
    LABEL_10:
    if ( passwordUtf8[0] && !_InterlockedSub(passwordUtf8[0], 1u) )
    free(&Block);
    if ( usernameUtf8[0] )
    {
    if ( !_InterlockedSub(usernameUtf8[0], 1u) )
    free(&Block);
    }
    }

    可以看到用户名采用的是哈希检验,密码采用的是RC4检验,密钥为用户名。

    密码检验套在用户名检验里,所以第一步先解出用户名。借助在线解密网站MD5 在線免費解密可以得到用户名”Kath”:

    sha256解密得到用户名

    接下来求解密码。RC4函数如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    __m128i *__fastcall RC4(
    __int64 a1,
    __int64 a2,
    __int64 a3,
    __m128i *a4,
    const __m128i *passwordBytes,
    __int64 usernameBytes)
    {
    volatile signed __int32 *v6; // rax
    __int64 v7; // rdx
    bool v8; // zf
    int keyLength; // r11d
    int *sBoxPtr; // r8
    __m128i initVal; // xmm0
    __m128i *sBoxInitPtr; // rax
    __m128i v15; // xmm2
    __m128i currentVal; // xmm1
    __int64 keyBytes; // rdi
    int i_ksa; // ecx
    int j_ksa; // r9d
    int v20; // eax
    int sBoxVal; // r10d
    __int64 sum; // kr00_8
    __int64 passwordIndex; // r12
    int j; // ebp
    __int64 i; // rdi
    int temp; // eax
    __int64 v27; // kr08_8
    char v28; // r14
    _DWORD S[256]; // [rsp+20h] [rbp-438h] BYREF
    char v31; // [rsp+420h] [rbp-38h] BYREF

    v6 = passwordBytes->m128i_i64[0];
    v7 = passwordBytes[1].m128i_i64[0];
    v8 = passwordBytes->m128i_i64[0] == 0;
    *a4 = _mm_loadu_si128(passwordBytes);
    a4[1].m128i_i64[0] = v7;
    if ( !v8 )
    _InterlockedAdd(v6, 1u);
    keyLength = *(usernameBytes + 16);
    sBoxPtr = S;
    initVal = _mm_load_si128(&xmmword_7FF7F2EB6200);
    sBoxInitPtr = S;
    v15 = _mm_load_si128(&xmmword_7FF7F2EB6210);
    do
    {
    currentVal = initVal;
    ++sBoxInitPtr;
    initVal = _mm_add_epi32(initVal, v15);
    sBoxInitPtr[-1] = currentVal;
    }
    while ( sBoxInitPtr != &v31 );
    keyBytes = *(usernameBytes + 8);
    i_ksa = 0;
    j_ksa = 0;
    do // RC4_KSA
    {
    v20 = i_ksa;
    sBoxVal = *sBoxPtr;
    ++i_ksa;
    ++sBoxPtr;
    sum = sBoxVal + j_ksa + *(keyBytes + v20 % keyLength);
    j_ksa = (HIBYTE(sum) + sum) - HIBYTE(HIDWORD(sum));// j = j % 256
    *(sBoxPtr - 1) = S[j_ksa];
    S[j_ksa] = sBoxVal;
    }
    while ( i_ksa != 256 );
    if ( passwordBytes[1].m128i_i64[0] > 0 ) // RC4_PRGA和RC4decrypt
    {
    passwordIndex = 0LL;
    j = 0;
    LODWORD(i) = 0;
    do
    {
    i = ((i + 1) % 256);
    temp = S[i];
    j = (temp + j) % 256;
    S[i] = S[j];
    S[j] = temp;
    v27 = S[i] + temp;
    v28 = *(passwordBytes->m128i_i64[1] + passwordIndex) ^ S[(HIBYTE(v27) + v27) - HIBYTE(HIDWORD(v27))];// v28 = passwordBytes[passwordIndex] ^ S[(S[i]+S[j])%256]
    if ( !a4->m128i_i64[0] || *a4->m128i_i64[0] > 1 )
    QByteArray::reallocData(i, passwordBytes, a4[1].m128i_i64[0], a4, 1LL, *initVal.m128i_i64);
    *(a4->m128i_i64[1] + passwordIndex++) = v28;
    }
    while ( passwordBytes[1].m128i_i64[0] > passwordIndex );
    }
    return a4;
    }

    可以看出是标准的RC4,直接复现加密函数即可。解密脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    def rc4_ksa(key):
    S = list(range(256))
    j = 0
    for i in range(256):
    j = (j + S[i] + key[i % len(key)]) % 256
    S[i], S[j] = S[j], S[i]
    return S

    def rc4_prga(S, n):
    i = j = 0
    keystream = []
    S = S[:] # copy
    for _ in range(n):
    i = (i + 1) % 256
    j = (j + S[i]) % 256
    S[i], S[j] = S[j], S[i]
    k = S[(S[i] + S[j]) % 256]
    keystream.append(k)
    return bytes(keystream)

    def rc4_decrypt(key, ciphertext):
    S = rc4_ksa(key)
    ks = rc4_prga(S, len(ciphertext))
    return bytes(c ^ k for c, k in zip(ciphertext, ks))

    key = b"Kath"
    cipher = "af33da5e152c15863b3a03c87601899a37d51b8b8168f65aca65352d3669e91959300ccb"

    # 转成bytes
    ciphertext = bytes.fromhex(cipher)

    # 解密
    plaintext = rc4_decrypt(key, ciphertext)

    # 输出
    print("Plaintext:", plaintext)

flag:0xGame{ce5e5621-3d6b-4429-b72a-957abf353390}

Calamity_Fortune

这题,哈哈,数据提取错了题目截止提交48分钟后我才发现这个问题解出flag😄🫠我永远都不会原谅我自己了😄哈哈哈哈

考点:函数重写、复杂加密算法逆向、动态调试绕过程序机制

  • 附件程序双击运行会提示如图

    双击提示

    然而,随便输一个数字的话会闪退

  • 程序用IDA打开,main函数反编译如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    int __fastcall main(int argc, const char **argv, const char **envp)
    {
    unsigned int v3; // eax
    int v4; // ebx
    int v5; // eax
    int v6; // ecx
    __int64 v7; // rbx
    char v8; // cl
    int v10; // [rsp+20h] [rbp-58h] BYREF
    char v11[9]; // [rsp+27h] [rbp-51h] BYREF
    _QWORD v12[5]; // [rsp+30h] [rbp-48h]
    int v13; // [rsp+58h] [rbp-20h]
    __int16 v14; // [rsp+5Ch] [rbp-1Ch]

    sub_401600(argc, argv, envp);
    SetConsoleOutputCP(0xFDE9u);
    SetConsoleCP(0xFDE9u);
    v3 = time64(0LL);
    srand(v3);
    v10 = 0;
    v4 = rand();
    puts(&Buffer);
    v5 = scanf("%d", &v10);
    v6 = 1;
    if ( v5 == 1 )
    {
    if ( v10 == v4 % 100 + 1 )
    {
    MessageBoxA(0LL, "You guessed right! Is it really right?", "Result", 0);
    return 0;
    }
    else
    {
    v7 = 0LL;
    puts("You guessed it wrong!");
    puts(&byte_405098);
    v12[0] = 0xD0F0C00002B1973LL;
    v12[1] = 0x182B1A043E1F082BLL;
    v12[2] = 0x151236080A0D071CLL;
    v12[3] = 0xA150C0111330622LL;
    v12[4] = 0xD2B190804073E26LL;
    v13 = 251992113;
    v14 = 5130;
    strcpy(v11, "Calamity");
    do
    {
    v8 = *((_BYTE *)v12 + v7) ^ v11[v7 & 7];
    ++v7;
    putchar(v8);
    }
    while ( v7 != 46 );
    return 0;
    }
    }
    return v6;
    }
  • v12看着像密文,的确是密文,但不是flag的密文,因为它在输入错误的提示里。暂时没有发现flag检验函数,尝试先绕过数字检验

    在汇编窗口找到数字判断语句(可以在反编译界面选中判断语句所在行,右键Jump to Disasm到达)

    判断语句的汇编

    可以看到这是一个jz,只需要在执行到这句但是还没步过时,修改ZF标志位即可绕过。在此处F2下断点,开始动调程序。随便输个数字,按回车

    随便输个数字

    此时程序停在jz处,即我们下断点的地方

    停在jz处

    在右上侧寄存器窗口找到ZF

    找到ZF

    双击ZF把它的值改成0x1

    修改ZF标志位

    可以看到jz语句处有一条箭头出来了

    绿色箭头

    F8执行jz跳转,跟踪程序执行流。可以看到程序运行到一个类似输出函数的地方

    类似输出函数的地方

    这时候,在这里多试几次会发现MessageBoxA可以步入。当然要是步过这个call MessageBoxA会发现程序没有弹出窗口,倒是终端输出了新的句子,十分可疑

    终端的新输出

    所以在程序执行到call cs:MessageBoxA这句的时候按F7步入,就会发现这个MessageBoxA别有洞天

    别有洞天

    这显然不像是MessageBoxA应该有的操作。可以看到一个可疑的地址被放到了rax里,跟踪这个jmp看看(jmp rax处F7步入)

    jmp后

    可以看到程序运行到了一个新函数,F5反编译,反编译代码如下(函数有多个模块的加密处理,还是很复杂的,以下是经过变量名优化的代码)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    __int64 realMain()
    {
    char v0; // dl
    char *v1; // rax
    char v2; // dl
    char *i; // rax
    int v4; // eax
    unsigned __int8 *pFlag1; // r9
    _BYTE *pFlag; // rdx
    unsigned int v7; // esi
    char v8; // dl
    char *j; // rax
    unsigned int *p_block0; // r10
    unsigned __int8 *current_1; // rdx
    int shift; // ecx
    unsigned int temp_2; // r8d
    int temp_1; // eax
    unsigned int cureent_block; // eax
    unsigned int delta; // r11d
    int key_value; // ebp
    unsigned int prev_block; // r9d
    unsigned int *block; // r8
    int rounds; // ecx
    unsigned int next; // edx
    unsigned int oriDelta; // ecx
    unsigned __int8 *encrypted_bytes_1; // r10
    unsigned int *v24; // rdi
    _DWORD *v25; // r8
    _DWORD *v26; // rax
    int shift_1; // ecx
    unsigned int v28; // edx
    _BYTE *v29; // r9
    unsigned int v30; // r8d
    unsigned __int64 k; // rax
    char v32; // dl
    _BYTE *current1; // rbx
    int randNum; // eax
    char tmp; // r8
    _BYTE *target; // rdx
    unsigned __int8 *v37; // rdx
    unsigned __int8 *end; // rbx
    unsigned __int8 *current; // rax
    char *v40; // rcx
    int m; // eax
    char *v42; // rax
    char n; // dl
    char v44; // dl
    char *ii; // rax
    _QWORD key[2]; // [rsp+20h] [rbp-218h] BYREF
    char notice2[22]; // [rsp+30h] [rbp-208h] BYREF
    __int16 v49; // [rsp+46h] [rbp-1F2h] BYREF
    unsigned int block0; // [rsp+50h] [rbp-1E8h] BYREF
    int v51; // [rsp+54h] [rbp-1E4h] BYREF
    unsigned int block10; // [rsp+78h] [rbp-1C0h]
    _BYTE encrypted_bytes[44]; // [rsp+80h] [rbp-1B8h] BYREF
    int v54; // [rsp+ACh] [rbp-18Ch] BYREF
    _BYTE flag[44]; // [rsp+B0h] [rbp-188h] BYREF
    char v56; // [rsp+DCh] [rbp-15Ch] BYREF
    _QWORD enc[7]; // [rsp+E0h] [rbp-158h] BYREF
    int v58; // [rsp+118h] [rbp-120h]
    _BYTE base64_output[59]; // [rsp+120h] [rbp-118h] BYREF
    _BYTE end_1[5]; // [rsp+15Bh] [rbp-DDh] BYREF
    char temp[64]; // [rsp+160h] [rbp-D8h] BYREF
    char notice1[85]; // [rsp+1A0h] [rbp-98h] BYREF
    _BYTE v63[3]; // [rsp+1F5h] [rbp-43h] BYREF

    v0 = 79;
    enc[0] = 0x280D30732B077874LL;
    enc[1] = 0x242D00103573060BLL;
    enc[2] = 0x141C3406727D2F73LL;
    enc[3] = 0xA71137676362833LL;
    enc[4] = 0xE232B242F04742ALL;
    enc[5] = 0x2F373F03033D7310LL;
    enc[6] = 0x77067C3612772D7DLL;
    qmemcpy(key, "Calamity_Fortune", sizeof(key));
    qmemcpy(
    notice1,
    "Ofqni`'jfcb'ns'sont'afu+'sob'tretbvrbis'bidu~wsnhi'tohrkc'eb'f'wnbdb'ha'dflb'ahu'~hry",
    sizeof(notice1));
    v1 = notice1;
    v58 = 0x37233104;
    while ( 1 )
    {
    *v1++ = v0 ^ 7;
    if ( v63 == v1 )
    break;
    v0 = *v1;
    }
    puts(notice1); // Having made it this far,......
    v2 = 87;
    qmemcpy(notice2, "Wkbftb'Niwrs'~hru'akf`", sizeof(notice2));
    for ( i = notice2; ; v2 = *i )
    {
    *i++ = v2 ^ 7;
    if ( i == &v49 )
    break;
    }
    puts(notice2); // Please input your flag
    scanf("%44s", flag);
    v4 = flag[0];
    v56 = 0;
    if ( !flag[0] )
    goto LABEL_11;
    pFlag1 = flag;
    pFlag = flag;
    do
    v7 = 1 - flag + pFlag++;
    while ( *pFlag );
    if ( v7 != 44 )
    {
    LABEL_11: // 错误时的输出
    v8 = 75;
    qmemcpy(temp, "Kbi`so'Buuhu&", 13);
    for ( j = temp; ; v8 = *j )
    {
    *j++ = v8 ^ 7;
    if ( &temp[13] == j )
    break;
    }
    puts(temp); // 错误时的输出
    exit(0);
    }
    p_block0 = &block0;
    while ( 1 ) // bytesToBlock,转成uint32_t
    {
    current_1 = pFlag1;
    shift = 0;
    temp_2 = 0;
    while ( 1 )
    {
    temp_1 = v4 << shift;
    shift += 8;
    ++current_1;
    temp_2 |= temp_1;
    if ( shift == 32 )
    break;
    v4 = *current_1;
    }
    pFlag1 += 4;
    *p_block0++ = temp_2;
    if ( &v56 == pFlag1 )
    break;
    v4 = *pFlag1;
    }
    cureent_block = block10;
    delta = 0x9E3779B9;
    key_value = 0x616C6143;
    prev_block = block0;
    while ( 1 ) // xxtea
    {
    block = &block0;
    for ( rounds = 0; ; ++rounds )
    {
    next = block[1];
    ++block;
    cureent_block = prev_block
    + (((cureent_block ^ *(key + (((delta >> 2) ^ rounds) & 3))) + (next ^ delta)) ^ (((4 * next) ^ (cureent_block >> 5)) + ((16 * cureent_block) ^ (next >> 3))));
    *(block - 1) = cureent_block;
    if ( rounds == 9 )
    break;
    prev_block = *block;
    }
    prev_block = block0;
    oriDelta = delta;
    delta -= 0x61C88647;
    cureent_block = block10
    + ((((cureent_block >> 5) ^ (4 * block0)) + ((16 * cureent_block) ^ (block0 >> 3))) ^ ((block0 ^ oriDelta) + (key_value ^ cureent_block)));
    block10 = cureent_block;
    if ( delta == 0xCC623AF3 )
    break;
    key_value = *(key + (((delta >> 2) ^ 0xA) & 3));
    }
    encrypted_bytes_1 = encrypted_bytes;
    v24 = &v51; // blockToBytes,uint32_t转成bytes
    v25 = encrypted_bytes;
    while ( 1 )
    {
    v26 = v25;
    for ( shift_1 = 0; shift_1 != 32; shift_1 += 8 )
    {
    v26 = (v26 + 1);
    v28 = prev_block >> shift_1;
    *(v26 - 1) = v28;
    }
    if ( &v54 == ++v25 )
    break;
    prev_block = *v24++;
    }
    qmemcpy(temp, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", sizeof(temp));
    v29 = base64_output;
    do // base64encode
    {
    v30 = *encrypted_bytes_1 << 16;
    if ( v7 != 1 )
    {
    v30 |= encrypted_bytes_1[1] << 8;
    if ( v7 > 2 )
    v30 |= encrypted_bytes_1[2];
    }
    for ( k = 0LL; k != 4; ++k )
    {
    v32 = 61;
    if ( k <= v7 )
    v32 = temp[(v30 >> (-6 * k + 18)) & 0x3F];
    v29[k] = v32;
    }
    encrypted_bytes_1 += 3;
    v29 += 4;
    v7 -= 3;
    }
    while ( (&v54 + 1) != encrypted_bytes_1 );
    current1 = end_1;
    end_1[1] = 0;
    srand(0x65u); // shuffle
    while ( 1 )
    {
    randNum = rand();
    tmp = *current1;
    target = &base64_output[randNum % (60 - end_1 + current1)];
    *current1 = *target;
    *target = tmp;
    v37 = current1 - 1;
    if ( base64_output == current1 - 1 )
    break;
    --current1;
    }
    end = current1 + 59;
    current = v37;
    do // xor
    *current++ ^= 0x45u;
    while ( end != current );
    v40 = enc + 1;
    for ( m = 116; ; m = *v40++ )
    {
    if ( *v37 != m ) // 错误处理的输出
    {
    qmemcpy(temp, "Dfkfjns~&'wkbftb'su~'f`fni", 26);
    v42 = temp;
    for ( n = 68; ; n = *v42 )
    {
    *v42++ = n ^ 7;
    if ( &temp[26] == v42 )
    break;
    }
    puts(temp);
    exit(0);
    } // 错误处理的输出
    if ( end == ++v37 )
    break;
    }
    v44 = 65;
    qmemcpy(temp, "Ahusrib&@hhc'Krdl&", 18);
    for ( ii = temp; ; v44 = *ii )
    {
    *ii++ = v44 ^ 7;
    if ( &temp[18] == ii )
    break;
    }
    puts(temp);
    return 1LL;
    }

    函数出现在程序输入flag的提示之后,应该就是flag检验函数了。函数使用了很多加密手段,甚至连提示词都是实时解密的。可以看到对输入的flag依次进行了格式转换、魔改XXTEA加密、格式转换、base64编码、shuffle洗牌算法打乱、异或处理,最后才和预设的密文进行比较检验。

    这里给出一个AI复现的、经过测试等价的脚本,方便理解这个复杂的算法(被函数改写整怕了,所以随机数生成算法也复现了):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    #include <stdio.h>
    #include <stdint.h>
    #include <string.h>

    // MSVC-compatible rand
    static uint32_t rand_seed;
    void custom_srand(uint32_t seed) { rand_seed = seed; }
    uint32_t custom_rand() {
    rand_seed = rand_seed * 214013 + 2531011;
    return (rand_seed >> 16) & 0x7FFF;
    }

    // XXTEA encryption (as in realMain)
    void encrypt_xxtea_variant(uint32_t block[11], const uint32_t key[4]) {
    uint32_t cureent_block = block[10];
    uint32_t delta = 0x9E3779B9U;
    uint32_t key_value = 0x616C6143U; // 初始值,但第一轮内循环不用它
    uint32_t prev_block = block[0];

    while (1) {
    uint32_t* bptr = &block[0];
    for (int rounds = 0; ; ++rounds) {
    uint32_t next = bptr[1];
    ++bptr;
    uint32_t k = key[((delta >> 2) ^ rounds) & 3];
    uint32_t term1 = (cureent_block ^ k) + (next ^ delta);
    uint32_t term2 = (4 * next ^ (cureent_block >> 5)) + (16 * cureent_block ^ (next >> 3));
    cureent_block = prev_block + (term1 ^ term2);
    *(bptr - 1) = cureent_block;
    if (rounds == 9) break;
    prev_block = *bptr; // 此时 *bptr 尚未被修改(是原始值)
    }

    prev_block = block[0];
    uint32_t oriDelta = delta;
    delta -= 0x61C88647U;
    key_value = key[((oriDelta >> 2) ^ 0xA) & 3];
    uint32_t term1 = ((cureent_block >> 5) ^ (4 * block[0])) + ((16 * cureent_block) ^ (block[0] >> 3));
    uint32_t term2 = (block[0] ^ oriDelta) + (key_value ^ cureent_block);
    cureent_block = block[10] + (term1 ^ term2);
    block[10] = cureent_block;

    if (delta == 0xCC623AF3U) break;
    }
    }

    // Base64 encode 44 bytes -> 60 chars
    void base64_encode(const uint8_t* input, char* output) {
    const char* table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    for (int i = 0; i < 44; i += 3) {
    uint32_t val = (input[i] << 16) |
    ((i + 1 < 44 ? input[i + 1] : 0) << 8) |
    ((i + 2 < 44 ? input[i + 2] : 0));
    output[0] = table[(val >> 18) & 0x3F];
    output[1] = table[(val >> 12) & 0x3F];
    output[2] = (i + 1 < 44) ? table[(val >> 6) & 0x3F] : '=';
    output[3] = (i + 2 < 44) ? table[val & 0x3F] : '=';
    output += 4;
    }
    }

    int main() {
    // Step 1: Input flag
    char flag[45] = "0xGame{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}";//测试样例
    printf("Input flag: %s\n", flag);

    // Step 2: Convert to 11 uint32_t (little-endian)
    uint32_t blocks[11];
    for (int i = 0; i < 11; i++) {
    blocks[i] = (uint8_t)flag[i * 4] |
    ((uint8_t)flag[i * 4 + 1] << 8) |
    ((uint8_t)flag[i * 4 + 2] << 16) |
    ((uint8_t)flag[i * 4 + 3] << 24);
    }

    // Step 3: XXTEA encrypt
    uint32_t key[4] = {
    0x616C6143, // 'C','a','l','a'
    0x7974696D, // 'm','i','t','y'
    0x726F465F, // '_','F','o','r'
    0x656E7574 // 't','u','n','e'
    };
    encrypt_xxtea_variant(blocks, key);
    printf("TEA encrypted blocks (hex, little-endian order):\n");
    for (int i = 0; i < 11; i++) {
    printf("%08x\n", blocks[i]);
    }
    printf("\n");
    // Step 4: Convert back to bytes (little-endian)
    uint8_t encrypted_bytes[44];
    for (int i = 0; i < 11; i++) {
    encrypted_bytes[i * 4] = blocks[i] & 0xFF;
    encrypted_bytes[i * 4 + 1] = (blocks[i] >> 8) & 0xFF;
    encrypted_bytes[i * 4 + 2] = (blocks[i] >> 16) & 0xFF;
    encrypted_bytes[i * 4 + 3] = (blocks[i] >> 24) & 0xFF;
    }

    // Step 5: Base64 encode
    char base64_str[61] = { 0 };
    base64_encode(encrypted_bytes, base64_str);
    printf("Base64: %s\n", base64_str);

    // Step 6: Shuffle (Fisher-Yates, i=59 downto 1)
    custom_srand(0x65);
    for (int i = 59; i >= 1; i--) {
    int j = custom_rand() % (i + 1);
    // Swap base64_str[i] and base64_str[j]
    char tmp = base64_str[i];
    base64_str[i] = base64_str[j];
    base64_str[j] = tmp;
    }

    // Output shuffled result (before XOR)
    printf("After shuffle (before XOR): %s\n", base64_str);
    printf("Hex dump:\n");
    for (int i = 0; i < 60; i++) {
    printf("%02x ", (uint8_t)base64_str[i]);
    if ((i + 1) % 16 == 0) printf("\n");
    }
    if (60 % 16 != 0) printf("\n");
    printf("\n");
    for (int i = 0; i < 60; i++) {
    printf("%02x ", (uint8_t)base64_str[i] ^ 0x45);
    }
    printf("\n");
    return 0;
    }

    解密思路:异或模块直接一模一样地重复一遍即可;shuffle模块可以先从加密算法获得随机数序列,这样方便从后往前索引还原;剩下的看脚本吧🫠(对了,要注意enc数据要从栈上提取,笔者让AI从反编译的代码里提取,肉眼检验没发现bytes截取错了,浪费了好多时间)

  • 解密脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    #include <stdio.h>
    #include <stdint.h>
    #include <string.h>

    // ========== 你提供的解密函数 ==========
    void decrypt_xxtea_variant(uint32_t block[11], const uint32_t key[4]) {
    const int TOTAL_ROUNDS = 10;
    uint32_t delta = 0xCC623AF3U;

    for (int round = 0; round < TOTAL_ROUNDS; round++) {
    uint32_t oriDelta = delta + 0x61C88647U;
    uint32_t key_value = key[((oriDelta >> 2) ^ 0xA) & 3];

    uint32_t C9 = block[9];
    uint32_t C0 = block[0];
    uint32_t term1 = ((C9 >> 5) ^ (4 * C0)) + ((16 * C9) ^ (C0 >> 3));
    uint32_t term2 = (C0 ^ oriDelta) + (key_value ^ C9);
    uint32_t G = term1 ^ term2;
    uint32_t original_block10 = block[10] - G;

    uint32_t saved_block10 = original_block10;

    for (int i = 9; i >= 0; i--) {
    uint32_t next = (i == 9) ? saved_block10 : block[i + 1];
    uint32_t prev_C = (i == 0) ? saved_block10 : block[i - 1];

    uint32_t k = key[((oriDelta >> 2) ^ i) & 3];
    uint32_t part1 = (prev_C ^ k) + (next ^ oriDelta);
    uint32_t part2 = (4 * next ^ (prev_C >> 5)) + (16 * prev_C ^ (next >> 3));
    uint32_t H = part1 ^ part2;

    block[i] = block[i] - H;
    }

    block[10] = original_block10;
    delta += 0x61C88647U;
    }
    }

    const char* table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int base64_decode(const char* input, uint8_t* output) {
    int out_len = 0;
    for (int i = 0; i < 60; i += 4) {
    uint32_t val = 0;
    for (int j = 0; j < 4; j++) {
    const char* p = strchr(table, input[i + j]);
    if (p) val = (val << 6) | (p - table);
    else val <<= 6;
    }
    output[out_len++] = (val >> 16) & 0xFF;
    if (input[i + 2] != '=') output[out_len++] = (val >> 8) & 0xFF;
    if (input[i + 3] != '=') output[out_len++] = val & 0xFF;
    }
    return out_len;
    }

    void deshuffle(char* cipher, uint32_t* rand_seq) {
    // 逆序遍历:t 从 58 到 0
    for (int t = 58; t >= 0; t--) {
    int i = 59 - t; // 加密时的 i
    int j = rand_seq[t]; // 加密时的 j
    // 再次交换 cipher[i] 和 cipher[j]
    char temp = cipher[i];
    cipher[i] = cipher[j];
    cipher[j] = temp;
    }
    }
    // ========== 主程序 ==========
    int main() {
    // Step 1: 使用你提供的 60 字节目标密文
    uint8_t encrypted_target_bytes[60] = {
    0x74, 0x78, 0x07, 0x2B, 0x73, 0x30, 0x0D, 0x28,
    0x0B, 0x06, 0x73, 0x35, 0x10, 0x00, 0x2D, 0x24,
    0x73, 0x2F, 0x7D, 0x72, 0x06, 0x34, 0x1C, 0x14,
    0x33, 0x28, 0x36, 0x76, 0x76, 0x13, 0x71, 0x0A,
    0x2A, 0x74, 0x4, 0x2f, 0x24, 0x2b, 0x23, 0xe,
    0x10, 0x73, 0x3D, 0x03, 0x03, 0x3F, 0x37, 0x2F,
    0x7D, 0x2D, 0x77, 0x12, 0x36, 0x7C, 0x06, 0x77,
    0x04, 0x31, 0x23, 0x37
    };

    // Step 2: XOR 0x45 得到 shuffled base64 字符串
    char shuffled_b64[60] = { 0 };
    //printf("step1: xor\n");
    for (int i = 0; i < 60; i++) {
    shuffled_b64[i] = encrypted_target_bytes[i] ^ 0x45;
    //printf("%c", shuffled_b64[i]);
    }
    //printf("\nstep2: deshuffle\n");
    // Step 3: 使用你提供的 rand() 序列(原始值,未取模)
    uint32_t rand_seq[59] = {
    8, 46, 21, 19, 35, 1, 1, 7, 37, 17,
    33, 13, 17, 10, 34, 34, 6, 20, 35, 10,
    22, 21, 7, 16, 6, 18, 30, 32, 6, 2,
    9, 0, 7, 4, 23, 18, 18, 16, 15, 15,
    6, 15, 16, 3, 13, 13, 9, 2, 11, 10,
    3, 2, 0, 1, 3, 0, 3, 0, 0
    };
    deshuffle(shuffled_b64, rand_seq);
    //for (int i = 0; i < 60; i++) {
    //printf("%c", shuffled_b64[i]);
    //}
    char original_b64[61] = { 0 };
    for (int i = 0; i < 60; i++) {
    original_b64[i] = shuffled_b64[i];
    }
    original_b64[60] = '\0';
    //printf("\n");

    // Step 4: Base64 解码
    uint8_t encrypted_bytes[44];
    int decoded_len = base64_decode(original_b64, encrypted_bytes);
    if (decoded_len != 44) {
    return 1;
    }

    // Step 5: 重组为 11 个 uint32_t(小端序)
    uint32_t block[11];
    for (int i = 0; i < 11; i++) {
    block[i] = ((uint32_t)encrypted_bytes[i * 4 + 0] << 0) |
    ((uint32_t)encrypted_bytes[i * 4 + 1] << 8) |
    ((uint32_t)encrypted_bytes[i * 4 + 2] << 16) |
    ((uint32_t)encrypted_bytes[i * 4 + 3] << 24);
    }

    // Step 6: 设置密钥 "Calamity_Fortune"(16 字节)
    uint32_t key[4] = {
    0x616C6143U,
    0x7974696DU,
    0x726F465FU,
    0x656E7574U
    };

    // Step 7: 调用你提供的解密函数
    decrypt_xxtea_variant(block, key);

    // Step 8: 转回 flag 字符串(小端)
    char flag[45] = { 0 };
    for (int i = 0; i < 11; i++) {
    flag[i * 4 + 0] = (block[i] >> 0) & 0xFF;
    flag[i * 4 + 1] = (block[i] >> 8) & 0xFF;
    flag[i * 4 + 2] = (block[i] >> 16) & 0xFF;
    flag[i * 4 + 3] = (block[i] >> 24) & 0xFF;
    }

    printf("Flag: %s\n", flag);
    return 0;
    }

flag:0xGame{f279c1e7-8b0d-4a3b-9c6f-5e4d2a1b0c89}

作者

SydzI

发布于

2025-10-28

更新于

2025-11-16

许可协议

评论