re学习(31)BUUCTF-xx(多层加密)

参考文章:
【BUUCTF逆向 [2019红帽杯]xx】_nb_What_DG的博客-CSDN博客

re学习笔记(26)BUUCTF-re-[2019红帽杯]xx_Forgo7ten的博客-CSDN博客 

还有B站 水番正文

IDA64位载入
shift+F12查看字符串

 

交叉引用找到关键代码

 

使用findcrypt插件找到加密算法为tea系列,进函数内部查看知道为xxtea 

 

// 代码审计(正向开发经验)
int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 flaglen2; // rbx
  __int64 flaglen1; // rax
  __int128 *malloc5; // rax
  __int64 data; // r11
  __int128 *malloc7; // r14
  int v8; // edi
  __int128 *malloc9; // rsi
  char tmp; // r10
  int v11; // edx
  __int64 v12; // r8
  unsigned __int64 datalen; // rcx
  __int64 v14; // rcx
  unsigned __int64 v15; // rax
  unsigned __int64 i; // rax
  __int64 encodeflag; // rax
  size_t encodeflaglen; // rsi
  _BYTE *enflag; // rbx
  _BYTE *v20; // r9
  int I; // r11d
  char *data1; // r8
  __int64 n; // rcx
  char v24; // al
  __int64 v25; // r9
  __int64 v26; // rdx
  __int64 v27; // rax
  size_t Size; // [rsp+20h] [rbp-48h] BYREF
  __int128 v30; // [rsp+28h] [rbp-40h] BYREF
  int v31; // [rsp+38h] [rbp-30h]
  int v32; // [rsp+3Ch] [rbp-2Ch]
  int flag[4]; // [rsp+40h] [rbp-28h] BYREF
  int v34; // [rsp+50h] [rbp-18h]

  *(_OWORD *)flag = 0i64;
  v34 = 0;
  sub_1400018C0(std::cin, argv, flag);          // 在C++中,std其实就是standard标准的意思。
                                                //  例如std::cin就是标准输入,std::cout就是标准输出的意思。
  flaglen2 = -1i64;
  flaglen1 = -1i64;
  do
    ++flaglen1;
  while ( *((_BYTE *)flag + flaglen1) );
  if ( flaglen1 != 19 )                         // flag 为 19位
  {
    sub_140001620((__int64 *)std::cout, (__int64)"error\n");
    _exit((int)flag);
  }
  malloc5 = (__int128 *)operator new(5ui64);    // operator new申请内存(operator new后面将进行详细说明,这里理解为C语言中的malloc)
  data = *(_QWORD *)&Code;                      // 将Code数据给data,
                                                // Code为"qwertyuiopasdfghjklzxcvbnm1234567890"
  malloc7 = malloc5;
  v8 = 0;
  malloc9 = malloc5;
  do
  {
    tmp = *((_BYTE *)malloc9 + (char *)flag - (char *)malloc5);
    v11 = 0;
    *(_BYTE *)malloc9 = tmp;
    v12 = 0i64;
    datalen = -1i64;
    do
      ++datalen;
    while ( *(_BYTE *)(data + datalen) );
    if ( datalen )
    {
      do
      {
        if ( tmp == *(_BYTE *)(data + v12) )
          break;
        ++v11;
        ++v12;
      }
      while ( v11 < datalen );
    }
    v14 = -1i64;
    do
      ++v14;
    while ( *(_BYTE *)(data + v14) );
    if ( v11 == v14 )
      _exit(data);
    malloc9 = (__int128 *)((char *)malloc9 + 1);// 下一位
  }
  while ( (char *)malloc9 - (char *)malloc5 < 4 );
  *((_BYTE *)malloc5 + 4) = 0;
  do
    ++flaglen2;
  while ( *((_BYTE *)flag + flaglen2) );
  v15 = 0i64;
  v30 = *malloc7;
  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;                   // 全部赋值为0
  encodeflag = (__int64)sub_140001AB0((__int64)flag, flaglen2, (unsigned __int8 *)&v30, &Size);
  encodeflaglen = Size;
  enflag = (_BYTE *)encodeflag;
  v20 = operator new(Size);                     // v20开辟一块内存区域
  I = 1;
  *v20 = enflag[2];
  data1 = v20 + 1;                              // v22与v20指向了同一片内存区域
  v20[1] = *enflag;
  v20[2] = enflag[3];
  v20[3] = enflag[1];
  v20[4] = enflag[6];
  v20[5] = enflag[4];
  v20[6] = enflag[7];
  v20[7] = enflag[5];
  v20[8] = enflag[10];
  v20[9] = enflag[8];
  v20[10] = enflag[11];
  v20[11] = enflag[9];
  v20[12] = enflag[14];
  v20[13] = enflag[12];
  v20[14] = enflag[15];
  v20[15] = enflag[13];
  v20[16] = enflag[18];
  v20[17] = enflag[16];
  v20[18] = enflag[19];
  v20[19] = enflag[17];
  v20[20] = enflag[22];
  v20[21] = enflag[20];
  v20[22] = enflag[23];
  for ( v20[23] = enflag[21]; I < encodeflaglen; ++data1 )// 异或操作
  {
    n = 0i64;
    if ( I / 3 > 0 )                            // I从3开始
    {
      v24 = *data1;
      do
      {
        v24 ^= v20[n++];                        // v22^v20
        *data1 = v24;
      }
      while ( n < I / 3 );                      // I/3是跟前多少位...
    }
    ++I;
  }
  *(_QWORD *)&v30 = 0xC0953A7C6B40BCCEui64;
  v25 = v20 - (_BYTE *)&v30;
  *((_QWORD *)&v30 + 1) = 0x3502F79120209BEFi64;
  v26 = 0i64;
  v31 = 0xC8021823;
  v32 = 0xFA5656E7;
  do
  {
    if ( *((_BYTE *)&v30 + v26) != *((_BYTE *)&v30 + v26 + v25) )
      _exit(v8 * v8);
    ++v8;
    ++v26;
  }
  while ( v26 < 24 );
  v27 = (__int64)sub_140001620((__int64 *)std::cout, (__int64)"You win!");
  std::ostream::operator<<(v27, sub_1400017F0);
  return 0;
}

