攻防世界reverse-box

题目下载:下载

这道题有两种方法,一个是写c来解,另一个是用gdb脚本。

经查wp知道,这道题攻防世界中少给了条件:

挑战描述
$ ./reverse_box ${FLAG}
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
flag格式:TWCTF{}

也就是你运行这个文件的时候,输入一字符串(flag),然后会回显'95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a'

载入IDA攻防世界reverse-box_第1张图片

发现有个if语句,其中a1,a2是main函数的形参

一般来说,我们写main函数,就定义main(),它的参数部分看似没有,实际上,main函数也是有参数的,只是在我们写时省略了。

main函数的参数由三部分组成,分别是argc(int型,表示命令行参数的个数)、argv[](char数组型,指向命令行的每一个命令参数)、envp[](char数组型,指向环境变量)。

说明:argc包括程序本身,所以它大于等于1。argv以NULL结尾,且也包括程序本身,所以它的元素个数大于等于2。
—摘自https://blog.csdn.net/smell201611010513/article/details/80130941
 

所以当我们运行可执行文件不进行输入的时候,即a1==1,会输出特定的东西"./reverse_box"攻防世界reverse-box_第2张图片

 而当输入一些东西的时候就会经过sub_804858D(v4)变化,并且发现每次输出不太一样攻防世界reverse-box_第3张图片

既然题目要求是输入flag就会回显特定的字符串,那一定是经过如下操作:攻防世界reverse-box_第4张图片

即输入作为下标,在v4当中索引一个值然后输出,又因为输入一定有'TWCTF{}',所以可以推断v4[84]=0x95,v4[87]=0xee,v4[67]=0xaf,v4[84]=0x95,v4[70]=0xef,v4[123]=0x94,v4[125]=0x4a,这可以作为后面爆破的一个条件。

那进入sub_804858D(v4)看看变化:

int __cdecl sub_804858D(_BYTE *a1)
{
  unsigned int v1; // eax
  char v2; // al
  char v3; // al
  int v4; // ecx
  int v5; // eax
  int v6; // ecx
  int v7; // eax
  int v8; // ecx
  int v9; // eax
  int v10; // ecx
  int result; // eax
  char v12; // [esp+1Ah] [ebp-Eh]
  unsigned __int8 v13; // [esp+1Bh] [ebp-Dh]
  char v14; // [esp+1Bh] [ebp-Dh]
  char v15; // [esp+1Bh] [ebp-Dh]
  int v16; // [esp+1Ch] [ebp-Ch]

  v1 = time(0);
  srand(v1);
  do
    v16 = rand();                               // 如果是0的话会继续rand()
  while ( !v16 );
  *a1 = v16;                                    // v16一定不是0
  v12 = 1;
  v13 = 1;
  do
  {
    if ( v12 >= 0 )
      v2 = 0;
    else
      v2 = 27;
    v12 ^= (2 * v12) ^ v2;
    v14 = (4 * ((2 * v13) ^ v13)) ^ (2 * v13) ^ v13;
    v15 = (16 * v14) ^ v14;
    if ( v15 >= 0 )
      v3 = 0;
    else
      v3 = 9;
    v13 = v15 ^ v3;
    v4 = v13;
    LOBYTE(v4) = __ROR1__(v13, 7);
    v5 = v4 ^ (v13 ^ *a1);
    v6 = v13;
    LOBYTE(v6) = __ROR1__(v13, 6);
    v7 = v6 ^ v5;
    v8 = v13;
    LOBYTE(v8) = __ROR1__(v13, 5);
    v9 = v8 ^ v7;
    v10 = v13;
    LOBYTE(v10) = __ROR1__(v13, 4);
    result = v10 ^ v9;
    a1[v12] = result;
  }
  while ( v12 != 1 );
  return result;
}

 发现有一个rand()函数(这是不是和前面同一输入但不同输出有关呢),然后在经过一系列的操作就能得到v4数组,又因为v16不同所以得到的v4数组也不同,所以就要根据前面的条件v4[84]=0x95,v4[87]=0xee,v4[67]=0xaf,v4[84]=0x95,v4[70]=0xef,v4[123]=0x94,v4[125]=0x4a爆破符合条件的v4数组。

