攻防世界RE练习区题目总结(1-10)

前言
这篇文章是我做完攻防世界练习区的题目后对这十道题做的总结。emmm,其实这几道题我几个月前就做完了,这几天又回去试着再做一遍发现还是有一些地方存在疑惑,并且速度也没有太大的提升。所以决定把之前做过的题目整理一下都写在博客里,便于以后复习顺便把一些我没搞懂的地方也记录在这里,希望以后可以解决。也希望能有大佬有缘看到我这个小菜鸟的文章,可以帮我指点迷津。
这一篇只写攻防世界新手区的几道题,之后我还会把BUUCTF做的题也做个总结,写一篇博客,希望可以把这个习惯保持下去。说了这么多废话,下面开始正文。

第一题:open-source


首先把附件下载下来,在exeinfope里查壳(虽然第一道题基本上没有加壳的可能性,但我觉得这是个很好的习惯) 在这里插入图片描述 可见这个文件没有加壳。这个文件的后缀是.c,所以直接在IDE里打开,这里我用的是DEVC++
#include <stdio.h>
#include <string.h>

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

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

unsigned int second = atoi(argv[2]);
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 = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;

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

}`
由最后的两个printf语句可以看出flag就是hash,由%x可以看出是十六进制表示。
所以向上看对hash进行运算的只有

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

可知hash的值与first,second,argv[3]的值有关,从上面的3个if语句可以判断出:当if()里的条件为真时,都会提示错误并退出程序,所以3个if()里的条件都为假,所以可以推测出下列条件;first=0xcafe,second % 5 != 3&&second % 17 == 8(最小的second是25),argv[3]=h4cky0u(所以strlen(argv[3])=7)。将上述条件带入计算hash的那段代码可以得出hash,下面是我写的代码`#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
int first=0xcafe;
int second=25;
int a=7;

int hash=0;
hash=first * 31337 + (second % 17) * 11 + 7 - 1615810207;

