buuCTF的wp

前言,主要是re方向的题,应该会持续更新,更新时间不定

re方向

1.easyre

签到题,直接用ida打开,shift+12查看字符串,如下

得到flag:flag{this_Is_a_EaSyRe}

2.reverse1

f5看关键代码如下

  sub_1400111D1("input the flag:");
  sub_14001128F("%20s", &Str1);
  v3 = j_strlen(Str2);
  if ( !strncmp(&Str1, Str2, v3) )//意思是输入的flag是str1,当str1和str2相等是就是正确的
    sub_1400111D1("this is the right flag!\n");
  else
    sub_1400111D1("wrong flag\n");
  sub_14001113B(&v5, &unk_140019D00);
  return 0i64;

str2点开是{hello_world},然后上面有个点是要把o改为0,所以flag:flag{hell0_w0rld}

3.reverse2

打开IDA,F5看关键代码。

将圈出来的代码用R键,得到

图片中说错了,应该是要把flag中的i和r改为1

点开&flag,得到关键代码

.data:0000000000601080 flag            db '{'//这个{也要用R键才看得到                  ; DATA XREF: main+34↑r
.data:0000000000601080                                         ; main+44↑r ...
.data:0000000000601081 aHackingForFun  db 'hacking_for_fun}',0
.data:0000000000601081 _data           ends

flag中的i和r改为1,最后得到flag:flag{hack1ng_fo1_fun}

4.内涵的软件

根据提示可能是一个upx加壳的软件,用PEID打开
直接IDA打开查看字符串,得到flag:flag{49d3c93df25caad81232130f3d2ebfad}

5.新年快乐

用ida打开什么也没有,用PEID打开

发现是个upx加壳的东西,用upx解壳,将upx.exe和该文件一起放在D盘下,再在cmd里面执行命令。

原来21kb的文件变成了28kb,再用ida打开,得到重要代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  char Str2; // [esp+12h] [ebp-3Ah]
  char Str1[2]; // [esp+20h] [ebp-2Ch]
  char v6; // [esp+22h] [ebp-2Ah]

  __main();
  strcpy(&Str2, "HappyNewYear!");
  *(_WORD *)Str1 = 0;
  memset(&v6, 0, 0x1Eu);
  printf("please input the true flag:");
  scanf("%s", Str1);
  if ( !strncmp(Str1, &Str2, strlen(&Str2)) )//str1是输入的flag,这个str1要和str2相等
    result = puts("this is true flag!");
  else
    result = puts("wrong!");
  return result;
}

所以flag就是:flag{HappyNewYear!}

6.[BJDCTF 2nd]guessgame

ida打开,得到flag:BJD{S1mple_ReV3r5e_W1th_0D_0r_IDA}

7.helloword

下载下来发现是个apk文件,直接解压,得到几个文件

可以把那个文件后缀名直接改txt,然后搜索flag,也可以用jeb软件打开,然后搜索字符串得到
flag{7631a988259a00816deda84afb29430a}

8.xor

考点 数组内异或

ida打开,找到主要函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rsi
  int result; // eax
  signed int i; // [rsp+2Ch] [rbp-124h]
  char v6[264]; // [rsp+40h] [rbp-110h]
  __int64 v7; // [rsp+148h] [rbp-8h]

  memset(v6, 0, 0x100uLL);
  v3 = (char *)256;
  printf("Input your flag:\n", 0LL);
  get_line(v6, 256LL);//v6是输入的flag
  if ( strlen(v6) != 33 )//输入的flag长度为33
    goto LABEL_12;
  for ( i = 1; i < 33; ++i )
    v6[i] ^= v6[i - 1];         //数组异或,可以自己举三个数,然后进行异或,然后在看看怎么异或回去。
  v3 = global;
  if ( !strncmp(v6, global, 0x21uLL) ) //global要和异或v6相等,要自己去找global的值。
    printf("Success", v3);
  else
LABEL_12:
    printf("Failed", v3);
  result = __stack_chk_guard;
  if ( __stack_chk_guard == v7 )
    result = 0;
  return result;
}

global为f\nk\fw&O.@\x11x\rZ;U\x11p\x19F\x1Fv"M#D\x0Eg\x06h\x0FG2O,可以在Python中用list改善一下’f’, ‘\n’, ‘k’, ‘\x0c’, ‘w’, ‘&’, ‘O’, ‘.’, ‘@’, ‘\x11’, ‘x’, ‘\r’, ‘Z’, ‘;’, ‘U’, ‘\x11’, ‘p’, ‘\x19’, ‘F’, ‘\x1f’, ‘v’, ‘“‘, ‘M’, ‘#’, ‘D’, ‘\x0e’, ‘g’, ‘\x06’, ‘h’, ‘\x0f’, ‘G’, ‘2’, ‘O’

开始写脚本

#include<stdio.h>

int main(void)
{
    int i;
    char a[]={'f', '\n', 'k', '\x0c', 'w', '&', 'O', '.', '@', '\x11', 'x', '\r', 'Z', ';', 'U', '\x11', 'p', '\x19', 'F', '\x1f', 'v', '"', 'M', '#', 'D', '\x0e', 'g', '\x06', 'h', '\x0f', 'G', '2', 'O'};

    for(i=32;i!=0;i--)
    {
        a[i]=a[i]^a[i-1];
     } 

    for(i=0;i<33;i++)
    {
        printf("%c",a[i]);
    }
 } 

得到flag{QianQiuWanDai_YiTongJiangHu}

9.reverse3

用ida打开,找到主要函数

  int v3; // edx
  __int64 v4; // ST08_8
  signed int j; // [esp+DCh] [ebp-ACh]
  signed int i; // [esp+E8h] [ebp-A0h]
  signed int v8; // [esp+E8h] [ebp-A0h]
  char Dest[108]; // [esp+F4h] [ebp-94h]
  char Str; // [esp+160h] [ebp-28h]
  char v11; // [esp+17Ch] [ebp-Ch]

  for ( i = 0; i < 100; ++i )
  {
    if ( (unsigned int)i >= 0x64 )
      j____report_rangecheckfailure();
    Dest[i] = 0;
  }
  sub_41132F("please enter the flag:");
  sub_411375("%20s", &Str);//输入的flag
  v0 = j_strlen(&Str);
  v1 = (const char *)sub_4110BE(&Str, v0, &v11);//对输入的flag进行了没有改表的base编码,可以直接用网上在线base64解码。
  strncpy(Dest, v1, 0x28u);//将v1给了Dest
  v8 = j_strlen(Dest);
  for ( j = 0; j < v8; ++j )//对Dest进行了一个简单加密。
    Dest[j] += j;
  v2 = j_strlen(Dest);
  if ( !strncmp(Dest, Str2, v2) )//将Dest和str2比较,就是要一样
    sub_41132F("rigth flag!\n");
  else
    sub_41132F("wrong flag!\n");
  HIDWORD(v4) = v3;
  LODWORD(v4) = 0;
  return v4;
}

先找到str2=‘e3nifIH9b_C@n@dH’,用这个来得到,解密后的Dest,脚本如下

#include<stdio.h>

int main(void)
{
    char a[]={'e', '3', 'n', 'i', 'f', 'I', 'H', '9', 'b', '_', 'C', '@', 'n', '@', 'd', 'H'};
    int i;

    for(i=0;i<16;i++)
    {
        a[i]=a[i]-i;
     } 
    for(i=0;i<16;i++)
    {
        printf("%c",a[i]);
    }

}

得到Dest=‘e2lfbDB2ZV95b3V9’,由于是未改表的base编码,所以可以直接用网上在线解码,得到flag{i_l0ve_you}

10.不一样的flag

考点,迷宫题。

直接打开,叫你输入1,2,3,4,分别代表上下左右。

有耐心的话可以一次一次试,只要输入对了,就会继续你输入,直到无论你输入那个数都是直接退出,将输入的数组合起来就是flag。

用ida查看源代码。

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  char v3; // [esp+17h] [ebp-35h]
  int v4; // [esp+30h] [ebp-1Ch]
  int v5; // [esp+34h] [ebp-18h]
  int v6; // [esp+38h] [ebp-14h]
  int i; // [esp+3Ch] [ebp-10h]
  char v8[12]; // [esp+40h] [ebp-Ch]

  __main();
  v4 = 0;
  v5 = 0;
  qmemcpy(&v3, _data_start__, 0x19u);
  while ( 1 )
  {
    puts("you can choose one action to execute");
    puts("1 up");
    puts("2 down");
    puts("3 left");
    printf("4 right\n:");
    scanf("%d", &v6);//你输入的数,也就是flag

    ————————————————————————————————————————————————
    if ( v6 == 2 )
    {
      ++v4;
    }
    else if ( v6 > 2 )
    {
      if ( v6 == 3 )
      {
        --v5;
      }
      else
      {
        if ( v6 != 4 )
LABEL_13:
          exit(1);
        ++v5;
      }
    }
    else
    {
      if ( v6 != 1 )
        goto LABEL_13;
      --v4;
    }
    for ( i = 0; i <= 1; ++i )
    {
      if ( *(&v4 + i) < 0 || *(&v4 + i) > 4 )
        exit(1);
    }
    if ( v8[5 * v4 - 41 + v5] == 49 )
      exit(1);
    if ( v8[5 * v4 - 41 + v5] == 35 )
    {
      puts("\nok, the order you enter is the flag!");
      exit(0);
    }//这一整段就是要通过你输入的一个个数字,使最后一个if调键满足,那个35实际上就是‘#’,也就是迷宫的终点,而49就是迷宫中的‘1’,不能碰到。
——————————————————————————————————————————————————————————
  }
}

真正的迷宫在字符串中

'*11110100001010000101111#'//25个数,盲猜5*5

用脚本画出迷宫

#include<stdio.h>

int main(void)
{
    int a[5][5]={'*', '1', '1', '1', '1', '0', '1', '0', '0', '0', '0', '1', '0', '1', '0', '0', '0', '0', '1', '0', '1', '1', '1', '1', '#'};
    int i,j;

    for(i=0;i<5;i++)
    {
        for(j=0;j<5;j++)
        {
        printf("%c",a[i][j]);
        }
        printf("\n");
    }    
 } 

得到迷宫

*1111
01000
01010
00010
1111#

根据1,2,3,4四个反向得到flag{222441144222}

11.SimpleRev

关键代码如下,要熟悉ascall码表中AZ,az的十进制数

