技术编程|自动重构Meterpreter绕过杀软·续( 三 )


然后运行:bash clang-astdump.sh test/messagebox_simple.c > test/messagebox_simple.c.ast

技术编程|自动重构Meterpreter绕过杀软·续
本文插图

在源代码中定位函数调用基本上等于查找CallExpr类型的AST节点 。如上面的截图所示 , 实际调用的函数名是在其某个子节点中被指定的 , 因此后面应该可以访问它 。 找到给定API的函数调用
为了枚举给定函数的每个函数调用 , 根据需要我们选择了ASTMatcher 。首先 , 正确使用这个匹配器的语法很重要 , 因为它比上一篇文章中使用的语法要复杂一些 。为了得到正确的结果 , 我们选择依靠clang-query来完成 , 这是一个非常有价值的交互式工具 , 允许在源代码上运行自定义查询 。有趣的是 , 它也基于libTooling , 并且其功能远比在本文章中展示的功能要强大得多(更多细节请参阅此处) 。 clang-query> match callExpr(callee(functionDecl(hasName("MessageBoxA"))))Match #1:/Users/vladimir/dev/scrt/avcleaner/test/messagebox_simple.c:6:5: note: "root" binds hereMessageBoxA(NULL, "Test", "Something", MB_OK);^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~1 match.clang-query>
反复试验可以迅速找到到有效的解决方案 。现在通过验证我们发现匹配器工作良好 , 我们可以像前面的文章中提到的一样创建一个新的ASTConsumer 。基本上 , 它是复制我们对clang-query所做的工作 , 只不过是在C++中:class ApiCallConsumer : public clang::ASTConsumer {public:ApiCallConsumer(std::string ApiName, std::string TypeDef, std::string Library): _ApiName(std::move(ApiName)), _TypeDef(std::move(TypeDef)), _Library(std::move(Library)) {}void HandleTranslationUnit(clang::ASTContext &Context) override {using namespace clang::ast_matchers;using namespace AVObfuscator;llvm::outs() << "[ApiCallObfuscation] Registering ASTMatcher for " << _ApiName << "\n";MatchFinder Finder;ApiMatchHandler Handler(&ASTRewriter, _ApiName, _TypeDef, _Library);const auto Matcher = callExpr(callee(functionDecl(hasName(_ApiName)))).bind("callExpr");Finder.addMatcher(Matcher, &Handler);Finder.matchAST(Context);}private:std::string _ApiName;std::string _TypeDef;std::string _Library;};
我们发现此实现最重要的细节是能否提供匹配许多不同函数的能力 , 并且由于最终的目标是为每个替换的API函数前面插入LoadLibrary/GetProcAddress , 因此我们需要能够在函数原型中提供要加载的DLL名称 。
这样做可以优雅地注册与要替换的API一样多的ASTConsumers 。此ASTConsumer的实例化必须在ASTFrontendAction中完成:

技术编程|自动重构Meterpreter绕过杀软·续
本文插图

这是我们在上一篇文章中对现有代码所做的唯一修改 。从这里开始 , 其他东西都可以通过我们将要添加的一组代码来实现 , 首先创建ApiMatchHandler.cpp , 匹配器必须提供一个回调函数 , 所以让我们给它一个:void ApiMatchHandler::run(const MatchResult &Result) {llvm::outs() << "Found " << _ApiName << "\n";const auto *CallExpression = Result.Nodes.getNodeAs
("callExpr");handleCallExpr(CallExpression, Result.Context);}
在本文开始的任务分解步骤的具体实现过程中 , 我们可以在代码层面给他做一些位置调换 , 例如使用以下方法:bool handleCallExpr(const clang::CallExpr *CallExpression, clang::ASTContext *const pContext);bool replaceIdentifier(const clang::CallExpr *CallExpression, const std::string &ApiName,const std::string &NewIdentifier);booladdGetProcAddress(const clang::CallExpr *pCallExpression, clang::ASTContext *const pContext,const std::string &NewIdentifier, std::string &ApiName);clang::SourceRange findInjectionSpot(clang::ASTContext *const Context, clang::ast_type_traits::DynTypedNode Parent,const clang::CallExpr &Literal, uint64_t Iterations);替换函数调用


推荐阅读