xctf社区题解2-reverse新手(新手看的,大佬绕道)

文章目录

      • reverse-新手模式:
        • re1:
        • game:
        • Hello, CTF
        • open-source
        • simple-unpack
        • RC3 CTF 2016
        • insanity
        • no_strings_attached
        • csaw2013reversing2
        • maze

  • status: 写完了新手模式,后面慢慢接触更深的吧。(20190819)

reverse-新手模式:

re1:

IDA中F5查看反汇编之后的C代码:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第1张图片
这里将输入的字符串放在变量v11中,然后与v7对比,v7和v8是连续的栈中变量,先后存放flag的一部分,找到给变量赋值的地方:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第2张图片
然后用python处理字符串输出:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第3张图片

game:

载入ida分析,找到得出flag的地方,分析一下,程序预设两个一样长度(56)的字符串,通过异或的方法恢复出flag,关键部分如下:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第4张图片
依据这个,写出python脚本来得到flag:

#! usr/bin/python3
str0 = [0x12,0x40,0x62,0x5,0x2,4,6,3,6,0x30,0x31,0x41,0x20,0x0C,0x30,0x41,0x1F,0x4E,0x3E,
        0x20,0x31,0x20,1,0x39,0x60,3,0x15,9,4,0x3E,3,5,4,1,2,3,0x2C, 0x41, 0x4E, 0x20,0x10,
        0x61,0x36, 0x10, 0x2C,0x34, 0x20, 0x40, 0x59, 0x2D, 0x20, 0x41, 0x0F, 0x22, 0x12,
        0x10]
str1 = [0x7b,0x20,0x12,0x62,0x77,0x6c,0x41,0x29,0x7c,0x50,0x7D,0x26,0x7c,0x6f,0x4a,0x31,0x53,
        0x6c,0x5e,0x6c,0x54,6,0x60,0x53,0x2c,0x79,0x68,0x6e,0x20,0x5f,0x75,0x65,0x63,0x7b,
        0x7f,0x77,0x60,0x30,0x6b,0x47,0x5c,0x1d,0x51,0x6b,0x5a,0x55,0x40,0x0c,0x2b,0x4c,
        0x56,0x0d,0x72,1,0x75,0x7e]
for i in range(56):
    str1[i] ^= str0[i]
    str1[i] ^= 0x13
for i in str1:
    print (chr(i),end='')

执行得到flag:
flag

Hello, CTF

直接拖入IDA分析,经过相关分析并注释如下:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第5张图片
查看v13的具体内容:
在这里插入图片描述
又是16进制转ASCII码:
在这里插入图片描述

open-source

题目直接给了源码,不难,依次满足给定的命令行参数条件即可,不过第一个参数好像构造不出来,因为0xcafe已经超出ascii的表示范围了,所以就直接改源码,将最后计算hash的那句代码改了,构造其他3个就行了。
修改源代码:注释对第一个参数的判断,修改最后hash的计算

#include 
#include 
#include 

int main (int argc, char *argv[]) {
	
	if (argc != 4) {
		printf ("what?\n");
		exit (1);
	}

	/*unsigned int first = atoi (argv[1]);
	printf ("%x\n", first);
	if (first != 0xcafe) {
		printf ("you are wrong, sorry.\n");
		exit (2);
	}*/

	unsigned int second = atoi (argv[2]);
	/*printf ("%d\n", first);*/
	if (second % 5 == 3 || second % 17 != 8) {
		printf ("ha, you won't get it!\n");
		exit (3);
	}

	if (strcmp ("h4cky0u", argv[3])) {
		printf ("so close, dude!\n");
		exit (4);
	}

	printf ("Brr wrrr grr\n");

	unsigned int hash = 0xcafe * 31337 + (second % 17) * 11 + strlen (argv[3]) - 1615810207;

	printf ("Get your key: ");
	printf ("%x\n", hash);
	return 0;
}

xctf社区题解2-reverse新手(新手看的,大佬绕道)_第6张图片
构造参数得到flag:
在这里插入图片描述

simple-unpack

