BUUCTF第一页wp
本文最后更新于 5 天前,其中的信息可能已经有所发展或是发生改变。

easyre

下载附件,无壳的64位程序

使用IDA打开,进入main函数

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int b; // [rsp+28h] [rbp-8h] BYREF
  int a; // [rsp+2Ch] [rbp-4h] BYREF

  _main();
  scanf("%d%d", &a, &b);
  if ( a == b )
    printf("flag{this_Is_a_EaSyRe}");
  else
    printf("sorry,you can't get flag");
  return 0;
}

直接就有flag

reverse1

64位无壳,IDA打开

进入main函数

int __fastcall main_0(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  __int64 i; // rcx
  size_t v5; // rax
  char v7; // [rsp+0h] [rbp-20h] BYREF
  int j; // [rsp+24h] [rbp+4h]
  char Str1[224]; // [rsp+48h] [rbp+28h] BYREF
  __int64 v10; // [rsp+128h] [rbp+108h]

  v3 = &v7;
  for ( i = 82i64; i; --i )
  {
    *(_DWORD *)v3 = -858993460;
    v3 += 4;
  }
  for ( j = 0; ; ++j )
  {
    v10 = j;
    if ( j > j_strlen(Str2) )
      break;
    if ( Str2[j] == 'o' )
      Str2[j] = '0';
  }
  sub_1400111D1("input the flag:");
  sub_14001128F("%20s", Str1);
  v5 = j_strlen(Str2);
  if ( !strncmp(Str1, Str2, v5) )
    sub_1400111D1("this is the right flag!\n");
  else
    sub_1400111D1("wrong flag\n");
  return 0;
}

查看Str2

Str2            db '{hello_world}',0

将Str2中的o替换成0:flag{hell0_w0rld}

reverse2

64位的ELF文件

用IDA打开,进入main函数

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int stat_loc; // [rsp+4h] [rbp-3Ch] BYREF
  int i; // [rsp+8h] [rbp-38h]
  __pid_t pid; // [rsp+Ch] [rbp-34h]
  char s2[24]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v8; // [rsp+28h] [rbp-18h]

  v8 = __readfsqword(0x28u);
  pid = fork();
  if ( pid )
  {
    waitpid(pid, &stat_loc, 0);
  }
  else
  {
    for ( i = 0; i <= strlen(&flag); ++i )
    {
      if ( *(&flag + i) == 'i' || *(&flag + i) == 'r' )
        *(&flag + i) = '1';
    }
  }
  printf("input the flag:");
  __isoc99_scanf("%20s", s2);
  if ( !strcmp(&flag, s2) )
    return puts("this is the right flag!");
  else
    return puts("wrong flag!");
}

查看if比较处的&flag:

.data:0000000000601080 flag            db '{'                  ; DATA XREF: main+34↑r
.data:0000000000601080                                         ; main+44↑r ...
.data:0000000000601081 aHackingForFun  db 'hacking_for_fun}',0

将flag中的‘i’和‘r’替换成1:flag{hack1ng_fo1_fun}

内涵的软件

下载附件,32位无壳exe,进入IDA后查找字符串+交叉引用直接就能看见flag

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char v4[4]; // [esp+4Ch] [ebp-Ch] BYREF
  const char *v5; // [esp+50h] [ebp-8h]
  int v6; // [esp+54h] [ebp-4h]

  v6 = 5;
  v5 = "DBAPP{49d3c93df25caad81232130f3d2ebfad}";
  while ( v6 >= 0 )
  {
    printf(&byte_4250EC, v6);
    sub_40100A();
    --v6;
  }
  printf(asc_425088);
  v4[0] = 1;
  scanf("%c", v4);
  if ( v4[0] == 'Y' )
  {
    printf(aOd);
    return sub_40100A();
  }
  else
  {
    if ( v4[0] == 'N' )
      printf(&byte_425034);
    else
      printf(&byte_42501C);
    return sub_40100A();
  }
}

新年快乐

下载附件,发现是一个32位带有UPX壳的程序,使用工具脱壳后进入IDA

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

  __main();
  strcpy(Str2, "HappyNewYear!");
  memset(Str1, 0, 32);
  printf("please input the true flag:");
  scanf("%s", Str1);
  if ( !strncmp(Str1, Str2, strlen(Str2)) )
    return puts("this is true flag!");
  else
    return puts("wrong!");
}

Str2就是我们的flag:flag{HappyNewYear!}

xor

64位无壳,从名字就可以看出这题涉及到异或运算,用IDA打开

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+2Ch] [rbp-124h]
  char __b[264]; // [rsp+40h] [rbp-110h] BYREF

  memset(__b, 0, 0x100uLL);
  printf("Input your flag:\n");
  get_line(__b, 256LL);
  if ( strlen(__b) != 33 )
    goto LABEL_7;
  for ( i = 1; i < 33; ++i )
    __b[i] ^= __b[i - 1];
  if ( !strncmp(__b, global, 0x21uLL) )
    printf("Success");
  else
LABEL_7:
    printf("Failed");
  return 0;

不难看出我们的输入从第二位开始每一位都与前一位做异或运算,然后与global作比较

查看global的值:

__cstring:0000000100000F6E aFKWOXZUPFVMDGH db 'f',0Ah              ; DATA XREF: __data:_global↓o
__cstring:0000000100000F70                 db 'k',0Ch,'w&O.@',11h,'x',0Dh,'Z;U',11h,'p',19h,'F',1Fh,'v"M#D',0Eh,'g'
__cstring:0000000100000F89                 db 6,'h',0Fh,'G2O',0

写出解密脚本:

num=[0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11,  
  0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F,  
  0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F,  
  0x47, 0x32, 0x4F,]  
flag="f"  
for i in range(1,33):  
    flag+=chr(num[i]^num[i-1])  
print(flag)

flag{QianQiuWanDai_YiTongJiangHu}

reverse3

32位无壳,IDA打开

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t v3; // eax
  const char *v4; // eax
  size_t v5; // eax
  char v7; // [esp+0h] [ebp-188h]
  char v8; // [esp+0h] [ebp-188h]
  signed int j; // [esp+DCh] [ebp-ACh]
  int i; // [esp+E8h] [ebp-A0h]
  signed int v11; // [esp+E8h] [ebp-A0h]
  char Destination[108]; // [esp+F4h] [ebp-94h] BYREF
  char Str[28]; // [esp+160h] [ebp-28h] BYREF
  char v14[8]; // [esp+17Ch] [ebp-Ch] BYREF

  for ( i = 0; i < 100; ++i )
  {
    if ( (unsigned int)i >= 0x64 )
      j____report_rangecheckfailure();
    Destination[i] = 0;                         // 初始化
  }
  sub_41132F("please enter the flag:", v7);
  sub_411375("%20s", (char)Str);
  v3 = j_strlen(Str);
  v4 = (const char *)sub_4110BE(Str, v3, v14);  // 这里是个Base64加密
  strncpy(Destination, v4, 0x28u);
  v11 = j_strlen(Destination);
  for ( j = 0; j < v11; ++j )
    Destination[j] += j;
  v5 = j_strlen(Destination);
  if ( !strncmp(Destination, Str2, v5) )        // 比较结果
    sub_41132F("rigth flag!\n", v8);
  else
    sub_41132F("wrong flag!\n", v8);
  return 0;
}

发现sub_4110BE函数是个Base64加密,同时查找字符串

很明显能看见base64input和base64码表,于是查看Str2的值

.data:0041A034 Str2            db 'e3nifIH9b_C@n@dH',0 ; DATA XREF: _main_0+142↑o

写出求解脚本:

import base64  

# 要解码的 Base64 编码文本  
encoded_text = ""  

Str="e3nifIH9b_C@n@dH"  
for i in range(16):  
    encoded_text+=chr(ord(Str[i])-i)  

# 使用 base64 模块进行解码  
decoded_bytes = base64.b64decode(encoded_text)  

# 打印解码结果  
print(decoded_bytes)

#b'{i_l0ve_you}'

flag{i_l0ve_you}

helloword

下载附件,是个.apk文件,也就是我们常说的安卓,使用JEB打开

进到主函数里面,还可以按tab键查看代码:

package com.example.helloword;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends ActionBarActivity {
    public MainActivity() {
        super();
    }

    protected void onCreate(Bundle arg5) {
        super.onCreate(arg5);
        this.setContentView(0x7F030018);
        "flag{7631a988259a00816deda84afb29430a}".compareTo("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    }

    public boolean onCreateOptionsMenu(Menu arg3) {
        this.getMenuInflater().inflate(0x7F0C0000, arg3);
        return 1;
    }

    public boolean onOptionsItemSelected(MenuItem arg3) {
        boolean v1 = arg3.getItemId() == 0x7F05003C ? true : super.onOptionsItemSelected(arg3);
        return v1;
    }
}

flag直接就出来了

不一样的flag

题目已经给了提示,下载附件看看

无壳,32位,用IDA打开

main函数如下

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

  __main();
  v3[26] = 0;                                   // 初始化
  *(_WORD *)&v3[27] = 0;
  v4 = 0;
  strcpy(v3, "*11110100001010000101111#");      // 地图
  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", &v5);
    if ( v5 == 2 )
    {
      ++*(_DWORD *)&v3[25];                     // [5][5]的二维数组,用于构建地图
    }
    else if ( v5 > 2 )
    {
      if ( v5 == 3 )
      {
        --v4;
      }
      else
      {
        if ( v5 != 4 )
LABEL_13:
          exit(1);
        ++v4;
      }
    }
    else
    {
      if ( v5 != 1 )
        goto LABEL_13;
      --*(_DWORD *)&v3[25];
    }
    for ( i = 0; i <= 1; ++i )
    {
      if ( *(_DWORD *)&v3[4 * i + 25] >= 5u )
        exit(1);
    }
    if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == '1' )// 碰到1时就结束
      exit(1);
    if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == '#' )// 结束于#
    {
      puts("\nok, the order you enter is the flag!");
      exit(0);
    }
  }
}

不难看出,这是一个迷宫题,将 *11110100001010000101111# 变成一个5*5的迷宫,如下:

*1111
01000
01010
00010
1111#

碰到1就停止(即走0的路径),碰到#就得出flag(即#为出口处)

得到下下下右右上上右右下下下,即222441144222

所以flag就是flag{222441144222}

SimpleRev

64位ELF,用IDA打开

主函数:

int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char v4; // [rsp+Fh] [rbp-1h]

  while ( 1 )
  {
    while ( 1 )
    {
      printf("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ");
      v4 = getchar();
      if ( v4 != 'd' && v4 != 'D' )
        break;
      Decry("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ", argv);
    }
    if ( v4 == 'q' || v4 == 'Q' )
      Exit("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ", argv);
    puts("Input fault format!");
    v3 = getchar();
    putchar(v3);
  }
}

主函数没什么好看的,主要看主函数中引用的Decry函数

进入Decry函数:

我们可以看见src和v9,这里需要注意x86的大小端序问题,转为字符串之后应该反转过来

下面是整理过后且加过注释的Decry函数:

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] BYREF
  __int64 v7; // [rsp+28h] [rbp-38h]
  int v8; // [rsp+30h] [rbp-30h]
  __int64 v9[2]; // [rsp+40h] [rbp-20h] BYREF
  int v10; // [rsp+50h] [rbp-10h]
  unsigned __int64 v11; // [rsp+58h] [rbp-8h]

  v11 = __readfsqword(0x28u);
  *(_QWORD *)src = 'SLCDN';                     // NDCLS
  v7 = 0LL;
  v8 = 0;
  v9[0] = 'wodah';                              // hadow
  v9[1] = 0LL;
  v10 = 0;
  text = (char *)join(key3, v9);                // key3="kills" text=key3+v9
  strcpy(key, key1);                            // key1="ADSFK"
  strcat(key, src);                             // key=key1+src
  v2 = 0;
  v3 = 0;
  getchar();
  v5 = strlen(key);
  for ( i = 0; i < v5; ++i )
  {
    if ( key[v3 % v5] > '@' && key[v3 % v5] <= 'Z' )// 如果key[]为大写字母
      key[i] = key[v3 % v5] + 32;               // 将key中的大写字母变为小写字母
    ++v3;
  }
  printf("Please input your flag:");
  while ( 1 )
  {
    v1 = getchar();
    if ( v1 == 10 )
      break;
    if ( v1 == 32 )
    {
      ++v2;
    }
    else
    {
      if ( v1 <= '`' || v1 > 'z' )
      {
        if ( v1 > '@' && v1 <= 'Z' )
        {
          str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
          ++v3;
        }
      }
      else
      {
        str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;// 主要加密过程
        ++v3;
      }
      if ( !(v3 % v5) )
        putchar(32);
      ++v2;
    }
  }
  if ( !strcmp(text, str2) )                    // 比较text于str2,其中text我们已经知道,str2可以由key得出
    puts("Congratulation!\n");
  else
    puts("Try again!\n");
  return __readfsqword(0x28u) ^ v11;
}

写出爆破脚本:

#include<stdio.h> 

int main()
{
    char key[] = "adsfkndcls";
    char text[] = "killshadow";
    int i;
    int v3=10;//长度 
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 128; j++)
        {
            if (j < 'A' || j > 'z' || j > 'Z' && j < 'a')   //限制在字母内爆破 
            {
                continue;
            }
            if ((j - 39 - key[v3 % 10] + 97) % 26 + 97 == text[i])  //满足判断条件则输出 
            {
                printf("%c",j);
                v3++;
                break;
            }
        }
    }
}

flag{KLDQCUDFZO}

[GXYCTF2019]luck_guy

ELF文件,IDA打开进入main函数:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+14h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  welcome();
  puts("_________________");
  puts("try to patch me and find flag");
  v4 = 0;
  puts("please input a lucky number");
  __isoc99_scanf("%d", &v4);
  patch_me(v4);
  puts("OK,see you again");
  return 0;
}

进入patch_me函数,再进入get_flag函数:

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

  v6 = __readfsqword(0x28u);
  v0 = time(0LL);
  srand(v0);
  for ( i = 0; i <= 4; ++i )
  {
    switch ( rand() % 200 )
    {
      case 1:
        puts("OK, it's flag:");
        memset(&s, 0, 0x28uLL);
        strcat((char *)&s, f1);                 // f1="GXY{do_not_"
        strcat((char *)&s, &f2);
        printf("%s", (const char *)&s);
        break;
      case 2:
        printf("Solar not like you");
        break;
      case 3:
        printf("Solar want a girlfriend");
        break;
      case 4:
        s = 0x7F666F6067756369LL;               //注意大小端
        v5 = 0;
        strcat(&f2, (const char *)&s);
        break;
      case 5:
        for ( j = 0; j <= 7; ++j )
        {
          if ( j % 2 == 1 )
            *(&f2 + j) -= 2;
          else
            --*(&f2 + j);
        }
        break;
      default:
        puts("emmm,you can't find flag 23333");
        break;
    }
  }
  return __readfsqword(0x28u) ^ v6;
}

已知f1,再通过s求出f2的值拼接就可以得到flag了

exp

s=[0x69,0x63,0x75,0x67,0x60,0x6F,0x66,0x7F]  
flag=""  
for i in range(8):  
    if i%2==1:  
        s[i]=s[i]-2  
    else:  
        s[i]=s[i]-1  
    flag+=chr(s[i])  
print(flag)

输出结果为:hate_me}

所以flag=GXY{do_not_hate_me}

Java逆向解密

下载附件,发现是一个.class文件,最开始我用JEB打开发现看不了,然后看看wp上都用的jadx,然后我想起来之前下过,就试了试用jadx打开:

package defpackage;

import java.util.ArrayList;

import java.util.Scanner;

/* renamed from: Reverse  reason: default package */

/* loaded from: Reverse.class */

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 (char c : arr) {

            int result = (c + '@') ^ 32;

            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 i : KEY) {

            KEYList.add(Integer.valueOf(i));

        }

        System.out.println("Result:");

        if (Resultlist.equals(KEYList)) {

            System.out.println("Congratulations!");

        } else {

            System.err.println("Error!");

        }

    }

}