unsigned __int64 Decry()
{
  char v1; // [rsp+Fh] [rbp-51h]
  int v2; // [rsp+10h] [rbp-50h]
  int v3; // [rsp+14h] [rbp-4Ch]
  int i; // [rsp+18h] [rbp-48h]
  int v5; // [rsp+1Ch] [rbp-44h]
  char src[8]; // [rsp+20h] [rbp-40h]
  __int64 v7; // [rsp+28h] [rbp-38h]
  int v8; // [rsp+30h] [rbp-30h]
  __int64 v9; // [rsp+40h] [rbp-20h]
  __int64 v10; // [rsp+48h] [rbp-18h]
  int v11; // [rsp+50h] [rbp-10h]
  unsigned __int64 v12; // [rsp+58h] [rbp-8h]

  v12 = __readfsqword(0x28u);
  *(_QWORD *)src = 357761762382LL;//src='SLCDN'
  v7 = 0LL;
  v8 = 0;
  v9 = 512969957736LL;//v9='wodah'
  v10 = 0LL;
  v11 = 0;
  text = (char *)join(key3, &v9);// key3='kills'    test=key3+v9=‘killshadow’  
  strcpy(key, key1);// Key1='ADSFK'
  strcat(key, src);//Key='ADSFKNDCLS'   注意要倒过来
  v2 = 0;
  v3 = 0;
  getchar();
  v5 = strlen(key);
  for ( i = 0; i < v5; ++i )
  {
    if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
      key[i] = key[v3 % v5] + 32;
    ++v3;
  }//将key全部换为小写。
  printf("Please input your flag:", src);
  while ( 1 )
  {
    v1 = getchar();//V1就是输入的flag,根据下面的if条件只能算出v1的范围为64~90,97~122,涉及到下面%求余的整数部分的问题。
    if ( v1 == 10 )
      break;
    if ( v1 == 32 )
    {
      ++v2;
    }
    else
    {
      if ( v1 <= 96 || v1 > 122 )
      {
        if ( v1 > 64 && v1 <= 90 )
          str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;//加密语句
      }
      else
      {
        str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
      }
      if ( !(v3 % v5) )
        putchar(32);
      ++v2;
    }
  }
  if ( !strcmp(text, str2) )//要求text=str2
    puts("Congratulation!\n");
  else
    puts("Try again!\n");
  return __readfsqword(0x28u) ^ v12;
}

感觉这道题有点问题,v1的范围有点没确定好,也可能是我忽略了某个地方。

脚本如下

#include<stdio.h>

int main(void)
{
    char a[11]="ADSFKNDCLS";
    char b[11]="killshadow";
    char c[11]="\0";
    int i=0,j=0;

    for(i=0;i<11;i++,j++)
    {
        if( a[j % 11] > 64 && a[j % 11] <= 90)
            a[i] = a[j % 10] + 32;//这个里面的j%11实际上就相当于i,只是在ida中打开变了样子。

        printf("%c",a[i]);
    }
    printf("\n");
    for(i=0;i<10;i++)
    {
        c[i]=(b[i]-97)+26+39-97+a[i];//因为不知道原加密语句求余的那个整数是多少所以只加了一个26, 
        if(c[i]>90)
        {
            c[i]=c[i]-26;
        }//加上这个主要是要所有字母都是大写的,不然会有乱码 
        printf("%c",c[i]);
    }

}

12.[BJDCTF 2nd]8086

根据文件名字,得知应该这是8086的32位汇编。

用ida打开,shift+12打开字符串,发现只有一句点进去,得到

查看重要代码如下

根据这个分析开始写脚本,如下

#include<stdio.h>

int main(void)
{
    int i;
    char a[]={']', 'U', '[', 'd', 'u', '~', '|', 't', '@', '{', 'z', '@', 'w', 'j', '.', '}', '.', '~', 'q', '@', 'g', 'j', 'z', '{', 'z', '@', 'w', 'z', 'q', 'W', '~', '/', 'b', ';'};

    for(i=0;i<33;i++)
    {
        a[i]=a[i]^0x1f;
        printf("%c",a[i]);
    }
} 

得到 flag:BJD{jack_de_hu1b1an_xuede_henHa0}

13.[GKCTF2020]Check_1n

这道题,首先明确一点,这是签到题,所以不会太难

先运行出来,发现是一个用c++写的一个代码电脑文件

↑ ↓ ← → 空格    进行操作

先开机,发现需要密码,打开ida,看字符串。

开机后发现有几个文件,其中有一个叫flag,打开

发现

解码内容为

Why don't you try the magic brick game
你为什么不试试魔法砖游戏

然后去玩魔法砖游戏


得到flag{f5dfd0f5-0343-4642-8f28-9adbb74c4ede}回头看看这

14.findit

下载下来发现是个apk文件,用jeb打开。

发现重要线索

 this.findViewById(0x7F05003D).setOnClickListener(new View$OnClickListener(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}, this.findViewById(0x7F05003E), new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}, this.findViewById(0x7F05003F))

提取有用信息

p v k q { m 1 6 4 6 7 5 2 6 2 0 3 3 l 4 m 4 9 l n p 7 p 9 m n k 2 8 k 7 5 } //实际上将apk文件解压,然后将文件classes.dex后缀改为txt,搜索‘{’也可以得到这个

分析jeb代码,发现好像和凯撒加密有一些关系,直接暴力破解,脚本如下

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(void)
{
    char table1[27]="abcdefghijklmnopqrstuvwxyz";
    char table2[27]="ABCDEFJHIJKLMNOPQRSTUVWXYZ";
    char str1[100]="pvkq{m164675262033l4m49lnp7p9mnk28k75}";
    char str2[100]="";
    int i,k,len;

    len=strlen(str1);
    for(k=0;k<26;k++)
    {
        for(i=0;i<len;i++)
        {
            if(str1[i]>=table1[k]&&str1[i]<='z')
            {
                str2[i]=str1[i]-k;
            }
            else if(str1[i]<table1[k]&&str1[i]>='a')
            {
                str2[i]=str1[i]+26-k;
            }
            else if(str1[i]>=table2[k]&&str1[i]<='Z')
            {
                str2[i]=str1[i]-k;
            }
            else if(str1[i]<table2[k]&&str1[i]>='A')
            {
                str2[i]=str1[i]+26-k;
            }            
            else
            {
                str2[i]=str1[i];
            }
        }
        printf("%s",str2);
        printf("\n");
    }
 } 

得到

pvkq{m164675262033l4m49lnp7p9mnk28k75}
oujp{l164675262033k4l49kmo7o9lmj28j75}
ntio{k164675262033j4k49jln7n9kli28i75}
mshn{j164675262033i4j49ikm7m9jkh28h75}
lrgm{i164675262033h4i49hjl7l9ijg28g75}
kqfl{h164675262033g4h49gik7k9hif28f75}
jpek{g164675262033f4g49fhj7j9ghe28e75}
iodj{f164675262033e4f49egi7i9fgd28d75}
hnci{e164675262033d4e49dfh7h9efc28c75}
gmbh{d164675262033c4d49ceg7g9deb28b75}
flag{c164675262033b4c49bdf7f9cda28a75}//这个就是flag
ekzf{b164675262033a4b49ace7e9bcz28z75}
djye{a164675262033z4a49zbd7d9aby28y75}
cixd{z164675262033y4z49yac7c9zax28x75}
bhwc{y164675262033x4y49xzb7b9yzw28w75}
agvb{x164675262033w4x49wya7a9xyv28v75}
zfua{w164675262033v4w49vxz7z9wxu28u75}
yetz{v164675262033u4v49uwy7y9vwt28t75}
xdsy{u164675262033t4u49tvx7x9uvs28s75}
wcrx{t164675262033s4t49suw7w9tur28r75}
vbqw{s164675262033r4s49rtv7v9stq28q75}
uapv{r164675262033q4r49qsu7u9rsp28p75}
tzou{q164675262033p4q49prt7t9qro28o75}
synt{p164675262033o4p49oqs7s9pqn28n75}
rxms{o164675262033n4o49npr7r9opm28m75}
qwlr{n164675262033m4n49moq7q9nol28l75}

也可以根据前几个字母,先算出位移量,到网上去在线解密。

15.[GXYCTF2019]luck_guy

自己ida打开,找到关键代码,找的过程就不说了

unsigned __int64 get_flag()
{
  unsigned int v0; // eax
  char v1; // al
  signed int i; // [rsp+4h] [rbp-3Ch]
  signed int j; // [rsp+8h] [rbp-38h]
  __int64 s; // [rsp+10h] [rbp-30h]
  char v6; // [rsp+18h] [rbp-28h]
  unsigned __int64 v7; // [rsp+38h] [rbp-8h]

  v7 = __readfsqword(40u);
  v0 = time(0LL);
  srand(v0);
  for ( i = 0; i <= 4; ++i )
  {
    switch ( rand() % 200 )//就是随机取数了,所以叫luck_guy,实际上有用的case也就1,4,5。
    {
      case 1:
        puts("OK, it's flag:");
        memset(&s, 0, 0x28uLL);                 // 将s0清0
        strcat((char *)&s, f1);                 // f1='GXY{do_not_'
        strcat((char *)&s, &f2);                // f2是剩下的那一半flag
        printf("%s", &s);
        break;
      case 2:
        printf("Solar not like you");
        break;
      case 3:
        printf("Solar want a girlfriend");
        break;
      case 4:
        v6 = 0;
        s = 'fo`guci';
        strcat(&f2, (const char *)&s);//将s给f2,注意要反序。
        break;
      case 5://就一个将f2的加密过程,算出来的f2就是另一半flag。
        for ( j = 0; j <= 7; ++j )
        {
          if ( j % 2 == 1 )
            v1 = *(&f2 + j) - 2;
          else
            v1 = *(&f2 + j) - 1;
          *(&f2 + j) = v1;
        }
        break;
      default:
        puts("emmm,you can't find flag 23333");
        break;
    }
  }
  return __readfsqword(0x28u) ^ v7;
}

根据分析,写出脚本

#include<stdio.h>

int main(void)
{
    int i;
    char a[]={'i', 'c', 'u', 'g', '`', 'o', 'f', '\x7f'};

    for(i=0;i<=7;i++)
    {
        if (i%2==1)
            a[i]=a[i]-2;
        else
            a[i]=a[i]-1;
        printf("%c",a[i]);
     } 
 }

得到f2:hate_me} 和f1拼起来,得到GXY{do_not_hate_me}

16.简单注册器

下载下来是个apk文件,jeb打开,与14题一样操作,得到重要代码。

