0
点赞
收藏
分享

微信扫一扫

buu [2019红帽杯]xx1 wp

90哦吼 2022-01-13 阅读 21
c++

首先用exeinfo打开它,发现是无壳的64位程序。

逆向先静后动,因此先用ida 打开:

main函数代码如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned __int64 v3; // rbx
  __int64 v4; // rax
  __int128 *v5; // rax
  __int64 v6; // r11
  __int128 *v7; // r14
  int v8; // edi
  __int128 *v9; // rsi
  char v10; // r10
  int v11; // edx
  __int64 v12; // r8
  unsigned __int64 v13; // rcx
  __int64 v14; // rcx
  unsigned __int64 v15; // rax
  unsigned __int64 i; // rax
  _BYTE *v17; // rax
  size_t v18; // rsi
  _BYTE *v19; // rbx
  _BYTE *v20; // r9
  int v21; // er11
  char *v22; // r8
  __int64 v23; // rcx
  char v24; // al
  signed __int64 v25; // r9
  __int64 v26; // rdx
  __int64 v27; // rax
  size_t Size; // [rsp+20h] [rbp-48h]
  __int128 v30; // [rsp+28h] [rbp-40h]
  int v31; // [rsp+38h] [rbp-30h]
  int v32; // [rsp+3Ch] [rbp-2Ch]
  int input_str[4]; // [rsp+40h] [rbp-28h]
  int v34; // [rsp+50h] [rbp-18h]

  *(_OWORD *)input_str = 0i64;
  v34 = 0;
  sub_1400018C0(std::cin, (__int64)argv, (__int64)input_str);
  v3 = -1i64;
  v4 = -1i64;
  do
    ++v4;
  while ( *((_BYTE *)input_str + v4) );
  if ( v4 != 19 )                               // 输入长度为19字节
  {
    sub_140001620(std::cout, "error\n");
    _exit((unsigned __int64)input_str);
  }
  v5 = (__int128 *)operator new(5ui64);
  v6 = *(_QWORD *)&Code;                        // 这个是qwertyuiopa....
  v7 = v5;
  v8 = 0;
  v9 = v5;
  do
  {
    v10 = *((_BYTE *)v9 + (char *)input_str - (char *)v5);
    v11 = 0;
    *(_BYTE *)v9 = v10;
    v12 = 0i64;
    v13 = -1i64;
    do
      ++v13;
    while ( *(_BYTE *)(v6 + v13) );             // v13=code_len-1
    if ( v13 )
    {
      do
      {
        if ( v10 == *(_BYTE *)(v6 + v12) )
          break;
        ++v11;
        ++v12;
      }
      while ( v11 < v13 );
    }
    v14 = -1i64;
    do
      ++v14;
    while ( *(_BYTE *)(v6 + v14) );
    if ( v11 == v14 )
      _exit(v6);
    v9 = (__int128 *)((char *)v9 + 1);       // v5的前4个等于input前四个,并且应该为code中的值
  }
  while ( (char *)v9 - (char *)v5 < 4 );
  *((_BYTE *)v5 + 4) = 0;                       // v5[4]=0
  do
    ++v3;
  while ( *((_BYTE *)input_str + v3) );         // v3=input_len
  v15 = 0i64;
  v30 = *v7;
  while ( *((_BYTE *)&v30 + v15) )
  {
    if ( !*((_BYTE *)&v30 + v15 + 1) )
    {
      ++v15;
      break;
    }
    if ( !*((_BYTE *)&v30 + v15 + 2) )
    {
      v15 += 2i64;
      break;
    }
    if ( !*((_BYTE *)&v30 + v15 + 3) )
    {
      v15 += 3i64;
      break;
    }
    v15 += 4i64;
    if ( v15 >= 0x10 )
      break;
  }
  for ( i = v15 + 1; i < 0x10; ++i )
    *((_BYTE *)&v30 + i) = 0;                   // v30一直到后面持续为0,前面4个字节为 input的前四个字节
  v17 = sub_140001AB0((__int64)input_str, v3, (unsigned __int8 *)&v30, &Size);// 用19字节的原始输入 加上原始输入的前四个字节 加上len=19 最后返回24字节的输出
  v18 = Size;
  v19 = v17;
  v20 = operator new(Size);
  v21 = 1;
  *v20 = v19[2];
  v22 = v20 + 1;
  v20[1] = *v19;
  v20[2] = v19[3];
  v20[3] = v19[1];
  v20[4] = v19[6];
  v20[5] = v19[4];
  v20[6] = v19[7];
  v20[7] = v19[5];
  v20[8] = v19[10];
  v20[9] = v19[8];
  v20[10] = v19[11];
  v20[11] = v19[9];
  v20[12] = v19[14];
  v20[13] = v19[12];
  v20[14] = v19[15];
  v20[15] = v19[13];
  v20[16] = v19[18];
  v20[17] = v19[16];
  v20[18] = v19[19];
  v20[19] = v19[17];
  v20[20] = v19[22];
  v20[21] = v19[20];
  v20[22] = v19[23];
  for ( v20[23] = v19[21]; v21 < v18; ++v22 )   // init: v22=v20+1
  {
    v23 = 0i64;
    if ( v21 / 3 > 0 )
    {
      v24 = *v22;
      do
      {
        v24 ^= v20[v23++];
        *v22 = v24;
      }
      while ( v23 < v21 / 3 );
    }
    ++v21;
  }
  *(_QWORD *)&v30 = 0xC0953A7C6B40BCCEi64;
  v25 = v20 - (_BYTE *)&v30;
  *((_QWORD *)&v30 + 1) = 0x3502F79120209BEFi64;
  v26 = 0i64;
  v31 = 0xC8021823;
  v32 = 0xFA5656E7;
  do
  {
    if ( *((_BYTE *)&v30 + v26) != *((_BYTE *)&v30 + v26 + v25) )// 右边等于v20+v26
      _exit(v8 * v8);
    ++v8;
    ++v26;
  }
  while ( v26 < 24 );
  v27 = sub_140001620(std::cout, "You win!");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v27, sub_1400017F0);
  return 0;
}

