RE WP February 14, 2020

攻防世界

Words count 26k Reading time 23 mins. Read count 1000000

666

放入detectit查看发现是64位elf文件,peid什么都没发现。

看看ida中的伪代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s; // [rsp+0h] [rbp-1E0h]
  char v5; // [rsp+F0h] [rbp-F0h]

  memset(&s, 0, 0x1EuLL);
  printf("Please Input Key: ", 0LL);
  __isoc99_scanf("%s", &v5);
  encode(&v5, &s);
  if ( strlen(&v5) == key )
  {
    if ( !strcmp(&s, enflag) )
      puts("You are Right");
    else
      puts("flag{This_1s_f4cker_flag}");
  }
  return 0;
}

结构很简单,输入的flag放到v5里面,然后再encode中加密,加密后的变量是s,然后比较长度,长度是key,key的值给出来了是18,然后加密后的s与系统中的变量enflag进行比较,相同则答案正确。

再看看encode中的加密过程:

int __fastcall encode(const char *a1, __int64 a2)
{
  char v3[32]; // [rsp+10h] [rbp-70h]
  char v4[32]; // [rsp+30h] [rbp-50h]
  char v5[40]; // [rsp+50h] [rbp-30h]
  int v6; // [rsp+78h] [rbp-8h]
  int i; // [rsp+7Ch] [rbp-4h]

  i = 0;
  v6 = 0;
  if ( strlen(a1) != key )
    return puts("Your Length is Wrong");
  for ( i = 0; i < key; i += 3 )
  {
    v5[i] = key ^ (a1[i] + 6);
    v4[i + 1] = (a1[i + 1] - 6) ^ key;
    v3[i + 2] = a1[i + 2] ^ 6 ^ key;
    *(_BYTE *)(a2 + i) = v5[i];
    *(_BYTE *)(a2 + i + 1LL) = v4[i + 1];
    *(_BYTE *)(a2 + i + 2LL) = v3[i + 2];
  }
  return a2;
}

将输入的字符串每三个为一组,第一个字母加6异或key的值也就是18,第二个字母减6异或key,第三个异或6异或key

然后逆向写出脚本:

a=[0x69,0x7a,0x77,0x68,0x72,0x6f,0x7a,0x22,0x22,0x77,0x22,0x76,0x2e,0x4b,0x22,0x2e,0x4e,0x69]
key=18
flag=''
for i in range(0,18,3):
    flag+=chr((a[i]^key)-6)
    flag+=chr((a[i+1]^key)+6)
    flag+=chr(a[i+2]^key^6)
print(flag)

流浪者

是一个elf的文件,程序逻辑很简单,因为找不到main函数,所以直接用字符串找到有加密的地方就好了。

直接给出脚本

a='abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ'
b='KanXueCTF2019JustForhappy'
c=[]
for i in range(len(b)):
    for j in range(len(a)):
        if a[j] == b[i]:
            c.append(j)
print(c)

for i in range(0,len(c)):
    if c[i]>9 or c[i]<0:
        if c[i]>35 or c[i]<10:
            if c[i]>61 or c[i]<36:
                print("error")
            else:
                c[i] = c[i]+29    
        else:
            c[i]=c[i]+87
    else:
        c[i]=c[i]+48
print(c)
flag=''
for i in range(len(c)):
    flag+=chr(c[i])
print(flag)

testre