根据分析写出脚本如下

#include<stdio.h>

int main(void)
{
    int m=0x1f,n=2,i=0,j;
    char a[33]="dd2940c04462b4dd7c450528835cca15";

    a[n] = ((char)(a[n] + a[3] - 50));
    a[4] = ((char)(a[n] + a[5] - 0x30));
    a[30] = ((char)(a[m] + a[9] - 0x30));
    a[14] = ((char)(a[27] + a[28] - 97));

    for(i=0;i<16;i++)
    {
        j=a[0x1f-i];
        a[0x1f-i]=a[i];
        a[i]=j;
    }
    printf("%s",a);
 } 

得到59acc538825054c7de4b26440c0999dd

所以flag{59acc538825054c7de4b26440c0999dd}

17.[GWCTF 2019]pyre

看题目,pyre,说明是python文件,下载下来是pyc,先反编译为py文件,在cmd里面输入命令(我11届极客大挑战的文章中中有也有一道题是反编译pyc文件)

C:\Users\hp>uncompyle6 -o C:\Users\hp\Downloads\attachment.py C:\Users\hp\Downloads\attachment.pyc
C:\Users\hp\Downloads\attachment.pyc --
# Successfully decompiled file

出现Successfully则表示成功。用python的IDLE打开这个py文件,得到源码,如下

print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l)://第一次for循环加密,注意i和1的区别,我就是因为把i看作了1,做了很久。
    num = ((input1[i] + i) % 128 + 128) % 128
    code += num//注意这一句,和c不太一样,它的意思是用num的值来填充code。

for i in range(l - 1)://第二个加密,就是数组内异或。
    code[i] = code[i] ^ code[(i + 1)]

print code
code = ['\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13']

写出脚本

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(void)
{
    int i,len;
    char code[23]={'\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13'};
    len=strlen(code);
    printf("%d",len);
    printf("\n");
    for(i=len-2;i!=-1;i--)//数组内异或解密
    {
        code[i]=code[i]^code[i+1];
    }
    for(i=0;i!=len;i++)
    {
        code[i]=code[i]-i;//这时候的一些code[i]的十进制值是负数
        if(code[i]<0)//注意这个if条件,保证flag内字符的值在64~127内。
        {
            code[i]+=128;
        }
        printf("%c",code[i]);
    }
 } 

得到flag:GWHT{Just_Re_1s_Ha66y!}

18.rsa

首先了解rsa是什么。
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
https://zhuanlan.zhihu.com/p/44185847?utm_source=wechat_session

下载下来得到两个文件,一个文件是flag,一个是公钥。

用公钥在线分析网站,分解公钥
http://tool.chacuo.net/cryptrsakeyparse/,也可以用Linux的kali来分解公钥(某位大佬的博客https://www.cnblogs.com/jane315/p/13175850.html)
得到

n=C0332C5C64AE47182F6C1C876D42336910545A58F7EEFEFC0BCAAF5AF341CCDD
e=65537 (0x10001)

用Python将n转化为十进制

>>> a=int('C0332C5C64AE47182F6C1C876D42336910545A58F7EEFEFC0BCAAF5AF341CCDD',16)
>>> print(a)
86934482296048119190666062003494800588905656017203025617216654058378322103517

得到n=86934482296048119190666062003494800588905656017203025617216654058378322103517

在继续分解n得到p,q。网站:http://factordb.com/得到

p=285960468890451637935629440372639283459
q=304008741604601924494328155975272418463

然后写脚本,这里是直接用的别人的,需要注意的是需要安装两个库。

import gmpy2
import rsa

e=65537
n=86934482296048119190666062003494800588905656017203025617216654058378322103517
p=285960468890451637935629440372639283459
q=304008741604601924494328155975272418463

phin = (p-1) * (q-1)
d=gmpy2.invert(e, phin)

key=rsa.PrivateKey(n,e,int(d),p,q)

with open("flag.enc","rb") as f://这里是打开那个flag.net文件
    f=f.read()
    print(rsa.decrypt(f,key))

得到flag{decrypt_256}

19.JustRE

文件下载下来,打开发现

说是要你一直点就会出flag。不过它说的还真是真的。

正确解法,放到ida里面,shift +12。

找到其所在函数,f5看伪代码。

不知道sprintf的意思,网上搜一搜,https://blog.csdn.net/wangkeyen/article/details/81942918

aBjdDD2069a4579就是BJD{%d%d2069a45792d233ac}
而 sprintf(&String, aBjdDD2069a4579, 19999, 0);就是要你把19999,0分别是那两个%d

得到flag{1999902069a45792d233ac}

20.[2019红帽杯]easyRE

下载文件用ida打开,shift+12;

找到重要代码

  unsigned __int64 v54; // [rsp+108h] [rbp-18h]

  v54 = __readfsqword(0x28u);
  v12 = 73;
  v13 = 111;
  v14 = 100;
  v15 = 108;
  v16 = 62;
  v17 = 81;
  v18 = 110;
  v19 = 98;
  v20 = 40;
  v21 = 111;
  v22 = 99;
  v23 = 121;
  v24 = 127;
  v25 = 121;
  v26 = 46;
  v27 = 105;
  v28 = 127;
  v29 = 100;
  v30 = 96;
  v31 = 51;
  v32 = 119;
  v33 = 125;
  v34 = 119;
  v35 = 101;
  v36 = 107;
  v37 = 57;
  v38 = 123;
  v39 = 105;
  v40 = 121;
  v41 = 61;
  v42 = 126;
  v43 = 121;
  v44 = 76;
  v45 = 64;
  v46 = 69;
  v47 = 67;
  memset(v48, 0, sizeof(v48));
  v49 = 0;
  v50 = 0;
  sub_4406E0(0LL, v48, 37LL);
  v50 = 0;
  if ( sub_424BA0(v48) == 36 )
  {
    for ( i = 0; i < (unsigned __int64)sub_424BA0(v48); ++i )
    {
      if ( (unsigned __int8)(v48[i] ^ i) != *(&v12 + i) )//v48是自己输入的,*(&v12 + i)就相当于一个数组,从v12到v47,一共36个数。简单的异或。
      {
        result = 4294967294LL;
        goto LABEL_13;
      }
    }
    sub_410CC0("continue!");
    memset(&v51, 0, 0x40uLL);
    v53 = 0;
    sub_4406E0(0LL, &v51, 64LL);
    v52 = 0;
    if ( sub_424BA0(&v51) == 39 )
    {
      v1 = sub_400E44(&v51);
      v2 = sub_400E44(v1);
      v3 = sub_400E44(v2);
      v4 = sub_400E44(v3);
      v5 = sub_400E44(v4);
      v6 = sub_400E44(v5);
      v7 = sub_400E44(v6);
      v8 = sub_400E44(v7);
      v9 = sub_400E44(v8);
      v10 = sub_400E44(v9);
      if ( !(unsigned int)sub_400360(v10, off_6CC090) )
      {
        sub_410CC0("You found me!!!");
        sub_410CC0("bye bye~");
      }
      result = 0LL;
    }
    else
    {
      result = 4294967293LL;
    }
  }
  else
  {
    result = 0xFFFFFFFFLL;
  }
LABEL_13:
  if ( __readfsqword(0x28u) != v54 )
    sub_444020();
  return result;
}

先看这一部分

if ( sub_424BA0(v48) == 36 )
  {
    for ( i = 0; i < (unsigned __int64)sub_424BA0(v48); ++i )
    {
      if ( (unsigned __int8)(v48[i] ^ i) != *(&v12 + i) )//v48是自己输入的,*(&v12 + i)就相当于一个数组,从v12到v47,一共36个数。简单的异或。
      {
        result = 4294967294LL;
        goto LABEL_13;
      }
    }

写出脚本

#include<stdio.h>

int main(void)
{
    int a[36]={73,111,100,108,62,81,110,98,40,111,99,121,127,121,46,105,127,100,96,51,119,125,119,101,107,57,123,105,121,61,126,121,76,64,69,67};     
    int i;

    for(i=0;i<36;i++)
    {
        a[i]=a[i]^i;
        printf("%c",a[i]); 
    }
 } 

得到

它说:前四个字符是“flag”,现在还不知道意思。

继续看

    memset(&v51, 0, 0x40uLL);//定义了一个数组叫v51
    v53 = 0;
    sub_4406E0(0LL, &v51, 64LL);
    v52 = 0;
    if ( sub_424BA0(&v51) == 39 )//v61长度是39
    {
      v1 = sub_400E44(&v51);//对v51base64编码
      v2 = sub_400E44(v1);//对v1base64编码
      v3 = sub_400E44(v2);
      v4 = sub_400E44(v3);
      v5 = sub_400E44(v4);
      v6 = sub_400E44(v5);
      v7 = sub_400E44(v6);
      v8 = sub_400E44(v7);
      v9 = sub_400E44(v8);
      v10 = sub_400E44(v9);
      if ( !(unsigned int)sub_400360(v10, off_6CC090) )将v10和off_6CC090对比。
      {
        sub_410CC0("You found me!!!");
        sub_410CC0("bye bye~");
      }

整个过程就是一个不断base64编码的过程,进行了10次,得到了off_6CC090,也就是

Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ==

直接网上解码10次,发现是https://bbs.pediy.com/thread-254172.htm,居然是一个网站。。。。无情被欺骗。最后通过看wp,发现真正的算法在

得到代码

unsigned __int64 __fastcall sub_400D35(__int64 a1, __int64 a2)
{
  __int64 v2; // rdx
  __int64 v3; // rdx
  __int64 v4; // rdx
  unsigned __int64 result; // rax
  unsigned __int64 v6; // rt1
  unsigned int v7; // [rsp+Ch] [rbp-24h]
  signed int i; // [rsp+10h] [rbp-20h]
  signed int j; // [rsp+14h] [rbp-1Ch]
  unsigned int v10; // [rsp+24h] [rbp-Ch]
  unsigned __int64 v11; // [rsp+28h] [rbp-8h]

  v11 = __readfsqword(0x28u);
  v7 = sub_43FD20() - qword_6CEE38;
  for ( i = 0; i <= '\x04�'; ++i )
  {
    sub_40F790(v7);
    sub_40FE60(v7, a2, v2);
    sub_40FE60(v7, a2, v3);
    v7 = (unsigned __int64)sub_40FE60(v7, a2, v4) ^ 2557891634;
  }
  v10 = v7;
  if ( ((unsigned __int8)v7 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v10) ^ (unsigned __int8)byte_6CC0A3) == 'g' )//这里的f和g是不是有点熟悉,再联系一下前面的提示:前四个字符是“flag”,通过这个来求key。
  {
    for ( j = 0; j <= 24; ++j )
      sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v10 + j % 4)));//加密算法,key实际上就是那个v10,通过j % 4,又可以知道,v10只有4个。
  }
  v6 = __readfsqword(0x28u);
  result = v6 ^ v11;
  if ( v6 != v11 )
    sub_444020();
  return result;
}

