信大校赛

信大校赛writeup

0x00 Android

题目比较简单,使用反编译工具查看java代码如下:

import android.content.Context;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.Toast;

class a
  implements View.OnClickListener
{
  a(MainActivity paramMainActivity, EditText paramEditText, Context paramContext) {}

  public void onClick(View paramView)
  {
    if (this.a.getText().toString().equals(String.format("flag{%s}", new Object[] { MainActivity.m.substring(12, 24) })))
    {
      Toast.makeText(this.b, "You are right!", 1).show();
      return;
    }
    Toast.makeText(this.b, "You are wrong!", 1).show();
  }
}

程序的主要算法流程是将
gUIDP@Rt}T10n$C#3m3Gu]d_par7|t)OnSCH3M3
这个字符串经过substring(12, 24)变化之后得到新的字符串,substring() 方法用于提取字符串中介于两个指定下标之间的字符,也就是说将下标12到下标20之间的字符串提取出来,结果是0n
$C#3m3Gu]。

0x01 warmup-re

拖到IDA里看一下伪代码:

// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax@1
  __int64 v4; // rdx@1
  __int64 v5; // rax@1
  __int64 v6; // rbx@1
  __int64 v7; // rdx@1
  __int64 v8; // rax@1
  __int64 v9; // rdx@1
  __int64 v10; // rax@2
  __int64 v11; // rdx@2
  int result; // eax@2
  __int64 v13; // rax@5
  __int64 v14; // rdx@5
  unsigned __int64 v15; // rbx@7
  unsigned __int64 v16; // rax@7
  __int64 v17; // rax@8
  __int64 v18; // rdx@8
  char v19[48]; // [sp+20h] [bp-60h]@1
  char v20[48]; // [sp+50h] [bp-30h]@1
  char v21[48]; // [sp+80h] [bp+0h]@4
  int v22; // [sp+B0h] [bp+30h]@1
  int v23; // [sp+B4h] [bp+34h]@1
  int v24; // [sp+B8h] [bp+38h]@1
  int v25; // [sp+BCh] [bp+3Ch]@1
  int v26; // [sp+C0h] [bp+40h]@1
  int v27; // [sp+C4h] [bp+44h]@1
  int v28; // [sp+C8h] [bp+48h]@1
  int v29; // [sp+CCh] [bp+4Ch]@1
  int v30; // [sp+D0h] [bp+50h]@1
  int v31; // [sp+D4h] [bp+54h]@1
  int v32; // [sp+D8h] [bp+58h]@1
  int v33; // [sp+DCh] [bp+5Ch]@1
  int v34; // [sp+E0h] [bp+60h]@1
  int v35; // [sp+E4h] [bp+64h]@1
  int v36; // [sp+E8h] [bp+68h]@1
  int v37; // [sp+ECh] [bp+6Ch]@1
  int v38; // [sp+F0h] [bp+70h]@1
  int v39; // [sp+F4h] [bp+74h]@1
  int v40; // [sp+F8h] [bp+78h]@1
  int v41; // [sp+FCh] [bp+7Ch]@1
  int v42; // [sp+100h] [bp+80h]@1
  char v43; // [sp+10Ah] [bp+8Ah]@4
  char v44; // [sp+10Bh] [bp+8Bh]@4
  int i; // [sp+10Ch] [bp+8Ch]@3

  _main();
  v22 = 86;
  v23 = 30;
  v24 = 24;
  v25 = 1;
  v26 = 21;
  v27 = 90;
  v28 = 27;
  v29 = 29;
  v30 = 6;
  v31 = 29;
  v32 = 76;
  v33 = 84;
  v34 = 22;
  v35 = 20;
  v36 = 85;
  v37 = 28;
  v38 = 22;
  v39 = 21;
  v40 = 30;
  v41 = 29;
  v42 = 23;
  LODWORD(v3) = std::operator>><char,std::char_traits<char>>(*(_QWORD *)&argc, argv, v19, refptr__ZSt3cin);
  std::operator>><char,std::char_traits<char>>(*(_QWORD *)&argc, argv, v20, v3);
  LODWORD(v5) = strlen(*(_QWORD *)&argc, argv, v4, v19);
  v6 = v5;
  LODWORD(v8) = strlen(*(_QWORD *)&argc, argv, v7, v20);
  if ( v6 == v8 )
  {
    for ( i = 0; ; ++i )
    {
      v15 = i;
      LODWORD(v16) = strlen(*(_QWORD *)&argc, argv, v9, v19);
      if ( v15 >= v16 )
        break;
      v44 = v19[i];
      v43 = v20[i];
      v21[i] = v43 ^ v44;
      v9 = (unsigned int)v21[i];
      if ( (_DWORD)v9 != *(&v22 + i) )
      {
        LODWORD(v13) = std::operator<<<std::char_traits<char>>(*(_QWORD *)&argc, argv, "key error", refptr__ZSt4cout);
        std::ostream::operator<<(
          *(_QWORD *)&argc,
          argv,
          refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
          v13);
        system(*(_QWORD *)&argc, argv, v14, "pause");
        return 0;
      }
    }
    LODWORD(v17) = std::operator<<<std::char_traits<char>>(
                     *(_QWORD *)&argc,
                     argv,
                     "Yes!input is flag",
                     refptr__ZSt4cout);
    std::ostream::operator<<(
      *(_QWORD *)&argc,
      argv,
      refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
      v17);
    system(*(_QWORD *)&argc, argv, v18, "pause");
    result = 0;
  }
  else
  {
    LODWORD(v10) = std::operator<<<std::char_traits<char>>(*(_QWORD *)&argc, argv, "lenth error", refptr__ZSt4cout);
    std::ostream::operator<<(
      *(_QWORD *)&argc,
      argv,
      refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
      v10);
    system(*(_QWORD *)&argc, argv, v11, "pause");
    result = 0;
  }
  return result;
}