发现是KEY加上@的ASCII码再与32做异或运算得到flag,开始写脚本:

flag=""  
key=[180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65]  
for i in range(18):  
    flag+=chr((key[i]-ord('@'))^32)  
print(flag)

This_is_theflag!

最后换了个JEB,可以打开了

[BJDCTF2020]JustRE

法一:

下载附件之后点开这个程序,发现它居然只需要点击就可以得到flag

32位的程序,用IDA打开,查找字符串之后可以交叉引用到关键函数:

INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
  CHAR String[100]; // [esp+0h] [ebp-64h] BYREF

  if ( a2 != 272 )
  {
    if ( a2 != 273 )
      return 0;
    if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
    {
      sprintf(String, Format, ++dword_4099F0);
      if ( dword_4099F0 == 19999 )
      {
        sprintf(String, " BJD{%d%d2069a45792d233ac}", 19999, 0);
        SetWindowTextA(hWnd, String);
        return 0;
      }
      SetWindowTextA(hWnd, String);
      return 0;
    }
    EndDialog(hWnd, (unsigned __int16)a3);
  }
  return 1;
}

很明显可以看见BJD开头的flag,再结合后面的19999和0就可以得到flag了

法二:

由于这题太过简单,做完之后我继续再思考有没有其他的解法,最后还是找到了

基于法一中给出的函数,我们可以分析得出当我们点击19999次时就可以得到flag了,于是在那句if判断是否点击19999次的地方下一个断点

启动动态调试:

一路F8运行,直到出现跳转处为止

这里可以看见有个jnz,我们需要使其往左边走输出flag,于是想到可以更改ZF寄存器的值,使判断可以通过

这时F8就可以看见程序往下走了:

然后继续F8

flag就出来了!

刮开有奖

这题涉及到WindowsAPI,我将其作为BUUCTF第一页Windows逆向的最后一题来给我学习的第一个阶段划上一个句号

用IDA打开,能看见很多API,而且没找到主函数,查找字符串看看

发现有一个base64表,猜测这题与base64加密有关,还看见了下面的U g3t 1T!,这代表着其所在位置很可能是我们这题的主逻辑所在地,交叉引用到调用它的函数:

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);
    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;
      sub_4010F0(v7, 0, 10);
      memset(v18, 0, 0xFFFFu);
      v18[0] = String[5];
      v18[2] = String[7];
      v18[1] = String[6];
      v4 = (const char *)sub_401000(v18, strlen(v18));
      memset(v18, 0, 0xFFFFu);
      v18[1] = String[3];
      v18[0] = String[2];
      v18[2] = String[4];
      v5 = (const char *)sub_401000(v18, strlen(v18));
      if ( String[0] == v7[0] + 34
        && String[1] == v10
        && 4 * String[2] - 141 == 3 * v8
        && String[3] / 4 == 2 * (v13 / 9)
        && !strcmp(v4, "ak1w")
        && !strcmp(v5, "V1Ax") )
      {
        MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
      }
    }
    return 0;
  }
  if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
    return 0;
  EndDialog(hDlg, (unsigned __int16)a3);
  return 1;
}

上面的API函数和判断字符串不管,查看这段函数调用的第一个函数 sub_4010F0():

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 = *(4 * i + a1);
    if ( a2 < result && i < result )
    {
      do
      {
        if ( v6 > *(a1 + 4 * result) )
        {
          if ( i >= result )
            break;
          ++i;
          *(v5 + a1) = *(a1 + 4 * result);
          if ( i >= result )
            break;
          while ( *(a1 + 4 * i) <= v6 )
          {
            if ( ++i >= result )
              goto LABEL_13;
          }
          if ( i >= result )
            break;
          v5 = 4 * i;
          *(a1 + 4 * result) = *(4 * i + a1);
        }
        --result;
      }
      while ( i < result );
    }
LABEL_13:
    *(a1 + 4 * result) = v6;
    sub_4010F0(a1, a2, i - 1);
    result = a3;
    ++i;
  }
  return result;
}

这段函数大致意思是将v7数组从小到大排列,即ASCII码排序,我们可以使用脚本将其还原:

#include <iostream>

using namespace std;

void restore(int* num, int size) {
    for (int j = 0; j < size - 1; j++) {
        for (int i = 0; i < size - 1 - j; i++) {
            if (num[i] > num[i + 1]) {
                int temp = num[i];
                num[i] = num[i + 1];
                num[i + 1] = temp;
            }
        }
    }
}

int main() {
    int v7[11] = { 90, 74, 83, 69, 67, 97, 78, 72, 51, 110, 103 };
    restore(v7, 11);
    for (int i = 0; i < 11; i++) {
        cout << char(v7[i]);
    }
    return 0;
}

输出3CEHJNSZagn

再往下看看见一些赋值以及调用了两次sub_401000()函数对v4和v5进行赋值,而sub_401000()函数是一个base64编码函数,因此将下方的v4和v5解密:

v4="jMp",v5="WP1"

但是由于前面的判断字符串长度,flag显然是8位的,而我们解码出来只有6位,因此还要寻找两个字符并且对它们进行排序,这时候主函数中的最后那段if显然就是我们寻找flag的关键,String数组为flag

String[0] == v7[0] + 34 flag第一位是上面输出中第一位的ASCII码加上34的字符即51+34="U"
String[1] == v10 flag第二位v10是上面输出的第5位即为"J"
4 * String[2] - 141 == 3 * v8 flag第三位的ASCII码乘4减去141与上面输出的第三位v8乘3相等,则flag第三位为"W"

基于上述字符可以推断,String中的前三个元素为”UJW“,则WP1在jMp前面

因此可以得到,String="UJWP1jMp"

[ACTF新生赛2020]easyre

未魔改的UPX壳,就不多介绍了

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE v4[12]; // [esp+12h] [ebp-2Eh] BYREF
  _DWORD v5[3]; // [esp+1Eh] [ebp-22h]
  _BYTE v6[5]; // [esp+2Ah] [ebp-16h] BYREF
  int v7; // [esp+2Fh] [ebp-11h]
  int v8; // [esp+33h] [ebp-Dh]
  int v9; // [esp+37h] [ebp-9h]
  char v10; // [esp+3Bh] [ebp-5h]
  int i; // [esp+3Ch] [ebp-4h]

  __main();
  qmemcpy(v4, "*F'\"N,\"(I?+@", sizeof(v4));
  printf("Please input:");
  scanf("%s", v6);
  if ( v6[0] != 'A' || v6[1] != 'C' || v6[2] != 'T' || v6[3] != 'F' || v6[4] != '{' || v10 != '}' )
    return 0;
  v5[0] = v7;
  v5[1] = v8;
  v5[2] = v9;
  for ( i = 0; i <= 11; ++i )
  {
    if ( v4[i] != _data_start__[*((char *)v5 + i) - 1] )
      return 0;
  }
  printf("You are correct!");
  return 0;
}

逻辑也很清晰_data_start__是一个表,直接搓脚本

v4 = [42, 70, 39, 34, 78, 44, 34, 40, 73, 63, 43, 64] #v4提出来的数据
flag = ''
__data_start__ = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"' 
for i in v4:
    flag = flag + chr(__data_start__.find(chr(i)) + 1)
    print(flag)

flag{U9X_1S_W6@T?}

简单注册器

下载附件,是个apk,用JEB打开看看,能直接看到main,tab看看代码

