首页
论坛
课程
招聘
[原创]macOS安装调试llvm入门
2020-12-26 14:02 5990

[原创]macOS安装调试llvm入门

2020-12-26 14:02
5990

macOS安装调试llvm入门

题目出自3W班9月第一题:编译llvm,动态调试Pass报告。老师的课件不是基于macos的,所以这篇文章算是自己踩坑之后的一个学习记录。

下载llvm源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看你的Android ndk版本
$ ./clang -v
Android (6454773 based on r365631c2) clang version 9.0.8 (https://android.googlesource.com/toolchain/llvm-project 98c855489587874b2a325e7a516b99d838599c6f) (based on LLVM 9.0.8svn)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Volumes/Work/SDK/AndroidSDK/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/.
# 下载依赖
$ brew install cmake zlib ninja
# 可以看到版本是clang version 9.0.8,所以我们去github寻找差不多的版本下载,这里我们找到了9.0.1,下载它
# 从这个地址下载源码:
$ https://github.com/llvm/llvm-project/releases
# 下载好后用clion打开项目,然后找到llvm文件夹中的CMakelist.txt双击打开,按照clion提示,点击同步
# clion-preferences-build,execution,deployment-cmake对debug与release设置cmake options
-G Ninja -DLLVM_ENABLE_PROJECTS="clang"
# 等待同步完成后,跳转到cmake-build-debug文件夹
$ ninja -j8

写第一个demo

打开clion新建一个项目,先写一个demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# main.c
#include <stdio.h>
 
void test(){
    printf("this is Test!\n");
}
 
void test01(){
    printf("test01\n");
}
int main() {
    test();
    test01();
    return 0;
}

尝试用你编译出来的clang来编译这个demo

1
2
3
4
# 配置clang地址
$ export PATH=/Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/cmake-build-debug/bin/:$PATH
# 尝试编译
$ clang main.cpp -o main_clang

编译报错:

1
2
3
4
5
$ clang main.cpp -o main_clang 
clang-9: error: no such file or directory: 'main.cpp'
clang-9: error: no input files
# 解决
$ export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)

重新编译

1
2
3
4
5
6
7
8
$ clang main.c -o main_clang
$ ./main_clang
this is Test!
test01
# 生成ll文件
$ clang -emit-llvm main.c -S -o main.ll
# 生成bc文件
$ llvm-as main.ll -o main.bc

编写第一个llvm pass

1
# 这是介绍https://llvm.org/docs/WritingAnLLVMPass.html#writing-an-llvm-pass-basiccode

Pass

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
// /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/lib/Transforms/EncodeFunctionName/EncodeFunctionName.cpp
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
 
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
 
using namespace llvm;
 
namespace llvm {
    struct EncodeFunctionName : public FunctionPass {
        static char ID; // Pass identification, replacement for typeid
        EncodeFunctionName() : FunctionPass(ID) {};
 
        bool runOnFunction(Function &F) override {
            errs() << "EncodeFunctionName";
            errs().write_escaped(F.getName()) << '\n';
            return false;
        }
    };
}
 
char EncodeFunctionName::ID = 0;
static RegisterPass<EncodeFunctionName> X("encode", "Hello EncodeFunctionName Pass");
 
static llvm::RegisterStandardPasses Y(
        llvm::PassManagerBuilder::EP_EarlyAsPossible,
        [](const llvm::PassManagerBuilder &Builder,
           llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });

Cmakelists

1
2
3
4
5
6
7
8
9
# /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/lib/Transforms/EncodeFunctionName/CMakeLists.txt
add_llvm_library( LLVMEncodeFunctionName MODULE BUILDTREE_ONLY
  EncodeFunctionName.cpp
 
  DEPENDS
  intrinsics_gen
  PLUGIN_TOOL
  opt
  )

编译

1
2
$ cd /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/cmake-build-debug
$ ninja LLVMEncodeFunctionName

使用opt打印

1
2
# opt -load passpath -encode llfile -o bcfile
$ opt -load /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/cmake-build-debug/lib/LLVMEncodeFunctionName.dylib -encode main.ll -o main.bc