程序的意思就是输入两个长度相等的字符串之后进行异或运算,将运算结果和程序给出的v22的数组中的内容比较,提示给出其中一个字符串是goodgoodstudydaydayup,解密脚本如下:

v1=[86,30,24,1,21,90,27,29,6,29,76,84,22,20,85,28,22,21,30,29,23]
v2=[103,111,111,100,103,111,111,100,115,116,117,100,121,100,97,121,100,97,121,117,112]
flag=[]
for i in xrange(0,21):
    flag+=chr(list(v1)[i]^list(v2)[i])
print(flag)

结果是

['1', 'q', 'w', 'e', 'r', '5', 't', 'y', 'u', 'i', '9', '0', 'o', 'p', '4', 'e', 'r', 't', 'g', 'h', 'g']

0x02 reverse1

使用IDA分析,关键函数是encrypt()

       v19 = 0;
  LODWORD(v16) = std::string::operator[](a2, 0LL);
  while ( *v16 )
  {
    LODWORD(v2) = std::string::operator[](a2, v19);
    v3 = *v2;
    LODWORD(v4) = std::vector<int,std::allocator<int>>::operator[](&primos, v19);
    v18 = v3 + *v4;
    LODWORD(v5) = std::string::operator[](a2, v19);
    if ( (unsigned __int8)is_lower(*v5) )
    {
      v6 = 122;
    }
    else
    {
      LODWORD(v7) = std::string::operator[](a2, v19);
      if ( (unsigned __int8)is_upper(*v7) )
      {
        v6 = 90;
      }
      else
      {
        LODWORD(v8) = std::string::operator[](a2, v19);
        v6 = *v8;
      }
    }
    while ( v18 > v6 )
      v18 -= 26;
    LODWORD(v9) = std::string::operator[](a2, v19);
    v10 = v9;
    LODWORD(v11) = std::string::operator[](a2, v19);
    if ( *v11 == 123 )
    {
      v14 = 125;
    }
    else
    {
      LODWORD(v12) = std::string::operator[](a2, v19);
      if ( *v12 == 125 )
      {
        v14 = 123;
      }
      else
      {
        LODWORD(v13) = std::string::operator[](a2, v19);
        if ( (unsigned __int8)is_alphabet(*v13) )
        {
          v14 = v18;
        }
        else
        {
          LODWORD(v15) = std::string::operator[](a2, v19);
          v14 = *v15;
        }
      }
    }
    *v10 = v14;
    LODWORD(v16) = std::string::operator[](a2, ++v19);
  }
  std::string::string(a1, a2);
  return a1;
}

