目录
法二:使用基于angr的脚本deflat.py去除控制流平坦化
本文主要针对x86架构下Obfuscator-LLVM的控制流平坦化
OLLVM
llvm是一个底层虚拟机,OLLVM(Obfuscator-LLVM)是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,这个项目的目标是提供一个LLVM编译套件的开源分支,能够通过代码混淆和防篡改,增加对逆向工程的难度,提供更高的软件安全性。目前,OLLVM已经支持LLVM-4.0.1版本;所使用的编译器是clang。
llvm保护的大概方法是:程序主要使用一个主分发器来控制程序基本块的执行流程进而模糊每个模块之间的联系。
控制流平坦化
控制流平坦化(control flow flattening)的基本思想主要是通过一个主分发器来控制程序基本块的执行流程,例如下图是正常的执行流程
添加控制流平坦化
build/bin/clang check_passwd.c -o check_passwd_flat -mllvm -fla
经过控制流平坦化后的执行流程就如下图
这样可以模糊基本块之间的前后关系,增加程序分析的难度,同时这个流程也很像VM的执行流程
平坦化程序特征
- 函数的开始地址为序言的地址
- 序言的后继为主分发器
- 后继为主分发器的块为预处理器
- 后继为预处理器的块为真实块
- 无后继的块为retn块
- 剩下的为无用块
去平坦化方法
法一:D810去平坦化插件使用方法
D-810是一个基于IDA Microcode实现的可拓展的ida去混淆插件,尤其是在CTF中碰到控制流平坦化时可以使用默认的脚本F5一键去平坦化。
1)安装
把文件夹和.py复制到IDA目录的plugin下即可
(另外建议安装Z3以便能够使用 D-810 的几个功能
pip install z3-solver
2)使用
启动IDA
在Edit->plugins-D-810 或 快捷键CTRL+shift+D 启动D810
在Current file loaded中可以选择要载入的去混淆规则,如去平坦化
1:选中你需要的反混淆规则,我是反ollvm所以选ollvm的
2:start 点击后右边会变成绿色loaded
3:回到需要反混淆的函数,F5大法好
(此处Decompiling需要等一会
同理,点击stop关闭去混淆
(如果安装完出现这种问题
参考
https://github.com/igogo-x86/HexRaysPyTools/issues/48
重新安装sip.pyd即可
法二:使用基于angr的脚本deflat.py去除控制流平坦化
安装
1) 首先安装angr库
Windows + R 输入cmd
pip install angr
2) 脚本github地址deflat: use angr to deobfuscation
把am_graph.py
与deflat.py放在同一目录下
所在目录下打开cmd命令行输入
python + deflat.py + 文件名 + 起始地址(基本就是main函数的地址)
#python deflat.py hardCpp 0x4007E0
关键之处在于寻找程序的 address
注意到“函数的开始地址为序言的地址”,因此我们需要在序言中找函数起始位置
期间可能会出现大量类似警告:
没事,只要最后是这样就好:
得到一个已成功去平坦化的recovered新文件,用这个文件继续分析
参考
反混淆器:D810的安装和使用 #ollvm去平坦化 #D-810 - 『逆向资源区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
OLLVM 与去平坦化 & [RoarCTF2019] polyre 详细WP - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
利用符号执行去除控制流平坦化 - 博客 - 腾讯安全应急响应中心
例题--[SUCTF2019]hardcpp
用ollvm混淆过的c++代码
开头给了一个类似哈希的东西,先不管。
ollvm中应该开了运算混淆,流程平坦化和一些虚假分支,调试一下发现主要流程就在那一堆lamda那里。
法一:D810插件去混淆
在Edit->plugins-D-810 或 快捷键CTRL+shift+D 启动D810
在Current file loaded中可以选择要载入的去混淆规则,我们要去ollvm平坦化所以选第二个
回到需要反混淆的函数,按F5,此处Decompiling需要等一会
这样就已经可读了
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
char v4; // al
char v5; // al
char v6; // al
char v8[8]; // [rsp+A0h] [rbp-90h] BYREF
char v9[8]; // [rsp+A8h] [rbp-88h] BYREF
char v10[8]; // [rsp+B0h] [rbp-80h] BYREF
char v11[8]; // [rsp+B8h] [rbp-78h] BYREF
char v12[8]; // [rsp+C0h] [rbp-70h] BYREF
char v13[7]; // [rsp+C8h] [rbp-68h] BYREF
char v14; // [rsp+CFh] [rbp-61h]
int i; // [rsp+D0h] [rbp-60h]
int v16; // [rsp+D4h] [rbp-5Ch]
int v17; // [rsp+D8h] [rbp-58h]
int v18; // [rsp+DCh] [rbp-54h]
char s; // [rsp+E0h] [rbp-50h] BYREF
char v20[23]; // [rsp+E1h] [rbp-4Fh] BYREF
char v21[8]; // [rsp+F8h] [rbp-38h] BYREF
char v22[8]; // [rsp+100h] [rbp-30h] BYREF
char v23[8]; // [rsp+108h] [rbp-28h] BYREF
char v24[4]; // [rsp+110h] [rbp-20h] BYREF
int v25; // [rsp+114h] [rbp-1Ch]
const char **v26; // [rsp+118h] [rbp-18h]
int v27; // [rsp+120h] [rbp-10h]
int v28; // [rsp+124h] [rbp-Ch]
int v29; // [rsp+128h] [rbp-8h]
bool v30; // [rsp+12Eh] [rbp-2h]
v28 = 0;
v27 = argc;
v26 = argv;
v25 = time(0LL);
puts("func(?)=\\"01abfc750a0c942167651c40d088531d\\"?");
s = getchar();
fgets(v20, 21, stdin);
v18 = time(0LL);
v17 = v18 - v25;
v29 = v18 - v25;
if ( v18 - v25 > 0 )
{
puts("Let the silent second hand take the place of my doubt...");
exit(0);
}
v16 = strlen(&s);
v30 = v16 != 21;
if ( v16 != 21 )
exit(0);
for ( i = 1; i < 21; ++i )
{
v14 = v17 ^ v20[i - 1];
v13[0] = main::$_0::operator()(v23, (unsigned int)v14);
v12[0] = main::$_1::operator()(v21, (unsigned int)*(&s + v17 + i - 1));
v3 = main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v12, 7LL);
v14 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v13, (unsigned int)v3);
v11[0] = main::$_2::operator()(v24, (unsigned int)v14);
v10[0] = main::$_2::operator()(v24, (unsigned int)*(&s + v17 + i - 1));
v4 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v10, 18LL);
v9[0] = main::$_3::operator()(v22, (unsigned int)v4);
v5 = main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v9, 3LL);
v8[0] = main::$_0::operator()(v23, (unsigned int)v5);
v6 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v8, 2LL);
v14 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v11, (unsigned int)v6);
if ( enc[i - 1] != v14 )
exit(0);
}
puts("You win");
return 0;
}
法二:deflat.py去OLLVM平坦化
在deflat.py同一目录下打开cmd命令行输入
python deflat.py hardCpp 0x4007E0
期间可能会出现大量类似警告:
没事,只要最后是这样就好:
得到
此时控制流
还有部分去平坦化操作
首先排除上面这两个循环有用
看到这一坨函数,其实就是c++里面的奇奇怪怪的东西(好像叫lambda表达式),来看看他们到底是干嘛的,
在对各函数分析的过程中发现在0x401310、0x4014E0和0x4016C0依然有平坦化,
类似这样
再跑脚本去一下
这样重复三次
python deflat.py hardCpp_recovered 0x401310
python deflat.py hardCpp_recovered_recovered 0x4016C0
python deflat.py hardCpp_recovered_recovered_recovered 0x4014E0
静态查看,发现其实是对输入的每一个字符执行了一系列lambda函数,输入的长度为21个字符
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
char v4; // al
char v5; // al
char v6; // al
char v8; // al
char v9; // al
char v10; // al
char v11; // al
char v12[8]; // [rsp+A0h] [rbp-90h] BYREF
char v13[8]; // [rsp+A8h] [rbp-88h] BYREF
char v14[8]; // [rsp+B0h] [rbp-80h] BYREF
char v15[8]; // [rsp+B8h] [rbp-78h] BYREF
char v16[8]; // [rsp+C0h] [rbp-70h] BYREF
char v17[7]; // [rsp+C8h] [rbp-68h] BYREF
char v18; // [rsp+CFh] [rbp-61h]
int v19; // [rsp+D0h] [rbp-60h]
int v20; // [rsp+D4h] [rbp-5Ch]
int v21; // [rsp+D8h] [rbp-58h]
int v22; // [rsp+DCh] [rbp-54h]
char s; // [rsp+E0h] [rbp-50h] BYREF
char v24[23]; // [rsp+E1h] [rbp-4Fh] BYREF
char v25[8]; // [rsp+F8h] [rbp-38h] BYREF
char v26[8]; // [rsp+100h] [rbp-30h] BYREF
char v27[8]; // [rsp+108h] [rbp-28h] BYREF
char v28[4]; // [rsp+110h] [rbp-20h] BYREF
int v29; // [rsp+114h] [rbp-1Ch]
const char **v30; // [rsp+118h] [rbp-18h]
int v31; // [rsp+120h] [rbp-10h]
int v32; // [rsp+124h] [rbp-Ch]
int v33; // [rsp+128h] [rbp-8h]
bool v34; // [rsp+12Eh] [rbp-2h]
v32 = 0;
v31 = argc;
v30 = argv;
v29 = time(0LL);
puts("func(?)=\\"01abfc750a0c942167651c40d088531d\\"?");
s = getchar();
fgets(v24, 21, stdin);
v22 = time(0LL);
v21 = v22 - v29;
v33 = v22 - v29;
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_13;
while ( 1 )
{
v20 = strlen(&s);
v34 = v20 != 21;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_13:
v20 = strlen(&s);
}
while ( 1 )
{
v19 = 1;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
v19 = 1;
}
while ( v19 < 21 )
{
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
{
v18 = v21 ^ v24[v19 - 1];
v17[0] = main::$_0::operator()(v27, (unsigned int)v18);
v16[0] = main::$_1::operator()(v25, (unsigned int)*(&s + v21 + v19 - 1));
v8 = main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v16, 7LL);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v17, (unsigned int)v8);
v15[0] = main::$_2::operator()(v28, (unsigned int)v18);
v14[0] = main::$_2::operator()(v28, (unsigned int)*(&s + v21 + v19 - 1));
v9 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v14, 18LL);
v13[0] = main::$_3::operator()(v26, (unsigned int)v9);
v10 = main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v13, 3LL);
v12[0] = main::$_0::operator()(v27, (unsigned int)v10);
v11 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v12, 2LL);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v15, (unsigned int)v11);
}
do
{
v18 = v21 ^ v24[v19 - 1];
v17[0] = main::$_0::operator()(v27, (unsigned int)v18);
v16[0] = main::$_1::operator()(v25, (unsigned int)*(&s + v21 + v19 - 1));
v3 = main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v16, 7LL);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v17, (unsigned int)v3);
v15[0] = main::$_2::operator()(v28, (unsigned int)v18);
v14[0] = main::$_2::operator()(v28, (unsigned int)*(&s + v21 + v19 - 1));
v4 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v14, 18LL);
v13[0] = main::$_3::operator()(v26, (unsigned int)v4);
v5 = main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v13, 3LL);
v12[0] = main::$_0::operator()(v27, (unsigned int)v5);
v6 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v12, 2LL);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v15, (unsigned int)v6);
}
while ( enc[v19 - 1] != v18 );
while ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
;
++v19;
}
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_16;
while ( 1 )
{
puts("You win");
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_16:
puts("You win");
}
return 0;
}
cpp的lambda这种是可以直接双击查看其具体实现的,逐个进去分析每个lambda的含义:
char __fastcall main::$_0::operator()
char __fastcall main::$_1::operator()
_int64 __fastcall main::$_1::operator() const(char)::{lambda(int)#1}::operator()
__int64 __fastcall main::$_0::operator() const(char)::{lambda(char)#1}::operator()
__int64 __fastcall main::$_0::operator() const(char)::{lambda(char)#1}::operator()(__int64 a1, char a2)
{
_QWORD v3[3]; // [rsp+0h] [rbp-40h]
__int64 v4; // [rsp+18h] [rbp-28h]
char v5; // [rsp+23h] [rbp-1Dh]
int v6; // [rsp+24h] [rbp-1Ch]
bool v7; // [rsp+2Ah] [rbp-16h]
bool v8; // [rsp+2Bh] [rbp-15h]
unsigned int v9; // [rsp+2Ch] [rbp-14h]
v7 = ((((_BYTE)x_5 - 1) * (_BYTE)x_5) & 1) == 0;
v8 = y_6 < 10;
v6 = 1023500310;
v5 = a2;
if ( y_6 >= 10 && !v7 )
goto LABEL_4;
while ( 1 )
{
v3[0] = v4;
LOBYTE(v3[-2]) = v5;
v9 = SLOBYTE(v3[-2]) + *(char *)v3[0]; // 相加
if ( y_6 < 10 || ((((_BYTE)x_5 - 1) * (_BYTE)x_5) & 1) == 0 )
break;
LABEL_4:
v3[0] = v4;
LOBYTE(v3[-2]) = v5;
}
return v9;
}
char __fastcall main::$_2::operator()
LOBYTE是一个宏,用于从一个整数或字符中提取最低有效字节(即最低8位)。它的作用是获取给定值的最低8位字节,并将其作为一个单独的字节值返回。
返回 (a1 & 0xff00) + (a2 & 0x00ff)
• 看出v_result = 高位(v_a1) 低位(v_a2)
char __fastcall main::$_2::operator()(__int64 a1, char a2)
{
_QWORD v3[4]; // [rsp+0h] [rbp-50h]
__int64 v4; // [rsp+20h] [rbp-30h]
char v5; // [rsp+2Fh] [rbp-21h]
int v6; // [rsp+30h] [rbp-20h]
bool v7; // [rsp+35h] [rbp-1Bh]
bool v8; // [rsp+36h] [rbp-1Ah]
char v9; // [rsp+37h] [rbp-19h]
v7 = ((((_BYTE)x_11 - 1) * (_BYTE)x_11) & 1) == 0;
v8 = y_12 < 10;
v6 = -1990873412;
v5 = a2;
if ( y_12 >= 10 && !v7 )
goto LABEL_4;
while ( 1 )
{
v3[0] = v4;
LOBYTE(v3[-2]) = v5;
LOBYTE(v3[0]) = v3[-2];
v9 = v3[0];
if ( y_12 < 10 || ((((_BYTE)x_11 - 1) * (_BYTE)x_11) & 1) == 0 )
break;
LABEL_4:
v3[0] = v4;
LOBYTE(v3[-2]) = v5;
LOBYTE(v3[0]) = v3[-2];
}
return v9;
}
__int64 __fastcall main::$_2::operator() const(char)::{lambda(char)#1}::operator()
char __fastcall main::$_3::operator()
char __fastcall main::$_3::operator()(__int64 a1, char a2)
{
_QWORD v3[4]; // [rsp+0h] [rbp-50h]
__int64 v4; // [rsp+20h] [rbp-30h]
char v5; // [rsp+2Fh] [rbp-21h]
int v6; // [rsp+30h] [rbp-20h]
bool v7; // [rsp+35h] [rbp-1Bh]
bool v8; // [rsp+36h] [rbp-1Ah]
char v9; // [rsp+37h] [rbp-19h]
v7 = ((((_BYTE)x_15 - 1) * (_BYTE)x_15) & 1) == 0;
v8 = y_16 < 10;
v6 = -538471561;
v5 = a2;
if ( y_16 >= 10 && !v7 )
goto LABEL_4;
while ( 1 )
{
v3[0] = v4;
LOBYTE(v3[-2]) = v5;
LOBYTE(v3[0]) = v3[-2];
v9 = v3[0];
if ( y_16 < 10 || ((((_BYTE)x_15 - 1) * (_BYTE)x_15) & 1) == 0 )
break;
LABEL_4:
v3[0] = v4;
LOBYTE(v3[-2]) = v5;
LOBYTE(v3[0]) = v3[-2];
}
return v9;
}
__int64 __fastcall main::$_3::operator() const(char)::{lambda(char)#1}::operator()
综上所述,程序的流程化简为
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
char v4; // al
char v5; // al
char v6; // al
char v8; // al
char v9; // al
char v10; // al
char v11; // al
char v12[8]; // [rsp+A0h] [rbp-90h] BYREF
char v13[8]; // [rsp+A8h] [rbp-88h] BYREF
char v14[8]; // [rsp+B0h] [rbp-80h] BYREF
char v15[8]; // [rsp+B8h] [rbp-78h] BYREF
char v16[8]; // [rsp+C0h] [rbp-70h] BYREF
char v17[7]; // [rsp+C8h] [rbp-68h] BYREF
char v18; // [rsp+CFh] [rbp-61h]
int v_pos; // [rsp+D0h] [rbp-60h]
int v20; // [rsp+D4h] [rbp-5Ch]
int v_equal_0; // [rsp+D8h] [rbp-58h]
int v22; // [rsp+DCh] [rbp-54h]
char v_input_chr; // [rsp+E0h] [rbp-50h] BYREF
char v_input[23]; // [rsp+E1h] [rbp-4Fh] BYREF
char v25[8]; // [rsp+F8h] [rbp-38h] BYREF
char v26[8]; // [rsp+100h] [rbp-30h] BYREF
char v27[8]; // [rsp+108h] [rbp-28h] BYREF
char v28[4]; // [rsp+110h] [rbp-20h] BYREF
int v29; // [rsp+114h] [rbp-1Ch]
const char **v30; // [rsp+118h] [rbp-18h]
int v31; // [rsp+120h] [rbp-10h]
int v32; // [rsp+124h] [rbp-Ch]
int v33; // [rsp+128h] [rbp-8h]
bool v34; // [rsp+12Eh] [rbp-2h]
v32 = 0;
v31 = argc;
v30 = argv;
v29 = time(0LL);
puts("func(?)=\\"01abfc750a0c942167651c40d088531d\\"?");
v_input_chr = getchar();
fgets(v_input, 21, stdin);
v22 = time(0LL);
v_equal_0 = v22 - v29;
v33 = v22 - v29;
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_13;
while ( 1 )
{
v20 = strlen(&v_input_chr);
v34 = v20 != 21;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_13:
v20 = strlen(&v_input_chr);
}
while ( 1 )
{
v_pos = 1;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
v_pos = 1;
}
while ( v_pos < 21 )
{
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
{
v18 = v_equal_0 ^ v_input[v_pos - 1]; // v_input[i-1]
v17[0] = f_a2((__int64)v27, v18); // v_input[i]
v16[0] = f_a2_same((__int64)v25, *(&v_input_chr + v_equal_0 + v_pos - 1));// v_input[i-1]
v8 = f_a1_mod_a2(v16, 7); // v_input[i-1] % 7
v18 = f_a1_add_a2(v17, (unsigned int)v8); // v_input[i] + v_input[i-1] % 7
v15[0] = f_byte_merge((__int64)v28, v18); // v_input[i] + v_input[i-1] % 7
v14[0] = f_byte_merge((__int64)v28, *(&v_input_chr + v_equal_0 + v_pos - 1));// v_input[i-1]
v9 = f_a1_xor_a2(v14, 18); // v_input[i-1] ^ 18
v13[0] = f_byte_merge_same(v26, (unsigned int)v9);// v_prev_xor_18
v10 = f_a1_multi_a2(v13, 3LL); // v_prev_xor_18_mutil_3
v12[0] = f_a2((__int64)v27, v10); // (v_prev ^ 18) * 3
v11 = f_a1_add_a2(v12, 2LL); // (v_prev ^ 18) * 3 + 2
v18 = f_a1_xor_a2(v15, v11); // (v_input[i] + v_input[i-1] % 7) ^ ((v_prev ^ 18) * 3 + 2)
}
do
{
v18 = v_equal_0 ^ v_input[v_pos - 1];
v17[0] = f_a2((__int64)v27, v18);
v16[0] = f_a2_same((__int64)v25, *(&v_input_chr + v_equal_0 + v_pos - 1));
v3 = f_a1_mod_a2(v16, 7);
v18 = f_a1_add_a2(v17, (unsigned int)v3);
v15[0] = f_byte_merge((__int64)v28, v18);
v14[0] = f_byte_merge((__int64)v28, *(&v_input_chr + v_equal_0 + v_pos - 1));
v4 = f_a1_xor_a2(v14, 18);
v13[0] = f_byte_merge_same(v26, (unsigned int)v4);
v5 = f_a1_multi_a2(v13, 3LL);
v12[0] = f_a2((__int64)v27, v5);
v6 = f_a1_add_a2(v12, 2LL);
v18 = f_a1_xor_a2(v15, v6);
}
while ( enc[v_pos - 1] != v18 ); // 判断enc[v_pos-1] == (v_input[i] + v_input[i-1] % 7) ^ ((v_prev ^ 18) * 3 + 2)
// }
while ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
;
++v_pos;
}
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_16;
while ( 1 )
{
puts("You win");
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_16:
puts("You win");
}
return 0;
}
• 综上,题意为迭代判断 enc[v_pos-1] == (v_input[i] + v_input[i-1] % 7) ^ ((v_prev ^ 18) * 3 + 2)
unsigned char enc[] =
{
0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C,
0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5
};
再次观察代码
猜测是md5,将其拖入到MD5在线加密/解密/破解—MD5在线的md5解密中,得到其值为md5('#') 则v_input[0] = ‘#’
EXP
v_enc = 'F32E1836E14C22D1F98C4076F40E0005A3900EA5'
v_enc = bytearray.fromhex(v_enc)
# puts("func(?)="01abfc750a0c942167651c40d088531d"?");// md5('#')
v_input_chr = ord('#')
v_input = [v_input_chr]
# 原式: (v_input[i] + v_input[i-1] % 7) ^ ((v_input[i-1] ^ 18) * 3 + 2) == v_enc[i-1]
# 两边同时异或((v_input[i-1] ^ 18) * 3 + 2),
# 化简为:(v_input[i] + v_input[i-1] % 7) = v_enc[i-1] ^ ((v_input[i-1] ^ 18) * 3 + 2)
# 将左边移到右边,化简为:v_input[i] = (v_enc[i-1] ^ ((v_input[i-1] ^ 18) * 3 + 2)) - v_input[i-1] % 7
for i in range(1, 21):
v = ((v_input[i-1] ^ 18) * 3 + 2)
v_input_i = (v_enc[i-1] ^ v) - (v_input[i-1] % 7)
v_input_i &= 0xff # 可打印的字符串范围必然在 0xff 以内,此处防溢出
v_input.append(v_input_i)
flag = [chr(x) for x in v_input]
print(''.join(flag))
#flag{mY-CurR1ed_Fns}
另一种写法exp
enc=[0xf3,0x2e,0x18,0x36,0xe1,0x4c,0x22,0xd1,0xf9,0x8c,0x40,0x76,0xf4,0xe,0x0,0x5,0xa3,0x90,0xe,0xa5]
flag='#'
for i in range(0,20):
flag+=chr(((enc[i]^((ord(flag[-1])^18)*3+2))-(ord(flag[-1])%7)) &0xff)
print(flag)
flag[-1]
是指变量flag
中的最后一个字符。
如果看不出是MD5,也可以爆破出第一位
只需要爆破下标为0的字符,即可求出flag
enc = [ 0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C,
0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5]
for j in range(256):
a = [j]
for i in range(0,20):
a.append(((enc[i] ^ ((a[i]^18)*3+2))-(a[i]%7))&0xff)
s = "".join(map(chr,a))
if "flag" in s:
print(s)
通过尝试不同的密钥值来解密加密列表 enc
,并检查解密后的结果是否包含 "flag" 字符串。如果找到了包含 "flag" 的解密结果,将其打印出来
SUCTF 2019 Writeup — De1ta - 先知社区 (aliyun.com)