本文通過clang的LibTooling來實(shí)現(xiàn)Objective-C源碼中方法名的混淆。
1.準(zhǔn)備環(huán)境
需要下載Clang和LLVM。簡單說一下,Clang是編譯器的前端,能夠解析C/C++/OC代碼,Clang生成Intermediate Representation代碼(簡稱IR);LLVM是編譯器的后端,使用Clang生成的IR來生成不同平臺(tái)的目標(biāo)代碼。
本人使用的是Xcode 9.4,下載的Clang和LLVM都是release_39這個(gè)分支的代碼,親測可用。
$ 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
下載后,進(jìn)入llvm目錄,創(chuàng)建llvm_build目錄,然后進(jìn)入:
$ cd llvm && mkdir llvm_build && cd llvm_build
使用Xcode完成cmake:
$ cmake -G "Xcode" ..
等待完成。
2.創(chuàng)建工程
Clang的源碼在llvm/tools/clang/下主要是include和lib文件夾,tools文件夾下是使用Clang的庫實(shí)現(xiàn)的一些工具。我們的混淆工具也是基于Clang庫,所以項(xiàng)目也創(chuàng)建在tools下邊,與其他工具在同一層級(jí)下,創(chuàng)建我們的工程目錄clang-autostats:
$ cd llvm/tools/clang/tools && mkdir clang-autostats
$ cd clang-autostats
然后創(chuàng)建源文件ClangAutoStats.cpp。在相同目錄下添加CMakeLists.txt文件,內(nèi)容如下:
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中,進(jìn)入llvm/tools/clang/tools目錄,在CMakeList.txt最后一行加入文字:
echo 'add_subdirectory(clang-autostats)' >> ./CMakeLists.txt
回到llvm_build,再次執(zhí)行“cmake -G "Xcode" ..”。打開llvm_build/LLVM.xcodeproj,在Clang executables文件夾下會(huì)出現(xiàn)我們的工程目錄。
使用Xcode打開LLVM.xcodeproj時(shí),創(chuàng)建新的scheme,選擇ClangAutoStats,如下圖所示。

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
解析結(jié)果如下:
|-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'
從上述語法樹解析結(jié)果來看,"- (void)hahaha"函數(shù)在頭文件中的定義:
ObjCMethodDecl 0x7fc9de353f50 <line:11:1, col:15> col:1 - hahaha 'void'
和源文件中的實(shí)現(xiàn):
ObjCMethodDecl 0x7fc9de354690 <line:16:1, line:20:1> line:16:1 - hahaha 'NSInteger':'long'
都可以從語法樹中獲取到,所以使用語法樹完全可以定位具體函數(shù)位置,并對(duì)函數(shù)名字進(jìn)行替換。
main函數(shù)
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());
// 替換函數(shù)名階段
selectorPass = false;
result = Tool.run(newFrontendActionFactory<ExampleFrontendAction>().get());
return result;
}
這里要關(guān)注的有兩點(diǎn):
1>提供需要遍歷語法樹的Objective-C源文件,即.m文件;當(dāng)然.h文件也可以,但是有個(gè)坑,后面會(huì)說明。
2> 創(chuàng)建了一個(gè)新的FrontedAction,接下來關(guān)注創(chuàng)建的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> 需要為每一個(gè)translation unit提供一個(gè)ASTConsumer
return make_unique<ExampleASTConsumer>(rewriter);
}
// 2> 解析完成后,將內(nèi)容回寫到對(duì)應(yīng)文件中
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結(jié)果輸出到文件中
rewriter.getEditBuffer(SM.getMainFileID()).write(outFile);
// 將Rewriter結(jié)果輸出在控制臺(tái)上
// rewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs());
}
};
ExampleFrontedAction->ASTFrontedAction->FrontedAction,這是這三個(gè)類的繼承關(guān)系。
FrontendAction是一個(gè)在編譯過程中允許執(zhí)行用戶特殊操作的接口。為了獲取語法樹AST,clang提供了ASTFrontendAction,而對(duì)語法樹進(jìn)行操作則需要用戶為每一個(gè)translation unit提供一個(gè)ASTConsumer,對(duì)應(yīng)標(biāo)注1>。
FrontendAction:Abstract base class for actions which can be performed by the frontend. FrontendAction有三個(gè)public interface。
BeginSourceFile():該函數(shù)運(yùn)行在options和FrontendAction初始化完成之后,每個(gè)文件Parse之前。如果該函數(shù)返回false,則后面的步驟不會(huì)執(zhí)行。
Excute():Set the source manager's main input file, and run the action.
EndSourceFile():每個(gè)文件在parse完之后,做一些清理和內(nèi)存釋放工作。(Perform any per-file post processing, deallocate per-file objects, and run statistics and output file cleanup code)。
我們通過遍歷AST過程中,對(duì)方法名進(jìn)行了修改,需要處理完一個(gè)文件后就將內(nèi)容回寫到對(duì)應(yīng)的文件中,因此我們重載了EndSourceFileAction()方法。對(duì)應(yīng)標(biāo)注2>。
ASTConsumer和RecursiveASTVisitor
ASTConsumer提供了對(duì)AST進(jìn)行操作的接口,具體對(duì)語法樹每一個(gè)節(jié)點(diǎn)的操作定義在了RecursiveASTVisitor中。
RecursiveASTVisitor提供了對(duì)大多數(shù)AST node節(jié)點(diǎn)訪問的hook方法。我們要對(duì)方法名進(jìn)行混淆,也就是重寫,需要能夠訪問到Interface/Category中方法名的定義;Interface/Category中Implementation中方法的實(shí)現(xiàn);發(fā)送消息處的方法名。
我們定義了兩個(gè)ASTConsumer:
- SelectorASTConsumer
主要作用是抽取源碼中@selector中的函數(shù)名并保存,因?yàn)閺腀selector中的函數(shù)反推到函數(shù)所在的類實(shí)現(xiàn)比較復(fù)雜,所以暫時(shí)不對(duì)@selector出現(xiàn)的函數(shù)進(jìn)行重命名。 - ExampleASTConsumer
主要作用就是對(duì)函數(shù)定義和消息發(fā)送中的函數(shù)名字進(jìn)行重命名。
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
{}
// 判斷函數(shù)是否能夠混淆
bool canObfuscate(ObjCMethodDecl *MD) {
// 如果該方法是協(xié)議方法,不進(jìn)行混淆
ObjCInterfaceDecl *ID = MD->getClassInterface();
if (!ID) {
return false;
}
for (ObjCProtocolDecl *protocol : ID->all_referenced_protocols()) {
if (protocol->lookupMethod(MD->getSelector(), MD->isInstanceMethod())) {
return false;
}
}
// 不混淆讀寫方法/系統(tǒng)方法/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> 混淆發(fā)送消息處的方法名字
bool VisitObjCMessageExpr(ObjCMessageExpr *messageExpr) {
// 跳過系統(tǒng)類
ObjCMethodDecl *MD = messageExpr->getMethodDecl();
if (MD) {
if(canObfuscate(MD) == false) {
return true;
}
Selector selector = messageExpr->getSelector();
// 方法是通過.調(diào)用還是通過發(fā)消息調(diào)用
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;
}
// 修改函數(shù)聲明處的函數(shù)名字
void renameFunctionName(ObjCMethodDecl *MD) {
// 判斷是否應(yīng)該混淆方法名
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()回調(diào)能夠獲取到AST中方法的聲明/定義節(jié)點(diǎn),唯一的區(qū)別是:在方法聲明處,D->hasBody()為false,而在方法定義處,D->hasBody()為true,但是這并不影響我們修改方法名字。
但是有些方法名字不能混淆,比如ViewController中實(shí)現(xiàn)協(xié)議UITableViewDelegate的方法的名字,在函數(shù)canObfuscate()中,通過查找該方法是否是類實(shí)現(xiàn)的協(xié)議方法來過濾掉。
目前沒有做屬性讀寫方法的混淆,直接過濾掉了,通過ObjCMethodDecl的** isPropertyAccessor()方法可以判斷是否是屬性讀寫方法。有時(shí)候在category中定義關(guān)聯(lián)屬性,并自定義讀寫方法,這時(shí)通過isPropertyAccessor()是無法判斷方法是否是屬性讀寫方法的,我們?cè)?/strong>canObfuscate()中判斷前綴是否是"set"**來判斷。
修改發(fā)送消息處的方法名
VisitObjCMessageExpr回調(diào)能夠獲取AST中發(fā)送消息的節(jié)點(diǎn),通過canObfuscate判斷是否能夠混淆,然后進(jìn)行混淆。
代碼中的混淆方法
代碼中的混淆方法僅僅是在方法selector第一個(gè)slot前加上了zdd_前綴,可以替換為自己的混淆方法。
4.運(yùn)行代碼
通過Xcode調(diào)試代碼
項(xiàng)目源碼地址:https://github.com/tom555cat/obfuscator-clang.git
將源代碼粘貼進(jìn)ClangAutoStats.cpp之后,還需要設(shè)置Xcode中Scheme的參數(shù):
/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
第一行參數(shù)為要修改代碼的目錄。
最后一行參數(shù)"-ObjC"主要是針對(duì)修改頭文件中方法名添加的。
編譯成可執(zhí)行文件
編譯完成的可執(zhí)行文件ClangAutoStats在github上的路徑為:https://github.com/tom555cat/obfuscator-clang。
下載后通過下面方式執(zhí)行:
./ClangAutoStats SourceFilesDirectory -- -ferror-limit=9999999 -ObjC