__int64 __fastcall sub_400700(void *a1, _QWORD *a2, __int64 a3, size_t a4)
{
  unsigned __int8 *v4; // rcx
  __int64 v6; // [rsp+0h] [rbp-C0h]
  int c; // [rsp+8h] [rbp-B8h]
  char v8; // [rsp+Fh] [rbp-B1h]
  int v9; // [rsp+10h] [rbp-B0h]
  bool v10; // [rsp+17h] [rbp-A9h]
  unsigned __int8 *v11; // [rsp+18h] [rbp-A8h]
  char v12; // [rsp+27h] [rbp-99h]
  int v13; // [rsp+28h] [rbp-98h]
  int v14; // [rsp+2Ch] [rbp-94h]
  unsigned __int64 i; // [rsp+30h] [rbp-90h]
  size_t n; // [rsp+38h] [rbp-88h]
  size_t v17; // [rsp+40h] [rbp-80h]
  size_t v18; // [rsp+48h] [rbp-78h]
  size_t j; // [rsp+50h] [rbp-70h]
  size_t v20; // [rsp+58h] [rbp-68h]
  int v21; // [rsp+64h] [rbp-5Ch]
  unsigned __int64 v22; // [rsp+68h] [rbp-58h]
  int v23; // [rsp+74h] [rbp-4Ch]
  __int64 *v24; // [rsp+78h] [rbp-48h]
  __int64 v25; // [rsp+80h] [rbp-40h]
  void *v26; // [rsp+88h] [rbp-38h]
  int v27; // [rsp+94h] [rbp-2Ch]
  size_t v28; // [rsp+98h] [rbp-28h]
  __int64 v29; // [rsp+A0h] [rbp-20h]
  _QWORD *v30; // [rsp+A8h] [rbp-18h]
  void *s; // [rsp+B0h] [rbp-10h]
  char v32; // [rsp+BFh] [rbp-1h]

  s = a1;
  v30 = a2;
  v29 = a3;
  v28 = a4;
  v27 = 0xDEADBEEF;
  v26 = malloc(0x100uLL);
  v25 = v29;
  v24 = &v6;
  v22 = 0LL;
  v17 = 0LL;
  for ( i = 0LL; i < v28; ++i )
  {
    v13 = *(unsigned __int8 *)(v25 + i);
    *((_BYTE *)v26 + i) = byte_400E90[i % 29] ^ v13;
    *((_BYTE *)v26 + i) += *(_BYTE *)(v25 + i);
  }                                                        //欺骗的部分,后面没有调用
  while ( 1 )
  {
    v12 = 0;
    if ( v17 < v28 )
      v12 = ~(*(_BYTE *)(v25 + v17) != 0);
    if ( !(v12 & 1) )
      break;
    ++v17;
  }
  n = ((unsigned __int64)(0x28F5C28F5C28F5C3LL * (unsigned __int128)(138 * (v28 - v17) >> 2) >> 64) >> 2) + 1;
  v23 = ((unsigned __int64)(0xAAAAAAAAAAAAAAABLL * (unsigned __int128)((v17 + v28) << 6) >> 64) >> 5) - 1;
  v11 = (unsigned __int8 *)&v6
      - ((((unsigned __int64)(0x28F5C28F5C28F5C3LL * (unsigned __int128)(138 * (v28 - v17) >> 2) >> 64) >> 2) + 16) & 0xFFFFFFFFFFFFFFF0LL);
  memset(v11, 0, n);
  v20 = v17;
  v18 = n - 1;
  while ( v20 < v28 )
  {
    v21 = *(unsigned __int8 *)(v25 + v20);
    for ( j = n - 1; ; --j )
    {
      v10 = 1;
      if ( j <= v18 )
        v10 = v21 != 0;
      if ( !v10 )
        break;
      v22 = v11[j] << 6;
      v21 += v11[j] << 8;
      v9 = 64;
      v11[j] = v21 % 58;
      *((_BYTE *)v26 + j) = v22 & 0x3F;
      v22 >>= 6;
      v21 /= 58;
      v27 /= v9;
      if ( !j )
        break;
    }
    ++v20;
    v18 = j;
  }
  for ( j = 0LL; ; ++j )
  {
    v8 = 0;
    if ( j < n )
      v8 = ~(v11[j] != 0);
    if ( !(v8 & 1) )
      break;
  }
  if ( *v30 > n + v17 - j )
  {
    if ( v17 )
    {
      c = 61;
      memset(s, 49, v17);
      memset(v26, c, v17);
    }
    v20 = v17;
    while ( j < n )
    {
      v4 = v11;
      *((_BYTE *)s + v20) = byte_400EB0[v11[j]];
      *((_BYTE *)v26 + v20++) = byte_400EF0[v4[j++]];//base64的符号表,但是这里也没有调用,看整个函数都使用的是base58的结构(因为有除以58)这个标志
    }
    *((_BYTE *)s + v20) = 0;
    *v30 = v20 + 1;
    if ( !strncmp((const char *)s, "D9", 2uLL)
      && !strncmp((const char *)s + 20, "Mp", 2uLL)
      && !strncmp((const char *)s + 18, "MR", 2uLL)
      && !strncmp((const char *)s + 2, "cS9N", 4uLL)
      && !strncmp((const char *)s + 6, "9iHjM", 5uLL)
      && !strncmp((const char *)s + 11, "LTdA8YS", 7uLL) )
    {
      HIDWORD(v6) = puts("correct!");
    }
    v32 = 1;
    v14 = 1;
  }
  else
  {
    *v30 = n + v17 - j + 1;
    v32 = 0;
    v14 = 1;
  }
  return v32 & 1;
}

