├── ClangAutoStats ├── ClangAutoStats.cpp └── README.md /ClangAutoStats: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tom555cat/obfuscator-clang/d7bbeb3e20c77dc7271e6a5c5b5e903f130d1ad1/ClangAutoStats -------------------------------------------------------------------------------- /ClangAutoStats.cpp: -------------------------------------------------------------------------------- 1 | #include "clang/Driver/Options.h" 2 | #include "clang/AST/AST.h" 3 | #include "clang/AST/ASTContext.h" 4 | #include "clang/AST/ASTConsumer.h" 5 | #include "clang/AST/RecursiveASTVisitor.h" 6 | #include "clang/Frontend/ASTConsumers.h" 7 | #include "clang/Frontend/FrontendActions.h" 8 | #include "clang/Frontend/CompilerInstance.h" 9 | #include "clang/Tooling/CommonOptionsParser.h" 10 | #include "clang/Tooling/Tooling.h" 11 | #include "clang/Rewrite/Core/Rewriter.h" 12 | #include "clang/Rewrite/Frontend/FixItRewriter.h" 13 | #include 14 | #include 15 | using namespace std; 16 | using namespace clang; 17 | using namespace clang::driver; 18 | using namespace clang::tooling; 19 | using namespace llvm; 20 | //Rewriter rewriter; 21 | int numFunctions = 0; 22 | 23 | std::map selectorMap; 24 | bool selectorPass = true; 25 | 26 | class ExampleVisitor : public RecursiveASTVisitor { 27 | private: 28 | //ASTContext *astContext; // used for getting additional AST info 29 | //typedef clang::RecursiveASTVisitor Base; 30 | Rewriter &rewriter; 31 | public: 32 | explicit ExampleVisitor(Rewriter &R) 33 | : rewriter{R} // initialize private members 34 | {} 35 | 36 | // 判断函数是否能够混淆 37 | bool canObfuscate(ObjCMethodDecl *MD) { 38 | // 如果该方法是协议方法,不进行混淆 39 | ObjCInterfaceDecl *ID = MD->getClassInterface(); 40 | if (!ID || isInSystem(ID)) { 41 | return false; 42 | } 43 | for (ObjCProtocolDecl *protocol : ID->all_referenced_protocols()) { 44 | if (protocol->lookupMethod(MD->getSelector(), MD->isInstanceMethod())) { 45 | return false; 46 | } 47 | } 48 | 49 | // 不混淆读写方法/系统方法/init前缀方法/set前缀方法/zdd_前缀方法 50 | string methodName = MD->getNameAsString(); 51 | 52 | map::iterator it; 53 | if (selectorMap.find(methodName) != selectorMap.end()) { 54 | return false; 55 | } 56 | 57 | if (MD->isPropertyAccessor() || isInSystem(MD) || methodName.find("set") == 0 || methodName.find("init") == 0 || MD->isOverriding() ||methodName.find("zdd_") == 0) { 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | bool VisitObjCMethodDecl(ObjCMethodDecl *D) { 65 | this->renameFunctionName(D); 66 | return true; 67 | } 68 | 69 | // 修改函数调用 70 | bool VisitObjCMessageExpr(ObjCMessageExpr *messageExpr) { 71 | // 跳过系统类 72 | ObjCMethodDecl *MD = messageExpr->getMethodDecl(); 73 | if (MD) { 74 | if(canObfuscate(MD) == false) { 75 | return true; 76 | } 77 | Selector selector = messageExpr->getSelector(); 78 | // 方法是通过.调用还是通过发消息调用 79 | string funcNameWithPrefix = "zdd_" + selector.getNameForSlot(0).str(); 80 | errs() << "first selector slot size:" << selector.getNameForSlot(0).size() << "\n"; 81 | rewriter.ReplaceText(messageExpr->getSelectorStartLoc(), 82 | selector.getNameForSlot(0).size(), 83 | funcNameWithPrefix); 84 | } 85 | return true; 86 | } 87 | 88 | // 修改函数声明处的函数名字 89 | void renameFunctionName(ObjCMethodDecl *MD) { 90 | // 判断是否应该混淆方法名 91 | if (canObfuscate(MD) == false) { 92 | return; 93 | } 94 | string funcName = MD->getNameAsString(); 95 | 96 | Selector selector = MD->getSelector(); 97 | string funcNameWithPrefix = "zdd_" + selector.getNameForSlot(0).str(); 98 | rewriter.ReplaceText(MD->getSelectorStartLoc(), selector.getNameForSlot(0).size(), funcNameWithPrefix); 99 | } 100 | 101 | bool isInSystem(Decl *decl) { 102 | SourceManager &SM = rewriter.getSourceMgr(); 103 | if (SM.isInSystemHeader(decl->getLocation()) || 104 | SM.isInExternCSystemHeader(decl->getLocation())) { 105 | return true; 106 | } 107 | return false; 108 | } 109 | }; 110 | class ExampleASTConsumer : public ASTConsumer { 111 | private: 112 | ExampleVisitor visitor; // doesn't have to be private 113 | 114 | public: 115 | // override the constructor in order to pass CI 116 | explicit ExampleASTConsumer(Rewriter &R) 117 | : visitor(R) // initialize the visitor 118 | { } 119 | 120 | // override this to call our ExampleVisitor on the entire source file 121 | virtual void HandleTranslationUnit(ASTContext &Context) { 122 | /* we can use ASTContext to get the TranslationUnitDecl, which is 123 | a single Decl that collectively represents the entire source file */ 124 | visitor.TraverseDecl(Context.getTranslationUnitDecl()); 125 | } 126 | }; 127 | 128 | class SelectorVisitor : public RecursiveASTVisitor { 129 | private: 130 | //ASTContext *astContext; // used for getting additional AST info 131 | //typedef clang::RecursiveASTVisitor Base; 132 | Rewriter &rewriter; 133 | public: 134 | explicit SelectorVisitor(Rewriter &R) 135 | : rewriter{R} // initialize private members 136 | {} 137 | 138 | bool VisitObjCSelectorExpr(ObjCSelectorExpr *selectorExpr) { 139 | Selector sel = selectorExpr->getSelector(); 140 | errs() << "the selector name is:" << sel.getAsString() << "\n"; 141 | selectorMap.insert({sel.getAsString(), sel}); 142 | return true; 143 | } 144 | }; 145 | 146 | class SelectorASTConsumer: public ASTConsumer { 147 | private: 148 | SelectorVisitor visitor; 149 | public: 150 | explicit SelectorASTConsumer(Rewriter &R) : visitor(R) {} 151 | 152 | virtual void HandleTranslationUnit(ASTContext &Context) { 153 | visitor.TraverseDecl(Context.getTranslationUnitDecl()); 154 | } 155 | }; 156 | 157 | class ExampleFrontendAction : public ASTFrontendAction { 158 | 159 | private: 160 | Rewriter rewriter; 161 | public: 162 | virtual unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef file) { 163 | rewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); 164 | CI.getPreprocessor(); 165 | if (selectorPass == true) { 166 | return make_unique(rewriter); 167 | } else { 168 | return make_unique(rewriter); 169 | } 170 | } 171 | 172 | void EndSourceFileAction() override { 173 | SourceManager &SM = rewriter.getSourceMgr(); 174 | llvm::errs() << "** EndSourceFileAction for: " 175 | << SM.getFileEntryForID(SM.getMainFileID())->getName() << "\n"; 176 | 177 | // Now emit the rewritten buffer. 178 | string Filename = SM.getFileEntryForID(SM.getMainFileID())->getName(); 179 | std::error_code error_code; 180 | llvm::raw_fd_ostream outFile(Filename, error_code, llvm::sys::fs::F_None); 181 | rewriter.getEditBuffer(SM.getMainFileID()).write(outFile); 182 | //rewriter.getEditBuffer(SM.getMainFileID()).write(llvm::outs()); 183 | } 184 | }; 185 | /// Return if filePath a code file(.h/.m/.c/.cpp) 186 | bool hasSuffix(string rawStr, string suffix) { 187 | return rawStr.find(suffix) == (rawStr.length() - suffix.length()); 188 | } 189 | bool isCodeFile(string filePath, bool ignoreHeader) { 190 | if (hasSuffix(filePath, ".h")) { 191 | return !ignoreHeader; 192 | } else { 193 | return hasSuffix(filePath, ".m"); 194 | } 195 | } 196 | /// Get all .h/.m/.cpp/.c files in rootDir 197 | void getFilesInDir(string rootDir, vector &files, bool ignoreHeader) { 198 | if (sys::fs::exists(rootDir)) { 199 | std::error_code code; 200 | sys::fs::recursive_directory_iterator end; 201 | for (auto it = sys::fs::recursive_directory_iterator(rootDir, code); it != end; it.increment(code)) { 202 | if (code) { 203 | llvm::errs() << "Error: " << code.message() << "\n"; 204 | break; 205 | } 206 | 207 | string path = (*it).path(); 208 | if (isCodeFile(path, ignoreHeader)) { 209 | files.push_back(getAbsolutePath(path)); 210 | } 211 | } 212 | 213 | } else { 214 | llvm::errs() << "Directory " << rootDir << " not exists!\n"; 215 | } 216 | } 217 | void getHeaderFilesInDir(string rootDir, vector &files) { 218 | if (sys::fs::exists(rootDir)) { 219 | std::error_code code; 220 | sys::fs::recursive_directory_iterator end; 221 | for (auto it = sys::fs::recursive_directory_iterator(rootDir, code); it != end; it.increment(code)) { 222 | if (code) { 223 | llvm::errs() << "Error: " << code.message() << "\n"; 224 | break; 225 | } 226 | 227 | string path = (*it).path(); 228 | if (hasSuffix(path, ".h")) { 229 | files.push_back(getAbsolutePath(path)); 230 | } 231 | } 232 | 233 | } else { 234 | llvm::errs() << "Directory " << rootDir << " not exists!\n"; 235 | } 236 | } 237 | static cl::OptionCategory OptsCategory("ClangAutoStats"); 238 | int main(int argc, const char **argv) { 239 | 240 | // parse the command-line args passed to your code 241 | CommonOptionsParser op(argc, argv, OptsCategory); 242 | // create a new Clang Tool instance (a LibTooling environment) 243 | 244 | vector commands; 245 | 246 | for (int i = 0; i < argc; ++i) { 247 | const char *arg = argv[i]; 248 | if (sys::fs::is_directory(arg)) { 249 | // 先读取.m文件,然后读取.h文件,防止先修改.h文件,导致.m中的方法未定义 250 | getFilesInDir(arg, commands, true); 251 | getHeaderFilesInDir(arg, commands); 252 | } else if (sys::fs::is_regular_file(arg)) { 253 | string fullPath = string(argv[i]); 254 | commands.push_back(getAbsolutePath(fullPath)); 255 | } else { 256 | commands.push_back(string(argv[i])); 257 | } 258 | } 259 | 260 | ClangTool Tool(op.getCompilations(), commands); 261 | 262 | // 搜集selector阶段 263 | selectorPass = true; 264 | int result = Tool.run(newFrontendActionFactory().get()); 265 | 266 | // 替换函数名阶段 267 | selectorPass = false; 268 | result = Tool.run(newFrontendActionFactory().get()); 269 | 270 | errs() << "\nFound " << numFunctions << " functions.\n\n"; 271 | return result; 272 | } 273 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # obfuscator-clang 2 | 混淆Objective-C的前端工具 3 | 4 | 对Objective-C中方法名增加前缀“zdd_”进行混淆,详细文档见: 5 | https://www.jianshu.com/p/3a8fb6f7c55f 6 | 7 | 直接下载可执行文件ClangAutoStats进行函数名字替换: 8 | ``` 9 | ./ClangAutoStats SourceFilesDirectory -- -ferror-limit=9999999 -ObjC 10 | ``` 11 | 12 | 选项"-ferror-limit=9999999"避免错误过多停止执行;选项"-ObjC"是在头文件.h替换函数名时需要,否则头文件中的Objective-C方法不会识别出来。 13 | 14 | --------------------------------------------------------------------------------