Buuctf 不一样的flag

拖进IDA:

f5查看伪代码

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  _BYTE v3[29]; // [esp+17h] [ebp-35h] BYREF
  int v4; // [esp+34h] [ebp-18h]
  int v5; // [esp+38h] [ebp-14h] BYREF
  int i; // [esp+3Ch] [ebp-10h]
  _BYTE v7[12]; // [esp+40h] [ebp-Ch] BYREF

  __main();
  v3[26] = 0;
  *(_WORD *)&v3[27] = 0;
  v4 = 0;
  strcpy(v3, "*11110100001010000101111#");
  while ( 1 )
  {
    puts("you can choose one action to execute");
    puts("1 up");
    puts("2 down");
    puts("3 left");
    printf("4 right\n:");
    scanf("%d", &v5);
    if ( v5 == 2 )
    {
      ++*(_DWORD *)&v3[25];
    }
    else 
        if ( v5 > 2 )
    	{
      		if ( v5 == 3 )
      		{
        	--v4;
      		}
      		else
      		{
        		if ( v5 != 4 )
LABEL_13:
          		exit(1);
        		++v4;
      		}
    	}
    	else
    	{
      		if ( v5 != 1 )
        	goto LABEL_13;
      		--*(_DWORD *)&v3[25];
    	}
    for ( i = 0; i <= 1; ++i )
    {
        // 如果i=0看的就是v3[25]的值,如果i=1看的就是v4的值
      if ( *(_DWORD *)&v3[4 * i + 25] >= 5u )   
        	exit(1);
    }
      // 寻址找到v3[0]-v3[24]的值并且判断是不是0,下面的if判断是不是#号
    if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == 0x31 )
      exit(1);
    if ( v7[5 * *(_DWORD *)&v3[25] - 0x29 + v4] == 0x23 )
    {
      puts("\nok, the order you enter is the flag!");
      exit(0);
    }
  }
}

根据一些大佬写的wp能够一眼看出这是一道迷宫题目,5x5的地图就是“*11110100001010000101111#”即为

*1111
01000
01010
00010
1111#

也就是控制*走到#且不能遇到1,根据代码显示1,2,3,4分别为up,down,left,right,那么flag就是
flag{222441144222}

探究本质:记录几个点
++*(_DWORD *)&v3[25];

这句代码,当用户第一次输入为2的时候,这里的加++加的是单位下标为25的v3,也就是esp+30的内存数据

loc_4013EA:
mov     eax, [esp+30h]
inc     eax
mov     [esp+30h], eax
jmp     short loc_401417

这段代码当i=0是用来判断v3[25]的值是否在控制范围,也就是上一步的++的值,当i=1,v3[4+25],但v3中的数据只有29位也就是下标0-28,下标29即为esp+34,v4的值,而这个if就是为了接下来的2个if判断做的准备。

for ( i = 0; i <= 1; ++i )
    {
        // 如果i=0看的就是v3[25]的值,如果i=1看的就是v4的值
      if ( *(_DWORD *)&v3[4 * i + 25] >= 5u )   
        	exit(1);
    }

程序的核心代码即为这一段。

if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == 0x31 )// 寻址找到v3[0]-v3[24]的值并且判断是不是0,下面的if判断是不是#号
      exit(1);
    if ( v7[5 * *(_DWORD *)&v3[25] - 0x29 + v4] == 0x23 )
    {
      puts("\nok, the order you enter is the flag!");
      exit(0);

对应的汇编:

loc_40147D:
mov     edx, [esp+30h]
mov     ecx, [esp+34h]
mov     eax, edx
shl     eax, 2
add     eax, edx
lea     esi, [esp+40h]
add     eax, esi
add     eax, ecx
sub     eax, 29h ; ')'
mov     al, [eax]
cmp     al, 23h ; '#'
jnz     loc_40136D

用了一段不知道怎么回事的计算,最终v7的下标为负数,如果从esp+40内存段中的数据向前数,会发现负下标的值刚好是v3[]存储地图的那段值的位置,最终判断是1还#,如果是1退出,#即走到终点,而第一步的那些代码的中间的部分就是来改变v4和v3[25]的值,这样v7计算出的负数下标就无法按照预期进行。

总结:

F5的伪代码不完全正确,且非常难以理解,多看汇编才是王道。

(仅为个人学习记录,如有错误,欢迎指正)

posted @ 2022-12-01 16:37  一个半  阅读(113)  评论(0)    收藏  举报