byte_6CC0A0[25]={0x40,0x35,0x20,0x56,0x5d,0x18,0x22,0x45,0x17,0x2f,0x24,0x6e,0x62,0x3c,0x27,0x54,0x48,0x6c,0x24,0x6e,0x72,0x3c,0x32,0x45,0x5b};

写出脚本

#include<stdio.h>

int main(void)
{
    int a[36]={73,111,100,108,62,81,110,98,40,111,99,121,127,121,46,105,127,100,96,51,119,125,119,101,107,57,123,105,121,61,126,121,76,64,69,67}; 
    int key[4]={'f','l','a','g'} ;
    int byte_6CC0A0[25]={0x40,0x35,0x20,0x56,0x5d,0x18,0x22,0x45,0x17,0x2f,0x24,0x6e,0x62,0x3c,0x27,0x54,0x48,0x6c,0x24,0x6e,0x72,0x3c,0x32,0x45,0x5b};
    int i,j;

    for(i=0;i<36;i++)
    {
        a[i]=a[i]^i;
        printf("%c",a[i]); 
    }
    printf("\n"); 
    for(i=0;i<4;i++)//通过前4个字符,也就是flag,求出key。
    {
        key[i]=key[i]^byte_6CC0A0[i];
    }
    for(j=0;j<25;j++)//用key来解密。
    {
        byte_6CC0A0[j]=byte_6CC0A0[j]^key[j%4];
        printf("%c",byte_6CC0A0[j]);
    }
 } 

得到flag{Act1ve_Defen5e_Test}

21.[ACTF新生赛2020]easyre

下载下来用PEID打开,发现是upx加壳的文件

由于前面已经介绍过upx解壳了,就不写过程了。

然后将文件拖入ida,shift+12,ctrl+x,一套操作。得到关键代码

  __main();
  v4 = 42;
  v5 = 70;
  v6 = 39;
  v7 = 34;
  v8 = 78;
  v9 = 44;
  v10 = 34;
  v11 = 40;
  v12 = 73;
  v13 = 63;
  v14 = 43;
  v15 = 64;//一个长度为12的数组。
  printf("Please input:");
  scanf("%s", &v19);
  if ( v19 != 'A' || v20 != 'C' || v21 != 'T' || v22 != 'F' || v23 != '{' || v27 != '}' )//这里是r键过后的,从这可以知道v24~v26是我们需要的东西
    return 0;
  v16 = v24;
  v17 = v25;
  v18 = v26;
  for ( i = 0; i <= 11; ++i )
  {
    if ( *(&v4 + i) != _data_start__[*((char *)&v16 + i) - 1] )// _data_start_是一个表,类似于base64编码表。*((char *)&v16 + i)是ACTF{}大括号中的内容。只不过这里我也有点疑惑,不应该只有3个吗。
      return 0;
  }
  printf("You are correct!");
  return 0;
}

写出脚本,这个脚本体现了一个从一个字符串中找某个字符的思想

#include<stdio.h>

int main(void)
{
    int a[120]={'~', '}', '|', '{', 'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '/', '.', '-', ',', '+', '*', ')', '(',0x27, '&', '%', '$', '#', ' ', '!', '"'};//a就是那个_data_start__
    int v4[12]={42,70,39,34,78,44,34,40,73,63,43,64};
    int i,j;

    for(i=0;i<12;i++)
    {
        for(j=0;j<=120;j++)
        {
            if(v4[i]==a[j])
            {
                printf("%c",j+1);//这个j+1是因为_data_start__[*((char *)&v16 + i) - 1]中的-1
                break;
            }
        }
    }
}

得到flag{U9X_1S_W6@T?}

22.[SUCTF2019]SignIn

文件下载下来直接用ida打开,一套操作找到关键代码

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char v4; // [rsp+0h] [rbp-4A0h]
  char v5; // [rsp+10h] [rbp-490h]
  char v6; // [rsp+20h] [rbp-480h]
  char v7; // [rsp+30h] [rbp-470h]
  char v8; // [rsp+40h] [rbp-460h]
  char v9; // [rsp+B0h] [rbp-3F0h]
  unsigned __int64 v10; // [rsp+498h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  puts("[sign in]");
  printf("[input your flag]: ", a2);
  __isoc99_scanf("%99s", &v8);
  sub_96A(&v8, &v9);
  __gmpz_init_set_str(&v7, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
  __gmpz_init_set_str(&v6, &v9, 16LL);
  __gmpz_init_set_str(&v4, "103461035900816914121390101299049044413950405173712170434161686539878160984549", 10LL);
  __gmpz_init_set_str(&v5, "65537", 10LL);
  __gmpz_powm(&v6, &v6, &v5, &v4);
  if ( (unsigned int)__gmpz_cmp(&v6, &v7) )
    puts("GG!");
  else
    puts("TTTTTTTTTTql!");
  return 0LL;
}

发现是一个rsa加密, __gmpz_init_set_str 函数是GNU 高精度算法库。

c=0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35//密文
n=103461035900816914121390101299049044413950405173712170434161686539878160984549
e=65537

先得到q,p。

q=282164587459512124844245113950593348271
p=366669102002966856876605669837014229419

用脚本跑出来

from Crypto.Util.number import *
import gmpy2

p = 282164587459512124844245113950593348271
q = 366669102002966856876605669837014229419
e = 65537
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35

n = q*p
phi = (q-1)*(p-1)
d = gmpy2.invert(e,phi)
m = gmpy2.powmod(c,d,n)
flag = long_to_bytes(m)

print(flag)

得到flag{Pwn_@_hundred_years}

23.Youngter-drive

首先文件下载下来,PEID打开,发现upx加壳了。

解壳

再用ida打开,找到主函数main_0,一进去就看得到,点进去在f5。

int __thiscall main_0(void *this)
{
  HANDLE v2; // [esp+D0h] [ebp-14h]
  HANDLE hObject; // [esp+DCh] [ebp-8h]

  sub_4110FF(this);
  ::hObject = CreateMutexW(0, 0, 0);
  j_strcpy(Dest, &Source);
  hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
  v2 = CreateThread(0, 0, sub_41119F, 0, 0, 0);
  CloseHandle(hObject);
  CloseHandle(v2);
  while ( dword_418008 != -1 )
    ;
  sub_411190();
  CloseHandle(::hObject);
  return 0;
}

这里是查阅的一些函数

CreateThread和CloseHandle:CreateThread是一种微软在Windows API中提供了建立新的线程的函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。

Sleep:Sleep函数可以使计算机程序(进程,任务或线程)进入休眠,使其在一段时间内处于非活动状态。当函数设定的计时器到期,或者接收到信号、程序发生中断都会导致程序继续执行。

本来线程是同时进行的,然而这一道题就是通过sleep这个函数让线程交替进行了,导致了偶数位没进行加密,奇数位进行了加密。

下面来分析这个主函数

然后主要分析这两个线程

进入那个加密函数,报错无法看源代码。

说是栈顶sp有问题,然后找到该函数位置。

修改sp

然后得到加密函数。

char *__cdecl sub_411940(int a1, int a2)//a1是那个source(你输入的flag),a2是那个dword_418008(29)
{
  char *result; // eax
  char v3; // [esp+D3h] [ebp-5h]

  v3 = *(_BYTE *)(a2 + a1);//相当与a[a2],source[a2]。
  if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
    exit(0);//这里是必须要你输入的flag是是A~Z,a~z。
  if ( v3 < 97 || v3 > 122 )//如果source[a2]是大写字母,就这样加密
  {
    result = off_418000[0];
    *(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 38];
  }
  else//如果source[a2]是小写字母,就这样加密
  {
    result = off_418000[0];
    *(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 96];
  }
  return result;
}

注意这一句

off_418000[0][*(char *)(a2 + a1) - 96];等价于off_418000[*(a2 + a1) - 96]//这个*(a2+a1)是未加密的,也就是你要求的flag。

一些数据

off_418000[]=QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm
加密后的*(_BYTE *)(a2 + a1)=TOiZiZtOrYaToUwPnToBsOaOapsyS

然后开始写脚本,

#include<stdio.h>

int main(void)
{
    char str[30]="TOiZiZtOrYaToUwPnToBsOaOapsyS";//off_418004
    char str1[53]="QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";//off_418000
    char flag[30];
    int a[30];
    int b[30];
    int i,j,m=0;

    for(i=0;i<29;i++)
    {
        if(i%2==0)
        flag[i]=str[i];
    }
    for(i=0;i<29;i++)
    {
        for(j=0;j<52;j++)
        {    
            if(str[i]==str1[j])
            {
//                printf("%d ",j);
                if(j<27)//这个27是算出来的,是决定是字母大写,字母小写的临界值 
                {
                    flag[m]=j+96;
                    m++;
                }
                else
                {
                    flag[m]=j+38;
                    m++;
                }
            }    
        }
    }

    for(i=0;i<29;i++)//这个for循环就是因为两个线程交替进行的原因,偶数位不加密,奇数位要加密 
    {
        if(i%2==0)
        flag[i]=str[i];
    }

    for(i=0;i<29;i++)
    {
        printf("%c",flag[i]);
    }
 } 

网上找的Python脚本如下

str="0abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
text1='TOiZiZtOrYaToUwPnToBsOaOapsyS'
text2='QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
flag=''
s=0
for i in range(len(text1)):
    if(i%2==0):
        flag+=text1[i]
    else:
        s=text2.index(text1[i])
        flag+=str[s]
print(flag)

得到ThisisthreadofwindowshahaIsES,但这不是flag,真正的flag还要在后面加一个E(我也不知道为什么),得到flag{ThisisthreadofwindowshahaIsESE}

24.[GUET-CTF2019]re

文件下载下来用Exeinfo PE打开

发现是upx加壳,解壳

IDA打开,找到关键代码

去看看加密函数

_BOOL8 __fastcall sub_4009AE(char *a1)
{
  if ( 1629056 * *a1 != 166163712 )
    return 0LL;
  if ( 6771600 * a1[1] != 731332800 )
    return 0LL;
  if ( 3682944 * a1[2] != 357245568 )
    return 0LL;
  if ( 10431000 * a1[3] != 1074393000 )
    return 0LL;
  if ( 3977328 * a1[4] != 489211344 )
    return 0LL;
  if ( 5138336 * a1[5] != 518971936 )
    return 0LL;
  if ( 7532250 * a1[7] != 406741500 )
    return 0LL;
  if ( 5551632 * a1[8] != 294236496 )
    return 0LL;
  if ( 3409728 * a1[9] != 177305856 )
    return 0LL;
  if ( 13013670 * a1[10] != 650683500 )
    return 0LL;
  if ( 6088797 * a1[11] != 298351053 )
    return 0LL;
  if ( 7884663 * a1[12] != 386348487 )
    return 0LL;
  if ( 8944053 * a1[13] != 438258597 )
    return 0LL;
  if ( 5198490 * a1[14] != 249527520 )
    return 0LL;
  if ( 4544518 * a1[15] != 445362764 )
    return 0LL;
  if ( 3645600 * a1[17] != 174988800 )
    return 0LL;
  if ( 10115280 * a1[16] != 981182160 )
    return 0LL;
  if ( 9667504 * a1[18] != 493042704 )
    return 0LL;
  if ( 5364450 * a1[19] != 257493600 )
    return 0LL;
  if ( 13464540 * a1[20] != 767478780 )
    return 0LL;
  if ( 5488432 * a1[21] != 312840624 )
    return 0LL;
  if ( 14479500 * a1[22] != 1404511500 )
    return 0LL;
  if ( 6451830 * a1[23] != 316139670 )
    return 0LL;
  if ( 6252576 * a1[24] != 619005024 )
    return 0LL;
  if ( 7763364 * a1[25] != 372641472 )
    return 0LL;
  if ( 7327320 * a1[26] != 373693320 )
    return 0LL;
  if ( 8741520 * a1[27] != 498266640 )
    return 0LL;
  if ( 8871876 * a1[28] != 452465676 )
    return 0LL;
  if ( 4086720 * a1[29] != 208422720 )
    return 0LL;
  if ( 9374400 * a1[30] == 515592000 )
    return 5759124 * a1[31] == 719890500;
  return 0LL;
}

直接除过去就能得到flag,只不过题有点问题,没有a[6],看了其他wp是1。
脚本如下,比较痛苦。

#include<stdio.h>
int main(void)
{
    long long a1[33];
    int i;

    a1[0] = 166163712/1629056;   
    a1[1] = 731332800/6771600;
    a1[2] = 357245568/3682944; 
    a1[3] = 1074393000/10431000;
    a1[4] = 489211344/3977328;
    a1[5] = 518971936/5138336;
    a1[7] = 406741500/7532250;
    a1[8] = 294236496/5551632;
    a1[9] = 177305856/3409728;
    a1[10] = 650683500/13013670;
    a1[11] = 298351053/6088797;
    a1[12] = 386348487/7884663;
    a1[13] = 438258597/8944053;
    a1[14] = 249527520/5198490;
    a1[15] = 445362764/4544518; 
    a1[17] = 174988800/3645600;
    a1[16] = 981182160/10115280;
    a1[18] = 493042704/9667504;
    a1[19] = 257493600/5364450;
    a1[20] = 767478780/13464540;
    a1[21] = 312840624/5488432;
    a1[22] = 1404511500/14479500;
    a1[23] = 316139670/6451830;
    a1[24] = 619005024/6252576;
    a1[25] = 372641472/7763364;
    a1[26] = 373693320/7327320;
    a1[27] = 498266640/8741520;
    a1[28] = 452465676/8871876;
    a1[29] = 208422720/4086720;
    a1[30] =515592000/9374400;
    a1[31] =719890500/5759124;


    for(i=0;i<33;i++)
    {
        printf("%c",a1[i]);
    }
    return 0;
}

得到flag{e165421110ba03099a1c039337}

25.[FlareOn4]login

文件下载下来是一个html文件,打开看源代码

<!DOCTYPE Html />
<html>
    <head>
        <title>FLARE On 2017</title>
    </head>
    <body>
        <input type="text" name="flag" id="flag" value="Enter the flag" />
        <input type="button" id="prompt" value="Click to check the flag" />
        <script type="text/javascript">
            document.getElementById("prompt").onclick = function () {
                var flag = document.getElementById("flag").value;
                var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});//这个就是加密过程。
                if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
                    alert("Correct flag!");
                } else {
                    alert("Incorrect flag, rot again");
                }
            }
        </script>
    </body>