还有一个疑问就是rand生成的数范围是多少?看一看汇编代码,攻防世界reverse-box_第5张图片

 所以应该是0-255之间,所以根据给的伪代码可以写出c,求出v4数组

#include 
#include 
#include 
int __ROR1__(unsigned __int8 value,int x){
	value= (value>>x) | (value<<(8-x));
	return value;	
} 

int main(){
	unsigned int v1; // eax
	char v2; // al
	char v3; // al
	int v4; // ecx
	int v5; // eax
	int v6; // ecx
	int v7; // eax
	int v8; // ecx
	int v9; // eax
	int v10; // ecx
	int result; // eax
	char v12; // [esp+1Ah] [ebp-Eh]
	unsigned __int8 v13; // [esp+1Bh] [ebp-Dh]
  	char v14; // [esp+1Bh] [ebp-Dh]
  	char v15; // [esp+1Bh] [ebp-Dh]
  	int v16; // [esp+1Ch] [ebp-Ch]
	unsigned __int8 a1[256]={0};
	int i;
  	for(i=1;i<256;i++){
  		memset(a1,0,sizeof(a1));
  		a1[0] = i;
  		v12 = 1;
  		v13 = 1;
  		do
  		{
    		if ( v12 >= 0 )
      			v2 = 0;
    		else
      			v2 = 27;
    		v12 ^= (2 * v12) ^ v2;
    		v14 = (4 * ((2 * v13) ^ v13)) ^ (2 * v13) ^ v13;
    		v15 = (16 * v14) ^ v14;
    		if ( v15 >= 0 )
      			v3 = 0;
    		else
      			v3 = 9;
    		v13 = v15 ^ v3;
    		v4 = v13;
    		v4 = __ROR1__(v13, 7);
    		v5 = v4 ^ (v13 ^ *a1);
    		v6 = v13;
    		v6 = __ROR1__(v13, 6);
    		v7 = v6 ^ v5;
    		v8 = v13;
    		v8 = __ROR1__(v13, 5);
    		v9 = v8 ^ v7;
    		v10 = v13;
    		v10 = __ROR1__(v13, 4);
    		result = v10 ^ v9;
    		a1[v12] = result;
  		}while ( v12 != 1 );
  		if(a1[84]==0x95 && a1[87]==0xee && a1[67]==0xaf && a1[84]==0x95 && a1[70]==0xef && a1[123]==0x94 && a1[125]==0x4a){
            printf("找到了正确的数组,此时的随机值i为:%d\n",i);
            break;
        }
  	} 
  	for(i=0;i<256;i++){
  		if(i%16==0)
        	printf("\n");
        printf("0x%02x ",a1[i]);
        
	}
}

//找到了正确的数组,此时的随机值i为:214

0xd6 0xc9 0xc2 0xce 0x47 0xde 0xda 0x70 0x85 0xb4 0xd2 0x9e 0x4b 0x62 0x1e 0xc3
0x7f 0x37 0x7c 0xc8 0x4f 0xec 0xf2 0x45 0x18 0x61 0x17 0x1a 0x29 0x11 0xc7 0x75
0x02 0x48 0x26 0x93 0x83 0x8a 0x42 0x79 0x81 0x10 0x50 0x44 0xc4 0x6d 0x84 0xa0
0xb1 0x72 0x96 0x76 0xad 0x23 0xb0 0x2f 0xb2 0xa7 0x35 0x57 0x5e 0x92 0x07 0xc0
0xbc 0x36 0x99 0xaf 0xae 0xdb 0xef 0x15 0xe7 0x8e 0x63 0x06 0x9c 0x56 0x9a 0x31
0xe6 0x64 0xb5 0x58 0x95 0x49 0x04 0xee 0xdf 0x7e 0x0b 0x8c 0xff 0xf9 0xed 0x7a
0x65 0x5a 0x1f 0x4e 0xf6 0xf8 0x86 0x30 0xf0 0x4c 0xb7 0xca 0xe5 0x89 0x2a 0x1d
0xe4 0x16 0xf5 0x3a 0x27 0x28 0x8d 0x40 0x09 0x03 0x6f 0x94 0xa5 0x4a 0x46 0x67
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