加密方法名

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
// /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/lib/Transforms/EncodeFunctionName/EncodeFunctionName.cpp
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
 
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
 
using namespace llvm;
 
namespace llvm {
    struct EncodeFunctionName : public FunctionPass {
        static char ID; // Pass identification, replacement for typeid
        EncodeFunctionName() : FunctionPass(ID) {};
 
        bool runOnFunction(Function &F) override {
            errs() << "EncodeFunctionName: " << F.getName() << " -> ";
 
            if(F.getName().compare("main") !=0){ # 过滤方法名,main方法名不能改,其他方法可以
                llvm::MD5 Hasher; # 使用md5算法修改方法名称
                llvm::MD5::MD5Result Hash; # 使用md5算法修改方法名称
                Hasher.update(F.getName()); # 使用md5算法修改方法名称
                Hasher.update("NewFunctionName"); # 使用md5算法修改方法名称
                Hasher.final(Hash); # 使用md5算法修改方法名称
 
                SmallString<32> HexString; # 使用md5算法修改方法名称
                llvm::MD5::stringifyResult(Hash, HexString);# 使用md5算法修改方法名称
                F.setName(HexString); # 修改方法名称
            }
            errs().write_escaped(F.getName()) << '\n';
            return false;
        }
    };
}
 
char EncodeFunctionName::ID = 0;
static RegisterPass<EncodeFunctionName> X("encode", "Hello EncodeFunctionName Pass");
 
static llvm::RegisterStandardPasses Y(
        llvm::PassManagerBuilder::EP_EarlyAsPossible,
        [](const llvm::PassManagerBuilder &Builder,
           llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });

Cmakelists

1
2
3
4
5
6
7
8
add_llvm_library( LLVMEncodeFunctionName MODULE BUILDTREE_ONLY
  EncodeFunctionName.cpp
 
  DEPENDS
  intrinsics_gen
  PLUGIN_TOOL
  opt
  )

编译

1
2
3
$ cd /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/cmake-build-debug
$ ninja LLVMEncodeFunctionName
[2/2] Linking CXX shared module lib/LLVMEncodeFunctionName.dylib

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 配置临时环境变量
$ export PATH=/Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/cmake-build-debug/bin/:$PATH
# 查看版本信息
$ clang --version
clang version 9.0.1
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/cmake-build-debug/bin
# 编译ll文件
$ clang -emit-llvm main.c -S -o main.ll
# 产生bc文件
$ opt -load /Volumes/Work/CLion/Projects/llvm-project-llvmorg-9.0.1/llvm/cmake-build-debug/lib/LLVMEncodeFunctionName.dylib -encode main.ll -o main.bc
EncodeFunctionName: test -> 5087472e3f661e2a53ecdb4d8dc398a7
EncodeFunctionName: test01 -> e63ae3cc6d7f9892993496b04573c87c
EncodeFunctionName: main -> main
# bc文件转可执行文件
$ clang main.bc -o main_clang

逆向

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
  5087472e3f661e2a53ecdb4d8dc398a7();
  e63ae3cc6d7f9892993496b04573c87c();
  return 0;
}

将pass注册到clang

以编写ObfString为例

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
# 创建ObfPass文件夹并编写ObfString.h
# llvm/include/llvm/Transforms/ObfPass/ObfString.h
 
#ifndef LLVM_OBFSTRING_H
#define LLVM_OBFSTRING_H
 
 
namespace llvm{
    Pass* createObfString(bool flag);
}
 
#endif //LLVM_OBFSTRING_H
 
# 创建ObfPass文件夹并编写ObfString.cpp
# llvm/lib/Transforms/ObfPass/ObfString.cpp
 
#include <llvm/IR/Constants.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Module.h"
 
 
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/ObfPass/ObfString.h"
#include "llvm/CryptoUtils.h"
 
using namespace llvm;
namespace {
    struct ObfString : public ModulePass {
        static char ID;
        bool flag;
 
        ObfString() : ModulePass(ID) {};
 