下面一步一步来分析,下图中,首先是输入,随后通过v4判断是否是19字节,若是19字节,那么就继续,否则就error报错并且退出。 值得注意的是v6指针对应的字符串code,是qwertyui.....字符。这个命名很搞,一开始和input_str是重名的,看了半天看不懂,后来自己修改了变量名字。

往下走就是这一段了,这一段干了啥呢,其实就是检查输入的字符串是否在v6所示的qwertyu...中,其次将输入的前四个字节,存到v5中,并且v5[4]=0。

随后这个v30=*v7。v7是等于v5的,v5刚刚说到,就是input的前四个字节。v30是128位的,因此下面的循环也就是16个字节,最后得到的效果就是v30这个128位的变量,前4个字节是input的输入值,后面的12个字节都是0。

下面的部分很重要,一个是sub_140001AB0函数,函数结束之后,将得到的24字节输出进行调换顺序的操作。

首先研究这个函数,函数还是很复杂的,但是这里有一个关键的数值,0x61C88647,百度搜索了这个数值之后,发现是和tea加密算法相关,最后经过对比,发现是xxtea算法。

 贴上tea的代码:

#include <stdio.h>  
#include <stdint.h>  
#define DELTA 0x9e3779b9  
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))  
  
void btea(uint32_t *v, int n, uint32_t const key[4])  
{  
    uint32_t y, z, sum;  
    unsigned p, rounds, e;  
    if (n > 1)            /* Coding Part */  
    {  
        rounds = 6 + 52/n;  
        sum = 0;  
        z = v[n-1];  
        do  
        {  
            sum += DELTA;  
            e = (sum >> 2) & 3;  
            for (p=0; p<n-1; p++)  
            {  
                y = v[p+1];  
                z = v[p] += MX;  
            }  
            y = v[0];  
            z = v[n-1] += MX;  
        }  
        while (--rounds);  
    }  
    else if (n < -1)      /* Decoding Part */  
    {  
        n = -n;  
        rounds = 6 + 52/n;  
        sum = rounds*DELTA;  
        y = v[0];  
        do  
        {  
            e = (sum >> 2) & 3;  
            for (p=n-1; p>0; p--)  
            {  
                z = v[p-1];  
                y = v[p] -= MX;  
            }  
            z = v[n-1];  
            y = v[0] -= MX;  
            sum -= DELTA;  
        }  
        while (--rounds);  
    }  
}  
  
  
int main()  
{  
    uint32_t v[2]= {1,2};  
    uint32_t const k[4]= {2,2,3,4};  
    int n= 2; //n的绝对值表示v的长度,取正表示加密,取负表示解密  
    // v为要加密的数据是两个32位无符号整数  
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位  
    printf("加密前原始数据:%u %u\n",v[0],v[1]);  
    btea(v, n, k);  
    printf("加密后的数据:%u %u\n",v[0],v[1]);  
    btea(v, -n, k);  
    printf("解密后的数据:%u %u\n",v[0],v[1]);  
    return 0;  
}  

这个程序就是将19位的输入作为明文,然后将前四个字节作为密钥,使用xxtea进行加密,我们猜测密钥为flag。加密之后。将字符串打乱顺序。随后进入下一步:

上图就是一个简单的异或操作。可以写一个反向操作的函数即可: 

 

最后就是与一个24字节的字符串进行比较。 

下面附上用C语言写的脚本,由于是新手,写的比较乱。