最后在根据条件给的那一串字符串,找到在v4数组中的位置就可以了

list =  [0xd6,0xc9,0xc2,0xce,0x47,0xde,0xda,0x70
	,0x85,0xb4,0xd2,0x9e,0x4b,0x62,0x1e,0xc3
	,0x7f,0x37,0x7c,0xc8,0x4f,0xec,0xf2,0x45
	,0x18,0x61,0x17,0x1a,0x29,0x11,0xc7,0x75
	,0x02,0x48,0x26,0x93,0x83,0x8a,0x42,0x79
	,0x81,0x10,0x50,0x44,0xc4,0x6d,0x84,0xa0
	,0xb1,0x72,0x96,0x76,0xad,0x23,0xb0,0x2f
	,0xb2,0xa7,0x35,0x57,0x5e,0x92,0x07,0xc0
	,0xbc,0x36,0x99,0xaf,0xae,0xdb,0xef,0x15
	,0xe7,0x8e,0x63,0x06,0x9c,0x56,0x9a,0x31
	,0xe6,0x64,0xb5,0x58,0x95,0x49,0x04,0xee
	,0xdf,0x7e,0x0b,0x8c,0xff,0xf9,0xed,0x7a
	,0x65,0x5a,0x1f,0x4e,0xf6,0xf8,0x86,0x30
	,0xf0,0x4c,0xb7,0xca,0xe5,0x89,0x2a,0x1d
	,0xe4,0x16,0xf5,0x3a,0x27,0x28,0x8d,0x40
	,0x09,0x03,0x6f,0x94,0xa5,0x4a,0x46,0x67]

flag = ""
s = "95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a";
for i in range(0, len(s), 2):
    s1 = int(s[i:i+2], 16)
    flag += chr(list.index(s1))
print (flag)
#TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}

方法二:gdb脚本https://blog.csdn.net/Onlyone_1314/article/details/120242744?ops_request_misc=&request_id=&biz_id=102&utm_term=%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8Creverse-box&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-120242744.142^v74^insert_down4,201^v4^add_ask,239^v2^insert_chatgpt&spm=1018.2226.3001.4187

gdb脚本第一次看,所以只能搬运别人的代码,自己来理解理解。

这里有2种方法,执行gdb脚本,一:直接输入defien命令,输入指令,最后执行。二:单独写成一个脚本文件使用 source 来执行。这里用source 脚本名来写。

分析一下汇编,看一看随机数和v4数组都存在哪里

攻防世界reverse-box_第6张图片攻防世界reverse-box_第7张图片

 

 所以esp+eax+1ch是数组的地址,但是当输出数组第一个值时,eax是0,所以数组初始地址是esp+1ch,所以根据这些写gdb脚本命名test.sh。

#设置从0到255的随机值爆破
set $i=0
set $total=256
while($i<$total)
  #在生成随机值之后的地址下第一个断点
  b *0x80485b1
  #在main函数中printf输出的位置下第二个断点
  b *0x8048707
  #运行程序,并输入参数"TWCTF"
  run TWCTF
  #$i变量的值递加1
  set $i=$i+1
  #程序在第一个断点停下来的时候,把当前的随机值赋值给eax寄存器
  set $eax=$i
  #继续运行程序
  continue
  #程序在第二个断点停下来的时候,判断此时的输出的第一个16进制值是否是0x95
  if ($eax==0x95)
	#如果是,则认为找到了正确的解,输出此时的随机值
    print $i
	#查看起始地址从esp+0x1c开始的256个16进制字节的内容,这里就是存放数组的地方
    x/256xb $esp+0x1c
	#令变量的值为256,退出循环
    set $i=256
  end
  stop
end
end

 运行:攻防世界reverse-box_第8张图片

 不断回车攻防世界reverse-box_第9张图片

 在根据给出的题目的回显值写flag即可。

TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}

你可能感兴趣的:(re简单题,c++,gdb,ctf,re)