首页 > 其他分享 >BUUCTF Reverse题解:第一部分(已完结)

BUUCTF Reverse题解:第一部分(已完结)

时间:2024-09-02 11:49:31浏览次数:12  
标签:BUUCTF Reverse esp int 题解 v16 a1 flag ebp

Welcome to C12AK's Re journal !


目录


题目传送门


前言

现在是我大一的暑假,这篇文章将记录我在这个假期做的题。

我上大学之前,只对网安有一些模糊的认识,第一次听说“CTF”这个东西,还是和一起转专业的同学闲聊的时候听他说的。直到开始写这篇文章的前一周,我还:
· 没学过关于开发的任何知识(前后端、Web等)
· 有一本CTF书,完全看不懂
· 对各种所谓“入门课”里提到的概念和工具一无所知(不仅没用过,根本就没听说过)
· 上网搜索入门教程,除了“CTF”,不知道该搜什么关键词
· 不知道常用的练习平台(就比如BUUCTF)

完全零基础入门真是太难了。于是我写了这篇文章,供自己整理思路,也希望能帮大家轻松入门。


1. easyre

下载IDA,把题目文件拖进去,在左侧函数栏找到main函数,按F5反编译,直接拿到flag。
具体操作看这里,如果还是不会,再看这里

回到顶部


2. reverse1

进入main函数,发现嵌套了一层main_0。

双击进入main_0:

分析这段代码,意思是,首先有一个字符串Str2,把Str2中所有ascii值为111的位置改为ascii值为48,也就是把所有的'o'替换为'0'(第17到24行)。然后输入Str1,如果Str1和修改后的Str2相同,那么Str1就是flag。
双击Str2查看:

可以看到Str2是hello_world。把o换成0,变成hell0_w0rld。这就是flag。

回到顶部


3. reverse2

还是先查看main,发现代码逻辑和reverse1基本相同,也是一个字符串经过字符替换得到flag。于是采取相同操作直接通过。

回到顶部


4. 内涵的软件

进入main_0,眼前直接就是一个字符串v5,下面有一堆操作。

把这些sub函数挨个点进去看,好像与flag无关啊!由于v5形似flag,但大括号外并非“flag”,于是猜测大括号内就是base64加密过的flag。
使用base64加解码器将大括号内的内容解码:

这啥啊!于是把v5直接包上flag{}提交,还是不对。最后尝试把“DBAPP”换成“flag”直接提交。
过了。6。

[补充]:这道题可能是搬运的,“DBAPP”是原比赛的flag格式,所以要改成“flag”。

回到顶部


5. 新年快乐

首先查壳,发现文件带壳:

用upx脱壳

再查一次壳,果然没了。好神奇
然后把文件拖入IDA并查看main函数:

显然Str2就是flag。

回到顶部


6. xor

查看main函数,发现,如果一个字符串v5经过“从前到后,每一位都异或上它的前一位”的操作之后,与golbal相同,那么v5就是flag。

由于异或的性质 a^b^b = a^0 = a,对global从后往前再跑一遍异或就能得到flag。
查看global:

此处还有一点:global的第一位是f,证明目前的判断没有错。

然后写脚本进行上述操作,得到flag:

回到顶部


7. reverse3

小蒟蒻被这题卡了一整天,后来才惊奇地发现,前面放那个视频的最后一段就是这个题……

在IDA里查看主函数:

依次检查每个sub函数,大致梳理代码逻辑如下:
1.清空Destination(19~24行);
2.对输入的Str进行一些操作,将操作后的字符串赋值给v4,然后又将v4赋值给Destination(25~29行);
3.Destination中每个字符的ascii值都加上它的下标(32~33行);
4.如果此时Destination与Str2相同,那么它就是flag。

然后查看Str2:

也就是说,将这个东西按上述过程逆向操作,就能得到flag。那就先把Str2的每个位置减去下标:

然后看看28行的函数是什么操作。点开:

int __fastcall sub_411AB0(int a1, int a2, _BYTE *a3, unsigned int a4, int *a5)
{
  int v5; // edx
  int v6; // ecx
  int v8; // [esp+D4h] [ebp-38h]
  int v9; // [esp+D4h] [ebp-38h]
  int v10; // [esp+D4h] [ebp-38h]
  int v11; // [esp+D4h] [ebp-38h]
  int i; // [esp+E0h] [ebp-2Ch]
  unsigned int v13; // [esp+ECh] [ebp-20h]
  int v14; // [esp+ECh] [ebp-20h]
  int v15; // [esp+ECh] [ebp-20h]
  void *v16; // [esp+F8h] [ebp-14h]
  _BYTE *v17; // [esp+104h] [ebp-8h]

  if ( a3 && a4 )
  {
    v13 = a4 / 3;
    if ( (int)(a4 / 3) % 3 )
      ++v13;
    v14 = 4 * v13;
    *a5 = v14;
    malloc(v14 + 1);
    v16 = (void *)sub_411127(v6, v5);
    if ( v16 )
    {
      j_memset(v16, 0, v14 + 1);
      v17 = a3;
      v15 = a4;
      v8 = 0;
      while ( v15 > 0 )
      {
        byte_41A144[2] = 0;
        a1 = 1;
        byte_41A144[1] = 0;
        a2 = 1;
        byte_41A144[0] = 0;
        for ( i = 0; i < 3 && v15 >= 1; ++i )
        {
          a1 = (int)v17;
          LOBYTE(a2) = *v17;
          byte_41A144[i] = *v17;
          --v15;
          ++v17;
        }
        if ( !i )
          break;
        switch ( i )
        {
          case 1:
            *((_BYTE *)v16 + v8) = aAbcdefghijklmn[(int)(unsigned __int8)byte_41A144[0] >> 2];
            v9 = v8 + 1;
            *((_BYTE *)v16 + v9) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | (16 * (byte_41A144[0] & 3))];
            a2 = ++v9;
            *((_BYTE *)v16 + v9) = aAbcdefghijklmn[64];
            a1 = (int)v16 + ++v9;
            LOBYTE(a2) = aAbcdefghijklmn[64];
            *((_BYTE *)v16 + v9) = a2;
            v8 = v9 + 1;
            break;
          case 2:
            *((_BYTE *)v16 + v8) = aAbcdefghijklmn[(int)(unsigned __int8)byte_41A144[0] >> 2];
            v10 = v8 + 1;
            *((_BYTE *)v16 + v10) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | (16 * (byte_41A144[0] & 3))];
            a2 = (int)v16 + ++v10;
            *((_BYTE *)v16 + v10) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | (4 * (byte_41A144[1] & 0xF))];
            a1 = (int)v16 + ++v10;
            LOBYTE(a2) = aAbcdefghijklmn[64];
            *((_BYTE *)v16 + v10) = a2;
            v8 = v10 + 1;
            break;
          case 3:
            *((_BYTE *)v16 + v8) = aAbcdefghijklmn[(int)(unsigned __int8)byte_41A144[0] >> 2];
            v11 = v8 + 1;
            *((_BYTE *)v16 + v11) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | (16 * (byte_41A144[0] & 3))];
            *((_BYTE *)v16 + ++v11) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | (4 * (byte_41A144[1] & 0xF))];
            a2 = (int)v16 + ++v11;
            *((_BYTE *)v16 + v11) = aAbcdefghijklmn[byte_41A144[2] & 0x3F];
            a1 = v11 + 1;
            v8 = v11 + 1;
            break;
        }
      }
      *((_BYTE *)v16 + v8) = 0;
    }
  }
  return sub_411127(a1, a2);
}

我去,这么多。不过它用了很多次 aAbcdefghijklmn 这个数组,那就看看它又是什么。点开:

原来是base64码表。看来把刚才得到的字符串进行base64解码就能拿到flag。

我也爱你!

回到顶部


8. helloworld

这是一道android逆向,可以用apkide或者apktool反编译。下图是用apkide打开题目文件后,输出框显示的内容:

正常来讲在apkide中直接可以查看.smali输出,但打开.smali时出现了错误,那就按照输出目录查看主函数,在这个位置:

就是这个MainActivity.smali,可以用各种方式打开,比如记事本,然后就看到了flag:

回到顶部


9. 不一样的flag

打开一看确实不太一样:

“选择一个方向,向上下左右行走”,合理猜测是一个东西在地图上行走,且v3[25]表示行数,v4表示列数。按照这个猜测,再看看具体的行走细节:

“如果v3[25] >= 5或者v3[29] >= 5,那就退出;如果v7[ 5*v3[25] - 41 + v4 ]是'1',就退出;如果v7[同上]是'#',就输出正确并退出。”

v3[29]是什么呢?查看前面声明变量的部分,可以看到v3是一个长为28的数组,所以v3[29]其实就是v4(“v4”这个名字就是IDA给命名的,之所以叫v4,是因为它在内存中的地址紧跟在v3后面)。如果上面的猜想成立,那么这部分意思就是“如果行数或列数不在0到4,那么就退出”。再加上v7下标中的“5*v3[25]”和“+v4”(尽管没看懂“v7”和“-41”),以及v3有值的长度正好是25,基本就可以确定 本题是在一个5*5的地图中行走,并且 flag是一个由1234组成的字符串,按照它可以从v3[0]开始,经过若干个非'1'的位置,走到一个值为'#'的位置

那就将v3写成5*5的形式,从左上角走到'#',依此写出flag。

*1111
01000
01010
00010
1111#

flag{222441144222}

回到顶部


10. SimpleRev

进入主函数分析代码:

意思是,如果输入D或者d就执行Decry函数,否则退出程序。
然后分析Decry函数:

由于大小端存储问题[待补],伪代码窗口下显示的字符串都是实际的字符串经过反转得到的,而汇编窗口下显示的字符串与实际的相同,也就是说src和v9[0]要进行反转。这样我们就通过Decry的前半部分得到了两个字符串text和key。接下来看后半部分:

这部分就是输入一个字符串,它经过一系列处理得到str2,如果str2和text相同,那么它就是flag。既然已知text,我们可以直接反推出flag。

回到顶部


11. luck_guy

就不细说了,直接快进到能直接接触到flag的地方:

这是循环执行操作,每次执行一系列操作中的某一个,看来如果按顺序执行了某几个操作,就可以拿到flag。case1显然是拿到flag的最后一步,分析case1代码可知,flag是f1和f2拼接得到的。点击查看f1,它是“GXY{do_not_”,但f2看不到,看来需要通过其他操作得到。case2和case3显然没什么用,case4是在f2后面拼接一个s,这里s和上一题一样需要反转,是“icug`of0x7F”(长度为8)。再看case5,它是对f2进行一个操作,且操作的下标恰好是0到7。那就可以猜测case4应该是在case5之前进行的。对case4得到的f2(也就是s)进行case5的操作,得到:

很好,这很像flag的后半部分。所以把f1和这个东西拼起来,然后把原比赛格式的“GXY”改为此题要求的“flag”,提交,通过。

回到顶部


12. Java逆向解密

下载文件后发现后缀名是.class,是java文件,要用jd-gui反编译。
在jd-gui中查看java代码:

flag的每一位都加上64^0x20可以得到key。即可编写脚本解出此题:

回到顶部


13. JustRE

看到题名里大写的RE,我才意识到RE还可以表示Runtime Error来着……
我不要RE!!!qwq

言归正传,打开文件……

这啥啊?完全看不懂,要不先点开这个东西看看?

原来这里没有flag啊。那不做了。
再看看捷径?

6。那看看getflag?

6。要不看看Help能不能帮我?

铭记在心!
现在大概知道这软件是干啥的了,再来看代码……还是看不懂。要不查看一下字符串,看看有没有和flag相关的?

翻到最下面,发现一个疑似flag的字符串。双击定位,再按X跳转到引用位置,再按F5反编译包含这个字符串的函数:

没学过这些东西,但大致可以看出sprintf输出的就是flag。与printf同理,把两个“%d”分别换成19999和0,应该就是flag。当然,“BJD”也要换成“flag”,尽管BJD万岁
然后一提交就过了。

回到顶部


14. 刮开有奖

用IDA打开并进入WinMain函数。

再双击进入DialogFunc:

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 ( 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 = sub_401000(v18, strlen(v18));
      memset(v18, 0, 0xFFFFu);
      v18[1] = String[3];
      v18[0] = String[2];
      v18[2] = String[4];
      v5 = 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 ( a3 != 1 && a3 != 2 )
    return 0;
  EndDialog(hDlg, a3);
  return 1;
}

发现其中有一行输出“You get it!”,往上看一点,又发现被比较的字符串是String,那么String就应该是flag了。而判断当前的String是否为flag,需要用到v7[0], v10, v8, v13, v4, v5,那就再往上寻找如何得到这些值。观察到两个函数sub_4010F0和sub_401000。点击查看sub_4010F0,发现这是对v7的前11个元素的加密,且加密方式比较难以分析。但由于v7[0]到v7[11]都已给出(根据IDA的变量命名规则,v8就是v7[2],v9就是v7[3],依此类推),我们可以直接在本地复现这个函数,得到加密后的v7。
这里要注意两点:
1.IDA对函数形参的类型判断有误,通过分析比较容易发现sub_4010F0的参数a1应该是int*类型。
2.IDA中*(a1+i)是指从a1开始偏移i个字节,所以IDA中的(a1 + 4i)实际上相当于a1[i],在本地复现时需要去掉函数中所有的“ 4 * ”(我在经历了无数次错误提交后,看了别人的题解才发现这一点)。

然后分析sub_401000,发现是base64加密,且返回值是v18的起始地址(关于这个返回值,本蒟蒻分析了好久……),所以,结合v18的赋值,可以确认v4和v5分别是经过base64加密的String[5 : 7]和String[2 : 4]。对“ak1w”和“V1Ax”进行解密,可得v4为“jMp”,v5为“WP1”。最后用v7求出String[0 : 1]即可。

本地总代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int 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 = 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;
    sub_4010F0(a1, a2, i - 1);
    result = a3;
    ++i;
  }
  return result;
}

int main(){
    //第一部分:解出v7
    int v7[11] = {90,74,83,69,67,97,78,72,51,110,103};
    sub_4010F0(v7, 0, 10);
    for(int i = 0; i < 11; i++) cout << (char)v7[i];
    cout << endl;

    //第二部分:解出String
    char str0 = v7[0] + 34;
    char str1 = v7[4];
    cout << str0 << str1 << "WP1" << "jMp" << endl;
    return 0;
}

回到顶部


15. 简单注册器

又是一道.apk的题,这次用jadx-gui备用下载地址)来做。

进入主函数,发现第60行输出了一个“flag{*}”格式的字符串,可知“public void onClick”这部分应当是得到flag的关键。这部分有一个字符串xx,如果满足一定条件,就会对另一个字符串x进行一些操作,得到flag。由于flag的得出与xx无关,那我们就不考虑xx。又因为x的原始形式已经给出,所以可以直接在本地写脚本求得flag:

回到顶部


结语

我要开学了!啊啊啊啊啊啊啊啊啊啊啊我不想开学qwq
写博客毕竟要花费不少时间,以后的话,如果一道题让我学到了新东西,那还是会记录在博客里,但平常的题就不写题解了。
这篇文章有点长了,就先完结吧。敬请期待第二部分。

回到顶部

标签:BUUCTF,Reverse,esp,int,题解,v16,a1,flag,ebp
From: https://www.cnblogs.com/qjsswz/p/18392428

相关文章

  • CF2008场题解
    Sakurako'sExam算法:模拟,分类讨论。题意简述:给\(a\)个数字\(1\)和\(b\)个数字\(2\),问能否在每个数字前加上加减号使得原始值为\(0\)。考虑\(1\)的个数如果是奇数,那么一定不行。否则如果\(2\)的个数是偶数,一定可以。当\(2\)的个数为奇数且还可能可以时,判断是否存......
  • ABC369F F - Gather Coins 题解
    题目链接:https://atcoder.jp/contests/abc369/tasks/abc369_f题目大意:在一个\(H\timesW\)的二维迷宫中有\(N\)枚硬币,其中第\(i\)枚硬币在\((R_i,C_i)\)(本题中,我们用\((r,c)\)表示二维迷宫中从上到下第\(r\)行从左到右第\(c\)列的那个格子)。你一开始在迷宫的左......
  • 如何解决《罗马2全面战争》中的twitchsdk_32_release.dll错误模块跳出问题?实用技巧与
    当您启动《罗马2全面战争》时,可能会遇到与twitchsdk_32_release.dll相关的错误提示,这可能导致游戏无法正常运行。本篇文章将深入探讨这一问题的原因以及提供多种解决方法,帮助您顺利启动游戏。twitchsdk_32_release.dll错误模块跳出的原因twitchsdk_32_release.dll文件出现......
  • 【题解】Solution Set - 「蓝」题板刷
    【题解】SolutionSet-「蓝」题板刷始于:2024/9/1(其实之前大概做了十来道,但是没有记录。估计题会比较多,如果从代码里面过来的话,建议直接Ctrl/⌘+F题目名称。「USACO18JAN」MooTubeG2024/9/1一句话题意:https://www.luogu.com/discuss/125319给定一棵有边权的树,每次询......
  • 题解:AT_abc257_d [ABC257D] Jumping Takahashi 2
    [ABC257D]JumpingTakahashi2博客食用更佳:Myblog。大体思路这题是一道二分题,因为\(S\)越大,就越容易满足题目要求,\(S\)越小,就越难满足题目要求,符合二分的特点。下面先贴上二分代码。LLl=0,r=1e10;while(l<r){intmid=l+r>>1;if(check(mid))......
  • [USACO2.4] 两只塔姆沃斯牛 The Tamworth Two--记忆化题解
    题目复述:链接跳转:[USACO2.4]两只塔姆沃斯牛TheTamworthTwo-洛谷#[USACO2.4]两只塔姆沃斯牛TheTamworthTwo##题目描述两只牛逃跑到了森林里。FarmerJohn开始用他的专家技术追捕这两头牛。你的任务是模拟他们的行为(牛和John)。追击在$10\times10$的平面网......
  • [蓝桥杯 2020 省 A1] 超级胶水--题解
    题目再现:链接跳转:[蓝桥杯2020省A1]超级胶水-洛谷#[蓝桥杯2020省A1]超级胶水##题目描述小明有$n$颗石子,按顺序摆成一排,他准备用胶水将这些石子粘在一起。 每颗石子有自己的重量,如果将两颗石子粘在一起,将合并成一颗新的石子,重量是这两颗石子的重量之和。为......
  • [蓝桥杯 2016 省 A] 密码脱落--最长公共子序列题解
    题目复述:题目链接:[蓝桥杯2016省A]密码脱落-洛谷#[蓝桥杯2016省A]密码脱落##题目描述X星球的考古学家发现了一批古代留下来的密码。这些密码是由A、B、C、D四种植物的种子串成的序列。仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的回文串)。......
  • 「NOI2022 D2T2 冒泡排序」题解
    题意uoj768构造长为\(n\)的序列\(a\),满足\(m\)条限制:\(\min_{j=L_i}^{R_i}\{a_j\}=V_i\),要求逆序对数最少题解21pts暴力先进行一些观察:逆序对只关心相对大小,所以\(\foralla_j\)必然\(\in\{V_i\}\),可以完全离散化经典结论:若\(i<j,a_i>a_j\)且交换后合法,则交换......
  • 【题解】Solution Set - NOIP2024模拟赛4
    【题解】SolutionSet-NOIP2024模拟赛4https://www.becoder.com.cn/contest/5501T2沉默乐团https://www.becoder.com.cn/submission/2593352T3深黯「军团」记录一下考场思路:首先对于长度为\(n\)所有排列,按顺序求出她的逆序对数量。然后找到了规律。后面基于此,写出......