menu Ga1@xy's Wor1d
Re:从零开始的逆向生活_buu篇(8.25更)
723 浏览 | 2020-06-21 | 阅读时间: 约 3 分钟 | 分类: Write Up,RE,BUUCTF | 标签: 刷题wp,BUUCTF,Re
请注意,本文编写于 530 天前,最后修改于 345 天前,其中某些信息可能已经过时。

xor

将程序拖进ida,找到flag相关字样,F5进去,直接可以看到主函数内容

其中代码大概意思为数组前后位异或后,与global对应字符串比较前33位,即ord('!'),如果相同则返回Success,也就是得到了flag

双击global可以找到其对应的一系列字符

将其提取出来按照主函数意思编写代码,还原flag

s = ['f',0xA,'k',0xC,'w&O.@',0x11,'x',0xD,'Z;U',0x11,'p',0x19,'F',0x1F,'v"M#D',0xE,'g',6,'h',0xF,'G2O',0]
for i in range(len(s)):
    if(isinstance(s[i],int)):
        s[i] = chr(s[i])
s = "".join(s)
flag = 'f'
for i in range(1,len(s)):
    flag += chr(ord(s[i]) ^ ord(s[i-1]))
print(flag)

得到flag:flag{QianQiuWanDai_YiTongJiangHu}

reverse3

程序拖进ida,可以先看到Str2,在之后会用到,再f5到主函数里

可以看到在Dest[]数组经过一番操作后与Str2比较,如果相同则为flag,而现在有了Str2,所以我们需要找出Dest,先对for循环进行逆操作

str2 = 'e3nifIH9b_C@n@dH'
dest = ''
for i in range(len(str2)):
    dest += chr(ord(str2[i]) - i)

print dest

得到dest后,再看上面的操作,先对v1进行一番操作,再将v1赋值给dest,所以再看v1的操作

看到这里多次出现的aAbcdefghijklmn,点进去可以看到很熟悉的base64编码表

于是尝试对dest进行base64解码,即可得到flag:flag{i_l0ve_you}

不一样的flag

程序拖进ida,可以看到一长串的01

*11110100001010000101111#

f5到主函数,可以看到里面有上下左右,推测是一个走迷宫游戏

01序列长度为25,从这可以看出是一个5*5的迷宫

*1111
01000
01010
00010
1111#

而且只要遇到1就退出,遇到#则返回

ok, the order you enter is the flag!

句意为:输入的顺序为flag

所以从*出发走0最后到#,得到flag:flag{222441144222}

SimpleRev

file命令查看,发现是64位程序,拖进ida找到关键函数

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;  // 数据在内存中是小端存储,所以转换成字符串的时候要逆序,实际是'NDCLS'
  v7 = 0LL;
  v8 = 0;
  v9 = 512969957736LL;  // 此处也同理,得到'hadow'
  v10 = 0LL;
  v11 = 0;
  text = join(key3, (const char *)&v9);  // 进一步分析可知join()函数起到衔接两个字符串的作用,key3='kills',连接在一起就是'killshadow'
  strcpy(key, key1);  // key1='ADSFK'
  strcat(key, src);  // 连接两个字符串后,key='ADSFKNDCLS'
  v2 = 0;
  v3 = 0;
  getchar();
  v5 = strlen(key);  // v5=10
  for ( i = 0; i < v5; ++i )
  {
    if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )  // 将key的大写字母转换成小写,此时key='adsfkndcls'
      key[i] = key[v3 % v5] + 32;
    ++v3;
  }
  printf("Please input your flag:", src);
  while ( 1 )  // 通过这里开始的while循环得到最终的flag
  {
    v1 = getchar();
    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; // key='adsfkndcls'
      }
      else // ( v1 > 96 && v1 <= 122 ) 对小写字母进行操作
      {
        str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
      }
      if ( !(v3 % v5) )
        putchar(32);
      ++v2;
    }
  }
  if ( !strcmp(text, str2) )  // 最后将str2与text进行比对,如果相同,输入的值即为flag,text='killshadow'
    puts("Congratulation!\n");
  else
    puts("Try again!\n");
  return __readfsqword(0x28u) ^ v12;
}

我们现在已知最后对比时的字符串为killshadow,而且只对大小写字母进行操作,所以直接写脚本用所有大小写字母试一下即可

key = 'adsfkndcls'
text = 'killshadow'
dic = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
flag = ''
for i in range(10):
    for x in dic:
        if chr((ord(x) - 39 - ord(key[i]) + 97) % 26 + 97) == text[i]:
            flag += x

print flag

可以发现最后输出flag的时候既有大写字母,也有小写字母,而且恰好是交替出现,说明其实有两种答案,但是本题flag只考虑了大写字母的情况,所以将其中的大写字母挑出来即可,flag:flag{KLDQCUDFZO}

刮开有奖

32位程序,用ida打开后,可以看到WinMain主函数,点进去,找到关键函数

反编译成C,其中比较关键的代码在下图

题干提示输入即为flag,上图中也提到了flag有8位,其中赋值了v7~v17,然后通过sub_4010F0函数对v7~v17进行了操作

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

把这段函数改成可以执行的C代码,把v7~v17代入后执行

#include<stdio.h>

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

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

执行完可以发现v7~v17对应的数字发生了改变,变成了:51 67 69 72 74 78 83 90 97 103 110

再看最后得到flag的部分

if ( String == v7 + 34      // v7经过操作后变为51,51+34=85,即'U'
        && v19 == v11       // v11经过操作后变为74,即'J'
        && 4 * v20 - 141 == 3 * v9
        && v21 / 4 == 2 * (v14 / 9)
        && !strcmp(v4, "ak1w")
        && !strcmp(v5, "V1Ax") )
      {
        MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
      }

其中v4和v5在之前被执行了sub_401000函数,进函数看到byte_407830这个类似数组一样的东西,点进去跳转到汇编中

可以看到熟悉的base64编码字符集,推测ak1wV1Ax是两个经过base64编码的字符串,拼接在一起解码后恰好可以得到WP1jMp(有个顺序上的前后关系,先是v23,后是v20,所以要颠倒一下),再加上开头的两位UJ恰好一共八位,即为flag:flag{UJWP1jMp}

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!");
    }
  }
}

源码中给出了KEY数组,我们需要按照其加密方式来逆向解密,即KEY[i] -= '@' ^ 0x20,python实现即可

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

print flag

得到flag:flag{This_is_the_flag_!}

findit

需要逆向apk文件,直接拖进JEB进行反编译:行为 → 解析,得到关键源码

可以看到其中有类似flag的字样:pvkq{m164675262033l4m49lnp7p9mnk28k75},而qvkq与flag恰好差10,想到凯撒加密,直接得到flag

flag{c164675262033b4c49bdf7f9cda28a75}

全部评论

info 评论功能已经关闭了呐!