[2019红帽杯]easyRE
新手一枚,如有错误(不足)请指正,谢谢!!
<mark>个人博客:点击进入</mark>
题目链接:BUUCTF-re-[2019红帽杯]xx
题目下载:点击下载
IDA64位载入
shift+F12查看字符串
交叉引用找到关键代码
使用findcrypt插件找到加密算法为tea系列,进函数内部查看知道为xxtea
__int64 __fastcall sub_1400011A0(__int64 a1, __int64 a2)
{
unsigned __int64 v2; // rbx
signed __int64 v3; // rax
__int128 *v4; // rax
__int64 v5; // r11
__int128 *v6; // r14
int v7; // edi
__int128 *v8; // rsi
char v9; // r10
int v10; // edx
__int64 v11; // r8
unsigned __int64 v12; // rcx
signed __int64 v13; // rcx
unsigned __int64 v14; // rax
unsigned __int64 i; // rax
_BYTE *v16; // rax
size_t v17; // rsi
_BYTE *v18; // rbx
_BYTE *v19; // r9
signed int v20; // er11
char *v21; // r8
signed __int64 v22; // rcx
char v23; // al
signed __int64 v24; // r9
signed __int64 v25; // rdx
__int64 v26; // rax
size_t Size; // [rsp+20h] [rbp-48h]
__int128 v29; // [rsp+28h] [rbp-40h]
int v30; // [rsp+38h] [rbp-30h]
int v31; // [rsp+3Ch] [rbp-2Ch]
int user_input[4]; // [rsp+40h] [rbp-28h]
int v33; // [rsp+50h] [rbp-18h]
*(_OWORD *)user_input = 0i64;
v33 = 0;
sub_1400018C0(std::cin, a2, user_input); // 读取用户输入
v2 = -1i64;
v3 = -1i64;
do
++v3;
while ( *((_BYTE *)user_input + v3) ); // 计算user_input的长度,即v3
if ( v3 != 19 ) // 输入的长度要等于19
{
sub_140001620(std::cout, "error\n");
_exit((unsigned __int64)user_input);
}
v4 = (__int128 *)sub_140001E5C(5ui64); // v4为动态分配出的一块长度为5的空间
v5 = *(_QWORD *)&Code; // v5为内存中存储的数据
// 为 "qwertyuiopasdfghjklzxcvbnm1234567890"
v6 = v4;
v7 = 0;
v8 = v4; // v6=v8=v4
do
{
v9 = *((_BYTE *)v8 + (char *)user_input - (char *)v4);// v9 = user_input
v10 = 0;
*(_BYTE *)v8 = v9;
v11 = 0i64;
v12 = -1i64;
do
++v12;
while ( *(_BYTE *)(v5 + v12) ); // v12 计算内存中存储数据的长度
if ( v12 )
{
do
{
if ( v9 == *(_BYTE *)(v5 + v11) ) // 输入的字符,是在内存存储的数据里中的一个元素
break;
++v10;
++v11;
}
while ( v10 < v12 );
}
v13 = -1i64;
do
++v13;
while ( *(_BYTE *)(v5 + v13) ); // v12 计算内存中存储数据的长度
if ( v10 == v13 )
_exit(v5);
v8 = (__int128 *)((char *)v8 + 1);
}
while ( (char *)v8 - (char *)v4 < 4 ); // 54~83行
// 输入数据的前四个元素能在内存中存储的数据中找到对应
*((_BYTE *)v4 + 4) = 0; // v4这块动态分配的内存空间中存放着 输入的前四个元素和一个0
do
++v2;
while ( *((_BYTE *)user_input + v2) ); // v2为输入字符串的长度
v14 = 0i64;
v29 = *v6; // v6等于v4 85行
while ( *((_BYTE *)&v29 + v14) )
{
if ( !*((_BYTE *)&v29 + v14 + 1) )
{
++v14;
break;
}
if ( !*((_BYTE *)&v29 + v14 + 2) )
{
v14 += 2i64;
break;
}
if ( !*((_BYTE *)&v29 + v14 + 3) )
{
v14 += 3i64;
break;
}
v14 += 4i64;
if ( v14 >= 16 )
break;
}
for ( i = v14 + 1; i < 16; ++i )
*((_BYTE *)&v29 + i) = 0; // 将四个字符之后的置零
v16 = sub_140001AB0((__int64)user_input, v2, (unsigned __int8 *)&v29, &Size); //xxtea加密
v17 = Size; // 加密后的长度
v18 = v16;
v19 = sub_140001E5C(Size); // 动态分配一块加密后字符串长度给v19
v20 = 1;
*v19 = v18[2];
v21 = v19 + 1;
v19[1] = *v18;
v19[2] = v18[3];
v19[3] = v18[1];
v19[4] = v18[6];
v19[5] = v18[4];
v19[6] = v18[7];
v19[7] = v18[5];
v19[8] = v18[10];
v19[9] = v18[8];
v19[10] = v18[11];
v19[11] = v18[9];
v19[12] = v18[14];
v19[13] = v18[12];
v19[14] = v18[15];
v19[15] = v18[13];
v19[16] = v18[18];
v19[17] = v18[16];
v19[18] = v18[19];
v19[19] = v18[17];
v19[20] = v18[22];
v19[21] = v18[20];
v19[22] = v18[23]; // 排序
for ( v19[23] = v18[21]; v20 < v17; ++v21 ) // v21=v19+1
{
v22 = 0i64;
if ( v20 / 3 > 0 )
{
v23 = *v21;
do
{
v23 ^= v19[v22++];
*v21 = v23;
}
while ( v22 < v20 / 3 );
}
++v20;
}
*(_QWORD *)&v29 = 0xC0953A7C6B40BCCEi64;
v24 = v19 - (_BYTE *)&v29;
*((_QWORD *)&v29 + 1) = 0x3502F79120209BEFi64;
v25 = 0i64;
v30 = 0xC8021823;
v31 = 0xFA5656E7;
do
{
if ( *((_BYTE *)&v29 + v25) != *((_BYTE *)&v29 + v25 + v24) )
_exit(v7 * v7);
++v7;
++v25;
}
while ( v25 < 24 );
v26 = sub_140001620(std::cout, "You win!");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v26, sub_1400017F0);
return 0i64;
}
流程为
- 先判断输入的字符串是否都在程序实现存储的数据Code中
- 然后取前四个字符作为xxtea的密钥,(不满位数右端补零)
- 然后对输入的字符串进行加密
- 之后对加密的字符串打乱顺序
- 之后异或操作
- 再与存储的数据进行比对
其中第6部存储的数据为
v29,v29+1,v30,v31在栈上存放的位置相连。小端序存放,需将其反过来写。
开始写脚本
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]
#进行的异或操作
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]#将其按照一定顺序置换
print(shuju)
print(len(shuju))
#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++)//导出加密的数据,将其转换为十六进制并用小端序来表示
{
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,");
}
解密得到十六进制字符串,这里用到了我自己写的头文件。头文件所用函数为
#define xxtea_DELTA 0x9e3779b9
#define xxtea_MX (((xxtea_z>>5^xxtea_y<<2) + (xxtea_y>>3^xxtea_z<<4)) ^ ((xxtea_sum^xxtea_y) + (xxtea_key[(xxtea_p&3)^xxtea_e] ^ xxtea_z)))
void xxtea(uint32_t* xxtea_origin, int xxtea_n, uint32_t const xxtea_key[4])
{
uint32_t xxtea_y, xxtea_z, xxtea_sum;
unsigned xxtea_p, xxtea_rounds, xxtea_e;
if (xxtea_n > 1) /* Coding Part */
{
xxtea_rounds = 6 + 52 / xxtea_n;
xxtea_sum = 0;
xxtea_z = xxtea_origin[xxtea_n - 1];
do
{
xxtea_sum += xxtea_DELTA;
xxtea_e = (xxtea_sum >> 2) & 3;
for (xxtea_p = 0; xxtea_p < xxtea_n - 1; xxtea_p++)
{
xxtea_y = xxtea_origin[xxtea_p + 1];
xxtea_z = xxtea_origin[xxtea_p] += xxtea_MX;
}
xxtea_y = xxtea_origin[0];
xxtea_z = xxtea_origin[xxtea_n - 1] += xxtea_MX;
} while (--xxtea_rounds);
}
else if (xxtea_n < -1) /* Decoding Part */
{
xxtea_n = -xxtea_n;
xxtea_rounds = 6 + 52 / xxtea_n;
xxtea_sum = xxtea_rounds * xxtea_DELTA;
xxtea_y = xxtea_origin[0];
do
{
xxtea_e = (xxtea_sum >> 2) & 3;
for (xxtea_p = xxtea_n - 1; xxtea_p > 0; xxtea_p--)
{
xxtea_z = xxtea_origin[xxtea_p - 1];
xxtea_y = xxtea_origin[xxtea_p] -= xxtea_MX;
}
xxtea_z = xxtea_origin[xxtea_n - 1];
xxtea_y = xxtea_origin[0] -= xxtea_MX;
xxtea_sum -= xxtea_DELTA;
} while (--xxtea_rounds);
}
}
因为用户输入的为19位字符,也就是38位十六进制字符串,而上面生成了40位,所以要去掉最后两位,也就是去掉13。
最后将生成的十六进制数用小端序表示
得到flag
最后粘贴一下写的代码:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "encode.h"
int main()
{
#if 0
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++)//导出加密的数据,将其转换为十六进制并用小端序来表示
{
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,");
uint32_t enc[6] = { (unsigned int)0x40cea5bc,(unsigned int)0xe7b2b2f4,(unsigned int)0x129d12a9,(unsigned int)0x5bc810ae,(unsigned int)0x1d06d73d,(unsigned int)0xdcf870dc };
int n = -6;
uint32_t const key[4] = { (unsigned int)0x67616c66,(unsigned int)0x0,(unsigned int)0x0,(unsigned int)0x0 };
btea(enc, n, key);
for (int i = 0; i < 6; i++)
printf("%x", enc[i]);
#endif
char string[] = "67616c665858437b646e615f742b2b5f7d6165";
int i = 1,j=0;
for (;i<6;i++)
{
for (j = 0; j < 8; j += 2)
{
if (j == 0 && i == 5)
continue;
printf("%c%c", string[i * 8 - j - 2], string[i * 8 - j - 1]);
}
}
}
flag为flag{CXX_and_++tea}