☆ 背景介绍
OLLVM支持"Bogus Control Flow/虚假控制流"。启用bcf编译源码,生成的二进制用IDA反汇编时,会看到许多实际执行时永不可达的基本块。冗余的不可达块对IDA F5带来干扰,肉眼看不出原始代码逻辑,同时不影响实际执行逻辑。bcf目的就是对抗静态分析。
OLLVM项目提供过一张示意图
上图那些条件判断,实际恒为true,执行时沿true前进,与原始逻辑无异。但静态分析时,受false干扰,并不知道false路径永不可达。
参看
作者用「angr符号执行」识别目标程序中不可达块,静态Patch目标程序,将不可达块NOP化。虽然IDA反汇编结构图仍然显乱,但F5比较智能,已能去干扰,显示出原始逻辑。
假设angr符号执行能精确识别干扰性质的条件跳转指令,我选择将所有干扰性质的条件跳转指令静态Patch成无条件跳转指令,直接跳向相应可达块;此方案本质上等价于NOP化不可达块。
本文以学习angr进阶用法为目的,借bcf反混淆为靶标,演示JMP化思路。
☆ hello.c
用某版OLLVM启用bcf编译,得到hello_bcf。
完整测试用例打包
☆ hello_bcf
IDA64反汇编hello_bcf
F5的伪代码没必要深究,看个大概即可。
☆ hello_bcf_patch.py
这是对付hello_bcf的完整代码,演示性质,非通用实现。
IDA64反汇编hello_bcf_new_*,F5查看main、sub_4014E0,已能看出hello.c所展示的代码逻辑。
☆ get_cond_jmp_list的技术原理
hello_bcf_patch.py的核心是获取那些永远只走同一条分支的条件跳转指令,与下列代码强相关
进一步说,核心代码是
是否hook子函数,要看子函数的返回值对父函数流程产生何种影响。单就hello_bcf而言,是否hook子函数,不影响最终结果,hello_bcf_new_*是一样的。
假设需要hook子函数,在successors()中完成,不必遍历"active stash",不必动用block.capstone.insns。
发现永远只走同一条分支的条件跳转指令后,记录(from,to);将来对from处的条件跳转指令(比如jnz)进行Patch,改成jmp,演示时假设均可用"eb xx"短跳转。
重载successors()的方案,带有巨大的Hacking性,只用hello_bcf测试过,仅为PoC,非健壮通用实现。不论目标样本如何变,本例展示的angr技术始终派得上用场,只是反混淆逻辑要case by case。
建议动态调试,加强理解angr流程。
创建:
2025
-
05
-
28
17
:
14
更新:
2025
-
06
-
03
14
:
31
目录:
☆ 背景介绍
☆ hello.c
☆ hello_bcf
☆ hello_bcf_patch.py
☆ get_cond_jmp_list的技术原理
创建:
2025
-
05
-
28
17
:
14
更新:
2025
-
06
-
03
14
:
31
目录:
☆ 背景介绍
☆ hello.c
☆ hello_bcf
☆ hello_bcf_patch.py
☆ get_cond_jmp_list的技术原理
原始流程
entry
|
______v______
| original |
|_____________|
|
v
return
原始流程
entry
|
______v______
| original |
|_____________|
|
v
return
启用bcf之后的流程
entry
|
____v_____
|condition
*
| (false)
|__________|
-
-
-
-
+
(true)| |
| |
______v______ |
+
-
-
>| original
*
| |
| |_____________| (true)
| (false)| !
-
-
-
-
-
-
-
-
-
-
-
>
return
| ______v______ |
| | altered |<
-
-
!
| |_____________|
|__________|
启用bcf之后的流程
entry
|
____v_____
|condition
*
| (false)
|__________|
-
-
-
-
+
(true)| |
| |
______v______ |
+
-
-
>| original
*
| |
| |_____________| (true)
| (false)| !
-
-
-
-
-
-
-
-
-
-
-
>
return
| ______v______ |
| | altered |<
-
-
!
| |_____________|
|__________|
利用angr符号执行去除虚假控制流
-
34r7hm4n
[
2021
-
02
-
10
]
https:
/
/
bbs.kanxue.com
/
thread
-
266005.htm
利用angr符号执行去除虚假控制流
-
34r7hm4n
[
2021
-
02
-
10
]
https:
/
/
bbs.kanxue.com
/
thread
-
266005.htm
NOP化之后的流程
entry
|
____v_____
|condition
*
| (false)
|__________|
-
-
-
-
+
(true)| |
| |
______v______ |
+
-
-
>| original
*
| |
| |_____________| (true)
| (false)| !
-
-
-
-
-
-
-
-
-
-
-
>
return
| ______v______ |
| | nop |<
-
-
!
| |_____________|
|__________|
NOP化之后的流程
entry
|
____v_____
|condition
*
| (false)
|__________|
-
-
-
-
+
(true)| |
| |
______v______ |
+
-
-
>| original
*
| |
| |_____________| (true)
| (false)| !
-
-
-
-
-
-
-
-
-
-
-
>
return
| ______v______ |
| | nop |<
-
-
!
| |_____________|
|__________|
JMP化之后的流程
entry
|
____v_____
|condition
*
|
|__________|
(jmp)|
|
______v______
+
-
-
>| original
*
|
| |_____________| (jmp)
| !
-
-
-
-
-
-
-
-
-
-
-
>
return
| ______ ______
| | altered |
| |_____________|
|__________|
JMP化之后的流程
entry
|
____v_____
|condition
*
|
|__________|
(jmp)|
|
______v______
+
-
-
>| original
*
|
| |_____________| (jmp)
| !
-
-
-
-
-
-
-
-
-
-
-
>
return
| ______ ______
| | altered |
| |_____________|
|__________|
$ pip3 show angr | grep Version
Version:
9.2
.
125.dev0
$ pip3 show angr | grep Version
Version:
9.2
.
125.dev0
static unsigned
int
foo ( unsigned
int
n )
{
unsigned
int
mod
=
n
%
4
;
unsigned
int
ret
=
0
;
if
( mod
=
=
0
)
{
ret
=
( n |
0xbaaad0bf
)
*
(
2
^ n );
}
else
if
( mod
=
=
1
)
{
ret
=
( n &
0xbaaad0bf
)
*
(
3
+
n );
}
else
if
( mod
=
=
2
)
{
ret
=
( n ^
0xbaaad0bf
)
*
(
4
| n );
}
else
{
ret
=
( n
+
0xbaaad0bf
)
*
(
5
& n );
}
return
ret;
}
int
main (
int
argc, char
*
argv[] )
{
unsigned
int
n,
ret;
if
( argc <
2
)
{
fprintf( stderr,
"Usage: %s <num>\n"
, argv[
0
] );
return
-
1
;
}
n
=
(unsigned
int
)strtoul( argv[
1
], NULL,
0
);
ret
=
foo( n );
fprintf( stdout,
"n=%#x ret=%#x\n"
, n, ret );
return
0
;
}
static unsigned
int
foo ( unsigned
int
n )
{
unsigned
int
mod
=
n
%
4
;
unsigned
int
ret
=
0
;
if
( mod
=
=
0
)
{
ret
=
( n |
0xbaaad0bf
)
*
(
2
^ n );
}
else
if
( mod
=
=
1
)
{
ret
=
( n &
0xbaaad0bf
)
*
(
3
+
n );
}
else
if
( mod
=
=
2
)
{
ret
=
( n ^
0xbaaad0bf
)
*
(
4
| n );
}
else
{
ret
=
( n
+
0xbaaad0bf
)
*
(
5
& n );
}
return
ret;
}
int
main (
int
argc, char
*
argv[] )
{
unsigned
int
n,
ret;
if
( argc <
2
)
{
fprintf( stderr,
"Usage: %s <num>\n"
, argv[
0
] );
return
-
1
;
}
n
=
(unsigned
int
)strtoul( argv[
1
], NULL,
0
);
ret
=
foo( n );
fprintf( stdout,
"n=%#x ret=%#x\n"
, n, ret );
return
0
;
}
clang
-
pipe
-
O0
-
s
-
mllvm
-
passes
=
bcf
-
o hello_bcf hello.c
clang
-
pipe
-
O0
-
s
-
mllvm
-
passes
=
bcf
-
o hello_bcf hello.c
https:
/
/
scz.
617.cn
/
unix
/
202505281714.txt
https:
/
/
scz.
617.cn
/
unix
/
202505281714.7z
https:
/
/
scz.
617.cn
/
unix
/
202505281714.txt
https:
/
/
scz.
617.cn
/
unix
/
202505281714.7z
$
file
-
b hello_bcf
ELF
64
-
bit LSB executable, x86
-
64
, ..., stripped
$
file
-
b hello_bcf
ELF
64
-
bit LSB executable, x86
-
64
, ..., stripped
__int64 __fastcall main(
int
a1, char
*
*
a2, char
*
*
a3)
{
...
v24
=
a1;
v25
=
a2;
/
/
/
/
unk_4040??位于.bss,实际运行时初始化为
0
。下列布尔值恒false
/
/
if
( ((unk_404098
*
(unk_404098
+
1
)) &
1
) !
=
0
&& unk_4040F8 >
=
10
)
/
/
/
/
此跳转永不发生
/
/
goto LABEL_11;
while
(
1
)
{
v3
=
v25;
v19
=
(unsigned
int
*
)(&v17
-
2
);
v20
=
(const char
*
*
*
)(&v17
-
2
);
v21
=
(unsigned
int
*
)(&v17
-
2
);
v22
=
(unsigned
int
*
)(&v17
-
2
);
*
((_DWORD
*
)&v17
-
4
)
=
0
;
v17
=
v3;
v23
=
(
int
)v3 <
2
;
/
/
/
/
下列布尔值恒true
/
/
if
( ((unk_404120
*
(unk_404120
+
1
)) &
1
)
=
=
0
|| unk_4040C4 <
10
)
/
/
/
/
此
break
必发生
/
/
break
;
LABEL_11:
/
/
/
/
永可不达的基本块
/
/
LODWORD(v17)
=
v24;
*
(&v17
-
2
)
=
v25;
}
if
( v23 )
{
/
/
/
/
下列布尔值恒false
/
/
if
( ((unk_40411C
*
(unk_40411C
+
1
)) &
1
) !
=
0
&& unk_4040C0 >
=
10
)
/
/
/
/
此跳转永不发生
/
/
goto LABEL_12;
while
(
1
)
{
fprintf(stderr,
"Usage: %s <num>\n"
,
*
*
v20);
*
v19
=
-
1
;
/
/
/
/
下列布尔值恒true
/
/
if
( ((unk_404110
*
(unk_404110
+
1
)) &
1
)
=
=
0
|| unk_4040B8 <
10
)
/
/
/
/
此
break
必发生
/
/
break
;
LABEL_12:
/
/
/
/
永可不达的基本块
/
/
fprintf(stderr,
"Usage: %s <num>\n"
,
*
*
v20);
*
v19
=
-
1
;
}
}
else
{
...
}
do
v18
=
*
v19;
/
/
/
/
下列布尔值恒false
/
/
while
( ((unk_4040E8
*
(unk_4040E8
+
1
)) &
1
) !
=
0
&& unk_404138 >
=
10
);
return
v18;
}
__int64 __fastcall main(
int
a1, char
*
*
a2, char
*
*
a3)
{
...
v24
=
a1;
v25
=
a2;
/
/
/
/
unk_4040??位于.bss,实际运行时初始化为
0
。下列布尔值恒false
/
/
if
( ((unk_404098
*
(unk_404098
+
1
)) &
1
) !
=
0
&& unk_4040F8 >
=
10
)
/
/
/
/
此跳转永不发生
/
/
goto LABEL_11;
while
(
1
)
{
v3
=
v25;
v19
=
(unsigned
int
*
)(&v17
-
2
);
v20
=
(const char
*
*
*
)(&v17
-
2
);
v21
=
(unsigned
int
*
)(&v17
-
2
);
v22
=
(unsigned
int
*
)(&v17
-
2
);
*
((_DWORD
*
)&v17
-
4
)
=
0
;
v17
=
v3;
v23
=
(
int
)v3 <
2
;
/
/
/
/
下列布尔值恒true
/
/
if
( ((unk_404120
*
(unk_404120
+
1
)) &
1
)
=
=
0
|| unk_4040C4 <
10
)
/
/
/
/
此
break
必发生
/
/
break
;
LABEL_11:
/
/
/
/
永可不达的基本块
/
/
LODWORD(v17)
=
v24;
*
(&v17
-
2
)
=
v25;
}
if
( v23 )
{
/
/
/
/
下列布尔值恒false
/
/
if
( ((unk_40411C
*
(unk_40411C
+
1
)) &
1
) !
=
0
&& unk_4040C0 >
=
10
)
/
/
/
/
此跳转永不发生
/
/
goto LABEL_12;
while
(
1
)
{
fprintf(stderr,
"Usage: %s <num>\n"
,
*
*
v20);
*
v19
=
-
1
;
[培训]科锐逆向工程师培训第53期2025年7月8日开班!