前言
之前寫過一個小工具 MixPlainText甩骏,可以將 Xcode 工程代碼中所有的明文加密香罐。原理是使用正則表達式來提取代碼中所有的明文∨;叮現在介紹一種新的更加完善逸绎、更有拓展性的方法惹恃。用到的工具主要是 libclang
。
這里我已經將所有代碼棺牧、配置上傳到 GitHub 了巫糙,所以有興趣也可以直接下載代碼查看,地址 UsingLibClang颊乘。
配置 Xcode 工程
由于我是用 C 語言來調用 libclang
的参淹。所以這里介紹一下怎么配置 Xcode 工程來使用這個庫。當然你也可以用 Python 來使用這個庫疲牵,具體方式可以看這個文章 Parsing C++ in Python with Clang
去 http://llvm.org/svn/llvm-project/cfe/trunk/include/clang-c/ 下載所有頭文件承二。
工程中添加上面下載的頭文件
點擊工程配置中的 ‘Build Phases’,打開 ‘Link Binary With Libraries’,點擊 ‘+’ 號纲爸,然后選擇 ‘Add other...’亥鸠。
使用快捷鍵 ??G 打開
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib
,將 Xcode 自帶的 libclang.dylib 添加到環(huán)境中。轉到 ‘Build Setting’识啦。
-
添加一個新的 ‘Runpath search paths’:‘
$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib
’這里的
$(DEVELOPER_DIR)
指代/Applications/Xcode.app/Contents/Developer
-
添加一個新的 ‘header search paths’:
$(SRCROOT)/path_to_clang_header_file
注意這里的
$(SRCROOT)
指代工程所在的目錄负蚊。后面的具體路徑就是上面下載的頭文件的位置。 添加新的 ‘Library Search Paths’:
$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib
將
Enable Modules (C and Objective-C)
設置為 NO
使用 libclang
API
代碼如下颓哮,必要地方有注釋說明:
#import <Foundation/Foundation.h>
#include <cstdio>
#include <string>
#include <cstdlib>
// libclang 公開 API 均在這里
#include "clang-c/Index.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 一個編譯單元家妆,通常是一個文件
CXTranslationUnit tu;
// 一個 index 可以包含多個編譯單元
CXIndex index = clang_createIndex(1, 1);
// 需要分析混淆的 Objective C 源代碼文件
const char *filePath = "/path_to_OC_code/HomeViewController.m";
//
tu = clang_parseTranslationUnit(index, filePath, NULL, 0, nullptr, 0, 0);
if (!tu) {
printf("Couldn't create translation unit");
return 1;
}
// 根 cursor
CXCursor rootCursor = clang_getTranslationUnitCursor(tu);
// 一個個經過詞法分析以后得到的 token
CXToken *tokens;
unsigned int numTokens;
CXCursor *cursors = 0;
CXSourceRange range = clang_getCursorExtent(rootCursor);
// 獲取所有的 token
clang_tokenize(tu, range, &tokens, &numTokens);
cursors = (CXCursor *)malloc(numTokens * sizeof(CXCursor));
// 獲取每個 token 對應的 cursor
clang_annotateTokens(tu, tokens, numTokens, cursors);
// 遍歷 token
for(int i=0; i < numTokens; i++) {
CXToken token = tokens[i];
CXCursor cursor = cursors[i];
CXString tokenSpelling = clang_getTokenSpelling(tu, token);
CXString cursorSpelling = clang_getCursorSpelling(cursor);
const char *tokenName = clang_getCString(tokenSpelling);
if (CXToken_Literal == clang_getTokenKind(token) // 是明文的 token
&& CXCursor_PreprocessingDirective != cursor.kind // 排除預編譯 token
&& strlen(tokenName) >= 2 // 排除所有非字符串 token
&& tokenName[0] == '\"'
&& tokenName[strlen(tokenName) - 1] == '\"' ) {
// Do some replacing.
NSData *content = [[NSFileManager defaultManager] contentsAtPath:[NSString stringWithUTF8String:filePath]];
NSString *contentString = [[NSString alloc] initWithData:content encoding:NSUTF8StringEncoding];
// stringToBeResplaced 是需要被混淆的代碼
NSString *stringToBeResplaced = [NSString stringWithUTF8String:tokenName];
// stringToBePutted 是經過混淆的代碼
NSString *stringToBePutted = @"\"Hello\"";
contentString = [contentString stringByReplacingOccurrencesOfString:stringToBeResplaced withString:stringToBePutted];
// 將經過混淆的代碼寫回原文件
[contentString writeToFile:[NSString stringWithUTF8String:filePath] atomically:YES encoding:NSUTF8StringEncoding error:nil];
printf("\t%d\t%s\n", cursor.kind, tokenName);
}
// 釋放內存
clang_disposeString(tokenSpelling);
clang_disposeString(cursorSpelling);
}
// 釋放內存
clang_disposeTokens(tu, tokens, numTokens);
clang_disposeIndex(index);
clang_disposeTranslationUnit(tu);
free(cursors);
}
return 0;
}
使用方式
建議將上述代碼編譯生成的二進制可執(zhí)行文件,放到您需要混淆的工程里面冕茅。添加一個新的 ‘Run path’伤极,調用這個二進制文件處理每個你想要處理的源代碼蛹找。