找到这一段后,可以发现有两块是虚假的,函数中都标注出来了。

直接使用base58解码就好了

key

本题是对动态调试的考察,ida中的main函数如下:

int sub_401100()
{
  signed int v0; // esi
  signed int v1; // esi
  unsigned int v2; // edi
  void **v3; // ebx
  void **v4; // eax
  int v5; // ecx
  int v6; // ST04_4
  int v7; // ST08_4
  int v8; // ST0C_4
  int v9; // eax
  char *v10; // esi
  int v11; // ecx
  void **v12; // eax
  signed int v13; // eax
  int v14; // ecx
  int v15; // eax
  int v16; // eax
  int v17; // eax
  int v18; // eax
  int v19; // eax
  int v20; // eax
  int v21; // eax
  const char *v22; // edx
  int v23; // eax
  int result; // eax
  int Dst; // [esp+14h] [ebp-124h]
  char v26[4]; // [esp+20h] [ebp-118h]
  char v27; // [esp+24h] [ebp-114h]
  int v28; // [esp+5Ch] [ebp-DCh]
  char v29; // [esp+61h] [ebp-D7h]
  int v30; // [esp+64h] [ebp-D4h]
  int v31; // [esp+68h] [ebp-D0h]
  char v32; // [esp+6Ch] [ebp-CCh]
  FILE *File; // [esp+70h] [ebp-C8h]
  char v34; // [esp+84h] [ebp-B4h]
  void *v35; // [esp+CCh] [ebp-6Ch]
  int v36; // [esp+DCh] [ebp-5Ch]
  unsigned int v37; // [esp+E0h] [ebp-58h]
  void *v38; // [esp+E4h] [ebp-54h]
  unsigned int v39; // [esp+F4h] [ebp-44h]
  unsigned int v40; // [esp+F8h] [ebp-40h]
  void *Memory[4]; // [esp+FCh] [ebp-3Ch]
  unsigned int v42; // [esp+10Ch] [ebp-2Ch]
  unsigned int v43; // [esp+110h] [ebp-28h]
  __int128 v44; // [esp+114h] [ebp-24h]
  __int16 v45; // [esp+124h] [ebp-14h]
  char v46; // [esp+126h] [ebp-12h]
  int v47; // [esp+134h] [ebp-4h]

  v40 = 15;
  v39 = 0;
  LOBYTE(v38) = 0;
  v47 = 0;
  v37 = 15;
  v36 = 0;
  LOBYTE(v35) = 0;
  LOBYTE(v47) = 1;
  v0 = 0;
  v42 = 1684630885;
  LOWORD(v43) = 97;
  *Memory = xmmword_40528C;
  v45 = '.<';
  v46 = 0;
  v44 = xmmword_4052A4;
  do
  {
    sub_4021E0(&v35, 1u, (*(Memory + v0) ^ *(&v44 + v0)) + 22);
    ++v0;
  }
  while ( v0 < 18 );
  v1 = 0;
  v43 = 15;
  v42 = 0;
  LOBYTE(Memory[0]) = 0;
  LOBYTE(v47) = 2;
  v2 = v37;
  v3 = v35;
  do
  {
    v4 = &v35;
    if ( v2 >= 16 )
      v4 = v3;
    sub_4021E0(Memory, 1u, *(v4 + v1++) + 9);
  }
  while ( v1 < 18 );
  memset(&Dst, 0, 184u);
  sub_401620(&Dst, v5, v6, v7, v8);
  LOBYTE(v47) = 3;
  if ( v26[*(Dst + 4)] & 6 )
  {
    v9 = sub_402A00(std::cerr, "?W?h?a?t h?a?p?p?e?n?");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v9, sub_402C50);
    exit(-1);
  }
  sub_402E90(&Dst, &v38);
  v10 = &v27;
  if ( File )
  {
    if ( !sub_4022F0(&v27) )
      v10 = 0;
    if ( fclose(File) )
      v10 = 0;
  }
  else
  {
    v10 = 0;
  }
  v32 = 0;
  v29 = 0;
  std::basic_streambuf<char,std::char_traits<char>>::_Init(&v27);
  v30 = dword_408590;
  File = 0;
  v31 = dword_408594;
  v28 = 0;
  if ( !v10 )
    std::basic_ios<char,std::char_traits<char>>::setstate(&Dst + *(Dst + 4), 2, 0);
  v12 = Memory;
  if ( v43 >= 16 )
    v12 = Memory[0];
  v13 = sub_4020C0(&v38, v11, v39, v12, v42);
  v14 = std::cout;
  if ( v13 )
  {
    v22 = "=W=r=o=n=g=K=e=y=";
  }
  else
  {
    v15 = sub_402A00(std::cout, "|------------------------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v15, sub_402C50);
    v16 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v16, sub_402C50);
    v17 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v17, sub_402C50);
    v18 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v18, sub_402C50);
    v19 = sub_402A00(std::cout, "\\  /\\  /\\  /\\  /\\==============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v19, sub_402C50);
    v20 = sub_402A00(std::cout, " \\/  \\/  \\/  \\/  \\=============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v20, sub_402C50);
    v21 = sub_402A00(std::cout, "                 |-------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v21, sub_402C50);
    std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_402C50);
    v14 = std::cout;
    v22 = "Congrats You got it!";
  }
  v23 = sub_402A00(v14, v22);
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v23, sub_402C50);
  sub_401570(&v34);
  std::basic_ios<char,std::char_traits<char>>::~basic_ios<char,std::char_traits<char>>(&v34);
  if ( v43 >= 0x10 )
    sub_402630(Memory[0], v43 + 1);
  if ( v2 >= 0x10 )
    sub_402630(v3, v2 + 1);
  result = v40;
  if ( v40 >= 0x10 )
    result = sub_402630(v38, v40 + 1);
  return result;
}

