Sherlock과 비슷한 프로그램 만들어보기 – 0
[ 개발 기록용으로 남기는 글입니다 ]
iOS 앱 개발을 하다보면 디버깅이나 개발에 도움이 되는 툴들을 찾게 된다. 실제로 생산성도 엄청나게 늘어나고..
나같은 경우 여러가지 프로그램들을 다 사용해봤는데 너무 신기해보이는 툴이 있어서 한번 비슷하게 만들어보려 한다
![](https://byeon.is/wp-content/uploads/2023/06/image-1024x1021.png)
Sherlock이라는 툴인데.. 아무런 설정도 필요없이 시뮬레이터에 설치된 앱들을 분석하고 실시간으로 수정할 수 있는 프로그램이다. 원리가 너무 궁금해서 자료들을 다 뒤져보면서 언젠가 한번 직접 만들어봐야겠다라는 생각을 가졌었고 직접 만들어보기로 했다!(궁금한 건 못참는 사람)
일단 Sherlock을 키게 되면 Simulator가 재부팅되면서 실시간으로 수정할 수 있게되는데 어떤 원리인지는 파악해야하므로 현재 앱에 load된 dylib들을 살펴보았다.
![](https://byeon.is/wp-content/uploads/2023/06/Pasted-Graphic.png)
![](https://byeon.is/wp-content/uploads/2023/06/Pasted-Graphic-5-538x1024.png)
살펴보니 bundlePath에 io.inspiredcode.Sherlock을 갖고 있는 Watson이라는 dylib가 로드되어있었다!
추측으로는 Watson이라는 라이브러리가 macOS에 설치된 앱과 통신을 하는 것 같은데… bundle이 저장된 곳을 열어봤다.
![](https://byeon.is/wp-content/uploads/2023/06/Pasted-Graphic-6-1024x686.png)
총 3가지 파일이 저장되어 있는데 Watson말고 나머지는 뭐하는 얘들인지 잘 모르겠다… 일단 Watson을 어떻게 로드하는건지 알아봐야 할 것 같아서 시뮬레이터 관련 자료를 전부 뒤져보았다.
일단 dylib 파일을 로드하는 방법은 아래와 같이 있었다.
일단 첫번째는 내가 개발한 앱에 대해서만 dlopen 함수를 쓸 수 있을테니… 이건 패스!
두번째는 왠지 macOS/iOS 보안 전문가들이 쓸법한데… 셜록이 이런 방식으로 구현했을것 같진 않았다. (돈 받고 파는 프로그램인데 설마 언제든지 막힐 수 있는 이 방법을 사용했을까?)
세번째 방법은 구글링을 통해 겨우겨우 찾았던 방법이다. WWDC 2019에서 공개된 simctl 명령어를 통해 특정 프로세스에 본인이 만든 dylib를 주입할 수 있다.
블로그 글을 따라해보면 dylib를 생성해서 SpringBoard 프로세스에 내가 만든 dylib를 주입할 수 있는데… 근데 이 방법으로는 모든 프로세스에는 주입할 수 없었다!
그래서 다른 방법을 찾아야해서 탈옥 관련 글 iOS 보완 관련 글들을 전부 뒤적거렸다.
뒤적거리다가 어느 보안 글에서 RunningBoard가 언급되었는데 ‘앱을 직접 실행하려면 RunningBoard를 거쳐야한다는 글이였다. 보자마자 바로 요놈이다!!! 싶어서 iOS Framework Runtime Header를 찾아봤다.
![](https://byeon.is/wp-content/uploads/2023/06/image-1-1024x882.png)
iOS에서 모든 프로세스를 실행할 때 해당 메소드를 거치게 된다. 해당 메소드를 Swizzling하면 해당 프로세스에 내가 원하는 dylib도 주입할 수 있지 않을까?? 싶어서 본격적으로 삽을 들었다.
일단 RBProcessManager에 있는 메소드를 아래의 코드로 스위즐링할 수 있다. (스위즐링에 관해서는 다음에 블로그로 한번 설명하는 글을 작성해보는걸로…)
#pragma mark - RBProcessManager
@interface RBProcessManager (Swizzle)
@end
@implementation RBProcessManager (Swizzle)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(executeLaunchRequest:withError:);
SEL swizzledSelector = @selector(alt_executeLaunchRequest:withError:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
그리고 아래와 같이 내가 낚아챌 메소드를 구현한다.
- (id)alt_executeLaunchRequest:(id)arg1 withError:(NSError **)error {
RBSLaunchRequest *launchRequest;
if ([arg1 isKindOfClass:[RBSLaunchRequest class]]) {
launchRequest = arg1;
NSLog(@"Magician: launchRequest: %@", launchRequest);
}
id result = [self alt_executeLaunchRequest:arg1 withError:error];
return result;
}
실제로 arg1이 어떤 객체로 들어올 지 몰라서 isKindOfClass로 RBSLaunchRequest 클래스가 맞는지 확인하고 출력하도록 해두었다.
이제 해당 dylib을 SpringBoard에 로드시켜보면…
![](https://byeon.is/wp-content/uploads/2023/06/image-2-1024x48.png)
위와 같이 모든 프로세스가 실행될때마다 로그가 찍히는 걸 확인할 수 있다!
일단 아무 앱을 실행시키지 않아도 xpcservice 라는 프로세스들이 전부 다 찍히는 걸 볼 수 있는데 앱만 찍히도록 해야한다. 이 역시 Runtime Header를 찾아보면 체크할 수 있는 방법이 있다.
![](https://byeon.is/wp-content/uploads/2023/06/image-3-1024x937.png)
이건 RunningBoard가 아닌 RunningBoardServices에 정의되어 있는데 해당 BOOL 값으로 application인지 아닌지 확인할 수 있다. 해당 값이 true인 경우에만 작동할 수 있게 분기문을 추가해주자.
근데 아직 정작 중요한 내가 원하는 dylib를 주입할 수 있는 함수를 찾지못했다… RBSLaunchRequest의 값들을 더 살펴보니 RBSLaunchContext라는 객체가 있고 additionalEnvironment라는 Dictionary를 갖고 있었다.
![](https://byeon.is/wp-content/uploads/2023/06/image-4-1024x831.png)
![](https://byeon.is/wp-content/uploads/2023/06/image-5-542x1024.png)
![](https://byeon.is/wp-content/uploads/2023/06/image-6-542x1024.png)
Apple 기본 앱에서도 FLEX가 나타난다! (dylib가 제대로 로드되었다는 뜻)
기본적인 로직은 구현된 것 같으니 다음은 macOS와 iOS에서 로드된 dylib 파일을 소켓 통신 시킬 예정이다.
최신 댓글