[网鼎杯 2020 青龙组]singal

无壳,直接进IDA

2024-11-04T14:07:14.png

主函数非常简单,将unk_403040复制给v4然后传递给vm_operad,接着分析vm_operad

vm_operad

int __cdecl vm_operad(int *a1, int a2)
{
  int result; // eax
  char Str[200]; // [esp+13h] [ebp-E5h] BYREF
  char v4; // [esp+DBh] [ebp-1Dh]
  int v5; // [esp+DCh] [ebp-1Ch]
  int v6; // [esp+E0h] [ebp-18h]
  int v7; // [esp+E4h] [ebp-14h]
  int v8; // [esp+E8h] [ebp-10h]
  int v9; // [esp+ECh] [ebp-Ch]

  v9 = 0;
  v8 = 0;
  v7 = 0;
  v6 = 0;
  v5 = 0;
  while ( 1 )
  {
    result = v9;
    if ( v9 >= a2 )
      return result;
    switch ( a1[v9] )
    {
      case 1:
        Str[v6 + 100] = v4;
        ++v9;
        ++v6;
        ++v8;
        break;
      case 2:
        v4 = a1[v9 + 1] + Str[v8];
        v9 += 2;
        break;
      case 3:
        v4 = Str[v8] - LOBYTE(a1[v9 + 1]);
        v9 += 2;
        break;
      case 4:
        v4 = a1[v9 + 1] ^ Str[v8];
        v9 += 2;
        break;
      case 5:
        v4 = a1[v9 + 1] * Str[v8];
        v9 += 2;
        break;
      case 6:
        ++v9;
        break;
      case 7:
        if ( Str[v7 + 100] != a1[v9 + 1] )
        {
          printf("what a shame...");
          exit(0);
        }
        ++v7;
        v9 += 2;
        break;
      case 8:
        Str[v5] = v4;
        ++v9;
        ++v5;
        break;
      case 10:
        read(Str);
        ++v9;
        break;
      case 11:
        v4 = Str[v8] - 1;
        ++v9;
        break;
      case 12:
        v4 = Str[v8] + 1;
        ++v9;
        break;
      default:
        continue;
    }
  }
}

发现应该是根据传入参数a1不同位置的参数来决定每次的行为,观察到a1的第一个值是0xa,对应的行为是10号读入,符合直接运行程序

v9: 0
Operation: 10
Operation: 10 Read Str

用python模拟出行为,然后观察,发现比较是最后15个行为,同时与read函数对比,发现也是读入15位

比较部分

Operation: 7
Compore position is 100
Encrypted_code: 34
v9: 86
Operation: 7
Compore position is 101
Encrypted_code: 63
v9: 88
Operation: 7
Compore position is 102
Encrypted_code: 52
v9: 90
Operation: 7
Compore position is 103
Encrypted_code: 50
v9: 92
Operation: 7
Compore position is 104
Encrypted_code: 114
v9: 94
Operation: 7
Compore position is 105
Encrypted_code: 51
v9: 96
Operation: 7
Compore position is 106
Encrypted_code: 24
v9: 98
Operation: 7
Compore position is 107
Encrypted_code: 167
v9: 100
Operation: 7
Compore position is 108
Encrypted_code: 49
v9: 102
Operation: 7
Compore position is 109
Encrypted_code: 241
v9: 104
Operation: 7
Compore position is 110
Encrypted_code: 40
v9: 106
Operation: 7
Compore position is 111
Encrypted_code: 132
v9: 108
Operation: 7
Compore position is 112
Encrypted_code: 193
v9: 110
Operation: 7
Compore position is 113
Encrypted_code: 30
v9: 112
Operation: 7
Compore position is 114
Encrypted_code: 122

2024-11-04T14:23:48.png

然后前面每个数都是执行两个操作后,保存等待比较

处理