该程序是要在指定的位置建立一个规定的内容文件,程序才能通过。

先建立好一个文本文件里面随便输入一些内容,然后在od中动态调试

用智能搜索字符串找到?W?h?a?t h?a?p?p?e?n?,然后对照ida反汇编找到main函数的入口位置,从头开始调试。

伪代码这个位置

v0 = 0;
  v42 = 1684630885;
  LOWORD(v43) = 97;
  *Memory = xmmword_40528C;
  v45 = '.<';
  v46 = 0;
  v44 = xmmword_4052A4;
  do
  {
    sub_4021E0(&v35, 1u, (*(Memory + v0) ^ *(&v44 + v0)) + 22);
    ++v0;
  }
  while ( v0 < 18 );

这段是系统内的两个变量生成的字符串,输入不影响。

对应的反汇编的位置是

.text:004011A0 loc_4011A0:                             ; CODE XREF: sub_401100+C4↓j
.text:004011A0                 mov     al, byte ptr [ebp+esi+var_24]
.text:004011A4                 lea     ecx, [ebp+var_6C]
.text:004011A7                 xor     al, byte ptr [ebp+esi+Memory]
.text:004011AB                 add     al, 16h
.text:004011AD                 mov     [ebp+var_128], al
.text:004011B3                 push    dword ptr [ebp+var_128] ; char
.text:004011B9                 push    1               ; Size
.text:004011BB                 call    sub_4021E0
.text:004011C0                 inc     esi
.text:004011C1                 cmp     esi, 12h
.text:004011C4                 jl      short loc_4011A0