查找字符串还找到了个flag

LNLNGW}o3A3g5Z_1b_Xv5d_WbzgGnbG{

程序流程是将你输入的正确的flag,每一位加上prime[]相应位置的值,如果是{}的话,{换成},}换成{。如果是字母,判断大小写运算后是否超出范围,如果超出范围就减去26直到符合大小写范围为止,然后逆推算法就可以了。比较麻烦的是prime[]是什么,程序里还有一个initial()

  for ( i = 2; i <= 1023; ++i )
    ehprimo[(signed __int64)i] = (i & 1) != 0;
  for ( j = 3; ; j += 2 )
  {
    result = j;
    if ( (signed int)j > 1023 )
      break;
    if ( ehprimo[(signed __int64)(signed int)j] )
    {
      std::vector<int,std::allocator<int>>::push_back(&primos, &j);
      for ( k = j * j; (signed int)k <= 1023; k += j )
        ehprimo[(signed __int64)(signed int)k] = 0;
    }
  }
  return result;
}

这个函数是求0~1024之间的素数,不包括2作为prime[]的数据。

最后逆推出来flag为IIECTF{r3V3r5E_1s_Ea5y_DeadBeeF}

0x03 CrackMe

IDA反编译发现想要获得flag需要通过4个check函数:

第一个cheack

signed int check1(void)
{
  signed int v1; // [sp+10h] [bp-8h]@7

  puts("Give me your favourite prime number");
  scanf("%lld", &n);
  a = 0x100000LL;
  b = 0LL;
  c = 0LL;
  while ( !(a & 1) )
  {
    a /= 2LL;
    ++b;
  }
  while ( !(b & 1) )
  {
    b /= 2LL;
    ++c;
  }
  if ( c == n )
  {
    flag += n;
    v1 = 1;
  }
  else
  {
    puts("^^^^Your math is bad^^^^");
    v1 = 0;
  }
  return v1;
}

python跑出来符合条件的数是2

a=1048576
b=0
c=0
var=a&1
while var==0:
    a=a/2
    var=a&1
    b=b+1
print(b)
vbr=b&1
while vbr==0:
    b=b/2
    vbr=b&1
    c=c+1
print(c)

chack2

signed int check2(void)
{
  signed int v1; // [sp+24h] [bp-14h]@10
  __int64 v2; // [sp+28h] [bp-10h]@1

  puts("Do you know how to calculate the number");
  puts("please enter a number");
  scanf("%lld", &n);
  v2 = n;
  if ( tmpans != n * tmp % mod || n > 999999 )
    goto LABEL_15;
  while ( n )
  {
    x[++pos] = n % 10;
    n /= 10LL;
  }
  if ( dword_448044 >= dword_448048
    || dword_448048 >= dword_44804C
    || dword_44804C >= dword_448050
    || dword_448050 >= dword_448054
    || dword_448054 >= dword_448058 )
  {
LABEL_15:
    puts("You do not know how to calculate the mod~~~~");
    v1 = 0;
  }
  else
  {
    flag += v2;
    v1 = 1;
  }
  return v1;
}

python代码得出符合条件的数是654321