package com.example.flag;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View.OnClickListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {
    public static class PlaceholderFragment extends Fragment {
        @Override  // android.support.v4.app.Fragment
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            return inflater.inflate(0x7F030018, container, false);  // layout:fragment_main
        }
    }

    @Override  // android.support.v7.app.ActionBarActivity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(0x7F030017);  // layout:activity_main
        if(savedInstanceState == null) {
            this.getSupportFragmentManager().beginTransaction().add(0x7F05003C, new PlaceholderFragment()).commit();  // id:container
        }

        Button button = (Button)this.findViewById(0x7F05003F);  // id:button1
        TextView textview = (TextView)this.findViewById(0x7F05003E);  // id:textView1
        button.setOnClickListener(new View.OnClickListener() {
            @Override  // android.view.View$OnClickListener
            public void onClick(View v) {
                int flag = 1;
                String s = ((EditText)this.findViewById(0x7F05003D)).getText().toString();  // id:editText1
                if(s.length() != 0x20 || s.charAt(0x1F) != 97 || s.charAt(1) != 98 || s.charAt(0) + s.charAt(2) - 0x30 != 56) {
                    flag = 0;
                }

                if(flag == 1) {
                    char[] arr_c = "dd2940c04462b4dd7c450528835cca15".toCharArray();
                    arr_c[2] = (char)(arr_c[2] + arr_c[3] - 50);
                    arr_c[4] = (char)(arr_c[2] + arr_c[5] - 0x30);
                    arr_c[30] = (char)(arr_c[0x1F] + arr_c[9] - 0x30);
                    arr_c[14] = (char)(arr_c[27] + arr_c[28] - 97);
                    int i;
                    for(i = 0; i < 16; ++i) {
                        char a = arr_c[0x1F - i];
                        arr_c[0x1F - i] = arr_c[i];
                        arr_c[i] = a;
                    }

                    textview.setText("flag{" + String.valueOf(arr_c) + "}");
                    return;
                }

                textview.setText("输入注册码错误");
            }
        });
    }

    @Override  // android.app.Activity
    public boolean onCreateOptionsMenu(Menu menu) {
        this.getMenuInflater().inflate(0x7F0C0000, menu);  // menu:main
        return true;
    }

    @Override  // android.app.Activity
    public boolean onOptionsItemSelected(MenuItem item) {
        return item.getItemId() == 0x7F050040 ? true : super.onOptionsItemSelected(item);  // id:action_settings
    }
}

显然中间这段就是主逻辑:

试试能不能写个脚本

最开始写出来是这样,发现运行报错了,然后去查了一下,应该是str类型的问题,要将其转换为列表才能进行正常的赋值运算,重新写脚本:

str=['d','d','2','9','4','0','c','0','4','4','6','2','b','4','d','d','7','c','4','5','0','5','2','8','8','3','5','c','c','a','1','5']  
a=""  
flag=""  
str[2]=chr(ord(str[2])+ord(str[3])-50)  
str[4]=chr(ord(str[2])+ord(str[5])-0x30)  
str[30]=chr(ord(str[0x1F])+ord(str[9])-0x30)  
str[14]=chr(ord(str[27])+ord(str[28])-97)  
for i in range(16):  
    a=str[0x1F-i]  
    str[0x1F-i]=str[i]  
    str[i]=a  
for i in range(len(str)):  
    flag+=chr(ord(str[i]))  
print(flag)

59acc538825054c7de4b26440c0999dd

[GWCTF 2019]pyre

下载下来是一个pyc文件,直接用在线网站反编译了

#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 2.7

print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
    num = ((input1[i] + i) % 128 + 128) % 128
    code += num

for i in range(l - 1):
    code[i] = code[i] ^ code[i + 1]

print code
code = [
    '%1f',
    '%12',
    '%1d',
    '(',
    '0',
    '4',
    '%01',
    '%06',
    '%14',
    '4',
    ',',
    '%1b',
    'U',
    '?',
    'o',
    '6',
    '*',
    ':',
    '%01',
    'D',
    ';',
    '%',
    '%13']

求解脚本:

# 假设的加密后的code列表,将特殊格式转换为对应的ASCII码
encrypted_code = [
    0x1f, 0x12, 0x1d, ord('('), ord('0'), ord('4'), 0x01, 0x06,
    0x14, ord('4'), ord(','), 0x1b, ord('U'), ord('?'), ord('o'),
    ord('6'), ord('*'), ord(':'), 0x01, ord('D'), ord(';'), ord('%'), 0x13]

# 第一步:逆向异或操作
original_code = [0] * len(encrypted_code)
original_code[-1] = encrypted_code[-1]  # 最后一个元素不变
for i in range(len(encrypted_code) - 2, -1, -1):
    original_code[i] = encrypted_code[i] ^ original_code[i + 1]

# 第二步:逆向计算input1
input1 = [''] * len(original_code)
for i in range(len(original_code)):
    num = original_code[i]
    original_input1_i = (num - i) % 128
    input1[i] = chr(original_input1_i)

# 打印原始输入
print(''.join(input1))

findit

使用JEB看看:

package com.example.findit;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {
    @Override  // android.support.v7.app.ActionBarActivity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(0x7F030018);  // layout:activity_main
        Button btn = (Button)this.findViewById(0x7F05003D);  // id:widget3
        EditText edit = (EditText)this.findViewById(0x7F05003E);  // id:widget2
        TextView text = (TextView)this.findViewById(0x7F05003F);  // id:widget1
        btn.setOnClickListener(new View.OnClickListener() {
            @Override  // android.view.View$OnClickListener
            public void onClick(View v) {
                char[] x = new char[17];
                char[] y = new char[38];
                int i;
                for(i = 0; i < 17; ++i) {
                    if(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] < 73 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 65 || new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] < 105 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 97) {
                        x[i] = (char)(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] + 18);
                    }
                    else if(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 65 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] <= 90 || new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 97 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] <= 0x7A) {
                        x[i] = (char)(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] - 8);
                    }
                    else {
                        x[i] = new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i];
                    }
                }

                if(String.valueOf(x).equals(edit.getText().toString())) {
                    int v1;
                    for(v1 = 0; v1 < 38; ++v1) {
                        if(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', '}'}[v1] >= 65 && 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', '}'}[v1] <= 90 || 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', '}'}[v1] >= 97 && 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', '}'}[v1] <= 0x7A) {
                            y[v1] = (char)(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', '}'}[v1] + 16);
                            if(y[v1] > 90 && y[v1] < 97 || y[v1] >= 0x7A) {
                                y[v1] = (char)(y[v1] - 26);
                            }
                        }
                        else {
                            y[v1] = 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', '}'}[v1];
                        }
                    }

                    text.setText(String.valueOf(y));
                    return;
                }

                text.setText("答案错了肿么办。。。不给你又不好意思。。。哎呀好纠结啊~~~");
            }
        });
    }

    @Override  // android.app.Activity
    public boolean onOptionsItemSelected(MenuItem item) {
        return item.getItemId() == 0x7F050040 ? true : super.onOptionsItemSelected(item);  // id:action_settings
    }
}

发现代码很乱,再用jadx看看:

package com.example.findit;

import android.os.Bundle;

import android.support.v7.app.ActionBarActivity;

import android.view.MenuItem;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

/* loaded from: classes.dex */

public class MainActivity extends ActionBarActivity {

    /* JADX INFO: Access modifiers changed from: protected */

    @Override // android.support.v7.app.ActionBarActivity, android.support.v4.app.FragmentActivity, android.app.Activity

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        Button btn = (Button) findViewById(R.id.widget3);

        final EditText edit = (EditText) findViewById(R.id.widget2);

        final TextView text = (TextView) findViewById(R.id.widget1);

        final char[] a = {'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'};

        final char[] b = {'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', '}'};

        btn.setOnClickListener(new View.OnClickListener() { // from class: com.example.findit.MainActivity.1

            @Override // android.view.View.OnClickListener

            public void onClick(View v) {

                char[] x = new char[17];

                char[] y = new char[38];

                for (int i = 0; i < 17; i++) {

                    if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {

                        x[i] = (char) (a[i] + 18);

                    } else if ((a[i] >= 'A' && a[i] <= 'Z') || (a[i] >= 'a' && a[i] <= 'z')) {

                        x[i] = (char) (a[i] - '\b');

                    } else {

                        x[i] = a[i];

                    }

                }

                String m = String.valueOf(x);

                if (m.equals(edit.getText().toString())) {

                    for (int i2 = 0; i2 < 38; i2++) {

                        if ((b[i2] >= 'A' && b[i2] <= 'Z') || (b[i2] >= 'a' && b[i2] <= 'z')) {

                            y[i2] = (char) (b[i2] + 16);

                            if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {

                                y[i2] = (char) (y[i2] - 26);

                            }

                        } else {

                            y[i2] = b[i2];

                        }

                    }

                    String n = String.valueOf(y);

                    text.setText(n);

                    return;

                }

                text.setText("答案错了肿么办。。。不给你又不好意思。。。哎呀好纠结啊~~~");

            }

        });

    }

    @Override // android.app.Activity

    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

        if (id == R.id.action_settings) {

            return true;

        }

        return super.onOptionsItemSelected(item);

    }

}

