简介
相关项目
- https://github.com/obfuscator-llvm/obfuscator/wiki/Installation
- https://github.com/HikariObfuscator/Hikari
- https://github.com/GoSSIP-SJTU/Armariris
git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF ../obfuscator/
make -j10
说明: 直接使用官方的编译选项时会报错
CMake Error at cmake/modules/AddLLVM.cmake:1163 (add_custom_target): add_custom_target cannot create target "check-llvm-bindings-ocaml" because another target with the same name already exists. The existing target is a custom target created in source directory "/opt/obfuscator-llvm-4.0/test". See documentation for policy CMP0002 for more details. Call Stack (most recent call first): cmake/modules/AddLLVM.cmake:1226 (add_lit_target) test/CMakeLists.txt:150 (add_lit_testsuites)
就要多加一个编译选项 -DLLVM_INCLUDE_TESTS=OFF
混淆配置参数
开启控制流扁平化
-mllvm -fla
开启控制流扁平化
-mllvm -split
激活基本块划分。一起使用时能提高打平能力。
-mllvm -split_num=3
如果激活控制流打平,对每一个基本块应用三次控制流打平。默认使用1次。
开启指令替换
-mllvm -sub
开启指令替换
-mllvm -sub_loop=3
如果激活了指令替换,使用这个选项在一个函数中应用3次指令替换。默认应用1次。
开启虚假控制流
-mllvm -bcf
开启虚假控制流
bcf
可以配合下面参数使用
-mllvm -bcf_loop=3
设置函数混淆次数为3次 不加此选项默认为1次
-mllvm -bcf_prob=40
设置代码块被混淆的概率是40%,默认30%
例如下面这个程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_password(char *passwd)
{
int i, sum = 0;
for (i = 0; ; i++)
{
if (!passwd[i])
{
break;
}
sum += passwd[i];
}
if (i == 4)
{
if (sum == 0x1a1 && passwd[3] > 'c' && passwd[3] < 'e' && passwd[0] == 'b')
{
if ((passwd[3] ^ 0xd) == passwd[1])
{
return 1;
}
puts("Orz...");
}
}
else
{
puts("len error");
}
return 0;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
puts("errors");
return 1;
}
if (check_password(argv[1]))
{
puts("Congratulationss!");
}
else
{
puts("errors");
}
return 0;
}
编译
ollvm_build/bin/clang 1.c -emit-llvm -S -mllvm -sub -mllvm -fla -mllvm -sub_loop=3 -mllvm -split_num=3 -mllvm -bcf
ollvm_build/bin/clang 1.ll -o 1
然后再用ida查看程序控制流。
Armariris
下面是一个更加全面的ollvm的版本Armariris
安装
mkdir obf
cd obf
clone git@github.com:gossip-sjtu/Armariris.git
cmake -DCMAKE_BUILD_TYPE:String=Release ./Armariris
make -j10
分析
我们以如下程序为例
#include<stdio.h>
void init(){
int t=0;
return;
}
int hello(){
return 5;
}
void say(){
printf("Hello This is HAPPY!");
}
int main(){
init();
int a=1;
int t=hello();
int b=5;
int c=a+b;
say();
//int d=a+c;
return 0;
}
编译混淆
clang 1.c -emit-llvm -S -mllvm -sub -mllvm -fla -mllvm -sobf
clang 1.ll -o 1
此处以字符串混淆为例,我们反汇编生成的二进制文件,发现
int say()
{
return printf(&format);
}
没有了直接显示的字符串,而且format的字符串也是加密后的
.data:0000000000404030 format db 8Bh ; DATA XREF: say+8↑o
.data:0000000000404030 ; _datadiv_decode12115958352748561385+53↑r ...
.data:0000000000404031 db 0A6h
.data:0000000000404032 db 0AFh
.data:0000000000404033 db 0AFh
.data:0000000000404034 db 0ACh
.data:0000000000404035 db 0E3h
.data:0000000000404036 db 97h
.data:0000000000404037 db 0ABh
.data:0000000000404038 db 0AAh
.data:0000000000404039 db 0B0h
.data:000000000040403A db 0E3h
.data:000000000040403B db 0AAh
.data:000000000040403C db 0B0h
.data:000000000040403D db 0E3h
.data:000000000040403E db 8Bh
.data:000000000040403F db 82h
.data:0000000000404040 db 93h
.data:0000000000404041 db 93h
.data:0000000000404042 db 9Ah
.data:0000000000404043 db 0E2h
.data:0000000000404044 db 0C3h
.data:0000000000404044 _data ends

然后在IDA的view
->Open Subviews
->Promixity Browser
打开概览图
我们发现,在程序init的时候,有一段解密代码将format进行了解密操作。
__int64 datadiv_decode12115958352748561385()
{
__int64 result; // rax
signed int v1; // er9
int v2; // [rsp+18h] [rbp-8h]
unsigned int v3; // [rsp+1Ch] [rbp-4h]
v3 = -566357037;
v2 = 0;
while ( 1 )
{
result = v3;
if ( v3 == -692340892 )
break;
if ( v3 == -566357037 )
{
format[v2] ^= 0xC3u;
v1 = -692340892;
if ( (unsigned __int64)v2 < 0x14 )
v1 = -566357037;
v3 = v1;
++v2;
}
}
return result;
}
我们可以模拟这段解密操作。下面是解密脚本
#include <iostream>
unsigned char ida_chars[] =
{
0x8B, 0xA6, 0xAF, 0xAF, 0xAC, 0xE3, 0x97, 0xAB, 0xAA, 0xB0,
0xE3, 0xAA, 0xB0, 0xE3, 0x8B, 0x82, 0x93, 0x93, 0x9A, 0xE2,
0xC3
};
void decode()
{
__int64 result; // rax
signed int v1; // er9
int v2; // [rsp+18h] [rbp-8h]
unsigned int v3; // [rsp+1Ch] [rbp-4h]
v3 = -566357037;
v2 = 0;
while (1)
{
result = v3;
if (v3 == -692340892)
break;
if (v3 == -566357037)
{
*(ida_chars + v2) ^= 0xC3u;
v1 = -692340892;
if ((unsigned __int64)v2 < 0x14)
v1 = -566357037;
v3 = v1;
++v2;
}
}
}
int main()
{
decode();
for (int i=0;i<(sizeof(ida_chars)/sizeof(unsigned char));i++)
{
std::cout << ida_chars[i];
}
}
运行程序后,可知原字符串为Hello This is HAPPY!
Comments | NOTHING