继续往下看

do
  {
    v4 = &v35;
    if ( v2 >= 16 )
      v4 = v3;
    sub_4021E0(Memory, 1u, *(v4 + v1++) + 9);
  }
  while ( v1 < 18 );

这一段和上面的函数调用相同,同样生成了一组字符串

反汇编

.text:004011E0 loc_4011E0:                             ; CODE XREF: sub_401100+108↓j
.text:004011E0                 cmp     edi, 10h
.text:004011E3                 lea     eax, [ebp+var_6C]
.text:004011E6                 lea     ecx, [ebp+Memory]
.text:004011E9                 cmovnb  eax, ebx
.text:004011EC                 mov     al, [eax+esi]
.text:004011EF                 add     al, 9
.text:004011F1                 mov     [ebp+var_128], al
.text:004011F7                 push    dword ptr [ebp+var_128] ; char
.text:004011FD                 push    1               ; Size
.text:004011FF                 call    sub_4021E0
.text:00401204                 inc     esi
.text:00401205                 cmp     esi, 12h
.text:00401208                 jl      short loc_4011E0

经过动态调试后,sub_401620这个函数是用来读取建立的文件中的内容的

再往下走

 if ( v43 >= 16 )
    v12 = Memory[0];
  v13 = sub_4020C0(&v38, v11, v39, v12, v42);
  v14 = std::cout;
  if ( v13 )
  {
    v22 = "=W=r=o=n=g=K=e=y=";
  }
  else
  {
    v15 = sub_402A00(std::cout, "|------------------------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v15, sub_402C50);
    v16 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v16, sub_402C50);
    v17 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v17, sub_402C50);
    v18 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v18, sub_402C50);
    v19 = sub_402A00(std::cout, "\\  /\\  /\\  /\\  /\\==============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v19, sub_402C50);
    v20 = sub_402A00(std::cout, " \\/  \\/  \\/  \\/  \\=============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v20, sub_402C50);
    v21 = sub_402A00(std::cout, "                 |-------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v21, sub_402C50);
    std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_402C50);
    v14 = std::cout;
    v22 = "Congrats You got it!";
  }

这一段是比较字符串的地方,用动态调试通过寄存器监视可以看出来,一个部分是你输入的字符串,另一个是第二个循环出来的字符串。如果正确就会输出congrats,如果不通过就输出wrong

对应的反汇编是

.text:00401310 loc_401310:                             ; CODE XREF: sub_401100+1F4↑j
.text:00401310                 cmp     [ebp+var_28], 10h
.text:00401314                 lea     eax, [ebp+Memory]
.text:00401317                 push    [ebp+var_2C]
.text:0040131A                 cmovnb  eax, [ebp+Memory]
.text:0040131E                 push    eax
.text:0040131F                 push    [ebp+var_44]
.text:00401322                 push    ecx
.text:00401323                 lea     ecx, [ebp+var_54]

最后的flag就是第二次的循环的字符串

BabyXor

本题考查的是手动脱壳。

先把文件放入ida中看源代码,反汇编是成堆的数据,显然是加壳了,但是又在查壳软件中找不到壳,可能是作者手动改文件头了,官方的wp中给的是把段地址改了。

把文件放到od中看看有什么突破口,

0043F000 >  60              pushad
0043F001    B9 00100300     mov ecx,0x31000
0043F006    BB 00100000     mov ebx,0x1000
0043F00B    BA 00004000     mov edx,babyXor.00400000
0043F010    03DA            add ebx,edx                              ; babyXor.<ModuleEntryPoint>
0043F012    8033 23         xor byte ptr ds:[ebx],0x23
0043F015    43              inc ebx
0043F016    E0 FA           loopdne short babyXor.0043F012
0043F018    61              popad
0043F019    E9 82D4FCFF     jmp babyXor.0040C4A0

pushad和popad都在眼前,直接试试esp定律脱壳好了

先单走一个F8执行以下pushad,然后再右边的寄存器监视一栏右键esp点击HW break[ESP]再单走一个f9,来到了jmp处,再F8来到一个一堆数据的地方,使用ctrl+a这就是原文件本来的入口处,然后再push ebp的地方右键点击用olldump脱壳调试进程,进去后再点击脱壳。

生成新的文件后,程序内容很简单,有两种做法,一种是用ida写脚本输出flag,另一种是动态调试,这边给出ida脚本。

a=[0x66,0x6d,0x63,0x64,0x7f,0x37,0x35,0x30,0x30,0x6b,0x3a,0x3c,0x3b,0x20]
b=[]
e=[0x1a,0x0,0x0,0x51,0x5,0x11,0x54,0x56,0x55,0x59,0x1d,0x9,0x5d,0x12,0x0,0x0,0x0,0x0]
c=[0x37,0x6f,0x38,0x62,0x36,0x7c,0x37,0x33,0x34,0x76,0x33,0x62,0x64,0x7a]
for i in range(14):
    b.append(i^a[i])
print(b)
flag1=''
for i in range(14):
    flag1+=chr(b[i])
print(flag1)
d=[0x37]
flag2=''
for i in range(1,14):
    d.append(a[i]^c[i]^a[i-1])
for i in range(len(d)):
    flag2+=chr(d[i])
print(flag2)
#print(len(d))
f=[]
for i in range(13):
    f.append(e[i+1]^d[i]^i)
flag3=chr(0x37^0x1a)
for i in range(13):
    flag3+=chr(f[i])
print(flag3)
flag=flag1+flag2+flag3
print(flag)

动态调试比较简单,可以自己尝试。

Replace

本题是AES的算法题

先把程序放入detectit里,然后发现有UPX的壳在里面,可以直接用软件脱掉不需要手脱。

脱完壳后放入ida查看源代码,有main函数就进入main函数。

代码如下

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // kr00_4
  char Buf; // [esp+4h] [ebp-2Ch]
  char Dst; // [esp+5h] [ebp-2Bh]

  Buf = 0;
  memset(&Dst, 0, 0x27u);
  printf("Welcome The System\nPlease Input Key:");
  gets_s(&Buf, 40u);
  v3 = strlen(&Buf);
  if ( (v3 - 35) <= 2 )
  {
    if ( sub_401090(&Buf, v3) == 1 )
      printf("Well Done!\n");
    else
      printf("Your Wrong!\n");
  }
  return 0;
}

