0xGame2025Week2WriteUp

0xGame2025 Week2 Reverse方向全解&详解

shuffle

  • 附件用IDA打开,main函数反编译后大概长这样:

    main函数

    这是shuffle函数:

    shuffle函数

    可以看出这是在考随机数,有了种子就可以得到一模一样的随机数序列。回到main函数,双击seed可以得到一个值:

    main函数中的seed

    但是经过测试这个seed解不出flag,回头看可以发现上图中seed还有另一个引用:runtime_env,很可疑了。双击跳转:

    另有其seed

    出现了对seed的赋值,结合名字的”runtime”可以推测这个才是真正的seed

  • 解密脚本:

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    void unshuffle_simple(unsigned char *str, int length) {
    int *rand_vals = (int*)malloc(length * sizeof(int));
    unsigned char temp;
    int i, j;

    // 先获取所有随机值
    for (i = length - 1; i > 0; --i) {
    rand_vals[i] = rand();
    }

    // 逆向洗牌:从前往后
    for (i = 1; i < length; i++) {
    j = rand_vals[i] % (i + 1);
    // 执行相同的交换
    temp = str[i];
    str[i] = str[j];
    str[j] = temp;
    }

    free(rand_vals);
    }

    int main() {
    unsigned int seed = 0x666;
    char flag[] = "23-64bed6}-xm5300-{faGa34-0e04c2e7c2a78f39a4";
    int length = strlen(flag);
    srand(seed);
    unshuffle_simple((unsigned char*)flag, length);
    printf("flag: %s\n", flag);

    return 0;
    }