ELF二进制程序,题目提示加壳,最近遇到的几个逆向的题也是加壳的,upx加壳和aspack加壳,在网上找的一些脱壳的软件大部分不能成功脱壳,应该是要学会自己手动脱壳吧,想起上学期老师说的脱壳技术,真的是头大啊,很菜,都没做出来,只能拿这个来攒攒信心了。
使用010 Editor二进制编辑器查看,文件类型:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第7张图片
有显示upx!说明是被upx加壳了,之前说的遇到的被upx加壳的软件这样打开来看的话会是upx0, upx1,如果是WindowsPE程序,可以使用PEid来看,这里的这个是ELF程序,是Unix上的二进制可执行程序,以后应该会遇到更多的吧。
所以我们就是用upx来脱壳,upx可以到SourceForge上去下,是一个GitHub上的项目,一直在不断的更新。下载解压之后,进入到exe文件所在目录打开命令行,就可以脱壳了,upx的具体命令用法可以查看下载的readme文件,都说的很清楚。
如下图:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第8张图片
这里我是把upx.exe的路径设为环境变量了,所以可以直接使用。
然后使用ida64位打开就可以直接看到flag了:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第9张图片

RC3 CTF 2016

好了,这回这个没有加壳了,是一个64位的ELF文件,拖入IDA分析,F5查看伪代码:

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  size_t v3; // rsi@1
  int i; // [sp+3Ch] [bp-54h]@3
  char s[36]; // [sp+40h] [bp-50h]@1
  int v6; // [sp+64h] [bp-2Ch]@1
  __int64 v7; // [sp+68h] [bp-28h]@1//var_28
  char v8[8]; // [sp+70h] [bp-20h]@1
  int v9; // [sp+8Ch] [bp-4h]@1

  v9 = 0;
  strcpy(v8, ":\"AL_RT^L*.?+6/46");
  v7 = 28537194573619560LL;//注意这里
  v6 = 7;
  printf("Welcome to the RC3 secure password guesser.\n", a2, a3);
  printf("To continue, you must enter the correct password.\n");
  printf("Enter your guess: ");
  __isoc99_scanf("%32s", s);
  v3 = strlen(s);
  if ( v3 < strlen(v8) )
    wrong();
  for ( i = 0; i < strlen(s); ++i )
  {
    if ( i >= strlen(v8) )
      wrong();
    if ( s[i] != (char)(*((_BYTE *)&v7 + i % v6) ^ v8[i]) )//关键点
      wrong();
  }
  success();
}

提示输入flags变量中去,然后在用一个for循环判断是否正确,比较简单,flag就是v7数组和v8数组的字符异或,但是往上看v7显示是一个长整型的值,跟踪过去进入汇编代码:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第10张图片
上图红色标记处把数据段的qword_40080数据传入raxrax再赋值给本地变量var_28,而由上面的伪代码,我们知道这个就是v7。我们再继续到数据段中去看:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第11张图片
使用R键将数据转换成字符形式,得到:
在这里插入图片描述
这里我就跟着去写python代码来恢复flag了,但是恢复出来提交不对。
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第12张图片
在这里插入图片描述
显然,结果也没有什么意义,虽然有些题的flag可能没有什么意义。
后来(如果不去看提示的话估计我会卡很久),比对了v8的赋值,才知道这是因为小端存放的原因,低字节的数据存放在高地址上面,如下面的两张图,即就是v8的赋值和数据存放的方式:
在这里插入图片描述
在这里插入图片描述
于是v7的值也需要把顺序倒过来,即:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第13张图片
在这里插入图片描述
这样flag就出来了,发现cygwinWindows下用着是真的方便啊,现在电脑上面可以使用的命令行就有power shell, cmd, git bash,cygwin

insanity

疯狂的;照例打开下载的文件,010Editor查看是ELF文件,所以使用IDA打开,发现F5不管用了,看了一下main函数,也没有什么逻辑,陷入尴尬境地。查看Strings窗口,发现一个类似flagflag
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第14张图片
这一看就不像真正的flag,况且还有其他字符串的信息。
结果真香,后来找思路的时候回到题目提示中去看,菜鸡觉得前面的太难了,来个简单的,结果就是把上面的flag提交就可以了。套路,都是套路!

no_strings_attached

题目提示运行程序就能拿flag,elf程序,kali中运行,显示段错误:
在这里插入图片描述
使用ida打开,查看导出表,有一个decrypt,解密的,再使用gdb调试设置断点在这儿:
在这里插入图片描述
然后使用r命令运行程序,就会在断点处停下,然后单步步过n,使用info reg查看寄存器的值,这时解密的结果应该在寄存器eax中了:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第15张图片
eflags寄存器为0x282,'x/282 $eax’查看eax的内容(这里查看文档的,还不太懂,以后懂了回来再写):
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第16张图片
红框中的数据即为flag

在这里插入图片描述

csaw2013reversing2

