首先用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");
}