Objective-C混淆之方法名混淆

本文通过clang的LibTooling来实现Objective-C源码中方法名的混淆。

1.准备环境

需要下载Clang和LLVM。简单说一下,Clang是编译器的前端,能够解析C/C++/OC代码,Clang生成Intermediate Representation代码(简称IR);LLVM是编译器的后端,使用Clang生成的IR来生成不同平台的目标代码。

本人使用的是Xcode 9.4,下载的Clang和LLVM都是release_39这个分支的代码,亲测可用。

$ git clone -b release_39 http://llvm.org/git/llvm llvm
$ cd llvm/tools
$ git clone -b release_39 http://llvm.org/git/clang clang

下载后,进入llvm目录,创建llvm_build目录,然后进入:

$ cd llvm && mkdir llvm_build && cd llvm_build

使用Xcode完成cmake:

$ cmake -G "Xcode" ..

等待完成。

2.创建工程

Clang的源码在llvm/tools/clang/下主要是include和lib文件夹,tools文件夹下是使用Clang的库实现的一些工具。我们的混淆工具也是基于Clang库,所以项目也创建在tools下边,与其他工具在同一层级下,创建我们的工程目录clang-autostats:

$ cd llvm/tools/clang/tools && mkdir clang-autostats
$ cd clang-autostats

然后创建源文件ClangAutoStats.cpp。在相同目录下添加CMakeLists.txt文件,内容如下:

set(LLVM_LINK_COMPONENTS
  Support
  )

add_clang_executable(ClangAutoStats
  ClangAutoStats.cpp
)

target_link_libraries(ClangAutoStats
    clangAST
    clangBasic
    clangDriver
    clangFormat
    clangLex
    clangParse
    clangSema
    clangFrontend
    clangTooling
    clangToolingCore
    clangRewrite
    clangRewriteFrontend
)

if(UNIX)
  set(CLANGXX_LINK_OR_COPY create_symlink)
else()
  set(CLANGXX_LINK_OR_COPY copy)
endif()

接下来将ClangAutoStats工程添加到LLVM中,进入llvm/tools/clang/tools目录,在CMakeList.txt最后一行加入文字:

echo 'add_subdirectory(clang-autostats)' >> ./CMakeLists.txt

回到llvm_build,再次执行“cmake -G "Xcode" ..”。打开llvm_build/LLVM.xcodeproj,在Clang executables文件夹下会出现我们的工程目录。

使用Xcode打开LLVM.xcodeproj时,创建新的scheme,选择ClangAutoStats,如下图所示。


创建新的scheme

3.替换方法名

语法树AST

示例代码:

Hello1.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

@protocol TTProtocol <NSObject>

- (void)protocolMethod;

@end

@interface HelloViewController : UIViewController
- (void)hahaha;
- (instancetype)sayHello;
- (void)sayOK:(NSString *)content toSomeOne:(NSString *)name;
+ (void)clmethod;
@end

Hello1.m

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "Hello1.h"

@interface HelloViewController () <TTProtocol>

@property (nonatomic, assign) NSInteger index;

@end

@interface HelloViewController (hehehe)

@end

@implementation HelloViewController
- (NSInteger)hahaha {
    NSInteger a = 100;
    a += 1;
    return a;
}
- (instancetype)sayHello {
    NSLog(@"Hi");
    return self;
}

- (void)sayOK:(NSString *)content toSomeOne:(NSString *)name {
    NSLog(@"123123");
}

- (void)protocolMethod {
    NSLog(@"3333");
}

+ (void)clmethod {
    NSLog(@"32233");
}

@end

输入如下命令,将示例代码解析为语法树:

clang -Xclang -ast-dump -fsyntax-only Hello1.m

解析结果如下:

|-ObjCProtocolDecl 0x7fc9de353d00 <./Hello1.h:4:1, line:8:2> line:4:11 TTProtocol
| |-ObjCProtocol 0x7fc9dc96c170 'NSObject'
| `-ObjCMethodDecl 0x7fc9de353db0 <line:6:1, col:23> col:1 - protocolMethod 'void'
|-ObjCInterfaceDecl 0x7fc9de353e38 <line:10:1, line:15:2> line:10:12 HelloViewController
| |-ObjCImplementation 0x7fc9de3545f8 'HelloViewController'
| |-ObjCMethodDecl 0x7fc9de353f50 <line:11:1, col:15> col:1 - hahaha 'void'
| |-ObjCMethodDecl 0x7fc9de353ff8 <line:12:1, col:25> col:1 - sayHello 'instancetype':'id'
| |-ObjCMethodDecl 0x7fc9de3540c0 <line:13:1, col:61> col:1 - sayOK:toSomeOne: 'void'
| | |-ParmVarDecl 0x7fc9de354148 <col:16, col:27> col:27 content 'NSString *'
| | `-ParmVarDecl 0x7fc9de3541a8 <col:46, col:57> col:57 name 'NSString *'
| `-ObjCMethodDecl 0x7fc9de354228 <line:14:1, col:17> col:1 + clmethod 'void'
|-ObjCCategoryDecl 0x7fc9de3542b0 <Hello1.m:5:1, line:9:2> line:5:12
| |-ObjCInterface 0x7fc9de353e38 'HelloViewController'
| |-ObjCProtocol 0x7fc9de353d00 'TTProtocol'
| |-ObjCPropertyDecl 0x7fc9de354370 <line:7:1, col:41> col:41 index 'NSInteger':'long' assign readwrite nonatomic unsafe_unretained
| |-ObjCMethodDecl 0x7fc9de3543e8 <col:41> col:41 implicit - index 'NSInteger':'long'
| `-ObjCMethodDecl 0x7fc9de354470 <col:41> col:41 implicit - setIndex: 'void'
|   `-ParmVarDecl 0x7fc9de3544f8 <col:41> col:41 index 'NSInteger':'long'
|-ObjCCategoryDecl 0x7fc9de354560 <line:11:1, line:13:2> line:11:12 hehehe
| `-ObjCInterface 0x7fc9de353e38 'HelloViewController'
`-ObjCImplementationDecl 0x7fc9de3545f8 <line:15:1, line:38:1> line:15:17 HelloViewController
  |-ObjCInterface 0x7fc9de353e38 'HelloViewController'
  |-ObjCMethodDecl 0x7fc9de354690 <line:16:1, line:20:1> line:16:1 - hahaha 'NSInteger':'long'
  | |-ImplicitParamDecl 0x7fc9de354cf8 <<invalid sloc>> <invalid sloc> implicit self 'HelloViewController *'
  | |-ImplicitParamDecl 0x7fc9de354d58 <<invalid sloc>> <invalid sloc> implicit _cmd 'SEL':'SEL *'
  | |-VarDecl 0x7fc9de354dc8 <line:17:5, col:19> col:15 used a 'NSInteger':'long' cinit
  | | `-ImplicitCastExpr 0x7fc9de354e48 <col:19> 'NSInteger':'long' <IntegralCast>
  | |   `-IntegerLiteral 0x7fc9de354e28 <col:19> 'int' 100
  | `-CompoundStmt 0x7fc9de354f68 <line:16:21, line:20:1>
  |   |-DeclStmt 0x7fc9de354e60 <line:17:5, col:22>
  |   | `-VarDecl 0x7fc9de354dc8 <col:5, col:19> col:15 used a 'NSInteger':'long' cinit
  |   |   `-ImplicitCastExpr 0x7fc9de354e48 <col:19> 'NSInteger':'long' <IntegralCast>
  |   |     `-IntegerLiteral 0x7fc9de354e28 <col:19> 'int' 100
  |   |-CompoundAssignOperator 0x7fc9de354ed8 <line:18:5, col:10> 'NSInteger':'long' '+=' ComputeLHSTy='long' ComputeResultTy='long'
  |   | |-DeclRefExpr 0x7fc9de354e78 <col:5> 'NSInteger':'long' lvalue Var 0x7fc9de354dc8 'a' 'NSInteger':'long'
  |   | `-ImplicitCastExpr 0x7fc9de354ec0 <col:10> 'long' <IntegralCast>
  |   |   `-IntegerLiteral 0x7fc9de354ea0 <col:10> 'int' 1
  |   `-ReturnStmt 0x7fc9de354f50 <line:19:5, col:12>
  |     `-ImplicitCastExpr 0x7fc9de354f38 <col:12> 'NSInteger':'long' <LValueToRValue>
  |       `-DeclRefExpr 0x7fc9de354f10 <col:12> 'NSInteger':'long' lvalue Var 0x7fc9de354dc8 'a' 'NSInteger':'long'
  |-ObjCMethodDecl 0x7fc9de354740 <line:21:1, line:24:1> line:21:1 - sayHello 'instancetype':'id'
  | |-ImplicitParamDecl 0x7fc9de354f90 <<invalid sloc>> <invalid sloc> implicit used self 'HelloViewController *'
  | |-ImplicitParamDecl 0x7fc9de354ff0 <<invalid sloc>> <invalid sloc> implicit _cmd 'SEL':'SEL *'
  | `-CompoundStmt 0x7fc9de3551f8 <col:26, line:24:1>
  |   |-CallExpr 0x7fc9de355140 <line:22:5, col:16> 'void'
  |   | |-ImplicitCastExpr 0x7fc9de355128 <col:5> 'void (*)(id, ...)' <FunctionToPointerDecay>
  |   | | `-DeclRefExpr 0x7fc9de355050 <col:5> 'void (id, ...)' Function 0x7fc9dd2bbc08 'NSLog' 'void (id, ...)'
  |   | `-ImplicitCastExpr 0x7fc9de355170 <col:11, col:12> 'id':'id' <BitCast>
  |   |   `-ObjCStringLiteral 0x7fc9de3550a8 <col:11, col:12> 'NSString *'
  |   |     `-StringLiteral 0x7fc9de355078 <col:12> 'char [3]' lvalue "Hi"
  |   `-ReturnStmt 0x7fc9de3551e0 <line:23:5, col:12>
  |     `-ImplicitCastExpr 0x7fc9de3551c8 <col:12> 'instancetype':'id' <BitCast>
  |       `-ImplicitCastExpr 0x7fc9de3551b0 <col:12> 'HelloViewController *' <LValueToRValue>
  |         `-DeclRefExpr 0x7fc9de355188 <col:12> 'HelloViewController *' lvalue ImplicitParam 0x7fc9de354f90 'self' 'HelloViewController *'
  |-ObjCMethodDecl 0x7fc9de354810 <line:26:1, line:28:1> line:26:1 - sayOK:toSomeOne: 'void'
  | |-ImplicitParamDecl 0x7fc9de355218 <<invalid sloc>> <invalid sloc> implicit self 'HelloViewController *'
  | |-ImplicitParamDecl 0x7fc9de355278 <<invalid sloc>> <invalid sloc> implicit _cmd 'SEL':'SEL *'
  | |-ParmVarDecl 0x7fc9de354898 <col:16, col:27> col:27 content 'NSString *'
  | |-ParmVarDecl 0x7fc9de3548f8 <col:46, col:57> col:57 name 'NSString *'
  | `-CompoundStmt 0x7fc9de3553b0 <col:62, line:28:1>
  |   `-CallExpr 0x7fc9de355368 <line:27:5, col:20> 'void'
  |     |-ImplicitCastExpr 0x7fc9de355350 <col:5> 'void (*)(id, ...)' <FunctionToPointerDecay>
  |     | `-DeclRefExpr 0x7fc9de3552d8 <col:5> 'void (id, ...)' Function 0x7fc9dd2bbc08 'NSLog' 'void (id, ...)'
  |     `-ImplicitCastExpr 0x7fc9de355398 <col:11, col:12> 'id':'id' <BitCast>
  |       `-ObjCStringLiteral 0x7fc9de355330 <col:11, col:12> 'NSString *'
  |         `-StringLiteral 0x7fc9de355300 <col:12> 'char [7]' lvalue "123123"
  |-ObjCMethodDecl 0x7fc9de354978 <line:30:1, line:32:1> line:30:1 - protocolMethod 'void'
  | |-ImplicitParamDecl 0x7fc9de3553c8 <<invalid sloc>> <invalid sloc> implicit self 'HelloViewController *'
  | |-ImplicitParamDecl 0x7fc9de355428 <<invalid sloc>> <invalid sloc> implicit _cmd 'SEL':'SEL *'
  | `-CompoundStmt 0x7fc9de355560 <col:24, line:32:1>
  |   `-CallExpr 0x7fc9de355518 <line:31:5, col:18> 'void'
  |     |-ImplicitCastExpr 0x7fc9de355500 <col:5> 'void (*)(id, ...)' <FunctionToPointerDecay>
  |     | `-DeclRefExpr 0x7fc9de355488 <col:5> 'void (id, ...)' Function 0x7fc9dd2bbc08 'NSLog' 'void (id, ...)'
  |     `-ImplicitCastExpr 0x7fc9de355548 <col:11, col:12> 'id':'id' <BitCast>
  |       `-ObjCStringLiteral 0x7fc9de3554e0 <col:11, col:12> 'NSString *'
  |         `-StringLiteral 0x7fc9de3554b0 <col:12> 'char [5]' lvalue "3333"
  |-ObjCMethodDecl 0x7fc9de354a18 <line:34:1, line:36:1> line:34:1 + clmethod 'void'
  | |-ImplicitParamDecl 0x7fc9de355578 <<invalid sloc>> <invalid sloc> implicit self 'Class':'Class'
  | |-ImplicitParamDecl 0x7fc9de3555d8 <<invalid sloc>> <invalid sloc> implicit _cmd 'SEL':'SEL *'
  | `-CompoundStmt 0x7fc9de355710 <col:18, line:36:1>
  |   `-CallExpr 0x7fc9de3556c8 <line:35:5, col:19> 'void'
  |     |-ImplicitCastExpr 0x7fc9de3556b0 <col:5> 'void (*)(id, ...)' <FunctionToPointerDecay>
  |     | `-DeclRefExpr 0x7fc9de355638 <col:5> 'void (id, ...)' Function 0x7fc9dd2bbc08 'NSLog' 'void (id, ...)'
  |     `-ImplicitCastExpr 0x7fc9de3556f8 <col:11, col:12> 'id':'id' <BitCast>
  |       `-ObjCStringLiteral 0x7fc9de355690 <col:11, col:12> 'NSString *'
  |         `-StringLiteral 0x7fc9de355660 <col:12> 'char [6]' lvalue "32233"
  |-ObjCIvarDecl 0x7fc9de354aa8 <line:7:41> col:41 implicit _index 'NSInteger':'long' synthesize private
  `-ObjCPropertyImplDecl 0x7fc9de354b00 <<invalid sloc>, col:41> <invalid sloc> index synthesize
    |-ObjCProperty 0x7fc9de354370 'index'
    `-ObjCIvar 0x7fc9de354aa8 '_index' 'NSInteger':'long'

从上述语法树解析结果来看,"- (void)hahaha"函数在头文件中的定义:

ObjCMethodDecl 0x7fc9de353f50 <line:11:1, col:15> col:1 - hahaha 'void'

和源文件中的实现:

ObjCMethodDecl 0x7fc9de354690 <line:16:1, line:20:1> line:16:1 - hahaha 'NSInteger':'long'

都可以从语法树中获取到,所以使用语法树完全可以定位具体函数位置,并对函数名字进行替换。

main函数

int main(int argc, const char **argv) {
    CommonOptionsParser op(argc, argv, OptsCategory); 
    vector<string> commands;
    ClangTool Tool(op.getCompilations(), commands);

    // 1> 添加要遍历的文件   
    commands.push_back("/Users/tom555cat/develop/RewriteDir/Hello1.m");

    // 搜集selector阶段
    selectorPass = true;
    int result = Tool.run(newFrontendActionFactory<ExampleFrontendAction>().get());
    
    // 替换函数名阶段
    selectorPass = false;
    result = Tool.run(newFrontendActionFactory<ExampleFrontendAction>().get());

    return result;
}

这里要关注的有两点:
1>提供需要遍历语法树的Objective-C源文件,即.m文件;当然.h文件也可以,但是有个坑,后面会说明。
2> 创建了一个新的FrontedAction,接下来关注创建的ExampleFrontedAction类。

ExampleFrontedAction

class ExampleFrontendAction : public ASTFrontendAction {
    
private:
    Rewriter rewriter;
public:
    virtual unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) {
        rewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts());
        CI.getPreprocessor();
        // 1> 需要为每一个translation unit提供一个ASTConsumer
        return make_unique<ExampleASTConsumer>(rewriter);    
    }
    
    // 2> 解析完成后,将内容回写到对应文件中
    void EndSourceFileAction() override {
        SourceManager &SM = rewriter.getSourceMgr();
        llvm::errs() << "** EndSourceFileAction for: "
        << SM.getFileEntryForID(SM.getMainFileID())->getName() << "\n";
        
        string Filename = SM.getFileEntryForID(SM.getMainFileID())->getName();
        std::error_code error_code;
        llvm::raw_fd_ostream outFile(Filename, error_code, llvm::sys::fs::F_None);
        // 将Rewriter结果输出到文件中
        rewriter.getEditBuffer(SM.getMainFileID()).write(outFile);
        // 将Rewriter结果输出在控制台上
        // rewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs());
    }
};

ExampleFrontedAction->ASTFrontedAction->FrontedAction,这是这三个类的继承关系。
FrontendAction是一个在编译过程中允许执行用户特殊操作的接口。为了获取语法树AST,clang提供了ASTFrontendAction,而对语法树进行操作则需要用户为每一个translation unit提供一个ASTConsumer,对应标注1>。

FrontendAction:Abstract base class for actions which can be performed by the frontend. FrontendAction有三个public interface。
BeginSourceFile():该函数运行在options和FrontendAction初始化完成之后,每个文件Parse之前。如果该函数返回false,则后面的步骤不会执行。
Excute():Set the source manager's main input file, and run the action.
EndSourceFile():每个文件在parse完之后,做一些清理和内存释放工作。(Perform any per-file post processing, deallocate per-file objects, and run statistics and output file cleanup code)。
我们通过遍历AST过程中,对方法名进行了修改,需要处理完一个文件后就将内容回写到对应的文件中,因此我们重载了EndSourceFileAction()方法。对应标注2>。

ASTConsumer和RecursiveASTVisitor

ASTConsumer提供了对AST进行操作的接口,具体对语法树每一个节点的操作定义在了RecursiveASTVisitor中。

RecursiveASTVisitor提供了对大多数AST node节点访问的hook方法。我们要对方法名进行混淆,也就是重写,需要能够访问到Interface/Category中方法名的定义;Interface/Category中Implementation中方法的实现;发送消息处的方法名。

我们定义了两个ASTConsumer:

  • SelectorASTConsumer
    主要作用是抽取源码中@selector中的函数名并保存,因为从@selector中的函数反推到函数所在的类实现比较复杂,所以暂时不对@selector出现的函数进行重命名。
  • ExampleASTConsumer
    主要作用就是对函数定义和消息发送中的函数名字进行重命名。
SelectorASTConsumer & SelectorVisitor
class SelectorVisitor : public RecursiveASTVisitor<SelectorVisitor> {
private:
    //ASTContext *astContext; // used for getting additional AST info
    //typedef clang::RecursiveASTVisitor<RewritingVisitor> Base;
    Rewriter &rewriter;
public:
    explicit SelectorVisitor(Rewriter &R)
    : rewriter{R} // initialize private members
    {}
    
    bool VisitObjCSelectorExpr(ObjCSelectorExpr *selectorExpr) {
        Selector sel = selectorExpr->getSelector();
        errs() << "the selector name is:" << sel.getAsString() << "\n";
        selectorMap.insert({sel.getAsString(), sel});
        return true;
    }
};

class SelectorASTConsumer: public ASTConsumer {
private:
    SelectorVisitor visitor;
public:
    explicit SelectorASTConsumer(Rewriter &R) : visitor(R) {}
    
    virtual void HandleTranslationUnit(ASTContext &Context) {
        visitor.TraverseDecl(Context.getTranslationUnitDecl());
    }
};
ExampleASTConsumer & ExampleVisitor
class ExampleASTConsumer : public ASTConsumer {
private:
    ExampleVisitor visitor; 
    
public:
    // override the constructor in order to pass CI
    explicit ExampleASTConsumer(Rewriter &R)
    : visitor(R) // initialize the visitor
    { }
    
    // override this to call our ExampleVisitor on the entire source file
    virtual void HandleTranslationUnit(ASTContext &Context) {
        /* we can use ASTContext to get the TranslationUnitDecl, which is
         a single Decl that collectively represents the entire source file */
        visitor.TraverseDecl(Context.getTranslationUnitDecl());
    }
};

class ExampleVisitor : public RecursiveASTVisitor<ExampleVisitor> {
private:
    //ASTContext *astContext; // used for getting additional AST info
    //typedef clang::RecursiveASTVisitor<RewritingVisitor> Base;
    Rewriter &rewriter;
public:
    explicit ExampleVisitor(Rewriter &R)
    : rewriter{R} // initialize private members
    {}
    
    // 判断函数是否能够混淆
    bool canObfuscate(ObjCMethodDecl *MD) {
        // 如果该方法是协议方法,不进行混淆
        ObjCInterfaceDecl *ID = MD->getClassInterface();
        if (!ID) {
            return false;
        }
        for (ObjCProtocolDecl *protocol : ID->all_referenced_protocols()) {
            if (protocol->lookupMethod(MD->getSelector(), MD->isInstanceMethod())) {
                return false;
            }
        }
        
        // 不混淆读写方法/系统方法/init前缀方法/set前缀方法/zdd_前缀方法
        string methodName = MD->getNameAsString();
        if (MD->isPropertyAccessor() || isInSystem(MD) || methodName.find("set") == 0 || methodName.find("init") == 0 || methodName.find("zdd_") == 0) {
            return false;
        }
        
        return true;
    }
    
    // 1> 混淆方法声明/定义处的名字
    bool VisitObjCMethodDecl(ObjCMethodDecl *D) {
        this->renameFunctionName(D);
        return true;
    }
    
    // 2> 混淆发送消息处的方法名字
    bool VisitObjCMessageExpr(ObjCMessageExpr *messageExpr) {
        // 跳过系统类
        ObjCMethodDecl *MD = messageExpr->getMethodDecl();
        if (MD) {
            if(canObfuscate(MD) == false) {
                return true;
            }
            Selector selector = messageExpr->getSelector();
            // 方法是通过.调用还是通过发消息调用
            string funcNameWithPrefix = "zdd_" + selector.getNameForSlot(0).str();
            errs() << "first selector slot size:" << selector.getNameForSlot(0).size() << "\n";
            rewriter.ReplaceText(messageExpr->getSelectorStartLoc(),
                                 selector.getNameForSlot(0).size(),
                                 funcNameWithPrefix);
        }
        return true;
    }
    


    // 修改函数声明处的函数名字
    void renameFunctionName(ObjCMethodDecl *MD) {
        // 判断是否应该混淆方法名
        if (canObfuscate(MD) == false) {
            return;
        }
        string funcName = MD->getNameAsString();
        
        Selector selector = MD->getSelector();
        string funcNameWithPrefix = "zdd_" + selector.getNameForSlot(0).str();
        rewriter.ReplaceText(MD->getSelectorStartLoc(), selector.getNameForSlot(0).size(), funcNameWithPrefix);
    }
    
    bool isInSystem(Decl *decl) {
        SourceManager &SM = rewriter.getSourceMgr();
        if (SM.isInSystemHeader(decl->getLocation()) ||
            SM.isInExternCSystemHeader(decl->getLocation())) {
            return true;
        }
        return false;
    }
};

修改方法声明/定义处的方法名字

VisitObjCMethodDecl()回调能够获取到AST中方法的声明/定义节点,唯一的区别是:在方法声明处,D->hasBody()为false,而在方法定义处,D->hasBody()为true,但是这并不影响我们修改方法名字。

但是有些方法名字不能混淆,比如ViewController中实现协议UITableViewDelegate的方法的名字,在函数canObfuscate()中,通过查找该方法是否是类实现的协议方法来过滤掉。

目前没有做属性读写方法的混淆,直接过滤掉了,通过ObjCMethodDecl的** isPropertyAccessor()方法可以判断是否是属性读写方法。有时候在category中定义关联属性,并自定义读写方法,这时通过isPropertyAccessor()是无法判断方法是否是属性读写方法的,我们在canObfuscate()中判断前缀是否是"set"**来判断。

修改发送消息处的方法名

VisitObjCMessageExpr回调能够获取AST中发送消息的节点,通过canObfuscate判断是否能够混淆,然后进行混淆。

代码中的混淆方法

代码中的混淆方法仅仅是在方法selector第一个slot前加上了zdd_前缀,可以替换为自己的混淆方法。

4.运行代码

通过Xcode调试代码

项目源码地址:https://github.com/tom555cat/obfuscator-clang.git
将源代码粘贴进ClangAutoStats.cpp之后,还需要设置Xcode中Scheme的参数:

/Users/tongleiming/Documents/test/RewriteDir
--
-mios-simulator-version-min=9.0
-isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
-isystem
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.1.0/include
-I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
-I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include
-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks
-ferror-limit=9999999
-ObjC

第一行参数为要修改代码的目录。
最后一行参数"-ObjC"主要是针对修改头文件中方法名添加的。

编译成可执行文件

编译完成的可执行文件ClangAutoStats在github上的路径为:https://github.com/tom555cat/obfuscator-clang
下载后通过下面方式执行:

./ClangAutoStats SourceFilesDirectory -- -ferror-limit=9999999 -ObjC
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容