Skip to content

Commit

Permalink
Auto insert LoadLibrary/GetProcAddress, add definitions for all APIs …
Browse files Browse the repository at this point in the history
…in samlib.dll
  • Loading branch information
plowsec committed Jun 20, 2020
1 parent 9375016 commit 8e53481
Show file tree
Hide file tree
Showing 11 changed files with 2,720 additions and 145 deletions.
105 changes: 95 additions & 10 deletions ApiMatchHandler.cpp
Expand Up @@ -13,27 +13,112 @@
#include "clang/Tooling/Inclusions/HeaderIncludes.h"
#include "clang/Tooling/Inclusions/IncludeStyle.h"
#include "Globals.h"
#include "Utils.h"

using namespace clang;

ApiMatchHandler::ApiMatchHandler(clang::Rewriter *rewriter) {
this->ASTRewriter = rewriter;
}

void ApiMatchHandler::run(const MatchResult &Result) {
const auto *CallExpression = Result.Nodes.getNodeAs<clang::CallExpr>("callExpr");
llvm::outs() << "Found WriteProcessMemory\n";
std::string ApiMatchHandler::getFunctionIdentifier(const CallExpr *CallExpression) {

const FunctionDecl *FnDeclaration = CallExpression->getDirectCallee();

//abort if invalid call
if (FnDeclaration == nullptr)
return;
return nullptr;

IdentifierInfo *II = FnDeclaration->getIdentifier();

if (II == nullptr) {
return;
return nullptr;
}

return II->getName();
}

bool ApiMatchHandler::replaceIdentifier(const CallExpr *CallExpression, const std::string &ApiName,
const std::string &NewIdentifier) {
return this->ASTRewriter->ReplaceText(CallExpression->getBeginLoc(), ApiName.length(), NewIdentifier);
}

bool ApiMatchHandler::handleCallExpr(const CallExpr *CallExpression, clang::ASTContext *const pContext) {

std::string Identifier = getFunctionIdentifier(CallExpression);
std::string Replacement = Utils::translateStringToIdentifier(Identifier);

if (!addGetProcAddress(CallExpression, pContext, Replacement, Identifier))
return false;

//Globs::PatchedSourceLocation.push_back(LiteralRange);

return replaceIdentifier(CallExpression, Identifier, Replacement);
}

void ApiMatchHandler::run(const MatchResult &Result) {

llvm::outs() << "Found " << _ApiName << "\n";

const auto *CallExpression = Result.Nodes.getNodeAs<clang::CallExpr>("callExpr");
handleCallExpr(CallExpression, Result.Context);
}


bool ApiMatchHandler::addGetProcAddress(const clang::CallExpr *pCallExpression, clang::ASTContext *const pContext,
const std::string &NewIdentifier, std::string &ApiName) {

SourceRange EnclosingFunctionRange = findInjectionSpot(pContext, clang::ast_type_traits::DynTypedNode(),
*pCallExpression, 0);

std::stringstream Result;

// add LoadLibrary with obfuscated strings
std::string LoadLibraryVariable = Utils::translateStringToIdentifier(_Library);
std::string LoadLibraryString = Utils::generateVariableDeclaration(LoadLibraryVariable, _Library);
std::string LoadLibraryHandleIdentifier = Utils::translateStringToIdentifier("hHandle_"+_Library);
Result << LoadLibraryString << std::endl;
Result << "\tHANDLE " << LoadLibraryHandleIdentifier << " = LoadLibrary(" << LoadLibraryVariable << ");\n";

// add GetProcAddress with obfuscated string: TypeDef NewIdentifier = (TypeDef) GetProcAddress(handleIdentifier, ApiName)
std::string ApiNameIdentifier = Utils::translateStringToIdentifier(ApiName);
std::string ApiNameDecl = Utils::generateVariableDeclaration(ApiNameIdentifier, ApiName);
Result << ApiNameDecl << "\n";
Result << "_ "<< ApiName << " " << NewIdentifier << " = (_" << ApiName << ") GetProcAddress("
<< LoadLibraryHandleIdentifier << ", " << ApiNameIdentifier << ");\n";

bool InsertResult = ASTRewriter->InsertText(EnclosingFunctionRange.getBegin(), Result.str());

if (InsertResult) {
llvm::errs() << " Could not finish to patch the function call.\n";
//Globs::PatchedSourceLocation.push_back(range);
}

return !InsertResult;
}

SourceRange
ApiMatchHandler::findInjectionSpot(clang::ASTContext *const Context, clang::ast_type_traits::DynTypedNode Parent,
const clang::CallExpr &Literal, uint64_t Iterations) {

if (Iterations > Globs::CLIMB_PARENTS_MAX_ITER)
throw std::runtime_error("Reached max iterations when trying to find a function declaration");

ASTContext::DynTypedNodeList parents = Context->getParents(Literal);;

if (Iterations > 0) {
parents = Context->getParents(Parent);
}

for (const auto &parent : parents) {

StringRef ParentNodeKind = parent.getNodeKind().asStringRef();

if (ParentNodeKind.find("FunctionDecl") != std::string::npos) {
auto FunDecl = parent.get<clang::FunctionDecl>();
auto *Statement = FunDecl->getBody();
auto *FirstChild = *Statement->child_begin();
return {FirstChild->getBeginLoc(), FunDecl->getEndLoc()};

}

return findInjectionSpot(Context, parent, Literal, ++Iterations);
}
std::string ApiName = II->getName();
this->ASTRewriter->ReplaceText(CallExpression->getBeginLoc(), ApiName.length(), "toto");
}
48 changes: 46 additions & 2 deletions ApiMatchHandler.h
Expand Up @@ -41,13 +41,57 @@ class ApiMatchHandler : public clang::ast_matchers::MatchFinder::MatchCallback {
public:
using MatchResult = clang::ast_matchers::MatchFinder::MatchResult;

ApiMatchHandler(clang::Rewriter *rewriter);
ApiMatchHandler(clang::Rewriter *rewriter, std::string ApiName, std::string TypeDef, std::string Library) : ASTRewriter(rewriter),
_ApiName(ApiName),
_TypeDef(TypeDef), _Library(Library) {}

void run(const MatchResult &Result) override; // callback function that runs when a Match is found.
private:
clang::Rewriter *ASTRewriter; // an instance to a Rewriter to manage changes to the AST.
std::string _ApiName;
std::string _TypeDef;
std::string _Library;

};
clang::SourceRange findInjectionSpot(clang::ASTContext *const Context, clang::ast_type_traits::DynTypedNode Parent,
const clang::CallExpr &Literal, uint64_t Iterations);

bool
addGetProcAddress(const clang::CallExpr *pCallExpression, clang::ASTContext *const pContext,
const std::string &NewIdentifier, std::string &ApiName);

std::string getFunctionIdentifier(const clang::CallExpr *CallExpression);

bool replaceIdentifier(const clang::CallExpr *CallExpression, const std::string &ApiName,
const std::string &NewIdentifier);

bool handleCallExpr(const clang::CallExpr *CallExpression, clang::ASTContext *const pContext);


};