v9: 1
Operation: 4
Operation: 4 arg1: 16 arg2: 48, xor
v9: 3
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 0
v9: 4
Operation: 3
Operation: 3 arg1: 32 arg2: 5, sub
v9: 6
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 100
v9: 7
Operation: 4
Operation: 4 arg1: 32 arg2: 48, xor
v9: 9
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 1
v9: 10
Operation: 5
Operation: 5 arg1: 3 arg2: 16, mul
v9: 12
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 101
v9: 13
Operation: 3
Operation: 3 arg1: 48 arg2: 2, sub
v9: 15
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 2
v9: 16
Operation: 11
Operation: 11 arg1: 46, sub 1
v9: 17
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 102
v9: 18
Operation: 12
Operation: 12 arg1: 48, add 1
v9: 19
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 3
v9: 20
Operation: 4
Operation: 4 arg1: 4 arg2: 49, xor
v9: 22
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 103
v9: 23
Operation: 5
Operation: 5 arg1: 3 arg2: 48, mul
v9: 25
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 4
v9: 26
Operation: 3
Operation: 3 arg1: 144 arg2: 33, sub
v9: 28
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 104
v9: 29
Operation: 11
Operation: 11 arg1: 48, sub 1
v9: 30
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 5
v9: 31
Operation: 11
Operation: 11 arg1: 47, sub 1
v9: 32
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 105
v9: 33
Operation: 4
Operation: 4 arg1: 9 arg2: 48, xor
v9: 35
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 6
v9: 36
Operation: 3
Operation: 3 arg1: 57 arg2: 32, sub
v9: 38
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 106
v9: 39
Operation: 2
Operation: 2 arg1: 81 arg2: 48, add
v9: 41
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 7
v9: 42
Operation: 4
Operation: 4 arg1: 36 arg2: 129, xor
v9: 44
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 107
v9: 45
Operation: 12
Operation: 12 arg1: 48, add 1
v9: 46
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 8
v9: 47
Operation: 11
Operation: 11 arg1: 49, sub 1
v9: 48
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 108
v9: 49
Operation: 5
Operation: 5 arg1: 2 arg2: 48, mul
v9: 51
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 9
v9: 52
Operation: 2
Operation: 2 arg1: 37 arg2: 96, add
v9: 54
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 109
v9: 55
Operation: 2
Operation: 2 arg1: 54 arg2: 48, add
v9: 57
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 10
v9: 58
Operation: 4
Operation: 4 arg1: 65 arg2: 102, xor
v9: 60
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 110
v9: 61
Operation: 2
Operation: 2 arg1: 32 arg2: 48, add
v9: 63
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 11
v9: 64
Operation: 5
Operation: 5 arg1: 1 arg2: 80, mul
v9: 66
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 111
v9: 67
Operation: 5
Operation: 5 arg1: 3 arg2: 48, mul
v9: 69
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 12
v9: 70
Operation: 2
Operation: 2 arg1: 37 arg2: 144, add
v9: 72
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 112
v9: 73
Operation: 4
Operation: 4 arg1: 9 arg2: 48, xor
v9: 75
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 13
v9: 76
Operation: 3
Operation: 3 arg1: 57 arg2: 32, sub
v9: 78
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 113
v9: 79
Operation: 2
Operation: 2 arg1: 65 arg2: 48, add
v9: 81
Operation: 8
Operation: 8 Push encrypted code to Str, now Str position is 14
v9: 82
Operation: 12
Operation: 12 arg1: 113, add 1
v9: 83
Operation: 1
Operation: 1 Push encrypted code to Str, now Str position is 114

所以写出exp

exp.py

op = [0xa, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x21, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x51, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x72, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0xa7, 0xff, 0xff, 0xff, 0x7, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0xf1, 0xff, 0xff, 0xff, 0x7, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x84, 0xff, 0xff, 0xff, 0x7, 0x0, 0x0, 0x0, 0xc1, 0xff, 0xff, 0xff, 0x7, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x7a, 0x0, 0x0, 0x0]

v9 = 0
v8 = 0
v7 = 0
v6 = 0
v5 = 0
v4 = ""

Str = ["0"] * 200
cnt = 0
encrypted_codes = []
operations = [[] for _ in range(15)]

while True:
    result = v9
    if v9 >= 114:
        break
    match op[v9 * 4]:
        case 1:
            Str[v6 + 100] = v4
            v9 += 1
            v6 += 1
            v8 += 1
        case 2:
            operations[v6].append({"opcode": 2, "arg": op[(v9 + 1) * 4]})
            v4 = chr(op[(v9 + 1) * 4] + ord(Str[v8]))
            v9 += 2
        case 3:
            operations[v6].append({"opcode": 3, "arg": op[(v9 + 1) * 4]})
            v4 = chr(ord(Str[v8]) - op[(v9 + 1)*4])
            v9 += 2
        case 4:
            operations[v6].append({"opcode": 4, "arg": op[(v9 + 1) * 4]})
            v4 = chr(op[(v9 + 1) * 4] ^ ord(Str[v8]))
            v9 += 2
        case 5:
            operations[v6].append({"opcode": 5, "arg": op[(v9 + 1) * 4]})
            v4 = chr(op[(v9 + 1) * 4] * ord(Str[v8]))
            v9 += 2
        case 6:
            v9 += 1
        case 7:
            cnt += 1
            encrypted_codes.append(op[(v9 + 1) * 4])
            v7 += 1
            v9 += 2
        case 8:
            Str[v5] = v4
            v5 += 1
            v9 += 1
        case 10:
            v9 += 1
        case 11:
            operations[v6].append({"opcode": 11, "arg": 1})
            v4 = chr(ord(Str[v8]) - 1)
            v9 += 1
        case 12:
            operations[v6].append({"opcode": 12, "arg": 1})
            v4 = chr(ord(Str[v8]) + 1)
            v9 += 1
        case _:
            continue


