0x00 查壳
无壳,发现是64位程序,用IDA64打开
ox01 IDA分析
搜索字符串,发现了可疑字符串,从you win开始分析
找到关键函数:
__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
_QWORD *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 code[4]; // [rsp+40h] [rbp-28h]
int v33; // [rsp+50h] [rbp-18h]
*code = 0i64;
v33 = 0;
sub_1400018C0(std::cin, a2, code); //和v5取值的code不是同一code,重命名即可看到
v2 = -1i64;
v3 = -1i64;
do
++v3;
while ( *(code + v3) );
if ( v3 != 19 ) // length == 19
{
sub_140001620(std::cout, "error\n");
_exit(code);
}
v4 = sub_140001E5C(5ui64);
v5 = *&Code; // qwertyuiopasdfghjklzxcvbnm1234567890
v6 = v4;
v7 = 0;
v8 = v4;
do
{
v9 = *(v8 + code - v4);
v10 = 0;
*v8 = v9;
v11 = 0i64;
v12 = -1i64;
do
++v12;
while ( *(v5 + v12) ); // v12 = len(Code) = 36
if ( v12 )
{
do
{
if ( v9 == *(v5 + v11) )
break; // 判断输入字符是否在Code字符集中
++v10;
++v11;
}
while ( v10 < v12 );
}
v13 = -1i64;
do
++v13;
while ( *(v5 + v13) ); // v13 = len(Code) = 36
if ( v10 == v13 )
_exit(v5);
v8 = (v8 + 1);
}
while ( v8 - v4 < 4 );
*(v4 + 4) = 0; // v4保存前4个字符 flag?
do
++v2;
while ( *(code + v2) ); // v2 = len(input)
v14 = 0i64;
v29 = *v6;
while ( *(&v29 + v14) )
{
if ( !*(&v29 + v14 + 1) )
{
++v14;
break;
}
if ( !*(&v29 + v14 + 2) )
{
v14 += 2i64;
break;
}
if ( !*(&v29 + v14 + 3) )
{
v14 += 3i64;
break;
}
v14 += 4i64;
if ( v14 >= 0x10 )
break;
}
for ( i = v14 + 1; i < 16; ++i )
*(&v29 + i) = 0; // 将key拓展为16位
v16 = sub_140001AB0(code, v2, &v29, &Size); // xxtea加密
v17 = Size;
v18 = v16;
v19 = sub_140001E5C(Size);
v20 = 1;
*v19 = v18[2]; // box替换
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 ) // v20 = 1,v21 = &v19[1]
{
v22 = 0i64;
if ( v20 / 3 > 0 )
{
v23 = *v21;
do
{
v23 ^= v19[v22++]; // 循环异或
*v21 = v23;
}
while ( v22 < v20 / 3 ); // 三个一组
}
++v20;
}
*&v29 = 0xC0953A7C6B40BCCEi64;
v24 = v19 - &v29;
*(&v29 + 1) = 0x3502F79120209BEFi64;
v25 = 0i64;
v30 = 0xC8021823;
v31 = 0xFA5656E7;
do
{
if ( *(&v29 + v25) != *(&v29 + v25 + v24) ) // &29+v24=v19
_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;
}
所以获取算法核心:
- 获取输入input,判断输入是否为19位;
- 检验输入的各个字符是否在字符集“qwertyuiopasdfghjklzxcvbnm1234567890”内;
- 将input的前四位保存在v4中,然后拓展为16位(用 ‘\0’ 填充)作为key;
- 用key对input进行xxtea加密,保存在v18中;
- 对v18进行乱序(box替换),保存在v19中;
- 对v19进行分组、循环异或;
- 与明文对比。
所以我们就可以从明文出发,所以在上面的核心中看到,只有4,5,6步是对数据进行了加密,所以也就只有三步而已。
这里的明文是小端存储的,所以应该逆序过来(hex数据格式,两个表示一位)
0x02 exp
exp1
v29 = "FA5656E7C80218233502F79120209BEFC0953A7C6B40BCCE"
crypt = []
for i in range(len(v29)-1,-1,-2):
crypt.append(int(v29[i-1]+v29[i],16))
#小端存储是逆序,先倒叙,因为是hex,两个一组。
print(crypt)
for i in crypt:
print(str(hex(i).upper())[2:],end='')
print('')#输出逆序后的v29
for i in range(len(crypt)-1,-1,-1):
d = int(i / 3)
if d > 0:
for j in range(d-1,-1,-1):
crypt[i] ^= crypt[j]
#逆分组循环异或
for i in crypt:
print(str(hex(i))[2:],end='')
print('')#输出逆异或后的结果
box = [2,0,3,1,6,4,7,5,10,8,11,9,14,12,15,13,18,16,19,17,22,20,23,21]
res = [0]*24
for i in range(24):
res[box[i]] = crypt[i]
#box替换,得到xxtea加密后的结果
res1 = ''
for i in res:
res1 += str(hex(i))[2:].zfill(2)
#字符处理,将res里的数转为hex再转str用切片方式删去'0x',用zfill()方法将一位的数前面补0
print(res1)
这里运行之后可以得到xxtea加密后的数据,只需要解码(“bca5ce40f4b2b2e7a9129d12ae10c85b3dd7061ddc70f8dc”)就好。
这里的解码就出现问题了……py是含有xxtea库的,但是用的时候……会报错我是用了错误的key……
exp2
没有办法只好找找其他的不用库的实现办法:
dec = 'bca5ce40f4b2b2e7a9129d12ae10c85b3dd7061ddc70f8dc'
dec1 = ''
for i in range(0,len(dec),2):
dec1 += chr(int(dec[i]+dec[i+1],16))
#将hex转为字符
import struct
_DELTA = 0x9E3779B9
def _long2str(v, w):
n = (len(v) - 1) << 2
if w:
m = v[-1]
if (m < n - 3) or (m > n): return ''
n = m
s = struct.pack('<%iL' % len(v), *v)
return s[0:n] if w else s
def _str2long(s, w):
n = len(s)
m = (4 - (n & 3) & 3) + n
s = s.ljust(m, "\0")
v = list(struct.unpack('<%iL' % (m >> 2), s))
if w: v.append(n)
return v
def encrypt(str, key):
if str == '': return str
v = _str2long(str, True)
k = _str2long(key.ljust(16, "\0"), False)
n = len(v) - 1
z = v[n]
y = v[0]
sum = 0
q = 6 + 52 // (n + 1)
while q > 0:
sum = (sum + _DELTA) & 0xffffffff
e = sum >> 2 & 3
for p in xrange(n):
y = v[p + 1]
v[p] = (v[p] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
z = v[p]
y = v[0]
v[n] = (v[n] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[n & 3 ^ e] ^ z))) & 0xffffffff
z = v[n]
q -= 1
return _long2str(v, False)
def decrypt(str, key):
if str == '': return str
v = _str2long(str, False)
k = _str2long(key.ljust(16, "\0"), False)
n = len(v) - 1
z = v[n]
y = v[0]
q = 6 + 52 // (n + 1)
sum = (q * _DELTA) & 0xffffffff
while (sum != 0):
e = sum >> 2 & 3
for p in xrange(n, 0, -1):
z = v[p - 1]
v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
y = v[p]
z = v[n]
v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff
y = v[0]
sum = (sum - _DELTA) & 0xffffffff
return _long2str(v, True)
key = 'flag'
dec2 = decrypt(dec1, key)
print (len(dec2))
print (dec2)
这里的第二个脚本需要在py2的环境下跑。然后得到了flag:
0x03 求解
拿到flag之后,我就试着用xxtea的库来加密之后和字符串dec对比,结果发现真的不一样。但是为什么出现这种问题?不应该啊
这个结果和dec里的值完全不一致,甚至连字符串长度都不一样。希望有大佬可以解惑 。