Xcode 调试开发 Pass

107 阅读2分钟

参考:mayuyu.io/2017/06/01/…

一、目的:

使用xcode更方便的调试和开发pass

二、创建llvm的xcode工程:

cmake指定生成xcode工程之前,先创建一个新的空的pass,然后在cmake生成xcode工程,在xcode项目中可以看到新模块,我使用的版本是llvm9.0.0

  1. 创建空文件:
  • 头文件:
    include/llvm/Transforms/Obfuscation/SymbolObfuscation.h
  • cpp文件:
    lib/Transforms/SymbolObfuscation/SymbolObfuscation.cpp
  • 相关 CMakeLists 的配置不在赘述,看过前几小节的应该知道
  1. cmake指定xcode工程:

**

mkdir build_xcode
cd build_xcode
cmake -G Xcode CMAKE_BUILD_TYPE="Debug" ../llvm
  1. 打开xcode工程:
  • 首次打开时,时间充足时可以全部构建一遍,选择ALL BUILD,时间略长;

  • 也可以在 manage scheme 选择时选择手动管理,添加 opt 模块 、clang模块和 LLVMSymbolObfuscation 模块,这样节省时间,也只需要编译这俩模块;

  • 可以看到新创建的pass模块:loadabl modules --> LLVMSymbolObfuscation

    image.png

三、pass的调试与开发:

  1. 完善头文件与cpp文件内容,我这里参考张总的教程,创建一个简单符号混淆的pass
  • 头文件内容:

**


#include "llvm/Pass.h"

#define DEBUG_TYPE "symbolobf"

namespace llvm {
    ModulePass* createSymbolObfuscationPass();
//    void initializeSymbolObfuscationPass(PassRegistry &Registry);
}
  • cpp文件代码:

**


#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/Obfuscation/SymbolObfuscation.h"
#include <string>
#include <iostream>
#include <cstdlib>

using namespace llvm;
using namespace std;
static string obfcharacters="qwertyuiopasdfghjklzxcvbnm1234567890";
namespace llvm{
    struct SymbolObfuscation : public ModulePass {
        static char ID;
        int seed = 0;
        SymbolObfuscation() : ModulePass(ID) {}
        string randomString(int length){
            string name;
            name.resize(length);
            srand(seed);
            seed++;
            for(int i=0;i<length;i++){
                name[i]=obfcharacters[rand()%(obfcharacters.length())];
            }
            return name;
        }
        bool runOnModule(Module &M) override {
            //F.setName(randomString(16));
            errs()<<"Start Symbol Rewrite!\n";
            for(Module::iterator Fun=M.begin();Fun!=M.end();Fun++){
                Function &F=*Fun;
                if (F.getName().str().compare("main")==0){
                    errs()<<"Skipping main\n";
                }
                else if(F.empty()==false){
                    //Rename
                    string newname = randomString(16);
                    errs()<<"Renaming Function: "<<F.getName()<<" --> New Name: "<<newname<<"\n";
                    F.setName(newname);
                }
                else{
                    errs()<<"Skipping External Function: "<<F.getName()<<"\n";
                }
            }
            
            return true;
        }
    };
    ModulePass * createSymbolObfuscationPass() {return new SymbolObfuscation();}
}

char SymbolObfuscation::ID = 0;
//INITIALIZE_PASS(SymbolObfuscation, "symbolobf", "Rewrite Symbols",
//                false, false)
static RegisterPass<SymbolObfuscation> X("symbolobf", "Rewrite Symbols", false, false);
  1. 单独调试这一个pass需要使用opt模块调用:
  • 构建库LLVMSymbolObfuscation:
    选择 模块 LLVMSymbolObfuscation 构建,command + B 编译;

    image.png

  • 构建opt模块:
    选择 模块 opt 构建,command + B 编译,时间略长;

    image.png

  • 设置opt调用参数:
    opt的用法不再赘述,文档:llvm.org/docs/Comman…

    image.png

输入的 .ll 文件时自己写的一个测试代码,如下:

**

#include <stdio.h>

static int add(int x, int y) {
    return x + y;
}

int sub(int x, int y) {
    return x - y;
}

int main(){
    printf("%d",add(3,4));
    printf("%d",sub(5,4));
    return 0;
}

生成 .ll 文件:
clang -c test_symbolobf.c -o test_symbolobf.ll -emit-llvm -S

  • 断点调试:
    上述全部OK后,可以直接在源文件打断点调试:
    如图,断点调试查看设置前的随机字符串。

    image.png

  1. 查看符号替换的结果:
  • 可以直接调试打印IR查看:

    image.png

  • 直接查看生成的文件:
    opt生成的bitcode,需要先转换为文本IR:
    llvm-dis /Users/qinyao/LLVM/test_symbolobf/test_symbolobf.ll.bc
    查看 llvm-dis 生成的IR:

    image.png

四、扩展:

符号混淆是一个比较难处理的点,涉及系统函数以及导出函数,所以 hikari 也并没有开放这个功能,作为学习还不错;
现在 llvm 在utils目录下有自带的 SymbolRewriter.cpp Pass,下一节章节分析一下。

作者:鸣人的大哥
链接:www.jianshu.com/p/735dde508…
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。