进入if比较的那个函数sub_401090。

如下:

signed int __fastcall sub_401090(int a1, int a2)
{
  int v2; // ebx
  int v4; // edx
  char v5; // al
  int v6; // esi
  int v7; // edi
  char v8; // al
  int v9; // eax
  char v10; // cl
  int v11; // eax
  int v12; // ecx

  v2 = a1;
  if ( a2 != 35 )
    return -1;
  v4 = 0;
  while ( 1 )
  {
    v5 = *(v4 + v2);
    v6 = (v5 >> 4) % 16;
    v7 = (16 * v5 >> 4) % 16;
    v8 = byte_402150[2 * v4];
    if ( v8 < 48 || v8 > '9' )
      v9 = v8 - 87;
    else
      v9 = v8 - 48;
    v10 = byte_402151[2 * v4];
    v11 = 16 * v9;
    if ( v10 < '0' || v10 > '9' )
      v12 = v10 - 87;
    else
      v12 = v10 - 48;
    if ( byte_4021A0[16 * v6 + v7] != ((v11 + v12) ^ 25) )
      break;
    if ( ++v4 >= 35 )
      return 1;
  }
  return -1;
}

有点像AES,但是可以发现可以写个脚本求出16*v6+v7,flag就是这个,就是要把最后一个字符变成}