#include<stdio.h>
#include<stdlib.h>
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void xxtea(uint32_t* v, int n, uint32_t* key)
{

	uint32_t y, z, sum;
	unsigned p, rounds, e;
	if (n > 1) // encrypt
	{

		rounds = 6 + 52 / n;
		sum = 0;
		z = v[n - 1];
		do
		{

			sum += DELTA;
			e = (sum >> 2) & 3;
			for (p = 0; p < n - 1; p++)
			{

				y = v[p + 1];
				z = v[p] += MX;
			}
			y = v[0];
			z = v[n - 1] += MX;
		} while (--rounds);
	}
	else if (n < -1) // decrypt
	{

		n = -n;
		rounds = 6 + 52 / n;
		sum = rounds * DELTA;
		y = v[0];
		do
		{

			e = (sum >> 2) & 3;
			for (p = n - 1; p > 0; p--)
			{

				z = v[p - 1];
				y = v[p] -= MX;
			}
			z = v[n - 1];
			y = v[0] -= MX;
			sum -= DELTA;
		} while (--rounds);
	}
}

int main() {
	unsigned char v20[30] = {
	0xce,0xbc,0x40,0x6b,0x7c,0x3a,0x95,0xc0,0xef,0x9b,0x20,0x20,0x91,0xf7,0x02,0x35,
	0x23,0x18,0x02,0xc8,0xe7,0x56,0x56,0xfa
	};
	unsigned char v19[30] = { 0xce,0xbc,0x40,0xa5,0xb2,0xf4,0xe7,0xb2,0x9d,0xa9,0x12,0x12,0xc8,0xae,0x5b,0x10,0x6,0x3d,0x1d,0xd7,0xf8,0xdc,0xdc,0x70 };

	int v21 = 1;
	unsigned char *v22 =v20+ 1;
	int v23;
	char v24;
/*
	for (; v21 < 24; ++v22)   // init: v22=v20+1
	{   v23 = 0;
		if (v21 / 3 > 0)
		{
			v24 = *v22;
			do
			{
				v24 ^= v20[v23++];
				*v22 = v24;
			} while (v23 < v21 / 3);
		}
		++v21;
	}
	*/

	v21 = 23; v22 = v20 + 23;
	for (; v21 >= 0; --v22)   // init: v22=v20+1
	{
		v23 = 0;
		if (v21 / 3 > 0)
		{
			v24 = *v22;
			do
			{
				v24 ^= v20[v23++];
				*v22 = v24;
			} while (v23 < v21 / 3);
		}
		--v21;
	}

	for (int i = 0; i < 24; i++) {
		printf("0x%x,", v20[i]);
	}
	//printf("%x %x %x %x", 'f','l','a','g');

	v19[2] = *v20;
	v19[0] = v20[1];
	v19[3] = v20[2];
	v19[1] = v20[3];
	v19[6] = v20[4];
	v19[4] = v20[5];
	v19[7] = v20[6];
	v19[5] = v20[7];
	v19[10] = v20[8];
	v19[8] = v20[9];
	v19[11] = v20[10];
	v19[9] = v20[11];
	v19[14] = v20[12];
	v19[12] = v20[13];
	v19[15] = v20[14];
	v19[13] = v20[15];
	v19[18] = v20[16];
	v19[16] = v20[17];
	v19[19] = v20[18];
	v19[17] = v20[19];
	v19[22] = v20[20];
	v19[20] = v20[21];
	v19[23] = v20[22];
	v19[21] = v20[23];
	printf("\n");
	for (int i = 0; i < 24; i++) {
		printf("%x,", v19[i]);

	}

	// 两个32位无符号整数,即待加密的64bit明文数据
	uint32_t v[7] = {
	0x12345678, 0x78563412 };
	// 四个32位无符号整数,即128bit的key
	uint32_t k[4] = {
	0x67616c66,0,0,0 };
	//n的绝对值表示v的长度,取正表示加密,取负表示解密

	for (int i = 0,j=0; i < 6; i++, j = j + 4) {
		unsigned int tt = (uint32_t)v19[j ] ;
		unsigned int tt1 = (uint32_t)v19[j + 1] << 8;
		unsigned int tt2 = (uint32_t)v19[j + 2] << 16;
		unsigned int tt3 = (uint32_t)v19[j + 3] << 24;
		v[i] = tt + tt1 + tt2 + tt3;

	}
	int n = 6;
//	printf("Data is : %x %x\n", v[0], v[1]);
//	xxtea(v, n, k);
//	printf("Encrypted data is : %x %x\n", v[0], v[1]);
	xxtea(v, -n, k);
	printf("\n");
	printf("Decrypted data is : %x %x %x %x %x %x %x %x\n", v[0], v[1],v[2],v[3],v[4],v[5],v[6],v[7]);
	printf("\n ");
	for (int i = 0; i < 6; i++) {
		char tt = (char)v[i];
		char tt1 = (uint32_t)v[i] >> 8;
		char tt2 = (uint32_t)v[i] >> 16;
		char tt3 = (uint32_t)v[i] >> 24;
		printf("%c%c%c%c", tt, tt1, tt2, tt3);

	}
	printf("\n");

}

 

举报

相关推荐

0 条评论