exe文件,使用ida打开,提示有调试信息,猜想后续可能和调试有关:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第17张图片
查看主函数的graph图:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第18张图片
在这之前已经试运行过这个程序,弹出了一个对话框,标题为flag,可是显示的是乱码:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第19张图片
仔细分析之后,程序会先使用IsDebuggerPresent函数判断程序是否在调试器中运行,是的话调到解密函数,否则弹框输出乱码的flag,仔细来看是在调试器中运行后的这一部分:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第20张图片
有一个int 3 中断,然后[ebp+lpMem]是存放乱码flag的地址,赋值给edx作为后续处理,接着调用函数sub_401000,一开始我不知道这是解密函数,进去看了之后才知道的,sub_401000函数分析出来是将乱码的flag4个字节为一组与0xAABBCCDD异或得到可识别的flag,本来想写一个脚本来解出flag的,但是想试试调试的过程。
使用OD打开exe文件,因为程序本来就有中断,所以运行几次之后就找到了将要调用解密函数的位置:
在这里插入图片描述
如上图,地址000B109E处就是调用解密函数的指令,在这里下一个断点,单步步过之后调到下一条指令,此时flag的地址是存放在edx中的,查看edx的值,为02D005B8
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第21张图片
到内存中去找,就可以看到flag了:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第22张图片

maze

注:学会了一个单词,maze-迷宫。
一个月前看着很难,现在看着就很简单了,函数的功能分析起来也容易得多。(2019.08.19)
下面是ida中分析的结果:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed __int64 i; // rbx
  signed int a_ch; // eax
  bool flag; // bp
  bool ret_value; // al
  const char *prompt; // rdi
  int *rows; // [rsp+0h] [rbp-28h]

  rows = 0LL;
  puts("Input flag:");
  scanf("%s", &input, 0LL);
  if ( strlen(&input) != 24 || strncmp(&input, "nctf{", 5uLL) || *(&byte_6010BF + 24) != '}' )
  {
wrong:
    puts("Wrong flag!");
    exit(-1);
  }
  i = 5LL;
  if ( strlen(&input) - 1 > 5 )
  {
    while ( 1 )
    {
      a_ch = *(&input + i);                     // a_ch = input[i]
      flag = 0;
      if ( a_ch > 'N' )
      {
        a_ch = (unsigned __int8)a_ch;
        if ( (unsigned __int8)a_ch == 'O' )
        {
          ret_value = left((_DWORD *)&rows + 1);// 列数减一
          goto set_flag;
        }
        if ( a_ch == 'o' )
        {
          ret_value = right((int *)&rows + 1);  // 列数加一
          goto set_flag;
        }
      }
      else
      {
        a_ch = (unsigned __int8)a_ch;
        if ( (unsigned __int8)a_ch == '.' )
        {
          ret_value = up(&rows);                // 行数减1
          goto set_flag;
        }
        if ( a_ch == '0' )
        {
          ret_value = down((int *)&rows);       // 行数加1
set_flag:
          flag = ret_value;
          goto LABEL_15;
        }
      }
LABEL_15:
      if ( !(unsigned __int8)is_in_maze((__int64)maze, SHIDWORD(rows), (int)rows) )
        goto wrong;
      if ( ++i >= strlen(&input) - 1 )          // 走完所有迷宫
      {
        if ( flag )
          break;
LABEL_20:
        prompt = "Wrong flag!";
        goto LABEL_21;
      }
    }
  }
  if ( maze[8 * (signed int)rows + SHIDWORD(rows)] != '#' )// cols = SHIWORD(rows)
    goto LABEL_20;
  prompt = "Congratulations!";
LABEL_21:
  puts(prompt);
  return 0LL;
}
/* Orphan comments:
高四位存放的是当前列数
*/

目的是找到’#'字符所在的位置,将maze的数据提取出来,实际上就是一个char数组,按照8*8排列,如下:
xctf社区题解2-reverse新手(新手看的,大佬绕道)_第23张图片
一开始处于(0,0)处,程序中用一个int64数来表示当前的位置,高位int表示的是列数,低位int表示的是行数。这样就很容易分析其中具体函数的作用是改变行数还是列数了(如果你是真小白不懂的话找我问,我也是从完全不懂的小白过来的),分析出来输入的字符和对应的操作应该是:

left:O
right:o
up:.
down:0

再结合前面的一些判断的信息就可以得到flag了。
在这里插入图片描述

你可能感兴趣的:(writeup)