0 题目说明
Windows CrackMe 64位
1 分析过程
1.1 查看字符串
.rdata:000000000048B008 00000041 C ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
.rdata:000000000048B04A 0000001B C please input your serial:\n
.rdata:000000000048B068 00000026 C Welcome/to/this/very/simple/challenge
.rdata:000000000048B08E 0000000B C incorrect!
.rdata:000000000048B099 00000006 C pause
.rdata:000000000048B09F 00000009 C correct!
.rdata:000000000048B0A8 00000021 C 44e4403b63620a2075d3fb2e0a6207d2
.rdata:000000000048B0D0 000000AA C **************@************-************--**----*****-***-**-*****-***#**-*****--*****-******-*****-******-------******-*-----******---**-*******-****--*****************
这几个字符串很可疑,尤其是.rdata:000000000048B0A8,.rdata:000000000048B0D0,一会可以注意一下。接下来追踪correct和incorrect,correct只存在于main函数中,查看main函数。
1.2 main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
...
_main();
std::string::string((std::string *)&v26);
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "please input your serial:\n");
std::operator>><char,std::char_traits<char>,std::allocator<char>>(refptr__ZSt3cin, (std::string *)&v26);
std::string::operator+=((std::string *)&v26, "Welcome/to/this/very/simple/challenge");// 输入的字符和预置字符组合
if ( (unsigned __int64)std::string::length((std::string *)&v26) > 85 )// 组合字符长度大于85报错
{
v3 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "incorrect!");
std::ostream::operator<<(v3, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
system("pause");
}
else
{
std::string::string((std::string *)&v27, (const std::string *)&v26);// 将组合字符串的地址复制到v27
v4 = !check_fun((__int64)&v27);
std::string::~string((std::string *)&v27);
if ( v4 )
{
v5 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "incorrect!");
std::ostream::operator<<(v5, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
system("pause");
}
else
{
std::string::substr((std::string *)&v28, (unsigned __int64)&v26, 0i64);
O0oo0oO0O((std::string *)&v25, (std::string *)&v28);
std::string::~string((std::string *)&v28);
v40 = std::string::length((std::string *)&v26);
v6 = alloca(16 * ((unsigned __int64)(v40 + 16) >> 4));
Str = (char *)&v20;
std::string::substr((std::string *)&v29, (unsigned __int64)&v25, 0x12ui64);
v7 = std::string::length((std::string *)&v29) + 1;
std::string::substr((std::string *)&v30, (unsigned __int64)&v25, 0x12ui64);
v8 = (const char *)std::string::c_str((std::string *)&v30);
strncpy(&Dest, v8, v7);
std::string::~string((std::string *)&v30);
std::string::~string((std::string *)&v29);
std::string::substr((std::string *)&v31, (unsigned __int64)&v26, 0i64);
v9 = std::string::length((std::string *)&v31) + 1;
std::string::substr((std::string *)&v32, (unsigned __int64)&v26, 0i64);
v10 = (const char *)std::string::c_str((std::string *)&v32);
strncpy(&v23, v10, v9);
std::string::~string((std::string *)&v32);
std::string::~string((std::string *)&v31);
std::string::substr((std::string *)&v33, (unsigned __int64)&v26, 8ui64);
v11 = std::string::length((std::string *)&v33) + 1;
std::string::substr((std::string *)&v34, (unsigned __int64)&v26, 8ui64);
v12 = (const char *)std::string::c_str((std::string *)&v34);
strncpy(&v22, v12, v11);
std::string::~string((std::string *)&v34);
std::string::~string((std::string *)&v33);
std::string::substr((std::string *)&v35, (unsigned __int64)&v26, 0x10ui64);
v13 = std::string::length((std::string *)&v35) + 1;
std::string::substr((std::string *)&v36, (unsigned __int64)&v26, 0x10ui64);
v14 = (const char *)std::string::c_str((std::string *)&v36);
strncpy(&v21, v14, v13);
std::string::~string((std::string *)&v36);
std::string::~string((std::string *)&v35);
v15 = std::string::length((std::string *)&v26) + 1;
v16 = (const char *)std::string::c_str((std::string *)&v26);
strncpy(Str, v16, v15);
Ooo0ooO0O(&Dest, &v21, &Dest);
Ooo0OO0oo(&Dest, &v22, &Dest);
v38 = strlen(Str);
md5(&v20, Str, v38);
Ooo0ooO0O(&Dest, &v23, &Dest);
std::string::replace((std::string *)&v25, 0x12ui64, 8ui64, &Dest);
std::string::string((std::string *)&v37, (const std::string *)&v25);
O0oOo0Oo0((unsigned __int64)&v37);
std::string::~string((std::string *)&v37);
if ( (unsigned __int8)std::operator==<char>(&v20, &O0oo0OOo0) )
{
if ( O0ooOO00o == 4 )
v17 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "correct!");
else
v17 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "incorrect!");
std::ostream::operator<<(v17, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
}
else
{
v18 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "incorrect!");
std::ostream::operator<<(v18, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
}
system("pause");
std::string::~string((std::string *)&v20);
std::string::~string((std::string *)&v25);
}
}
std::string::~string((std::string *)&v26);
return 0;
}
1.2.1 check_fun
参数是组合字符串,然后进入checkfun函数。
通过调试可以发现,函数对参数中的第一个字符进行判断,通过第一个字符,经过梅森旋转算法,生成了三个随机数,然后将随机数与密钥keytea同时放入btea算法求解。
并且发现,相同的字母加密成为相同的内容
_BOOL1 __fastcall check_fun(__int64 a1)
{
...
v1 = (char *)std::string::operator[](a1, 0i64);
std::mersenne_twister_engine<unsigned int,32ull,624ull,397ull,31ull,2567483615u,11ull,4294967295u,7ull,2636928640u,15ull,4022730752u,18ull,1812433253u>::mersenne_twister_engine(
(__int64)&v4,
*v1);
v9 = std::mersenne_twister_engine<unsigned int,32ull,624ull,397ull,31ull,2567483615u,11ull,4294967295u,7ull,2636928640u,15ull,4022730752u,18ull,1812433253u>::operator()((__int64)&v4);
v8 = std::mersenne_twister_engine<unsigned int,32ull,624ull,397ull,31ull,2567483615u,11ull,4294967295u,7ull,2636928640u,15ull,4022730752u,18ull,1812433253u>::operator()((__int64)&v4);
v2 = std::mersenne_twister_engine<unsigned int,32ull,624ull,397ull,31ull,2567483615u,11ull,4294967295u,7ull,2636928640u,15ull,4022730752u,18ull,1812433253u>::operator()((__int64)&v4);
v7 = v2;
v5 = v9;
v6 = v2;
btea(&v5, 2, &keytea);
return v8 - (v5 + v6) == 0xA504A8F7;
}
1.2.1.1 btea
btea算法链接https://blog.csdn.net/libinjlu/article/details/43030369,是一种微型加密算法,算法密钥btea已知,故可以反解第一个字符。
__int64 __fastcall btea(unsigned int *a1, signed int a2, unsigned int *a3)
{
...
v19 = a1[a2 - 1];
v18 = *a1;
v16 = 0;
if ( a2 <= 1 )
{
if ( a2 >= -1 )
{
result = 1i64;
}
else
{
v20 = -a2;
for ( i = -1640531527 * (52 / -a2 + 6); i; i += 1640531527 )
{
v12 = (i >> 2) & 3;
for ( j = v20 - 1; j; --j )
{
v8 = a1[j - 1];
v9 = &a1[j];
*v9 -= ((4 * v18 ^ (v8 >> 5)) + ((v18 >> 3) ^ 16 * v8)) ^ ((v18 ^ i) + (v8 ^ a3[v12 ^ j & 3]));
v18 = *v9;
}
v10 = a1[v20 - 1];
*a1 -= ((4 * v18 ^ (v10 >> 5)) + ((v18 >> 3) ^ 16 * v10)) ^ ((v18 ^ i) + (v10 ^ a3[v12]));
v18 = *a1;
}
result = 0i64;
}
}
else
{
v13 = 52 / a2 + 6;
while ( 1 )
{
v6 = v13--;
if ( v6 == 0 )
break;
v16 -= 1640531527;
v11 = (v16 >> 2) & 3;
for ( k = 0; a2 - 1 > k; ++k )
{
v3 = a1[k + 1];
v4 = &a1[k];
*v4 += ((4 * v3 ^ (v19 >> 5)) + ((v3 >> 3) ^ 16 * v19)) ^ ((v3 ^ v16) + (v19 ^ a3[v11 ^ k & 3]));
v19 = *v4;
}
v5 = &a1[a2 - 1];
*v5 += ((4 * *a1 ^ (v19 >> 5)) + ((*a1 >> 3) ^ 16 * v19)) ^ ((*a1 ^ v16) + (v19 ^ a3[v11 ^ k & 3]));
v19 = *v5;
}
result = 0i64;
}
return result;
}
1.2.2 解第一个字符
脚本如下,参考了百度百科中梅森旋转算法https://baike.baidu.com/item/%E6%A2%85%E6%A3%AE%E6%97%8B%E8%BD%AC%E7%AE%97%E6%B3%95/22800014?fr=aladdin,博客BTEA算法C语言实现https://blog.csdn.net/libinjlu/article/details/43030369,以及writeup https://bbs.pediy.com/thread-254655.htm
_DELTA = 0x9E3779B9
def _int32(x):
# Get the 32 least significant bits.
return int(0xFFFFFFFF & x)
class MT19937:
def __init__(self, seed):
# Initialize the index to 0
self.index = 624
self.mt = [0] * 624
self.mt[0] = seed # Initialize the initial state to the seed
for i in range(1, 624):
self.mt[i] = _int32(
1812433253 * (self.mt[i - 1] ^ self.mt[i - 1] >> 30) + i)
#print map(hex,self.mt)
def extract_number(self):
if self.index >= 624:
self.twist()
y = self.mt[self.index]
# Right shift by 11 bits
y = y ^ y >> 11
# Shift y left by 7 and take the bitwise and of 2636928640
y = y ^ y << 7 & 2636928640
# Shift y left by 15 and take the bitwise and of y and 4022730752
y = y ^ y << 15 & 4022730752
# Right shift by 18 bits
y = y ^ y >> 18
self.index = self.index + 1
return _int32(y)
def twist(self):
for i in range(624):
# Get the most significant bit and add it to the less significant
# bits of the next number
y = _int32((self.mt[i] & 0x80000000) +
(self.mt[(i + 1) % 624] & 0x7fffffff))
self.mt[i] = self.mt[(i + 397) % 624] ^ y >> 1
if y % 2 != 0:
self.mt[i] = self.mt[i] ^ 0x9908b0df
self.index = 0
def encrypt(v,n,k):
#if str == '': return str
#v = _str2long(str, True)
#k = _str2long(key.ljust(16, "\0"), False)
#n = len(v) - 1
n=n-1
z = v[n]
y = v[0]
sum = 0
q = 6 + 52 // (n + 1)
#print map(hex,v)
#print n
#print map(hex,k)
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 v
k=[0x67452301, 0x0EFCDAB89, 0x98BADCFE, 0x10325476]
n=2
for i in range(0xFF):
MT=MT19937(i)
ret1=MT.extract_number()
ret2=MT.extract_number()
ret3=MT.extract_number()
v=[ret1,ret3]
v=encrypt(v,n,k)
if ret2 - (v[0] + v[1]) == 0xA504A8F7:
print "Found:",i,chr(i)
break
可以解出,第一位为V
1.3 O0oo0oO0O
函数主要进行对“输入+预置“字符串进行base64解码,根据”=”判断解码的位数
std::string *__fastcall O0oo0oO0O(std::string *a1, std::string *a2)
{
...
v24 = a1;
v25 = a2;
std::allocator<char>::allocator((char *)&v18 + 54);
std::string::string(&v19, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", &v20);
std::allocator<char>::~allocator(&v20);
v22 = std::string::length(v25);
std::allocator<char>::allocator(&v21);
std::string::string(v24, &unk_48B049, &v21);
std::allocator<char>::~allocator(&v21);
if ( std::string::find(v25, 61, 0i64) && (unsigned __int64)std::string::find(v25, 61, 0i64) <= 0x23 )
v2 = std::string::find(v25, 61, 0i64); // 根据“=”号的位置,判断解码位数
else
v2 = v22;
v22 = v2;
for ( i = 0; v22 - 4 >= i; i += 4 ) // b64变换过程
{
v3 = (char *)std::string::operator[](v25, i);
std::string::find((std::string *)&v19, *v3, 0i64);
v4 = (char *)std::string::operator[](v25, i + 1);
std::string::find((std::string *)&v19, *v4, 0i64);
std::string::operator+=(v24);
v5 = (char *)std::string::operator[](v25, i + 1);
std::string::find((std::string *)&v19, *v5, 0i64);
v6 = (char *)std::string::operator[](v25, i + 2);
std::string::find((std::string *)&v19, *v6, 0i64);
std::string::operator+=(v24);
v7 = (char *)std::string::operator[](v25, i + 2);
std::string::find((std::string *)&v19, *v7, 0i64);
v8 = (char *)std::string::operator[](v25, i + 3);
std::string::find((std::string *)&v19, *v8, 0i64);
std::string::operator+=(v24);
}
v9 = std::string::length(v25);
if ( v9 - std::string::find(v25, 61, 0i64) == 1 )
{
v10 = (char *)std::string::operator[](v25, i);
std::string::find((std::string *)&v19, *v10, 0i64);
v11 = (char *)std::string::operator[](v25, i + 1);
std::string::find((std::string *)&v19, *v11, 0i64);
std::string::operator+=(v24);
v12 = (char *)std::string::operator[](v25, i + 1);
std::string::find((std::string *)&v19, *v12, 0i64);
v13 = (char *)std::string::operator[](v25, i + 2);
std::string::find((std::string *)&v19, *v13, 0i64);
std::string::operator+=(v24);
}
else
{
v14 = std::string::length(v25);
if ( v14 - std::string::find(v25, 61, 0i64) == 2 )
{
v15 = (char *)std::string::operator[](v25, i);
std::string::find((std::string *)&v19, *v15, 0i64);
v16 = (char *)std::string::operator[](v25, i + 1);
std::string::find((std::string *)&v19, *v16, 0i64);
std::string::operator+=(v24);
}
}
std::string::~string((std::string *)&v19);
return v24;
}
1.4 分割
经过base64解码之后,将结果分割,分为3组
std::string::~string((std::string *)&v28);
v40 = std::string::length((std::string *)&v26);// 组合的字符串
v6 = alloca(16 * ((unsigned __int64)(v40 + 16) >> 4));
Str = (char *)&v20;
std::string::substr((std::string *)&v29, (const std::string *)&v25, 18ui64, 0xFFFFFFFFFFFFFFFFui64);
v7 = std::string::length((std::string *)&v29) + 1;
std::string::substr((std::string *)&v30, (const std::string *)&v25, 18ui64, 0xFFFFFFFFFFFFFFFFui64);
v8 = (const char *)std::string::c_str((std::string *)&v30);// 分成两部分
strncpy(&Dest, v8, v7); // 18位以后的数据
std::string::~string((std::string *)&v30);
std::string::~string((std::string *)&v29);
std::string::substr((std::string *)&v31, (const std::string *)&v26, 0i64, 8ui64);// substr切分字符串,将base64之后的结果从0开始读8位
v9 = std::string::length((std::string *)&v31) + 1;
std::string::substr((std::string *)&v32, (const std::string *)&v26, 0i64, 8ui64);
v10 = (const char *)std::string::c_str((std::string *)&v32);// 转换为字符串数组
strncpy(&v23, v10, v9); // 然后赋值
std::string::~string((std::string *)&v32);
std::string::~string((std::string *)&v31);
std::string::substr((std::string *)&v33, (const std::string *)&v26, 8ui64, 8ui64);// substr切分字符串,将base64之后的结果从8开始读8位
v11 = std::string::length((std::string *)&v33) + 1;
std::string::substr((std::string *)&v34, (const std::string *)&v26, 8ui64, 8ui64);
v12 = (const char *)std::string::c_str((std::string *)&v34);// 转换为字符串数组
strncpy(&v22, v12, v11); // 然后赋值
std::string::~string((std::string *)&v34);
std::string::~string((std::string *)&v33);
std::string::substr((std::string *)&v35, (const std::string *)&v26, 16ui64, 8ui64);// substr切分字符串,将base64之后的结果从16开始读8位
v13 = std::string::length((std::string *)&v35) + 1;
std::string::substr((std::string *)&v36, (const std::string *)&v26, 16ui64, 8ui64);
v14 = (const char *)std::string::c_str((std::string *)&v36);// 转换为字符串数组
strncpy(&v21, v14, v13); // 然后赋值
std::string::~string((std::string *)&v36);
std::string::~string((std::string *)&v35);
v15 = std::string::length((std::string *)&v26) + 1;
v16 = (const char *)std::string::c_str((std::string *)&v26);
1.5 Ooo0ooO0O(&Dest, &v21, &Dest)
这是des的解密算法,解密内容为 base64 解码的数据 18 位以后的数据,密钥为【16:24】.
上面的具体算法判断主要有以下几点:
1.16轮密钥密钥编排,追踪密钥的流程,可以找到如下代码:
__int64 __fastcall OOoo0oO0O(const int *a1, int (*a2)[48])
{
...
v17 = a2;
v10 = 1;
v9 = 2;
memset(v5, 0, sizeof(v5));
memset(v8, 0, sizeof(v8));
memset(v6, 0, sizeof(v6));
memset(v7, 0, sizeof(v7));
result = OOoo0Oo0O(a1, v5, &OOoOOoo00); // 初始置换,64bit->56bit
for ( i = 0; i <= 27; ++i )
{
v3[i] = v5[i];
result = i;
v4[i] = v5[i + 28];
}
v15 = 0;
for ( j = 1; j <= 16; ++j ) // 16轮密钥
{
if ( j != 1 && j != 2 && j != 9 && j != 16 )// 左移两位
{
v15 += v9;
OOoo0O0Oo(v3, &v6[28 * (j - 1)], v15);
result = OOoo0O0Oo(v4, &v7[28 * (j - 1)], v15);
}
else // 左移一位
{
v15 += v10;
OOoo0O0Oo(v3, &v6[28 * (j - 1)], v15);
result = OOoo0O0Oo(v4, &v7[28 * (j - 1)], v15);
}
}
for ( k = 0; k <= 15; ++k )
{
for ( l = 0; l <= 27; ++l )
{
v8[l + 56i64 * k] = v6[l + 28i64 * k];
result = l + 28 + 56i64 * k;
v8[result] = v7[l + 28i64 * k];
}
}
for ( m = 0; m <= 15; ++m )
result = OOoooO0O0(&v8[56 * m], v17[m], &OOOo00oOo);// 获取48位密钥Ki
return result;
}
des中的密钥编排有如下规则:
- 初始置换PC-1(置换选择1):去除校验位,将64位密钥缩短为56位
- 循环移位:将第一步得到的56位密钥分为C0和D0两个部分,长度均为28位的左右两部分将周期性的左移一位或者两位,移动的具体位数取决于轮数i,规则如下:
- 在第i=1,2,9,16轮中,左移一位
- 其他轮中,左移两位
- 置换选择2(PC-2):通过再一次置换从56位密钥(C0和D0)中获取48位轮密钥ki
1.6 Ooo0OO0oo(&Dest, &v22, &Dest);
对应于des解密算法,这个函数也有相同的特点,是des加密算法,加密内容为base64解密后18位,密钥为【8:16】位
接下来又是一个1.5 Ooo0ooO0O,des解密,密钥为【0:8】位
小结一下,也就是3des算法
1.7 O0oOo0Oo0((unsigned __int64)&v37);
赋值之后开始走迷宫, 判断是迷宫,因为发现了全局变量Oooo0O0Oo,它的引用来自函数static_initialization_and_destruction_0(int a1, int a2)
2 迷宫分析
2.1 static_initialization_and_destruction_0(int a1, int a2)
初始化一个迷宫
int __fastcall __static_initialization_and_destruction_0(int a1, int a2)
{
int result; // eax
char v3; // [rsp+2Eh] [rbp-52h]
char v4; // [rsp+2Fh] [rbp-51h]
if ( a1 == 1 && a2 == 0xFFFF )
{
std::ios_base::Init::Init((std::ios_base::Init *)&std::__ioinit);
atexit(_tcf_0);
std::allocator<char>::allocator(&v3);
std::string::string(&O0oo0OOo0, "44e4403b63620a2075d3fb2e0a6207d2", &v3);// 赋值一个md5,后面会进行比较
std::allocator<char>::~allocator(&v3);
atexit(_tcf_1);
std::allocator<char>::allocator(&v4);
std::string::string(
&Oooo0O0Oo,
"**************@************-************--**----*****-***-**-*****-***#**-*****--*****-******-*****-******-------*"
"*****-*-----******---**-*******-****--*****************",
&v4); // 赋值一个字符串,初始化一个迷宫
std::allocator<char>::~allocator(&v4);
result = atexit(_tcf_2);
}
return result;
}
2.2 迷宫行进方向
由函数O0oO00ooO(std::string *a1)定义,内容如下:
_BOOL8 __fastcall O0oO00ooO(std::string *a1)
{
...
v7 = a1;
v6 = std::string::find((std::string *)&Oooo0O0Oo, 64, 0i64);
for ( i = 0i64; std::string::length(v7) > i; ++i )
{
for ( j = 6; j >= 0; j -= 2 )
{
v1 = ((signed int)*(unsigned __int8 *)std::string::operator[](v7, i) >> j) & 3;
if ( v1 == 1 )
{
v6 += 13;
}
else if ( v1 > 1 )
{
if ( v1 == 2 )
{
--v6;
}
else if ( v1 == 3 )
{
++v6;
}
}
else if ( !v1 )
{
v6 -= 13;
}
v2 = *(_BYTE *)std::string::operator[]((std::string *)&Oooo0O0Oo, v6) != '-'
&& *(_BYTE *)std::string::operator[]((std::string *)&Oooo0O0Oo, v6) != '#';
if ( v2 )
return 0i64;
}
}
return *(_BYTE *)std::string::operator[]((std::string *)&Oooo0O0Oo, v6) == '#';
}
迷宫13个字符一行,然后移动方向定义为:1,向下;2,向左;3,向右;0,向上。
2.3 初始状态
简单写脚本查看初始化状态:
import re
sn="**************@************-************--**----*****-***-**-*****-***#**-*****--*****-******-*****-******-------******-*-----******---**-*******-****--*****************"
b=re.findall(r'.{13}',sn)
for i in ralnge(len(b)):
print b[i]
*************
*@***********
*-***********
*--**----****
*-***-**-****
*-***#**-****
*--*****-****
**-*****-****
**-------****
**-*-----****
**---**-*****
**-****--****
*************
2.4 交换迷宫行函数
这个函数O0oOo0Oo0(4, 5),起到了交换两行的作用,两个参数分别代表要交换的两行,起到了交换两行的作用,代码不好理解,这个可以通过动态调试看到
__int64 __fastcall O0oOo0Oo0(int a1, int a2)
{
...
v6 = a1 - 1;
v7 = a2 - 1;
std::string::substr((std::string *)(&v3 + 6), (const std::string *)&Oooo0O0Oo, (a1 - 1) * O0ooO0oO0, O0ooO0oO0);
std::string::substr((std::string *)&v4, (const std::string *)&Oooo0O0Oo, v7 * O0ooO0oO0, O0ooO0oO0);
std::string::replace((std::string *)&Oooo0O0Oo, v6 * O0ooO0oO0, O0ooO0oO0, (const std::string *)&v4);
std::string::replace((std::string *)&Oooo0O0Oo, v7 * O0ooO0oO0, O0ooO0oO0, (const std::string *)&v5);
std::string::~string((std::string *)&v4);
return std::string::~string((std::string *)&v5);
}
2.5 迷宫函数
迷宫函数为O0oOo0Oo0(unsigned __int64 a1),参数为输入数据转换的数据,也就是走法,上下左右。判断的逻辑就是根据输入数据,判断是否能从“@”走到“#”。如果走到了,则O0ooOO00o ++。迷宫函数中,迷宫经历了三次变换,加上初始的迷宫,总共四个。
__int64 __fastcall O0oOo0Oo0(unsigned __int64 a1)
{
...
v7 = (const std::string *)a1;
std::string::substr((std::string *)(&v2 + 4), (const std::string *)a1, 0i64, 6ui64);
O0ooOO00o += (unsigned __int8)O0oO00ooO((std::string *)&v3);// 第一次判断有没有通过
std::string::~string((std::string *)&v3);
O0oOo0Oo0(4, 5);
O0oOo0Oo0(9, 10);
O0oOo0Oo0(10, 11);
std::string::substr((std::string *)&v4, v7, 6ui64, 6ui64);
O0ooOO00o += (unsigned __int8)O0oO00ooO((std::string *)&v4);// 迷宫变换,第二次判断有没有通过
std::string::~string((std::string *)&v4);
O0oOo0Oo0(5, 7);
O0oOo0Oo0(9, 12);
O0oOo0Oo0(11, 12);
std::string::substr((std::string *)&v5, v7, 0xCui64, 6ui64);
O0ooOO00o += (unsigned __int8)O0oO00ooO((std::string *)&v5);// 迷宫变换,第三次判断有没有通过
std::string::~string((std::string *)&v5);
O0oOo0Oo0(2, 12);
O0oOOo0o0(7, 9);
O0oOOo0o0(5, 3);
O0oOOo0o0(5, 11);
O0oOOo0o0(4, 7);
O0oOOo0o0(4, 8);
std::string::substr((std::string *)&v6, v7, 0x12ui64, 0xFFFFFFFFFFFFFFFFui64);
O0ooOO00o += (unsigned __int8)O0oO00ooO((std::string *)&v6);// 迷宫变换,第四次判断有没有通过
return std::string::~string((std::string *)&v6);
}
通过动态调试,直接可以扣出来四个迷宫,四个不同的迷宫分别为
1
*************
*@***********
*-***********
*--**----****
*-***-**-****
*-***#**-****
*--*****-****
**-*****-****
**-------****
**-*-----****
**---**-*****
**-****--****
*************
2
*************
*@***********
*-***********
*-***-**-****
*--**----****
*-***#**-****
*--*****-****
**-*****-****
**-*-----****
**---**-*****
**-------****
**-****--****
*************
3
*************
*@***********
*-***********
*-***-**-****
*--*****-****
*-***#**-****
*--**----****
**-*****-****
**-****--****
**---**-*****
**-*-----****
**-------****
*************
4
*************
**-------****
*--*****-****
*-***-**-****
*--*****-****
*-***#**-****
*-***-**-****
*-***-**-****
*--**----****
**---**-*****
*--*****-****
*@***********
*************
其对应的走法分别为,其中走路的方向按照2.2中的定义来,将每一组数字为4进制编码
第1次
1111
1311
3333
3300
0002
2211
第2次
1111
1311
1330
3333
0000
2221
第3次
1111
1311
1331
3330
0300
2220
第4次
0300
2000
0003
0333
3331
1111
1122
2000
将其转换为16进制:
0x55,0x75,0xFF,0xF0,0x02,0xA5,0x55,0x75,0x7C,0xFF,0x00,0xA9,0x55,0x75,0x7D,0xFC,0x30,0xA8,0x30,0x80,0x03,0x3F,0xFD,0x55,0x5A,0x80
3 破解思路
理一下整体流程:
- 输入36个字符base64解码后变成26个 刚好和上面的 6 + 6 + 6 +8 对应起来
- 将输入的字符串的[16:24] 作为密钥加密由base64 解码后的 数据[18:26]
- 利用输入的[8:16]作为密钥解密上一步加密的结果
- 利用 输入的[0:8] 作为密钥加密上一步解密的结果
- 用 解密后的[0:18]+后面 输入的的8个字符作为迷宫步数来走
破解思路就是根据走迷宫的步数逆推
import base64 as base
from Crypto.Cipher import DES
input=base.b64encode(''.join(map(chr,[0x55,0x75,0xff,0xf0,0x2,0xa5, 0x55,0x75,0x7c,0xff,0x0,0xa9, 0x55,0x75,0x7d,0xfc,0x30,0xa8, 0x30,0x80,0x3,0x3f,0xfd,0x55,0x5a,0x80])))
key1=input[0:8]
D=DES.new(key1,DES.MODE_ECB)
dest=''
f=B.b64decode(input)[18:]
dest=D.encrypt(f)
key2=input[8:16]
D=DES.new(key2,DES.MODE_ECB)
dest=D.decrypt(dest)
key3=input[16:24]
D=DES.new(key3,DES.MODE_ECB)
dest=D.encrypt(dest)
f=input[:24]+base.b64encode(dest)
print 'flag is :'+f
得到flag:VXX/8AKlVXV8/wCpVXV9/DCogeWKJMtrmeY=