</html>

上面的加密过程由于没学JS有点看不懂,有点像C语言的简写条件(?:)条件语句的简写。但是看到了’a’,’z’,’A’,’Z’,就用凯撒脚本爆破了一下。

#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(void)
{
    char table1[27]="abcdefghijklmnopqrstuvwxyz";
    char table2[27]="ABCDEFJHIJKLMNOPQRSTUVWXYZ";
    char str1[100]="PyvragFvqrYbtvafNerRnfl@syner-ba.pbz";
    char str2[100]="";
    int i,k,len;

    len=strlen(str1);
    for(k=0;k<26;k++)
    {
        for(i=0;i<len;i++)
        {
            if(str1[i]>=table1[k]&&str1[i]<='z')
            {
                str2[i]=str1[i]-k;
            }
            else if(str1[i]<table1[k]&&str1[i]>='a')
            {
                str2[i]=str1[i]+26-k;
            }
            else if(str1[i]>=table2[k]&&str1[i]<='Z')
            {
                str2[i]=str1[i]-k;
            }
            else if(str1[i]<table2[k]&&str1[i]>='A')
            {
                str2[i]=str1[i]+26-k;
            }            
            else
            {
                str2[i]=str1[i];
            }
        }
        printf("%s",str2);
        printf("\n");
    }
 } 

得到

PyvragFvqrYbtvafNerRnfl@syner-ba.pbz
OxuqzfEupqXasuzeMdqQmek@rxmdq-az.oay
NwtpyeDtopWzrtydLcpPldj@qwlcp-zy.nzx
MvsoxdCsnoVyqsxcKboOkci@pvkbo-yx.myw
LurnwcBrmnUxprwbJanNjbh@oujan-xw.lxv
KtqmvbAqlmTwoqvaIzmMiag@ntizm-wv.kwu
JspluaZpklSvnpuzHylLhzf@mshyl-vu.jvt
IroktzYojkRumotyGxkKgye@lrgxk-ut.ius
HqnjsyXnijQtlnsxFwjJfxd@kqfwj-ts.htr
GpmirxWmhiPskmrwEviIewc@jpevi-sr.gsq
FolhqwVlghOrjlqvDuhHdvb@ioduh-rq.frp
EnkgpvUkfgNqikpuCtgGcua@hnctg-qp.eqo
DmjfouTjefMphjotBsfFbtz@gmbsf-po.dpn
ClientSideLoginsAreEasy@flare-on.com//flag
BkhdmsRhcdKnfhmrZqdDzrx@ekzqd-nm.bnl
AjgclrQgbcJmeglqYpcCyqw@djypc-ml.amk
ZifbkqPfabIldfkpXobBxpv@cixob-lk.zlj
YheajpOezaHkcejoWnaAwou@bhwna-kj.yki
XgdzioNdyzGjbdinVmzZvnt@agvmz-ji.xjh
WfcyhnMcxyFiachmUlyYums@zfuly-ih.wig
VebxgmLbwxEhzbglTkxXtlr@yetkx-hg.vhf
UdawflKavwDgyafkSjwWskq@xdsjw-gf.uge
TczvekJzuvCfxzejRivVrjp@wcriv-fe.tfd
SbyudjIytuBewydiQhuUqio@vbqhu-ed.sec
RaxtciHxstAdvxchPgtTphn@uapgt-dc.rdb
QzwsbhGwrsZcuwbgOfsSogm@tzofs-cb.qca

感觉实际上那个加密就是位移为13的凯撒加密。得到flag{ClientSideLoginsAreEasy@flare-on.com}

26.[ACTF新生赛2020]usualCrypt

文件直接用IDA打开,然后找到关键代码部分

然后看加密函数

换表函数

大写字母转小写字母,小写字母转大写字母函数。

写出脚本

脚本1,大写字母转小写字母,小写字母转大写字母

#include<stdio.h>

int main(void)
{
    char a[37]="zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9";
    int i;

    for(i=0;i<36;i++)
    { 
        if(a[i]>='a'&&a[i]<='z')
        {
            a[i]=a[i]-32;
        }
        else if(a[i]>='A'&&a[i]<='Z')
        {
            a[i]=a[i]+32;
        }
        else
        {
            a[i]=a[i];
        }
        printf("%c",a[i]);
    }
 } 

得到ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9,由于不想去改表了所以弄了个Python的脚本。

import base64
import string

str1 = "ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9"

string1 = "ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))

得到flag{bAse64_h2s_a_Surprise}

27.[GWCTF 2019]xxor

直接IDA打开,找到关键代码,分析过程也在里面了。

一些资料

#define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
#define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
这是baiwindef.h头文件中对宏LOWORD和HIWORD的定义。du
作用分别是取出无符号长整型zhi参数dao的高16位和低16位。
因为一zhuan个长整型占32位,其中高低16位的值可能有不同的意义,需要通过这2个宏分别取出来使用。取出来的结果是一个无符号短整型的值。
其原理正如定义那样,取低16位的宏LOWORD使用按位与操作符与数字0xffff运算,而数字0xffff是一个低16位全为1的数字,那么对其位与操作可以得到参数的低16位。
而取高16位的宏HIWORD则更简单,只需将参数右移16位,剩下的就是原高16位的值了。

脚本如下

#include<stdio.h>