pos = 0
ans = []
while True:
    if pos >= len(encrypted_codes):
        break
    encrypted_code = encrypted_codes[pos]
    for i in range(1, -1, -1):
        match operations[pos][i]["opcode"]:
            case 2:
                encrypted_code = encrypted_code - operations[pos][i]["arg"]
            case 3:
                encrypted_code = encrypted_code + operations[pos][i]["arg"]
            case 4:
                encrypted_code = encrypted_code ^ operations[pos][i]["arg"]
            case 5:
                encrypted_code = encrypted_code // operations[pos][i]["arg"]
            case 11:
                encrypted_code = encrypted_code + operations[pos][i]["arg"]
            case 12:
                encrypted_code = encrypted_code - operations[pos][i]["arg"]
            case _:
                continue
    ans.append(encrypted_code)
    pos += 1

print("".join([chr(i) for i in ans]))
# buuoj: flag{757515121f3d478}

crackMe

无壳,直接进IDA

2024-11-06T12:55:53.png

大致流程是输入用户名和密码,其中sub_401000是检测输入是否为字母或者数字

核心的分析判断应该就是sub_401090loc_4011A0sub_401830

2024-11-06T13:02:39.png

其中sub_401090是RC4算法初始化,传入的a1是刚刚输入的用户名,在题目提示有是welcomebeijing,把初始化后的结果存放在byte_416050,作为S-box

2024-11-06T13:04:19.png

发现有脏东西,用NOP patch掉,保存然后重新解析

2024-11-06T13:11:44.png
2024-11-06T13:12:00.png

发现变成了sub_4011A0,整理后发现这里面是准备验证后的提示词,没有任何有效信息,那就只有从sub_401830里找信息了

2024-11-06T13:16:17.png

果然校验逻辑在这里面,从后向前分析

最后v13的结果要为0xAB94,而v13的值来自sub_401470sub_401470的另一个参数v16来自上面的while循环,这个对v5的循环实际上是RC4加密,而在上面一个循环是对输入a2即密码的一个转换处理。先进sub_401470分析

2024-11-06T13:56:24.png

观察可知v16应该有8个东西组成,如果if不成立,那就是任意值都可以执行的计算,所以得出v16 = [0x64, 0x62, 0x61, 0x70, 0x70, 0x73, 0x65, 0x63],同验证也通过

在拿到v16的值后,就是通过还原while循环的代码,得到password,写出脚本

exp.py

v16 = [0x64, 0x62, 0x61, 0x70, 0x70, 0x73, 0x65, 0x63]
v5 = 0
v11 = 0
v10 = 0
byte_416050 = [i for i in range(256)]
v2 = 0
v9 = 0
v3 = 0
a1 = "welcomebeijing"

while v2 < 256:
    v8 = byte_416050[v2]
    v9 += (v8 + ord(a1[v3 % len(a1)]))
    v9 &= 0xff
    v7 = byte_416050[v9]
    v3 += 1
    byte_416050[v9] = v8
    byte_416050[v2] = v7
    v2 += 1

while v5 < 8:
    v11 += 1
    v10 += byte_416050[v11]
    v10 &= 0xff
    v12 = byte_416050[v11]
    v7 = byte_416050[v10]
    byte_416050[v10] = v12
    byte_416050[v11] = v7

    v16[v5] ^= byte_416050[(v12 + v7) & 0xff]
    v5 += 1

password = "".join([hex(i)[2:] for i in v16])
print(f"password: {password}")

import hashlib
flag = hashlib.md5(password.encode()).hexdigest()
print(f"flag: {flag}")

[GUET-CTF2019]number_game

