首页
社区
课程
招聘
[原创]使用unidbg去ollvm虚假分支反混淆
发表于: 2021-5-11 23:19 15264

[原创]使用unidbg去ollvm虚假分支反混淆

2021-5-11 23:19
15264

虚假分支的混淆会在增加大量的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());
}
 
 
#include <iostream>
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;
}
#include <iostream>
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
#include <iostream>
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());
}
#include <iostream>
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函数

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 4
支持
分享
最新回复 (3)
雪    币: 3368
活跃值: (14038)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
2
和无名大佬的 unicorn 还原差不多,最大的问题是不知道如何区分真实块,虚假块。
2021-5-12 10:43
2
雪    币: 2875
活跃值: (7833)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
可以 是个好方案
2021-5-14 13:12
0
雪    币: 248
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
ollvm_bcf  跟着大佬利用 unicorn+idapython走了一遍流程!受益匪浅
2021-8-19 20:19
0
游客
登录 | 注册 方可回帖
返回
//