这题是一个用g++编译的C++逆向题,第一次碰到这种类型,边做边学习吧。
因为是初次接触C++逆向,如有不足,欢迎评论区指正。
一、main
IDA64查看main函数的伪代码如下,各个反汇编后伪代码的注释也已经给出,仅供参考:
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rbx
__int64 v5; // rax
char v6; // bl
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rax
__int64 v10; // rax
__int64 v11; // rax
__int64 v12; // rax
char v14[40]; // [rsp+0h] [rbp-140h] BYREF
__int64 v15; // [rsp+28h] [rbp-118h] BYREF
__int64 v16; // [rsp+30h] [rbp-110h] BYREF
int v17; // [rsp+3Ch] [rbp-104h] BYREF
char v18[32]; // [rsp+40h] [rbp-100h] BYREF
char v19[48]; // [rsp+60h] [rbp-E0h] BYREF
char v20[31]; // [rsp+90h] [rbp-B0h] BYREF
char v21; // [rsp+AFh] [rbp-91h] BYREF
char v22[47]; // [rsp+B0h] [rbp-90h] BYREF
char v23; // [rsp+DFh] [rbp-61h] BYREF
char v24[36]; // [rsp+E0h] [rbp-60h] BYREF
int v25; // [rsp+104h] [rbp-3Ch]
char *v26; // [rsp+108h] [rbp-38h]
int *v27; // [rsp+110h] [rbp-30h]
_DWORD *v28; // [rsp+118h] [rbp-28h]
int *v29; // [rsp+120h] [rbp-20h]
int i; // [rsp+128h] [rbp-18h]
unsigned int v31; // [rsp+12Ch] [rbp-14h]
v31 = 0;
/*创建一个int型容器*/
std::vector<int>::vector(v20, argv, envp);
/*创建一个bool型容器*/
std::vector<bool>::vector(v19);
/*构建一个字符串变量*/
std::allocator<char>::allocator(&v21);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v18, &unk_500E, &v21);
// 构造函数,中间那个basic_string<char,std::char_traits<char>,std::allocator<char>>是类模板,后面那个basic_string函数名表明这个函数是构造函数。
std::allocator<char>::~allocator(&v21);
/*输出字符串"give me your key!\n"*/
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "give me your key!");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
/*输入的数据存入keys[]数组*/
for ( i = 0; i <= 8; ++i )
{
std::istream::operator>>(&std::cin, &keys[i]);
//调用string头文件里的to_string全局函数,把输入的数据转为字符的形式存到v22中
std::__cxx11::to_string(v22, keys[i]);
//调用string头文件里basic_string类模板中的operator+函数把输入的数据存入v18
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(v18, v22);
//调用string头文件里basic_string类模板中的析构函数
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v22);
}
v28 = keys;
v29 = keys;
v27 = &unk_83E4;
while ( v29 != v27 )
{
v17 = *v29;
//将输入的压入v20容器
std::vector<int>::push_back(v20, &v17);
++v29;
}
/*获取容器尾指针*/
v4 = std::vector<int>::end(v20);
/*获取容器头指针*/
v5 = std::vector<int>::begin(v20);
/*调用for_each函数模板对从头指针到尾指针的每一项执行lambda函数*/
std::for_each<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,main::{lambda(int &)#1}>(v5, v4);
/*这里是一样的获取头尾指针*/
v26 = v20;
v16 = std::vector<int>::begin(v20);
v15 = std::vector<int>::end(v26);
//while循环的条件用的是gcc编译器的使用的C++库中定义的命名空间__gnu_cxx,其实是判断头尾指针是否相同。
while ( __gnu_cxx::operator!=<int *,std::vector<int>>(&v16, &v15) )
{
//取v16指向的地址中的值存入v25
v25 = *__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(&v16);
/*创建字符串变量v14*/
std::allocator<char>::allocator(&v23);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v14, &unk_500E, &v23);
std::allocator<char>::~allocator(&v23);
//加密后结果放到v14中
depart(v25, v14);
{lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> &)#1}::operator()(&func, v14);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v24, v14);
v6 = {lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,int)#2}::operator()(
&check,
v24,
v31) ^ 1;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v24);
if ( v6 )
{
v7 = std::operator<<<std::char_traits<char>>(&std::cout, "Wrong password!");
std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
system("pause");
exit(0);
}
++v31;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v14);
__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v16);
}
/*下面就是告诉我们md5加密(32位大写)后的v18就是flag*/
v8 = std::operator<<<std::char_traits<char>>(&std::cout, "right!");
std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
v9 = std::operator<<<std::char_traits<char>>(&std::cout, "flag:MRCTF{md5(");
v10 = std::operator<<<char>(v9, v18);
v11 = std::operator<<<std::char_traits<char>>(v10, ")}");
std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
v12 = std::operator<<<std::char_traits<char>>(
&std::cout,
"md5()->{32/upper case/put the string into the function and transform into md5 hash}");
std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
system("pause");
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v18);
std::vector<bool>::~vector(v19);
std::vector<int>::~vector(v20);
return 0;
}
二、depart
接下来看一下depart函数的伪代码。
其实看到这个函数的第一个循环我就想到了之前大一学C语言的时候学到的素数判断的算法,利用的数学原理如下:
待测试的数num只需要界于2到num的平方根之间的所有数,看它们是否可以整除num。
- 如果可以整除,说明输入的数不是素数;
- 如果不可整除,说明输入的数是素数。
就这个函数而言,可以发现,每找到一个因数就把除过后的数再进入depart找一次因数,可以看出是对数据的分解,把一个数分解成素因子的乘积,各因子间用空格分隔组合成字符串。
__int64 __fastcall depart(int a1, __int64 a2)
{
char v3[32]; // [rsp+20h] [rbp-60h] BYREF
char v4[40]; // [rsp+40h] [rbp-40h] BYREF
int i; // [rsp+68h] [rbp-18h]
int v6; // [rsp+6Ch] [rbp-14h]
/*分解成素因数*/
v6 = a1;
for ( i = 2; std::sqrt<int>(a1) >= i; ++i )
{
if ( !(a1 % i) )
{
v6 = i;
depart(a1 / i, a2);
break;
}
}
//运算后转换成字符串v4
std::__cxx11::to_string(v4, v6);
//合并&unk_500C(空格)和v4
std::operator+<char>(v3, &unk_500C, v4);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(a2, v3);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v3);
return std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v4);
}
三、3个lambda
我们接下来对下面这三个lambda函数进行分析。
(一)
__int64 __fastcall std::for_each<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,main::{lambda(int &)#1}>(__int64 a1, __int64 a2)
{
unsigned int v2; // ebx
__int64 v3; // rax
char v5; // [rsp+Fh] [rbp-21h] BYREF
__int64 v6; // [rsp+10h] [rbp-20h] BYREF
__int64 v7[3]; // [rsp+18h] [rbp-18h] BYREF
v7[0] = a1;
v6 = a2;
//循环条件是利用重载运算符判断两个是否相等
while ( __gnu_cxx::operator!=<int *,std::vector<int>>(v7, &v6) )
{
//取头指针v7的值
v3 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(v7);
main::{lambda(int &)#1}::operator()(&v5, v3);
//头指针移动
__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(v7);
}
return v2;
}
里面还嵌套了一个lambda函数:
_DWORD *__fastcall main::{lambda(int &)#1}::operator()(__int64 a1, _DWORD *a2)
{
_DWORD *result; // rax
result = a2;
*a2 ^= 1u;
return result;
}
可以看出是对每一项进行与1异或的操作。
(二)
进入第二个lambda函数,看着像是替换。
__int64 __fastcall {lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> &)#1}::operator()(__int64 a1, __int64 a2)
{
__int64 v2; // rbx
__int64 v3; // rax
__int64 v4; // rbx
__int64 v5; // rax
__int64 v6; // rbx
__int64 v7; // rax
__int64 v8; // rbx
__int64 v9; // rax
__int64 v10; // rbx
__int64 v11; // rax
__int64 v12; // rbx
__int64 v13; // rax
__int64 v14; // rbx
__int64 v15; // rax
__int64 v16; // rbx
__int64 v17; // rax
__int64 v18; // rbx
__int64 v19; // rax
__int64 v20; // rbx
__int64 v21; // rax
__int64 v22; // rbx
__int64 v23; // rax
char v25; // [rsp+1Ah] [rbp-26h] BYREF
char v26; // [rsp+1Bh] [rbp-25h] BYREF
char v27; // [rsp+1Ch] [rbp-24h] BYREF
char v28; // [rsp+1Dh] [rbp-23h] BYREF
char v29; // [rsp+1Eh] [rbp-22h] BYREF
char v30; // [rsp+1Fh] [rbp-21h] BYREF
char v31; // [rsp+20h] [rbp-20h] BYREF
char v32; // [rsp+21h] [rbp-1Fh] BYREF
char v33; // [rsp+22h] [rbp-1Eh] BYREF
char v34; // [rsp+23h] [rbp-1Dh] BYREF
char v35; // [rsp+24h] [rbp-1Ch] BYREF
char v36; // [rsp+25h] [rbp-1Bh] BYREF
char v37; // [rsp+26h] [rbp-1Ah] BYREF
char v38; // [rsp+27h] [rbp-19h] BYREF
char v39; // [rsp+28h] [rbp-18h] BYREF
char v40; // [rsp+29h] [rbp-17h] BYREF
char v41; // [rsp+2Ah] [rbp-16h] BYREF
char v42; // [rsp+2Bh] [rbp-15h] BYREF
char v43; // [rsp+2Ch] [rbp-14h] BYREF
char v44; // [rsp+2Dh] [rbp-13h] BYREF
char v45; // [rsp+2Eh] [rbp-12h] BYREF
char v46[17]; // [rsp+2Fh] [rbp-11h] BYREF
v25 = 'O';
v26 = '0';
v2 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v3 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v3,
v2,
&v26,
&v25);
v27 = 'l';
v28 = '1';
v4 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v5 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v5,
v4,
&v28,
&v27);
v29 = 'z';
v30 = '2';
v6 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v7 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v7,
v6,
&v30,
&v29);
v31 = 69;
v32 = 51;
v8 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v9 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v9,
v8,
&v32,
&v31);
v33 = 'A';
v34 = '4';
v10 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v11 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v11,
v10,
&v34,
&v33);
v35 = 's';
v36 = '5';
v12 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v13 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v13,
v12,
&v36,
&v35);
v37 = 'G';
v38 = '6';
v14 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v15 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v15,
v14,
&v38,
&v37);
v39 = 'T';
v40 = '7';
v16 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v17 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v17,
v16,
&v40,
&v39);
v41 = 'B';
v42 = '8';
v18 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v19 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v19,
v18,
&v42,
&v41);
v43 = 'q';
v44 = '9';
v20 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v21 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v21,
v20,
&v44,
&v43);
v45 = '=';
v46[0] = ' ';
v22 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
v23 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
return std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
v23,
v22,
v46,
&v45);
}
看看这个replace函数是怎么替换的。如下:
__int64 __fastcall std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(__int64 a1, __int64 a2, _BYTE *a3, _BYTE *a4)
{
__int64 result; // rax
__int64 v7; // [rsp+10h] [rbp-10h] BYREF
__int64 v8; // [rsp+18h] [rbp-8h] BYREF
v8 = a1;
v7 = a2;
while ( 1 )
{
result = __gnu_cxx::operator!=<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>(
&v8,
&v7);
if ( !result )
break;
if ( *__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>::operator*(&v8) == *a3 )
*__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>::operator*(&v8) = *a4;
__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>::operator++(&v8);
}
return result;
}
原来是对应替换,比如说,如果是0就变成了O,差不多这样的意思。
(三)
紧接着的lambda是一个检查函数,把加密后的字符串与一个已知字符串ans[abi:cxx11]做对比。
_BOOL8 __fastcall {lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,int)#2}::operator()(__int64 a1, __int64 a2, int a3)
{
const char *v3; // rbx
const char *v4; // rax
//调用basic_string类模板中的c_str函数把字符串转化为C语言的字符串,目的是为了接下来用strcmp函数。【兼容性考虑】
v3 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&ans[abi:cxx11] + 32 * a3);
v4 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(a2);
return strcmp(v4, v3) == 0;
}
我们现在来找这个已知字符串ans[abi:cxx11]。选中那个按X查看交叉引用,双击进入到函数。
int __fastcall __static_initialization_and_destruction_0(int a1, int a2)
{
int result; // eax
char v3; // [rsp+17h] [rbp-29h] BYREF
char v4; // [rsp+18h] [rbp-28h] BYREF
char v5; // [rsp+19h] [rbp-27h] BYREF
char v6; // [rsp+1Ah] [rbp-26h] BYREF
char v7; // [rsp+1Bh] [rbp-25h] BYREF
char v8; // [rsp+1Ch] [rbp-24h] BYREF
char v9; // [rsp+1Dh] [rbp-23h] BYREF
char v10; // [rsp+1Eh] [rbp-22h] BYREF
char v11[33]; // [rsp+1Fh] [rbp-21h] BYREF
if ( a1 == 1 && a2 == 0xFFFF )
{
std::ios_base::Init::Init(&std::__ioinit);
__cxa_atexit(&std::ios_base::Init::~Init, &std::__ioinit, &_dso_handle);
std::allocator<char>::allocator(&v3);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11],
"=zqE=z=z=z",
&v3);
std::allocator<char>::~allocator(&v3);
std::allocator<char>::allocator(&v4);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 32,
"=lzzE",
&v4);
std::allocator<char>::~allocator(&v4);
std::allocator<char>::allocator(&v5);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 64,
"=ll=T=s=s=E",
&v5);
std::allocator<char>::~allocator(&v5);
std::allocator<char>::allocator(&v6);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 96,
"=zATT",
&v6);
std::allocator<char>::~allocator(&v6);
std::allocator<char>::allocator(&v7);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 128,
"=s=s=s=E=E=E",
&v7);
std::allocator<char>::~allocator(&v7);
std::allocator<char>::allocator(&v8);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 160,
"=EOll=E",
&v8);
std::allocator<char>::~allocator(&v8);
std::allocator<char>::allocator(&v9);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 192,
"=lE=T=E=E=E",
&v9);
std::allocator<char>::~allocator(&v9);
std::allocator<char>::allocator(&v10);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 224,
"=EsE=s=z",
&v10);
std::allocator<char>::~allocator(&v10);
std::allocator<char>::allocator(v11);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
&ans[abi:cxx11] + 256,
"=AT=lE=ll",
v11);
std::allocator<char>::~allocator(v11);
result = __cxa_atexit(_tcf_0, 0LL, &_dso_handle);
}
return result;
}
都是很简单的初始化操作。很容易得到ans[abi:cxx11]。
分析完毕,梳理一下,对输入的9个数据先每项与1异或,然后遍历每项分解成素因子,排布,变成字符串之后(比如0 -> ‘0’)做替换然后与已有字符串作比较。
现在开始写Exp。
EXP
贴上我写的exp:
str1 = ["=zqE=z=z=z", "=lzzE", "=ll=T=s=s=E", "=zATT", "=s=s=s=E=E=E", "=EOll=E","=lE=T=E=E=E", "=EsE=s=z", "=AT=lE=ll"]
flagdata = [1,1,1,1,1,1,1,1,1]
flag=""
for i in range(len(str1)):
k = -1
str1[i] = str1[i].replace("O", "0")
str1[i] = str1[i].replace("l", "1")
str1[i] = str1[i].replace("z", "2")
str1[i] = str1[i].replace("E", "3")
str1[i] = str1[i].replace("A", "4")
str1[i] = str1[i].replace("s", "5")
str1[i] = str1[i].replace("G", "6")
str1[i] = str1[i].replace("T", "7")
str1[i] = str1[i].replace("B", "8")
str1[i] = str1[i].replace("q", "9")
str1[i] = str1[i].replace("=", " ")
str1[i] += ' '
a = []
x = 0
for j in str1[i]:
if j == ' ':
a.append(x)
k += 1
x = 0
continue
x = 10 * x + int(j)
for b in range(1,len(a)):
flagdata[i] *= a[b]
for i in range(len(flagdata)):
flag+=str(flagdata[i] ^ 1)
print(flag)
别的博主的exp:
strs=["=zqE=z=z=z","=lzzE","=ll=T=s=s=E","=zATT","=s=s=s=E=E=E","=EOll=E","=lE=T=E=E=E","=EsE=s=z","=AT=lE=ll"]
def replacediy(str):
str=str.replace("O","0")
str=str.replace("l","1")
str=str.replace("z","2")
str=str.replace("E","3")
str=str.replace("A","4")
str=str.replace("s","5")
str=str.replace("G","6")
str=str.replace("T","7")
str=str.replace("B","8")
str=str.replace("q","9")
str=str.replace("="," ")
return str
flag=""
for i in strs:
tmp=replacediy(i).split(" ")[1:]
print tmp
sum=1
for j in range(len(tmp)):
sum*=int(tmp[j],10)
sum^=1
flag+=str(sum)
print flag
用了split函数进行数值划分,这是我没想到的。。。。。
最后是md5加密,32位大写,这里我直接用模块来加密了,exp如下:
import hashlib
m=hashlib.md5()
str1="234512225774247633749032245635316720"
m.update(str1.encode('utf-8'))
print(m.hexdigest())
最后的flag就是
flag{4367FB5F42C6E46B2AF79BF409FB84D3}
相关参考与拓展:
1.有关注释中的知识,可以参考:
2.特别的,有关C++中c_str函数参考C++中的C_str()函数