无壳,直接进IDA,分析主函数

2024-11-04T14:26:38.png

发现判断有两个部分sub_4006D6sub_400917两个函数,首先看第一个函数

__int64 __fastcall sub_4006D6(const char *a1)
{
  int i; // [rsp+1Ch] [rbp-4h]

  if ( strlen(a1) == 10 )
  {
    for ( i = 0; i <= 9; ++i )
    {
      if ( a1[i] > 52 || a1[i] <= 47 )
        goto LABEL_2;
    }
    return 1LL;
  }
  else
  {
LABEL_2:
    puts("Wrong!");
    return 0LL;
  }
}

发现输入是10位数,且只能在0-4之间,接着分析sub_400917

__int64 sub_400917()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  int i; // [rsp+4h] [rbp-Ch]
  int j; // [rsp+8h] [rbp-8h]
  int k; // [rsp+Ch] [rbp-4h]

  v1 = 1;
  for ( i = 0; i <= 4; ++i )
  {
    for ( j = 0; j <= 4; ++j )
    {
      for ( k = j + 1; k <= 4; ++k )
      {
        if ( *((_BYTE *)&unk_601060 + 5 * i + j) == *((_BYTE *)&unk_601060 + 5 * i + k) )
          v1 = 0;
        if ( *((_BYTE *)&unk_601060 + 5 * j + i) == *((_BYTE *)&unk_601060 + 5 * k + i) )
          v1 = 0;
      }
    }
  }
  return v1;
}

这是把unk_601060视为了一个5x5的数组,检查每列和每行是否有相同的内容,没有就通过
那么接下来就是分析,怎么从输入变到这个unk_601060
回到主函数,发现输入分别经过了三个函数sub_400758sub_400807sub_400881

_QWORD *__fastcall sub_400758(__int64 a1, signed int a2, signed int a3)
{
  char v5; // [rsp+1Fh] [rbp-11h]
  _QWORD *v6; // [rsp+28h] [rbp-8h]

  v5 = *(_BYTE *)(a2 + a1);
  if ( v5 == 32 || v5 == 10 || a2 >= a3 )
    return 0LL;
  v6 = malloc(24uLL);
  *(_BYTE *)v6 = v5;
  v6[1] = sub_400758(a1, 2 * a2 + 1, a3);
  v6[2] = sub_400758(a1, 2 * (a2 + 1), a3);
  return v6;
}

这个函数应该是构建一个二叉树

__int64 __fastcall sub_400807(__int64 a1, __int64 a2)
{
  __int64 result; // rax

  result = a1;
  if ( a1 )
  {
    sub_400807(*(_QWORD *)(a1 + 8), a2);
    *(_BYTE *)(a2 + dword_601080++) = *(_BYTE *)a1;
    return sub_400807(*(_QWORD *)(a1 + 16), a2);
  }
  return result;
}

这个函数进行了一次中序遍历

__int64 __fastcall sub_400881(char *a1)
{
  __int64 result; // rax

  byte_601062 = *a1;
  byte_601067 = a1[1];
  byte_601069 = a1[2];
  byte_60106B = a1[3];
  byte_60106E = a1[4];
  byte_60106F = a1[5];
  byte_601071 = a1[6];
  byte_601072 = a1[7];
  byte_601076 = a1[8];
  result = (unsigned __int8)a1[9];
  byte_601077 = a1[9];
  return result;
}

而这个函数,则将刚刚中序遍历的结果拿去修改data区域的数据,根据地址推测,修改了前文提到的5x5矩阵