这个清楚多了

有两行可疑字符串,分别为ThisIsTheFlagHomepvkq{m164675262033l4m49lnp7p9mnk28k75}

然后我将花括号中的字符串作为flag交了上去,发现不对,这个字符串应该是经过了某种位移

使用工具看一下:

可以看见那行flag了:flag{c164675262033b4c49bdf7f9cda28a75}

[ACTF新生赛2020]rome

32位无壳,用IDA打开,没找到主要加密函数,查找字符串之后交叉引用到关键函数

进入func函数:

int func()
{
  int result; // eax
  int v1[4]; // [esp+14h] [ebp-44h]
  unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
  unsigned __int8 v3; // [esp+25h] [ebp-33h]
  unsigned __int8 v4; // [esp+26h] [ebp-32h]
  unsigned __int8 v5; // [esp+27h] [ebp-31h]
  unsigned __int8 v6; // [esp+28h] [ebp-30h]
  int v7; // [esp+29h] [ebp-2Fh]
  int v8; // [esp+2Dh] [ebp-2Bh]
  int v9; // [esp+31h] [ebp-27h]
  int v10; // [esp+35h] [ebp-23h]
  unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
  char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF

  strcpy(v12, "Qsw3sj_lz4_Ujw@l");
  printf("Please input:");
  scanf("%s", &v2);
  result = v2;
  if ( v2 == 'A' )
  {
    result = v3;
    if ( v3 == 'C' )
    {
      result = v4;
      if ( v4 == 'T' )
      {
        result = v5;
        if ( v5 == 'F' )
        {
          result = v6;
          if ( v6 == '{' )
          {
            result = v11;
            if ( v11 == '}' )
            {
              v1[0] = v7;
              v1[1] = v8;
              v1[2] = v9;
              v1[3] = v10;
              *(_DWORD *)&v12[17] = 0;
              while ( *(int *)&v12[17] <= 15 )
              {
                if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '@' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'Z' )
                  *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
                if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '`' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'z' )
                  *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
                ++*(_DWORD *)&v12[17];
              }
              *(_DWORD *)&v12[17] = 0;
              while ( *(int *)&v12[17] <= 15 )
              {
                result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
                if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
                  return result;
                ++*(_DWORD *)&v12[17];
              }
              return printf("You are correct!");
            }
          }
        }
      }
    }
  }
  return result;
}

分析函数:
主体加密为第一个while循环,两个if分别代表v12字符串中的大小写字符进行加密,最后与result作比较,正确的话就会输出correct

exp

#include<stdio.h>
int main()
{
    char str[]="Qsw3sj_lz4_Ujw@l";
    int i;
    char flag[17]={0};
    for(i=0;i<=15;i++)
    {
        if(str[i]>'@'&&str[i]<='Z')
        {
        flag[i]=str[i]-65+51;
        if(flag[i]<64)
            {
             flag[i]+=26;
            }
        }
        else if(str[i]>'`'&&str[i]<='z')
        {
            flag[i]=str[i]-97+79;
            if(flag[i]<96)
            {
                flag[i]+=26;
            }
        }
        else 
        flag[i]=str[i];
    }
    for(i = 0 ; i < 16 ; i ++)
   {
       printf("%c",flag[i]);
   }
} 

flag{Cae3ar_th4_Gre@t}

rsa

密码题别来沾边

[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代码,由于我没学,就直接丢给AI了:

这段HTML代码实现了一个简单的网页,包含一个文本输入框和一个按钮。用户可以在文本框中输入数据,并通过点击按钮来检查输入的数据是否符合特定条件。这个检查是通过JavaScript脚本实现的。

JavaScript代码的核心功能是对用户输入的数据进行ROT13加密,然后将加密后的结果与预设的字符串"PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"比较。如果两者相等,则弹出提示框显示“Correct flag!”;如果不相等,则提示“Incorrect flag, rot again”。

ROT13是一种简单的字符替换加密方法,它将字母表中的每个字母替换为它在字母表中13位之后的字母。这种加密方法是可逆的,即对加密后的文本再次应用ROT13加密,就能得到原文。

要找到正确的flag,我们需要对给定的字符串"PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"进行ROT13解密。

flag{ClientSideLoginsAreEasy@flare-on.com}

[WUSTCTF2020]level1

下载附件,有一个ELF和一个output.txt文件,先用IDA64查看ELF文件

进入main函数:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+4h] [rbp-2Ch]
  FILE *stream; // [rsp+8h] [rbp-28h]
  char ptr[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  stream = fopen("flag", "r");
  fread(ptr, 1uLL, 0x14uLL, stream);
  fclose(stream);
  for ( i = 1; i <= 19; ++i )
  {
    if ( (i & 1) != 0 )
      printf("%ld\n", (unsigned int)(ptr[i] << i));
    else
      printf("%ld\n", (unsigned int)(i * ptr[i]));
  }
  return 0;
}
  • 通过一个for循环,从1迭代到19(包含),对ptr数组中的每个字符进行处理和输出。
  • 如果i是奇数,将ptr[i]的值左移i位(即ptr[i] << i),然后输出。
  • 如果i是偶数,计算iptr[i]的乘积(即i * ptr[i]),然后输出。

再看看那个.txt文件,里面是一串数字

那就应该是将flag文件进行上述运算最后得到output,然后就可以开始写逆向求解脚本了:

#include<stdio.h>
int main()
{
    int output[]={1,198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782,65536000};
    int i;
    char flag[20]="0";
     for ( i = 1; i <= 19; ++i )
     {
        if ( (i & 1) != 0 )
        flag[i]=char((output[i])>>i);
        else
        flag[i]=char((output[i])/i);
     }
     flag[i]='\0';
     printf("%s",flag);
}
//(注意循环是从1开始的,txt中只有19位,首位没有,先随便写一个)

flag{d9-dE6-20c}

[GUET-CTF2019]re

简单的UPX壳,进去没有main函数,直接查找字符串

最终定位到主体

__int64 __fastcall sub_400E28(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
    int v6; // edx
    int v7; // ecx
    int v8; // r8d
    int v9; // r9d
    __int64 result; // rax
    __int64 v11; // [rsp+0h] [rbp-30h] BYREF
    unsigned __int64 v12; // [rsp+28h] [rbp-8h]

    v12 = __readfsqword(0x28u);
    sub_40F950((unsigned int)"input your flag:", a2, a3, a4, a5, a6, 0LL, 0LL, 0LL, 0LL);
    sub_40FA80((unsigned int)"%s", (unsigned int)&v11, v6, v7, v8, v9, v11);
    if ( (unsigned int)sub_4009AE(&v11) )
        sub_410350("Correct!");
    else
        sub_410350("Wrong!");
    result = 0LL;
    if ( __readfsqword(0x28u) != v12 )
        sub_443550();
    return result;
}

不难看出sub_4009AE是关键的函数,点进去发现是一个z3

_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;
}

然后用Python写了个z3求解,发现求不出来,查了一下说伪代码少了a1[6],而且a1[16],a1[17]反了,爆破出来a1[6]=1,可以写脚本了