static std::map<std::string, std::string> ApiToHide_samlib = {
{"SamConnect", "typedef NTSTATUS (__stdcall* _SamEnumerateDomainsInSamServer)(SAMPR_HANDLE ServerHandle, DWORD * EnumerationContext, PSAMPR_RID_ENUMERATION* Buffer, DWORD PreferedMaximumLength,DWORD * CountReturned);"},
{"SamConnectWithCreds", "typedef NTSTATUS(__stdcall* _SamConnect)(PUNICODE_STRING ServerName, SAMPR_HANDLE * ServerHandle, ACCESS_MASK DesiredAccess, BOOLEAN Trusted);"},
{"SamEnumerateDomainsInSamServer", "typedef NTSTATUS(__stdcall* _SamConnectWithCreds)(PUNICODE_STRING ServerName, SAMPR_HANDLE * ServerHandle, ACCESS_MASK DesiredAccess, LSA_OBJECT_ATTRIBUTES * ObjectAttributes, RPC_AUTH_IDENTITY_HANDLE AuthIdentity, PWSTR ServerPrincName, ULONG * unk0);"},
{"SamLookupDomainInSamServer", "typedef NTSTATUS(__stdcall* _SamLookupDomainInSamServer)(SAMPR_HANDLE ServerHandle, PUNICODE_STRING Name, PSID * DomainId);"},
{"SamOpenDomain", "typedef NTSTATUS(__stdcall* _SamOpenDomain)(SAMPR_HANDLE SamHandle, ACCESS_MASK DesiredAccess, PSID DomainId, SAMPR_HANDLE * DomainHandle);"},
{"SamOpenUser", "typedef NTSTATUS(__stdcall* _SamOpenUser)(SAMPR_HANDLE DomainHandle, ACCESS_MASK DesiredAccess, DWORD UserId, SAMPR_HANDLE * UserHandle);"},
{"SamOpenGroup", "typedef NTSTATUS(__stdcall* _SamOpenGroup)(SAMPR_HANDLE DomainHandle, ACCESS_MASK DesiredAccess, DWORD GroupId, SAMPR_HANDLE * GroupHandle);"},
{"SamOpenAlias", "typedef NTSTATUS(__stdcall* _SamOpenAlias)(SAMPR_HANDLE DomainHandle, ACCESS_MASK DesiredAccess, DWORD AliasId, SAMPR_HANDLE * AliasHandle);"},
{"SamQueryInformationUser", "typedef NTSTATUS(__stdcall* _SamQueryInformationUser)(SAMPR_HANDLE UserHandle, USER_INFORMATION_CLASS UserInformationClass, PSAMPR_USER_INFO_BUFFER* Buffer);"},
{"SamSetInformationUser", "typedef NTSTATUS(__stdcall* _SamSetInformationUser)(SAMPR_HANDLE UserHandle, USER_INFORMATION_CLASS UserInformationClass, PSAMPR_USER_INFO_BUFFER Buffer);"},
{"SamiChangePasswordUser", "typedef NTSTATUS(__stdcall* _SamiChangePasswordUser)(SAMPR_HANDLE UserHandle, BOOL isOldLM, const BYTE oldLM[LM_NTLM_HASH_LENGTH], const BYTE newLM[LM_NTLM_HASH_LENGTH], BOOL isNewNTLM, const BYTE oldNTLM[LM_NTLM_HASH_LENGTH], const BYTE newNTLM[LM_NTLM_HASH_LENGTH]);"},
{"SamGetGroupsForUser", "typedef NTSTATUS(__stdcall* _SamGetGroupsForUser)(SAMPR_HANDLE UserHandle, PGROUP_MEMBERSHIP * Groups, DWORD * CountReturned);"},
{"SamGetAliasMembership", "typedef NTSTATUS(__stdcall* _SamGetAliasMembership)(SAMPR_HANDLE DomainHandle, DWORD Count, PSID * Sid, DWORD * CountReturned, PDWORD * RelativeIds);"},
{"SamGetMembersInGroup", "typedef NTSTATUS(__stdcall* _SamGetMembersInGroup)(SAMPR_HANDLE GroupHandle, PDWORD *Members, PDWORD *Attributes, DWORD * CountReturned);"},
{"SamGetMembersInAlias", "typedef NTSTATUS(__stdcall* _SamGetMembersInAlias)(SAMPR_HANDLE AliasHandle, PSID ** Members, DWORD * CountReturned);"},
{"SamEnumerateUsersInDomain", "typedef NTSTATUS(__stdcall* _SamEnumerateUsersInDomain)(SAMPR_HANDLE DomainHandle, PDWORD EnumerationContext, DWORD UserAccountControl, PSAMPR_RID_ENUMERATION* Buffer, DWORD PreferedMaximumLength, PDWORD CountReturned);"},
{"SamEnumerateGroupsInDomain", "typedef NTSTATUS(__stdcall* _SamEnumerateGroupsInDomain)(SAMPR_HANDLE DomainHandle, PDWORD EnumerationContext, PSAMPR_RID_ENUMERATION * Buffer, DWORD PreferedMaximumLength, PDWORD CountReturned);"},
{"SamEnumerateAliasesInDomain", "typedef NTSTATUS(__stdcall* _SamEnumerateAliasesInDomain)(SAMPR_HANDLE DomainHandle, PDWORD EnumerationContext, PSAMPR_RID_ENUMERATION * Buffer, DWORD PreferedMaximumLength, PDWORD CountReturned);"},
{"SamLookupNamesInDomain", "typedef NTSTATUS(__stdcall* _SamLookupNamesInDomain)(SAMPR_HANDLE DomainHandle, DWORD Count, PUNICODE_STRING Names, PDWORD * RelativeIds, PDWORD * Use);"},
{"SamLookupIdsInDomain", "typedef NTSTATUS(__stdcall* _SamLookupIdsInDomain)(SAMPR_HANDLE DomainHandle, DWORD Count, PDWORD RelativeIds, PUNICODE_STRING * Names, PDWORD * Use);"},
{"SamRidToSid", "typedef NTSTATUS(__stdcall* _SamRidToSid)(SAMPR_HANDLE ObjectHandle, DWORD Rid, PSID * Sid);"},
{"SamCloseHandle", "typedef NTSTATUS(__stdcall* _SamCloseHandle)(SAMPR_HANDLE SamHandle);"},
{"SamFreeMemory", "typedef NTSTATUS(__stdcall* _SamFreeMemory)(PVOID Buffer);"}
};
#endif //AVCLEANER_APIMATCHHANDLER_H
1 change: 1 addition & 0 deletions Globals.cpp
Expand Up @@ -2,4 +2,5 @@