tmpans=779852816
tmp=123456
mod=1000000007
i=1
for i in xrange(1,1000000):
    if(i*tmp%mod==tmpans):
        print(i)

check3

python脚本得出符合条件的字符串是Lcont=gpfoog`q

str1=[]
for i in range(14):
    j=i%6+1
    str1.append(j)
print(str1)

v18="MerryChristmas"
list=list(v18)
for i in range(len(list)):
    list[i]=ord(list[i])
print(list)

flag=[]
for i in range(14):
    flag.append(list[i]-str1[i])
print(flag)

flag1=""
for i in range(14):
    flag1+=chr(flag[i])
print(flag1)

check4

在OD里动态调试修改跳转最后一个符合条件的数字是176455667

四关都过了,最后将每一关的key输入就可以了。
flag is:176455667HqGpGpFoFoEnGpHqBk

0x04 61dre

int __cdecl main(int argc, const char **argv, const char **envp)
{
  bool v3; // r13@2
  int v4; // eax@5
  const char *v5; // rdi@11
  size_t v6; // rax@11
  size_t v7; // rax@15
  char *v8; // rcx@15
  const char *v9; // rsi@15
  bool v10; // di@17
  const char **v12; // [sp+0h] [bp-80h]@2
  unsigned __int64 v13; // [sp+8h] [bp-78h]@11
  int v14; // [sp+14h] [bp-6Ch]@5
  char *s1; // [sp+18h] [bp-68h]@2
  const char **v16; // [sp+20h] [bp-60h]@2
  const char ***v17; // [sp+28h] [bp-58h]@2
  bool v18; // [sp+37h] [bp-49h]@2
  int *v19; // [sp+38h] [bp-48h]@2
  const char ***v20; // [sp+40h] [bp-40h]@2
  const char **v21; // [sp+48h] [bp-38h]@1
  int v22; // [sp+54h] [bp-2Ch]@1

  v22 = argc;
  v21 = argv;
  if ( !(((unsigned __int8)((y < 10) ^ ((((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) | (y < 10
                                                                               && (((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) & 1) )
    goto LABEL_20;
  while ( 1 )
  {
    v12 = v21;
    v3 = (((_BYTE)x - 106 + 105) * (_BYTE)x & 1) == 0;
    v20 = &v12 - 2;
    v19 = (int *)&v12;
    v18 = (_DWORD)v21 != 2;
    v17 = &v12;
    v16 = (const char **)&v12;
    s1 = (char *)(&v12 - 6);
    if ( ((y < 10 && v3) | (unsigned __int8)((y < 10) ^ v3)) & 1 )
      break;
LABEL_20:
    LODWORD(v12) = 0;
    *((_DWORD *)&v12 - 4) = v22;
    *(&v12 - 2) = v21;
  }
  if ( v18 )
  {
    while ( !(((unsigned __int8)((y < 10) ^ ((((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) | (y < 10
                                                                                    && (((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) & 1) )
      ;
LABEL_5:
    v4 = printf("Usage: %s flag\n", **v17, v12);
    *v19 = 0;
    v14 = v4;
    return *v19;
  }
  *v16 = (const char *)&unk_400E54;
  if ( strlen((*v17)[1]) != 32 )
  {
    while ( !(((y < 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) == 0) | (unsigned __int8)((y >= 10) ^ ((((_BYTE)x - 1)
                                                                                                * (_BYTE)x & 1) != 0))) & 1) )
      ;
    goto LABEL_5;
  }
  if ( !(((y < 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) == 0) | (unsigned __int8)((y >= 10) ^ ((((_BYTE)x - 1) * (_BYTE)x & 1) != 0))) & 1) )
    goto LABEL_23;
  while ( 1 )
  {
    *(_DWORD *)v20 = 0;
    if ( ((y < 10 && (((_BYTE)x - 1) * (_BYTE)x & 1) == 0) | (unsigned __int8)((y >= 10) ^ ((((_BYTE)x - 1) * (_BYTE)x & 1) != 0))) & 1 )
      break;
LABEL_23:
    *(_DWORD *)v20 = 0;
  }
  while ( 1 )
  {
    v5 = (*v17)[1];
    v13 = *(_DWORD *)v20;
    v6 = strlen(v5);
    if ( v13 >= v6 )
      break;
    if ( !(((unsigned __int8)((y < 10) ^ ((((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) | (y < 10
                                                                                 && (((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) & 1) )
      goto LABEL_24;
    while ( 1 )
    {
      s1[*(_DWORD *)v20] = (*(_BYTE *)v20 & 0x89 | ~*(_BYTE *)v20 & 0x76) ^ ((*v17)[1][*(_DWORD *)v20] & 0x89 | ~(*v17)[1][*(_DWORD *)v20] & 0x76);
      if ( ((unsigned __int8)((y < 10) ^ ((((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) | (y < 10
                                                                                 && (((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) & 1 )
        break;
LABEL_24:
      s1[*(_DWORD *)v20] = (*(_BYTE *)v20 & 0x38 | ~*(_BYTE *)v20 & 0xC7) ^ ((*v17)[1][*(_DWORD *)v20] & 0x38 | ~(*v17)[1][*(_DWORD *)v20] & 0xC7);
    }
    *(_DWORD *)v20 = *(_DWORD *)v20 - 403331085 + 403331086;
  }
  v7 = strlen((*v17)[1]);
  v8 = s1;
  s1[v7] = 0;
  v9 = *v16;
  if ( !strcmp(v8, *v16) )
    HIDWORD(v12) = printf("yes\n", v9, v12);
  do
    v10 = (((_BYTE)x - 1) * (_BYTE)x & 1) == 0;
  while ( !(((y < 10 && v10) | (unsigned __int8)((y < 10) ^ v10)) & 1) );
  *v19 = 0;
  return *v19;
}

代码很多,猛地一看肯定有点蒙,我也是看了人家的writeup才懂。。。
关键就在

(((unsigned __int8)((y < 10) ^ ((((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) | (y< 10&& (((_BYTE)x - 1) * (_BYTE)x & 1) == 0)) & 1)

这段代码里的通过动态调试x,y一直都是0,这个条件始终为真。
那么算法逻辑接下来就是

s1[*(_DWORD *)v20] = (*(_BYTE *)v20 & 0x89 | ~*(_BYTE *)v20 & 0x76) ^ ((*v17
)[1][*(_DWORD *)v20] & 0x89 | ~(*v17)[1][*(_DWORD *)v20] & 0x76);

最后s1与

0x36,0x62,0x76,0x65,0x7F,0x6D,0x63,0x6B,0x64,0x66,0x55,0x7C,0x63,0x7
F,0x62,0x6B,0x4F,0x65,0x7A,0x76,0x4B,0x7A,0x74,0x71,0x79,0x6A,0x79,0x7A,0x68
,0x72,0x6C,0x62]

这串字符比较,爆破脚本如下:

    str=[ 0x36,0x62,0x76,0x65,0x7F,0x6D,0x63,0x6B,
      0x64,0x66,0x55,0x7C,0x63,0x7F,0x62,0x6B,
      0x4F,0x65,0x7A,0x76,0x4B,0x7A,0x74,0x71,
      0x79,0x6A,0x79,0x7A,0x68,0x72,0x6C,0x62]
flag=""
for i in range(len(str)):
    for j in xrange(32,128):
        if((j&0x89 | ~j&0x76)^(i&0x89 | ~i&0x76)==str[i]):
            flag=flag+chr(j)
print(flag)
文章目录
  1. 1. 信大校赛writeup
  2. 2. 0x00 Android
  3. 3. 0x01 warmup-re
  4. 4. 0x02 reverse1
  5. 5. 0x03 CrackMe
  6. 6. 0x04 61dre
|