Skip to content

Commit

Permalink
Support strings originating from ConstantExpr expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
antoniofrighetto committed Nov 21, 2023
1 parent 071c2f0 commit 013943f
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 9 deletions.
75 changes: 67 additions & 8 deletions src/passes/strings-encoding/StringEncoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
#include <llvm/ADT/StringExtras.h>
#include <llvm/Bitcode/BitcodeWriter.h>
#include <llvm/Demangle/Demangle.h>
#include <llvm/IR/Constant.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Instruction.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/NoFolder.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Support/Error.h>
Expand Down Expand Up @@ -88,6 +91,22 @@ inline bool isSkip(const StringEncodingOpt& encInfo) {
return std::get_if<StringEncOptSkip>(&encInfo) != nullptr;
}

GlobalVariable *extractGlobalVariable(ConstantExpr *Expr) {
while (Expr) {
if (Expr->getOpcode() == llvm::Instruction::IntToPtr ||
llvm::Instruction::isBinaryOp(Expr->getOpcode())) {
Expr = dyn_cast<ConstantExpr>(Expr->getOperand(0));
} else if (Expr->getOpcode() == llvm::Instruction::PtrToInt ||
Expr->getOpcode() == llvm::Instruction::GetElementPtr) {
return dyn_cast<GlobalVariable>(Expr->getOperand(0));
} else {
break;
}
}

return nullptr;
}

