buu-reverse-SimpleRev 题解

基本

通过string找到入口关键字

int __cdecl __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: ", argv, envp);
      v4 = getchar();
      if ( v4 != 100 && v4 != 68 )
        break;
      Decry();
    }
    if ( v4 == 113 || v4 == 81 )
      Exit();
    puts("Input fault format!");
    v3 = getchar();
    putchar(v3);
  }
}

看英文就能理解了,因此答案就在Decry()里面,也就是主函数了

十六进制表示的连续字符

Decry()其中的诸如

  *(_QWORD *)src = 357761762382LL;
  v9 = 512969957736LL;

本质上这是用连续的ascii码赋值一个数组,先转换为十六进制(十六进制每两位为1Byte正好是一个字符的长度)然后因为计算机内存中存储的数据是小端序,所以实际上是从右边开始读字符串的,因此转16位后每两位逆序

编写python代码用于转换如下

while 1:
    # 如357761762382
    str = hex(int(input("get source:")))
    str_hex = str[2:]
    str_hex_re = []
    for i in range(0, len(str_hex), 2):
        str_hex_re.append(str_hex[i:i+2])
    str_hex_re.reverse()
    print(str_hex_re)
    # 输出结果为实际的字符值如['4e', '44', '43', '4c', '53']

    for i in str_hex_re:
        print(chr(int(i, 16)), end="")
    print()
    # 转为字符为如 NDCLS

对应到代码中

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;
  //根据函数名猜测join:text为key3和v9连接的值
  //text经双击查询是kills,而v9在初始化处是hadow连接后就是killshadow
  text = (char *)join(key3, &v9);
  strcpy(key, key1);//key = ADSFK(key1双击查询值)
  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;
  }
  printf("Please input your flag:", src);
  while ( 1 )
  {
    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;
      }
      else
      {
        str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
      }
      if ( !(v3 % v5) )
        putchar(32);
      ++v2;
    }
  }
  //str2是输入值的处理结果和text一样则输入值就是flag,因此flag就是text值对应的str2处理的逆过程
  if ( !strcmp(text, str2) )
    puts("Congratulation!\n");
  else
    puts("Try again!\n");
  return __readfsqword(0x28u) ^ v12;
}

主函数代码解析

因此我转换为了可执行的.cpp文件,用于理解测试,加上代码阅读加注释如下

#include<bits/stdc++.h>
using namespace std;

int main(){
  char key[]="ADSFKNDCLS";
  int v2 = 0;
  int v3 = 0;
  int v5 = strlen(key);
  for (int i = 0; i < v5; ++i )
  {
    if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
      key[i] = key[v3 % v5] + 32;
    ++v3;
  }
  //输出:adsfkndcls
  //根据输出结果可以知道这段代码就是小写用的
  cout<<key<<endl;
  
  char str2[100];
  printf("Please input your flag:"); 
  while ( 1 )
  {
    int v1 = getchar();
    if ( v1 == 10 ) //换行终止循环
      break;
    if ( v1 == 32 ) //空格 下标v2++
    {
      ++v2;
    }
    else
    {
      if ( v1 <= 96 || v1 > 122 )
      {
        if ( v1 > 64 && v1 <= 90 ) //输入值∈(64,96]∪ (122,90]
          str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
      }
      else //输入值∈(96,122]
      {
        str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
      }//以上这个if语句即输入值∈(64,90]就执行str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
      if ( !(v3 % v5) )
        putchar(32);
      ++v2;
    }
  }
  
    cout<<str2<<endl;

  char text[]="killshadow";
  if ( !strcmp(text, str2) )
    puts("Congratulation!\n");
  else
    puts("Try again!\n");
  
    return 0; 
}

因此核心的转换语句为

str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;

编写逆向破解代码

尝试逆向,现在就是已知str2=text="killshadow"求原本每个v1,又由以上代码分析可以知道v1∈(64,90],编写逆向破解代码

#include <bits/stdc++.h> 
using namespace std;

int main(){
    char key[105]="ADSFKNDCLS";
    int v2 = 0;
    int v3 = 0;
//    getchar();
    int v5 = strlen(key);
    for (int  i = 0; i < v5; ++i )
    {
        if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
              key[i] = key[v3 % v5] + 32;
            ++v3;
    }
    cout<<key<<endl<<v3<<endl;//adsfkslcdn

    char text[105]="killshadow";
    char flag[105],p=-1;
    memset(flag, 0, sizeof(flag));
    char tmp;
    for(int i=0; i<strlen(text); i++){
        for(int j=97; j<=122; j++){

        }
        for(int j=0; j<=128; j++){
            if((j>64&&j<=90)){//||(j>96&&j<=122)){
                //flag[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
                tmp = (j - 39 - key[v3 % v5] + 97) % 26 + 97;
                if(tmp==text[i]){
                    flag[i]=j;
                    printf("【%d】:%c %d | %c %d \n", i, j, j, tmp, tmp);
                }
            }
        }
        v3++;
    }
    cout<<flag<<endl;//KLDQCUDFZO
    return 0;
}

因此答案即为flag{KLDQCUDFZO}

技术总结

  1. 可以尝试运行主函数代码来赋值理解含义
  2. 找到关键的转换式加上1可以编写暴力破解代码
Last modification:March 12, 2021
如果觉得我的文章对你有用,请随意赞赏。咖啡(12RMB)进度+100%,一块巧克力(1RMB)进度+6%。
(赞赏请备注你的名称哦!后台记录中来自理工小菜狗)