当前位置:   article > 正文

2022CTF培训(六)逆向题目选讲_ctf逆向题目讲解

ctf逆向题目讲解

附件下载链接

SMC

babysmc 主函数逻辑如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[36]; // [esp+0h] [ebp-28h] BYREF

  memset(v4, 0, 0x22u);
  printf("please input: \n", v4[0]);
  scanf("%34s", v4);
  patch_code1(v4[0], v4[33]);
  ((void (__cdecl *)(char *, int))dword_401850)(&v4[1], 32);
  if ( (unsigned __int8)sub_4018C0(&v4[1], 16) && (unsigned __int8)sub_402F30(&v4[17]) )
    printf("Great\nYour input is right!!!\n", v4[0]);
  else
    printf("Sorry\nYour input is wrong...\n", v4[0]);
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

可以看到在输入 34 字节长度的字符串后会以输入的第一个和最后一个字节作为参数调用 patch_code1 函数。
patch_code1 以及它调用的 patch_code2 函数逻辑如下:

BOOL __cdecl patch_code2(char a1)
{
  DWORD flOldProtect; // [esp+0h] [ebp-Ch] BYREF
  SIZE_T dwSize; // [esp+4h] [ebp-8h]
  SIZE_T i; // [esp+8h] [ebp-4h]

  dwSize = 256;
  VirtualProtect(unk_4017C0, 0x100u, 0x40u, &flOldProtect);
  for ( i = 0; i < dwSize; ++i )
    unk_4017C0[i] ^= a1;
  return VirtualProtect(unk_4017C0, dwSize, flOldProtect, &flOldProtect);
}

BOOL __cdecl patch_code1(char a1, char a2)
{
  DWORD flOldProtect; // [esp+0h] [ebp-Ch] BYREF
  SIZE_T dwSize; // [esp+4h] [ebp-8h]
  SIZE_T i; // [esp+8h] [ebp-4h]

  dwSize = 256;
  VirtualProtect(dword_401850, 0x100u, 0x40u, &flOldProtect);
  for ( i = 0; i < dwSize; ++i )
    dword_401850[i] ^= a1;
  VirtualProtect(dword_401850, dwSize, flOldProtect, &flOldProtect);
  return patch_code2(a2);
}
  • 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

两个函数 patch 的范围如下:
在这里插入图片描述

在执行完 patch_code1 之后,主函数又调用了 0x401850 和 0x4018c0 两处地址,因此这两个地址是函数入口。
一般情况下函数入口处的第一条指令是 push ebp ,对应的硬编码为 0x55 。
因此可以确定 a1 = 0x23,a2 = 0x21 。
使用如下 IDAPython 脚本将代码解密。

import ida_bytes


def patch_code(addr, len, a):
    content = bytearray(ida_bytes.get_bytes(addr, len))
    for _ in range(len): content[_] ^= a
    ida_bytes.patch_bytes(addr, bytes(content))


patch_code(0x401850, 256, 0x23)
patch_code(0x4017C0, 256, 0x21)
print("done")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

解密后的 sub_401850 内容:

unsigned int __cdecl sub_401850(char *a1, int a2)
{
  unsigned int result; // eax
  unsigned int i; // [esp+0h] [ebp-14h]
  char v4[12]; // [esp+4h] [ebp-10h] BYREF

  memset(v4, 0, 0xAu);
  result = sub_4017C0(v4);
  for ( i = 0; i < 10; ++i )
    result = funcs_4018AB[(unsigned __int8)v4[i]]((int)a1, a2);
  return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

分析可知,函数首先调用 sub_4017C0 初始化 v4 数组,之后根据 v4 数组中的内容依次调用 funcs_4018AB 数组中的函数对输入的 1 到 32 字节进行变换。
sub_4017C0 函数内容如下:

char __cdecl sub_401780(unsigned int a1)
{
  unsigned int i; // [esp+0h] [ebp-4h]

  for ( i = 2; i <= a1 >> 1; ++i )
  {
    if ( !(a1 % i) )
      return 0;
  }
  return 1;
}

unsigned int __cdecl sub_4017C0(_BYTE *a1)
{
  unsigned int result; // eax
  unsigned int i; // [esp+0h] [ebp-8h]
  unsigned int v3; // [esp+4h] [ebp-4h]

  *a1 = 2;
  a1[1] = 3;
  result = 2;
  v3 = 2;
  for ( i = 4; i <= 0x1E && v3 < 0xA; ++i )
  {
    result = (unsigned __int8)sub_401780(i);
    if ( (_BYTE)result )
    {
      a1[v3] = i;
      result = ++v3;
    }
  }
  return result;
}
  • 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

将上面的代码复制到 IDE 中运行可以得到 v4 的内容:

2,3,5,7,11,13,17,19,23,29
  • 1

funcs_4018AB 是一个长度为 30 的函数表
在这里插入图片描述
函数表中的函数功能相似,其中的 sub_401000 函数内容如下。这些都是通过一个表对输入进行变换。

unsigned int __cdecl sub_401000(char *a, unsigned int len)
{
  unsigned int result; // eax
  unsigned int i; // [esp+0h] [ebp-4h]

  for ( i = 0; i < len; ++i )
  {
    a[i] = byte_422080[(unsigned __int8)a[i]];
    result = i + 1;
  }
  return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

由于函数结构相似因此直接利用脚本将每个函数对应表中的数据提取出来:

import ida_bytes
from idc import *

funcs_4018AB = get_name_ea_simple('funcs_4018AB')

s = []

for i in range(30):
    func_addr = get_wide_dword(funcs_4018AB + i * 4)
    data_addr = get_name_ea_simple(print_operand(func_addr + 45, 1))
    s += [list(ida_bytes.get_bytes(data_addr, 256))]
print(s)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

最后 sub_4018C0 和 sub_402F30 都是通过一组等式对变换后的输入进行验证。

在这里插入图片描述
由于存在多解,因此可以按照定义列出方程求解出变换后的 a,之后按照逆过程变换检验 flag 是否都是可见字符。主要代码如下(完整代码见附件):

from z3 import *

if __name__ == '__main__':
    s = Solver()
    a = [BitVec("%d" % i, 8) for i in range(32)]
    s.add(dword_422000[12] == 199 * a[9] + 98 * a[7] + 192 * a[8] + 23 * a[12] + 79 * a[14] + 77 * a[10] + 185 * a[13] + 135 * a[15] + 119 * a[4] + 54 * a[0] + 41 * a[1] + 124 * a[6] + 18 * a[2] + 181 * a[11] + 191 * a[5] + 7 * a[3])
    s.add(dword_422000[3] == 210 * a[11] + 26 * a[15] + 169 * a[0] + 177 * a[13] + a[6] + 205 * a[8] + 223 * a[10] + 32 * a[5] + 225 * a[3] + 61 * a[14] + 72 * a[1] + 186 * a[9] + 253 * a[12] + 205 * a[2] + 49 * a[4] + 232 * a[7])
    s.add(dword_422000[13] == 192 * a[3] + 22 * a[10] + 175 * a[1] + 184 * a[7] + 116 * a[15] + 70 * a[13] + 153 * a[14] + 119 * a[0] + 217 * a[6] + 123 * a[5] + 17 * a[2] + 244 * a[12] + 116 * a[8] + 46 * a[4] + 19 * a[9] + 130 * a[11])
    s.add(dword_422000[7] == 41 * a[12] + 71 * a[7] + 185 * a[1] + 69 * a[11] + 142 * a[8] + 221 * a[5] + 24 * a[3] + 208 * a[6] + 41 * a[9] + 159 * a[2] + 231 * a[14] + 235 * a[13] + 225 * a[0] + (a[4] << 6) + 162 * a[10] + 134 * a[15])
    s.add(dword_422000[11] == 36 * a[12] + 220 * a[4] + 110 * a[13] + 45 * a[7] + 123 * a[9] + 133 * a[1] + 101 * a[5] + 137 * a[10] + 102 * a[0] + 227 * a[14] + 94 * a[15] + 18 * a[2] + 22 * a[6] + 189 * a[11] + 218 * a[8])
    s.add(dword_422000[15] == 86 * a[11] + 31 * a[9] + 229 * a[6] + 27 * a[3] + 6 * a[12] + 13 * a[10] + 158 * a[1] + 89 * a[7] + 35 * a[15] + 126 * a[8] + 165 * a[13] + 220 * a[0] + 138 * a[5] + 100 * a[4] + 84 * a[14] + 175 * a[2])
    s.add(dword_422000[8] == 7 * a[1] + 28 * a[8] + 131 * a[10] + 6 * a[6] + 254 * a[0] + 130 * a[13] + 124 * a[3] + 55 * a[12] + 157 * a[14] + 175 * a[5] + 140 * a[4] + 241 * a[9] + 11 * a[11] + 211 * a[2] + 121 * a[7] + 200 * a[15])
    s.add(dword_422000[6] == 195 * a[14] + 197 * a[13] + 218 * a[7] + 83 * a[1] + 98 * a[2] + 70 * a[10] + 229 * a[15] + 148 * a[11] + 195 * a[0] + 94 * a[6] + 211 * a[12] + 220 * a[9] + 81 * a[5] + 253 * a[8] + 78 * a[4] + 4 * a[3])
    s.add(dword_422000[14] == 3 * a[4] + 136 * a[7] + 156 * a[3] + 189 * a[1] + 244 * a[12] + 157 * a[15] + 83 * a[9] + 6 * a[0] + 113 * a[6] + 63 * a[14] + 35 * a[2] + 22 * a[8] + 26 * a[10] + 62 * a[11] + 98 * a[5] + 110 * a[13])
    s.add(dword_422000[4] == 96 * a[4] + 248 * a[8] + 191 * a[9] + 194 * a[2] + 154 * a[1] + 31 * a[6] + 157 * a[7] + 248 * a[13] + 81 * a[15] + 56 * a[10] + 52 * a[0] + 94 * a[12] + 212 * a[5] + 83 * a[3] + 83 * a[14] + 158 * a[11])
    s.add(dword_422000[1] == 67 * a[4] + 220 * a[2] + 123 * a[11] + 168 * a[5] + 23 * a[12] + 148 * a[7] + 127 * a[10] + 194 * a[1] + 132 * a[8] + 44 * a[0] + 60 * a[13] + 98 * a[15] + 38 * a[14] + 245 * a[9] + 159 * a[6] + 146 * a[3])
    s.add(dword_422000[5] == 132 * a[3] + 10 * a[7] + 95 * a[0] + 83 * a[10] + 99 * a[1] + 77 * a[12] + 195 * a[2] + 47 * a[6] + 38 * a[13] + 178 * a[8] + 74 * a[4] + 86 * a[11] + 208 * a[9] + 240 * a[14] + 120 * a[5] + 43 * a[15])
    s.add(dword_422000[9] == 172 * a[1] + 110 * a[2] + 92 * a[7] + 126 * a[15] + 91 * a[0] + 77 * a[6] + 207 * a[5] + 249 * a[11] + 240 * a[12] + 129 * a[10] + 6 * a[13] + 100 * a[3] + a[14] + 76 * a[9] + 127 * a[4] + 4 * a[8])
    s.add(dword_422000[10] == 46 * a[15] + 37 * a[0] + 3 * a[3] + 72 * a[6] + 116 * a[7] + 186 * a[1] + 221 * a[14] + 236 * a[4] + 79 * a[2] + 175 * a[10] + 184 * a[9] + 160 * a[11] + 227 * a[12] + 99 * a[8] + 71 * a[13] + 4 * a[5])
    s.add(dword_422000[0] == 203 * a[3] + 31 * a[0] + 11 * a[14] + 149 * a[7] + 215 * a[5] + 206 * a[1] + 245 * a[6] + 9 * a[11] + 16 * a[10] + 241 * a[13] + 110 * a[8] + 175 * a[2] + 38 * a[4] + 227 * a[9] + 208 * a[12] + 8 * a[15])
    s.add(dword_422000[2] == 132 * a[3] + 119 * a[14] + 26 * a[8] + 24 * a[6] + 121 * a[11] + 235 * a[2] + 228 * a[12] + 34 * a[5] + 37 * a[15] + 24 * a[9] + 145 * a[13] + 199 * a[4] + 173 * a[10] + 58 * a[0] + 246 * a[7] + 199 * a[1])
    s.add(dword_422040[0] == 159 * a[16 + 8] + 109 * a[16 + 12] + 14 * a[16 + 0] + 92 * a[16 + 14] + 211 * a[16 + 4] + 178 * a[16 + 7] + 57 * a[16 + 2] + 175 * a[16 + 5] + 170 * a[16 + 11] + 59 * a[16 + 6] + 200 * a[16 + 9] + 5 * a[16 + 15] + 48 * a[16 + 13] + 28 * a[16 + 3] + 18 * a[16 + 10] + 228 * a[16 + 1])
    s.add(dword_422040[6] == 173 * a[16 + 11] + 34 * a[16 + 5] + 69 * a[16 + 4] + 216 * a[16 + 14] + 225 * a[16 + 9] + 160 * a[16 + 1] + 207 * a[16 + 10] + 175 * a[16 + 7] + 121 * a[16 + 0] + 122 * a[16 + 2] + 179 * a[16 + 12] + 91 * a[16 + 13] + 181 * a[16 + 8] + 93 * a[16 + 3] + 121 * a[16 + 6] + 12 * a[16 + 15])
    s.add(dword_422040[8] == 215 * a[16 + 11] + 164 * a[16 + 5] + 97 * a[16 + 2] + 99 * a[16 + 3] + 188 * a[16 + 4] + (a[16 + 9] << 7) + 214 * a[16 + 6] + 106 * a[16 + 8] + 169 * a[16 + 0] + 28 * a[16 + 14] + 18 * a[16 + 12] + a[16 + 1] + 177 * a[16 + 10] + 114 * a[16 + 7] + 176 * a[16 + 15] + 25 * a[16 + 13])
    s.add(dword_422040[9] == 175 * a[16 + 14] + 42 * a[16 + 4] + 214 * a[16 + 12] + 43 * a[16 + 13] + 147 * a[16 + 6] + 53 * a[16 + 10] + 12 * a[16 + 1] + 213 * a[16 + 7] + 241 * a[16 + 9] + 223 * a[16 + 5] + 65 * a[16 + 3] + 42 * a[16 + 15] + 131 * a[16 + 2] + 81 * a[16 + 0] + 92 * a[16 + 11] + 110 * a[16 + 8])
    s.add(dword_422040[13] == 57 * a[16 + 0] + 109 * a[16 + 7] + 60 * a[16 + 2] + 228 * a[16 + 13] + 166 * a[16 + 4] + 236 * a[16 + 9] + 100 * a[16 + 6] + 179 * a[16 + 11] + 20 * a[16 + 12] + 45 * a[16 + 8] + 204 * a[16 + 3] + 182 * a[16 + 14] + 84 * a[16 + 10] + 170 * a[16 + 15] + 199 * a[16 + 5] + 138 * a[16 + 1])
    s.add(dword_422040[10] == 98 * a[16 + 11] + 122 * a[16 + 9] + 237 * a[16 + 12] + 117 * a[16 + 0] + 34 * a[16 + 3] + 168 * a[16 + 8] + 135 * a[16 + 10] + 119 * a[16 + 6] + 91 * a[16 + 2] + 161 * a[16 + 15] + 152 * a[16 + 7] + 186 * a[16 + 4] + 187 * a[16 + 13] + 72 * a[16 + 14] + 36 * a[16 + 5] + 171 * a[16 + 1])
    s.add(dword_422040[7] == 184 * a[16 + 9] + 112 * a[16 + 0] + 107 * a[16 + 11] + 170 * a[16 + 13] + 55 * a[16 + 8] + 85 * a[16 + 14] + 212 * a[16 + 10] + 173 * a[16 + 15] + 166 * a[16 + 12] + 142 * a[16 + 4] + 202 * a[16 + 5] + 63 * a[16 + 2] + 30 * a[16 + 7] + 175 * a[16 + 3] + 217 * a[16 + 6] + 63 * a[16 + 1])
    s.add(dword_422040[15] == (a[16 + 7] << 6) + 228 * a[16 + 4] + 90 * a[16 + 11] + 85 * a[16 + 3] + 196 * a[16 + 6] + 219 * a[16 + 0] + 93 * a[16 + 14] + 183 * a[16 + 15] + 156 * a[16 + 12] + 197 * a[16 + 8] + 119 * a[16 + 13] + 36 * a[16 + 10] + 205 * a[16 + 2] + 94 * a[16 + 9] + 153 * a[16 + 5])
    s.add(dword_422040[5] == 9 * a[16 + 4] + (a[16 + 5] << 6) + 62 * a[16 + 1] + 58 * a[16 + 7] + 100 * a[16 + 13] + 137 * a[16 + 11] + 6 * a[16 + 0] + 119 * a[16 + 9] + 180 * a[16 + 6] + 228 * a[16 + 8] + 88 * a[16 + 12] + 107 * a[16 + 15] + 56 * a[16 + 14] + 207 * a[16 + 2] + 248 * a[16 + 10] + 150 * a[16 + 3])
    s.add(dword_422040[3] == 38 * a[16 + 7] + 194 * a[16 + 4] + 105 * a[16 + 0] + 150 * a[16 + 6] + 75 * a[16 + 1] + 89 * a[16 + 15] + 99 * a[16 + 14] + 98 * a[16 + 3] + 91 * a[16 + 8] + 178 * a[16 + 12] + 117 * a[16 + 2] + 48 * a[16 + 13] + 239 * a[16 + 10] + 233 * a[16 + 11] + 63 * a[16 + 5] + 250 * a[16 + 9])
    s.add(dword_422040[11] == 30 * a[16 + 8] + 13 * a[16 + 5] + 206 * a[16 + 3] + 234 * a[16 + 15] + 71 * a[16 + 7] + 239 * a[16 + 12] + 141 * a[16 + 10] + 179 * a[16 + 13] + 113 * a[16 + 14] + 181 * a[16 + 9] + 52 * a[16 + 6] + 74 * a[16 + 11] + 168 * a[16 + 4] + 239 * a[16 + 1] + 164 * a[16 + 0] + 179 * a[16 + 2])
    s.add(dword_422040[14] == 211 * a[16 + 1] + 74 * a[16 + 5] + 144 * a[16 + 8] + 234 * a[16 + 0] + 241 * a[16 + 2] + 157 * a[16 + 11] + 25 * a[16 + 15] + 6 * a[16 + 10] + 243 * a[16 + 6] + 107 * a[16 + 9] + 77 * a[16 + 12] + 127 * a[16 + 4] + 67 * a[16 + 7] + 13 * a[16 + 14] + 151 * a[16 + 3] + 127 * a[16 + 13])
    s.add(dword_422040[2] == 209 * a[16 + 9] + 110 * a[16 + 7] + 22 * a[16 + 10] + 102 * a[16 + 11] + 187 * a[16 + 1] + 58 * a[16 + 8] + 236 * a[16 + 6] + 146 * a[16 + 13] + 205 * a[16 + 15] + 63 * a[16 + 2] + 211 * a[16 + 4] + 152 * a[16 + 3] + 82 * a[16 + 14] + 14 * a[16 + 5] + 49 * a[16 + 12] + 251 * a[16 + 0])
    s.add(dword_422040[12] == 230 * a[16 + 0] + 27 * a[16 + 3] + 186 * a[16 + 10] + 58 * a[16 + 7] + 121 * a[16 + 1] + 59 * a[16 + 14] + 90 * a[16 + 12] + 40 * a[16 + 2] + 230 * a[16 + 11] + 25 * a[16 + 6] + 198 * a[16 + 5] + 81 * a[16 + 4] + 71 * a[16 + 13] + 180 * a[16 + 8] + 149 * a[16 + 9] + 73 * a[16 + 15])
    s.add(dword_422040[4] == 188 * a[16 + 5] + 80 * a[16 + 1] + 221 * a[16 + 6] + (a[16 + 12] << 6) + 230 * a[16 + 3] + 123 * a[16 + 8] + 124 * a[16 + 11] + 253 * a[16 + 0] + 202 * a[16 + 10] + 63 * a[16 + 2] + 40 * a[16 + 7] + 109 * a[16 + 9] + 195 * a[16 + 15] + 199 * a[16 + 13] + 82 * a[16 + 4] + 225 * a[16 + 14])
    s.add(dword_422040[1] == 236 * a[16 + 15] + 44 * a[16 + 14] + 214 * a[16 + 13] + 52 * a[16 + 8] + 37 * a[16 + 6] + 101 * a[16 + 9] + 244 * a[16 + 10] + 238 * a[16 + 11] + 109 * a[16 + 0] + 188 * a[16 + 1] + 20 * a[16 + 3] + 87 * a[16 + 7] + 93 * a[16 + 4] + 158 * a[16 + 5] + 105 * a[16 + 12] + 3 * a[16 + 2])

    rmap = [[-1 for _ in range(len(map[0]))] for _ in range(len(map))]
    for i in range(len(map)):
        for j in range(len(map[i])):
            rmap[i][map[i][j]] = j
        assert -1 not in rmap[i]

    while s.check() == sat:
        m = s.model()
        res = [int("%s" % m[a[_]]) for _ in range(len(m))]
        for i in range(len(id) - 1, -1, -1):
            for j in range(len(res)):
                res[j] = rmap[id[i]][res[j]]
        flag = chr(0x23) + "".join([chr(_) for _ in res]) + chr(0x21)
        if flag.isprintable():
            print(flag)
            break
  • 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

运行即可求得 flag #y0u_4r3_7h3_m4573r_0f_r3v3r51n6_!

反调试

将 TEAAA4.exe 用 IDA 在 main 函数下断点调试会卡死。
在这里插入图片描述
在导入表中搜索发现使用检测调试的 API 。
在这里插入图片描述
查找到该 API 的调用位置,发现程序会根据是否处在调试状态来修改 main 函数中用到的数据。

int sub_AD1050()
{
  int i; // [esp+D0h] [ebp-14h]
  char *v2; // [esp+DCh] [ebp-8h]

  __CheckForDebuggerJustMyCode(&unk_B82015);
  if ( !IsDebuggerPresent() )
  {
    v2 = Str2;
    for ( i = 0; i < 8; ++i )
      *v2++ ^= 0x75u;
  }
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

只需将该条语句 nop 掉就可以绕过此处反调试。
在这里插入图片描述

然而这里并不能导致 IDA 卡死,因此还存在其他检测调试的位置。在该函数位置下断点,发现 IDA 无法停下来,因此在这个函数调用前执行了检测调试的代码。
在这里插入图片描述
继续查找该函数的引用,发现该函数位于一个函数表中。
在这里插入图片描述
该函数表的边界作为参数传到了 sub_B232A0 函数中。
在这里插入图片描述
而 sub_B232A0 依次调用了这个函数表中的所有函数。

int __cdecl sub_B232A0(_DWORD *a1, _DWORD *a2)
{
  int v3; // [esp+0h] [ebp-Ch]

  while ( a1 != a2 )
  {
    if ( *a1 )
    {
      v3 = ((int (__thiscall *)(_DWORD))*a1)(*a1);
      if ( v3 )
        return v3;
    }
    ++a1;
  }
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

通过改变断点位置可以判断出,该函数表中 sub_B232A0 函数的前一个函数 sub_AD1000 同样存在反调试。具体反调试位置在 sub_AD1A50 中,该函数内容如下:

int sub_AD1A50()
{
  HMODULE LibraryW; // eax
  HANDLE CurrentThread; // eax
  FARPROC ZwSetInformationThread; // [esp+D0h] [ebp-8h]

  __CheckForDebuggerJustMyCode(&unk_B82015);
  LibraryW = LoadLibraryW(L"ntdll.dll");
  ZwSetInformationThread = GetProcAddress(LibraryW, "ZwSetInformationThread");
  CurrentThread = GetCurrentThread();
  return ((int (__stdcall *)(HANDLE, int, _DWORD, _DWORD))ZwSetInformationThread)(CurrentThread, 0x11, 0, 0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

该函数调用的 ZwSetInformationThread() 是一个未被公开的 API。如果有调试器挂载在目标进程上,并且传递 0x11 给这个函数的第二个参数,操作系统将会立即迫使所有已挂载的调试器 detach,并且终止进程。
这里只需要将第二个参数 patch 成 0 即可绕过反调试。
在这里插入图片描述
patch 掉这两处反调试后,可以正常调试了。
在这里插入图片描述
main 函数内容如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char input[68]; // [esp+D0h] [ebp-48h] BYREF

  __CheckForDebuggerJustMyCode(byte_652015);
  memset(input, 0, 0x40u);
  printf("Plz give me your flag\n");
  scanf("%64s", input);
  if ( strlen(input) == 32 && checkHex(input) && check(input) )
    printf("Right!\n");
  else
    printf("Wrong!\n");
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输入字符串后,首先判断字符串长度等于 32 ,之后判断字符串是一个可以表示 16 进制类型的数的字符串,具体逻辑如下:

char __cdecl checkHex(char *input)
{
  size_t i; // [esp+D0h] [ebp-14h]
  char v3; // [esp+DFh] [ebp-5h]

  __CheckForDebuggerJustMyCode(&byte_652015);
  v3 = 1;
  for ( i = 0; i < strlen(input); ++i )
  {
    if ( !isxdigit(input[i]) )
      v3 = 0;
  }
  return v3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

之后是检验函数 check ,内容如下:

// local variable allocation has failed, the output may be wrong!
char __cdecl check(char *input)
{
  char Buffer[56]; // [esp+D0h] [ebp-C0h] BYREF
  char v3[56]; // [esp+108h] [ebp-88h] BYREF
  int i; // [esp+140h] [ebp-50h]
  _WORD hex2[8]; // [esp+14Ch] [ebp-44h] BYREF
  _DWORD hex1[4]; // [esp+164h] [ebp-2Ch] OVERLAPPED BYREF
  char tmp[9]; // [esp+17Ch] [ebp-14h] BYREF

  __CheckForDebuggerJustMyCode(byte_652015);
  memset(tmp, 0, sizeof(tmp));
  memset(hex1, 0, sizeof(hex1));
  memset(hex2, 0, sizeof(hex2));
  for ( i = 0; i < 4; ++i )
  {
    strncpy_s(tmp, &input[8 * i], 8u);
    vsscanf(tmp, "%x", &hex1[i]);
    vsscanf(tmp, "%x", &hex2[2 * i]);
  }
  delta = 0x6EA237A6;
  tea(hex1, key);
  tea(&hex1[1], &key[1]);
  tea(&hex1[2], &key[2]);
  if ( strncmp((const char *)hex1, data, 0x10u) )
    return 0;
  memset(v3, 0, 0x30u);
  memset(v3, 45, 0x20u);
  crypt(*(int *)hex2, v3, 0x10u);
  crypt(hex2[3], &v3[9], 0x10u);
  crypt(hex2[2], &v3[14], 0x10u);
  crypt(hex2[5], &v3[19], 0x10u);
  crypt(hex2[4], &v3[24], 0x10u);
  crypt(*(int *)&hex2[6], &v3[28], 0x10u);
  memset(Buffer, 0, 0x30u);
  sub_5A10E0(Buffer, "DASCTF{%s}\n", (char)v3);
  printf("You get the flag: %s");
  return 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

该函数首先将 32 字节的输入转为 4 个 DWORD 类型的数。并用魔改的 tea 对其进行加密。
对应的写出解密代码:

#include <bits/stdc++.h>

using u32 = unsigned int;

u32 data[4] = {0x8E7D580B, 0x0C48EEF9, 0x9A5449EA, 0x6028B46D};
u32 key[6] = {0x147D3EAD, 0x72CD938D, 0x0DC14B5C9, 0x5BC6532D, 0x0F85F32FC, 0x0CDEA7FEC};
constexpr u32 delta = 0x6EA237A6;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    for (int i = 0; i < 3; i++) {
        key[i] -= 1335;
        key[i + 1] += 1351;
        key[i + 2] -= 1367;
        key[i + 3] += 1383;
    }

    auto tea = [&](u32 *data, u32 *key) {
        u32 rdelta = delta * 0x10;
        for (int i = 0; i < 0x10; i++) {
            data[1] -= (key[3] + (data[0] >> 6)) ^ (rdelta + data[0]) ^ (key[2] + 8 * data[0]);
            data[0] -= (key[1] + (data[1] >> 6)) ^ (rdelta + data[1]) ^ (key[0] + 8 * data[1]);
            rdelta -= delta;
        }
        key[0] += 1335;
        key[1] -= 1351;
        key[2] += 1367;
        key[3] -= 1383;
    };

    for (int i = 2; i >= 0; i--) {
        tea(data + i, key + i);
    }

    for (int i = 0; i < 4; i++) {
        std::cout << std::hex << data[i];
    }

    return 0;
}
  • 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

运行结果为

b6bab60548884e957d3952225fd29278
  • 1

将其输入到程序中即可得到 flag:DASCTF{b6bab605-4888-4e95-7d39-52225fd29278}

异常处理

ida 分析 babytea.exe,其中主函数内容如下。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int i; // [esp+4h] [ebp-28h]
  char input[32]; // [esp+8h] [ebp-24h] BYREF

  memset(input, 0, sizeof(input));
  printf("please input: \n");
  scanf("%32s", input);
  for ( i = 0; i < 4; ++i )
    tea(t, (unsigned int *)&input[8 * i], key);
  if ( !memcmp(input, &data, 32u) )
    printf("Great\nYour input is right!!!\n");
  else
    printf("Sorry\nYour input is wrong...\n");
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

主函数主要逻辑是读入 32 字节的字符串,然后每 8 个字节为一组进行 tea 加密后与 data 做比较。
tea 加密函数内容如下:

int __cdecl tea(unsigned int t, unsigned int *a, int *key)
{
  int result; // eax
  unsigned int i; // [esp+20h] [ebp-28h]
  int v; // [esp+24h] [ebp-24h]
  unsigned int a1; // [esp+28h] [ebp-20h]
  unsigned int a0; // [esp+2Ch] [ebp-1Ch]

  a0 = *a;
  a1 = a[1];
  v = 0;
  for ( i = 0; i < t; ++i )
  {
    v += delta;
    a0 += (key[1] + (a1 >> 5)) ^ (v + a1) ^ (*key + 16 * a1);
    a1 += (key[3] + (a0 >> 5)) ^ (v + a0) ^ (key[2] + 16 * a0);
  }
  *a = a0;
  result = 4;
  a[1] = a1;
  return result;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

函数逻辑比较简单。但是查看汇编发信该函数中存在异常处理代码,而这些代码不会出现在 IDA 的反编译代码中,因此需要对函数汇编进行分析。

函数的栈结构如下:

-00000038 key3            dd ?
-00000034 key2            dd ?
-00000030 key1            dd ?
-0000002C key0            dd ?
-00000028 i               dd ?
-00000024 v               dd ?
-00000020 a1              dd ?
-0000001C a0              dd ?
-00000018 ms_exc          CPPEH_RECORD ?
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)
+00000008 t               dd ?
+0000000C a               dd ?
+00000010 key             dd ?                    ; offset
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

首先函数初始化局部变量以及异常处理结构。

.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 push    0FFFFFFFEh
.text:00401005                 push    offset stru_41E0C0
.text:0040100A                 push    offset __except_handler4
.text:0040100F                 mov     eax, large fs:0
.text:00401015                 push    eax
.text:00401016                 add     esp, 0FFFFFFD8h
.text:00401019                 push    ebx
.text:0040101A                 push    esi
.text:0040101B                 push    edi
.text:0040101C                 mov     eax, ___security_cookie
.text:00401021                 xor     [ebp+ms_exc.registration.ScopeTable], eax
.text:00401024                 xor     eax, ebp
.text:00401026                 push    eax
.text:00401027                 lea     eax, [ebp+ms_exc.registration]
.text:0040102A                 mov     large fs:0, eax
.text:00401030                 mov     [ebp+ms_exc.old_esp], esp
.text:00401033                 mov     eax, 4
.text:00401038                 imul    ecx, eax, 0
.text:0040103B                 mov     edx, [ebp+a]
.text:0040103E                 mov     eax, [edx+ecx]
.text:00401041                 mov     [ebp+a0], eax
.text:00401044                 mov     ecx, 4
.text:00401049                 shl     ecx, 0
.text:0040104C                 mov     edx, [ebp+a]
.text:0040104F                 mov     eax, [edx+ecx]
.text:00401052                 mov     [ebp+a1], eax
.text:00401055                 mov     [ebp+v], 0
.text:0040105C                 mov     ecx, 4
.text:00401061                 imul    edx, ecx, 0
.text:00401064                 mov     eax, [ebp+key]
.text:00401067                 mov     ecx, [eax+edx]
.text:0040106A                 mov     [ebp+key0], ecx
.text:0040106D                 mov     edx, 4
.text:00401072                 shl     edx, 0
.text:00401075                 mov     eax, [ebp+key]
.text:00401078                 mov     ecx, [eax+edx]
.text:0040107B                 mov     [ebp+key1], ecx
.text:0040107E                 mov     edx, 4
.text:00401083                 shl     edx, 1
.text:00401085                 mov     eax, [ebp+key]
.text:00401088                 mov     ecx, [eax+edx]
.text:0040108B                 mov     [ebp+key2], ecx
.text:0040108E                 mov     edx, 4
.text:00401093                 imul    eax, edx, 3
.text:00401096                 mov     ecx, [ebp+key]
.text:00401099                 mov     edx, [ecx+eax]
.text:0040109C                 mov     [ebp+key3], edx
  • 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

之后函数会进入第一个异常处理,由于 ecx 清 0,因此 idiv ecx 必然会触发除零异常,因此会执行 loc_4010BE 处的代码,将局部变量 a0 和 a1 分别异或 dword_41F038 和 dword_41F03C 。

.text:0040109F ;   __try { // __except at loc_4010BE
.text:0040109F                 mov     [ebp+ms_exc.registration.TryLevel], 0
.text:004010A6                 xor     edx, edx
.text:004010A8                 xor     eax, eax
.text:004010AA                 inc     eax
.text:004010AB                 xor     ecx, ecx
.text:004010AD                 idiv    ecx
.text:004010AD ;   } // starts at 40109F
.text:004010AF                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:004010B6                 jmp     short loc_4010E0
.text:004010B8 ; ---------------------------------------------------------------------------
.text:004010B8
.text:004010B8 loc_4010B8:                             ; DATA XREF: .rdata:stru_41E0C0↓o
.text:004010B8 ;   __except filter // owned by 40109F
.text:004010B8                 mov     eax, 1
.text:004010BD                 retn
.text:004010BE ; ---------------------------------------------------------------------------
.text:004010BE
.text:004010BE loc_4010BE:                             ; DATA XREF: .rdata:stru_41E0C0↓o
.text:004010BE ;   __except(loc_4010B8) // owned by 40109F
.text:004010BE                 mov     esp, [ebp+ms_exc.old_esp]
.text:004010C1                 mov     eax, [ebp+a0]
.text:004010C4                 xor     eax, dword_41F038
.text:004010CA                 mov     [ebp+a0], eax
.text:004010CD                 mov     ecx, [ebp+a1]
.text:004010D0                 xor     ecx, dword_41F03C
.text:004010D6                 mov     [ebp+a1], ecx
.text:004010D9                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
  • 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

修复完异常后,程序会跳转到 loc_4010E0 继续执行,这里首先会将循环变量 i 初始化为 0 。
然后跳过 i++ 的代码到 loc_4010F2 处执行。

.text:004010E0 loc_4010E0:                             ; CODE XREF: tea+B6↑j
.text:004010E0                 mov     [ebp+i], 0
.text:004010E7                 jmp     short loc_4010F2
  • 1
  • 2
  • 3

在 loc_4010F2 处,程序首先判断循环次数是否足够,然后将用于 tea 加密的变量 v 加上 delta 。之后进入一个异常处理。
如果在执行 idiv ecx 这条指令时触发异常,那么 ecx 需要等于 0 ,也就是说 (v >> 0x1F) == 0 。否则不会触发异常。
如果触发异常会将变量 v 异或 0x1234567 。

.text:004010F2 loc_4010F2:                             ; CODE XREF: tea+E7↑j
.text:004010F2                 mov     eax, [ebp+i]
.text:004010F5                 cmp     eax, [ebp+t]
.text:004010F8                 jnb     loc_40118C
.text:004010FE                 mov     ecx, [ebp+v]
.text:00401101                 add     ecx, delta
.text:00401107                 mov     [ebp+v], ecx
.text:0040110A ;   __try { // __except at loc_40112D
.text:0040110A                 mov     [ebp+ms_exc.registration.TryLevel], 1
.text:00401111                 mov     ecx, [ebp+v]
.text:00401114                 shr     ecx, 1Fh
.text:00401117                 xor     edx, edx
.text:00401119                 xor     eax, eax
.text:0040111B                 inc     eax
.text:0040111C                 idiv    ecx
.text:0040111C ;   } // starts at 40110A
.text:0040111E                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:00401125                 jmp     short loc_401143
.text:00401127 ; ---------------------------------------------------------------------------
.text:00401127
.text:00401127 loc_401127:                             ; DATA XREF: .rdata:stru_41E0C0↓o
.text:00401127 ;   __except filter // owned by 40110A
.text:00401127                 mov     eax, 1
.text:0040112C                 retn
.text:0040112D ; ---------------------------------------------------------------------------
.text:0040112D
.text:0040112D loc_40112D:                             ; DATA XREF: .rdata:stru_41E0C0↓o
.text:0040112D ;   __except(loc_401127) // owned by 40110A
.text:0040112D                 mov     esp, [ebp+ms_exc.old_esp]
.text:00401130                 mov     edx, [ebp+v]
.text:00401133                 xor     edx, 1234567h
.text:00401139                 mov     [ebp+v], edx
.text:0040113C                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
  • 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

之后执行的这一部分对应的是反编译代码中的

    a0 += (key[1] + (a1 >> 5)) ^ (v + a1) ^ (*key + 16 * a1);
    a1 += (key[3] + (a0 >> 5)) ^ (v + a0) ^ (key[2] + 16 * a0);
  • 1
  • 2
.text:00401143 loc_401143:                             ; CODE XREF: tea+125↑j
.text:00401143                 mov     eax, [ebp+a1]
.text:00401146                 shl     eax, 4
.text:00401149                 add     eax, [ebp+key0]
.text:0040114C                 mov     ecx, [ebp+a1]
.text:0040114F                 add     ecx, [ebp+v]
.text:00401152                 xor     eax, ecx
.text:00401154                 mov     edx, [ebp+a1]
.text:00401157                 shr     edx, 5
.text:0040115A                 add     edx, [ebp+key1]
.text:0040115D                 xor     eax, edx
.text:0040115F                 add     eax, [ebp+a0]
.text:00401162                 mov     [ebp+a0], eax
.text:00401165                 mov     eax, [ebp+a0]
.text:00401168                 shl     eax, 4
.text:0040116B                 add     eax, [ebp+key2]
.text:0040116E                 mov     ecx, [ebp+a0]
.text:00401171                 add     ecx, [ebp+v]
.text:00401174                 xor     eax, ecx
.text:00401176                 mov     edx, [ebp+a0]
.text:00401179                 shr     edx, 5
.text:0040117C                 add     edx, [ebp+key3]
.text:0040117F                 xor     eax, edx
.text:00401181                 add     eax, [ebp+a1]
.text:00401184                 mov     [ebp+a1], eax
.text:00401187                 jmp     loc_4010E9
  • 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

之后跳转到 loc_4010E9 更新循环变量 i 的值然后进入下一次循环。

.text:004010E9 loc_4010E9:                             ; CODE XREF: tea+187↓j
.text:004010E9                 mov     edx, [ebp+i]
.text:004010EC                 add     edx, 1
.text:004010EF                 mov     [ebp+i], edx
  • 1
  • 2
  • 3
  • 4

跳出循环后首先会进入一个异常处理。由于 ecx = 0,因此必然触发异常,如果触发异常就将 dword_41F038 和 dword_41F03C 分别异或 a0 和 a1。
最后,程序会将计算出的 a0 和 a1 赋给 a 数组的对应位置然后返回。

.text:0040118C loc_40118C:                             ; CODE XREF: tea+F8↑j
.text:0040118C ;   __try { // __except at loc_4011AB
.text:0040118C                 mov     [ebp+ms_exc.registration.TryLevel], 2
.text:00401193                 xor     edx, edx
.text:00401195                 xor     eax, eax
.text:00401197                 inc     eax
.text:00401198                 xor     ecx, ecx
.text:0040119A                 idiv    ecx
.text:0040119A ;   } // starts at 40118C
.text:0040119C                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:004011A3                 jmp     short loc_4011C6
.text:004011A5 ; ---------------------------------------------------------------------------
.text:004011A5
.text:004011A5 loc_4011A5:                             ; DATA XREF: .rdata:stru_41E0C0↓o
.text:004011A5 ;   __except filter // owned by 40118C
.text:004011A5                 mov     eax, 1
.text:004011AA                 retn
.text:004011AB ; ---------------------------------------------------------------------------
.text:004011AB
.text:004011AB loc_4011AB:                             ; DATA XREF: .rdata:stru_41E0C0↓o
.text:004011AB ;   __except(loc_4011A5) // owned by 40118C
.text:004011AB                 mov     esp, [ebp+ms_exc.old_esp]
.text:004011AE                 mov     eax, [ebp+a0]
.text:004011B1                 mov     dword_41F038, eax
.text:004011B6                 mov     ecx, [ebp+a1]
.text:004011B9                 mov     dword_41F03C, ecx
.text:004011BF                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:004011C6
.text:004011C6 loc_4011C6:                             ; CODE XREF: tea+1A3↑j
.text:004011C6                 mov     edx, 4
.text:004011CB                 imul    eax, edx, 0
.text:004011CE                 mov     ecx, [ebp+a]
.text:004011D1                 mov     edx, [ebp+a0]
.text:004011D4                 mov     [ecx+eax], edx
.text:004011D7                 mov     eax, 4
.text:004011DC                 shl     eax, 0
.text:004011DF                 mov     ecx, [ebp+a]
.text:004011E2                 mov     edx, [ebp+a1]
.text:004011E5                 mov     [ecx+eax], edx
.text:004011E8                 mov     ecx, [ebp+ms_exc.registration.Next]
.text:004011EB                 mov     large fs:0, ecx
.text:004011F2                 pop     ecx
.text:004011F3                 pop     edi
.text:004011F4                 pop     esi
.text:004011F5                 pop     ebx
.text:004011F6                 mov     esp, ebp
.text:004011F8                 pop     ebp
.text:004011F9                 retn
  • 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

根据上述分析个写出如下解密程序:

#include <bits/stdc++.h>

using u8 = unsigned char;
using u32 = unsigned int;

constexpr u32 key[4] = {0x67452301, 0x0EFCDAB89, 0x98BADCFE, 0x10325476};
constexpr u32 delta = (int) 0x9E3779B1;
constexpr u32 t = 32;

u8 data[33]{0x30, 0xB5, 0x27, 0x5E, 0xF3, 0xF7, 0xBE, 0xBD, 0x8F, 0x6A, 0x51, 0xE3, 0xFE, 0x6C, 0x83, 0x5D, 0x09, 0xFA, 0x3D, 0xD8, 0x7A, 0x73, 0xFC, 0x8E, 0xA3, 0x53, 0xA8, 0x55, 0xC5, 0x4E, 0x56, 0x7A, 0x00};
u32 dword_41F038 = 0x1234567;
u32 dword_41F03C = 0x89ABCDEF;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    std::vector<u32> v(t);
    for (int i = 0; i < t; i++) {
        v[i] = (i == 0 ? 0 : v[i - 1]) + delta;
        if ((v[i] >> 0x1F) == 0) {
            v[i] ^= 0x1234567;
        }
    }
    std::reverse(v.begin(), v.end());

    auto tea = [&](u32 *a) {
        u32 a0 = a[0];
        u32 a1 = a[1];
        for (int i = 0; i < t; i++) {
            a[1] -= (key[3] + (a[0] >> 5)) ^ (v[i] + a[0]) ^ (key[2] + 16 * a[0]);
            a[0] -= (key[1] + (a[1] >> 5)) ^ (v[i] + a[1]) ^ (key[0] + 16 * a[1]);
        }
        a[0] ^= dword_41F038;
        a[1] ^= dword_41F03C;
        dword_41F038 = a0;
        dword_41F03C = a1;
    };

    for (int i = 0; i < 4; i++) {
        tea((u32 *) &data[8 * i]);
    }

    std::cout << data << std::endl;

    return 0;
}
  • 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

运行即可得到 flag:600d_y0u_r34lly_kn0w_734_4nd_53h

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/509224
推荐阅读
相关标签
  

闽ICP备14008679号