namespace Globs {
std::vector<clang::SourceRange> PatchedSourceLocation;
const uint64_t CLIMB_PARENTS_MAX_ITER = 1000; // fail safe to prevent a recursion loop when climbing the list of parents.
}
2 changes: 1 addition & 1 deletion Globals.h
Expand Up @@ -8,5 +8,5 @@
namespace Globs {

extern std::vector<clang::SourceRange> PatchedSourceLocation;

extern const uint64_t CLIMB_PARENTS_MAX_ITER; // fail safe to prevent a recursion loop when climbing the list of parents.
}
88 changes: 6 additions & 82 deletions MatchHandler.cpp
Expand Up @@ -12,6 +12,7 @@
#include "clang/Tooling/Inclusions/HeaderIncludes.h"
#include "clang/Tooling/Inclusions/IncludeStyle.h"
#include "Globals.h"
#include "Utils.h"

using namespace clang;

Expand All @@ -24,7 +25,7 @@ MatchHandler::getNodeParents(const StringLiteral &NodeString, clang::ast_type_tr
clang::ASTContext *const Context, std::vector<std::string> &CurrentParents,
uint64_t Iterations) {

if (Iterations > CLIMB_PARENTS_MAX_ITER) {
if (Iterations > Globs::CLIMB_PARENTS_MAX_ITER) {
return CurrentParents;
}

Expand Down Expand Up @@ -134,16 +135,14 @@ bool MatchHandler::handleExpr(const clang::StringLiteral *pLiteral, clang::ASTCo
if(shouldAbort(pLiteral, pContext, LiteralRange))
return false;

std::string Replacement = translateStringToIdentifier(pLiteral->getBytes().str());
std::string Replacement = Utils::translateStringToIdentifier(pLiteral->getBytes().str());

if(!insertVariableDeclaration(pLiteral, pContext, LiteralRange, Replacement))
return false ;

bool res = replaceStringLiteral(pLiteral, pContext, LiteralRange, Replacement);

Globs::PatchedSourceLocation.push_back(LiteralRange);

return res;
return replaceStringLiteral(pLiteral, pContext, LiteralRange, Replacement);
}

void MatchHandler::handleCallExpr(const clang::StringLiteral *pLiteral, clang::ASTContext *const pContext,
Expand Down Expand Up @@ -176,7 +175,7 @@ bool MatchHandler::insertVariableDeclaration(const clang::StringLiteral *pLitera
// inject code to declare the string in an encrypted fashion
SourceRange FreeSpace = findInjectionSpot(pContext, clang::ast_type_traits::DynTypedNode(), *pLiteral,
IsInGlobalContext, 0);
std::string StringVariableDeclaration = generateVariableDeclaration(Replacement, StringLiteralContent);
std::string StringVariableDeclaration = Utils::generateVariableDeclaration(Replacement, StringLiteralContent);

if (!IsInGlobalContext) {
//StringVariableDeclaration += "\tdprintf(\"" + Replacement + "\");\n";
Expand Down Expand Up @@ -217,47 +216,11 @@ bool MatchHandler::replaceStringLiteral(const clang::StringLiteral *pLiteral, cl
return ASTRewriter->ReplaceText(LiteralRange, Replacement);
}

void MatchHandler::cleanParameter(std::string &Argument) {

auto Index = Argument.find_first_of('\"');

Argument.erase(Argument.begin(), Argument.begin() + Index + 1);

if (Argument.back() == '\"') {
Argument.pop_back();
}
}

std::string MatchHandler::translateStringToIdentifier(const std::string &StrLiteral) {

std::string NewIdentifier = std::regex_replace(StrLiteral, std::regex("[^A-Za-z]"), "_");
return "hid_" + NewIdentifier.substr(0, 6) + '_' + randomString(12);
}

std::string MatchHandler::randomString(std::string::size_type Length) {

static auto &chrs = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";

thread_local static std::mt19937 rg{std::random_device{}()};
thread_local static std::uniform_int_distribution<std::string::size_type> pick(0, sizeof(chrs) - 2);

std::string s;

s.reserve(Length);

while (Length--)
s += chrs[pick(rg)];

return s;
}

SourceRange
MatchHandler::findInjectionSpot(clang::ASTContext *const Context, clang::ast_type_traits::DynTypedNode Parent,
const clang::StringLiteral &Literal, bool IsGlobal, uint64_t Iterations) {

if (Iterations > CLIMB_PARENTS_MAX_ITER)
if (Iterations > Globs::CLIMB_PARENTS_MAX_ITER)
throw std::runtime_error("Reached max iterations when trying to find a function declaration");

ASTContext::DynTypedNodeList parents = Context->getParents(Literal);;
Expand Down Expand Up @@ -287,45 +250,6 @@ MatchHandler::findInjectionSpot(clang::ASTContext *const Context, clang::ast_typ
}
}

std::string
MatchHandler::generateVariableDeclaration(const std::string &StringIdentifier, std::string &StringValue) {

std::stringstream Result;

//Result << "\n#ifdef _UNICODE\n\twchar_t\n";
//Result << "#else\n\tchar\n#endif\n\t";
Result << "TCHAR " << StringIdentifier << "[] = {";

auto CleanString = std::string(StringValue);
cleanParameter(CleanString);
bool ToggleZero = std::count(CleanString.begin(), CleanString.end(), 0) >= CleanString.size() / 2;
for (std::string::iterator it = CleanString.begin(); it != CleanString.end(); it++) {

if (*it == '\'') {
Result << "'\\" << *it << "'";
} else if (*it == '\\') {
Result << "'\\\\'";
} else if (*it == '\n') {
Result << "'\\n'";
} else if (*it != 0) {
Result << "'" << *it << "'";
} else {
continue;
}

uint32_t offset = 1;
if (it + offset != CleanString.end()) {
Result << ",";
}
}

if(*Result.str().end() == ',')
Result << "0};\n";
else
Result << ",0};\n";
return std::regex_replace(Result.str(), std::regex(",,"), ",");
}

bool MatchHandler::isBlacklistedFunction(const CallExpr *FunctionCall) {

const FunctionDecl *FnDeclaration = FunctionCall->getDirectCallee();
Expand Down
34 changes: 1 addition & 33 deletions MatchHandler.h
Expand Up @@ -45,32 +45,6 @@ class MatchHandler : public clang::ast_matchers::MatchFinder::MatchCallback {

void run(const MatchResult &Result) override; // callback function that runs when a Match is found.

/**
* strip metacharacters decorating a string.
* For instance, L"ntdll" -> ntdll
* @param Argument the string to be cleaned.
*/
static void cleanParameter(std::string &Argument);

/**
* used to replace a string literal by a variable's identifier
* must not collide with existing identifiers.
* Format: 12-first characters, letters only + random part
* TODO: remember allocated identifiers for collision prevention
* TODO: extract constants into readable identifiers.
* @param StrLiteral the string literal
*/
static std::string translateStringToIdentifier(const std::string &StrLiteral);

/**
* @brief declares and instantiate a variable holding a string that was moved out from a function's arguments.
* @param StringIdentifier the new variable identifier.
* @param StringValue the actual value of the string literal.
* @return the generated code snippet.
*/
static std::string
generateVariableDeclaration(const std::string &StringIdentifier, std::string &StringValue);

private:
clang::Rewriter *ASTRewriter; // an instance to a Rewriter to manage changes to the AST.

Expand Down Expand Up @@ -109,12 +83,6 @@ generateVariableDeclaration(const std::string &StringIdentifier, std::string &St
void handleCallExpr(const clang::StringLiteral *pLiteral, clang::ASTContext *pContext,
clang::ast_type_traits::DynTypedNode node);

/**
* @brief generates a random string of size Length.
* @param Length desired length of the generated string.
* @return a random string of size Length.
*/
static std::string randomString(unsigned long Length);

/**
* @brief Finds some free space to inject code that must run before the string literals usages.
Expand All @@ -128,7 +96,7 @@ generateVariableDeclaration(const std::string &StringIdentifier, std::string &St
findInjectionSpot(clang::ASTContext *Context, clang::ast_type_traits::DynTypedNode Parent,
const clang::StringLiteral &Literal, bool IsGlobal, uint64_t Iterations);

const static uint64_t CLIMB_PARENTS_MAX_ITER = 1000; // fail safe to prevent a recursion loop when climbing the list of parents.


/**
* offers a chance to bail out from the refactoring process if the string literal is found in an unpatchable location.
Expand Down

0 comments on commit 8e53481

Please sign in to comment.