反逆向分析
Ollvm
0x01 Ollvm介绍
OLLVM(Obfuscator-LLVM)是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对LLVM的代码混淆工具,以增加逆向工程的难度, 只不过Ollvm仅更新到llvm的4.0,2017年开始就没再更新。
0x02 Ollvm混淆介绍
Ollvm混淆主要分成三种模式,这三种模式主要是流程平坦化,指令替换,以及控制流伪造。
流程平坦化 :这个模式主要通过将if-else语句替换成do-while语句,然后通过switch语句来对流程的控制,这样就能模糊基本块之间的前后关系。
指令替换 :这个模式主要通过使用更复杂的指令序列来替换一些标准的二元运算符,从而增加逆向的难度。
控制流伪造 :这个模式主要是会在一个简单的运算中外包好几层if-else的判断,从而增加逆向的难度。
对Ollvm有更深入的理解,可参阅论文《Obfuscating C++ programs via control flow flattening》
0x03 Ollvm编译
env: arch-linux
使用apt源安装g++和cmake,从github获取ollvm的源码,并进行编译。
Ollvm 6.0: https://github.com/yazhiwang/ollvm-tll.git (编译成功!)
原版src: https://github.com/obfuscator-llvm/obfuscator/tree/llvm-4.0 (编译失败!)
ollvm-til编译
1 2 3 4
| mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release ../ollvm-tll-master make -j8
|
官方编译
1 2 3 4
| cd mkdir build cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF ../ make -j8
|
0x4 命令介绍
控制流扁平化
这个模式主要是把一些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 指定随机数种子生成器 bcf可以配合下面参数使用
例子
对a.c进行混淆
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include<stdio.h> void exec() { printf("OOOKKKK!"); } int main(void) { int a = 0; a ++; printf("Hello World"); if(a >= 1) { exec(); } return 0; }
|
1
| ~/tools/ollvm/bin/clang a.c -mllvm -fla -mllvm -sobf -mllvm -bcf -o a
|
ida64打开F5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| int __cdecl main(int argc, const char **argv, const char **envp) { signed int v3; __int64 *v4; int v5; signed int v6; signed int v7; signed int v8; signed int v9; __int64 *v11; int v12; __int64 v13; int v14; int v15; __int64 *v16; int v17; int v18; int v19; int v20; int v21; int v22; int v23; int v24; int v25; int v26; int v27; bool v28; bool v29; bool v30;
v28 = (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0; v29 = y_3 < 10; v27 = 1567572003; while ( 1 ) { while ( 1 ) { while ( 1 ) { v26 = v27; v25 = v27 + 1130483098; if ( v27 != -1130483098 ) break; v9 = -1065940243; if ( y_3 < 10 || (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0 ) v9 = -714366873; v27 = v9; } v24 = v26 + 1065940243; if ( v26 != -1065940243 ) break; v27 = -1130483098; } v23 = v26 + 714366873; if ( v26 == -714366873 ) break; v22 = v26 + 511865542; if ( v26 == -511865542 ) { v8 = -1065940243; if ( y_3 < 10 || (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0 ) v8 = -1130483098; v27 = v8; } else { v21 = v26 + 329192939; if ( v26 == -329192939 ) { v11 = &v13 - 2; *((_DWORD *)&v13 - 4) = 0; *(_DWORD *)v11 = 0; *(_DWORD *)v11 = *((_DWORD *)&v13 - 4) + 1; v12 = printf(&byte_404030); v27 = 191033970; v14 = v12; } else { v20 = v26 - 191033970; if ( v26 == 191033970 ) { v4 = &v13 - 2; *((_DWORD *)&v13 - 4) = 0; *(_DWORD *)v4 = 0; *(_DWORD *)v4 = *((_DWORD *)&v13 - 4) + 1; v16 = &v13 - 2; v5 = printf(&byte_404030); v6 = -329192939; v30 = *(_DWORD *)v16 >= 1; if ( y_3 < 10 || (((_BYTE)x_2 - 1) * (_BYTE)x_2 & 1) == 0 ) v6 = 1347608926; v27 = v6; v15 = v5; } else { v19 = v26 - 1347608926; if ( v26 == 1347608926 ) { v7 = -511865542; if ( v30 ) v7 = 1700479422; v27 = v7; } else { v18 = v26 - 1567572003; if ( v26 == 1567572003 ) { v3 = -329192939; if ( v29 || v28 ) v3 = 191033970; v27 = v3; } else { v17 = v26 - 1700479422; if ( v26 == 1700479422 ) { exec(); v27 = -511865542; } } } } } } } return 0; }
|
以上成功实现代码混淆
参考
Obfuscating C++ programs via control flow flattening
去除符号表
查看符号表
readelf -s test
采用strip工具对符号表进行去除
.symtab因为是在调试和链接时有用的,所以,可以从生产的二进制执行文件中移除。可以使用strip。strip是可以移除符号以及节的工具。
移除.symtab 符号表 以及 .strtab 符号字符串表
直接运行
strip --remove-section=.symtab file_in
strip --remove-section=.strtab file_in
想要让可执行文件没有.dynsym
动态链接表,.dynstr
动态链接字符表。
gcc编译时添加参数,从而使用静态编译。gcc -static
或者 gcc -nostdlib
gcc -nostdlib
:不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。
gcc -static
:在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上 无效。