printf("%x",hash);
return 0;

}`
结果是在这里插入图片描述
所以flag就是c0ffee。
第一题拿下!

第二题:simple-unpack


由题目描述可以看出这是一个加壳的文件,所以先拖到exeinfope里查壳

在这里插入图片描述
可以看出加了UPX这一类壳。所谓加壳就是为了隐藏程序真正的OEP(入口点),所以为了正常地打开程序使程序正常运行,我们必须脱壳。我选择的是用upx脱壳。
在这里插入图片描述
脱壳成功!关于upx脱壳我之前看到一篇博客讲得挺详细的,我把超链接放在下面。
使用upx脱壳工具脱壳
脱壳结束后再把脱壳后的文件放在exeinfope里看一下。

可以看到现在已经没有UPX壳了,之后把文件拉到IDA里打开。

在这里插入图片描述
按F5反汇编后发现flag相当可疑,双击flag追踪过去看到
在这里插入图片描述
发现了flag{Upx_1s_n0t_a_d3liv3r_c0mp4ny}。
第二题拿下!

第三题:logmein


下载附件后老规矩拿到exeinfope里查壳,

在这里插入图片描述
发现没壳,而且是64位的文件,所以放到IDA64里静态分析。ctrl+f搜索main函数,再按F5反汇编,如图

在这里插入图片描述
双击sub_4007F0看到

在这里插入图片描述
说明输入正确的flag会让程序输出上述printf的一句话并退出。
再双击sub_4007C0看到

在这里插入图片描述
所以当输入错误的flag时会使程序输出上述一句话并退出。
所以不能让程序运行sub_4007C0这个函数,所以*s[i] = (char)(*((_BYTE )&v7 + i % v6) ^ v8[i])
必须成立。去上面的代码找到v7,v6,v8[]

在这里插入图片描述
看出v6=7 , v8[]=:"AL_RT^L*.?+6/46 , v7=28537194573619560LL,将28537194573619560LL转换成字符,如图在这里插入图片描述
因为IDA是小段法存储,所以真正的v7应该是ebmarah倒过来变成harambe。下面是我写的解题代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char v8[50]=":\"AL_RT^L*.?+6/46";
	char v7[50]="harambe";
	int v6 =7;
	char flag[50];
	for ( int i = 0; i < strlen(v8); ++i )
  {
     flag[i] = (char)(*((unsigned char*)&v7 + i % v6) ^ v8[i]) ;
  }
    printf("%s",flag);
	return 0;
}

结果为

在这里插入图片描述
第三题拿下!

第四题:insanity


老规矩先把附件下载后放在exeinfope里查壳

在这里插入图片描述
看出文件无壳且是32位的,用IDA打开

在这里插入图片描述
发现整段代码没有什么运算,感觉&strs有点可疑,双击进去

在这里插入图片描述
发现9447{This_is_a_flag}…感觉简单得有点过分

又去字符串窗口看了一下
在这里插入图片描述
flag确实就是这个了(难道这才是第一题?)

第五题:python-trade


老规矩查壳

在这里插入图片描述
无壳是用python写的。发现IDA打开之后无法反汇编,所以当时我去找了一些博客,发现是要用python反汇编,这里我用的是在线Python反汇编,超链接我放在下面。

在线Python反编译工具
在这里插入图片描述
推测出当flag在encode函数中加密后与correct相等就成功。

在这里插入图片描述
由这一段看出将correct通过base64解码后,经过xor异或可以再得出原来的flag。
下面是我写的解题代码

import base64

correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
the_flag = base64.b64decode(correct)

flag=''
for i in the_flag:
    x =(ord(chr(i))-16) ^ 32
    flag+=chr(x)

print(flag)

得出结果nctf{d3c0mpil1n9_PyC}

第五题拿下!

第六题:re1


老规矩先查壳

在这里插入图片描述
无壳且是32位,拉到IDA里反汇编

在这里插入图片描述
双击aFlag_0进去

在这里插入图片描述
发现aFlag_0意思是flag错误,双击unk_413E90进去

在这里插入图片描述
发现显示的是flag get,所以正确的flag会使程序进行unk_413E90

所以if()里的条件要为假才可以不进行aFlag_0,所以v3要为0
因为v3是由 *v3 = strcmp((const char )&v5, &v9) 决定的,当v5=v9时v3才=0,发现v9由 **_mm_storeu_si128((__m128i )&v5, _mm_loadu_si128((const __m128i )&xmmword_413E34))决定,
发现
xmmword_413E34
有点可疑,双击进去

在这里插入图片描述

猜测v9就等于xmmword的值,将3074656D30633165577B465443545544h转换成字符

在这里插入图片描述
因为IDA是小端法,所以这一串字符应该反过来输出,即为DUTCTF{We1c0met0}
一看就知道这就是flag

第六题拿下!

第七题:game


老规矩下载之后放到exeinfope里查壳

在这里插入图片描述
发现无壳且是32位的exe文件,发现是exe可执行文件,直接双击打开

在这里插入图片描述
这一看我就乐了,经典点灯小游戏,简单地说就是12345678分别控制1234578这8盏灯及其相邻的灯的灭亮(1和8也算作相邻的),了解过这个游戏的都知道直接按12345678的顺序点一遍就可以全部点亮了。全点亮后如下图

在这里插入图片描述
……发现flag真的出来了。

下面放在IDA里看一下,发现下面这一段很明显是判断灯是否全亮的

在这里插入图片描述
双击sub_457AB4()进去,最后发现一段循环异或,猜测这就是flag的算法

在这里插入图片描述
写一段代码把v2到v58,v59到v115的值分别放在2个数组里,再进行异或运算,最后输出结果。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main()
{
	
	int a[57]={123,32,18,98,119,108,65,41,124,80,125,38,124,111,74,49,83,108,94,108,84,6,96,83,44,121,104,110,32,95,117,101,99,123,127,119,96,48,107,71,92,29,81,107,90,85,64,12,43,76,86,13,114,1,117,126,0};
	int b[57]={18,64,98,5,2,4,6,3,6,48,49,65,32,12,48,65,31,78,62,32,49,32,1,57,96,3,21,9,4,62,3,5,4,1,2,3,44,65,78,32,16,97,54,16,44,52,32,64,89,45,32,65,15,34,18,16,0};
	
	for (int i = 0; i < 57; ++i )
  {
    a[i] ^= b[i];
    a[i] ^= 0x13u;
    printf("%c",a[i]);
  }
	return 0;
}

(把这些数据拿到数组里真是太麻烦了,本人太菜,一个个复制粘贴过去的,有大佬知道什么快速的方法希望能告诉我 (~ ̄▽ ̄)~)

有个地方我想说一下,本来我的代码是把printf放到for循环之外的,我打算一次性输出整个字符串,但是控制台老是显示NULL,我到现在也不清楚是为什么,最后只能一个一个字符输出了,有大佬知道为什么的话希望能告知 o()o。

最后结果如下图

在这里插入图片描述
第七题拿下!

第八题:Hello,CTF


老规矩下载附件拖到exeinfope查壳

在这里插入图片描述
发现无壳且是32位的exe文件,啧啧,一看又是exe我想不会又可以不写代码直接玩出flag吧,结果我双击进去发现只有一句话:please input your serial:……emmm还是老老实实的IDA打开吧。ctrl+F查找main之后按F5反汇编,然后直接去找判断成功的代码如下

在这里插入图片描述
看出v3要大于等于17,并且v10与v13要相同才可以进行aSuccess。继续往上看

在这里插入图片描述

要输入v9,并且v9的长度要小于等于17,然后在do循环里把v9[v3]赋给v4,发现sprintf很像printf,猜测是输出函数,感觉asc_408044可疑,双击进去发现

在这里插入图片描述
有%x表示十六进制,猜测sprintf就是把v4用十六进制表示,再往上看

在这里插入图片描述
发现v13=437261636b4d654a757374466f7246756e,所以flag应该就是把437261636b4d654a757374466f7246756e用十六进制表示出来,为了方便我就直接用在线转换工具把它转换成16进制了,结果如下。

在这里插入图片描述
flag就是CrackMeJustForFun
第八题拿下!

第九题:no-strings-attached


老规矩下载附件拉到exeinfope查壳。

在这里插入图片描述
发现无壳且是32位的,拉到IDA里去反汇编,发现main函数反汇编后只有这些东西

在这里插入图片描述
发现authenticate是证实的意思,所以猜测flag与authenticate有关系,双击进去看见

在这里插入图片描述
发现最后的if-else与判断胜负的函数很像,双击unk_8048B44进去

在这里插入图片描述
发现有Success,估计正确的flag就可以进入unk_8048B44
发现wcscmp和strcmp很像估计作用也是一样的,就是当ws=s2的时候就可以进入if了,再往上看发现decrypt函数,decrypt是翻译的意思,猜测是加密函数,双击进去。

在这里插入图片描述
发现这段代码先把s的值复制给dest,然后将dest的每个值减去a2的每个值,然后返回值是dest,从原函数可知dest的值就是之前的s2。接下来就是要找出传入的s和a2的值,即原来的s和dword_8048A90,双击原来的dword_8048A90进去,看见了s和dword_8048A90的值,分别选中它们的值后按Shift+E,选择C无符号字符数组(十进制),将值复制下来准备写解题代码时用。记得最后的4个0不要复制,因为字符串默认结尾为0。

在这里插入图片描述
在这里插入图片描述
下面是我写的解题代码。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main()
{
	int a2[]={1,20,0,0,2,20,0,0,3,20,0,0,4,20,0,0,5,20,0,0};
    int s[]={58,20,0,0,54,20,0,0,55,20,
			0,0,59,20,0,0,128,20,0,0,
			122,20,0,0,113,20,0,0,120,20,
			0,0,99,20,0,0,102,20,0,0,
			115,20,0,0,103,20,0,0,98,20,
			0,0,101,20,0,0,115,20,0,0,
			96,20,0,0,107,20,0,0,113,20,
			0,0,120,20,0,0,106,20,0,0,
			115,20,0,0,112,20,0,0,100,20,
			0,0,120,20,0,0,110,20,0,0,
			112,20,0,0,112,20,0,0,100,20,
			0,0,112,20,0,0,100,20,0,0,
			110,20,0,0,123,20,0,0,118,20,
			0,0,120,20,0,0,106,20,0,0,
			115,20,0,0,123,20,0,0,128,20,
			0,0,0,0,0,0,83,0,0,0,
			117,0,0,0,99,0,0,0,99,0,
			0,0,101,0,0,0,115,0,0,0,
			115,0,0,0,33,0,0,0,32,0,
			0,0,87,0,0,0,101,0,0,0,
			108,0,0,0,99,0,0,0,111,0,
			0,0,109,0,0,0,101,0,0,0,
			32,0,0,0,98,0,0,0,97,0,
			0,0,99,0,0,0,107,0,0,0,
			33,0,0,0,10,0,0,0};
			
			int x=0,i=0,j=0;
			while ( i < 156 ){
    		x=s[i++]-a2[j++%20];//写EXP的要点是把字符都转换成整数,并且最后一个一个地将整数换成字符输出 ,之前也提过了,为了加深我自己的印象,又啰嗦了一遍
//    		if(x>32){
    			printf("%c",x);
//			}
  			}
// 这里我把if(x>32)给注释掉了,如果直接用这段代码,会发现结果的字符之间会有很多的空格,为了消除这些空格,我设置了x>32这个条件,因为空格的ASCII代码对应的十进制是32
	return 0;
}

最后删去空格的结果如下

在这里插入图片描述
第九题拿下!

第十题:maze


l老规矩拿到附件之后查壳

在这里插入图片描述
发现无壳且是64位的文件,根据题目描述这应该是一道迷宫题。关于迷宫题,我当时做完这道题之后专门去找了一些资料学习,其中有一篇博客讲的很好,由题目也有解析,我把超链接和网址放在下面。
逆向迷宫题总结
网址:https://blog.csdn.net/weixin_50549897/article/details/110633105

用IDA打开文件,ctrl+F查找main函数,F5反汇编之后,看了看发现有4个if语句,因为是迷宫题,所以推测这四个if就是用来判断上下左右的,把if()里的数字都换成字符,发现4个方向键是 ‘O’ , ‘o’ , ‘.’ , ‘0’ 。发现四个if语句最后都会到LABEL_14,一直追踪下去,发现如下代码

在这里插入图片描述

发现**if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != ‘#’ )**应该就是判断是否到达终点的函数,双击asc_601060进去发现如下图在这里插入图片描述
数了数空格,*,#一共有64个字符,推测应该是8x8的迷宫
四个if语句进行的函数代码如下

bool __fastcall sub_400650(_DWORD *a1)//对应O
{
  int v1; // eax

  v1 = (*a1)--;
  return v1 > 0;
}
bool __fastcall sub_400660(int *a1)//对应o
{
  int v1; // eax

  v1 = *a1 + 1;
  *a1 = v1;
  return v1 < 8;
}
bool __fastcall sub_400670(_DWORD *a1)//对应.
{
  int v1; // eax

  v1 = (*a1)--;
  return v1 > 0;
}
bool __fastcall sub_400680(int *a1)//对应0
{
  int v1; // eax

  v1 = *a1 + 1;
  *a1 = v1;
  return v1 < 8;
}

可以看出上下和左右应该分别由 ‘O’,‘o’ 和 ‘.’ ,'0’控制,并且发现有>0和<8来控制边界所以可以确定这个迷宫就是8x8的迷宫了。
因为O和o传入的参数是+1的,4个方向函数传入的参数都是4个字节的,所以O和o应该是高位4字节,而.和0应该是低位4字节,推测高位决定行低位决定列,所以推测O和o应该决定左右,.和0应该决定上下,所以上下左右分别为 ‘.’ , ‘0’ , ‘O’,‘o’ .方向搞定。。再往上看发现如下代码

puts("Input flag:");
  scanf("%s", &s1, 0LL);
  if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5uLL) || *(&byte_6010BF + 24) != '}' )

可以看出输入的s1就是flag,并且flag的长度等于24,并且开头前5个字符就是 “nctf{”,并且结尾的字符是 ‘}’。所以能够操作的步数是并且只能是24-6=18步,下面是我写的打印迷宫的代码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	char v[100]="  *******   *  **** * ****  * ***  *#  *** *** ***     *********";
	printf("%d\n",strlen(v));
	for(int i=0;i<64;i++)
	{
		printf("%c",v[i]);
		if((i+1)%8==0)
		{
			printf("\n");
		}
	}
	return 0;
}

打印出来为

在这里插入图片描述
其中*为墙壁,空格为可走的路径。走几次迷宫就可以判断出来走法了,走法为:右下右右下下左下下下右右右右上上左左,所以flag=nctf{o0oo00O000oooo…OO}。
第十题拿下!

总结


首先当时做题时遇到的最大问题就是见过的题目太少,基本每一道题都是新的题型,媒体都要去查资料。所以我认为,想要提高自己的技术,最重要的就是要都做题,关键是做不同类型的题,不然很容易遇到无从下手的情况。
还有两道题我还要再琢磨一下,这次再次回顾做题的时候发现那两道题思路还是不太清晰,所以我这次没有写进来。等之后有空我就会写出来的。

后续要学习的东西,在此立个flag

1.脱壳方法与技术
目前脱壳我只会用upx脱壳,但我之前在找资料的时候看到很多大佬都是用的ollydbg动态调试来脱壳的,之后我肯定是要重点学习一下的。
2.相关汇编基础打牢
没有系统地专门学习过汇编,只是在网上找资料学会了一些皮毛,对于堆栈的理解还是不够深刻,之后也要找时间打打牢。
3.做题做题再做题
果然做题才是积累经验的好方法,之后题目肯定不能断
4.博客继续写
这次回顾攻防世界的题目让我再次对自己的记性产生了怀疑,果然好记性不如烂笔头,之后每做完一些题我都要写一下博客。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值