0xGame2025Week4WriteUp

0xGame2025 Week4 Reverse方向全解&详解

镜渊折枝

考点:native层逆向,AES

  • 附件apk运行起来是这样:

    运行附件apk

  • JEB打开,可以得到下面这些MainActivity和相关函数的内容:

    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
    package com.example.easynative;

    import android.content.Context;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;
    import androidx.activity.ComponentActivity;
    import java.io.InputStream;
    import kotlin.Metadata;
    import kotlin.io.ByteStreamsKt;
    import kotlin.jvm.internal.Intrinsics;

    @Metadata(d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\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\u0014¨\u0006\u0007"}, d2 = {"Lcom/example/easynative/MainActivity;", "Landroidx/activity/ComponentActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "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));
    }

    private static final void onCreate$lambda$0(EditText arg2, MainActivity arg3, View arg4) {
    Intrinsics.checkNotNullParameter(arg3, "this$0");
    String v2 = arg2.getText().toString();
    InputStream v4 = arg3.getAssets().open("secret.bin");
    Intrinsics.checkNotNullExpressionValue(v4, "open(...)");
    byte[] v4_1 = ByteStreamsKt.readBytes(v4);
    if(((CharSequence)v2).length() > 0) {
    Context v1 = (Context)arg3;
    if(new FlagChecker(v1, v2).check(v4_1)) { // v4从secret.bin中被读取出来,读取的内容给了v4_1作为check的参数,输入的内容作为FlagChecker的第二个参数
    Toast.makeText(v1, ((CharSequence)arg3.getString(string.correct)), 0).show();
    return;
    }

    Toast.makeText(v1, ((CharSequence)arg3.getString(string.wrong)), 0).show();
    }
    }
    }

    package com.example.easynative;

    import android.content.Context;
    import java.security.Key;
    import java.security.spec.AlgorithmParameterSpec;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import kotlin.Metadata;
    import kotlin.jvm.internal.DefaultConstructorMarker;
    import kotlin.jvm.internal.Intrinsics;
    import kotlin.text.Charsets;

    @Metadata(d1 = {"\u0000.\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000E\n\u0002\b\u0002\n\u0002\u0010\u000B\n\u0000\n\u0002\u0010\u0012\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0005\b\u0007\u0018\u0000 \u00112\u00020\u0001:\u0001\u0011B\u0015\u0012\u0006\u0010\u0002\u001A\u00020\u0003\u0012\u0006\u0010\u0004\u001A\u00020\u0005¢\u0006\u0002\u0010\u0006J\u000E\u0010\u0007\u001A\u00020\b2\u0006\u0010\t\u001A\u00020\nJ\u0019\u0010\u000B\u001A\u00020\b2\u0006\u0010\f\u001A\u00020\r2\u0006\u0010\u000E\u001A\u00020\nH\u0086 J\u001E\u0010\u000F\u001A\u00020\n2\u0006\u0010\u0004\u001A\u00020\u00052\u0006\u0010\u0010\u001A\u00020\n2\u0006\u0010\t\u001A\u00020\nR\u000E\u0010\u0002\u001A\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000E\u0010\u0004\u001A\u00020\u0005X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0012"}, d2 = {"Lcom/example/easynative/FlagChecker;", "", "context", "Landroid/content/Context;", "plainText", "", "(Landroid/content/Context;Ljava/lang/String;)V", "check", "", "iv", "", "compare", "length", "", "data", "encrypt", "key", "Companion", "app_release"}, k = 1, mv = {1, 9, 0}, xi = 0x30)
    public final class FlagChecker {
    @Metadata(d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002¨\u0006\u0003"}, d2 = {"Lcom/example/easynative/FlagChecker$Companion;", "", "()V", "app_release"}, k = 1, mv = {1, 9, 0}, xi = 0x30)
    public static final class Companion {
    private Companion() {
    }

    public Companion(DefaultConstructorMarker arg1) {
    }
    }

    public static final int $stable;
    public static final Companion Companion;
    private final Context context;
    private final String plainText;

    static {
    FlagChecker.Companion = new Companion(null);
    FlagChecker.$stable = 8;
    System.loadLibrary("easynative");
    }

    public FlagChecker(Context arg2, String arg3) {
    Intrinsics.checkNotNullParameter(arg2, "context");
    Intrinsics.checkNotNullParameter(arg3, "plainText");
    super();
    this.context = arg2;
    this.plainText = arg3; // 输入的内容被赋值给plaintext
    }

    public final boolean check(byte[] arg4) { // 参数是从secret.bin读取的内容
    Intrinsics.checkNotNullParameter(((Object)arg4), "iv"); // arg4即从secret.bin读取的内容被标识为iv
    String v0 = this.context.getString(string.key); // v0为string.key
    Intrinsics.checkNotNullExpressionValue(v0, "getString(...)");
    String v1 = this.plainText; // v1是输入的内容plaintext
    byte[] v0_1 = v0.getBytes(Charsets.UTF_8); // v0读取为bytes
    Intrinsics.checkNotNullExpressionValue(((Object)v0_1), "this as java.lang.String).getBytes(charset)");
    byte[] v4 = this.encrypt(v1, v0_1, arg4); // 三个参数分别为:输入的plaintext,string.key,从secret.bin读取的iv
    return this.compare(v4.length, v4); // compare从native层easynative.so导入
    }

    public final native boolean compare(int arg1, byte[] arg2) {
    }

    public final byte[] encrypt(String arg3, byte[] arg4, byte[] arg5) {
    Intrinsics.checkNotNullParameter(arg3, "plainText"); // 此处再次对参数进行了标识
    Intrinsics.checkNotNullParameter(((Object)arg4), "key");
    Intrinsics.checkNotNullParameter(((Object)arg5), "iv");
    SecretKeySpec v0 = new SecretKeySpec(arg4, "AES"); // 加密算法AES,CTR模式
    Cipher v4 = Cipher.getInstance("AES/CTR/NoPadding");
    v4.init(1, ((Key)v0), ((AlgorithmParameterSpec)new IvParameterSpec(arg5)));
    byte[] v3 = arg3.getBytes(Charsets.UTF_8);
    Intrinsics.checkNotNullExpressionValue(((Object)v3), "this as java.lang.String).getBytes(charset)");
    byte[] v3_1 = v4.doFinal(v3);
    Intrinsics.checkNotNull(((Object)v3_1));
    return v3_1;
    }
    }

    可以看到Mainactivity使用了FlagChecker来校验,而FlagChecker对输入的内容进行了AES加密,然后用native层的compare函数来检验加密结果。下面是native层compare函数的内容(IDA中显示的函数名是check而非compare):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    __int64 __fastcall check(JNIEnv *a1, __int64 a2, int a3, void *a4)
    {
    __int64 v5; // x0
    __int64 i; // x9
    int v7; // w11
    int v8; // w10

    v5 = (__int64)(*a1)->GetByteArrayElements(a1, a4, 0LL);
    if ( a3 != 48 )
    return 0LL;
    for ( i = 0LL; i != 48; ++i )
    {
    v7 = (unsigned __int8)byte_5A2[i];
    v8 = *(unsigned __int8 *)(v5 + i) ^ (unsigned __int8)i;
    if ( v8 != v7 )
    return 0LL;
    }
    return 1LL;
    }

    可以看到compare函数是先拿AES加密结果异或索引后才进行校验的,所以解密应该拿compare中的密文和索引异或得到AES加密的数据,然后找出key和iv进行AES解密。

    脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from Crypto.Cipher import AES

    enc=[0x38, 0xEA, 0x9A, 0x5A, 0x3C, 0xFB, 0x0C, 0x98, 0x1A, 0x66, 0x8B, 0x46, 0x6A, 0x06, 0x19, 0x08, 0x41, 0xCB, 0x36, 0x9E, 0x16, 0xC5, 0xBE, 0x2F, 0xF0, 0x9D, 0xBF, 0xA7, 0x34, 0x67, 0x51, 0x4C, 0xE5, 0xC2, 0x78, 0xA5, 0x0F, 0x35, 0xFC, 0x1D, 0x39, 0x8B, 0x49, 0x38, 0x34, 0x69, 0x30, 0xF7]
    #先对数据进行异或还原
    for i in range(len(enc)):
    enc[i]=enc[i]^i
    #提取AES相关参数
    key=b"0xgame-SecretKey"
    iv=bytes.fromhex("8A A5 61 0C 74 41 1B 11 A3 53 68 DE 56 BF 6A B3")
    #AES解密
    cipher=AES.new(key, AES.MODE_CTR, nonce=b'', initial_value=iv)
    flag=cipher.decrypt(bytes(enc))
    print("flag: ",flag.decode())

flag:0xGame{Native_1s_also_1nt3r3st1ng_ef492d80a257c}

夜雀之歌

考点:迷宫题,动态调试,反调试

  • 附件程序运行起来是这样的:

    运行

    用IDA打开,通过Shift+F12字符串窗口可以找到一些信息定位main函数的位置

    字符串窗口的信息

    main函数有一大串的循环语句,结合上图终端输出的WASD可以推测这是一道迷宫逆向题。#是迷宫的墙,终端显示的是迷宫的一小部分,在终端按下WASD可以控制P移动。所以第一步要提取迷宫

    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
    //main函数
    __int64 sub_7FF6645A1D2B()
    {
    int v0; // eax
    int v1; // eax
    int v2; // eax
    int v3; // eax
    int v5; // [rsp+28h] [rbp-8h]
    int v6; // [rsp+2Ch] [rbp-4h]

    sub_7FF7EC581FC0();
    sub_7FF7EC581B30();
    sub_7FF7EC5815B6();
    v6 = 0;
    do
    {
    sub_7FF7EC581C5F();
    sub_7FF7EC5815B6();
    puts(&aWasdEsc);
    v5 = getch();
    sub_7FF7EC583060("%c\n", (unsigned int)v5);

    if ( v5 == 119 )
    {
    LABEL_19:
    sub_7FF7EC581BB7(0LL, 0xFFFFFFFFLL);
    v0 = v6++;
    byte_7FF7EC58E0E0[v0] = 119;
    continue;
    }
    if ( v5 <= 119 )
    {
    if ( v5 == 115 )
    goto LABEL_22;
    if ( v5 <= 115 )
    {
    if ( v5 == 100 )
    goto LABEL_21;
    if ( v5 <= 100 )
    {
    if ( v5 == 97 )
    goto LABEL_20;
    if ( v5 <= 97 )
    {
    if ( v5 == 87 )
    goto LABEL_19;
    if ( v5 <= 87 )
    {
    if ( v5 == 83 )
    {
    LABEL_22:
    sub_7FF7EC581BB7(0LL, 1LL);
    v3 = v6++;
    byte_7FF7EC58E0E0[v3] = 115;
    continue;
    }
    if ( v5 <= 83 )
    {
    if ( v5 == 68 )
    {
    LABEL_21:
    sub_7FF7EC581BB7(1LL, 0LL);
    v2 = v6++;
    byte_7FF7EC58E0E0[v2] = 100;
    continue;
    }
    if ( v5 <= 68 )
    {
    if ( v5 == 27 )
    return 0LL;
    if ( v5 == 65 )
    {
    LABEL_20:
    sub_7FF7EC581BB7(0xFFFFFFFFLL, 0LL);
    v1 = v6++;
    byte_7FF7EC58E0E0[v1] = 97;
    }
    }
    }
    }
    }
    }
    }
    }
    }
    while ( dword_7FF7EC58E0C4 != dword_7FF7EC58E0D4 || dword_7FF7EC58E0C8 != dword_7FF7EC58E0D8 );
    sub_7FF7EC583060("Congratulations your flag is: 0xGame{md5(your_path)} (lowercase)");
    return 0LL;
    }

    点进LABEL_19下的第一个函数可以看到一个比较逻辑

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    __int64 __fastcall sub_7FF7EC581BB7(int a1, int a2)
    {
    int v3; // [rsp+24h] [rbp-Ch]
    int v4; // [rsp+28h] [rbp-8h]
    int v5; // [rsp+2Ch] [rbp-4h]

    v5 = dword_7FF7EC58E0C4 + a1;
    v4 = dword_7FF7EC58E0C8 + a2;
    v3 = dword_7FF7EC58E0C4 + a1 + 101 * (dword_7FF7EC58E0C8 + a2);
    if ( byte_7FF7EC589040[v3] == 35 )//比较逻辑
    {
    puts("不能移动到墙壁");
    }
    else
    {
    byte_7FF7EC589040[101 * dword_7FF7EC58E0C8 + dword_7FF7EC58E0C4] = 32;
    dword_7FF7EC58E0C4 = v5;
    dword_7FF7EC58E0C8 = v4;
    byte_7FF7EC589040[v3] = 80;
    }
    return 0LL;
    }

    在Hex View跟随比较逻辑中的这个数组的地址(7FF7EC589040)可以看到迷宫:

    迷宫

    但是提取出迷宫会发现,这个迷宫和直接运行程序时终端显示的迷宫不一样

    推测程序加了反调试。最直接的绕过方法就是用xdbg调试,用插件ScyllaHide直接绕过反调试检测。在转储找到相同的地址可以得到真正的迷宫

    真正的迷宫

    选中迷宫地图数据右键二进制保存到文件,把文件格式改为txt

    保存迷宫地图

    然后就可以让AI写个解迷宫脚本解出路径,按照程序的提示把路径进行MD5加密就可以得到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
    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
    from collections import deque

    def solve_maze(maze, start, end, wall='#', free=' '):
    """Maze solver using BFS"""
    directions = {
    (-1, 0): 'w', # up
    (1, 0): 's', # down
    (0, -1): 'a', # left
    (0, 1): 'd' # right
    }

    rows, cols = len(maze), len(maze[0])
    visited = [[False] * cols for _ in range(rows)]

    # BFS
    queue = deque([(start[0], start[1], "")])
    visited[start[0]][start[1]] = True

    while queue:
    row, col, path = queue.popleft()

    if (row, col) == end:
    return path

    for dr, dc in directions.keys():
    new_row, new_col = row + dr, col + dc

    if (0 <= new_row < rows and 0 <= new_col < cols and
    not visited[new_row][new_col] and
    maze[new_row][new_col] != wall):

    visited[new_row][new_col] = True
    new_path = path + directions[(dr, dc)]
    queue.append((new_row, new_col, new_path))

    return None

    # Load maze and solve
    with open("final_maze.txt", "r") as f:
    maze = [list(line.strip()) for line in f]

    start = (1, 1)
    end = (99, 99)

    print(f"Maze size: {len(maze)} x {len(maze[0])}")
    print(f"Start: {start}, End: {end}")

    # Solve the maze
    solution = solve_maze(maze, start, end, wall='#', free=' ')

    if solution:
    print(f"Solution found!")
    print(f"Path length: {len(solution)}")

    # Print FULL path
    print(f"\nFULL PATH:")
    print(solution)

    # Save full path
    with open("maze_solution.txt", "w") as f:
    f.write(solution)
    print(f"\nFull path saved to maze_solution.txt")
    else:
    print("No solution found!")

flag:0xGame{44fc4bc18c87b3992af1d7726185ed36}

绯想天则

考点:unity逆向改游戏机制

  • 附件是个unity游戏

    unity游戏

    boss血条很厚,怎么打都打不过,角色很容易死。所以考虑改游戏机制

  • 参考从出题到做题——浅探Unity游戏逆向,找到IWANNATENSHIFUMOのINESBATTLE\I WANNA TENSHI FUMOのFIGHT WITH INES_Data\Managed\Assembly-CSharp.dll,用dnspy打开进行修改。修改思路是给角色加上锁血和一击毙命buff,修改的类是CharacterStats,至于怎么修改可以把整个类的代码发给AI问怎么改锁血和一击毙命,因为附件程序工程比较大,没有上下文的话不好描述,此处省略修改方案了。修改方法是在类代码页右键选择编辑类

    右键编辑类

    修改完点击右下角的编译即可。此外,笔者在编译的时候遇到了一些报错:random’is anbiguous reference between ‘unityengine.random’and ‘system.random,此时把代码中所有的Random.Range都改成UnityEngine.Random.Range即可。

    修改完成后,重新运行游戏就可以速通得到flag了

    速通

flag:0xGame{TenshiSamaSaiko_IWANNATENSHIFUMO_INESBATTLE}

幻视调律

考点:函数重写,RC4,TEA

  • 附件程序运行起来是很常规的输入flag提示:

    常规

    用IDA打开,Shift+F12可以看到可疑字符串

    可疑字符串

    跟踪可疑字符串可以找到main函数

    main函数

    简单优化一下

    优化后的main函数

    可以看到函数对输入的内容进行了RC4加密后才校验。但是作为第四周的题目,这样子显然还是太直白了。动调一下可以发现,strcmp函数被重写了

    跟踪strcmp1

    跟踪strcmp2

    跟踪strcmp3

    可以看到,strcmp最后被导向了sub_140003000,函数反编译如下(经过优化),是个TEA加密

    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
    _BOOL8 __fastcall sub_140003000(void *a1, _DWORD *a2)
    {
    _DWORD Buf2[14]; // [rsp+20h] [rbp-60h] BYREF
    _DWORD *key; // [rsp+58h] [rbp-28h]
    void *Buf1; // [rsp+60h] [rbp-20h]
    int j; // [rsp+6Ch] [rbp-14h]
    int sum; // [rsp+70h] [rbp-10h]
    unsigned int p2; // [rsp+74h] [rbp-Ch]
    unsigned int p1; // [rsp+78h] [rbp-8h]
    int i; // [rsp+7Ch] [rbp-4h]

    Buf1 = a1;
    key = a2;
    Buf2[0] = 1111022553;
    Buf2[1] = 987309029;
    Buf2[2] = -1041056711;
    Buf2[3] = 979189025;
    Buf2[4] = 189565873;
    Buf2[5] = 173973774;
    Buf2[6] = 1090109393;
    Buf2[7] = -591836169;
    Buf2[8] = -394857933;
    Buf2[9] = 668605093;
    Buf2[10] = 0xB952BACB;
    for ( i = 0; i <= 9; ++i )
    {
    sum = 0;
    p1 = *((_DWORD *)Buf1 + i);
    p2 = *((_DWORD *)Buf1 + i + 1);
    sum = 0x9E3779B9;
    for ( j = 0; j <= 31; ++j )
    {
    p1 += (sum + p2) ^ (*key + 16 * p2) ^ ((p2 >> 5) + key[1]);
    sum -= 0x61C88647;
    p2 += (sum + p1) ^ (key[2] + 16 * p1) ^ ((p1 >> 5) + key[3]);
    }
    *((_DWORD *)Buf1 + i) = p1;
    *((_DWORD *)Buf1 + i + 1) = p2;
    }
    return memcmp(Buf1, Buf2, 0x2CuLL) != 0;
    }

    所以,完整的加密流程是先RC4再TEA,TEA的key是main函数中strcmp的参数str2,密文是TEA最后面出现的Buf2。

    还有一些需要关注的点:程序事实上进行了两次RC4加密,第一次是为了输出”input your flag:”,第二次才是加密输入的内容。所以,动调时在函数进行输出后、第二次RC4加密前才能提取出真的S盒

    在反编译出来的函数的基础上可以写出解密脚本:

    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
    #include<stdio.h>
    #include<stdint.h>
    void RC4_decrypt(char *a1, int a2) {
    //直接提取S盒
    unsigned char byte_140008060[256] = {
    0x99, 0x9B, 0x45, 0x40, 0x22, 0xF6, 0x8B, 0x79, 0x85, 0xD8, 0x7E, 0xF2, 0xBD, 0x98, 0x4C, 0x87,
    0x2C, 0xB5, 0x1F, 0x25, 0xA5, 0xD7, 0xAA, 0x56, 0x3C, 0x42, 0x9A, 0x1D, 0x27, 0xC7, 0x0D, 0x4E,
    0x3E, 0x71, 0x63, 0x8A, 0x82, 0x46, 0xF3, 0x53, 0x70, 0xE2, 0x11, 0x16, 0x2A, 0xD3, 0xA0, 0x2D,
    0x5F, 0x60, 0x8D, 0xD9, 0xFA, 0xD0, 0xC5, 0x64, 0x00, 0xEE, 0xDF, 0xB3, 0x4A, 0x06, 0x8E, 0xB4,
    0x8C, 0x86, 0xD2, 0x38, 0x58, 0x01, 0x48, 0x61, 0x32, 0xD6, 0x90, 0xD1, 0x5B, 0xAD, 0x7F, 0x77,
    0x4B, 0x09, 0x12, 0xCE, 0x75, 0xC8, 0x05, 0x96, 0xDE, 0xF9, 0xA2, 0xE0, 0x66, 0x0B, 0xAB, 0xE1,
    0xCD, 0x4D, 0xF0, 0x92, 0x9D, 0x83, 0x7B, 0xBF, 0x6E, 0xF7, 0xF8, 0x41, 0x6C, 0x30, 0x37, 0xCA,
    0x89, 0xA8, 0xD5, 0xEF, 0x23, 0x68, 0xBB, 0xE3, 0xB2, 0x9F, 0xC6, 0xB6, 0xA3, 0xFD, 0xE8, 0xEB,
    0x3A, 0x8F, 0x10, 0xA6, 0x1B, 0x67, 0xE5, 0xC0, 0xF5, 0x81, 0x97, 0x39, 0x57, 0x6A, 0x2E, 0xE9,
    0x3F, 0x80, 0x2F, 0xA4, 0xE4, 0xEC, 0xE7, 0x24, 0x9C, 0x43, 0xDB, 0x52, 0xED, 0x08, 0x21, 0x51,
    0xFB, 0xD4, 0x69, 0xC9, 0xFE, 0x17, 0x78, 0x14, 0x54, 0xDD, 0xCF, 0x74, 0xFC, 0x59, 0xC2, 0x7D,
    0x7C, 0x84, 0x1E, 0x15, 0x72, 0x5A, 0x31, 0x0F, 0xDA, 0xB0, 0xCB, 0xBE, 0xAE, 0x73, 0x3D, 0x7A,
    0xC3, 0x62, 0x29, 0xC4, 0x20, 0x6D, 0x36, 0x34, 0xDC, 0xAC, 0x33, 0xA7, 0xFF, 0x55, 0x93, 0xB1,
    0xE6, 0xB8, 0xC1, 0xF1, 0xF4, 0x18, 0x6B, 0x91, 0xBA, 0x47, 0x65, 0x0C, 0x13, 0x1C, 0xB7, 0x3B,
    0x35, 0xA1, 0x19, 0x6F, 0x0E, 0x49, 0x0A, 0x07, 0x5C, 0x02, 0x94, 0x1A, 0x4F, 0xAF, 0x04, 0x2B,
    0xCC, 0xB9, 0x88, 0xA9, 0x5E, 0x26, 0xEA, 0x50, 0x44, 0x95, 0x28, 0x5D, 0x03, 0xBC, 0x76, 0x9E
    };
    unsigned __int8 v3; // [rsp+0h] [rbp-10h]
    int i; // [rsp+4h] [rbp-Ch]
    int v5; // [rsp+8h] [rbp-8h]
    int v6; // [rsp+Ch] [rbp-4h]

    v6 = 0;
    v5 = 0;
    for ( i = 0; i < a2; ++i )
    {
    v6 = (v6 + 1) % 256;
    v5 = (byte_140008060[v6] + v5) % 256;
    v3 = byte_140008060[v6];
    byte_140008060[v6] = byte_140008060[v5];
    byte_140008060[v5] = v3;
    *(char *)(i + a1) = byte_140008060[(unsigned __int8)(byte_140008060[v6] + byte_140008060[v5])] ^ *(char *)(a1 + i);
    }
    }


    int main(){
    //用作key的str2
    unsigned char Str2[44] = {
    0x9F, 0xBE, 0xAF, 0x3F, 0x87, 0x12, 0xF0, 0x50, 0x75, 0xC0, 0xA4, 0xF6, 0x78, 0x42, 0x77, 0x55,
    0x80, 0x7B, 0x02, 0x67, 0x5E, 0xFE, 0x07, 0xEC, 0x76, 0x77, 0xC8, 0x31, 0x66, 0x95, 0x0A, 0x25,
    0x7B, 0xE2, 0x83, 0x02, 0x9C, 0x7B, 0x9C, 0xE9, 0x93, 0x2E, 0x66, 0xC7
    };

    //下面是在反编译TEA的基础上改的,直接解密Buf2
    uint32_t Buf2[14]; // [rsp+20h] [rbp-60h] BYREF
    uint32_t *key; // [rsp+58h] [rbp-28h]
    //void *Buf1; // [rsp+60h] [rbp-20h]
    int j; // [rsp+6Ch] [rbp-14h]
    int sum; // [rsp+70h] [rbp-10h]
    unsigned int p2; // [rsp+74h] [rbp-Ch]
    unsigned int p1; // [rsp+78h] [rbp-8h]
    int i; // [rsp+7Ch] [rbp-4h]

    //Buf1 = a1;
    key = (uint32_t *)Str2;
    Buf2[0] = 1111022553;
    Buf2[1] = 987309029;
    Buf2[2] = -1041056711;
    Buf2[3] = 979189025;
    Buf2[4] = 189565873;
    Buf2[5] = 173973774;
    Buf2[6] = 1090109393;
    Buf2[7] = -591836169;
    Buf2[8] = -394857933;
    Buf2[9] = 668605093;
    Buf2[10] = 0xB952BACB;
    //这里原来是从<Buf[0],Buf[1]>到<Buf[9],Buf[10]>一对一对加密,现在反过来从<Buf[9],Buf[10]>开始到<Buf[0],Buf[1]>
    for ( i = 9; i >= 0; --i )
    {
    sum = 0;
    p1 = *((uint32_t*)Buf2 + i);
    p2 = *((uint32_t*)Buf2 + i + 1);
    sum = 0x9E3779B9;
    //模拟正向加密得到最后的sum值
    for(int i = 0; i < 32; i++){
    sum-=0x61C88647;
    }
    //p1,p2的处理顺序调换,加减调换,sum加减调换
    for ( j = 0; j <= 31; ++j )
    {
    p2 -= (sum + p1) ^ (key[2] + 16 * p1) ^ ((p1 >> 5) + key[3]);
    sum += 0x61C88647;
    p1 -= (sum + p2) ^ (*key + 16 * p2) ^ ((p2 >> 5) + key[1]);
    }
    *((uint32_t*)Buf2 + i) = p1;
    *((uint32_t*)Buf2 + i + 1) = p2;
    }
    RC4_decrypt((char *)Buf2,44);
    for(i=0;i<44;i++)
    printf("%c",((char *)Buf2)[i]);
    return 0;
    }

flag:0xGame{20994d4d-30ab-4918-b00a-5ca6ae138613}

花鸟风月

考点:花指令,魔改base64,异或,动态调试

  • 附件程序运行起来是这样的:

    运行程序

    用IDA打开,Shift+F12在字符串窗口可以找到类似base64编码表的可疑字符串

    可疑的字符串

    随后可以跟踪到这样一个貌似base64编码的函数

    跟踪到一个类似base64编码的函数

    选中函数名按X回溯上层函数,会发现无法反编译了,栈帧报错

    栈帧报错

    在报错的附近可以看到反汇编失败的一堆数字,以及一个典型的jnz+jz花指令

    选中上图中的loc_4012F2按U可以得到下图

    undefine

    此时只需要把jnz和jz以及后面的0E8h给nop掉即可,再选中unk_4012F3按C即可得到修正后的汇编代码

    修正后的汇编代码

    同样的花指令大概有3个,去除后可以在上文找到另一个报错

    另一个报错

    这里可以发现报错处的上一句是retn,所以把下面的loc_4012C0右键create function

    create function

    然后再回过头看这个报错,前面不远有一个很可疑的call $+5,显然是人为修改的

    可疑的call $+5

    仔细分析这段汇编代码会发现,这也是一个花指令

    分析汇编代码

    实际上程序在jz处不跳直接retn后,会回到xor eax,1这一行,原因是:call $+5即call 0x0040122D,又即push rip(0x0040122D) jmp 0x0040122D,所以retn后程序回到栈上的rip(0x0040122D)处,即xor eax,1。IDA反编译会被这个retn给欺骗,误以为函数到此结束,其实后面还有一些内容。所以直接吧loc_401223这一整段nop掉,就可以得到完整的反编译代码

    完整的反编译代码

    优化一下这个函数和上文的类似base编码的函数的命名,可以得到。

    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
    int sub_4012C0()
    {
    char *v0; // eax
    char *v1; // eax
    char *v2; // eax
    char *v3; // eax
    char *v5; // eax
    char *v6; // eax
    char *v7; // eax
    char *v8; // eax
    int v9; // [esp-4h] [ebp-ECh]
    int v10; // [esp+10h] [ebp-D8h] BYREF
    int v11; // [esp+14h] [ebp-D4h]
    int i; // [esp+18h] [ebp-D0h]
    _BYTE v13[100]; // [esp+1Ch] [ebp-CCh] BYREF
    _BYTE v14[100]; // [esp+80h] [ebp-68h] BYREF

    v11 = 0x81428113;
    v0 = sub_401000((int)&unk_422054);
    sub_401670(v0);
    v1 = sub_401000((int)&unk_422068);
    sub_4016B0(v1, (char)&v10);
    for ( i = 0; i < 10; ++i )
    {
    v11 = 21106755 * v11 + 1126795285;
    v11 ^= __ROL4__(
    v10,
    i * (byte_422007 + byte_422004)
    % (byte_422008
    + byte_422004
    + byte_422005 * byte_422008
    + byte_422AA0 * (byte_422002 + byte_422006 + byte_422003)
    - byte_422005 * byte_422005));
    }
    if ( v11 == 0xA745DA5D )
    {
    v2 = sub_401000((int)&unk_42206C);
    sub_401670(v2);
    v5 = sub_401000((int)&unk_422094);
    sub_401670(v5);
    v6 = sub_401000((int)&unk_4220A4);
    sub_4016B0(v6, (char)v14);
    XORencrypt(v14, v10, byte_422004 + byte_422002 + byte_422008 * byte_422003);
    baseEncode((int)v14, (int)v13, byte_422005 + byte_422001 + byte_422008 * byte_422003);
    v9 = byte_422004 * byte_422003 * byte_422002;
    v7 = sub_401000((int)&unk_4220A8);
    if ( sub_40B460(v13, v7, v9) )
    v8 = sub_401000((int)&unk_4220FC);
    else
    v8 = sub_401000((int)&unk_4220E8);
    sub_401670(v8);
    return 0;
    }
    else
    {
    v3 = sub_401000((int)&unk_422080);
    sub_401670(v3);
    return 0;
    }
    }

    其中byte_42200x的值都是x+1,所以可以进一步优化

    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
    int sub_4012C0()
    {
    char *v0; // eax
    char *v1; // eax
    char *v2; // eax
    char *v3; // eax
    char *v5; // eax
    char *v6; // eax
    char *v7; // eax
    char *v8; // eax
    int v9; // [esp-4h] [ebp-ECh]
    int v10; // [esp+10h] [ebp-D8h] BYREF
    int v11; // [esp+14h] [ebp-D4h]
    int i; // [esp+18h] [ebp-D0h]
    _BYTE v13[100]; // [esp+1Ch] [ebp-CCh] BYREF
    _BYTE v14[100]; // [esp+80h] [ebp-68h] BYREF

    v11 = 0x81428113;
    v0 = sub_401000((int)&unk_422054);
    sub_401670(v0);
    v1 = sub_401000((int)&unk_422068);
    sub_4016B0(v1, (char)&v10);
    for ( i = 0; i < 10; ++i )
    {
    v11 = 0x1421043 * v11 + 0x43298815;
    v11 ^= __ROL4__(v10, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
    }
    if ( v11 == 0xA745DA5D )
    {
    v2 = sub_401000((int)&unk_42206C);
    sub_401670(v2);
    v5 = sub_401000((int)&unk_422094);
    sub_401670(v5);
    v6 = sub_401000((int)&unk_4220A4);
    sub_4016B0(v6, (char)v14);
    XORencrypt(v14, v10, 5 + 3 + 9 * 4);
    baseEncode((int)v14, (int)v13, 6 + 2 + 9 * 4);
    v9 = 5 * 4 * 3;
    v7 = sub_401000((int)&unk_4220A8);
    if ( sub_40B460(v13, v7, v9) )
    v8 = sub_401000((int)&unk_4220FC);
    else
    v8 = sub_401000((int)&unk_4220E8);
    sub_401670(v8);
    return 0;
    }
    else
    {
    v3 = sub_401000((int)&unk_422080);
    sub_401670(v3);
    return 0;
    }
    }

    动调可以发现程序的提示词是实时解密的,sub_401000是解密函数,sub_401670是输出函数,sub_4016B0是输入函数。下面是具体过程:

    在函数的开头下断点,开始调试。程序停在了下图这个地方,可以看到随后有一些奇怪的数据,直接nop掉

    奇怪的数据

    nop掉

    继续运行程序会发现执行过sub_7C1670(对应静态分析时的sub_401670,动态调试换基址了所以地址前两位有些不同)后程序会有提示输出,因此推断sub_7C1670是print

    提示输出

    而观察静态分析时的代码可以发现,sub_401000几乎和sub_401670是同时出现的,动态调试对应的sub_7C1000函数反编译如下

    sub_7C1000反编译

    同时可以发现byte_7E3440此时存储的正好是Input key (Hex) (此时已经步过这个函数了,所以byte_7E3440已经被处理过了):

    byte_7E3440

    所以可以推测提示词是实时解密的,sub_401000是解密函数

    接下来还有个sub_7C16B0,推测是scanf

    sub_7C16B0

    在动态调试随后的代码里,会发现还有花指令(有两处,不知道是不是静态分析时的去混淆没有保存)

    花指令

    同样的操作nop掉后,反编译得到

    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
    void sub_7C12C0()
    {
    char *v0; // eax
    char *v1; // eax
    char *v2; // eax
    char *v3; // eax
    char *v4; // eax
    char *v5; // eax
    char *v6; // eax
    char *v7; // eax
    int v8; // [esp-4h] [ebp-ECh]
    int v9; // [esp+10h] [ebp-D8h] BYREF
    int v10; // [esp+14h] [ebp-D4h]
    int i; // [esp+18h] [ebp-D0h]
    _BYTE v12[100]; // [esp+1Ch] [ebp-CCh] BYREF
    _BYTE v13[100]; // [esp+80h] [ebp-68h] BYREF

    v10 = 0x81428113;
    v0 = decodeNotice((int)&unk_7E2054);
    print(v0);
    v1 = decodeNotice((int)&unk_7E2068);
    scanf(v1, (char)&v9);
    for ( i = 0; i < 10; ++i )
    {
    v10 = 0x1421043 * v10 + 0x43298815;
    v10 ^= __ROL4__(v9, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
    }
    if ( v10 == 0xA745DA5D )
    {
    v2 = decodeNotice((int)&unk_7E206C);
    print(v2);
    v4 = decodeNotice((int)&unk_7E2094);
    print(v4);
    v5 = decodeNotice((int)&unk_7E20A4);
    scanf(v5, (char)v13);
    XORencrypt((int)v13, v9, 5 + 3 + 9 * 4);
    baseEncode((int)v13, (int)v12, 6 + 2 + 9 * 4);
    v8 = 5 * 4 * 3;
    v6 = decodeNotice((int)&unk_7E20A8);
    if ( sub_7CB460(v12, v6, v8) )
    v7 = decodeNotice((int)&unk_7E20FC);
    else
    v7 = decodeNotice((int)&unk_7E20E8);
    print(v7);
    }
    else
    {
    v3 = decodeNotice((int)&unk_7E2080);
    print(v3);
    }
    }

    此时得到更加清晰的代码了,同时可以发现程序首先对输入的key进行了校验。

    为了继续了解程序逻辑,这里选择修改if语句条件的判断结果来绕过key检验,只需要在cmp后修改ZF标志位,让jnz不成立即可

    绕过key检验

    成功绕过key检验

    继续运行程序会有输入flag的提示

    输入flag的提示

    至此,可以猜出程序的大致逻辑:接收一个key并进行检验,检验成功则继续检验flag,同时需要注意key会作为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
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    void sub_7C12C0()
    {
    char *v0; // eax
    char *v1; // eax
    char *v2; // eax
    char *v3; // eax
    char *v4; // eax
    char *v5; // eax
    char *targetResult; // eax
    char *v7; // eax
    int v8; // [esp-4h] [ebp-ECh]
    int key; // [esp+10h] [ebp-D8h] BYREF
    int v10; // [esp+14h] [ebp-D4h]
    int i; // [esp+18h] [ebp-D0h]
    _BYTE encodedInput[100]; // [esp+1Ch] [ebp-CCh] BYREF
    _BYTE input[100]; // [esp+80h] [ebp-68h] BYREF

    v10 = 0x81428113;
    v0 = decodeNotice((int)&unk_7E2054); // input key
    print(v0);
    v1 = decodeNotice((int)&unk_7E2068);
    scanf(v1, (char)&key);
    for ( i = 0; i < 10; ++i )
    {
    v10 = 0x1421043 * v10 + 0x43298815;
    v10 ^= __ROL4__(key, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
    }
    if ( v10 == 0xA745DA5D )
    {
    v2 = decodeNotice((int)&unk_7E206C); // key is correct
    print(v2);
    v4 = decodeNotice((int)&unk_7E2094); // input flag
    print(v4);
    v5 = decodeNotice((int)&unk_7E20A4);
    scanf(v5, (char)input);
    XORencrypt((int)input, key, 5 + 3 + 9 * 4);
    baseEncode((int)input, (int)encodedInput, 6 + 2 + 9 * 4);
    v8 = 5 * 4 * 3;
    targetResult = decodeNotice((int)&unk_7E20A8);// 此处解密的值被用来和输入值的加密结果进行比较了,所以推测是真正的flag密文
    if ( strcmp(encodedInput, targetResult, v8) )
    v7 = decodeNotice((int)&unk_7E20FC);
    else
    v7 = decodeNotice((int)&unk_7E20E8);
    print(v7);
    }
    else
    {
    v3 = decodeNotice((int)&unk_7E2080);
    print(v3);
    }
    }

    targetResult也是实时解密的结果,所以可以动调提取

    程序的加密流程是先XORencrypt再baseEncode,所以解密要先baseDecode再XORenc。baseEncode优化后如下,可以发现不是标准的base64编码

    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
    int __cdecl baseEncode(int input, int output, int length)
    {
    int result; // eax
    char code2; // [esp+Ah] [ebp-56h]
    int i; // [esp+Ch] [ebp-54h]
    char code4; // [esp+12h] [ebp-4Eh]
    char code3; // [esp+13h] [ebp-4Dh]
    int v8; // [esp+14h] [ebp-4Ch]
    int v9; // [esp+14h] [ebp-4Ch]
    char baseTable[68]; // [esp+18h] [ebp-48h] BYREF

    v8 = 0;
    strcpy(baseTable, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
    for ( i = 0; i < length; i += 3 )
    {
    code3 = baseTable[((*(i + input + 1) >> 4) | (16 * *(i + input))) & 63];// baseTable[((input[i+1]>>4)|(input[i]<<4))&63]
    if ( i + 1 >= length )
    code3 = '=';
    else
    code2 = baseTable[((*(i + input + 2) >> 6) | (4 * *(i + input + 1))) & 63];// baseTable[((input[i+2]>>6)|(input[i+1]<<2))&63]
    if ( i + 2 >= length )
    code4 = '=';
    else
    code4 = baseTable[*(i + input + 2) & 63]; // baseTable[input[i+2]&63]
    *(v8 + output) = baseTable[(*(i + input) >> 2) & 63];// baseTable[(input[i]>>2)&63]
    v9 = v8 + 1;
    *(v9 + output) = code2;
    *(++v9 + output) = code3;
    *(++v9 + output) = code4;
    v8 = v9 + 1;
    result = i + 3;
    }
    *(v8 + output) = 0;
    return result;
    }

    标准base64编码是按照字符顺序,把3个8bit的char转成4个6bit,即

    1
    2
    3
    4
    code1=baseTable[input[i]>>2];
    code2=baseTable[(input[i]<<6)|(input[i+1]>>4)];
    code3=baseTable[(input[i+1]<<4)|(input[i+2]>>6)];
    code4=baseTable[input[i+2]<<2];

    而程序里code2和code3顺序反了

    1
    2
    3
    4
    5
    code1=baseTable[(input[i]>>2)&63];
    code2=baseTable[((input[i+1]<<2)|(input[i+2]>>6))&63];
    code3=baseTable[((input[i]<<4)|(input[i+1]>>4))&63];
    code4=baseTable[input[i+2]&63];
    //PS:这里因为&了63,所以拼成8位的code被截掉了高2位,剩下6位

    也就是说,程序的魔改base64编码和标准base64编码的不同点在于,编码时置换了第2、3个字符。因此我们此处的解密就是把每4个字符的第2、3个字符互换位置,然后使用标准base64解码即可。下面是basedecode的脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import base64

    enc=[0x4D, 0x41, 0x50, 0x44, 0x63, 0x33, 0x47, 0x74, 0x50, 0x51, 0x33, 0x34, 0x76, 0x4D, 0x33, 0x70, 0x4E, 0x42, 0x62, 0x31, 0x50, 0x57, 0x47, 0x78, 0x63, 0x63, 0x48, 0x74, 0x76, 0x78, 0x48, 0x31, 0x4E, 0x56, 0x61, 0x39, 0x4A, 0x50, 0x54, 0x74, 0x61, 0x63, 0x53, 0x35, 0x37, 0x49, 0x69, 0x6C, 0x4E, 0x35, 0x75, 0x78, 0x4A, 0x53, 0x57, 0x35, 0x49, 0x77, 0x6D, 0x3D]
    basedecoded=""
    for i in range(0, len(enc),4):
    basedecoded+=chr(enc[i])
    basedecoded+=chr(enc[i+2])
    basedecoded+=chr(enc[i+1])
    basedecoded+=chr(enc[i+3])
    print(basedecoded)
    #MPADcG3tP3Q4v3MpNbB1PGWxcHctvHx1NaV9JTPtaSc57iIlNu5xJWS5Imw=
    xorencoded=base64.b64decode(basedecoded)

    接下来是XORdecrypt。首先需要得到密钥,既然程序给了密钥的检验算法,可以复现这个算法来爆破密钥。下面是爆破脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include<stdio.h>
    #define __ROL4__(x, y) ((((x) << (y)) | ((x) >> (32 - (y))))&0xFFFFFFFF)
    int main(){
    unsigned int v9,key;
    for(key=0;key<=0xFFFFFFFF;key++){
    v9 = 0x81428113;
    for ( int i = 0; i < 10; ++i )
    {
    v9 = 0x1421043 * v9 + 0x43298815;
    v9 ^= __ROL4__(key, i * (8 + 5) % (9 + 5 + 6 * 9 + 0 * (3 + 7 + 4) - 6 * 6));
    }
    if ( v9 == 0xA745DA5D )
    printf("key: 0x%08x\n",key);
    }
    return 0;
    }
    //key: 0x98bf3b77

    然后是XOR解密,由于异或过程中不涉及密文前后位,只是单纯使用key,所以只需要复现异或过程就行了。但是照抄也不是很对,XORencrypt使用key的时候是进行了取位操作,把key分成了四个部分的,所以这部分要单独拿出来处理。脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def XORdecrypt(data, key):
    for i in range(44):
    key_byte=(key >> (8 * (i % 4)))&0xff#单独处理每次使用的key,每次只取四分之一,直接抄原代码没有&0xff的话会解密失败。
    data[i]^=key_byte
    data[i]=data[i]&0xff
    for j in range(44):
    key_byte=(key >> (8 * (j % 4)))&0xff
    data[j]^=((key_byte >> 4) | (16 * key_byte))
    data[j]=data[j]&0xff
    return bytes(data)

    完整脚本:

    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
    import base64

    def XORdecrypt(data, key):
    for i in range(44):
    key_byte=(key >> (8 * (i % 4)))&0xff
    data[i]^=key_byte
    data[i]=data[i]&0xff
    for j in range(44):
    key_byte=(key >> (8 * (j % 4)))&0xff
    data[j]^=((key_byte >> 4) | (16 * key_byte))
    data[j]=data[j]&0xff
    return bytes(data)

    enc=[0x4D, 0x41, 0x50, 0x44, 0x63, 0x33, 0x47, 0x74, 0x50, 0x51, 0x33, 0x34, 0x76, 0x4D, 0x33, 0x70, 0x4E, 0x42, 0x62, 0x31, 0x50, 0x57, 0x47, 0x78, 0x63, 0x63, 0x48, 0x74, 0x76, 0x78, 0x48, 0x31, 0x4E, 0x56, 0x61, 0x39, 0x4A, 0x50, 0x54, 0x74, 0x61, 0x63, 0x53, 0x35, 0x37, 0x49, 0x69, 0x6C, 0x4E, 0x35, 0x75, 0x78, 0x4A, 0x53, 0x57, 0x35, 0x49, 0x77, 0x6D, 0x3D]
    basedecoded=""
    for i in range(0, len(enc),4):
    basedecoded+=chr(enc[i])
    basedecoded+=chr(enc[i+2])
    basedecoded+=chr(enc[i+1])
    basedecoded+=chr(enc[i+3])
    #print(basedecoded)
    xorencoded=base64.b64decode(basedecoded)
    #print(len(xorencoded))
    flag=XORdecrypt(list(xorencoded),0x98bf3b77)
    print(flag)

flag:0xGame{e8778581-e94f-48d5-943e-69ff46f54d1f}

云消雾散

考点:复杂程序分析,魔改RC4

难点在于复杂,没有其他套路

  • 附件给了一个img.exe和enc.bmp。bmp是一种图片格式,推测img.exe是bmp图片加密程序,enc.bmp是加密的图片,要求逆向加密程序进而解密图片

  • img.exe用IDA打开,shift+F12可以找到”enc.bmp”

    字符串表的enc.bmp

    跟踪可以到这样一个函数

    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
    __int64 __fastcall sub_140001936(int a1, int a2)
    {
    int v2; // r9d
    int v3; // edx
    int v4; // r8d
    int v5; // r9d
    int v6; // r9d
    int v7; // edx
    int v8; // r8d
    int v9; // r9d
    int v10; // r9d
    int v11; // edx
    int v12; // r8d
    int v13; // r9d
    int v14; // r9d
    int v15; // edx
    int v16; // r8d
    int v17; // r9d
    int v18; // r9d
    int v19; // r9d
    int v20; // r8d
    int v21; // r9d
    int v22; // edx
    int v23; // r8d
    int v24; // r9d
    int v25; // edx
    int v26; // r8d
    int v27; // r9d
    int v28; // r9d
    int v29; // r9d
    int v30; // r8d
    int v31; // r9d
    int v32; // edx
    int v33; // r8d
    int v34; // r9d
    int v35; // edx
    int v36; // r8d
    int v37; // r9d
    int v38; // edx
    int v39; // r8d
    int v40; // r9d
    int v41; // edx
    int v42; // r8d
    int v43; // r9d
    int v44; // edx
    int v45; // r8d
    int v46; // r9d
    int v47; // edx
    int v48; // r8d
    int v49; // r9d
    __int64 v51; // [rsp+0h] [rbp-80h]
    __int64 v52; // [rsp+0h] [rbp-80h]
    __int64 v53; // [rsp+0h] [rbp-80h]
    __int64 v54; // [rsp+0h] [rbp-80h]
    __int64 v55; // [rsp+0h] [rbp-80h]
    __int64 v56; // [rsp+0h] [rbp-80h]
    __int64 v57; // [rsp+0h] [rbp-80h]
    __int64 v58; // [rsp+0h] [rbp-80h]
    __int64 v59; // [rsp+0h] [rbp-80h]
    __int64 v60; // [rsp+0h] [rbp-80h]
    __int64 v61; // [rsp+0h] [rbp-80h]
    __int64 v62; // [rsp+0h] [rbp-80h]
    __int64 v63; // [rsp+0h] [rbp-80h]
    __int64 v64; // [rsp+0h] [rbp-80h]
    __int64 v65; // [rsp+0h] [rbp-80h]
    __int64 v66; // [rsp+8h] [rbp-78h]
    __int64 v67; // [rsp+10h] [rbp-70h]
    _BYTE v68[32]; // [rsp+20h] [rbp-60h] BYREF
    _BYTE v69[32]; // [rsp+40h] [rbp-40h] BYREF
    _BYTE v70[32]; // [rsp+60h] [rbp-20h] BYREF
    _BYTE v71[44]; // [rsp+80h] [rbp+0h] BYREF
    char v72; // [rsp+ACh] [rbp+2Ch] BYREF
    char v73; // [rsp+ADh] [rbp+2Dh] BYREF
    char v74; // [rsp+AEh] [rbp+2Eh] BYREF
    char v75; // [rsp+AFh] [rbp+2Fh] BYREF
    _BYTE v76[32]; // [rsp+B0h] [rbp+30h] BYREF
    _BYTE v77[32]; // [rsp+D0h] [rbp+50h] BYREF
    _BYTE v78[32]; // [rsp+F0h] [rbp+70h] BYREF
    _BYTE v79[32]; // [rsp+110h] [rbp+90h] BYREF
    char *v80; // [rsp+130h] [rbp+B0h]
    char *v81; // [rsp+138h] [rbp+B8h]
    char *v82; // [rsp+140h] [rbp+C0h]
    char *v83; // [rsp+148h] [rbp+C8h]

    sub_140001D70();
    v83 = &v72;
    sub_140004130(a1, a2, "E:\\WorkSpace\\Lab\\0xGame 2025", v71, &v72, v2);
    sub_140003580(a1, a2, v3, &v72, v4, v5);
    v82 = &v73;
    sub_140004130(a1, a2, "flag.bmp", v70, &v73, v6);
    sub_140003580(a1, a2, v7, &v73, v8, v9);
    v81 = &v74;
    sub_140004130(a1, a2, "enc.bmp", v69, &v74, v10);
    sub_140003580(a1, a2, v11, &v74, v12, v13);
    v80 = &v75;
    sub_140004130(a1, a2, "114514puiqneravcixpbhqn;afnv-92piwgspdifjv", v68, &v75, v14);
    sub_140003580(a1, a2, v15, &v75, v16, v17);
    sub_140004670(a1, a2, v71, v77, "\\", v18, v51);
    sub_140004620(a1, a2, v77, v76, v70, v19, v52);
    sub_140004210(a1, a2, v76, v70, v20, v21, v53);
    sub_1400041E0(a1, a2, v22, v76, v23, v24, v54);
    sub_1400041E0(a1, a2, v25, v77, v26, v27, v55);
    sub_140004670(a1, a2, v71, v79, "\\", v28, v56);
    sub_140004620(a1, a2, v79, v78, v69, v29, v57);
    sub_140004210(a1, a2, v78, v69, v30, v31, v58);
    sub_1400041E0(a1, a2, v32, v78, v33, v34, v59);
    sub_1400041E0(a1, a2, v35, v79, v36, v37, v60);
    sub_14000164D(a1, a2, v69, v70, v68, 1, v61, v66, v67);
    sub_1400041E0(a1, a2, v38, v68, v39, v40, v62);
    sub_1400041E0(a1, a2, v41, v69, v42, v43, v63);
    sub_1400041E0(a1, a2, v44, v70, v45, v46, v64);
    sub_1400041E0(a1, a2, v47, v71, v48, v49, v65);
    return 0LL;
    }

    借助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
    __int64 __fastcall sub_140001936(char *input, char *output)
    {
    int v2; // r9d
    int v3; // r9d
    int v4; // r9d
    int v5; // r9d
    int v6; // r9d
    int v7; // r9d
    int v8; // r8d
    int v9; // r9d
    int v10; // edx
    int v11; // r8d
    int v12; // r9d
    int v13; // edx
    int v14; // r8d
    int v15; // r9d
    int v16; // r9d
    int v17; // r9d
    int v18; // r8d
    int v19; // r9d
    int v20; // edx
    int v21; // r8d
    int v22; // r9d
    int v23; // edx
    int v24; // r8d
    int v25; // r9d
    int v26; // edx
    int v27; // r8d
    int v28; // r9d
    int v29; // edx
    int v30; // r8d
    int v31; // r9d
    int v32; // edx
    int v33; // r8d
    int v34; // r9d
    int v35; // edx
    int v36; // r8d
    int v37; // r9d
    _BYTE v39[32]; // [rsp+20h] [rbp-60h] BYREF
    _BYTE outputFilename[32]; // [rsp+40h] [rbp-40h] BYREF
    _BYTE v41[32]; // [rsp+60h] [rbp-20h] BYREF
    _BYTE v42[44]; // [rsp+80h] [rbp+0h] BYREF
    char v43; // [rsp+ACh] [rbp+2Ch] BYREF
    char v44; // [rsp+ADh] [rbp+2Dh] BYREF
    char v45; // [rsp+AEh] [rbp+2Eh] BYREF
    char v46; // [rsp+AFh] [rbp+2Fh] BYREF
    _BYTE v47[32]; // [rsp+B0h] [rbp+30h] BYREF
    _BYTE v48[32]; // [rsp+D0h] [rbp+50h] BYREF
    _BYTE v49[32]; // [rsp+F0h] [rbp+70h] BYREF
    _BYTE v50[32]; // [rsp+110h] [rbp+90h] BYREF
    char *v51; // [rsp+130h] [rbp+B0h]
    char *v52; // [rsp+138h] [rbp+B8h]
    char *v53; // [rsp+140h] [rbp+C0h]
    char *v54; // [rsp+148h] [rbp+C8h]

    sub_140001D70();
    v54 = &v43;
    constructString(input, output, "E:\\WorkSpace\\Lab\\0xGame 2025", v42, &v43, v2);
    nothing(); // 什么也没有的一个函数
    v53 = &v44;
    constructString(input, output, "flag.bmp", v41, &v44, v3);
    nothing();
    v52 = &v45;
    constructString(input, output, "enc.bmp", outputFilename, &v45, v4);
    nothing();
    v51 = &v46;
    constructString(input, output, "114514puiqneravcixpbhqn;afnv-92piwgspdifjv", v39, &v46, v5);
    nothing();
    makePath1(input, output, v42, v48, "\\", v6);
    makePath2(input, output, v48, v47, v41, v7);
    makePath3(input, output, v47, v41, v8, v9);
    clearup(input, output, v10, v47, v11, v12);
    clearup(input, output, v13, v48, v14, v15);
    makePath1(input, output, v42, v50, "\\", v16);
    makePath2(input, output, v50, v49, outputFilename, v17);
    makePath3(input, output, v49, outputFilename, v18, v19);
    clearup(input, output, v20, v49, v21, v22);
    clearup(input, output, v23, v50, v24, v25);
    encrypt(input, output, outputFilename, v41, v39, 1);
    clearup(input, output, v26, v39, v27, v28);
    clearup(input, output, v29, outputFilename, v30, v31);
    clearup(input, output, v32, v41, v33, v34);
    clearup(input, output, v35, v42, v36, v37);
    return 0LL;
    }

    这里实际上是在初始化文件相关的信息,程序应该是从E:\WorkSpace\Lab\0xGame 2025下获取flag.bmp,加密后输出为enc.bmp。那一串字符串应该是encrypt函数使用的密钥。encpyt函数优化后是这样的:

    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
    __int64 __fastcall encrypt(
    std::istream *input,
    char *output,
    __int64 outputFilename,
    __int64 inputfilename,
    __int64 a5,
    char a6)
    {
    __int64 v6; // rdx
    __int64 v7; // rdx
    __int64 v9; // rax
    __int64 v10; // rdx
    unsigned int v11; // ebx
    int v12; // eax
    __int64 v13; // rdx
    __int64 v14; // rdx
    __int64 v15; // rdx
    __int64 v17; // [rsp+20h] [rbp-60h] BYREF
    int v18; // [rsp+28h] [rbp-58h]
    unsigned __int16 v19; // [rsp+2Eh] [rbp-52h]
    __int64 v20; // [rsp+52h] [rbp-2Eh] BYREF
    _BYTE output_file_buffer[224]; // [rsp+60h] [rbp-20h] BYREF
    __int64 v22; // [rsp+140h] [rbp+C0h] BYREF
    _BYTE input_file_buffer[232]; // [rsp+250h] [rbp+1D0h] BYREF
    __int64 v24; // [rsp+338h] [rbp+2B8h] BYREF
    __int64 outputBuffer; // [rsp+448h] [rbp+3C8h]
    __int64 v26; // [rsp+450h] [rbp+3D0h]
    int v27; // [rsp+45Ch] [rbp+3DCh]
    __int64 header_buffer; // [rsp+460h] [rbp+3E0h]
    __int64 header_buffer_size; // [rsp+468h] [rbp+3E8h]

    std::ifstream::basic_ifstream(input, output, inputfilename, input_file_buffer, 4LL);
    std::ofstream::basic_ofstream(input, output, outputFilename, output_file_buffer, 4LL);
    if ( std::ios::operator!(input, output, v6, &v24) || std::ios::operator!(input, output, v7, &v22) )
    { // 错误处理
    v9 = std::operator<<<std::char_traits<char>>(input, output, &unk_7FF770D06000, &std::cerr);
    std::ostream::operator<<(input, output, &std::endl<char,std::char_traits<char>>, v9);
    v11 = 0;
    }
    else
    {
    header_buffer_size = 1024LL;
    header_buffer = operator new[](input);
    std::istream::read(input, output, &v20); // 获取bitmap_file_header
    std::istream::read(input, output, &v17); // 获取bitmap_info_header
    std::istream::read(input, output, header_buffer);
    std::ostream::write(input, output, &v20);
    std::ostream::write(input, output, &v17);
    std::ostream::write(input, output, header_buffer);
    if ( header_buffer )
    operator delete[](input);
    v12 = HIDWORD(v17) * v19 + 31;
    if ( v12 < 0 )
    v12 = HIDWORD(v17) * v19 + 62;
    v27 = 4 * (v12 >> 5);
    v26 = (v27 * abs32(v18));
    outputBuffer = operator new[](input);
    std::istream::read(input, output, outputBuffer);
    std::ifstream::close(input, output, v13, input_file_buffer);
    if ( a6 )
    modifiedRC4(input, output, v26, outputBuffer, a5);
    std::ostream::write(input, output, outputBuffer);
    std::ofstream::close(input, output, v14, output_file_buffer);
    if ( outputBuffer )
    operator delete[](input);
    v11 = 1;
    }
    std::ofstream::~ofstream(input, output, v10, output_file_buffer);
    std::ifstream::~ifstream(input, output, v15, input_file_buffer);
    return v11;
    }

    //modifiedRC4
    __int64 __fastcall modifiedRC4(const char *a1, __int64 a2, int numOfDataBytes, __int64 outputBuffer, __int64 a5)
    {
    unsigned __int64 v5; // rax
    _BYTE *v6; // rax
    char v7; // dl
    __int64 result; // rax
    int i; // [rsp+24h] [rbp-Ch]
    int j; // [rsp+24h] [rbp-Ch]
    int k; // [rsp+24h] [rbp-Ch]
    int j_sbox; // [rsp+28h] [rbp-8h]
    int j_; // [rsp+28h] [rbp-8h]
    int i_sbox; // [rsp+2Ch] [rbp-4h]
    int i_; // [rsp+2Ch] [rbp-4h]

    for ( i = 0; i <= 255; ++i )
    S[i] = i;
    i_sbox = 0;
    j_sbox = 0;
    for ( j = 0; j <= 255; ++j )
    {
    i_sbox = (i_sbox + 1) % 256;
    a2 = S[i_sbox] + j_sbox;
    v5 = sub_7FF770D032B0(a1, a2, j_sbox, a5);
    v6 = sub_7FF770D033D0(a1, a2, i_sbox % v5, a5);
    v7 = a2 + *v6;
    LODWORD(v6) = ((a2 + *v6) >> 31) >> 24;
    j_sbox = (v6 + v7) - v6;
    swapbytes(a1, a2, &S[j_sbox], &S[i_sbox]);
    }
    i_ = 0;
    j_ = 0;
    for ( k = 0; ; ++k )
    {
    result = ((numOfDataBytes / 256) << 8);
    if ( k >= result )
    break;
    i_ = (i_ + 1) % 256;
    j_ = (S[i_] + j_) % 256;
    swapbytes(a1, a2, &S[j_], &S[i_]);
    swapBytes(
    a1,
    a2,
    (((k / 256) << 8) + j_ + outputBuffer), // outputBuffer[((k/256)<<8)+j_]
    (outputBuffer + ((k / 256) << 8) + i_)); // outputBuffer[((k/256)<<8)+i_]
    }
    return result;
    }

    从IDA的反编译结果里可以得到一些大致的印象:encrypt函数刚开始对文件进行了三次读与写,采用的加密方式是类似RC4的算法,但是相较于标准RC4,此处只是单纯的置换,没有异或操作。为了进一步理解程序,接下来先研究读写文件的操作逻辑。

    简单了解一下bmp文件格式,可以知道文件开头前0x36h是文件头,这一部分肯定是不会怎么变的,否则就会改变文件的格式了。在电脑里复刻一个一摸一样的路径,自己创建一个flag.bmp运行img.exe可以得到enc.bmp,此时用十六进制编辑器打开两个文件可以发现它们之间差了1024bytes,更准确的说,是加密后的文件比加密前多了1024bytes。

    下面转用xdbg。对照IDA可以找到读写文件的位置,分析结果如下:

    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
    00007FF6077B164D | 55                  | PUSH RBP                                                 | encrypt
    00007FF6077B164E | 53 | PUSH RBX |
    00007FF6077B164F | 48:81EC 78040000 | SUB RSP,478 |
    00007FF6077B1656 | 48:8DAC24 80000000 | LEA RBP,QWORD PTR SS:[RSP+80] | [rsp+80]:_register_onexit_function+210
    00007FF6077B165E | 48:898D 10040000 | MOV QWORD PTR SS:[RBP+410],RCX |
    00007FF6077B1665 | 48:8995 18040000 | MOV QWORD PTR SS:[RBP+418],RDX |
    00007FF6077B166C | 4C:8985 20040000 | MOV QWORD PTR SS:[RBP+420],R8 |
    00007FF6077B1673 | 44:89C8 | MOV EAX,R9D |
    00007FF6077B1676 | 8885 28040000 | MOV BYTE PTR SS:[RBP+428],AL |
    00007FF6077B167C | 48:8B95 10040000 | MOV RDX,QWORD PTR SS:[RBP+410] |
    00007FF6077B1683 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B168A | 41:B8 04000000 | MOV R8D,4 |
    00007FF6077B1690 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1693 | E8 E0050000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch | 错误处理
    00007FF6077B1698 | 48:8B95 18040000 | MOV RDX,QWORD PTR SS:[RBP+418] |
    00007FF6077B169F | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B16A3 | 41:B8 04000000 | MOV R8D,4 |
    00007FF6077B16A9 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B16AC | E8 AF050000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
    00007FF6077B16B1 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B16B8 | 48:05 E8000000 | ADD RAX,E8 | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B16BE | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B16C1 | E8 DA050000 | CALL <JMP.&std::basic_ios<char, std::char_traits<char> > |
    00007FF6077B16C6 | 84C0 | TEST AL,AL |
    00007FF6077B16C8 | 75 16 | JNE img.7FF6077B16E0 |
    00007FF6077B16CA | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B16CE | 48:05 E0000000 | ADD RAX,E0 | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B16D4 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B16D7 | E8 C4050000 | CALL <JMP.&std::basic_ios<char, std::char_traits<char> > |
    00007FF6077B16DC | 84C0 | TEST AL,AL |
    00007FF6077B16DE | 74 07 | JE img.7FF6077B16E7 |
    00007FF6077B16E0 | B8 01000000 | MOV EAX,1 |
    00007FF6077B16E5 | EB 05 | JMP img.7FF6077B16EC |
    00007FF6077B16E7 | B8 00000000 | MOV EAX,0 |
    00007FF6077B16EC | 84C0 | TEST AL,AL |
    00007FF6077B16EE | 74 32 | JE img.7FF6077B1722 |
    00007FF6077B16F0 | 48:8D15 09490000 | LEA RDX,QWORD PTR DS:[7FF6077B6000] | rdx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\enc.bmp", 00007FF6077B6000:u8"无法打开文件!"ame 2025\\enc.bmp"
    00007FF6077B16F7 | 48:8B05 024F0000 | MOV RAX,QWORD PTR DS:[<&std::cerr>] | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B16FE | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1701 | E8 22050000 | CALL <JMP.&std::basic_ostream<char, std::char_traits<cha |
    00007FF6077B1706 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1709 | 48:8B05 004F0000 | MOV RAX,QWORD PTR DS:[<&JMP.&std::basic_ostream<char, st | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1710 | 48:89C2 | MOV RDX,RAX | rdx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\enc.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1713 | E8 70050000 | CALL <JMP.&std::ostream::operator<<(std::ostream& (*)(st |
    00007FF6077B1718 | BB 00000000 | MOV EBX,0 |
    00007FF6077B171D | E9 BD010000 | JMP img.7FF6077B18DF |
    00007FF6077B1722 | 48:C785 E8030000 00 | MOV QWORD PTR SS:[RBP+3E8],400 |
    00007FF6077B172D | B9 00040000 | MOV ECX,400 |
    00007FF6077B1732 | E8 D9040000 | CALL <JMP.&operator new[](unsigned long long)> |
    00007FF6077B1737 | 48:8985 E0030000 | MOV QWORD PTR SS:[RBP+3E0],RAX |
    00007FF6077B173E | 48:8D55 D2 | LEA RDX,QWORD PTR SS:[RBP-2E] | RBP-2E:输入流缓冲区1(存放header)
    00007FF6077B1742 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B1749 | 41:B8 0E000000 | MOV R8D,E | 读取长度:Eh
    00007FF6077B174F | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1752 | E8 41050000 | CALL <JMP.&std::istream::read(char*, long long)> | 读入header
    00007FF6077B1757 | 48:8D55 A0 | LEA RDX,QWORD PTR SS:[RBP-60] | RBP-60:输入流缓冲区2(存放header info)
    00007FF6077B175B | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B1762 | 41:B8 28000000 | MOV R8D,28 | 读取长度:28h
    00007FF6077B1768 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B176B | E8 28050000 | CALL <JMP.&std::istream::read(char*, long long)> | 读入header info
    00007FF6077B1770 | 48:8B95 E0030000 | MOV RDX,QWORD PTR SS:[RBP+3E0] | [rbp-3E0]:输入流缓冲区3(存放文件头后的1024bytes)
    00007FF6077B1777 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B177E | 41:B8 00040000 | MOV R8D,400 | 读取长度:400h
    00007FF6077B1784 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1787 | E8 0C050000 | CALL <JMP.&std::istream::read(char*, long long)> | 读入文件头后的1024bytes
    00007FF6077B178C | 48:8D55 D2 | LEA RDX,QWORD PTR SS:[RBP-2E] | [rbp-2E]:write参数(header)
    00007FF6077B1790 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B1794 | 41:B8 0E000000 | MOV R8D,E |
    00007FF6077B179A | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B179D | E8 EE040000 | CALL <JMP.&std::ostream::write(char const*, long long)> |
    00007FF6077B17A2 | 48:8D55 A0 | LEA RDX,QWORD PTR SS:[RBP-60] | [rbp-60]:"114514puiqneravcixpbhqn;afnv-92piwgspdifjv"
    00007FF6077B17A6 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B17AA | 41:B8 28000000 | MOV R8D,28 | 28:'('
    00007FF6077B17B0 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B17B3 | E8 D8040000 | CALL <JMP.&std::ostream::write(char const*, long long)> |
    00007FF6077B17B8 | 48:8B95 E0030000 | MOV RDX,QWORD PTR SS:[RBP+3E0] |
    00007FF6077B17BF | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B17C3 | 41:B8 00040000 | MOV R8D,400 |
    00007FF6077B17C9 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B17CC | E8 BF040000 | CALL <JMP.&std::ostream::write(char const*, long long)> |
    00007FF6077B17D1 | 48:83BD E0030000 00 | CMP QWORD PTR SS:[RBP+3E0],0 |
    00007FF6077B17D9 | 74 0F | JE img.7FF6077B17EA |
    00007FF6077B17DB | 48:8B85 E0030000 | MOV RAX,QWORD PTR SS:[RBP+3E0] |
    00007FF6077B17E2 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B17E5 | E8 36040000 | CALL <JMP.&operator delete[](void*)> |
    00007FF6077B17EA | 8B55 A4 | MOV EDX,DWORD PTR SS:[RBP-5C] |
    00007FF6077B17ED | 0FB745 AE | MOVZX EAX,WORD PTR SS:[RBP-52] |
    00007FF6077B17F1 | 0FB7C0 | MOVZX EAX,AX |
    00007FF6077B17F4 | 0FAFC2 | IMUL EAX,EDX |
    00007FF6077B17F7 | 83C0 1F | ADD EAX,1F |
    00007FF6077B17FA | 8D50 1F | LEA EDX,QWORD PTR DS:[RAX+1F] |
    00007FF6077B17FD | 85C0 | TEST EAX,EAX |
    00007FF6077B17FF | 0F48C2 | CMOVS EAX,EDX |
    00007FF6077B1802 | C1F8 05 | SAR EAX,5 |
    00007FF6077B1805 | C1E0 02 | SHL EAX,2 |
    00007FF6077B1808 | 8985 DC030000 | MOV DWORD PTR SS:[RBP+3DC],EAX |
    00007FF6077B180E | 8B45 A8 | MOV EAX,DWORD PTR SS:[RBP-58] |
    00007FF6077B1811 | 99 | CDQ |
    00007FF6077B1812 | 31D0 | XOR EAX,EDX |
    00007FF6077B1814 | 29D0 | SUB EAX,EDX |
    00007FF6077B1816 | 0FAF85 DC030000 | IMUL EAX,DWORD PTR SS:[RBP+3DC] |
    00007FF6077B181D | 48:98 | CDQE |
    00007FF6077B181F | 48:8985 D0030000 | MOV QWORD PTR SS:[RBP+3D0],RAX |
    00007FF6077B1826 | 48:8B85 D0030000 | MOV RAX,QWORD PTR SS:[RBP+3D0] |
    00007FF6077B182D | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1830 | E8 DB030000 | CALL <JMP.&operator new[](unsigned long long)> |
    00007FF6077B1835 | 48:8985 C8030000 | MOV QWORD PTR SS:[RBP+3C8],RAX | 111
    00007FF6077B183C | 48:8B8D D0030000 | MOV RCX,QWORD PTR SS:[RBP+3D0] |
    00007FF6077B1843 | 48:8B95 C8030000 | MOV RDX,QWORD PTR SS:[RBP+3C8] | 111
    00007FF6077B184A | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B1851 | 49:89C8 | MOV R8,RCX | r8:&"114514puiqneravcixpbhqn;afnv-92piwgspdifjv", rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B1854 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1857 | E8 3C040000 | CALL <JMP.&std::istream::read(char*, long long)> |
    00007FF6077B185C | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B1863 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1866 | E8 15040000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch |
    00007FF6077B186B | 80BD 28040000 00 | CMP BYTE PTR SS:[RBP+428],0 |
    00007FF6077B1872 | 74 24 | JE img.7FF6077B1898 |
    00007FF6077B1874 | 48:8B85 D0030000 | MOV RAX,QWORD PTR SS:[RBP+3D0] |
    00007FF6077B187B | 89C1 | MOV ECX,EAX |
    00007FF6077B187D | 48:8B95 20040000 | MOV RDX,QWORD PTR SS:[RBP+420] |
    00007FF6077B1884 | 48:8B85 C8030000 | MOV RAX,QWORD PTR SS:[RBP+3C8] | 关注此处[rbp+3C8],疑似存放处理后的数据的缓冲区
    00007FF6077B188B | 49:89D0 | MOV R8,RDX | r8:&"114514puiqneravcixpbhqn;afnv-92piwgspdifjv", rdx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\enc.bmp"v"
    00007FF6077B188E | 89CA | MOV EDX,ECX |
    00007FF6077B1890 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1893 | E8 A8FBFFFF | CALL img.7FF6077B1440 | modifiedRC4
    00007FF6077B1898 | 48:8B8D D0030000 | MOV RCX,QWORD PTR SS:[RBP+3D0] |
    00007FF6077B189F | 48:8B95 C8030000 | MOV RDX,QWORD PTR SS:[RBP+3C8] | [rbp+3C8]:输出的图像数据
    00007FF6077B18A6 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B18AA | 49:89C8 | MOV R8,RCX | R8:数据长度
    00007FF6077B18AD | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B18B0 | E8 DB030000 | CALL <JMP.&std::ostream::write(char const*, long long)> | 写入加密后的数据
    00007FF6077B18B5 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B18B9 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B18BC | E8 A7030000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
    00007FF6077B18C1 | 48:83BD C8030000 00 | CMP QWORD PTR SS:[RBP+3C8],0 |
    00007FF6077B18C9 | 74 0F | JE img.7FF6077B18DA |
    00007FF6077B18CB | 48:8B85 C8030000 | MOV RAX,QWORD PTR SS:[RBP+3C8] |
    00007FF6077B18D2 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B18D5 | E8 46030000 | CALL <JMP.&operator delete[](void*)> |
    00007FF6077B18DA | BB 01000000 | MOV EBX,1 |
    00007FF6077B18DF | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B18E3 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B18E6 | E8 6D030000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
    00007FF6077B18EB | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B18F2 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B18F5 | E8 76030000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch |
    00007FF6077B18FA | 89D8 | MOV EAX,EBX |
    00007FF6077B18FC | EB 2E | JMP img.7FF6077B192C |
    00007FF6077B18FE | 48:89C3 | MOV RBX,RAX | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1901 | 48:8D45 E0 | LEA RAX,QWORD PTR SS:[RBP-20] | [rbp-20]:"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp""
    00007FF6077B1905 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1908 | E8 4B030000 | CALL <JMP.&std::basic_ofstream<char, std::char_traits<ch |
    00007FF6077B190D | EB 03 | JMP img.7FF6077B1912 |
    00007FF6077B190F | 48:89C3 | MOV RBX,RAX | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1912 | 48:8D85 D0010000 | LEA RAX,QWORD PTR SS:[RBP+1D0] |
    00007FF6077B1919 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B191C | E8 4F030000 | CALL <JMP.&std::basic_ifstream<char, std::char_traits<ch |
    00007FF6077B1921 | 48:89D8 | MOV RAX,RBX | rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1924 | 48:89C1 | MOV RCX,RAX | rcx:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp", rax:&"E:\\WorkSpace\\Lab\\0xGame 2025\\flag.bmp"
    00007FF6077B1927 | E8 74140000 | CALL <JMP.&_Unwind_Resume> |
    00007FF6077B192C | 48:81C4 78040000 | ADD RSP,478 |
    00007FF6077B1933 | 5B | POP RBX |
    00007FF6077B1934 | 5D | POP RBP |
    00007FF6077B1935 | C3 | RET |

    通过跟踪三次read的参数(查看读入内存的数据)可以发现,程序除了读取文件头之外还读取了随后的1024bytes。这刚开始其实只是一个猜想,但是通过修改自己创建的flag.bmp文件头后的部分数据可以验证猜想

    修改文件数据

    输入流缓冲区3读取的正是文件头后的1024bytes

    继续运行程序,可以发现生成的enc.bmp中保留了这一部分修改后的数据,于是猜想程序在加密结果中保留了输入流缓冲区3的1024bytes,可以通过扩大修改的数据范围来验证猜想(这里选择进一步修改flag.bmp里1024bytes的结尾,这样开头结尾都有被修改的特征了)

    进一步修改原文件文件头后1024bytes的结尾

    加密结果文件头后1024bytes的开头

    加密结果文件头后1024bytes的结尾

    于是加密结果中多出来的1024bytes来源就显而易见了。至此,程序读写文件的逻辑基本可以明确了:先保留文件头(0-0x36h)和文件头后的1024bytes,然后读取文件头之外的数据(0x36h-0x3589A9h)进行加密,加密完成后先向enc.bmp写入事先保留的文件头和1024bytes,再拼接上加密后的数据

    1
    2
    flag.bmp:文件头(大小0x36h)+图片数据(大小0x358974h)
    enc.bmp:文件头(大小0x36h)+图片数据前1024bytes(大小0x400h)+加密后的图片数据(大小0x358974h)

    解决完文件读写逻辑分析后,接下来着手解决算法逆向。下面是modefiedRC4的详细分析

    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
    __int64 __fastcall modifiedRC4(const char *input, __int64 a2, int numOfDataBytes, __int64 outputBuffer, __int64 key)
    {
    unsigned __int64 v5; // rax
    _BYTE *v6; // rax
    char v7; // dl
    __int64 numOfEncBytes; // rax
    int i; // [rsp+24h] [rbp-Ch]
    int j; // [rsp+24h] [rbp-Ch]
    int k; // [rsp+24h] [rbp-Ch]
    int j_sbox; // [rsp+28h] [rbp-8h]
    int j_; // [rsp+28h] [rbp-8h]
    int i_sbox; // [rsp+2Ch] [rbp-4h]
    int i_; // [rsp+2Ch] [rbp-4h]

    for ( i = 0; i <= 255; ++i )
    S[i] = i;
    i_sbox = 0;
    j_sbox = 0;
    for ( j = 0; j <= 255; ++j )
    {
    i_sbox = (i_sbox + 1) % 256;
    a2 = S[i_sbox] + j_sbox;
    v5 = sub_7FF7688232B0(input, a2, j_sbox, key);// 实际上return key[8],调试可以发现v5即key[8]是0x2A,对应密钥长度
    v6 = sub_7FF7688233D0(input, a2, i_sbox % v5, key);// if(key_len >= i_sbox%key_len ) return key[i_sbox%key_len]
    v7 = a2 + *v6; // v7=S[i_sbox] + j_sbox + key[i_sbox%key_len]
    LODWORD(v6) = ((a2 + *v6) >> 31) >> 24;
    j_sbox = (v6 + v7) - v6; // 这两步实际上等价为j_sbox = j_sboxv7%256
    swapbytes(input, a2, &S[j_sbox], &S[i_sbox]);// 实际上是swapbytes(&S[j_sbox],&S[i_sbox]),前两个参数没有使用
    }
    i_ = 0;
    j_ = 0;
    for ( k = 0; ; ++k )
    {
    numOfEncBytes = ((numOfDataBytes / 256) << 8);// 总数据量对256向下取整,保证加密数据量为256倍数
    if ( k >= numOfEncBytes ) // k从0开始直到最大加密数据量
    break;
    i_ = (i_ + 1) % 256;
    j_ = (S[i_] + j_) % 256;
    swapbytes(input, a2, &S[j_], &S[i_]);
    swapBytes(
    input,
    a2,
    (((k / 256) << 8) + j_ + outputBuffer), // outputBuffer[((k/256)<<8)+j_],此处(k/256)<<8即取每块256bytes的基址,因此交换范围是当前所在的256bytes块
    (outputBuffer + ((k / 256) << 8) + i_)); // outputBuffer[((k/256)<<8)+i_]
    }
    return numOfEncBytes;
    }

    算法中第一、二个循环在初始化S盒,没有对输出数据outputBuffer进行处理,第三个循环才利用S盒的数据来加密outputBuffer。加密outputBuffer的时候,记录了加密的数据量,加密轮数和加密数据量一致。每轮加密同步交换了 i_ 和 j_ 处的S盒数据以及outputBuffer数据,需要注意的是outputBuffer在交换的时候被分成了若干个256bytes块,交换在每个256bytes块内进行。最直观的解密算法是记录下每轮加密使用的 i_ 和 j_ ,然后逆序使用这些索引对交换数据。解密脚本如下:

    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
    def decrypt(input,output):
    with open(input,'rb') as f:
    enc=f.read()

    bmp_header=enc[:0x36]#保留文件头
    enc_data=bytearray(enc[0x436:])#加密数据为文件头+1024bytes后的数据,起始偏移即为0x436
    data_len=len(enc_data)
    aligned_len=(data_len//256)*256#总数据量向下对256取整,即保证数据量为256的倍数

    key="114514puiqneravcixpbhqn;afnv-92piwgspdifjv"
    key_len=len(key)

    #初始化S盒
    S=list(range(256))
    j_sbox=0
    i_sbox=0
    for i in range(256):
    i_sbox=(i_sbox+1)%256
    j_sbox=(j_sbox+S[i_sbox]+ord(key[i_sbox%key_len]))%256
    S[i_sbox],S[j_sbox]=S[j_sbox],S[i_sbox]

    #解密
    #生成交换对
    i_=0
    j_=0
    swap_couples=[]
    for k in range(aligned_len):
    i_=(i_+1)%256
    j_=(j_+S[i_])%256
    S[i_],S[j_]=S[j_],S[i_]

    base=(k//256)*256#取每块256bytes的基址
    pos1=base+i_
    pos2=base+j_
    if pos1<aligned_len and pos2<aligned_len:
    swap_couples.append((pos1,pos2))

    #逆序使用交换对,解密数据
    for pos1,pos2 in reversed(swap_couples):
    enc_data[pos1],enc_data[pos2]=enc_data[pos2],enc_data[pos1]

    with open(output,'wb') as f:
    f.write(bmp_header)
    f.write(enc_data)
    print("[+]decrypt done!")

    decrypt("enc.bmp","flag.bmp")

    解密可以得到下图(没有完全解密成功,但是不影响拿到flag)

    flag.bmp

flag:0xGame{5d0e6f18-ebc0-4231-b0e6-a5821327aefd}

蔷薇地狱

考点:复杂程序分析,RSA解密

难点还是在于复杂

  • 附件是一个Int.exe,一个流量包,还有两个enc文件。运行程序会显示connect failed然后打印一些数据后闪退,同时同一目录下的文件会被加密生成加密文件

    附件

    运行程序显示

  • 用IDA打开程序,shift+F12可以在字符串窗口找到运行程序时显示的”connect failed”(以及其他一些看起来和网络通信有关的字符串,如”127.0.0.1”,”socket failed”)

    字符串窗口

    然后可以跟踪到下面这样一个很像网络通信的函数

    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
    __int64 __fastcall sub_140003D46(__int64 a1, __int64 a2, u_short a3, int a4)
    {
    __int64 v4; // rbx
    unsigned int Error; // eax
    __int64 v6; // rax
    unsigned int v7; // ebx
    __int64 v8; // rbx
    unsigned int v9; // eax
    __int64 v10; // rax
    int v11; // r8d
    int v12; // r9d
    __int64 v13; // rbx
    unsigned int v14; // eax
    __int64 v15; // rax
    int v16; // edx
    int v17; // r8d
    int v18; // r9d
    int v19; // ebx
    int v20; // edx
    int v21; // r8d
    int v22; // r9d
    const char *v23; // rax
    __int64 v24; // rbx
    unsigned int v25; // eax
    __int64 v26; // rax
    __int64 v27; // rax
    __int64 v28; // rax
    __int64 v29; // rax
    int v30; // edx
    int v31; // r8d
    int v32; // r9d
    __int64 v34[4]; // [rsp+0h] [rbp-80h] BYREF
    _BYTE v35[32]; // [rsp+20h] [rbp-60h] BYREF
    sockaddr name; // [rsp+40h] [rbp-40h] BYREF
    unsigned int v37; // [rsp+1F4h] [rbp+174h]
    SOCKET s; // [rsp+1F8h] [rbp+178h]

    if ( WSAStartup(0x202u, (LPWSADATA)&v34[10]) )
    {
    v4 = std::operator<<<std::char_traits<char>>(a1, a2, "WSAStartup failed: ", &std::cerr);
    Error = WSAGetLastError();
    v6 = std::ostream::operator<<(a1, a2, Error, v4);
    std::operator<<<std::char_traits<char>>(a1, a2, "\n", v6);
    return 1;
    }
    else
    {
    s = socket(2, 1, 0);
    if ( s == -1LL )
    {
    v8 = std::operator<<<std::char_traits<char>>(a1, a2, "socket failed: ", &std::cerr);
    v9 = WSAGetLastError();
    v10 = std::ostream::operator<<(a1, a2, v9, v8);
    std::operator<<<std::char_traits<char>>(a1, a2, "\n", v10);
    WSACleanup();
    return 1;
    }
    else
    {
    name.sa_family = 2;
    *(_WORD *)name.sa_data = htons(a3);
    *(_DWORD *)&name.sa_data[2] = inet_addr("127.0.0.1");
    if ( connect(s, &name, 16) == -1 )
    {
    v13 = std::operator<<<std::char_traits<char>>(a1, a2, "connect failed: ", &std::cerr);
    v14 = WSAGetLastError();
    v15 = std::ostream::operator<<(a1, a2, v14, v13);
    std::operator<<<std::char_traits<char>>(a1, a2, "\n", v15);
    closesocket(s);
    WSACleanup();
    return 1;
    }
    else
    {
    sub_140001760(a1, a2, a4, (unsigned int)v35, v11, v12, v34[0], v34[1]);
    v19 = sub_1400087B0(a1, a2, v16, (unsigned int)v35, v17, v18, v34[0]);
    v23 = (const char *)sub_1400087F0(a1, a2, v20, (unsigned int)v35, v21, v22, v34[0]);
    v37 = send(s, v23, v19, 0);
    if ( v37 == -1 )
    {
    v24 = std::operator<<<std::char_traits<char>>(a1, a2, "send failed: ", &std::cerr);
    v25 = WSAGetLastError();
    v26 = std::ostream::operator<<(a1, a2, v25, v24);
    }
    else
    {
    v27 = std::operator<<<std::char_traits<char>>(a1, a2, "Sent ", &std::cout);
    v28 = std::ostream::operator<<(a1, a2, v37, v27);
    v29 = std::operator<<<std::char_traits<char>>(a1, a2, " bytes: ", v28);
    v26 = std::operator<<<char>(a1, a2, v35, v29);
    }
    std::operator<<<std::char_traits<char>>(a1, a2, "\n", v26);
    closesocket(s);
    WSACleanup();
    v7 = 0;
    sub_14000B400(a1, a2, v30, (unsigned int)v35, v31, v32, v34[0]);
    }
    }
    }
    return v7;
    }

    回溯到上级函数,可以得到一个快700行代码的函数。代码量太大,人工分析很有难度,于是借助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
    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
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    __int64 __fastcall sub_140005769(__int64 a1, __int64 a2, _QWORD *a3, int a4)
    {
    int v4; // r9d
    char v5; // bl
    int v6; // esi
    int v7; // edx
    int v8; // r8d
    int v9; // r9d
    char v10; // bl
    int v11; // esi
    int v12; // edx
    int v13; // r8d
    int v14; // r9d
    char v15; // bl
    __int64 v16; // rsi
    int v17; // edx
    int v18; // r8d
    int v19; // r9d
    int v20; // edx
    int v21; // r8d
    int v22; // r9d
    unsigned int v23; // ebx
    int v24; // r8d
    int v25; // r9d
    int v26; // r8d
    int v27; // r9d
    int v28; // edx
    int v29; // r8d
    int v30; // r9d
    int v31; // r8d
    int v32; // r9d
    int v33; // r8d
    int v34; // r9d
    int v35; // edx
    int v36; // r8d
    int v37; // r9d
    int v38; // r8d
    int v39; // r9d
    __int64 v40; // rax
    int v41; // edx
    int v42; // r8d
    int v43; // r9d
    int v44; // edx
    int v45; // r8d
    int v46; // r9d
    int v47; // r8d
    int v48; // r9d
    __int64 v49; // rax
    int v50; // edx
    int v51; // r8d
    int v52; // r9d
    int v53; // edx
    int v54; // r8d
    int v55; // r9d
    int v56; // r8d
    int v57; // r9d
    int v58; // edx
    int v59; // r8d
    int v60; // r9d
    int v61; // edx
    int v62; // r8d
    int v63; // r9d
    int v64; // r8d
    int v65; // r9d
    int v66; // edx
    int v67; // r8d
    int v68; // r9d
    int v69; // edx
    int v70; // r8d
    int v71; // r9d
    __int64 v72; // rbx
    int v73; // r8d
    int v74; // r9d
    __int64 v75; // rax
    int v76; // edx
    int v77; // r8d
    int v78; // r9d
    int v79; // edx
    int v80; // r8d
    int v81; // r9d
    __int64 v82; // rbx
    int v83; // r8d
    int v84; // r9d
    __int64 v85; // rax
    int v86; // edx
    int v87; // r8d
    int v88; // r9d
    int v89; // edx
    int v90; // r8d
    int v91; // r9d
    __int64 v92; // rdx
    int v93; // r8d
    int v94; // r9d
    int v95; // edx
    int v96; // r8d
    int v97; // r9d
    int v98; // r8d
    int v99; // r9d
    int v100; // r8d
    int v101; // r9d
    int v102; // edx
    int v103; // r8d
    int v104; // r9d
    int v105; // r8d
    int v106; // r9d
    int v107; // r8d
    int v108; // r9d
    int v109; // edx
    int v110; // r8d
    int v111; // r9d
    int v112; // r8d
    int v113; // r9d
    int v114; // edx
    int v115; // r8d
    int v116; // r9d
    __int64 v117; // rdx
    int v118; // r8d
    int v119; // r9d
    int v120; // eax
    int v121; // r8d
    int v122; // r9d
    char v123; // r12
    char v124; // r13
    char v125; // r14
    char v126; // r15
    int v127; // r8d
    int v128; // r9d
    int v129; // r8d
    int v130; // r9d
    int v131; // r8d
    int v132; // r9d
    int v133; // edx
    int v134; // r8d
    int v135; // r9d
    int v136; // r8d
    int v137; // r9d
    int v138; // r8d
    int v139; // r9d
    int v140; // r8d
    int v141; // r9d
    int v142; // r8d
    int v143; // r9d
    int v144; // r8d
    int v145; // r9d
    int v146; // r8d
    int v147; // r9d
    int v148; // edx
    int v149; // r8d
    int v150; // r9d
    int v151; // edx
    int v152; // r8d
    int v153; // r9d
    int v154; // r8d
    int v155; // r9d
    int v156; // r8d
    int v157; // r9d
    char v158; // bl
    int v159; // edx
    int v160; // r8d
    int v161; // r9d
    int v162; // edx
    int v163; // r8d
    int v164; // r9d
    __int64 v165; // rbx
    int v166; // r8d
    int v167; // r9d
    __int64 v168; // rax
    int v169; // edx
    int v170; // r8d
    int v171; // r9d
    int v172; // r8d
    int v173; // r9d
    int v174; // r8d
    int v175; // r9d
    int v176; // r9d
    int v177; // r9d
    int v178; // r9d
    __int64 v179; // rdx
    __int64 v180; // rdx
    int v181; // edx
    int v182; // r8d
    int v183; // r9d
    int v184; // r8d
    int v185; // r9d
    int v186; // r8d
    int v187; // r9d
    char v188; // bl
    int v189; // edx
    int v190; // r8d
    int v191; // r9d
    int v192; // edx
    int v193; // r8d
    int v194; // r9d
    __int64 v195; // rbx
    int v196; // r8d
    int v197; // r9d
    __int64 v198; // rax
    int v199; // edx
    int v200; // r8d
    int v201; // r9d
    int v202; // r8d
    int v203; // r9d
    int v204; // r9d
    int v205; // edx
    int v206; // r8d
    int v207; // r9d
    int v208; // r9d
    int v209; // r9d
    int v210; // r9d
    __int64 v211; // rdx
    __int64 v212; // rdx
    int v213; // edx
    int v214; // r8d
    int v215; // r9d
    __int64 v216; // rdx
    int v217; // r8d
    int v218; // r9d
    int v219; // edx
    int v220; // r8d
    int v221; // r9d
    int v222; // edx
    int v223; // r8d
    int v224; // r9d
    int v225; // edx
    int v226; // r8d
    int v227; // r9d
    int v228; // edx
    int v229; // r8d
    int v230; // r9d
    __int64 v232; // [rsp+0h] [rbp-80h]
    __int64 v233; // [rsp+0h] [rbp-80h]
    __int64 v234; // [rsp+0h] [rbp-80h]
    __int64 v235; // [rsp+0h] [rbp-80h]
    __int64 v236; // [rsp+0h] [rbp-80h]
    __int64 v237; // [rsp+0h] [rbp-80h]
    __int64 v238; // [rsp+0h] [rbp-80h]
    __int64 v239; // [rsp+0h] [rbp-80h]
    __int64 v240; // [rsp+0h] [rbp-80h]
    __int64 v241; // [rsp+0h] [rbp-80h]
    __int64 v242; // [rsp+0h] [rbp-80h]
    __int64 v243; // [rsp+0h] [rbp-80h]
    __int64 v244; // [rsp+0h] [rbp-80h]
    __int64 v245; // [rsp+0h] [rbp-80h]
    __int64 v246; // [rsp+0h] [rbp-80h]
    __int64 v247; // [rsp+0h] [rbp-80h]
    __int64 v248; // [rsp+0h] [rbp-80h]
    __int64 v249; // [rsp+0h] [rbp-80h]
    __int64 v250; // [rsp+0h] [rbp-80h]
    __int64 v251; // [rsp+0h] [rbp-80h]
    __int64 v252; // [rsp+0h] [rbp-80h]
    __int64 v253; // [rsp+0h] [rbp-80h]
    __int64 v254; // [rsp+0h] [rbp-80h]
    __int64 v255; // [rsp+0h] [rbp-80h]
    __int64 v256; // [rsp+0h] [rbp-80h]
    __int64 v257; // [rsp+0h] [rbp-80h]
    __int64 v258; // [rsp+0h] [rbp-80h]
    __int64 v259; // [rsp+0h] [rbp-80h]
    __int64 v260; // [rsp+0h] [rbp-80h]
    __int64 v261; // [rsp+0h] [rbp-80h]
    __int64 v262; // [rsp+0h] [rbp-80h]
    __int64 v263; // [rsp+0h] [rbp-80h]
    __int64 v264; // [rsp+0h] [rbp-80h]
    __int64 v265; // [rsp+0h] [rbp-80h]
    __int64 v266; // [rsp+0h] [rbp-80h]
    __int64 v267; // [rsp+0h] [rbp-80h]
    __int64 v268; // [rsp+0h] [rbp-80h]
    __int64 v269; // [rsp+0h] [rbp-80h]
    __int64 v270; // [rsp+0h] [rbp-80h]
    __int64 v271; // [rsp+0h] [rbp-80h]
    __int64 v272; // [rsp+0h] [rbp-80h]
    __int64 v273; // [rsp+0h] [rbp-80h]
    __int64 v274; // [rsp+0h] [rbp-80h]
    __int64 v275; // [rsp+0h] [rbp-80h]
    __int64 v276; // [rsp+0h] [rbp-80h]
    __int64 v277; // [rsp+0h] [rbp-80h]
    __int64 v278; // [rsp+0h] [rbp-80h]
    __int64 v279; // [rsp+0h] [rbp-80h]
    __int64 v280; // [rsp+0h] [rbp-80h]
    __int64 v281; // [rsp+0h] [rbp-80h]
    __int64 v282; // [rsp+0h] [rbp-80h]
    __int64 v283; // [rsp+0h] [rbp-80h]
    HCRYPTKEY v284; // [rsp+0h] [rbp-80h]
    __int64 v285; // [rsp+0h] [rbp-80h]
    __int64 v286; // [rsp+0h] [rbp-80h]
    __int64 v287; // [rsp+0h] [rbp-80h]
    __int64 v288; // [rsp+0h] [rbp-80h]
    __int64 v289; // [rsp+0h] [rbp-80h]
    __int64 v290; // [rsp+0h] [rbp-80h]
    __int64 v291; // [rsp+0h] [rbp-80h]
    __int64 v292; // [rsp+0h] [rbp-80h]
    __int64 v293; // [rsp+0h] [rbp-80h]
    __int64 v294; // [rsp+0h] [rbp-80h]
    __int64 v295; // [rsp+0h] [rbp-80h]
    HCRYPTKEY v296; // [rsp+0h] [rbp-80h]
    __int64 v297; // [rsp+0h] [rbp-80h]
    __int64 v298; // [rsp+0h] [rbp-80h]
    __int64 v299; // [rsp+0h] [rbp-80h]
    __int64 v300; // [rsp+0h] [rbp-80h]
    __int64 v301; // [rsp+0h] [rbp-80h]
    __int64 v302; // [rsp+0h] [rbp-80h]
    __int64 v303; // [rsp+8h] [rbp-78h]
    __int64 v304; // [rsp+8h] [rbp-78h]
    __int64 v305; // [rsp+8h] [rbp-78h]
    __int64 v306; // [rsp+8h] [rbp-78h]
    __int64 v307; // [rsp+8h] [rbp-78h]
    __int64 v308; // [rsp+8h] [rbp-78h]
    __int64 v309; // [rsp+8h] [rbp-78h]
    __int64 v310; // [rsp+8h] [rbp-78h]
    __int64 v311; // [rsp+8h] [rbp-78h]
    __int64 v312; // [rsp+8h] [rbp-78h]
    __int64 v313; // [rsp+8h] [rbp-78h]
    __int64 v314; // [rsp+8h] [rbp-78h]
    __int64 v315; // [rsp+8h] [rbp-78h]
    __int64 v316; // [rsp+8h] [rbp-78h]
    __int64 v317; // [rsp+8h] [rbp-78h]
    __int64 v318; // [rsp+8h] [rbp-78h]
    __int64 v319; // [rsp+8h] [rbp-78h]
    __int64 v320; // [rsp+8h] [rbp-78h]
    __int64 v321; // [rsp+8h] [rbp-78h]
    __int64 v322; // [rsp+8h] [rbp-78h]
    __int64 v323; // [rsp+8h] [rbp-78h]
    __int64 v324; // [rsp+8h] [rbp-78h]
    __int64 v325; // [rsp+8h] [rbp-78h]
    __int64 v326; // [rsp+8h] [rbp-78h]
    __int64 v327; // [rsp+8h] [rbp-78h]
    __int64 v328; // [rsp+8h] [rbp-78h]
    __int64 v329; // [rsp+8h] [rbp-78h]
    __int64 v330; // [rsp+8h] [rbp-78h]
    __int64 v331; // [rsp+8h] [rbp-78h]
    __int64 v332; // [rsp+8h] [rbp-78h]
    __int64 v333; // [rsp+8h] [rbp-78h]
    __int64 v334; // [rsp+8h] [rbp-78h]
    __int64 v335; // [rsp+8h] [rbp-78h]
    __int64 v336; // [rsp+8h] [rbp-78h]
    __int64 v337; // [rsp+8h] [rbp-78h]
    __int64 v338; // [rsp+8h] [rbp-78h]
    __int64 v339; // [rsp+8h] [rbp-78h]
    int v340; // [rsp+10h] [rbp-70h]
    int v341; // [rsp+10h] [rbp-70h]
    __int64 v342; // [rsp+10h] [rbp-70h]
    int v343; // [rsp+10h] [rbp-70h]
    int v344; // [rsp+10h] [rbp-70h]
    __int64 v345; // [rsp+10h] [rbp-70h]
    char v346; // [rsp+30h] [rbp-50h]
    char v347; // [rsp+38h] [rbp-48h]
    _BYTE v348[48]; // [rsp+40h] [rbp-40h] BYREF
    _BYTE v349[48]; // [rsp+70h] [rbp-10h] BYREF
    int v350[124]; // [rsp+A0h] [rbp+20h] BYREF
    int v351[124]; // [rsp+290h] [rbp+210h] BYREF
    _BYTE v352[16]; // [rsp+480h] [rbp+400h] BYREF
    _BYTE v353[16]; // [rsp+490h] [rbp+410h] BYREF
    BYTE v354[28]; // [rsp+4A0h] [rbp+420h] BYREF
    BYTE v355[4]; // [rsp+4BCh] [rbp+43Ch] BYREF
    BYTE pbData[2]; // [rsp+4C0h] [rbp+440h] BYREF
    __int16 v357; // [rsp+4C2h] [rbp+442h]
    int v358; // [rsp+4C4h] [rbp+444h]
    int v359; // [rsp+4C8h] [rbp+448h]
    __int64 v360; // [rsp+4CCh] [rbp+44Ch]
    __int64 v361; // [rsp+4D4h] [rbp+454h]
    __int64 v362; // [rsp+4DCh] [rbp+45Ch]
    __int64 v363; // [rsp+4E4h] [rbp+464h]
    _BYTE IV[32]; // [rsp+4F0h] [rbp+470h] BYREF IV
    _BYTE key[32]; // [rsp+510h] [rbp+490h] BYREF 密钥
    BYTE MODE[32]; // [rsp+530h] [rbp+4B0h] BYREF 模式参数
    BYTE v367[16]; // [rsp+550h] [rbp+4D0h] BYREF
    BYTE pbBuffer[8]; // [rsp+560h] [rbp+4E0h] BYREF
    __int64 v369; // [rsp+568h] [rbp+4E8h]
    __int64 v370; // [rsp+570h] [rbp+4F0h]
    __int64 v371; // [rsp+578h] [rbp+4F8h]
    HCRYPTKEY hKey; // [rsp+588h] [rbp+508h] BYREF
    HCRYPTPROV phProv; // [rsp+590h] [rbp+510h] BYREF
    char v374; // [rsp+59Ah] [rbp+51Ah] BYREF
    char v375; // [rsp+59Bh] [rbp+51Bh] BYREF
    char v376; // [rsp+59Ch] [rbp+51Ch] BYREF
    char v377; // [rsp+59Dh] [rbp+51Dh] BYREF
    char v378; // [rsp+59Eh] [rbp+51Eh] BYREF
    char v379; // [rsp+59Fh] [rbp+51Fh] BYREF
    _BYTE v380[32]; // [rsp+5A0h] [rbp+520h] BYREF
    _BYTE v381[32]; // [rsp+5C0h] [rbp+540h] BYREF
    _BYTE v382[32]; // [rsp+5E0h] [rbp+560h] BYREF
    _BYTE v383[16]; // [rsp+600h] [rbp+580h] BYREF
    _BYTE v384[32]; // [rsp+610h] [rbp+590h] BYREF
    _BYTE v385[16]; // [rsp+630h] [rbp+5B0h] BYREF
    _BYTE v386[16]; // [rsp+640h] [rbp+5C0h] BYREF
    _BYTE v387[16]; // [rsp+650h] [rbp+5D0h] BYREF
    _BYTE v388[16]; // [rsp+660h] [rbp+5E0h] BYREF
    _BYTE v389[16]; // [rsp+670h] [rbp+5F0h] BYREF
    _BYTE v390[32]; // [rsp+680h] [rbp+600h] BYREF
    _BYTE v391[16]; // [rsp+6A0h] [rbp+620h] BYREF
    _BYTE v392[32]; // [rsp+6B0h] [rbp+630h] BYREF
    _BYTE v393[16]; // [rsp+6D0h] [rbp+650h] BYREF
    _BYTE v394[48]; // [rsp+6E0h] [rbp+660h] BYREF
    _BYTE v395[16]; // [rsp+710h] [rbp+690h] BYREF
    _BYTE v396[16]; // [rsp+720h] [rbp+6A0h] BYREF
    _BYTE v397[32]; // [rsp+730h] [rbp+6B0h] BYREF
    _BYTE v398[48]; // [rsp+750h] [rbp+6D0h] BYREF
    _BYTE v399[32]; // [rsp+780h] [rbp+700h] BYREF
    _BYTE v400[48]; // [rsp+7A0h] [rbp+720h] BYREF
    _BYTE v401[32]; // [rsp+7D0h] [rbp+750h] BYREF
    _BYTE v402[48]; // [rsp+7F0h] [rbp+770h] BYREF
    _BYTE v403[32]; // [rsp+820h] [rbp+7A0h] BYREF
    _BYTE v404[48]; // [rsp+840h] [rbp+7C0h] BYREF
    _BYTE v405[32]; // [rsp+870h] [rbp+7F0h] BYREF
    _BYTE v406[48]; // [rsp+890h] [rbp+810h] BYREF
    _BYTE v407[32]; // [rsp+8C0h] [rbp+840h] BYREF
    _BYTE v408[32]; // [rsp+8E0h] [rbp+860h] BYREF
    _BYTE v409[48]; // [rsp+900h] [rbp+880h] BYREF
    _BYTE v410[32]; // [rsp+930h] [rbp+8B0h] BYREF
    _BYTE v411[40]; // [rsp+950h] [rbp+8D0h] BYREF
    char *v412; // [rsp+978h] [rbp+8F8h]
    char *v413; // [rsp+980h] [rbp+900h]
    char *v414; // [rsp+988h] [rbp+908h]
    char *v415; // [rsp+990h] [rbp+910h]
    char *v416; // [rsp+998h] [rbp+918h]
    char *v417; // [rsp+9A0h] [rbp+920h]
    __int64 v418; // [rsp+9A8h] [rbp+928h]
    BYTE *v419; // [rsp+9B0h] [rbp+930h]
    char v420; // [rsp+9BFh] [rbp+93Fh]

    sub_140006CC0();
    v420 = 0;
    v5 = 0;
    v6 = 0;
    if ( a4 <= 1 ) // 此处开始解析命令行参数,这是第一个参数的处理
    {
    v416 = &v375;
    v6 = 1;
    sub_14000B350(a1, 1, (unsigned int)&unk_14000E1D0, (unsigned int)MODE, (unsigned int)&v375, v4);
    }
    else
    {
    v417 = &v374;
    v5 = 1;
    sub_14000B350(a1, 0, a3[1], (unsigned int)MODE, (unsigned int)&v374, v4);
    }
    if ( (_BYTE)v6 )
    sub_140009C30(a1, v6, v7, (unsigned int)&v375, v8, v9);
    if ( v5 )
    sub_140009C30(a1, v6, v7, (unsigned int)&v374, v8, v9);
    v10 = 0;
    v11 = 0;
    if ( a4 <= 2 ) // 这是第二个参数的处理
    {
    v414 = &v377;
    v11 = 1;
    sub_14000B350(a1, 1, (unsigned int)&unk_14000E1D0, (unsigned int)key, (unsigned int)&v377, v9);
    }
    else
    {
    v415 = &v376;
    v10 = 1;
    sub_14000B350(a1, 0, a3[2], (unsigned int)key, (unsigned int)&v376, v9);
    }
    if ( (_BYTE)v11 )
    sub_140009C30(a1, v11, v12, (unsigned int)&v377, v13, v14);
    if ( v10 )
    sub_140009C30(a1, v11, v12, (unsigned int)&v376, v13, v14);
    v15 = 0;
    v16 = 0LL;
    if ( a4 <= 3 ) // 这是第三个参数的处理
    {
    v412 = &v379;
    v16 = 1LL;
    sub_14000B350(a1, 1, (unsigned int)&unk_14000E1D0, (unsigned int)IV, (unsigned int)&v379, v14);
    }
    else
    {
    v413 = &v378;
    v15 = 1;
    sub_14000B350(a1, 0, a3[3], (unsigned int)IV, (unsigned int)&v378, v14);
    }
    if ( (_BYTE)v16 )
    sub_140009C30(a1, v16, v17, (unsigned int)&v379, v18, v19);
    if ( v15 )
    sub_140009C30(a1, v16, v17, (unsigned int)&v378, v18, v19);
    if ( CryptAcquireContextA(&phProv, 0LL, 0LL, 0x18u, 0xF0000000) )// 使用Microsoft CryptoAPI,0X18表示PROV_RSA_AES,程序使用RSA加密密钥和IV发送到服务器
    {
    if ( a4 <= 3 )
    {
    if ( !CryptGenRandom(phProv, 0x20u, pbBuffer) )// 生成32字节随机密钥
    {
    v23 = 1;
    goto LABEL_63;
    }
    if ( !CryptGenRandom(phProv, 0x10u, v367) )// 生成16字节随机IV
    {
    v23 = 1;
    goto LABEL_63;
    }
    sub_1400028FE(a1, v16, (__int64)pbBuffer, (__int64)v387, 32);
    sub_140005071(a1, v16, (unsigned int)v387, (unsigned int)v386, v56, v57, v232, v308);// 疑似加密函数
    sub_140003D46(a1, v16, 0x1F90u, (int)v386);// 疑似网络通信的函数
    sub_1400029A2(a1, v16, v58, (unsigned int)v386, v59, v60, v243);
    sub_1400029A2(a1, v16, v61, (unsigned int)v387, v62, v63, v244);
    sub_1400028FE(a1, v16, (__int64)v367, (__int64)v389, 16);
    sub_140005071(a1, v16, (unsigned int)v389, (unsigned int)v388, v64, v65, v245, v309);
    sub_140003D46(a1, v16, 0x1F90u, (int)v388);
    sub_1400029A2(a1, v16, v66, (unsigned int)v388, v67, v68, v246);
    sub_1400029A2(a1, v16, v69, (unsigned int)v389, v70, v71, v247);
    }
    else
    {
    if ( sub_14000C650(a1, v16, (int)"-d", (int)MODE, v21, v22) )
    v420 = 1; // 命令行参数包含"-d"的时候,v449被设为1,进入解密模式
    sub_14000B130(a1, v16, (unsigned int)key, (unsigned int)v380, v24, v25, v232, v303);// 从命令行获取key
    sub_1400051EC(a1, v16, (__int64)pbBuffer, (int)v380, v26, v27);// 处理key
    sub_14000B400(a1, v16, v28, (unsigned int)v380, v29, v30, v233);
    sub_14000B130(a1, v16, (unsigned int)IV, (unsigned int)v381, v31, v32, v234, v304);// 从命令行获取IV
    sub_1400051EC(a1, v16, (__int64)v367, (int)v381, v33, v34);// 处理IV
    sub_14000B400(a1, v16, v35, (unsigned int)v381, v36, v37, v235);
    sub_1400028FE(a1, v16, (__int64)pbBuffer, (__int64)v383, 32);
    sub_140001534(a1, v16, (unsigned int)v383, (unsigned int)v382, v38, v39, v236, v305);
    v40 = std::operator<<<char>(a1, v16, v382, &std::cout);
    std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v40);
    sub_14000B400(a1, v16, v41, (unsigned int)v382, v42, v43, v237);
    sub_1400029A2(a1, v16, v44, (unsigned int)v383, v45, v46, v238);
    sub_1400028FE(a1, v16, (__int64)v367, (__int64)v385, 16);
    sub_140001534(a1, v16, (unsigned int)v385, (unsigned int)v384, v47, v48, v239, v306);
    v49 = std::operator<<<char>(a1, v16, v384, &std::cout);
    std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v49);
    sub_14000B400(a1, v16, v50, (unsigned int)v384, v51, v52, v240);
    sub_1400029A2(a1, v16, v53, (unsigned int)v385, v54, v55, v241);
    }
    v72 = std::operator<<<std::char_traits<char>>(a1, v16, "Key: ", &std::cout);
    sub_1400028FE(a1, v16, (__int64)pbBuffer, (__int64)v391, 32);
    sub_140001534(a1, v16, (unsigned int)v391, (unsigned int)v390, v73, v74, v242, v307);
    v75 = std::operator<<<char>(a1, v16, v390, v72);
    std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v75);
    sub_14000B400(a1, v16, v76, (unsigned int)v390, v77, v78, v248);
    sub_1400029A2(a1, v16, v79, (unsigned int)v391, v80, v81, v249);
    v82 = std::operator<<<std::char_traits<char>>(a1, v16, "IV: ", &std::cout);
    sub_1400028FE(a1, v16, (__int64)v367, (__int64)v393, 16);
    sub_140001534(a1, v16, (unsigned int)v393, (unsigned int)v392, v83, v84, v250, v310);
    v85 = std::operator<<<char>(a1, v16, v392, v82);
    std::ostream::operator<<(a1, v16, &std::endl<char,std::char_traits<char>>, v85);
    sub_14000B400(a1, v16, v86, (unsigned int)v392, v87, v88, v251);
    sub_1400029A2(a1, v16, v89, (unsigned int)v393, v90, v91, v252);
    pbData[0] = 8;
    pbData[1] = 2;
    v357 = 0;
    v358 = 26128;
    v359 = 32;
    v360 = *(_QWORD *)pbBuffer;
    v361 = v369;
    v362 = v370;
    v363 = v371;
    *(_DWORD *)v355 = 2;
    CryptImportKey(phProv, pbData, 0x2Cu, 0LL, 0, &hKey);// 导入密钥
    CryptSetKeyParam(hKey, 4u, v355, 0); // 设置密钥参数
    CryptSetKeyParam(hKey, 1u, v367, 0); // 设置IV
    std::filesystem::current_path[abi:cxx11](a1, v16, v92, v394);// 获取当前路径
    sub_140008E50(a1, v16, (unsigned int)v394, (unsigned int)v354, v93, v94, v253, v311);
    v419 = v354;
    sub_1400091B0(a1, v16, v95, (unsigned int)v394, v96, v97, v254);
    sub_140008E90(a1, v16, (_DWORD)v419, (unsigned int)v395, v98, v99, v255, v312);
    sub_140009230(a1, v16, (unsigned int)v395, (unsigned int)v353, v100, v101, v256, v313);
    sub_140008EE0(a1, v16, v102, (unsigned int)v395, v103, v104, v257);
    sub_140008E90(a1, v16, (_DWORD)v419, (unsigned int)v396, v105, v106, v258, v314);
    sub_140008F00(a1, v16, (unsigned int)v396, (unsigned int)v352, v107, v108, v259, v315);
    sub_140008EE0(a1, v16, v109, (unsigned int)v396, v110, v111, v260);
    while ( (unsigned __int8)sub_1400092E0(a1, v16, (unsigned int)v352, (unsigned int)v353, v112, v113, v261, v316) )// 遍历当前路径下的所有文件
    {
    v418 = std::filesystem::__cxx11::directory_iterator::operator*(a1, v16, v216, v353);
    if ( (unsigned __int8)sub_1400082F0(a1, v16, v114, v418, v115, v116, v298) == 1 )
    {
    v120 = sub_140008320(a1, v16, v117, v418, v118, v119, v261);
    sub_140009030(a1, v16, v120, (unsigned int)v348, v121, v122, v262, v316);
    a1 = 0LL;
    v123 = 0;
    v124 = 0;
    v125 = 0;
    v126 = 0;
    v347 = 0;
    sub_140008440(0, 0, (unsigned int)v348, (unsigned int)v398, v127, v128, v263, v317);
    sub_1400083D0(0, 0, (unsigned int)v398, (unsigned int)v397, v129, v130, v264, v318);
    v16 = 1LL;
    if ( (unsigned __int8)sub_14000C6D0(0, 1, (unsigned int)L".exe", (unsigned int)v397, v131, v132, v265, v319) )// 跳过exe文件
    goto LABEL_39;
    sub_140008440(0, 1, (unsigned int)v348, (unsigned int)v400, v134, v135, v266, v316);
    a1 = 1LL;
    sub_1400083D0(1, 1, (unsigned int)v400, (unsigned int)v399, v136, v137, v267, v320);
    v123 = 1;
    if ( (unsigned __int8)sub_14000C6D0(1, 1, (unsigned int)".", (unsigned int)v399, v138, v139, v268, v321) )
    goto LABEL_39;
    sub_140008440(1, 1, (unsigned int)v348, (unsigned int)v402, v134, v135, v266, v316);
    v124 = 1;
    sub_1400083D0(1, 1, (unsigned int)v402, (unsigned int)v401, v140, v141, v269, v322);
    v125 = 1;
    if ( (unsigned __int8)sub_14000C6D0(1, 1, (unsigned int)L".dec", (unsigned int)v401, v142, v143, v270, v323)// 跳过dec文件
    || (sub_140008440(1, 1, (unsigned int)v348, (unsigned int)v404, v134, v135, v266, v316),
    v126 = 1,
    sub_1400083D0(1, 1, (unsigned int)v404, (unsigned int)v403, v144, v145, v271, v324),
    v347 = 1,
    (unsigned __int8)sub_14000C6D0(1, 1, (unsigned int)".", (unsigned int)v403, v146, v147, v272, v325)) )// 跳过当前目录的.文件
    {
    LABEL_39:
    v346 = 1;
    }
    else
    {
    v346 = 0;
    }
    if ( v347 )
    sub_14000C290(a1, 1, v133, (unsigned int)v403, v134, v135, v266);
    if ( v126 )
    sub_1400091B0(a1, 1, v133, (unsigned int)v404, v134, v135, v266);
    if ( v125 )
    sub_14000C290(a1, 1, v133, (unsigned int)v401, v134, v135, v266);
    if ( v124 )
    sub_1400091B0(a1, 1, v133, (unsigned int)v402, v134, v135, v266);
    if ( v123 )
    sub_14000C290(a1, 1, v133, (unsigned int)v399, v134, v135, v266);
    if ( (_BYTE)a1 )
    sub_1400091B0(a1, 1, v133, (unsigned int)v400, v134, v135, v266);
    sub_14000C290(a1, 1, v133, (unsigned int)v397, v134, v135, v266);
    sub_1400091B0(a1, 1, v148, (unsigned int)v398, v149, v150, v273);
    if ( !v346 )
    {
    if ( v420 != 1 ) // 加密模式
    {
    sub_140008440(a1, 1, (unsigned int)v348, (unsigned int)v406, v152, v153, v274, v316);
    sub_1400083D0(a1, 1, (unsigned int)v406, (unsigned int)v405, v154, v155, v275, v326);
    v158 = sub_14000C6D0(a1, 1, (unsigned int)".", (unsigned int)v405, v156, v157, v276, v327);
    sub_14000C290(a1, 1, v159, (unsigned int)v405, v160, v161, v277);
    sub_1400091B0(a1, 1, v162, (unsigned int)v406, v163, v164, v278);
    if ( !v158 )
    {
    v165 = std::operator<<<wchar_t,std::char_traits<wchar_t>>(a1, 1LL, L"Encrypting file: ", &std::wcout);
    sub_1400083D0(a1, 1, (unsigned int)v348, (unsigned int)v407, v166, v167, v274, v316);
    v168 = std::operator<<<wchar_t>(a1, 1LL, v407, v165);
    std::wostream::operator<<(a1, 1LL, &std::endl<wchar_t,std::char_traits<wchar_t>>, v168);
    sub_14000C290(a1, 1, v169, (unsigned int)v407, v170, v171, v279);
    sub_140009030(a1, 1, (unsigned int)v348, (unsigned int)v349, v172, v173, v280, v328);
    sub_1400091E0(a1, 1, (unsigned int)".", (unsigned int)v349, v174, v175, v281, v329);
    sub_140009B20(a1, 1, (unsigned int)v348, (unsigned int)v351, 4, v176, v282, v330, v340);// 读取输入文件
    sub_140009B60(a1, 1, (unsigned int)v349, (unsigned int)v350, 4, v177, v283, v331, v341);// 准备输出文件
    sub_1400052D0(a1, 1, (int)v351, hKey, (int)v350, v178, v284, v332, v342);// 执行加密
    std::ofstream::~ofstream(a1, 1LL, v179, v350);
    std::ifstream::~ifstream(a1, 1LL, v180, v351);
    sub_1400091B0(a1, 1, v181, (unsigned int)v349, v182, v183, v285);
    }
    }
    else // 解密模式
    {
    sub_140008440(a1, 1, (unsigned int)v348, (unsigned int)v409, v152, v153, v274, v316);
    sub_1400083D0(a1, 1, (unsigned int)v409, (unsigned int)v408, v184, v185, v286, v333);
    v188 = sub_14000C750(a1, 1, (unsigned int)".", (unsigned int)v408, v186, v187, v287, v334);
    sub_14000C290(a1, 1, v189, (unsigned int)v408, v190, v191, v288);
    sub_1400091B0(a1, 1, v192, (unsigned int)v409, v193, v194, v289);
    if ( !v188 )
    {
    v195 = std::operator<<<wchar_t,std::char_traits<wchar_t>>(a1, 1LL, L"Decrypting file: ", &std::wcout);
    sub_1400083D0(a1, 1, (unsigned int)v348, (unsigned int)v410, v196, v197, v274, v316);
    v198 = std::operator<<<wchar_t>(a1, 1LL, v410, v195);
    std::wostream::operator<<(a1, 1LL, &std::endl<wchar_t,std::char_traits<wchar_t>>, v198);
    sub_14000C290(a1, 1, v199, (unsigned int)v410, v200, v201, v290);
    sub_140009030(a1, 1, (unsigned int)v348, (unsigned int)v349, v202, v203, v291, v335);
    sub_1400090C0(a1, 1, (unsigned int)L"dec", (unsigned int)v411, 2, v204, v292, v336, v340);
    std::filesystem::__cxx11::path::replace_extension(
    (std::filesystem::__cxx11::path *)a1,
    (const std::filesystem::__cxx11::path *)1);
    sub_1400091B0(a1, 1, v205, (unsigned int)v411, v206, v207, v293);
    sub_140009B20(a1, 1, (unsigned int)v348, (unsigned int)v351, 4, v208, v294, v337, v343);// 读取加密文件
    sub_140009B60(a1, 1, (unsigned int)v349, (unsigned int)v350, 4, v209, v295, v338, v344);// 准备输出文件
    sub_140005522(a1, 1, (int)v351, hKey, (int)v350, v210, v296, v339, v345);// 执行解密
    std::ofstream::~ofstream(a1, 1LL, v211, v350);
    std::ifstream::~ifstream(a1, 1LL, v212, v351);
    sub_1400091B0(a1, 1, v213, (unsigned int)v349, v214, v215, v297);
    }
    }
    }
    sub_1400091B0(a1, 1, v151, (unsigned int)v348, v152, v153, v274);
    }
    std::filesystem::__cxx11::directory_iterator::operator++(a1, v16, v117, v353);
    }
    sub_140008EE0(a1, v16, v216, (unsigned int)v352, v217, v218, v298);
    sub_140008EE0(a1, v16, v219, (unsigned int)v353, v220, v221, v299);
    sub_140008EE0(a1, v16, v222, (unsigned int)v354, v223, v224, v300);
    CryptDestroyKey(hKey);
    CryptReleaseContext(phProv, 0);
    v23 = 0;
    }
    else
    {
    v23 = 1;
    }
    LABEL_63:
    sub_14000B400(a1, v16, v20, (unsigned int)IV, v21, v22, v232);
    sub_14000B400(a1, v16, v225, (unsigned int)key, v226, v227, v301);
    sub_14000B400(a1, v16, v228, (unsigned int)MODE, v229, v230, v302);
    return v23;
    }

    下面是疑似的加密函数的内容

    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
    __int64 __fastcall sub_140005071(int a1, int a2, int a3, __int64 a4, __int64 a5, int a6)
    {
    int v6; // r8d
    int v7; // r9d
    int v8; // edx
    int v9; // r8d
    int v10; // r9d
    int v11; // edx
    int v12; // r8d
    int v13; // r9d
    int v14; // r9d
    int v15; // r8d
    int v16; // r9d
    int v17; // edx
    int v18; // r8d
    int v19; // r9d
    int v20; // edx
    int v21; // r8d
    int v22; // r9d
    int v23; // edx
    int v24; // r8d
    int v25; // r9d
    int v26; // edx
    int v27; // r8d
    int v28; // r9d
    __int64 v30; // [rsp+0h] [rbp-B0h] BYREF
    __int64 v31; // [rsp+8h] [rbp-A8h]
    __int64 v32; // [rsp+10h] [rbp-A0h]
    __int64 v33; // [rsp+18h] [rbp-98h]
    _BYTE v34[16]; // [rsp+20h] [rbp-90h] BYREF
    _BYTE v35[16]; // [rsp+30h] [rbp-80h] BYREF
    _BYTE v36[47]; // [rsp+40h] [rbp-70h] BYREF
    char v37; // [rsp+6Fh] [rbp-41h] BYREF
    _BYTE v38[47]; // [rsp+70h] [rbp-40h] BYREF
    char v39; // [rsp+9Fh] [rbp-11h] BYREF
    char *v40; // [rsp+A0h] [rbp-10h]
    char *v41; // [rsp+A8h] [rbp-8h]

    v41 = (char *)&v30 + 111;
    sub_14000B350(
    a1,
    a2,
    (unsigned int)"1294787702630841806287693875819503767329411267154281759595683996937344294354199",
    (unsigned int)(&v30 - 14) + 176,
    (unsigned int)&v30 - 65 + 176,
    a6);
    sub_1400024EE(a1, a2, (unsigned int)v36, (unsigned int)v35, v6, v7, v30, v31);
    sub_14000B400(a1, a2, v8, (unsigned int)v36, v9, v10, v30);
    sub_140009C30(a1, a2, v11, (unsigned int)&v37, v12, v13);
    v40 = &v39;
    sub_14000B350(a1, a2, (unsigned int)"65537", (unsigned int)v38, (unsigned int)&v39, v14);// 65537是典型RSA加密的特征,因此推测此函数为RSA加密
    sub_1400024EE(a1, a2, (unsigned int)v38, (unsigned int)v34, v15, v16, v30, v31);
    sub_14000B400(a1, a2, v17, (unsigned int)v38, v18, v19, v30);
    sub_140009C30(a1, a2, v20, (unsigned int)&v39, v21, v22);
    sub_1400031A0(a1, a2, a3, a4, (unsigned int)v34, (unsigned int)v35, v30, v31, v32, v33);
    sub_1400029A2(a1, a2, v23, (unsigned int)v34, v24, v25, v30);
    sub_1400029A2(a1, a2, v26, (unsigned int)v35, v27, v28, v30);
    return a4;
    }

    可以得到如下的印象点:

    • 程序通过参数个数判断使用加密模式还是解密模式;
    • 程序在加密模式下会遍历同一路径下的文件并进行加密,输出.enc文件,同时程序会将加密使用的key和IV进行RSA加密后发送到服务器(此外由于网络通信函数出现了127.0.0.1,推测是本地服务器);
    • 程序在解密模式下只需要使用Int.exe -d key IV就可以解密文件。

    结合附件的形式(附件包含一个流量包),可以推测本题的突破口在于分析流量包提取出RSA加密后的key和IV,然后就可以解密出key和IV,使用程序自带的解密功能解密文件。

    安全起见,第一步先验证猜想。借助AI搭建一个本地服务器来接收程序的通信数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 本地服务器脚本
    # fake_server.py
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('127.0.0.1', 8080))
    sock.listen(1)
    print("Listening on 127.0.0.1:8080...")
    while True:
    conn, addr = sock.accept()
    data = conn.recv(1024)
    print(f"Received {len(data)} bytes: {data.hex()}")
    conn.close()

    启用服务器后,通过命令行运行Int.exe可以看到本次加密使用的key和IV,以及发送的十进制数据

    运行Int.exe

    服务器会接收到如下数据(服务器接收的数据是发送的数据的十六进制形式)

    服务器接收到的数据

    服务器接收的第一个数据hex解码后和发送的第一个数据对应上了

    然后参考RSA加密解密原理深度剖析可以写出一个脚本来解密

    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
    import gmpy2
    #求解d
    p =gmpy2.mpz(973424135910675582062565817184635606523)
    q =gmpy2.mpz( 1330137249390799512563582841814876337813)
    e =gmpy2.mpz(65537)
    phi_n= (p - 1) * (q - 1)
    d = gmpy2.invert(e, phi_n)
    print("d is:")
    print (d)

    #解密key和IV
    n = gmpy2.mpz(1294787702630841806287693875819503767329411267154281759595683996937344294354199)
    #key_raw是服务器接收的第一个数据,即key加密数据的十六进制数据流
    key_raw="31323532333634383535303738343134373534303233363633363831313039303038333936313636303538303434333331323331383735303239343634393636353236373133353939343233363539"
    #iv_raw是服务器接收的第二个数据,即iv加密数据的十六进制数据流
    iv_raw="393235323537333232303933353132333132353838343232323434353732343930303235353236393838363134393132323438313237363234363634333434323034333234353839393230383333"
    #十六进制解码
    key_hexdec=bytes.fromhex(key_raw)
    iv_hexdec=bytes.fromhex(iv_raw)

    key_enc = gmpy2.mpz(key_hexdec)
    IV_enc = gmpy2.mpz(iv_hexdec)

    #RSA解密
    key_dec = pow(key_enc, d, n)
    IV_dec = pow(IV_enc, d, n)

    print("key (hex):", hex(key_dec))
    print("IV (hex):", hex(IV_dec))

    运行解密脚本得到如下,key和iv与Int.exe的输出一致

    运行解密脚本

    至此,可以确定发送的数据为key和IV的RSA加密结果,服务器接收的数据是加密结果的十六进制编码数据。

    接下来分析流量包提取加密数据流(需要用到WireShark)。由于程序将数据发送到本地8080端口,且数据长度大约为78bytes,根据这个特征可以筛选出key和IV的流量

    key的流量

    IV的流量

    将找到的两串数据复制到脚本的key_raw和iv_raw,可以解密出key和IV

    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
    import gmpy2
    #求解d
    p =gmpy2.mpz(973424135910675582062565817184635606523)
    q =gmpy2.mpz( 1330137249390799512563582841814876337813)
    e =gmpy2.mpz(65537)
    phi_n= (p - 1) * (q - 1)
    d = gmpy2.invert(e, phi_n)
    print("d is:")
    print (d)

    #解密key和IV
    n = gmpy2.mpz(1294787702630841806287693875819503767329411267154281759595683996937344294354199)
    #key_raw是服务器接收的第一个数据,即key加密数据的十六进制数据流
    key_raw="393430353632303933303434363731383332313634363031353936303437303036303837353338373330383137373132363136313931393431323130333436353936333530353534323336333632"
    #iv_raw是服务器接收的第二个数据,即iv加密数据的十六进制数据流
    iv_raw="323636373435303139343936383836343938363534323735373736393036303037343539343139323635373931333033373337353137333038363234343631363938313438363430393939363639"
    #十六进制解码
    key_hexdec=bytes.fromhex(key_raw)
    iv_hexdec=bytes.fromhex(iv_raw)

    key_enc = gmpy2.mpz(key_hexdec)
    IV_enc = gmpy2.mpz(iv_hexdec)

    #RSA解密
    key_dec = pow(key_enc, d, n)
    IV_dec = pow(IV_enc, d, n)

    print("key (hex):", hex(key_dec))
    print("IV (hex):", hex(IV_dec))

    解密结果

    然后就可以使用Int.exe -d key iv解密文件了(注意key和IV不要带0x)

    使用解密模式

    解密出来的文件

    打开txt.dec文件就可以找到flag了

    txt.dec文件

flag:0xGame{I_4m_3o_S1eepy_(´~`)zzZ_zzZ_zzZ_zzZ}

作者

SydzI

发布于

2025-11-16

更新于

2025-11-23

许可协议

评论