        ObfString(bool flag) : ModulePass(ID) {
            this->flag = flag;
        };
 
        bool runOnModule(Module &M) override {
            return false;
        };
    };
}
 
char ObfString::ID = 0;
static RegisterPass<ObfString> X("obfstr", "String encrypt Pass",
                                 false /* Only looks at CFG */,
                                 false /* Analysis Pass */);
 
Pass *llvm::createObfString(bool flag) {
    return new ObfString(flag);
}
 
# 同文件夹下编写CMakeLists.txt与LLVMBuild.txt
# CMakeLists.txt
add_llvm_library(LLVMObfPass
        CryptoUtils.cpp
        ObfString.cpp
        )
 
add_dependencies(LLVMObfPass intrinsics_gen)
 
# LLVMBuild.txt
[component_0]
type = Library
name = ObfPass
parent = Transforms
library_name = ObfPass
 
# llvm/lib/Transforms/CMakeLists.txt添加
add_subdirectory(ObfPass)
# llvm/lib/Transforms/LLVMBuild.txt添加
ObfPass
# llvm/lib/Transforms/IPO/LLVMBuild.txt添加
ObfPass
 
# llvm/lib/Transforms/IPO/PassManagerBuilder.cpp添加
 
#include "llvm/Transforms/ObfPass/ObfString.h"
......
static cl::opt<bool> ObfString("obfstr", cl::init(false),
                               cl::desc("Enable string obf"));
 