然后。。原来就是数独简单版(

flag

# [idc.get_wide_byte(0x601060 + i) for i in range(0x601078-0x601060+1)]
matrix = [0x31, 0x34, 0x23, 0x32, 0x33, 0x33, 0x30, 0x23, 0x31, 0x23, 0x30, 0x23, 0x32, 0x33, 0x23, 0x23, 0x33, 0x23, 0x23, 0x30, 0x34, 0x32, 0x23, 0x23, 0x31]
for i in range(5):
    for j in range(5):
        print(chr(matrix[i*5+j]), end=' ')
    print()
1 4 # 2 3 
3 0 # 1 # 
0 # 2 3 # 
# 3 # # 0 
4 2 # # 1

所以“数独”解出来是0421421430还需要通过中序遍历还原

ans = "0421421430"
order = []
ret = [0] * len(ans)

def inorder(i):
    global order
    if i >= len(ans):
        return ""
    inorder(2 * i + 1)
    order.append(i)
    inorder(2 * (i + 1))

inorder(0)
for i in range(len(ans)):
    ret[order[i]] = ans[i]

print("".join(ret))

buuoj: flag{1134240024}

findKey

无壳,直接进IDA,看到是vc gui类程序,从字符串开始找线索

2024-11-05T08:48:58.png

发现端倪,找到这个串的位置,查调用,然后。。。怎么有恶心东西啊!

2024-11-05T08:52:47.png

nop!应用修补,重新打开,看到代码

2024-11-05T08:54:46.png

分析一下大致逻辑,发现sub_40101E是计算md5,sub_401005是对v8做异或,然后与Str对比

还原异或结果得到c8837b23ff8aaa8a2dde915473ce0991,解MD5得到原文是123321符合前面,strlen(String1)>6就推出程序的预期

接着发现从&unk_423030取值,与123321异或后输出结果,可以还原flag了

exp.py

Str1 = "0kk`d1a`55k222k2a776jbfgd`06cjjb"
md5 = ""
for i in Str1:
    md5 += chr(ord(i) ^ ord("S"))

print(md5)

Str2 = [0x57, 0x5e, 0x52, 0x54, 0x49, 0x5f, 0x1, 0x6d, 0x69, 0x46, 0x2, 0x6e, 0x5f, 0x2, 0x6c, 0x57, 0x5b, 0x54, 0x4c]
flag = ""
md55 = "123321"
pos = 0
for i in Str2:
    flag += chr(i ^ ord(md55[pos % 6]))
    pos += 1

print(flag)
# flag{n0_Zu0_n0_die}

[FlareOn5]Minesweeper Championship Registration

进jadx-gui,直接看到flag
2024-11-07T02:59:07.png

[ACTF新生赛2020]SoulLike

无壳,进IDA

2024-11-07T03:01:21.png

主函数逻辑还是比较清晰,首先比较前5位是不是actf{开头,然后去LABEL_6进行后序的比较,而后序的比较核心就在sub_83A,但是由于函数巨大,ida会提示too big,这时候就需要去ida/cfg/hexrays.cfg修改MAX_FUNCSIZE为更大就可以了

sub_83A发现是一对异或,对其处理一下,就得到了flag

exp.py


sub_83A的内容复制到file.txt中,然后去头去尾

import pathlib

file = pathlib.Path('func.txt')
file_out = pathlib.Path('func_out.py')
funcs = []

with file.open() as f:
    while True:
        line = f.readline()
        if not line:
            break
        line = line.replace("\n", "")
        line = line.replace("u;", "")
        line = line.replace("u", "")
        line = line.replace(";", "")
        line = line.replace("*a1", "a1[0]")
        line = line.replace("v3", "a1")
        if line.startswith("++"):
            line = line[2:] + " -= 1"
        funcs.append(line)
    funcs.append("a1 = [0] * 12")
        

with file_out.open("w") as f:
    for i in range(len(funcs) - 1, -1, -1):
        f.write(funcs[i] + "\n")
    f.write('print("".join([chr(i) for i in a1]))')

最后执行python func_out.py,得到flag
buuoj:flag{b0Nf|Re_LiT!}

[GWCTF 2019]re3

无壳,进IDA

2024-11-07T13:11:53.png

发现是输入长度为32,但是有mprotect,这个函数改变了内存段上的保护属性,允许该内存段可写可读可执行,结合下面的循环可知,在sub_402219上代码被保护了,先把内容进行异或0x99后再执行

第一次遇到这种,参考其他师傅的wp使用如下代码还原

import idc

funcAddr = 0x402219
for i in range(224):
    enc = get_bytes(funcAddr + i, 1)
    dec = ord(enc) ^ 0x99
    patch_byte(funcAddr + i, dec)

2024-11-07T13:19:39.png

来到地址 0x402219 处,光标放在 sub_402219 标签上,按 U 取消函数定义,再按 C 数据转汇编代码,最后按 P 创建函数,F5 反编译

2024-11-07T13:21:50.png

就可以看到sub_402219函数了,然后查看sub_400A71sub_40196E都是和AES有关的函数,推测第一个是准备Key,第二个是计算AES,但是由于只有两个参数,那应该就没有iv,是ECB模式加密的。在加密完成后与byte_6030A0的值对比

而Key则应该是由sub_40207B计算出来的

最后修改:2024 年 11 月 07 日
如果觉得我的文章对你有用,请随意赞赏