大星星的专栏

你不必等到非常厉害,才敢开始;你需要开始,才会变得非常厉害!www.zhupite.com...

Windows下的LLVM之把pass抽离到DLL中

先说一下为什么要剥离出去?

LLVM6.0的工程在Windows下用VisualStudio2017第一次编译五个多小时,单独编译一次OPT或者clang需要半个多小时,即使是只修改cpp文件编译链接也要半个多小时,而且这期间电脑卡死啥也干不了。

这是在Windows下玩LLVM的现状,我估计MAC下或者Linux系统下没这个问题吧,我看网上也没人提编译速度慢的事。有人该说了,你特么用MAC或者Linux玩不就可以了吗?但是大部分的AndroidStudio开发NDK的时候还是在Windows上开发的,如果要提供一套安全编译工具的话,还是要准备Windows版本的LLVM安全编译器不是?

另外一点是,LLVM官方代码本身就不支持把pass编译出dll来,网上的案例大多都是MAC下或者Linux系统下的,可以用-load来加载动态库调用。LLVM官网也介绍说,用的一个括号说明的,说是Windows下会生成相应的扩展名的动态库,骗人的,不会生成dll的。

所以事情就演变成:

Windows下没有生成pass动态库–》自己集成(参考Windows下LLVM6.0集成并编译OLLVM中的Obfuscator的各个pass - 朱皮特个人博客)–》虽然集成通过,但是编译速度巨慢啊–》解决编译速度慢的问题–》剥离变化部分到DLL中。

LLVM6.0生成的VisualStudio解决方案下共有502个项目,你叫我怎么编译?但是我们在研究pass的过程中,修改的文件就那几个,LLVM中的绝大部分代码是不需要修改的,那么就可以单独把我们增加的pass代码剥离出来制作成dll,让CLANG去加载调用即可。

步骤1 创建DLL工程

创建一个工程的目录,例如:custompass,在目录下创建Obfuscator文件夹,作为我们的源码目录,把OLLVM的相关代码放到该目录下:
- MyPass.cpp
- Custom.cpp
- Custom.h
- Obfuscator.cpp
- Obfuscator.h
- Flattening.cpp
- Flattening.h
- StringObfuscation.cpp
- StringObfuscation.h
- Substitution.cpp
- Substitution.h
- BogusControlFlow.cpp
- BogusControlFlow.h
- CryptoUtils.cpp
- CryptoUtils.h
- Utils.cpp
- Utils.h

至于为什么会多出一些头文件和源文件,是因为我把自定义的pass相关文件和组织整理后的文件也都放进去了,可以参考之前的文章(Windows下LLVM6.0集成并编译OLLVM中的Obfuscator的各个pass - 朱皮特个人博客)。

其中Obfuscator.h:

#pragma once

//多个pass
#include "Utils.h"
#include "Substitution.h"
#include "StringObfuscation.h"
#include "Flattening.h"
#include "BogusControlFlow.h"
#include "llvm/IR/LegacyPassManager.h"

namespace llvm {

    // 在里面创建其他多个pass
    FunctionPass *createObfuscatorPasses(bool flag);

    //必须放在llvm命名空间下
    void initializeSubstitutionPass(PassRegistry&);
    void initializeFlatteningPass(PassRegistry&);
    void initializeStringObfuscationPassPass(PassRegistry&);
    void initializeBogusControlFlowPass(PassRegistry&);
}

extern "C" {
    // 在clang里根据配置创建自定义pass,called by PassManagerBuilder::populateModulePassManager
    __declspec(dllexport)  void __stdcall clangAddCustomPass(legacy::PassManagerBase &MPM);
}

Obfuscator.cpp:

#include "stdafx.h"
#include "Obfuscator.h"

#include "llvm/Transforms/Custom.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"

#include "llvm/InitializePasses.h"
#include "llvm-c/Initialization.h"
#include "llvm/InitializePasses.h"
#include "llvm/PassRegistry.h"
using namespace llvm;


#if _WINDOWS
#include <wtypes.h>
#include <WinBase.h>
#include <windef.h>
#include <WinUser.h>
#endif // _WINDOWS


// initializeObfuscator - Initialize all passes in the Custom library
void llvm::initializeObfuscator(PassRegistry &Registry) {
    initializeSubstitutionPass(Registry);
    initializeFlatteningPass(Registry);
    initializeStringObfuscationPassPass(Registry);
    initializeBogusControlFlowPass(Registry);
}

FunctionPass *llvm::createObfuscatorPasses(bool flag) {
    createFlattening(flag);
    createStringObfuscation(flag);
    createSubstitution(flag);
    createBogus(flag);
    return NULL;
}

// 在clang里根据配置创建自定义pass,called by PassManagerBuilder::populateModulePassManager
void clangAddCustomPass(legacy::PassManagerBase &MPM) {
    void *p = &MPM;
    if ( p != NULL ) {
#if _WINDOWS
        if (MyPass == false) {
            ::MessageBoxA(0, "MyPass==false", 0, 0);
            MyPass = true;
        }
#endif // _WINDOWS

        MPM.add(createMyPassPass(true));
        MPM.add(createFlattening(true));
        MPM.add(createStringObfuscation(true));
        MPM.add(createSubstitution(true));
        MPM.add(createBogus(true));
    } else {
#if _WINDOWS
        ::MessageBoxA(0, "MPM == NULL", 0, 0);
#endif // _WINDOWS
    }
}

custompass目录下创建CMakeLists.txt文件,内容如下:

cmake_minimum_required(VERSION 3.4)

project(custompass)

# 设置编译模式
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")  #/MT
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")     #/MTd

# 添加源码目录
aux_source_directory(./Obfuscator  src_1)
set(srcs ${src_1})