流程为

  1. 先判断输入的字符串是否都在程序实现存储的数据Code中
  2. 然后取前四个字符作为xxtea的密钥,(不满位数右端补零)
  3. 然后对输入的字符串进行加密
  4. 之后对加密的字符串打乱顺序
  5. 之后异或操作
  6. 再与存储的数据进行比对

其中第6部存储的数据为

 

v30,v30+1,v31,v32在栈上存放的位置相连。小端序存放,需将其反过来写。 

 开始写脚本:

一.逆异或  二.正序

data0 = "CEBC406B7C3A95C0EF9B202091F70235231802C8E75656FA"#为提取出来的v29,v29+1,v30,v31
data = []
for i in range(0,len(data0),2):
    data.append(int(data0[i]+data0[i+1],16))#每两位为整体,将16进制转换为10进制
print(data)
for i in range(len(data)-1,-1,-1):
    for j in range(i//3):
        data[i] ^= data[j]
        #1.进行的异或操作
print(data)
biao = [2,0,3,1,6,4,7,5,10,8,11,9,14,12,15,13,18,16,19,17,22,20,23,21]#置换表
shuju = [1]*24
for i in range(24):
    shuju[biao[i]] = data[i]#2.将其按照一定顺序置换
print(shuju)
print(len(shuju))

三.得到加密数据 密钥key xxtea解密

 因为用户输入的为19位字符,也就是38位十六进制字符串,而上面生成了40位,所以要去掉最后两位,也就是去掉13。

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "encode.h"
int main()
{
	int i,j,data[] = { 188, 165, 206, 64, 244, 178, 178, 231, 169, 18, 157, 18, 174, 16, 200, 91, 61, 215, 6, 29, 220, 112, 248, 220 };
	for (i = 1; i <= 6; i++)//3.导出加密的数据,将其转换为十六进制并用小端序来表示
	{
		printf("0x");
		for (j = i * 4 - 1; j > (i - 1) * 4 - 1; j--)
			printf("%x", data[j]);
		printf(",");
	}
	printf("\n0x");
	int key[] = { 'f','l','a','g' };//导出密钥,将其转换为十六进制并用小端序来表示
	for (i = 3; i >= 0; i--)
		printf("%x", key[i]);
	printf(",");
	for (i = 0; i < 3; i++)//密钥为4个32位的数,1个字符4bit,4个字符为32bit,还差3个32bit的数
		printf("0x0,");
}

 图片展示

#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[6]= {0x40cea5bc,0xe7b2b2f4,0x129d12a9,0x5bc810ae,0x1d06d73d,0xdcf870dc};
    uint32_t const k[4]= {0x67616c66,0,0,0};
    int n= 6; //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);
    for (int i = 0 ; i < 6;i++){
        printf("%x",v[i]);
        printf(" ");
    }
    return 0;
}

m = [0x67616c66,0x5858437b,0x646e615f,0x742b2b5f,0x7d6165]

四.最后将生成的十六进制数用小端序表示

m = [0x67616c66,0x5858437b,0x646e615f,0x742b2b5f,0x7d6165]
flag = ''
for i in m:
    flag += libnum.n2s(i).decode()[::-1]
    
print(flag)
#flag{CXX_and_++tea}

 

总结:
1.如果涉及多字节了,应该考虑字节的顺序,比如这个题,就应该将处理的数据都转换为小端序

2.这个题的加密步骤有多步(3步:xxtea/乱序/异或)

3.要增强python工具,提高代码能力

猜你喜欢

转载自blog.csdn.net/m0_66039322/article/details/132303374