from z3 import *
a1 = [Int(f'a1[{i}]') for i in range(32)]
flag=""
solver = Solver()
solver.add(1629056 * a1[0] == 166163712)
solver.add(6771600 * a1[1] == 731332800)
solver.add(3682944 * a1[2] == 357245568)
solver.add(10431000 * a1[3] == 1074393000)
solver.add(3977328 * a1[4] == 489211344)
solver.add(5138336 * a1[5] == 518971936)
solver.add(a1[6]==ord('1'))
solver.add(7532250 * a1[7] == 406741500)
solver.add(5551632 * a1[8] == 294236496)
solver.add(3409728 * a1[9] == 177305856)
solver.add(13013670 * a1[10] == 650683500)
solver.add(6088797 * a1[11] == 298351053)
solver.add(7884663 * a1[12] == 386348487)
solver.add(8944053 * a1[13] == 438258597)
solver.add(5198490 * a1[14] == 249527520)
solver.add(4544518 * a1[15] == 445362764)
solver.add(10115280 * a1[16] == 981182160)
solver.add(3645600 * a1[17] == 174988800)
solver.add(9667504 * a1[18] == 493042704)
solver.add(5364450 * a1[19] == 257493600)
solver.add(13464540 * a1[20] == 767478780)
solver.add(5488432 * a1[21] == 312840624)
solver.add(14479500 * a1[22] == 1404511500)
solver.add(6451830 * a1[23] == 316139670)
solver.add(6252576 * a1[24] == 619005024)
solver.add(7763364 * a1[25] == 372641472)
solver.add(7327320 * a1[26] == 373693320)
solver.add(8741520 * a1[27] == 498266640)
solver.add(8871876 * a1[28] == 452465676)
solver.add(4086720 * a1[29] == 208422720)
solver.add(9374400 * a1[30] == 515592000)
solver.add(5759124 * a1[31] == 719890500)
if solver.check() == sat:
    model = solver.model()
    solution = [model[a1[i]].as_long() for i in range(32)]
    print("Solution found:")
    print(solution)
else:
    print("No solution found.")
for j in range(len(solution)):
    flag+=chr(solution[j])
print(flag)

CrackRTF

扫了一眼,这题好像有点难度,于是一边看wp一边写写看

直接可以进入主逻辑函数:

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  DWORD v3; // eax
  DWORD v4; // eax
  char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
  int v7; // [esp+150h] [ebp-20Ch]
  char String1[260]; // [esp+154h] [ebp-208h] BYREF
  char Destination[260]; // [esp+258h] [ebp-104h] BYREF

  memset(Destination, 0, sizeof(Destination));
  memset(String1, 0, sizeof(String1));
  v7 = 0;
  printf("pls input the first passwd(1): ");
  scanf("%s", Destination);                     // 输入第一个密码
  if ( strlen(Destination) != 6 )               // 长度不为6退出
  {
    printf("Must be 6 characters!\n");
    ExitProcess(0);
  }
  v7 = atoi(Destination);
  if ( v7 < 100000 )                            // 密码小于100000退出
    ExitProcess(0);
  strcat(Destination, "@DBApp");                // 拼接函数
  v3 = strlen(Destination);
  sub_40100A((BYTE *)Destination, v3, String1); // SHA-1
  if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )// 比较哈希值
  {
    printf("continue...\n\n");
    printf("pls input the first passwd(2): ");
    memset(Str, 0, sizeof(Str));
    scanf("%s", Str);                           // 输入第二个密码
    if ( strlen(Str) != 6 )                     // 长度不为6退出
    {
      printf("Must be 6 characters!\n");
      ExitProcess(0);
    }
    strcat(Str, Destination);
    memset(String1, 0, sizeof(String1));
    v4 = strlen(Str);
    sub_401019((BYTE *)Str, v4, String1);       // MD5
    if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
    {
      if ( !(unsigned __int8)sub_40100F(Str) )  // 生成包含flag的文件
      {
        printf("Error!!\n");
        ExitProcess(0);
      }
      printf("bye ~~\n");
    }
  }
  return 0;
}

在sub_40100A函数中可以通过哈希算法标识符判断出是SHA-1

第一个密码给了限制条件,可以通过哈希值爆破出第一个密码:

import hashlib
string='@DBApp'
for i in range(100000,999999):
    flag=str(i)+string
    x = hashlib.sha1(flag.encode("utf8"))
    y = x.hexdigest()
    if "6e32d0943418c2c33385bc35a1470250dd8923a9" == y:
            print(flag)
            break

爆破出来是密码是123321

往后看发现第二个密码只给了MD5值,并没有给其他的限制条件,因此爆破是很困难的,只知道是一个6位密码

在后面判断的时候有一个sub_40100F函数,应该是关键函数,进去看看

跳转到sub_4014D0函数

char __cdecl sub_4014D0(LPCSTR lpString)
{
  LPCVOID lpBuffer; // [esp+50h] [ebp-1Ch]
  DWORD NumberOfBytesWritten; // [esp+58h] [ebp-14h] BYREF
  DWORD nNumberOfBytesToWrite; // [esp+5Ch] [ebp-10h]
  HGLOBAL hResData; // [esp+60h] [ebp-Ch]
  HRSRC hResInfo; // [esp+64h] [ebp-8h]
  HANDLE hFile; // [esp+68h] [ebp-4h]

  hFile = 0;
  hResData = 0;
  nNumberOfBytesToWrite = 0;
  NumberOfBytesWritten = 0;
  hResInfo = FindResourceA(0, (LPCSTR)0x65, "AAA");
  if ( !hResInfo )
    return 0;
  nNumberOfBytesToWrite = SizeofResource(0, hResInfo);
  hResData = LoadResource(0, hResInfo);
  if ( !hResData )
    return 0;
  lpBuffer = LockResource(hResData);
  sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite);
  hFile = CreateFileA("dbapp.rtf", 0x10000000u, 0, 0, 2u, 0x80u, 0);
  if ( hFile == (HANDLE)-1 )
    return 0;
  if ( !WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
    return 0;
  CloseHandle(hFile);
  return 1;
}

看不懂这堆,只能问一下AI了:

再去查了一下,使用ResourceHacker可以查找到“AAA”的文件源,下面还有一个sub_401005函数:

unsigned int __cdecl sub_401420(LPCSTR lpString, int a2, unsigned int a3)
{
  unsigned int result; // eax
  unsigned int i; // [esp+4Ch] [ebp-Ch]
  unsigned int v5; // [esp+54h] [ebp-4h]

  v5 = lstrlenA(lpString);
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= a3 )
      break;
    *(_BYTE *)(i + a2) ^= lpString[i % v5];
  }
  return result;
}

发现是简单的异或,再去问了问学长,为了保证输出的文件可用,需保证其文件头为.rtf文件,于是寻找.rtf文件的文件头:{\rtf1

密码与“AAA”异或得到.rtf文件

再查看“AAA”中的前六位16进制

因为异或是可逆运算,所以就可以求解密码了

抄个脚本:

rtf = '{\\rtf1' \\需要注意,\r需要转义,变成\\r
A = [0x05, 0x7D, 0x41, 0x15, 0x26, 0x01]
password=''
for i in range(len(rtf)):
    x = ord(rtf[i]) ^ A[i]
    password+=chr(x)
print(password)

得到第二个密码是~!3a@0

这时就可以去程序中验证了,输入两次密码之后控制台就关掉了,然后桌面上就生成了一个.rtf文件

打开文件就是flag啦:Flag{N0_M0re_Free_Bugs}

[WUSTCTF2020]level2

下载附件,查壳发现有一个UPX壳

脱完壳之后用IDA打开

flag直接就送了,我没想到居然这么简单

[MRCTF2020]Transform

下载附件,用IDA打开,进去之后就是函数的主逻辑:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char Str[104]; // [rsp+20h] [rbp-70h] BYREF
  int j; // [rsp+88h] [rbp-8h]
  int i; // [rsp+8Ch] [rbp-4h]

  sub_402230(argc, argv, envp);
  printf("Give me your code:\n");
  scanf("%s", Str);
  if ( strlen(Str) != 33 )
  {
    printf("Wrong!\n");
    system("pause");
    exit(0);
  }
  for ( i = 0; i <= 32; ++i )
  {
    result[i] = Str[init[i]];
    result[i] ^= LOBYTE(init[i]);
  }
  for ( j = 0; j <= 32; ++j )
  {
    if ( byte_40F0E0[j] != result[j] )
    {
      printf("Wrong!\n");
      system("pause");
      exit(0);
    }
  }
  printf("Right!Good Job!\n");
  printf("Here is your flag: %s\n", Str);
  system("pause");
  return 0;
}

发现是一个打乱字符串在异或运算的操作,LOBYTE()的意思是取八位最低字节,如图所示:

最后还要加个00,然后我们就可以取得需要进行异或的两个数组了,由此写出脚本