int main(void)
{
    unsigned int v7[6];
    unsigned int a2[4]={2,2,3,4};
    unsigned int i,j,v5=0,v4,v3;//注意,这里必须是unsigned int代表无符号整型。
    int v6[6];

    v7[0]=3746099070;
    v7[1]=550153460;
//    V7[3]+V7[4]=4201428739;
//    V7[3]-V7[4]=-1103824215;
//    V7[2]-V7[3]=2225223423;
    v7[2]=3774025685;
    v7[3]=1548802262;
    v7[4]=2652626477;
    v7[5]=2230518816;

    for(i=0;i<6;i++)
    {
        printf("%#x ",v7[i]);
    }

    for(j=0;j<5;j+=2)
    {
        v3=v7[j];
        v4=v7[j+1];
        v5 = 1166789954*0x40;
        for(i=0;i<=63;i++)
        {
            v4 -=(v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
            v3 -= (v4 + v5 + 11) ^ ((v4 << 6) + a2[0]) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
            v5 -= 1166789954;
        }
        v7[j]=v3;
        v7[j+1]=v4;
    }
    printf("\n");
    for(i=0;i<6;i++)
    {
        printf("%x",v7[i]); 
    }

    printf("\n");
     for (int i = 0; i < 6; ++i) 
        /*将整型数组作为字符输出,注意计算机小端排序*/
        printf("%c%c%c", *((char*)&v7[i] + 2), *((char*)&v7[i] + 1), *(char*)&v7[i]);

 } 

得到


然后脚本提到的小端排序是指


flag{re_is_great!}

28.[HDCTF2019]Maze

文件下载下来用Exeinfo PE打开

解壳过程因为前面题已经有了,所以略过,解壳后的程序用IDA打开,然后发现有一个花指令

去花(怎么去花指令可以看看我那篇关于花指令的文章)

改E8为90

然后p键

f5看伪代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [esp+10h] [ebp-14h]
  char v5[16]; // [esp+14h] [ebp-10h] BYREF

  sub_401140(aGoThroughTheMa);
  scanf("%14s", v5);
  for ( i = 0; i <= 13; ++i )
  {
    switch ( v5[i] )//上下左右键分别为wsad。
    {
      case 'a':
        --*(_DWORD *)asc_408078;
        break;
      case 'd':
        ++*(_DWORD *)asc_408078;
        break;
      case 's':
        --dword_40807C;
        break;
      case 'w':
        ++dword_40807C;
        break;
      default:
        continue;
    }
  }
  if ( *(_DWORD *)asc_408078 == 5 && dword_40807C == -4 )//这里是通过你的移动然后改变了asc_408078(初始值是7),dword_40807C,然后如果这两个值满足这个条件,就是说明到了终点
  {
    sub_401140(aCongratulation);
    sub_401140(aHereIsTheFlagF);
  }
  else
  {
    sub_401140(aTryAgain);
  }
  return 0;
}

shift+12,shift+e,得到地图,

脚本画出地图

#include<stdio.h>
//ssaaasaassdddw
int main(void)
{
    int a[7][10]={42,  42,  42,  42,  42,  42,  42,  43,  42,  42, 
   42,  42,  42,  42,  42,  42,  42,  32,  42,  42, 
   42,  42,  42,  42,  32,  32,  32,  32,  42,  42, 
   42,  42,  32,  32,  32,  42,  42,  42,  42,  42, 
   42,  42,  32,  42,  42,  70,  42,  42,  42,  42, 
   42,  42,  32,  32,  32,  32,  42,  42,  42,  42, 
   42,  42,  42,  42,  42,  42,  42,  42,  42,  42, };
   int i,j;

   for(i=0;i<7;i++)
   {
           for(j=0;j<10;j++)
           {
               printf("%c",a[i][j]);
        }
        printf("\n");
   }
} 

地图7*10的地图

*******+**
******* **
****    **
**   *****
** **F****
**    ****
**********

得到flag{ssaaasaassdddw}

29.[WUSTCTF2020]level1

文件下载下来有两个,用IDA打开level1,找到关键代码。

开始写脚本

#include<stdio.h>

int main(void)
{
    int a[20]={0,198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782,65536000};//注意a[0]设置的值为0;
    int i;

    for(i=1;i<20;i++)
    {
        if(i%2==0)
            a[i]=a[i]/i;
        else
            a[i]=a[i]>>i;
    }
    for(i=1;i<20;i++)
    {
        printf("%c",a[i]);
    }
} 

得到flag{d9-dE6-20c}

30.[WUSTCTF2020]level2

文件下载下来用Exeinfo PE打开,发现是upx加壳

然后解壳

再用IDA打开文件,直接就可以看到flag{Just_upx_-d}

31.[FlareOn4]IgniteMe

这道题需要IDA静态调试和x32dbg动态调试一起。

先放到IDA里面

在继续到x32dbg里面看主函数框架。

然后进入那个比较函数。

现在就来动调找v4的值,在x32dbg里面进入那个比较函数之后。

得到v4=4,现在开始写脚本,如下。

#include<stdio.h>

int main(void)
{
    int v4[40]={ 0x0D, 0x26, 0x49, 0x45, 0x2A, 0x17, 0x78, 0x44, 0x2B, 0x6C, 
  0x5D, 0x5E, 0x45, 0x12, 0x2F, 0x17, 0x2B, 0x44, 0x6F, 0x6E, 
  0x56, 0x09, 0x5F, 0x45, 0x47, 0x73, 0x26, 0x0A, 0x0D, 0x13, 
  0x17, 0x48, 0x42, 0x01, 0x40, 0x4D, 0x0C, 0x02, 0x69};//byte_403000[]的内容。注意我设置v4的长度为40。
   int i;

   v4[39]=4; //v4的值
   for(i=38;i>=0;i--)//得到flag的值
   {
       v4[i]=v4[i]^v4[i+1];
       printf("%c ",v4[i]);
   }
   printf("\n");
   for(i=0;i<39;i++)//正序输出
   {
       printf("%c",v4[i]);
   }
 } 

32.[MRCTF2020]Xor

IDA打开

然后f5,看关键代码。

开始写脚本。

#include<stdio.h>

int main(void)
{
    int a[27]={'M', 'S', 'A', 'W', 'B', '~', 'F', 'X', 'Z', ':', 'J', ':', '`', 't', 'Q', 'J', '"', 'N', '@', ' ', 'b', 'p', 'd', 'd', '}', '8', 'g'};
    int i;

    for(i=0;i<27;i++)
    {
        a[i]=a[i]^i;
        printf("%c",a[i]);
    }
 } 

得到flag{@_R3@1ly_E2_R3verse!}

33.[GKCTF2020]BabyDriver

迷宫题,IDA打开

__int64 __fastcall sub_140001380(__int64 a1, __int64 a2)
{
  __int64 v3; // rdi
  __int64 v4; // rax
  int v5; // ecx
  __int16 *v6; // rsi
  __int64 v7; // rbp
  __int16 v8; // dx
  char v9; // dl
  const CHAR *v10; // rcx

  if ( *(int *)(a2 + 48) >= 0 )
  {
    v3 = *(_QWORD *)(a2 + 24);
    v4 = *(_QWORD *)(a2 + 56) >> 3;
    if ( (_DWORD)v4 )
    {
      v5 = dword_1400030E4;
      v6 = (__int16 *)(v3 + 2);
      v7 = (unsigned int)v4;
      while ( *(_WORD *)(v3 + 4) )
      {
LABEL_28:
        v6 += 6;
        if ( !--v7 )
          goto LABEL_29;
      }
      aO[v5] = 46;
      v8 = *v6;
      if ( *v6 == 23 )
      {
        if ( (v5 & 0xFFFFFFF0) != 0 )
        {
          v5 -= 16;//可以知道宽度为16
          goto LABEL_21;
        }
        v5 += 208;
        dword_1400030E4 = v5;
      }
      if ( v8 == 37 )
      {
        if ( (v5 & 0xFFFFFFF0) != 208 )
        {
          v5 += 16;
          goto LABEL_21;
        }
        v5 -= 208;
        dword_1400030E4 = v5;
      }
      if ( v8 == 36 )
      {
        if ( (v5 & 0xF) != 0 )
        {
          --v5;
          goto LABEL_21;
        }
        v5 += 15;
        dword_1400030E4 = v5;
      }
      if ( v8 != 38 )
        goto LABEL_22;
      if ( (v5 & 0xF) == 15 )
        v5 -= 15;
      else
        ++v5;
LABEL_21:
      dword_1400030E4 = v5;
LABEL_22:
      v9 = aO[v5];
      if ( v9 == 42 )//等于42(*)就不行。
      {
        v10 = "failed!\n";
      }
      else
      {
        if ( v9 != 35 )//35(#),结尾
        {
LABEL_27:
          aO[v5] = 111;//111(o),开头
          goto LABEL_28;
        }
        v10 = "success! flag is flag{md5(input)}\n";//最后得到的移动字符串还要进行md5加密。
      }
      dword_1400030E4 = 16;
      DbgPrint(v10);
      v5 = dword_1400030E4;
      goto LABEL_27;
    }
  }
LABEL_29:
  if ( *(_BYTE *)(a2 + 65) )
    *(_BYTE *)(*(_QWORD *)(a2 + 184) + 3i64) |= 1u;
  return *(unsigned int *)(a2 + 48);
}

