虚假分支的混淆会在增加大量的if else分支。增加静态分析的复杂度。但是实际在动态执行的时候。很多if else实际都是没有执行的。所以去掉虚假分支其实就是删除掉那些没有执行到的代码块。那么我们只要知道目标函数中,哪些汇编代码执行了,并且记录下执行汇编的address。然后把这些汇编以外的代码全部标记为nop。然后再用ida反汇编看到的结果。就直接是去掉虚假分支的结果了。
下面我们从准备环境开始。
mkdir ~/ollvm/llvm-project-llvmorg-9.0.1/build-release
cd ~/ollvm/llvm-project-llvmorg-9.0.1/build-release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang++" ../llvm
ninja
首先下载ndk,地址:https://developer.android.google.cn/ndk/downloads/
然后将ollvm编译好的cmake-build-release这个目录下的bin、lib、include三个目录拷贝到下载的ndk的目录~/ndk/toolchains/llvm/prebuilt/linux-x86_64/中。
然后修改我们的apk项目中的使用ndk的目录。否则会默认使用sdk中的ndk来编译。找到local.properties增加ndk.dir=/home/king/android-ndk-r21e
最后配置ollvm的参数在cpp/CMakeLists.txt中添加配置add_definitions("-mllvm -bcf -mllvm -bcf_loop=3")
然后这里碰到了一个问题。就是带上-mllvm -bcf这个参数。就会导致一直在gradle build中。这个实际上是ollvm的一个bug。由于我拉去的分支是没有修复这个bug的。所以需要根据这个地址的提交代码修改下就可以了https://github.com/obfuscator-llvm/obfuscator/pull/76
编译后。使用ida打开native-lib.so。然后发现混淆没有起作用
这里编译使用的是clang++编译的。所以我们用clang++简单的测试一下混淆
然后在ollvm的混淆处手动打个日志。看看混淆的目标函数是什么
接着编译一下我们的测试例子
clang++ -mllvm -bcf main.cpp
编译结果如下,并没有看到对我们的main函数进行混淆处理。然后我们再测试下clang的。
clang -mllvm -bcf main.c
编译结果如下,有对我们的main函数进行混淆
那么可以看出区别了。当我们是对c++的项目进行混淆时。要避免需要保护的代码在主函数中。因此,我们再修改下上面的测试代码
那么再进行一次混淆。结果如下。
我们再看看calcKey里面想要保护的代码是否有混淆
到这里准备的案例程序ok了。接着我们开始还原这个虚假分支的混淆。首先我们需要知道这个函数中哪些汇编执行了。所以我们要trace打印每一句执行了的汇编代码地址。
先贴上大佬的github地址:https://github.com/zhkl0228/unidbg
然后先用unidbg把我们的so给模拟执行起来。下面贴上代码
执行结果是直接显示ceshi
。接着我们需要把这个函数执行过程中的每个执行的汇编地址打印一下。
首先找到calcKey函数的起始位置0x124CC以及函数大小0x838
接着给我们之前的代码加上一个tracecode监控。直接修改那个调用函数。在调用前先挂上监控
执行完成后得到如下结果
这里的汇编地址就都是确定有执行到的了。
上面获取到执行了的汇编地址。接下来我们用ida来执行py脚本把执行的部分进行高亮。并且将这个函数范围中的未执行的部分代码修改成nop。改成nop之后ida就不会把未执行的汇编解析出来了。这里可以直接使用idapython,也可以用对idapython进行包装的库。这里的例子用的sark来进行处理。https://github.com/tmr232/Sark
用ida的插件keypatch看了下nop的对应字节是1f 20 03 d5
下面附上py代码
最后ida执行脚本一下。下面附上处理后的效果图
然后看看F5解析出来的变成什么样子了
extern
"C"
JNIEXPORT jstring JNICALL
Java_com_example_ollvmdemo2_MainActivity_stringFromJNI(
JNIEnv
*
env,
jobject
/
*
this
*
/
) {
std::string hello
=
"Hello from C++"
;
if
(hello.length()>
10
){
hello
=
"ceshi"
;
}
else
if
(hello.length()>
30
){
hello
=
"ollvm"
;
}
else
{
hello
=
"fla"
;
}
return
env
-
>NewStringUTF(hello.c_str());
}
extern
"C"
JNIEXPORT jstring JNICALL
Java_com_example_ollvmdemo2_MainActivity_stringFromJNI(
JNIEnv
*
env,
jobject
/
*
this
*
/
) {
std::string hello
=
"Hello from C++"
;
if
(hello.length()>
10
){
hello
=
"ceshi"
;
}
else
if
(hello.length()>
30
){
hello
=
"ollvm"
;
}
else
{
hello
=
"fla"
;
}
return
env
-
>NewStringUTF(hello.c_str());
}
int
main(
int
argc,const char
*
*
argv) {
std::cout <<
"Hello, World!"
<< std::endl;
std::string hello
=
"heheda ollvm"
;
if
(hello.length()>
10
){
hello
=
"ceshi"
;
}
else
if
(hello.length()>
30
){
hello
=
"ollvm"
;
}
else
{
hello
=
"fla"
;
}
std::cout << hello.c_str() << std::endl;
return
0
;
}
int
main(
int
argc,const char
*
*
argv) {
std::cout <<
"Hello, World!"
<< std::endl;
std::string hello
=
"heheda ollvm"
;
if
(hello.length()>
10
){
hello
=
"ceshi"
;
}
else
if
(hello.length()>
30
){
hello
=
"ollvm"
;
}
else
{
hello
=
"fla"
;
}
std::cout << hello.c_str() << std::endl;
return
0
;
}
void bogus(Function &F) {
errs() <<
"bcf: Started on function "
<< F.getName() <<
"\n"
;
...
...
}
void bogus(Function &F) {
errs() <<
"bcf: Started on function "
<< F.getName() <<
"\n"
;
...
...
}
bcf: Started on function __cxx_global_var_init
bcf: Started on function _GLOBAL__sub_I_main.cpp
bcf: Started on function __cxx_global_var_init
bcf: Started on function _GLOBAL__sub_I_main.cpp
bcf: Started on function main
bcf: Started on function main
std::string calcKey(std::string data){
if
(data.length()>
10
){
data
=
"ceshi"
;
}
else
if
(data.length()>
30
){
data
=
"ollvm"
;
}
else
{
data
=
"fla"
;
}
return
data;
}
extern
"C"
JNIEXPORT jstring JNICALL
Java_com_example_ollvmdemo2_MainActivity_stringFromJNI(
JNIEnv
*
env,
jobject
/
*
this
*
/
) {
std::string hello
=
"Hello from C++"
;
hello
=
calcKey(hello);
return
env
-
>NewStringUTF(hello.c_str());
}
std::string calcKey(std::string data){
if
(data.length()>
10
){
data
=
"ceshi"
;
}
else
if
(data.length()>
30
){
data
=
"ollvm"
;
}
else
{
data
=
"fla"
;
}
return
data;
}
extern
"C"
JNIEXPORT jstring JNICALL
Java_com_example_ollvmdemo2_MainActivity_stringFromJNI(
JNIEnv
*
env,
jobject
/
*
this
*
/
) {
std::string hello
=
"Hello from C++"
;
hello
=
calcKey(hello);
return
env
-
>NewStringUTF(hello.c_str());
}
public
class
BcfTest {
private final AndroidEmulator emulator;
private final VM vm;
private final DvmClass mainActivityDvm;
public static void main(String[] args) {
BcfTest bcfTest
=
new BcfTest();
bcfTest.call_calckey();
}
private BcfTest(){
emulator
=
AndroidEmulatorBuilder
.for64Bit()
.build();
Memory memory
=
emulator.getMemory();
LibraryResolver resolver
=
new AndroidResolver(
23
);
memory.setLibraryResolver(resolver);
vm
=
emulator.createDalvikVM(null);
vm.setVerbose(false);
mainActivityDvm
=
vm.resolveClass(
"com/example/ollvmdemo2/MainActivity"
);
DalvikModule dm
=
vm.loadLibrary(new
File
(
"unidbg-android/src/test/resources/example_binaries/ollvm_bcf/libnative-lib.so"
), false);
dm.callJNI_OnLoad(emulator);
}
/
/
主动调用目标函数
private void call_calckey(){
/
/
调用一个返回值为
object
的静态的jni函数
StringObject res
=
mainActivityDvm.callStaticJniMethodObject(emulator,
"stringFromJNI()Ljava/lang/String;"
);
System.out.println(res.toString());
}
public
class
BcfTest {
private final AndroidEmulator emulator;
private final VM vm;
private final DvmClass mainActivityDvm;
public static void main(String[] args) {
BcfTest bcfTest
=
new BcfTest();
bcfTest.call_calckey();
}
private BcfTest(){
emulator
=
AndroidEmulatorBuilder
.for64Bit()
.build();
Memory memory
=
emulator.getMemory();
LibraryResolver resolver
=
new AndroidResolver(
23
);
memory.setLibraryResolver(resolver);
vm
=
emulator.createDalvikVM(null);
vm.setVerbose(false);
mainActivityDvm
=
vm.resolveClass(
"com/example/ollvmdemo2/MainActivity"
);
DalvikModule dm
=
vm.loadLibrary(new
File
(
"unidbg-android/src/test/resources/example_binaries/ollvm_bcf/libnative-lib.so"
), false);
dm.callJNI_OnLoad(emulator);
}
/
/
主动调用目标函数
private void call_calckey(){
/
/
调用一个返回值为
object
的静态的jni函数
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课