result=[0x67,0x79,0x7B,0x7F,0x75,0x2B,0x3C,0x52,0x53,0x79,0x57,0x5E,0x5D,0x42,0x7B,0x2D,0x2A,0x66,0x42,0x7E,0x4C,0x57,0x79,0x41,0x6B,0x7E,0x65,0x3C,0x5C,0x45,0x6F,0x62,0x4D]  
init  =[0x09,0x0A,0x0F,0x17,0x07,0x18,0x0C,0x06,0x01,0x10,0x03,0x11,0x20,0x1D,0x0B,0x1E,0x1B,0x16,0x04,0x0D,0x13,0x14,0x15,0x02,0x19,0x05,0x1F,0x08,0x12,0x1A,0x1C,0x0E,0x00]  
str=[0]*33  
flag=""  
for i in range(33):  
  result[i]^=init[i]  
for j in range(33):  
  str[init[j]]=result[j]  
for k in range(33):  
  flag+=chr(str[k])  
print(flag)

MRCTF{Tr4nsp0sltiON_Clph3r_1s_3z}

[2019红帽杯]easyRE

最开始写了一下,逆天题目

elf文件,用IDA64打开,发现里面有很多函数,shift+F12查找一下

发现有一长串字符和一个表,看着就是base64

本来以为解密之后就能得到flag了,还是我想的太简单了

解密之后发现还是个base64加密,然后一直一直一直解密,10次之后得到一个网站,是看雪的帖子

被骗了!!!

然后从查找到的you found me入手,一直交叉引用找到主要函数:

__int64 sub_4009C6()
{
  __int64 result; // rax
  int i; // [rsp+Ch] [rbp-114h]
  __int64 v2; // [rsp+10h] [rbp-110h]
  __int64 v3; // [rsp+18h] [rbp-108h]
  __int64 v4; // [rsp+20h] [rbp-100h]
  __int64 v5; // [rsp+28h] [rbp-F8h]
  __int64 v6; // [rsp+30h] [rbp-F0h]
  __int64 v7; // [rsp+38h] [rbp-E8h]
  __int64 v8; // [rsp+40h] [rbp-E0h]
  __int64 v9; // [rsp+48h] [rbp-D8h]
  __int64 v10; // [rsp+50h] [rbp-D0h]
  __int64 v11; // [rsp+58h] [rbp-C8h]
  char v12[13]; // [rsp+60h] [rbp-C0h] BYREF
  char v13[4]; // [rsp+6Dh] [rbp-B3h] BYREF
  char v14[19]; // [rsp+71h] [rbp-AFh] BYREF
  char v15[32]; // [rsp+90h] [rbp-90h] BYREF
  int v16; // [rsp+B0h] [rbp-70h]
  char v17; // [rsp+B4h] [rbp-6Ch]
  char v18[72]; // [rsp+C0h] [rbp-60h] BYREF
  unsigned __int64 v19; // [rsp+108h] [rbp-18h]

  v19 = __readfsqword(0x28u);
  qmemcpy(v12, "Iodl>Qnb(ocy", 12);
  v12[12] = 127;
  qmemcpy(v13, "y.i", 3);
  v13[3] = 127;
  qmemcpy(v14, "d`3w}wek9{iy=~yL@EC", sizeof(v14));
  memset(v15, 0, sizeof(v15));
  v16 = 0;
  v17 = 0;
  sub_4406E0(0LL, v15, 37LL);
  v17 = 0;
  if ( sub_424BA0(v15) == 36 )
  {
    for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i )
    {
      if ( (unsigned __int8)(v15[i] ^ i) != v12[i] )
      {
        result = 4294967294LL;
        goto LABEL_13;
      }
    }
    sub_410CC0("continue!");
    memset(v18, 0, 65);
    sub_4406E0(0LL, v18, 64LL);
    v18[39] = 0;
    if ( sub_424BA0(v18) == 39 )
    {
      v2 = sub_400E44(v18);
      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);
      v11 = sub_400E44(v10);
      if ( !(unsigned int)sub_400360(v11, off_6CC090) )
      {
        sub_410CC0("You found me!!!");
        sub_410CC0("bye bye~");
      }
      result = 0LL;
    }
    else
    {
      result = 4294967293LL;
    }
  }
  else
  {
    result = 0xFFFFFFFFLL;
  }
LABEL_13:
  if ( __readfsqword(0x28u) != v19 )
    sub_444020();
  return result;
}

发现函数开头的地方做了一个位移异或运算,写个脚本解密一下:

v12="Iodl>Qnb(ocy\x7Fy.i\x7Fd`3w}wek9{iy=~yL@EC"  
v15=""  
for i in range(len(v12)):  
    v15+=chr(ord(v12[i])^i)  
print(v15)

得到的结果是:Info:The first four chars are flag

前四个字节是flag……你不说我也知道的

再往下看就是base64了,但是这里并没有看见之前需要解密的字符串

if判断那里有一行

后面看了别人的wp说base64结束之后下面又调用了一个函数,就在这张图的下半部分

进入函数:

unsigned __int64 sub_400D35()
{
  unsigned __int64 result; // rax
  unsigned int v1; // [rsp+Ch] [rbp-24h]
  int i; // [rsp+10h] [rbp-20h]
  int j; // [rsp+14h] [rbp-1Ch]
  unsigned int v4; // [rsp+24h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v1 = sub_43FD20(0LL) - qword_6CEE38;
  for ( i = 0; i <= 1233; ++i )
  {
    sub_40F790(v1);
    sub_40FE60();
    sub_40FE60();
    v1 = sub_40FE60() ^ 0x98765432;
  }
  v4 = v1;
  if ( (v1 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v4) ^ byte_6CC0A3) == 'g' )
  {
    for ( j = 0; j <= 24; ++j )
      sub_410E90((byte_6CC0A0[j] ^ *(&v4 + j % 4)));
  }
  result = __readfsqword(0x28u) ^ v5;
  if ( result )
    sub_444020();
  return result;
}

最开始我是不能理解这段代码的,于是只能去多找几个相对详细的wp去看看

HIBYTE()是取最高位字节的意思,而v4又是int型,所以v4byte_6CC0A0数组的前四个字节异或就能得到flag头的字符串,而下文for循环又是byte_6CC0A0数组与v4数组中与4取模之后的元素异或,也就是对v4数组中的四个元素循环异或,因此我们需要将byte_6CC0A0数组中的前四个元素与“flag”异或得到作为“密钥”的v4数组,直到这里我才发现前文解出的前四个字节为flag是有用的提示

这个时候就可以搓脚本了:

v4=""  
byte_6CC0A0="@5 V]\x18\"E\x17/$nb<'THl$nr<2E["  
xor="flag"  
flag=""  
for i in range(4):  
    v4+=chr(ord(byte_6CC0A0[i])^ord(xor[i]))  
print(v4)  
for j in range(len(byte_6CC0A0)):  
    flag+=chr(ord(byte_6CC0A0[j])^ord(v4[j%4]))  
print(flag)

得到的结果就是:


很有意思的一道题,不过真让我头大,看着wp写都有点磕磕绊绊的

[SUCTF2019]SignIn

下载附件之后IDA打开,没看见主要函数,查找字符串之后发现有个input flag,交叉引用到具体函数:

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

  v10 = __readfsqword(0x28u);
  puts("[sign in]");
  printf("[input your flag]: ");
  __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;
}

看见这个65537的时候我第一反应就是RSA,然后C是用16进制表示的,n是那长串大素数,先去在线网站分解一下n,然后去抄个RSA脚本就可以解密了:

import gmpy2  
from Crypto.Util.number import long_to_bytes  

q = 366669102002966856876605669837014229419  
p = 282164587459512124844245113950593348271  

e = 65537  
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35  
# n = 103461035900816914121390101299049044413950405173712170434161686539878160984549  
n = q * p  
# print(n)  
d = gmpy2.invert(e, (p - 1) * (q - 1))  
print("d=", d)  
m = pow(c, d, n)  
print(m)  
print(long_to_bytes(m))

放虚拟机里面跑出来:

suctf{Pwn_@_hundred_years}

[ACTF新生赛2020]usualCrypt

下载附件,用IDA打开,查找字符串之后发现有一个base64表以及一个字符串