bool StringEncoding::runOnBasicBlock(Module& M, Function& F, BasicBlock& BB,
ObfuscationConfig& userConfig)
{
Expand All @@ -103,10 +122,14 @@ bool StringEncoding::runOnBasicBlock(Module& M, Function& F, BasicBlock& BB,
for (Use& Op : I.operands()) {
GlobalVariable *G = dyn_cast<GlobalVariable>(Op->stripPointerCasts());

if (G == nullptr) {
continue;
if (!G) {
if (auto *CE = dyn_cast<ConstantExpr>(Op))
G = extractGlobalVariable(CE);
}

if (!G)
continue;

if (!isEligible(*G)) {
continue;
}
Expand Down Expand Up @@ -269,6 +292,24 @@ bool StringEncoding::injectOnStack(BasicBlock& BB, Instruction& I, Use& Op, Glob
return true;
}

std::pair<Instruction *, Instruction *>
materializeConstantExpression(Instruction *Point, ConstantExpr *CE) {
auto *Inst = CE->getAsInstruction();
auto *Prev = Inst;
Inst->insertBefore(Point);

Value *Expr = Inst->getOperand(0);
while (isa<ConstantExpr>(Expr)) {
auto *NewInst = cast<ConstantExpr>(Expr)->getAsInstruction();
NewInst->insertBefore(Prev);
Prev->setOperand(0, NewInst);
Expr = NewInst->getOperand(0);
Prev = NewInst;
}

return {Inst, Prev};
}

bool StringEncoding::injectOnStackLoop(BasicBlock& BB, Instruction& I, Use& Op, GlobalVariable& G,
ConstantDataSequential& data, const StringEncoding::EncodingInfo& info)
{
Expand All @@ -285,8 +326,9 @@ bool StringEncoding::injectOnStackLoop(BasicBlock& BB, Instruction& I, Use& Op,
IRBuilder<NoFolder> IRB(&BB);
IRB.SetInsertPoint(&I);

AllocaInst* clearBuffer = IRB.CreateAlloca(IRB.getInt8Ty(),
IRB.getInt32(str.size()));
AllocaInst *ClearBuffer =
IRB.CreateAlloca(ArrayType::get(IRB.getInt8Ty(), str.size()));
auto *CastClearBuffer = IRB.CreateBitCast(ClearBuffer, IRB.getInt8PtrTy());

AllocaInst* Key = IRB.CreateAlloca(IRB.getInt64Ty());
AllocaInst* StrSize = IRB.CreateAlloca(IRB.getInt32Ty());
Expand All @@ -311,6 +353,14 @@ bool StringEncoding::injectOnStackLoop(BasicBlock& BB, Instruction& I, Use& Op,
fatalError("Can't find the 'decode' routine");
}

// TODO: support ObjC strings as well
Value *CastEncPtr = nullptr;
if (auto *CE = dyn_cast<ConstantExpr>(EncPtr)) {
assert(extractGlobalVariable(CE) == &G &&
"Previously extracted global variable need to match");
CastEncPtr = IRB.CreateBitCast(&G, IRB.getInt8PtrTy());
}

auto NewF = Function::Create(FDecode->getFunctionType(), llvm::GlobalValue::PrivateLinkage,
"__omvll_decode", BB.getModule());

Expand All @@ -323,9 +373,8 @@ bool StringEncoding::injectOnStackLoop(BasicBlock& BB, Instruction& I, Use& Op,
SmallVector<ReturnInst*, 8> Returns;
CloneFunctionInto(NewF, FDecode, VMap, CloneFunctionChangeType::DifferentModule, Returns);

std::vector<Value*> Args = {
clearBuffer, EncPtr, KeyVal, VStrSize
};
std::vector<Value *> Args = {
CastClearBuffer, CastEncPtr ? CastEncPtr : EncPtr, KeyVal, VStrSize};

if (NewF->arg_size() != 4) {
fatalError(fmt::format("Expecting 4 arguments ({} provided)", NewF->arg_size()));
Expand All @@ -344,7 +393,17 @@ bool StringEncoding::injectOnStackLoop(BasicBlock& BB, Instruction& I, Use& Op,
CallInst* callee = IRB.CreateCall(NewF->getFunctionType(), NewF, Args);
inline_wlist_.push_back(callee);

I.setOperand(Op.getOperandNo(), clearBuffer);
if (auto *CE = dyn_cast<ConstantExpr>(EncPtr)) {
auto [First, Last] = materializeConstantExpression(&I, CE);
assert(((First != Last) ||
(isa<GetElementPtrInst>(First) || isa<PtrToIntInst>(First))) &&
"Nested constantexpr in getelementptr/ptrtoint should not appear?");
Last->setOperand(0, ClearBuffer);
I.setOperand(Op.getOperandNo(), First);
} else {
I.setOperand(Op.getOperandNo(), ClearBuffer);
}

return true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from lit.llvm import llvm_config

config.name = "O-MVLL Tests"
config.suffixes = ['.c', '.cpp']
config.suffixes = ['.c', '.cpp', '.ll']
config.test_format = lit.formats.ShTest(True)
config.test_source_root = os.path.dirname(__file__)

Expand Down
18 changes: 18 additions & 0 deletions src/test/passes/strings-encoding/basic-ios-swift.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
; REQUIRES: aarch64-registered-target

; The 'replace' configuration encodes the string and adds logic that decodes it at load-time:
; RUN: env OMVLL_CONFIG=%S/config_replace.py clang++ -fpass-plugin=%libOMVLL \
; RUN: -target arm64-apple-ios -fno-legacy-pass-manager -O1 -c %s -o - | strings | FileCheck %s
;
; CHECK-NOT: {{.*Hello, Swift.*}}

%swift.bridge = type opaque

declare swiftcc { i64, %swift.bridge*, i1, %swift.bridge* } @"foo"(i64 %0, %swift.bridge* %1)

@.str.4 = private constant [13 x i8] c"Hello, Swift\00"

define { i64, %swift.bridge*, i1, %swift.bridge* } @test_function() {
%1 = call swiftcc { i64, %swift.bridge*, i1, %swift.bridge* } @"foo"(i64 -3458764513820540911, %swift.bridge* inttoptr (i64 or (i64 sub (i64 ptrtoint ([13 x i8]* @.str.4 to i64), i64 32), i64 -9223372036854775808) to %swift.bridge*))
ret { i64, %swift.bridge*, i1, %swift.bridge* } %1
}
2 changes: 2 additions & 0 deletions src/test/passes/strings-encoding/config_replace.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ def __init__(self):
def obfuscate_string(self, _, __, string: bytes):
if string.endswith(b".cpp"):
return omvll.StringEncOptGlobal()
if string.endswith(b"Swift"):
return omvll.StringEncOptStack()

@lru_cache(maxsize=1)
def omvll_get_config() -> omvll.ObfuscationConfig:
Expand Down

0 comments on commit 013943f

Please sign in to comment.