算数高手

  • 附件程序是个pythonexe,相关知识可以参考Day17:python逆向(王婆卖瓜ing。使用pyinstxtractor解包后可以得到一个文件夹:

    pyinstxtractor解包

    解包得到的文件夹

  • 根据pyinstxtractor的提示可以找到疑似入口文件,同名的文件可能性大点。然后用uncompyle6把pyc转成py格式:

    pyc转成py(忽略我的环境报错)

  • 直接得到flag

BabyJar

  • 附件程序是个jar文件,用jd-gui打开:

    main函数

    encrypt函数

  • 可以看到这是一个java的flag检验程序,加密逻辑在encrypt方法里。上图选中的那行把字节的高位移到低位,低位移到高位,所以再操作一次就可以还原。还要注意encrypt的返回值进行了base64编码。

  • 解密脚本:

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

    def decrypt(enc,key):
    enc=base64.b64decode(enc)
    flag=""
    for i in enc:
    temp=((i&240)>>4|(i&15)<<4)
    flag+=chr(temp^key)
    return flag

    enc="QsY1V5cX9jJyF2JSAgdikwfCEneTAgICUpNnd1Iyk8IXUkJ3QhcyZ8J3YpY="
    key=20
    flag=""
    flag=decrypt(enc,key)
    print(flag)

16bit

  • 根据题目名称和提示可以推测考察16位环境逆向。附件程序不能运行,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
    seg000:0000 ; =============== S U B R O U T I N E =======================================
    seg000:0000
    seg000:0000 ; Attributes: noreturn
    seg000:0000
    seg000:0000 public start
    seg000:0000 start proc near
    seg000:0000 mov ax, seg dseg
    seg000:0003 mov ds, ax ; dseg是数据段,有flag加密后的数据
    seg000:0005 assume ds:dseg
    seg000:0005 mov cx, 17h ; count寄存器,一般存放循环次数
    seg000:0008 mov si, 0
    seg000:000B mov di, 0
    seg000:000E
    seg000:000E loc_1000E: ; CODE XREF: start+1C↓j
    seg000:000E mov al, [si+0Ah] ; si=0时[0Ah]即dseg:000A,是flag密文的位置,此处是在取出密文
    seg000:0012 sub al, 9 ; 减9
    seg000:0014 xor al, 0Eh ; 异或
    seg000:0016 mov [di+38h], al ; 解密后的数据存放到以[38h]即dseg:0038h为起始的区域
    seg000:001A inc si ; 往下取
    seg000:001B inc di ; 往下存
    seg000:001C loop loc_1000E ; si=0时[0Ah]即dseg:000A,是flag密文的位置,此处是在取出密文
    seg000:001E mov cx, 17h ; 更新循环次数
    seg000:0021
    seg000:0021 loc_10021: ; CODE XREF: start+2F↓j
    seg000:0021 mov al, [si+0Ah] ; 此处si=17h,所以取的是以dseg:0021h为起始的数据
    seg000:0025 xor al, 0Eh ; 异或
    seg000:0027 sub al, 9 ; 减9
    seg000:0029 mov [di+38h], al ; 解密后的数据存放到seg:004f为起始的区域
    seg000:002D inc si ; 往下取
    seg000:002E inc di ; 往下存
    seg000:002F loop loc_10021 ; 此处si=17h,所以取的是以dseg:0021h为起始的数据
    seg000:0031 mov byte_100A6, 24h ; '$' ; byte_100A6是"flag=",此处是在打印flag
    seg000:0036 mov dx, 67h ; 'g'
    seg000:0039 mov ah, 9
    seg000:003B int 21h ; DOS - PRINT STRING
    seg000:003B ; DS:DX -> string terminated by "$"
    seg000:003D mov dx, 38h ; '8'
    seg000:0040 mov ah, 9
    seg000:0042 int 21h ; DOS - PRINT STRING
    seg000:0042 ; DS:DX -> string terminated by "$"
    seg000:0044 mov ax, 4C00h
    seg000:0047 int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
    seg000:0047 start endp ; AL = exit code
    seg000:0047
    seg000:0047 seg000 ends
    seg000:0047
    dseg:0009 ; ===========================================================================
    dseg:0009
    dseg:0009 ; Segment type: Pure data
    dseg:0009 dseg segment para public 'DATA' use16
    dseg:0009 assume cs:dseg
    dseg:0009 ;org 9
    dseg:0009 align 2
    dseg:000A db 47h ; G
    dseg:000B db 7Fh ; 
    dseg:000C db 52h ; R
    dseg:000D db 78h ; x
    dseg:000E db 6Ch ; l
    dseg:000F db 74h ; t
    dseg:0010 db 7Eh ; ~
    dseg:0011 db 72h ; r
    dseg:0012 db 47h ; G
    dseg:0013 db 47h ; G
    dseg:0014 db 73h ; s
    dseg:0015 db 5Ah ; Z
    dseg:0016 db 84h
    dseg:0017 db 5Ah ; Z
    dseg:0018 db 43h ; C
    dseg:0019 db 85h
    dseg:001A db 46h ; F
    dseg:001B db 5Ah ; Z
    dseg:001C db 83h
    dseg:001D db 6Fh ; o
    dseg:001E db 46h ; F
    dseg:001F db 5Ah ; Z
    dseg:0020 db 6Ch ; l
    dseg:0021 db 33h ; 3
    dseg:0022 db 30h ; 0
    dseg:0023 db 73h ; s
    dseg:0024 db 32h ; 2
    dseg:0025 db 75h ; u
    dseg:0026 db 66h ; f
    dseg:0027 db 37h ; 7
    dseg:0028 db 61h ; a
    dseg:0029 db 66h ; f
    dseg:002A db 33h ; 3
    dseg:002B db 30h ; 0
    dseg:002C db 78h ; x
    dseg:002D db 66h ; f
    dseg:002E db 40h ; @
    dseg:002F db 35h ; 5
    dseg:0030 db 61h ; a
    dseg:0031 db 4Eh ; N
    dseg:0032 db 64h ; d
    dseg:0033 db 34h ; 4
    dseg:0034 db 65h ; e
    dseg:0035 db 32h ; 2
    dseg:0036 db 33h ; 3
    dseg:0037 db 88h
    dseg:0038 db 0
    dseg:0039 db 0
    dseg:003A db 0
    dseg:003B db 0
    dseg:003C db 0
    dseg:003D db 0
    dseg:003E db 0
    dseg:003F db 0
    dseg:0040 db 0
    dseg:0041 db 0
    dseg:0042 db 0
    dseg:0043 db 0
    dseg:0044 db 0
    dseg:0045 db 0
    dseg:0046 db 0
    dseg:0047 db 0
    dseg:0048 db 0
    dseg:0049 db 0
    dseg:004A db 0
    dseg:004B db 0
    dseg:004C db 0
    dseg:004D db 0
    dseg:004E db 0
    dseg:004F db 0
    dseg:0050 db 0
    dseg:0051 db 0
    dseg:0052 db 0
    dseg:0053 db 0
    dseg:0054 db 0
    dseg:0055 db 0
    dseg:0056 db 0
    dseg:0057 db 0
    dseg:0058 db 0
    dseg:0059 db 0
    dseg:005A db 0
    dseg:005B db 0
    dseg:005C db 0
    dseg:005D db 0
    dseg:005E db 0
    dseg:005F db 0
    dseg:0060 db 0
    dseg:0061 db 0
    dseg:0062 db 0
    dseg:0063 db 0
    dseg:0064 db 0
    dseg:0065 db 0
    dseg:0066 byte_100A6 db 0 ; DATA XREF: start+31↑w
    dseg:0067 db 66h ; f
    dseg:0068 db 6Ch ; l
    dseg:0069 db 61h ; a
    dseg:006A db 67h ; g
    dseg:006B db 20h
    dseg:006C db 3Dh ; =
    dseg:006D db 24h ; $
    dseg:006E db ? ;
    dseg:006F db ? ;
    dseg:006F dseg ends
    dseg:006F
    seg002:0000 ; ===========================================================================
  • 可以看出程序分两部分对flag的密文进行了解密,模拟这段代码就可以得到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
    data = [
    0x47, 0x7F, 0x52, 0x78, 0x6C, 0x74, 0x7E, 0x72, 0x47, 0x47,
    0x73, 0x5A, 0x84, 0x5A, 0x43, 0x85, 0x46, 0x5A, 0x83, 0x6F,
    0x46, 0x5A, 0x6C, 0x33, 0x30, 0x73, 0x32, 0x75, 0x66, 0x37,
    0x61, 0x66, 0x33, 0x30, 0x78, 0x66, 0x40, 0x35, 0x61, 0x4E,
    0x64, 0x34, 0x65, 0x32, 0x33, 0x88
    ]

    output = []

    # 第一段解密(前 0x17 字节)
    for i in range(0x17):
    al = data[i]
    al = (al - 9) & 0xFF
    al = al ^ 0x0E
    output.append(al)

    # 第二段解密(后 0x17 字节)
    for i in range(0x17,0x2E):
    al = data[i]
    al = al ^ 0x0E
    al = (al - 9) & 0xFF
    output.append(al)

    # 输出为字符串
    flag = ''.join(chr(i) for i in output)
    print(flag)

TELF

  • 题目提示加壳,DIE检查发现附件程序加了upx壳:

    DIE检查

    这是个elf文件,考察elf手动脱壳的话难度不小。使用upx工具会发现脱不了壳,推测有upx特征魔改。用010editor打开附件程序,注意到出现了可疑字样”X1c”:

    可疑字样

    修改”X1c”为”UPX”后程序可正常工具脱壳:

    工具脱壳

    脱壳后的程序用IDA打开,main函数如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    int __fastcall main(int argc, const char **argv, const char **envp)
    {
    __int64 block; // [rsp+8h] [rbp-A8h] BYREF
    _QWORD fl4g[8]; // [rsp+10h] [rbp-A0h] BYREF
    char flag[8]; // [rsp+50h] [rbp-60h] BYREF
    __int64 v7; // [rsp+58h] [rbp-58h]
    __int64 v8; // [rsp+60h] [rbp-50h]
    __int64 v9; // [rsp+68h] [rbp-48h]
    __int64 v10; // [rsp+70h] [rbp-40h]
    __int64 v11; // [rsp+78h] [rbp-38h]
    __int64 v12; // [rsp+80h] [rbp-30h]
    _DWORD key[5]; // [rsp+90h] [rbp-20h] BYREF
    int k; // [rsp+A4h] [rbp-Ch]
    int j; // [rsp+A8h] [rbp-8h]
    int i; // [rsp+ACh] [rbp-4h]

    srand(0xF6950u);
    for ( i = 0; i <= 3; ++i )
    key[i] = rand();
    printf("Please input your flag: ");
    if ( __isoc99_scanf("%56s", flag) == 1 )
    {
    if ( strlen(flag) == 56 )
    {
    fl4g[0] = *flag;
    fl4g[1] = v7;
    fl4g[2] = v8;
    fl4g[3] = v9;
    fl4g[4] = v10;
    fl4g[5] = v11;
    fl4g[6] = v12;
    for ( j = 0; j <= 55; j += 8 )
    {
    block = fl4g[j / 8u];
    encrypt(&block, key);
    fl4g[j / 8u] = block;
    }
    for ( k = 0; k <= 55; ++k )
    {
    if ( enc[k] != *(fl4g + k) )
    {
    printf("Try Again!");
    return 1;
    }
    }
    puts("Congratulation!");
    return 0;
    }
    else
    {
    puts("Length Error!");
    return 1;
    }
    }
    else
    {
    fwrite("Input error or EOF\n", 1uLL, 0x13uLL, _bss_start);
    return 1;
    }
    }

    可以看出程序用先随机数生成了key,然后对输入的flag进行了加密,最后是校验。加密函数如下,是tea加密:

    encrypt函数

    但是用相同种子生成的随机数组成的key会解密失败,动调会发现key和自己使用同一种子生成的key不同(IDAelf动调教程见ida动态调试elf(无坑详细)):

    动调时的key

    这样就能解密了。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
    #include <stdio.h>
    #include <stdint.h>

    void decrypt(uint32_t* block, uint32_t* key) {
    uint32_t p1 = block[0];
    uint32_t p2 = block[1];
    int sum = 0;

    for (int i = 0; i < 32; i++) {
    sum -= 0x61C88647;
    }

    for (int i = 0; i < 32; i++) {
    p2 -= (p1 + sum) ^ (16 * p1 + key[2]) ^ ((p1 >> 5) + key[3]);
    p1 -= (p2 + sum) ^ (16 * p2 + key[0]) ^ ((p2 >> 5) + key[1]);
    sum += 0x61C88647;
    }
    block[0] = p1;
    block[1] = p2;
    }

    int main() {
    uint32_t key[4] = { 0x7e4d087b, 0x7a4db733, 0x70fe9df0, 0x595607f7 };
    uint32_t enc[14] = {
    0xDC01DAAD, 0x088A5BAE, 0x8F4FF54E, 0x9E9D5F6E,
    0x08A94E0A, 0xC245AB25, 0x438FC94B, 0x28D6513D,
    0xF4CD72F6, 0x3B4AB42B, 0xEF6636FB, 0xB28C8AD6,
    0x1B9C1AEB, 0x531F9C0A
    };

    for (int i = 0; i < 14; i += 2) {
    decrypt(&enc[i], key);
    }

    char* flagChar = (char*)enc;
    for (int i = 0; i < 56; i++) {
    printf("%c", flagChar[i]);
    }
    return 0;
    }
作者

SydzI

发布于

2025-10-19

更新于

2025-10-23

许可协议

评论