我第一反应是去解这个base64,结果发现这并不是一个有效的base64字符串

于是去看了眼wp的思路

进入_main()函数看看:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // esi
  int v5[3]; // [esp+8h] [ebp-74h] BYREF
  __int16 v6; // [esp+14h] [ebp-68h]
  char v7; // [esp+16h] [ebp-66h]
  char v8[100]; // [esp+18h] [ebp-64h] BYREF

  printf(&unk_40E140);
  scanf("%s", v8);
  memset(v5, 0, sizeof(v5));
  v6 = 0;
  v7 = 0;
  base64_fun(v8, strlen(v8), v5);
  v3 = 0;
  while ( *(v5 + v3) == byte_40E0E4[v3] )
  {
    if ( ++v3 > strlen(v5) )
      goto LABEL_6;
  }
  printf(aError);
LABEL_6:
  if ( v3 - 1 == strlen(byte_40E0E4) )
    return printf(aAreYouHappyYes);
  else
    return printf(aAreYouHappyNo);
}

部分函数我已重命名,进入base64_fun():

int __cdecl sub_401080(int a1, int a2, int a3)
{
  int v3; // edi
  int v4; // esi
  int v5; // edx
  int v6; // eax
  int v7; // ecx
  int v8; // esi
  int v9; // esi
  int v10; // esi
  int v11; // esi
  _BYTE *v12; // ecx
  int v13; // esi
  int v15; // [esp+18h] [ebp+8h]

  v3 = 0;
  v4 = 0;
  sub_401000();
  v5 = a2 % 3;
  v6 = a1;
  v7 = a2 - a2 % 3;
  v15 = a2 % 3;
  if ( v7 > 0 )
  {
    do
    {
      LOBYTE(v5) = *(a1 + v3);
      v3 += 3;
      v8 = v4 + 1;
      *(v8 + a3 - 1) = aAbcdefghijklmn[(v5 >> 2) & 0x3F];
      *(++v8 + a3 - 1) = aAbcdefghijklmn[16 * (*(a1 + v3 - 3) & 3) + ((*(a1 + v3 - 2) >> 4) & 0xF)];
      *(++v8 + a3 - 1) = aAbcdefghijklmn[4 * (*(a1 + v3 - 2) & 0xF) + ((*(a1 + v3 - 1) >> 6) & 3)];
      v5 = *(a1 + v3 - 1) & 0x3F;
      v4 = v8 + 1;
      *(v4 + a3 - 1) = aAbcdefghijklmn[v5];
    }
    while ( v3 < v7 );
    v5 = v15;
  }
  if ( v5 == 1 )
  {
    LOBYTE(v7) = *(v3 + a1);
    v9 = v4 + 1;
    *(v9 + a3 - 1) = aAbcdefghijklmn[(v7 >> 2) & 0x3F];
    v10 = v9 + 1;
    *(v10 + a3 - 1) = aAbcdefghijklmn[16 * (*(v3 + a1) & 3)];
    *(v10 + a3) = 61;
LABEL_8:
    v13 = v10 + 1;
    *(v13 + a3) = 61;
    v4 = v13 + 1;
    goto LABEL_9;
  }
  if ( v5 == 2 )
  {
    v11 = v4 + 1;
    *(v11 + a3 - 1) = aAbcdefghijklmn[(*(v3 + a1) >> 2) & 0x3F];
    v12 = (v3 + a1 + 1);
    LOBYTE(v6) = *v12;
    v10 = v11 + 1;
    *(v10 + a3 - 1) = aAbcdefghijklmn[16 * (*(v3 + a1) & 3) + ((v6 >> 4) & 0xF)];
    *(v10 + a3) = aAbcdefghijklmn[4 * (*v12 & 0xF)];
    goto LABEL_8;
  }
LABEL_9:
  *(v4 + a3) = 0;
  return sub_401030(a3);
}

函数头有一个sub_401000()函数:

int sub_401000()
{
  int result; // eax
  char v1; // cl

  for ( result = 6; result < 15; ++result )
  {
    v1 = aAbcdefghijklmn[result + 10];
    aAbcdefghijklmn[result + 10] = aAbcdefghijklmn[result];
    aAbcdefghijklmn[result] = v1;
  }
  return result;
}

这里是对base64表进行操作,具体来说就是将表中6-14与16-24中的元素进行互换

发现函数最后还有一个返回值,进入sub_401030()函数:

int __cdecl sub_401030(const char *a1)
{
  __int64 v1; // rax
  char v2; // al

  v1 = 0i64;
  if ( strlen(a1) )
  {
    do
    {
      v2 = a1[HIDWORD(v1)];
      if ( v2 < 'a' || v2 > 'z' )
      {
        if ( v2 < 'A' || v2 > 'Z' )
          goto LABEL_9;
        LOBYTE(v1) = v2 + 32;
      }
      else
      {
        LOBYTE(v1) = v2 - 32;
      }
      a1[HIDWORD(v1)] = v1;
LABEL_9:
      LODWORD(v1) = 0;
      ++HIDWORD(v1);
    }
    while ( HIDWORD(v1) < strlen(a1) );
  }
  return v1;
}

这个函数的作用是将base64加密后的字符串中的大写字母转小写,小写字母转大写

由此就可以构造脚本解密了:

#include<iostream>
#include<string>
#include<cstring>

using namespace std;

void base64_fun() {
    char b[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char result;
    for (int i = 6; i < 15; i++) {
        result = b[i];
        b[i] = b[i + 10];
        b[i + 10] = result;
    }
    cout<<b<<endl;
}

void exchange() {
    char str[]="zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9";
    int len_str=strlen(str);
    for (int i=0;i<len_str;i++) {
        if (str[i] >= 'a' && str[i] <= 'z') {
            str[i] = str[i] - 32; 
        } else if (str[i] >= 'A' && str[i] <= 'Z') {
            str[i] = str[i] + 32; 
        }
    }
    cout<<str<<endl;
}

int main()
{
    base64_fun();
    exchange();

}

这样以来就可以得到原本的表和原本的字符串了:

然后使用Python的base64模块进行换表的解密:

import base64  
import string  

str1 = "ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9"  # 待解秘字符串  

string1 = "ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/"  # 新表  
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"  

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

b'flag{bAse64_h2s_a_Surprise}'

[MRCTF2020]Xor

进去之后直接就是主函数,但是按F5之后会报错

无法反汇编,直接看汇编


flag其实储存在byte_4212C0中,看到 loc_4010B6 我们可以知道 edx 寄存器不断自增,最后退出循环,然后再与27 进行比较,这个时候我们就知道,flag的长度就为27,如果输入的长度 大于27 那么就提示错误,如果不小于 那么先与eax异或 然后 再与byte_41EA08进行比较,所以byte_41EA08就是异或后的flag,我们写脚本还原就行了

str1 = 'MSAWB~FXZ:J:`tQJ"N@ bpdd}8g'
flag = ""
for i in range(len(str1)):
    flag+= chr(i ^ ord(str1[i]))
print(flag)

[HDCTF2019]Maze

下载附件,有一个简单的UPX壳:

直接用工具去除了,用IDA打开:

发现一处爆红,一大堆字节码以及一个jnz原地跳转,因为跳转的地址+1所以才是原地跳转(我猜的)

先按D将call的错误的地址转换为字节码:

又去查了一下,发现0E8h是添加花指令,将jnz与0E8h都nop掉:

这样就很正常了,但是还是不能F5,我们需要按P重新定义函数,选中需要定义的部分按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] )
    {
      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 )
  {
    sub_401140(aCongratulation);
    sub_401140(aHereIsTheFlagF);
  }
  else
  {
    sub_401140(aTryAgain);
  }
  return 0;
}

可以看见我们用键盘中的adsw进行操作,而asc_408078和dword_40807C分别代表横坐标和纵坐标,则终点就是(5,-4)

结合题目名字和伪代码adsw的提示,这是一个迷宫题:

我们很容易可以查看到地图,且起点则是(7,0),地图总共占了70个字节,可以绘制地图了:

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

ssaaasaassdddw

[MRCTF2020]hello_world_go

好像是go语言逆向,用IDA8.3打开就给flag

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