llvm学习(三):移植ollvm等作品到单独编译的Pass
上篇讲了单独编译一个 Pass,加载起来非常优雅,而ollvm等项目非常不优雅,为了方便开发,就把它们都移植了一下。工欲善其事必先利其器,突然发现这玩意支持 CLion,开发起来更加优雅了。
一、使用 CLion 开发
接着上篇,在llvm-pass-tutorial 项目里,直接用 CLion 打开就可以了,效果如图。。。我也不知道怎么解释比较好。
二、ollvm 等项目核心代码位置
以ollvm为例,作者直接将整个 llvm 都传到了 github 上,后人下载的话都得把好几十兆的代码拉下来,但其实他只是写了几个 Pass 而已,完全没有必要拿全部的代码。
只有两个位置,分别在:
1 | lib/Transforms/Obfuscation/ |
和一处巨tm隐蔽的位置,不知道作者为什么要放这么隐蔽,理解不来。
1 | include/llvm/CryptoUtils.h |
lib/Transforms
目录下还放着一些其他的 Pass,代码写不出来时候可以抄抄看。
如果只为了肉眼看一下这部分代码的话,推荐一款工具,可以下载某分支的指定目录,叫 DownGit https://minhaskamal.github.io/DownGit/,非常好用,谁用谁知道。
三、移植ollvm
好了,代码都拿到了,这里讲一下如何移植吧,曾经移植了我一整天,脑阔疼!顺便吐槽一下三个项目作者的代码风格,一个比一个不规范。。。完成的作品放在了 https://github.com/LeadroyaL/llvm-pass-tutorial/tree/dev ,每次commit记录都在,在dev分支上面,不在master上,我懒得改了!!! 主要做以下几件事
- 编写CMakeLists.txt
- 改写include文件的路径
- 修其他 bug,检查有没有漏掉的东西
- 创建注册的入口,让clang能加载到它
先在外层的 CMakeLists.txt 里加上
1 | add_subdirectory(ollvm) # ollvm |
创建个目录,把那些cpp文件都丢进去。
1 | ➜ ollvm git:(dev) lsa |
为了方便管理,再创建一个include目录,里面放那些头文件。记得把那个傻逼的 CryptoUtils.h 也复制过来。
1 | ➜ include git:(dev) tree |
以 BogusControlFlow.cpp 为例,原本内容是:
1 | #include "llvm/Transforms/Obfuscation/BogusControlFlow.h" |
但我觉得不优雅,就把最外面的那层给删了。(其实可以删更多的,把Transforms 删了也行,只是写这篇文章时候发现当初删的少了,其实可以多删一点的)
1 | #include "Transforms/Obfuscation/BogusControlFlow.h" |
创建子模块,把那些文件都包含进去,其他部分从 demo 里复制一下。
1 | add_library(Ollvm MODULE |
将所有的include 都改掉之后,大部分的符号都可以被找到了,这时候点击编译,大概率会不给过的,应该还有几处 Not Found,边搜边改即可,我记得有一处是 Utils.h 的冲突引起的,得手动操作一下。反正不大记得了,参考github里的代码就行。
之后写一个注册它们的函数,例如这个(具体每句话我会在下一篇文章解释的,这里先大概看看),在最开始就注册我们的各个 Pass,很优雅。
1 |
|
此时,点击Build按钮,就可以找到那个 libollvm.so
了。
然后使用 clang 的命令去加载这个so,执行可以正常运行,使用ida打开发现跟 shit 一样,就
1 | ➜ /tmp clang -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include -Xclang -load -Xclang /Users/leadroyal/CLion_code/llvm-pass-tutorial/cmake-build-debug/ollvm/libollvm.so -w test.c -o test.bin |
熟悉的配方,熟悉的味道,对不对!
四、移植Armariris、Hikari
大部分内容跟之前的相同,需要注意的一点,就是它们包含 ModulePass,而 ModulePass 的注册方法和 FunctionPass 的注册方法是不一样的,如果使用 PassManagerBuilder::EP_EarlyAsPossible ,会导致编译 Crash,我猜是因为还没初始化好,不大懂。
当时搜了一下午一晚上,最后在AFL源码里找到一段代码。。。。抄来可以用。。。真有意思。。。还好我先移植的是 Armariris,就三个 Pass。
改为下面,这样,在不同时机注册2次的方法,就可以保证稳稳地被加载了。
参考链接:
https://github.com/mirrorer/afl/blob/master/llvm_mode/afl-llvm-pass.so.cc
https://github.com/sampsyo/llvm-pass-skeleton/issues/7
1 | static void registerArmaririsModulePass(const PassManagerBuilder &, |
五、总结
完成的作品放在了
https://github.com/LeadroyaL/llvm-pass-tutorial/tree/dev ,每次commit记录都在,在dev分支上面,不在master上,我懒得改了!!!
本篇讲了3个移植的案例,都可以正常运行,这样开发、阅读、调试起来就很优雅了。
吐槽一下吧,先说 ollvm
,为什么要把 CryptoUtils.h
丢到那么隐蔽的位置;
再吐槽 Armariris
,连 gitignore
都不会写,一堆垃圾文件都被传上去了,代码里面全是无用的调试信息;
最后吐槽 Hikari
,在头文件里包含不必要的头文件,重复 include,非常不优雅。
最后,向三个开源项目的作者致敬,感谢开源,让后人能够更快接触到 llvm 的 pass编写,感谢椒哥持续的帮助和不厌其烦的答疑。
下集预告:简单的小知识和代码分析~~~