脚本如下:

a='2a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6'
#print(len(a)//2)
v8=[]
v9=[]
for i in range(len(a)//2):
    v8.append(ord(a[2*i]))
for i in range(len(v8)):
    if v8[i] < 48 or v8[i] > 57:
        v9.append(v8[i]-87) 
    else:
        v9.append(v8[i]-48)
#print(v9)
v11=[]
v10='a49f69c38395cde96d6de96d6f4e025484954d6195448def6e2dad67786e21d5adae6'
for i in range(len(v9)):
    v11.append(16*v9[i])
print(v11)
v12=[]
v13=[]
print(len(v10)//2)
for i in range(len(v10)//2):
    v13.append(ord(v10[2*i]))
for i in range(len(v13)):
    if v13[i] < 48 or v13[i] > 57:
        v12.append(v13[i]-87) 
    else:
        v12.append(v13[i]-48)
v12.append(0)
for i in range(len(v11)):
    print((v11[i]+v12[i])^25)
sbox=[99, 124, 119, 123, 242, 107, 111, 197,   48,   1, 103,  43, 254, 215, 171, 118,
202, 130, 201, 125, 250,  89, 71, 240,  173, 212, 162, 175, 156, 164, 114, 192,
183, 253, 147,  38,  54,  63, 247, 204,   52, 165, 229, 241, 113, 216,  49,  21,
  4, 199,  35, 195,  24, 150,   5, 154, 7, 18, 128, 226, 235,  39, 178, 117,
  9, 131,  44,  26,  27, 110,  90, 160,   82,  59, 214, 179,  41, 227,  47, 132,
 83, 209,   0, 237,  32, 252, 177,  91,  106, 203, 190,  57,  74,  76,  88, 207,
208, 239, 170, 251,  67,  77,  51, 133,   69 ,249,   2, 127,  80,  60, 159, 168,
 81, 163,  64, 143, 146, 157,  56 ,245,  188 ,182, 218 , 33,  16 ,255, 243 ,210,
205,  12 , 19, 236,  95, 151 , 68 , 23,  196 ,167, 126 , 61, 100 , 93 , 25 ,115,
 96, 129 , 79, 220 , 34,  42 ,144 ,136,   70 ,238, 184 , 20, 222 , 94  ,11, 219,
224,  50,  58,  10,  73,   6 , 36 , 92,  194 ,211, 172  ,98, 145 ,149, 228 ,121,
231, 200,  55, 109, 141, 213 , 78 ,169,  108 , 86, 244 ,234, 101 ,122, 174,   8,
186, 120,  37,  46,  28, 166 ,180 ,198,  232 ,221, 116  ,31,  75, 189, 139, 138,
112,  62, 181, 102,  72,   3 ,246 , 14,   97 , 53,  87 ,185, 134, 193,  29, 158,
225, 248, 152,  17, 105, 217 ,142 ,148,  155,  30, 135, 233, 206,  85,  40 ,223,
140, 161, 137,  13, 191, 230 , 66 ,104,   65 ,153,  45,  15, 176,  84, 187,  22,]
number=[]
for i in range(len(v11)):
    for j in range(256):
        if sbox[j] == ((v11[i]+v12[i])^25):
            number.append(j)
print(number)
flag1=''
for i in range(len(number)):
    flag1+=chr(number[i])
print(flag1)

babyre1

xxtea加上CRC16的加密

进入main函数看加密的部分:

else if ( v6 == 16 )
  {
    v7 = sub_556D83F90C00(&v14, 16, &ptr);
    if ( v7
      && (v8 = sub_556D83F91180(ptr, v7, &byte_556D84192010, 16, &v12), (v9 = v8) != 0LL)
      && v12 > 0
      && sub_556D83F913D0(v8, v12) == 0x69E2 )
    {
      for ( i = 0LL; v12 > i; ++i )
        v9[i] ^= 0x17u;
      puts(v9);
      if ( ptr )
        free(ptr);
      free(v9);
    }

题目中说答案对了会输出Bingo!

可以看到后面的puts(v9),在这之前v9还进行了和0x17的异或。

可以求出v9的变换之前的答案。

这是前六位还有10位不清楚

原题目还给出了MD5(rctf{your answer}) == 5f8243a662cf71bf31d2b2602638dc1d

还有第一个函数是说把你输入的字符转换为十六进制比如输入‘1234567890’就变成了0x12,0x34,0x56,0x78,0x90

第二个函数是xxtea加密。

第三个函数是经过两个函数变换之后的CRC校验

看一下脚本:

from hashlib import md5
import xxtea

def cmd5():
  hh = '5f8243a662cf71bf31d2b2602638dc1d'
  s = '557e79707836%02x%02x'
  for j in range(256):
     for i in range(256):
         t = s%(i,j)
         enc = xxtea.encrypt_hex(t.decode('hex'),'C7E0C7E0D7D3F1C6D3C6D3C6CED2D0C4'.decode('hex'),padding = False)
         flag = 'rctf{'+enc+'}'
         if md5(flag).hexdigest() == hh:
            print flag,t
            break

def main():
  cmd5()
  print 'end.'

if __name__ == '__main__':
  main()

just reverse it

一道md5的密码题目,关键看一下关键部分

signed int __cdecl sub_4025E3(int a1, unsigned __int8 *a2)
{
  char v2; // al
  char v3; // al
  char v4; // al
  char v5; // al
  int v7; // [esp+4h] [ebp-1Ch]
  int v8; // [esp+8h] [ebp-18h]
  int v9; // [esp+Ch] [ebp-14h]
  int v10; // [esp+10h] [ebp-10h]
  int v11; // [esp+14h] [ebp-Ch]
  int j; // [esp+18h] [ebp-8h]
  int i; // [esp+1Ch] [ebp-4h]

  v8 = 0;
  v9 = 0;
  v10 = 0;
  v11 = 0;
  v7 = 0;
  if ( *a2 >> 4 <= 9 )
    v2 = (*a2 >> 4) + 48;
  else
    v2 = (*a2 >> 4) + 87;
  HIBYTE(v7) = v2;
  if ( (*a2 & 0xF) <= 9 )
    v3 = (*a2 & 0xF) + 48;
  else
    v3 = (*a2 & 0xF) + 87;
  BYTE1(v7) = v3;
  if ( a2[1] >> 4 <= 9 )
    v4 = (a2[1] >> 4) + 48;
  else
    v4 = (a2[1] >> 4) + 87;
  LOBYTE(v7) = v4;
  if ( (a2[1] & 0xF) <= 9 )
    v5 = (a2[1] & 0xF) + 48;
  else
    v5 = (a2[1] & 0xF) + 87;
  BYTE2(v7) = v5;
  for ( i = 0; i <= 3; ++i )
  {
    if ( !*(&v8 + i) && *(&v7 + i) == *(i + a1) )
      *(&v8 + i) = 1;
  }
  for ( j = 0; j <= 3; ++j )
  {
    if ( !*(&v8 + j) )
      return 0;
  }
  return 1;
}

主要是对前面输入的值进行了变换位置然后变形,官方wp说是MD5加密。

位置变换如下abcd->dbac

看下解密脚本:

import hashlib
for i in "0123456789abcdef":
        for j in "0123456789abcdef":
                for k in "0123456789abcdef":
                        for l in "0123456789abcdef":
                                md5=hashlib.md5()
                                md5.update((i+j+k+l).encode())
                                #print(md5.hexdigest())
                                if md5.hexdigest()[:4]==l+j+i+k:
                                        print(i+j+k+l)
#31795a469327c6e6
answer='31795a469327c6e6'
flag=''
box=[100,1,64,102,108,81,65,105,122,65,83,84,8,105,84,66]
for i in range(16):
        flag+=chr(box[i]^ord(answer[i]))
print(flag)
0%