# ollvm 环境配置
# 介绍
Ubuntu 虚拟机 22.04
OLLVM 介绍看这个 Deobfuscation: recovering an OLLVM-protected program
Ollvm 混淆主要分成三种模式,这三种模式主要是流程平坦化,指令替换,以及控制流伪造。
流程平坦化 :这个模式主要通过将 if-else 语句替换成 do-while 语句,然后通过 switch 语句来对流程的控制,这样就能模糊基本块之间的前后关系。
指令替换 :这个模式主要通过使用更复杂的指令序列来替换一些标准的二元运算符,从而增加逆向的难度。
控制流伪造 :这个模式主要是会在一个简单的运算中外包好几层 if-else 的判断,从而增加逆向的难度。
# 一些参数
# 控制流扁平化
这个模式主要是把一些 if-else 语句,嵌套成 do-while 语句
-mllvm -fla:激活控制流扁平化
-mllvm -split:激活基本块分割。在一起使用时改善展平。
-mllvm -split_num=3:如果激活了传递,则在每个基本块上应用 3 次。默认值:1
# 指令替换
这个模式主要用功能上等效但更复杂的指令序列替换标准二元运算符 (+ , – , & , | 和 ^)
-mllvm -sub:激活指令替换
-mllvm -sub_loop=3:如果激活了传递,则在函数上应用 3 次。默认值:1
# 虚假控制流程
这个模式主要嵌套几层判断逻辑,一个简单的运算都会在外面包几层 if-else,所以这个模式加上编译速度会慢很多因为要做几层假的逻辑包裹真正有用的代码。
-mllvm -bcf:激活虚假控制流程 -mllvm -bcf_loop=3:如果激活了传递,则在函数上应用 3 次。默认值:1 -mllvm -bcf_prob=40:如果激活了传递,基本块将以 40%的概率进行模糊处理。默认值:30
# 其他
-mllvm -sobf 开启字符串混淆
-mllvm -seed=0xdeadbeaf 指定随机数种子生成器
# 开搭
How to Build: | |
$ git clone https://github.com/yazhiwang/ollvm-tll.git | |
$ mkdir build | |
$ cd build | |
$ cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release ../ollvm-tll/ | |
$ make -j7 |
然后我的 gcc cmake g++ 版本如下
编译个 Ollvm 编 2 个小时了 还是 57% 一直跳 warning 我都不知道对不对
好像确实要两个小时 是正常的
# 编译一个小例子看看
#include<stdio.h> | |
void exec() { | |
printf("VAIBIBABO"); | |
} | |
int main(void) { | |
int k=0; | |
for (int i=0;i<10;i++){ | |
printf("%d",i);} | |
printf("Hello World"); | |
exec(); | |
return 0; | |
} |
/home/mou/ 桌面 /build/bin/clang vaibi.c -mllvm -fla -mllvm -sobf -mllvm -bcf -o a
IDA 中看看
int __cdecl main(int argc, const char **argv, const char **envp) | |
{ | |
int v3; // eax | |
int v4; // eax | |
int v5; // eax | |
int v7; // [rsp+30h] [rbp-10h] | |
int v8; // [rsp+34h] [rbp-Ch] | |
v8 = 0; | |
v7 = 1860951317; | |
while ( 1 ) | |
{ | |
while ( 1 ) | |
{ | |
while ( v7 == -2018413106 ) | |
{ | |
argv = (const char **)(unsigned int)v8; | |
v7 = 1565625762; | |
printf(&byte_404030, (unsigned int)v8, envp); | |
} | |
if ( v7 != -1375518149 ) | |
break; | |
printf(&byte_404033, argv, envp); | |
exec(); | |
v7 = 1843433062; | |
} | |
if ( v7 == 24246500 ) | |
break; | |
switch ( v7 ) | |
{ | |
case 560024662: | |
v4 = -1375518149; | |
argv = (const char **)(unsigned int)y_4; | |
envp = (const char **)((((_BYTE)x_3 - 1) * (_BYTE)x_3) & 1); | |
if ( y_4 < 10 || (_DWORD)envp == 0 ) | |
v4 = 1843433062; | |
v7 = v4; | |
break; | |
case 1565625762: | |
++v8; | |
v7 = 1860951317; | |
break; | |
case 1843433062: | |
printf(&byte_404033, argv, envp); | |
exec(); | |
v5 = -1375518149; | |
argv = (const char **)(unsigned int)y_4; | |
envp = (const char **)((((_BYTE)x_3 - 1) * (_BYTE)x_3) & 1); | |
if ( y_4 < 10 || (_DWORD)envp == 0 ) | |
v5 = 24246500; | |
v7 = v5; | |
break; | |
default: | |
v3 = 560024662; | |
if ( v8 < 10 ) | |
v3 = -2018413106; | |
v7 = v3; | |
break; | |
} | |
} | |
return 0; | |
} |
成功混淆
如果没有进行 sudo make install , 则需要在 bin 目录下编译源程序或者指定 clang 路径
LLVM 编译器框架下,不同的前端可以生成统一的中间表示 (LLVM IR)
LLVM 编译器可分为三个部分
前端对源代码进行分析并将其转换为 IR (中间代码)
(优化器) 中端对生成的 IR 进行分析和优化
后端将 IR 转换为原生机器代码