大概分析一下就是a0[i]是迷宫是一个,14(行)*16(列)的迷宫,v5是到达的位置(也就是a0[i]中的i),然后35(#)结尾,111(o)开头。上下左右分别是23,37,36,38。

shift+e提取迷宫


开始走迷宫,得到38373737383837383737373838383737373838383838,然后进行md5加密,发现提交时不对,然后去看了wp,发现由于这是sys驱动文件,是由键盘过滤驱动获取键盘扫描码来控制上下左右,所以上下左右不是这几个值,而是IKJL。

键盘扫描码

最后得到flag{403950a6f64f7fc4b655dea696997851}

34.[MRCTF2020]hello_world_go

文件下载下来,直接用ida打开,选到hex view的窗口,Alt+t,搜索flag,就行了。至于为什么这么做,是因为shift+12出来的字符串太多了。也可以直接改文件后缀名为.txt,然后记事本打开搜索flag。

最后得到flag{hello_world_gogogo}

35.[WUSTCTF2020]level3

这是一道该base表的题。IDA打开

如果直接解的话,会是这个结果wa�f2�4�c�d4_is_p�e_stӲt�of_reverq�|,感觉有一点flag的影子,推测是变表的base编码。然后由于眼瞎找了半天才找到这个位置。

开始写脚本

import base64
import string

str1 = "d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD=="

string1 = "TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))

得到flag{Base64_is_the_start_of_reverse}

36.[FlareOn6]Overlong

文件下载下来,还有提示,说是flag被以一种方式藏了起来,直接打开,会弹出一个窗口。

然后拖到IDA里面,代码很少。

然后通过动调来该那个28,将他改成&unk_402008的长度B7-8。

然后改1C为AF。

得到flag

37.[WUSTCTF2020]Cr0ssfun

IDA打开

然后开始写脚本

#include<stdio.h>

int main(void)
{
    puts(" _    _ _   _ _____ _____   _____           ");
      puts("| |  | | | | /  ___|_   _| /  ___|          ");
      puts("| |  | | | | \\ `--.  | |   \\ `--.  ___  ___ ");
      puts("| |/\\| | | | |`--. \\ | |    `--. \\/ _ \\/ __|");
      puts("\\  /\\  / |_| /\\__/ / | |   /\\__/ /  __/ (__ ");
      puts(" \\/  \\/ \\___/\\____/  \\_/   \\____/ \\___|\\___|");

      int a1[42];
      int i;
      printf("\n");
    a1[10] = 112;
    a1[13] = 64;
    a1[3] = 102;
    a1[26] = 114;
    a1[20] = 101;
    a1[7] =48;
       a1[16] =95;
       a1[11] = 112;
    a1[23] =101;
    a1[30] = 117;
    a1[0] = 119;
    a1[6] = 50;
    a1[22] = 115;
    a1[31] = 110;
    a1[12] = 95 ;
    a1[15] = 100;
    a1[8] = 123;
    a1[18] = 51;
    a1[28] = 95;
    a1[21] = 114;
    a1[2] = 116;
    a1[9] = 99;
    a1[32] = 125;
    a1[19] = 118;
    a1[5] = 48;
    a1[14] = 110;
    a1[4] = 50;
    a1[17] = 114;
    a1[29] = 102;
    a1[17] = 114;
    a1[24] = 95;
    a1[1] = 99;
    a1[25] = 64;
    a1[27] = 101;

    for(i=0;i<42;i++)
    {
        printf("%c",a1[i]);
    }
 } 

得到flag{cpp_@nd_r3verse_@re_fun}

38.[ACTF新生赛2020]Oruga

这道题类似于迷宫题,只不过走的方式有些特别。

下面来分析如何走的迷宫。

得到flag{MEWEMEWJMEWJM}

39.[MRCTF2020]Transform

IDA打开,找到关键代码

开始写脚本

#include<stdio.h>

int main(void)
{
    int a[33]={  103, 121, 123, 127, 117,  43,  60,  82,  83, 121, 
   87,  94,  93,  66, 123,  45,  42, 102,  66, 126, 
   76,  87, 121,  65, 107, 126, 101,  60,  92,  69, 
  111,  98,  77};
      int b[33]={9,0x0a,0x0f,0x17,7,0x18,0x0c,6,1,0x10,3,0x11,0x20,0x1d,0x0b,0x1e,0x1b,0x16,4,0x0d,0x13,0x14,0x15,2,0x19,5,0x1f,8,0x12,0x1a,0x1c,0x0e,0};
      int flag[33];
      int i,j;

      for(i=0;i<33;i++)
      {
          flag[i]=a[i]^b[i];
          printf("%c",flag[i]);
    }
    printf("\n");
    for(i=0;i<33;i++)
    {
        for(j=0;j<33;j++)
        {
            if(b[j]==i)
            printf("%c",flag[j]);
        }
    }
 } 

得到flag{Tr4nsp0sltiON_Clph3r_1s_3z}

40.Java逆向解密

.class文件,用jd-gui打开,得到代码

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Scanner;

public class Reverse
{
  public static void main(String[] args)
  {
    Scanner s = new Scanner(System.in);
    System.out.println("Please input the flag ��");
    String str = s.next();
    System.out.println("Your input is ��");
    System.out.println(str);
    char[] stringArr = str.toCharArray();
    Encrypt(stringArr);
  }

  public static void Encrypt(char[] arr)
  {
    ArrayList<Integer> Resultlist = new ArrayList();
    for (int i = 0; i < arr.length; i++)
    {
      int result = arr[i] + '@' ^ 0x20;
      Resultlist.add(Integer.valueOf(result));
    }
    int[] KEY = { 180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65 };
    ArrayList<Integer> KEYList = new ArrayList();
    for (int j = 0; j < KEY.length; j++) {
      KEYList.add(Integer.valueOf(KEY[j]));
    }
    System.out.println("Result:");
    if (Resultlist.equals(KEYList)) {
      System.out.println("Congratulations��");
    } else {
      System.err.println("Error��");
    }
  }
}

中间就只有一个result = arr[i] + ‘@’ ^ 0x20;的处理,开始写脚本

#include<stdio.h>

int main(void)
{
    int a[]={180,136,137,147,191,137,147,191,148,136,133,191,134,140,129,135,191,65};
    int i;

    for(i=0;i<18;i++)
    {
        a[i]=a[i]-'@'^0x20;
        printf("%c",a[i]);
    }
}

得到flag{This_is_the_flag_!}

41.crackMe

IDA打开,找到主函数。

int wmain()
{
  FILE *v0; // eax
  FILE *v1; // eax
  char v3; // [esp+3h] [ebp-405h]
  char v4; // [esp+4h] [ebp-404h] BYREF
  char v5[255]; // [esp+5h] [ebp-403h] BYREF
  char Format; // [esp+104h] [ebp-304h] BYREF
  char v7[255]; // [esp+105h] [ebp-303h] BYREF
  char v8; // [esp+204h] [ebp-204h] BYREF
  char v9[255]; // [esp+205h] [ebp-203h] BYREF
  char v10; // [esp+304h] [ebp-104h] BYREF
  char v11[255]; // [esp+305h] [ebp-103h] BYREF

  printf("Come one! Crack Me~~~\n");
  v10 = 0;
  memset(v11, 0, sizeof(v11));
  v8 = 0;
  memset(v9, 0, sizeof(v9));
  while ( 1 )
  {
    do
    {
      do
      {
        printf("user(6-16 letters or numbers):");
        scanf("%s", &v10);  //这里是输入的用户名
        v0 = (FILE *)sub_4024BE();
        fflush(v0);
      }
      while ( !(unsigned __int8)sub_401000(&v10) );
      printf("password(6-16 letters or numbers):");
      scanf("%s", &v8);  //输入的密码,注意这里输入的是字符。
      v1 = (FILE *)sub_4024BE();
      fflush(v1);
    }
    while ( !(unsigned __int8)sub_401000(&v8) );
    sub_401090(&v10); //对用户名进行了一个操作,这个函数没有什么用。
    Format = 0;
    memset(v7, 0, sizeof(v7));
    v4 = 0;
    memset(v5, 0, sizeof(v5));
    v3 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&Format, &v4);  //这里要去花,最后得到Format=congratulation   v4=Please try again
    if ( sub_401830((int)&v10, &v8) )//这里是关于用户名和密码的一个函数
    {
      if ( v3 )//v3肯定会等于1。
        break;
    }
    printf(&v4);
  }
  printf(&Format);
  return 0;
}

分析sub_401830函数。

// v7----v15----v17,大概加密过程
bool __usercall sub_401830@<al>(int ebx0@<ebx>, int a1, const char *a2)
{
  int v4; // [esp+18h] [ebp-22Ch]
  signed int v5; // [esp+1Ch] [ebp-228h]
  signed int v6; // [esp+28h] [ebp-21Ch]
  unsigned int v7; // [esp+30h] [ebp-214h]
  char v8; // [esp+36h] [ebp-20Eh]
  char v9; // [esp+37h] [ebp-20Dh]
  char v10; // [esp+38h] [ebp-20Ch]
  unsigned __int8 v11; // [esp+39h] [ebp-20Bh]
  unsigned __int8 v12; // [esp+3Ah] [ebp-20Ah]
  char v13; // [esp+3Bh] [ebp-209h]
  int v14; // [esp+3Ch] [ebp-208h]
  char v15; // [esp+40h] [ebp-204h]
  char v16; // [esp+41h] [ebp-203h]
  char v17; // [esp+140h] [ebp-104h]
  char v18; // [esp+141h] [ebp-103h]

  v5 = 0;
  v6 = 0;
  v12 = 0;
  v11 = 0;
  v17 = 0;
  memset(&v18, 0, 0xFFu);
  v15 = 0;
  memset(&v16, 0, 0xFFu);
  v10 = 0;
  v7 = 0;
  v4 = 0;
  while ( v7 < strlen(a2) )
  {
    if ( isdigit(a2[v7]) )                      // 是不是10进制,若参数c为阿拉伯数字0~9,则返回非0值,否则返回0
    {
      v9 = a2[v7] - 48;//将输入的0~9字符,换为十进制的0~9
    }
    else if ( isxdigit(a2[v7]) )                // 是不是16进制0123456789abcdefABCDEF,是就返回非0
    {
      if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
        a2[v7] = 34;
      v9 = (a2[v7] | 0x20) - 87;//将输入的abcdefABCDEF,转换为十进制的10~15。
    }
    else
    {
      v9 = ((a2[v7] | 0x20) - 97) % 6 + 10;
    }
    v10 = v9 + 16 * v10;
    if ( !((signed int)(v7 + 1) % 2) )          // 如果a[v7]中的v7是奇数就进入,这个的作用差不多就是,将输入的2个值,然后变成0~15的两个值,再通过v10 = v9 + 16 * v10;,就变成了一个值,给了*v15。例如1,a结果变换后就会变成一个值'1a'=26
    {
      *(&v15 + v4++) = v10;
      ebx0 = v4;
      v10 = 0;
    }
    ++v7;
  }
  while ( v6 < 8 )//实际上从这里可以推测出密码长度为16。
  {
    v11 += byte_416050[++v12];
    v13 = byte_416050[v12];
    v8 = byte_416050[v11];
    byte_416050[v11] = v13;
    byte_416050[v12] = v8;
    if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 )// 反调试,调试时if判断改为jmp。
      v13 = v11 + v12;
    *(&v17 + v6) = byte_416050[(unsigned __int8)(v8 + v13)] ^ *(&v15 + v5);//通过这个来得到v15,但是byte_416050[(unsigned __int8)(v8 + v13)]需要动调来获得。
    if ( *(_DWORD *)(__readfsdword(0x30u) + 2) & 0xFF )// 反调试,调试时if判断改为jmp。
    {
      v11 = -83;
      v12 = 43;
    }
    sub_401710((int)&v17, (const char *)a1, v6++);
    v5 = v6;
    if ( v6 >= (unsigned int)(&v15 + strlen(&v15) + 1 - &v16) )
      v5 = 0;
  }
  v14 = 0;
  sub_401470(ebx0, &v17, &v14);     // 要让v14!=0,所以if里面的条件都要满足,只不过要注意里面有一个a1[5]是反调试, 得到v17=dbappsec
  return v14 == 0xAB94;
}

现在已经知道了v17,现在通过动调来获得byte_416050[(unsigned __int8)(v8 + v13)],从而得到,&15。

