本文章分析AntiClassDump的实现细节,以下源码参考来自https://github.com/61bcdefg/Hikari-LLVM15,感谢Hikari原作者以及更多贡献者的付出。
一
前置知识
1. 类方法、实例方法
-
开头的方法是实例方法。它属于类的某一个或某几个实例对象,类对象必须实例化后才可以使用。+
开头的方法是类方法。Objc中的类方法类似Java中的static方法,它是属于类本身的方法,不需要实例化类,用类名即可使用。struct __objc_data
、struct __objc_method
、struct __objc_method_list
这几个结构体,可以看到:-
)对应_OBJC_CLASS_$_XXX
中的data域里的 method_list;+
)对应_OBJC_METACLASS_$_XXX
中的data域里的 method_list。2. +initialize 和 +load 方法
3. Objective-C 的 Category
MyAddition
,向类MyClass
添加了函数printNameAddition
:@interface MyClass : NSObject
- (void)printName;
@end
@interface MyClass(MyAddition)
- (void)printNameAddition;
@end
@" 1l_OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition" = ...
@" 1l_OBJC_$_CATEGORY_MyClass_$_MyAddition" =
private global %struct._category_t { ... }, section "__DATA, __objc_const", align 8
...
@"OBJC_LABEL_CATEGORY_$" = private global ...
@" 1l_OBJC_$_CATEGORY_MyClass_$_MyAddition"
是一个结构体struct _category_t
,其定义如下:struct _category_t {
const char * const name; // CategoryName
struct _class_t *const cls; // the class to be extended
const struct _method_list_t * const instance_methods;
const struct _method_list_t * const class_methods;
const struct _protocol_list_t * const protocols;
const struct _prop_list_t * const properties;
const struct _prop_list_t * const class_properties;
const uint32_t size;
}
MyAddition
这一名称,而函数printNameAddition()
已经在MyClass
的方法列表中。Category的方法被放到了方法列表的前面。因此由于查找方法时是顺序查找的,category的方法会“覆盖”掉原来类的同名方法。二
实现原理
◆对类的方法进行重命名:可以通过替换类的方法实现,给方法添加新的名字。这样反编译工具在分析代码时会遇到重命名的方法,增加了阅读和理解代码的困难度。
◆修改类的方法实现:可以向方法的实现中插入一些无用的代码片段,或者进行代码混淆,使得反编译工具难以还原出原始的代码逻辑。
◆加入代码验证逻辑:可以向方法的实现中插入一些验证逻辑,例如检查函数参数、返回值等,对代码进行验证,防止反编译工具逆向分析。
◆使用编译器提供的安全特性:例如Apple的ptrauth和Opaque Pointers,可以使得函数指针和类指针更难以被破解和篡改。
三
代码分析
0. doInitialization
Module
)的目标三元组信息,并存储在变量triple
中,这通常包括了架构、厂商和操作系统信息。然后,代码检查了这个三元组是否代表苹果公司的架构,因为此Pass专门用于处理苹果的Objective-C实现。triple = Triple(M.getTargetTriple());
if (triple.getVendor() != Triple::VendorType::Apple) {
// 只支持苹果的ObjC实现,对于GNU Step等其它实现给出警告
// ...
return false;
}
int8
类型指针的LLVM类型(Int8PtrTy
),这个类型在Objective-C运行时函数声明中会用到。Type *Int8PtrTy = Type::getInt8PtrTy(M.getContext());
// 添加ObjC运行时函数的声明
FunctionType *IMPType =
FunctionType::get(Int8PtrTy, {Int8PtrTy, Int8PtrTy}, true);
PointerType *IMPPointerType = PointerType::getUnqual(IMPType);
FunctionType *class_replaceMethod_type = FunctionType::get(
IMPPointerType, {Int8PtrTy, Int8PtrTy, IMPPointerType, Int8PtrTy},
false);
M.getOrInsertFunction("class_replaceMethod", class_replaceMethod_type);
FunctionType *sel_registerName_type =
FunctionType::get(Int8PtrTy, {Int8PtrTy}, false);
M.getOrInsertFunction("sel_registerName", sel_registerName_type);
// ...
appleptrauth
)和LLVM上下文是否支持不透明指针(opaquepointers
)。通过这两个布尔变量将决定后续Pass的行为,尤其是在处理指针和类型时。// 判断是否支持ApplePtrauth和Opaque Pointers
appleptrauth = hasApplePtrauth(&M);
opaquepointers = !M.getContext().supportsTypedPointers();
true
,表示初始化成功。如果检测到不支持苹果的架构,则输出错误信息并返回false
。doInitialization
函数负责为一个LLVM Pass做准备工作,包括确认目标平台、声明所需的运行时函数,并检查相关特性支持,以便Pass在后续的操作中可以正确地处理Objective-C代码。1. runOnModule
OBJC_LABEL_CLASS_$
:OBJC_LABEL_CLASS_$
的全局变量,它包含Objective-C类的信息。GlobalVariable *OLCGV = M.getGlobalVariable("OBJC_LABEL_CLASS_$", true);
false
。if (!OLCGV) {
errs() << "No ObjC Class Found in :" << M.getSourceFileName() << "n";
// 没有找到ObjC类,Pass不进行处理
return false;
}
OLCGV
是否有初始化器,并将其转换为常量数组OBJC_LABEL_CLASS_CDS
。assert(OLCGV->hasInitializer() &&
"OBJC_LABEL_CLASS_$ Doesn't Have Initializer.");
// 获取OBJC_LABEL_CLASS_$的初始化值,即包含所有类信息的常量数组
ConstantArray *OBJC_LABEL_CLASS_CDS =
dyn_cast<ConstantArray>(OLCGV->getInitializer());
readyclses
存储可处理的类,tmpclses
临时存储有依赖关系的类,dependency
存储类与父类的依赖关系,GVMapping
将类名映射到相应的全局变量。std::vector<std::string> readyclses; // 存储可以处理的类
std::deque<std::string> tmpclses; // 临时存储类的队列,用于处理依赖关系
std::map<std::string /*class*/, std::string /*super class*/> dependency; // 存储类与其父类的对应关系
std::map<std::string /*Class*/, GlobalVariable *> GVMapping; // 存储类名与对应的全局变量的映射
OBJC_LABEL_CLASS_CDS
数组,提取每个类的名称和父类信息,然后根据是否有父类及父类是否已经初始化来决定将类名放入哪个容器。for (...) {
...
if (supclsName == "" || (SuperClassGV && !SuperClassGV->hasInitializer())) {
readyclses.emplace_back(clsName);// 父类为空或者父类未初始化,说明该类是可以处理的
} else {
tmpclses.emplace_back(clsName);// 父类不为空,暂时无法处理,加入临时队列
}
}
dependency
中的依赖关系对类进行排序,以确保在处理子类之前,其父类已经处理。// 根据依赖关系排序,保证父类在子类之前处理
while (tmpclses.size()) {
std::string clstmp = tmpclses.front();
tmpclses.pop_front();
std::string SuperClassName = dependency[clstmp];
if (SuperClassName != "" &&
std::find(readyclses.begin(), readyclses.end(), SuperClassName) ==
readyclses.end()) {
// 父类还未处理,将该类重新加入临时队列
tmpclses.emplace_back(clstmp);
} else {
// 父类已经处理,将该类加入可以处理的队列
readyclses.emplace_back(clstmp);
}
}
readyclses
中的每个类,调用handleClass
函数进行进一步处理。for (std::string className : readyclses) {
handleClass(GVMapping[className], &M);
}
true
,表示模块处理完成。runOnModule
函数的目的是处理Objective-C模块中所有的类。它首先会找到一个包含类定义的全局变量,然后遍历该变量中的所有类,并根据每个类是否有父类和父类是否已初始化来建立处理顺序。最后,函数会按照确定的顺序处理每个类。2. handleClass
GlobalVariable
参数GV
(代表一个Objective-C类)有一个初始化器。如果没有,断言将失败。// 判断类的全局变量是否有初始值,如果没有则直接返回
assert(GV->hasInitializer() &&
"ObjC Class Structure's Initializer Missing");
GV
的初始化值,该值为一个ConstantStruct
类型的常量。接着提取类名和父类名,这些信息用于输出日志,以及后续处理。ConstantStruct *CS = dyn_cast<ConstantStruct>(GV->getInitializer());
StringRef ClassName = GV->getName();
ClassName = ClassName.substr(strlen("OBJC_CLASS_$_"));
StringRef SuperClassName =
readPtrauth(
cast<GlobalVariable>(CS->getOperand(1)->stripPointerCasts()))
->getName();
SuperClassName = SuperClassName.substr(strlen("OBJC_CLASS_$_"));
errs() << "Handling Class:" << ClassName
<< " With SuperClass:" << SuperClassName << "n";
ConstantStruct
中提取出元类(metaclassGV
)和只读类信息(class_ro
),这些可能包含方法列表等重要数据。GlobalVariable *metaclassGV = readPtrauth(
cast<GlobalVariable>(CS->getOperand(0)->stripPointerCasts()));
GlobalVariable *class_ro = readPtrauth(
cast<GlobalVariable>(CS->getOperand(4)->stripPointerCasts()));
assert(metaclassGV->hasInitializer() && "MetaClass GV Initializer Missing");
GlobalVariable *metaclass_ro = readPtrauth(cast<GlobalVariable>(
metaclassGV->getInitializer()
->getOperand(metaclassGV->getInitializer()->getNumOperands() - 1)
->stripPointerCasts()));
如果找到了元类中的方法列表,遍历列表并提取方法的名称、类型和实现。
如果方法的名称是initialize或load,这意味着找到了现有的初始化器方法,将记录该方法的入口基本块(EntryBB)。
if (Info.find("METHODLIST") != Info.end()) {
ConstantArray *method_list = cast<ConstantArray>(Info["METHODLIST"]);
for (unsigned i = 0; i < method_list->getNumOperands(); i++) {
ConstantStruct *methodStruct =
cast<ConstantStruct>(method_list->getOperand(i));
// 从方法结构体中提取方法的名称、类型和实现
GlobalVariable *SELNameGV = cast<GlobalVariable>(
opaquepointers ? methodStruct->getOperand(0)
: methodStruct->getOperand(0)->getOperand(0));
ConstantDataSequential *SELNameCDS =
cast<ConstantDataSequential>(SELNameGV->getInitializer());
StringRef selname = SELNameCDS->getAsCString();
if ((selname == "initialize" && UseInitialize) ||
(selname == "load" && !UseInitialize)) {
// 如果找到了现有的初始化方法(initialize或load),则记录EntryBB为该方法的入口基本块
Function *IMPFunc = cast<Function>(readPtrauth(cast<GlobalVariable>(
methodStruct->getOperand(2)->stripPointerCasts())));
errs() << "Found Existing initializern";
EntryBB = &(IMPFunc->getEntryBlock());
}
}
} else {
errs() << "Didn't Find ClassMethod Listn";
}
EntryBB
。if (!EntryBB) {
FunctionType *InitializerType = FunctionType::get(
Type::getVoidTy(M->getContext()), ArrayRef<Type *>(), false);
Function *Initializer = Function::Create(
InitializerType, GlobalValue::LinkageTypes::PrivateLinkage,
"AntiClassDumpInitializer", M);
EntryBB = BasicBlock::Create(M->getContext(), "", Initializer);
ReturnInst::Create(M->getContext(), EntryBB);
}
EntryBB
初始化IRBuilder
,用于构建LLVM中间表示(IR)指令。IRBuilder<> *IRB = new IRBuilder<>(EntryBB, EntryBB->getFirstInsertionPt());
objc_getClass
函数的声明,它将在后续的处理中使用。// We now prepare ObjC API Definitions
Function *objc_getClass = M->getFunction("objc_getClass");
// End of ObjC API Definitions
Value *ClassNameGV = IRB->CreateGlobalStringPtr(ClassName);
// Now Scan For Props and Ivars in OBJC_CLASS_RO AND OBJC_METACLASS_RO
// Note that class_ro_t's structure is different for 32 and 64bit runtime
CallInst *Class = IRB->CreateCall(objc_getClass, {ClassNameGV});
对于元类和类的方法列表,调用HandleMethods函数添加和处理方法。
如果方法列表不为空,处理实例方法(对于元类)和类方法(对于类本身)。
// Add Methods
ConstantStruct *metaclassCS =
cast<ConstantStruct>(class_ro->getInitializer());
ConstantStruct *classCS =
cast<ConstantStruct>(metaclass_ro->getInitializer());
if (!metaclassCS->getAggregateElement(5)->isNullValue()) {
errs() << "Handling Instance Methods For Class:" << ClassName << "n";
HandleMethods(metaclassCS, IRB, M, Class, false);
// ...
}
// 继续处理实例方法和类方法
// 这里只处理类方法,实例方法的处理类似
GlobalVariable *methodListGV = nullptr;// 存储类方法列表的全局变量
if (!classCS->getAggregateElement(5)->isNullValue()) {
errs() << "Handling Class Methods For Class:" << ClassName << "n";
HandleMethods(classCS, IRB, M, Class, true);
methodListGV = readPtrauth(cast<GlobalVariable>(
classCS->getAggregateElement(5)->stripPointerCasts()));
}
创建新的方法结构体以及方法列表。
//定义方法类型
Type *objc_method_type =
StructType::getTypeByName(M->getContext(), "struct._objc_method");
//创建新方法的结构体
Constant *newMethod = ConstantStruct::get(
cast<StructType>(objc_method_type),
ArrayRef<Constant *>(methodStructContents)); // objc_method_t
//创建新方法的列表
ArrayType *AT = ArrayType::get(objc_method_type, 1);
Constant *newMethodList = ConstantArray::get(
AT, ArrayRef<Constant *>(newMethod)); // Container of objc_method_t
//1. 为新方法列表创建结构体类型和实例
std::vector<Type *> newStructType;
std::vector<Constant *> newStructValue;
// ...(省略初始化newStructType和newStructValue的代码)
StructType *newType = StructType::get(M->getContext(), ArrayRef<Type *>(newStructType));
Constant *newMethodStruct = ConstantStruct::get(newType, ArrayRef<Constant *>(newStructValue)); // l_OBJC_$_CLASS_METHODS_
//2. 创建并注册新的全局变量:
GlobalVariable *newMethodStructGV = new GlobalVariable(
*M, newType, true, GlobalValue::LinkageTypes::PrivateLinkage,
newMethodStruct, "ACDNewClassMethodMap");
appendToCompilerUsed(*M, {newMethodStructGV});
//3. 替换旧的方法列表引用并删除旧的全局变量:
/**
ConstantExpr::getBitCast和replaceAllUsesWith负责将新的方法列表安全地转换成所需类型并用其替换旧的方法列表,完成对类结构的更新。
通过dropAllReferences和eraseFromParent从模块中移除旧的全局变量,确保不会有内存泄漏或其他问题。
*/
Constant *bitcastExpr = ConstantExpr::getBitCast(
newMethodStructGV, PointerType::getUnqual(StructType::getTypeByName(
M->getContext(), "struct.__method_list_t")));
opaquepointers ? classCS->setOperand(5, bitcastExpr)
: classCS->handleOperandChange(
classCS->getAggregateElement(5), bitcastExpr);
if (methodListGV) {
methodListGV->replaceAllUsesWith(ConstantExpr::getBitCast(
newMethodStructGV,
methodListGV->getType()));
methodListGV->dropAllReferences();
methodListGV->eraseFromParent();
}
bitcast
操作将新的方法结构体变量的地址转换为所需的类型,并更新类结构中的方法列表指针,替换旧的方法列表。3.HandleMethods
HandleMethods
函数的实现,并在每个步骤中突出关键代码。// 获取函数指针 `sel_registerName`、`class_replaceMethod`、`class_getName`、`objc_getMetaClass`
Function *sel_registerName = M->getFunction("sel_registerName");
Function *class_replaceMethod = M->getFunction("class_replaceMethod");
Function *class_getName = M->getFunction("class_getName");
Function *objc_getMetaClass = M->getFunction("objc_getMetaClass");
objc_method_list_t_type
StructType *objc_method_list_t_type =
StructType::getTypeByName(M->getContext(), "struct.__method_list_t");
class_ro
结构体的每个元素,寻找方法列表,并跳过空值元素。for (unsigned int i = 0; i < class_ro->getType()->getNumElements(); i++) {
Constant *tmp = dyn_cast<Constant>(class_ro->getAggregateElement(i));
if (tmp->isNullValue()) {
continue; // 跳过空值元素
}
// ...
}
Type *type = tmp->getType();
if ((type == PointerType::getUnqual(objc_method_list_t_type)) ||
(tmp->getName().startswith("_OBJC_$_INSTANCE_METHODS") ||
tmp->getName().startswith("_OBJC_$_CLASS_METHODS"))) {
// 处理方法列表
// ...
}
// 读取全局变量,获取方法列表的全局变量 `methodListGV`
GlobalVariable *methodListGV =
readPtrauth(cast<GlobalVariable>(tmp->stripPointerCasts()));
//检查方法列表的全局变量是否有初始化器,如果没有,则返回
assert(methodListGV->hasInitializer() &&
"MethodListGV doesn't have initializer");
// 获取方法列表结构体 `methodListStruct`
ConstantStruct *methodListStruct =
cast<ConstantStruct>(methodListGV->getInitializer());
methodStruct
包含了方法名、类型签名和方法的实现(IMP)。// 从方法列表结构体中提取 `methodList`
ConstantArray *methodList = cast<ConstantArray>(methodListStruct->getOperand(2));
// 遍历方法列表 `methodList` 的每个元素
for (unsigned int i = 0; i < methodList->getNumOperands(); i++) {
// 获取方法结构体 `methodStruct`
ConstantStruct *methodStruct = cast<ConstantStruct>(methodList->getOperand(i));
// ...
}
sel_registerName
注册其选择器(SEL)。这是Objective-C的消息发送机制中标识方法的关键。// 提取方法名称
Constant *SELName = IRB->CreateGlobalStringPtr(
cast<ConstantDataSequential>(
cast<GlobalVariable>(
opaquepointers
? methodStruct->getOperand(0)
: cast<ConstantExpr>(methodStruct->getOperand(0))
->getOperand(0))
->getInitializer())
->getAsCString());
// 注册SEL
CallInst *SEL = IRB->CreateCall(sel_registerName, {SELName});
// 提取方法实现
Value *BitCastedIMP = IRB->CreateBitCast(
appleptrauth
? opaquepointers
? cast<GlobalVariable>(methodStruct->getOperand(2))
->getInitializer()
->getOperand(0)
: cast<ConstantExpr>(
cast<GlobalVariable>(methodStruct->getOperand(2))
->getInitializer()
->getOperand(0))
->getOperand(0)
: methodStruct->getOperand(2),
IMPType);
class_replaceMethod
调用的参数class_getName
和objc_getMetaClass
或直接使用Class
参数,来准备调用class_replaceMethod
的参数列表。std::vector<Value *> replaceMethodArgs;
if (isMetaClass) {
// 如果是元类(Meta Class),则获取类名并调用objc_getMetaClass函数获取元类
CallInst *className = IRB->CreateCall(class_getName, {Class});
CallInst *MetaClass =
IRB->CreateCall(objc_getMetaClass, {className});
replaceMethodArgs.emplace_back(MetaClass); // Class
} else {
replaceMethodArgs.emplace_back(Class); // Class
}
replaceMethodArgs.emplace_back(SEL); // SEL
replaceMethodArgs.emplace_back(BitCastedIMP); // imp
methodStruct
提取方法类型,并将其转换为全局字符串指针。// 提取方法类型
replaceMethodArgs.emplace_back(IRB->CreateGlobalStringPtr(
cast<ConstantDataSequential>(
cast<GlobalVariable>(
opaquepointers
? methodStruct->getOperand(1)
: cast<ConstantExpr>(methodStruct->getOperand(1))
->getOperand(0))
->getInitializer())
->getAsCString())); // type
class_replaceMethod
替换方法实现class_replaceMethod
,实际替换或注册类中的方法实现。// 调用class_replaceMethod函数进行方法的替换和注册
IRB->CreateCall(class_replaceMethod,ArrayRef<Value *>(replaceMethodArgs));
RenameMethodIMP
选项,则将方法实现重命名为"ACDMethodIMP"
。if (RenameMethodIMP) {
// 如果需要重命名方法实现
Function *MethodIMP = cast<Function>(
appleptrauth
? opaquepointers
? cast<GlobalVariable>(methodStruct->getOperand(2))
->getInitializer()
->getOperand(0)
: cast<ConstantExpr>(
cast<GlobalVariable>(
methodStruct->getOperand(2)->getOperand(0))
->getInitializer()
->getOperand(0))
->getOperand(0)
: opaquepointers ? methodStruct->getOperand(2)
: methodStruct->getOperand(2)->getOperand(0));
MethodIMP->setName("ACDMethodIMP");
}
HandleMethods
函数中,分析了如何操作Objective-C的运行时以动态修改类的方法列表。显示了如何构建并使用IRBuilder来创建和调用LLVM IR指令,如何使用Objective-C运行时函数,以及如何处理Objective-C类结构体中的方法列表数据。四
总结
看雪ID:ElainaDaemon
https://bbs.kanxue.com/user-home-945395.htm
# 往期推荐
2、Glibc-2.35下对tls_dtor_list的利用详解
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):Hikari源码分析 – AntiClassDump