......
void PassManagerBuilder::populateModulePassManager(
 
MPM.add(createObfString(ObfString));
 
# clion-preferences-build,excution,deployment-cmake-debug-cmake options
-G Ninja -DLLVM_ENABLE_PROJECTS="clang"

编译通过。

调试

在代码中编写一个错误代码

1
2
3
bool runOnModule(Module &M) override {
  int *err = (int *)8; # 编写一个错误
  *err = 9; # 编写一个错误

编译

1
2
3
$ cd /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug
$ ninja LLVMObfPass
$ ninja clang

编写一个测试main.cpp尝试调试

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
#include <iostream>
 
 
int main(int argc, char const *argv[]) {
    std::string cppstr = "adkngk222中国";
    std::cout << cppstr << std::endl;
 
    return 0;
}
 
/**
 * export PATH=/Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/bin:$PATH
 * clang -mllvm -sub main.c -o main_clang
 * clang -mllvm -sub -mllvm -sub_loop=3 main.c -o main_clang
 * clang -mllvm -bcf main.c -o main_clang
 * clang -mllvm -bcf -mllvm -bcf_loop=3 main.c -o main_clang
 * clang -mllvm -bcf -mllvm -bcf_prob=60 main.c -o main_clang
 * clang -mllvm -fla main.c -o main_clang
 * clang -mllvm -fla -mllvm -split main.c -o main_clang
 * clang -mllvm -fla -mllvm -split -mllvm -split_num=10 main.c -o main_clang
 * clang -mllvm -sub -mllvm -sub_loop=3 -mllvm -bcf -mllvm -bcf_prob=60 -mllvm -fla -mllvm -split -mllvm -split_num=10 main.c -o main_clang
 * clang -mllvm -enable-encode-function-name -mllvm -sub -mllvm -sub_loop=3 -mllvm -bcf -mllvm -bcf_prob=60 -mllvm -fla -mllvm -split -mllvm -split_num=10 main.c -o main_clang
 * export C_INCLUDE_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include
 * export CPLUS_INCLUDE_PATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
 * clang++ -Wall -std=c++11 -S -emit-llvm main.cpp -o main.ll
 * clang++ -Wall -std=c++11 -mllvm -obfstr -S -emit-llvm main.cpp -o main_strobf.ll
 */

打开shell尝试编译

1
2
3
4
5
6
7
8
9
10
11
12
$ export PATH=/Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/bin:$PATH
$ export CPLUS_INCLUDE_PATH=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
$ export C_INCLUDE_PATH=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include
 
# 编译
$ clang++ -Wall -std=c++11 main.cpp -o main.test
# 报错
Stack dump:
0.    Program arguments: /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/bin/clang-9 -cc1 -triple x86_64-apple-macosx10.15.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -main-file-name main.cpp -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -masm-verbose -munwind-tables -target-sdk-version=10.15.6 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -ggnu-pubnames -target-linker-version 609 -resource-dir /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/lib/clang/9.0.1 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -c-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -cxx-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 -stdlib=libc++ -internal-isystem /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/bin/../include/c++/v1 -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1 -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include -internal-isystem /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/lib/clang/9.0.1/include -internal-externc-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include -Wall -std=c++11 -fdeprecated-macro -fdebug-compilation-dir /Volumes/Work/CLion/Projects/Test -ferror-limit 19 -fmessage-length 80 -stack-protector 1 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.15.0 -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o /var/folders/vh/l2bql3ns2d5g5w6w2rk1q16m0000gn/T/main-6e5ff9.o -x c++ main.cpp
1.    <eof> parser at end of file
2.    Per-module optimization passes
3.    Running pass 'String encrypt Pass' on module 'main.cpp'.

可以看出clang++的编译实际上是使用了clang-9做的编译,那么修改clion编译配置,尝试调试:

1
2
3
clion-run-edit configurations
左边选择clang,右边executable选择clang-9, program arguments配置:
 -cc1 -triple x86_64-apple-macosx10.15.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj -mrelax-all -disable-free -main-file-name main.cpp -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -masm-verbose -munwind-tables -target-sdk-version=10.15.6 -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -ggnu-pubnames -target-linker-version 609 -resource-dir /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/lib/clang/9.0.1 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -c-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -cxx-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 -stdlib=libc++ -internal-isystem /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/bin/../include/c++/v1 -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1 -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include -internal-isystem /Volumes/Work/CLion/Projects/llvm9.0.1/llvm/cmake-build-debug/lib/clang/9.0.1/include -internal-externc-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include -Wall -std=c++11 -fdeprecated-macro -fdebug-compilation-dir /Volumes/Work/CLion/Projects/Test -ferror-limit 19 -fmessage-length 80 -stack-protector 1 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fobjc-runtime=macosx-10.15.0 -fcxx-exceptions -fexceptions -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -o /var/folders/vh/l2bql3ns2d5g5w6w2rk1q16m0000gn/T/main-6e5ff9.o -x c++ /Volumes/Work/CLion/Projects/Test/main.cpp

就可以调试了


恭喜ID[飞翔的猫咪]获看雪安卓应用安全能力认证高级安全工程师!!

收藏
点赞2
打赏
分享
最新回复 (9)
雪    币: 7508
活跃值: 活跃值 (1466)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jgs 活跃值 2020-12-26 17:35
2
0
收藏,mark,不定哪天会用上。
雪    币: 5358
活跃值: 活跃值 (2454)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
尐进 活跃值 2020-12-26 17:54
3
0
mark
雪    币: 11074
活跃值: 活跃值 (3156)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
LowRebSwrd 活跃值 4 2020-12-28 10:21
4
0
mark
雪    币: 1316
活跃值: 活跃值 (1295)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 活跃值 1 2020-12-28 10:38
5
0
感谢分享!
雪    币: 1983
活跃值: 活跃值 (654)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
灬哈密瓜 活跃值 2020-12-28 11:52
6
0
收藏,mark,不定哪天会用上
雪    币: 2637
活跃值: 活跃值 (1233)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Vn小帆 活跃值 2020-12-29 10:53
7
0
Ubuntu 不香么
雪    币: 35
活跃值: 活跃值 (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
哈哈大侠 活跃值 2020-12-31 08:11
8
0
雪    币: 350
活跃值: 活跃值 (70)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mllaopang 活跃值 2020-12-31 08:19
9
0

分享快乐
雪    币: 97
活跃值: 活跃值 (545)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xxRea 活跃值 2022-4-8 20:17
10
0
收藏,mark,不定哪天会用上。
游客
登录 | 注册 方可回帖
返回