图片中有个错误,应该是记录ecx的值

开始写脚本

#include<stdio.h>

int main(void)
{
    int a[8]={0x2a,0xd7,0x92,0xe9,0x53,0xe2,0xc4,0xcd};//动调得到的byte_416050[(unsigned __int8)(v8 + v13)],也就是ecx。
    int b[8]={'d', 'b', 'a', 'p', 'p', 's', 'e', 'c'};
    int c[8];
    int d[16];
    int i,j,m,n;

    for(i=0;i<8;i++)
    {
        c[i]=b[i]^a[i];
        printf("%d ",c[i]);
    }
    for(i=0,j=0;i<8;i++,j+=2)
    {
        m=c[i]/16;
        n=c[i]%16;
        d[j]=m;
        d[j+1]=n;
    }
    for(j=0;j<16;j++)
    {
        if(d[j]<10)
        printf("%d",d[j]);
        else
        printf("%c",d[j]+87);
    }
} 

得到4eb5f3992391a1ae,再进行MD5加密得到flag{d2be2981b84f2a905669995873d6a36c}

42.刮开有奖

INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
  const char *v4; // esi
  const char *v5; // edi
  int v7[2]; // [esp+8h] [ebp-20030h] BYREF
  int v8; // [esp+10h] [ebp-20028h]
  int v9; // [esp+14h] [ebp-20024h]
  int v10; // [esp+18h] [ebp-20020h]
  int v11; // [esp+1Ch] [ebp-2001Ch]
  int v12; // [esp+20h] [ebp-20018h]
  int v13; // [esp+24h] [ebp-20014h]
  int v14; // [esp+28h] [ebp-20010h]
  int v15; // [esp+2Ch] [ebp-2000Ch]
  int v16; // [esp+30h] [ebp-20008h]
  CHAR String[65536]; // [esp+34h] [ebp-20004h] BYREF
  char v18[65536]; // [esp+10034h] [ebp-10004h] BYREF

  if ( a2 == 272 )
    return 1;
  if ( a2 != 273 )
    return 0;
  if ( (_WORD)a3 == 1001 )
  {
    memset(String, 0, 0xFFFFu);
    GetDlgItemTextA(hDlg, 1000, String, 0xFFFF);//这里的string是flag。
    if ( strlen(String) == 8 )
    {
      v7[0] = 90;
      v7[1] = 74;
      v8 = 83;
      v9 = 69;
      v10 = 67;
      v11 = 97;
      v12 = 78;
      v13 = 72;
      v14 = 51;
      v15 = 110;
      v16 = 103;//上面10个数都是v7[]
      sub_4010F0(v7, 0, 10);//对v7[]进行了处理。
      memset(v18, 0, 0xFFFFu);
      v18[0] = String[5];
      v18[2] = String[7];
      v18[1] = String[6];
      v4 = (const char *)sub_401000(v18, strlen(v18));//sub_401000是长得比较丑的base64编码。对String[5],String[6],String[7]3个字母编码,得到了v4。
      memset(v18, 0, 0xFFFFu);
      v18[1] = String[3];
      v18[0] = String[2];
      v18[2] = String[4];
      v5 = (const char *)sub_401000(v18, strlen(v18));对String[2],String[3],String[4]3个字母编码,得到了v5。
      if ( String[0] == v7[0] + 34//得到sting[0]
        && String[1] == v10//得到sting[1]
        && 4 * String[2] - 141 == 3 * v8
        && String[3] / 4 == 2 * (v13 / 9)
        && !strcmp(v4, "ak1w")//通过这个来得到String[5],String[6],String[7]。'jMp'
        && !strcmp(v5, "V1Ax") )//通过这个来得到,String[2],String[3],String[4]。'WP1'
      {
        MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
      }
    }
    return 0;
  }
  if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
    return 0;
  EndDialog(hDlg, (unsigned __int16)a3);
  return 1;
}

现在看sub_4010F0对v7[]进行了什么操作

int __cdecl sub_4010F0(int a1, int a2, int a3)
{
  int result; // eax
  int i; // esi
  int v5; // ecx
  int v6; // edx

  result = a3;
  for ( i = a2; i <= a3; a2 = i )
  {
    v5 = 4 * i;
    v6 = *(_DWORD *)(4 * i + a1);
    if ( a2 < result && i < result )
    {
      do
      {
        if ( v6 > *(_DWORD *)(a1 + 4 * result) )
        {
          if ( i >= result )
            break;
          ++i;
          *(_DWORD *)(v5 + a1) = *(_DWORD *)(a1 + 4 * result);
          if ( i >= result )
            break;
          while ( *(_DWORD *)(a1 + 4 * i) <= v6 )
          {
            if ( ++i >= result )
              goto LABEL_13;
          }
          if ( i >= result )
            break;
          v5 = 4 * i;
          *(_DWORD *)(a1 + 4 * result) = *(_DWORD *)(4 * i + a1);
        }
        --result;
      }
      while ( i < result );
    }
LABEL_13:
    *(_DWORD *)(a1 + 4 * result) = v6;
    sub_4010F0(a1, a2, i - 1);//这里还有个递归
    result = a3;
    ++i;
  }
  return result;
}

由于所有值都知道,所以可以直接复制下来,得到改变后的v7[]。需要注意的是,里面出现的(_DWORD *)都删了,特别是4 *也要删除,原因是这些变量都是int型(占了4个字节),而这部分在IDA里面看汇编代码

开始写脚本得到v7[]

#include<stdio.h>
int fun(int *a1,int a2,int a3)
{
    int result; // eax
  int i; // esi
  int v5; // ecx
  int v6; // edx

  result = a3;
  for ( i = a2; i <= a3; a2 = i )
  {
    v5 =i;
    v6 = *( i + a1);
    if ( a2 < result && i < result )
    {
      do
      {
        if ( v6 > *(a1 + result) ) 
        {
          if ( i >= result )
            break;
          ++i;
          *(v5 + a1) = *(a1 +result);
          if ( i >= result )
            break;
          while ( *(a1 + i) <= v6 )
          {
            if ( ++i >= result )
              goto LABEL_13;
          }
          if ( i >= result )
            break;
          v5 = i;
          *(a1 + result) = *(i + a1);
        }
        --result;
      }
      while ( i < result );
    }
LABEL_13:
    *(a1 +result) = v6;
    fun(a1, a2, i - 1);
    result = a3;
    ++i;
  }
  return result;

}
int main(void)
{
    int v7[11]={90,74,83,69,67,97,78,72,51,110,103};
    int i;
    fun(v7,0,10);
    for(i=0;i<11;i++)
    {
        printf("%d ",v7[i]); 
    }
 } 

得到v7=51 67 69 72 74 78 83 90 97 103 110;从而得到string[0]=’U’,string[1]=’J’

得到flag{UJjMpWP1}

43.[ACTF新生赛2020]rome

脚本

code=list('Qsw3sj_lz4_Ujw@l')
flag=''
i=0
while(i<=15):
    for k in range(0,127):
        z=k
        if(64 < k <= 90):
            k = (k - 51) % 26 + 65
        if(96<k<=122):
            k = (k - 79) % 26 + 97
        if(chr(k)==code[i]):
            flag += chr(z)
    i=i+1
print(flag)

Crypto方向

1.[BJDCTF2020]这是base??

下载下来得到一个txt文件,发现是一个换表的base64编码的密文。写出脚本如下

#include<stdio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h> 

int main(void)
{
    char str1[100]="FlZNfnF6Qol6e9w17WwQQoGYBQCgIkGTa9w3IQKw";
    char str2[100];
    int table[]={0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,0,0,0,0,0,
             0,0,0,0,0,0,0,62,0,0,0,
             63,42,43,44,45,46,47,48,
             19,20,21,0,0,0,0,0,0,0,11,
             12,13,14,15,16,17,18,24,0,1,2,3,
             4,5,22,23,49,50,51,8,9,
             38,39,40,41,0,0,0,0,0,0,25,
             26,27,28,29,30,31,32,33,34,
             35,36,37,52,53,54,55,56,57,
             58,59,60,61,6,7,10
           }; 

    int len1;
    int len2;
    int i,j; 

    len1=strlen(str1);
    if(strstr(str1,"=="))
    {
        len2=(len1/4)*3-2;
    }   
    else if(strstr(str1,"="))
    {
        len2=(len1/4)*3-1;
    }
    else
    {
        len2=(len1/4)*3;
    }

    for(i=0,j=0;i<len1-2;i+=4,j+=3)
    {
        str2[j]=((table[str1[i]])<<2)|((table[str1[i+1]])>>4);
        str2[j+1]=(table[str1[i+1]]<<4)|((table[str1[i+2]])>>2);
        str2[j+2]=((table[str1[i+2]])<<6)|(table[str1[i+3]]);
    }


//  printf("%d",len2); 
    for(i=0;i<len2;i++)
    {
        printf("%c",str2[i]);
    }
    return 0;
 } 

得到flag{D0_Y0u_kNoW_Th1s_b4se_map}

网上找的Python脚本如下

import base64
dict={0: 'J', 1: 'K', 2: 'L', 3: 'M', 4: 'N', 5: 'O', 6: 'x', 7: 'y', 8: 'U', 9: 'V', 10: 'z', 11: 'A', 12: 'B', 13: 'C', 14: 'D', 15: 'E', 16: 'F', 17: 'G', 18: 'H', 19: '7', 20: '8', 21: '9', 22: 'P', 23: 'Q', 24: 'I', 25: 'a', 26: 'b', 27: 'c', 28: 'd', 29: 'e', 30: 'f', 31: 'g', 32: 'h',33: 'i', 34: 'j', 35: 'k', 36: 'l', 37: 'm', 38: 'W', 39: 'X', 40: 'Y', 41: 'Z', 42: '0', 43: '1', 44: '2', 45: '3', 46: '4', 47: '5', 48: '6', 49: 'R', 50: 'S', 51: 'T', 52: 'n', 53: 'o', 54: 'p', 55: 'q', 56: 'r', 57: 's', 58: 't', 59: 'u', 60: 'v', 61: 'w', 62: '+', 63: '/', 64: '='}
base64_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P','Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f','g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v','w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/']
cipher='FlZNfnF6Qol6e9w17WwQQoGYBQCgIkGTa9w3IQKw'
res=''
for i in range(len(cipher)):
    for j in range(64):
        if(dict[j]==cipher[i]):
            res+=base64_list[j]
flag=base64.b64decode(res)
print(flag)