# 生成动态链接库,那么在Windows下就是dll,最终会生成custompass.dll
add_library(custompass SHARED ${srcs})

# 添加头文件包含目录,头文件包含还有CMake后生成的include目录
include_directories(../llvm/include xxx/llvm/build/include)


# link_directories不起作用 参见 https://stackoverflow.com/questions/31438916/cmake-cannot-find-library-using-link-directories
# TARGET_LINK_LIBRARIES(custompass LLVMPasses LLVMCore)

set(mylibdir "xxx/llvm/build/Debug/lib")
set(VTKLIBS LLVMCore LLVMSupport LLVMAnalysis LLVMBinaryFormat LLVMTransformUtils)
foreach(libname ${VTKLIBS})
        SET(FOUND_LIB "FOUND_LIB-NOTFOUND")
        find_library(FOUND_LIB NAMES ${libname} HINTS ${mylibdir} NO_DEFAULT_PATH)
        IF (FOUND_LIB)
            message("found lib: ${FOUND_LIB}")
            LIST(APPEND mylibs ${FOUND_LIB})
        ELSE() 
            MESSAGE("not lib found: ${libname}") 
        ENDIF ()
endforeach(libname)
#message(${mylibs})

#message(${CPPUNIT_LIBRARY})
target_link_libraries(custompass PUBLIC ${mylibs}) 

然后使用CMakeGui生成一个VisualStudio的解决方案,记得设置64为标记(host=x64),可能再需要稍作修改,不过都比较简单,最终编译成功生成custompass.dll,第一次编译耗时40秒左右。

步骤2 修改LLVM的CLANG代码

为了让CLANG能使用上自定义的pass(这里不再对opt修改了),修改llvm/lib/Transforms/IPO/PassManagerBuilder.cpp,添加代码:

/////////////////////////////////////////////////////////
#include "llvm/Transforms/Obfuscator/Obfuscator.h"
#if _WINDOWS
#include <wtypes.h>
#include <WinBase.h>
#include <windef.h>
#include <string>

typedef void(__stdcall *pfn_clangAddCustomPass)(legacy::PassManagerBase &MPM);
std::string GetStartPath(HMODULE hModule = NULL);

std::string GetStartPath(HMODULE hModule/* = NULL*/) {
    char szTemp[260] = { 0 };
    GetModuleFileNameA(hModule, szTemp, sizeof(szTemp) / sizeof(TCHAR));
    strrchr(szTemp, '\\')[1] = 0;
    return szTemp;
}

#endif // _WINDOWS
/////////////////////////////////////////////////////////

并在函数PassManagerBuilder::populateModulePassManager入口处添加:

///////////////////////////////////////////////////////
//clangAddCustomPass(MPM);
#if _WINDOWS
std::string strDllPath = GetStartPath(NULL) + "custompass.dll";
HMODULE hPassDll = ::LoadLibraryA(strDllPath.c_str());
pfn_clangAddCustomPass pfn = (pfn_clangAddCustomPass)::GetProcAddress(hPassDll, "clangAddCustomPass");
if (pfn != NULL) {
    pfn(MPM);
} else {
    //not found custompass.dll or clangAddCustomPass
}
#endif // _WINDOWS
///////////////////////////////////////////////////////

步骤4 优化加快编译速度

利用Windows下的VisualStudio的预编译头来加快编译速度,不然每次编译都需要花费40秒也是够慢的。

对VisualStudio的项目工程设置下使用预编译头,然后对每个cpp文件的开头一行增加:

#include "stdafx.h"

在custompass/build目录下创建一个stdafx.h,内容为:

#pragma once

创建一个stdafx.cpp,内容为:

#include "stdafx.h"

并设置stdafx.cpp的预编译头文件为:创建。

完成配置后,二次编译(仅仅修改CPP文件的情况下)只需要一两秒时间即可完成,大大提高了编译效率,生成的dll体积debug版本的有15MB多点,相比200MB多的CLANG小太多了。

工欲善其事必先利其器,前期花点时间把实验环境搭建好是很值得的,后面就可以随便修改混淆PASS来随意把玩研究了。

TODO opt参数解析失效了

以下代码配置的参数解析如果放在dll里会失效,因为命令行参数解析没有打通,一个在clang的exe里,无法遍历在dll里的这些opt变量,这些变量永远为false了:

static cl::opt<bool>
MyPass("mypass", cl::init(false), cl::Hidden,
    cl::ZeroOrMore, cl::desc("Print all function names"));

static cl::opt<bool>
StringObfuscationPass("GVDiv", cl::init(false), cl::Hidden,
    cl::ZeroOrMore, cl::desc("Global variable (i.e., const char*) diversification pass"));

static cl::opt<bool>
Flattening("flattening", cl::init(false), cl::Hidden,
    cl::ZeroOrMore, cl::desc("Call graph flattening"));

static cl::opt<bool>
Substitution("substitution", cl::init(false), cl::Hidden,
    cl::ZeroOrMore, cl::desc("operators substitution"));

static cl::opt<bool>
BogusControlFlow("boguscf", cl::init(false), cl::Hidden,
    cl::ZeroOrMore, cl::desc("inserting bogus control flow"));

如果继续放在clang里,可以通过clangAddCustomPass的参数进行传递,这个我目前没有做,后期再说。

目前是手动修改clangAddCustomPass函数中createMyPassPass的参数,不过问题不大,前期学习研究不成问题,后期可以弄成配置文件,或增加clangAddCustomPass的函数参数来传递。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。欢迎访问我的个人博客:www.zhupite.com https://blog.csdn.net/asmcvc/article/details/80105237
文章标签: llvm
个人分类: llvm
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