보통 요즘 나오는 탈옥툴에 사용될텐데...
ipa 의 코드사인을 다시 하는 방법.

iOS App Signer 라는 앱이 있는데.. 왜인지 이걸로 하면 안된다. https://dantheman827.github.io/ios-app-signer/

--------------------

 

난 보통 소스코드를 받아서 xcode 를 이용하는 방법으로 했는데, 이번에 그게 좀 여의치 않아서 찾아봄.
예전 방법이 많이 올라와있는데 그것들은 안되더라.

 

개발자 인증서는 키체인에 들어있는걸 가정하고, Profile 은 애플 개발자 홈페이지에서 다운받으면 됨.
와일드카드를 쓰더라도 앱 아이디가 중복되면 안된다는 것 같은데,, 문제가 생기면 Info.plist 에서 앱 아이디도 바꿔주자

 

# entitlements 정보 추출
security cms -D -i path/to/MyProfile.mobileprovision > provision.plist
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' provision.plist > entitlements.plist

# 압축풀고 기존 사이닝 제거
unzip MyApp.ipa
rm -rf Payload/MyApp.app/_CodeSignature

# 프로파일 넣어주기 및 사이닝
cp path/to/MyProfile.mobileprovision Payload/MyApp.app/embedded.mobileprovision
codesign -f -s "iPhone Developer: Some Body (XXXXXXXXXX)" --entitlements entitlements.plist Payload/MyApp.app

# 프레임워크가 있다면 그것들도 다시 사이닝
codesign -f -s "iPhone Developer: Some Body (XXXXXXXXXX)" --entitlements entitlements.plist Payload/MyApp.app/Frameworks/*

# 패키징
zip -qr app-resigned.ipa Payload

대충 이런 과정을 거치면 된다.

 

이 후 앱스토어에서 Apple Configurator 2 라는 앱을 받아서, USB 연결된 장비에 생성된 ipa 를 던져주면 설치됨.

 

설치 이후에 아이콘이 어둡고 실행이 안된다면 사이닝이 잘못되었거나 profile 에 해당 기기가 포함이 안된 경우.

 

참고
https://stackoverflow.com/a/37172815

결론부터 말하자면 그냥 우분투에 3.6을 깔자..

다만 그 과정은 다른 상황에서 적용할 수 있을테니 남겨둠.


----------


Ubuntu 서버들은 파이썬 버전이 3.4(14.04) 혹은 3.5(16.04) 이고, 3.6 을 설치하긴 귀찮았다.

마침 맥의 파이썬이 3.6이길래 zappa 설치를 시도했는데, 의존성 설치에서 fail. 언제나 그렇듯 인코딩 문제...


의존성에 있는 kappa 라이브러리의 패키지 정보에 넣기 위한 README 파일을 제대로 읽어오지 못하는 거였고,

tree 구조를 표현하는 문자가 문제였음.


builtins.open 함수에서 encoding 을 지정해주지 않으면 기본값은 locale.getpreferredencoding(False) 을 사용하고,

이 문자열이 우분투에서는 UTF-8 이였지만, 맥에서는 US-ASCII 였다.


기본값을 어떻게 바꿔줘야하나 찾아보다가, 잘 안나오고 귀찮아서 소스코드를 받아 설치하는 방법으로 진행했다.


$ git clone https://github.com/garnaat/kappa

$ cd kappa


zappa 는 kappa (=0.6.0) 을 의존성으로 가지니

$ git checkout 0.6.0


$ vi setup.py

14     return open(os.path.join(os.path.dirname(__file__), fname), encoding='utf-8')


$ sudo pip3 install -e .


이렇게 한 후에 다시 zappa 설치를 진행하면 끗!


다만, 이후에도 비슷한 문제가 생기고 어차피 zappa 는 virtualenv 환경을 필요로한다.

그냥 우분투에 가상환경으로 3.6을 만들어두고 하자...

매번 검색하기 귀찮아서 작성함

그냥 UNetbootin 을 사용하는것도 괜찮은 선택지인 것 같다.


디스크 목록 보기

$ diskutil list


디스크 추출

$ diskutil unmountDisk /dev/disk#


ISO -> IMG

$ hdiutil convert -format UDRW -o ~/path/to/output.img /path/to/input.iso

$ mv /path/to/output.img.dmg /path/to/output.img


Flashing image

$ sudo dd if=/path/to/output.img of=/dev/rdisk# bs=8m


압축된 이미지 Flashing

$ gunzip -c /path/to/image.gz | sudo dd of=/dev/rdisk# bs=8m



bs 는 한번에 작업할 block size. 1m 을 쓰라고 하는 곳도 있는데.. 뭐 상관없겠지


dd 작업 중간에 Ctrl+T 를 누르면 작업상황을 확인할 수 있다.

난 직접 서버에서 최신버전을 갱신하는 식으로 해야할 줄 알았는데...

apple 에서 json 으로 결과를 뱉어주는게 있다.. 씐기..


잊어먹을까봐 적어둠.


http://itunes.apple.com/lookup?bundleId=[bundleIdentification]


으로 쿼리를 보내면 스크린샷 이미지, 장르, 버전 등의 앱정보를 JSON 으로 반환해준다.

이를 읽으면 됨.


아래 출처에 코드도 있음.



출처:

http://stackoverflow.com/questions/6256748/check-if-my-app-has-a-new-version-on-appstore

Apple 개발자 페이지에서 App ID 등록 및 Push 인증서 발급까지 끝낸 이 후.


0. 개발용 인증서 등록

잘 까먹는 과정 중 하나라서..

Push 서비스를 이용하려면 개발용 인증서 새로 발급해야 한다. 와일드카드 ㄴㄴ

발급 후 다운받을 수 있는 .mobileprovision 파일을 실행 후

프로젝트 Build Settings -> Code Signing Identity 에서 해당 인증서 선택


==========


1. .cer 파일을 실행해서 키체인에 등록

2. 키체인 - 인증서 카테고리 에서 인증서 찾아서 (앱아이디로 검색)


3. 왼쪽 접힌걸 열어서 인증서와 개인키를 같이 선택, 우클릭 -> 2개 항목 보내기

ㄴ 정확한건 아니지만, sandbox 용은 개인키와 함께, production 용은 인증서만 선택해서 추출해야 작동을 하는 것 같다.

원래 잘 되던게 언제부턴가 이러던데, 정확한 원인이나 이유는 잘 모르겠음.


4. .p12 포맷으로 적당한 곳에 저장. 비밀번호는 설정안해도 된다.

5. $ openssl pkcs12 -in cert.p12 -out cert.pem -nodes

6. 위에서 비밀번호를 지정했다면 해당 비밀번호를, 아니라면 그냥 엔터

7. 완성

IPC, Inter Process Communication..


말 그대로 프로세스 간에 통신을 위한 방법이다.

예전에 deVbug 님 글에 몇가지 소개가 있었는데 ... IPC (Inter Process Communication) in iOS


나도 이것에 대해 잠깐 끄적여 볼까 한다..

BeeKeyboard 의 온갖 버그의 주범이자, BeeKeyboard 가 iOS7 대응하는데 2달이나 걸리게 한 장본인이기도 하다.. orz


IPC 자체에 대해 쓰기보다는, iOS7 의 강화된 샌드박스에 의해 IPC 를 제대로 사용하지 못하면서 생긴 문제와 그를 풀어갔던 삽질.. 에 대해서 써볼려고...

BeeKeyboard 업데이트 일기..? 의 느낌으로 써보려 했으나 글의 목적에 어긋나는 것 같아 

BeeKeyboard 보다는 IPC 자체의 삽질에 좀더 초점을 맞춰보려 한다.

일대기.. 식으로 회상하면서 써내려 갈 거임!! ㅋㅋㅋ


시작하기 전에 IPC 에 대해 잠깐 정리.

- mach_msg : 기본적으로 모든 IPC 는 mach_msg 라는걸 기본으로 한다. 그런 만큼 얘는 매우 low level 이고, 메세지 하나 보내는데 20줄 가까이를 요구한다.. 래핑하지 않으면 더럽게 쓰기 어려운 녀석이지.. Ryan Petrich 의 LightMessaging 이 얘를 래핑했다. 나의 경우 BoatMessaging 이라는 녀석으로 래핑해서 사용 중.

- CFMachPort : mach_msg 에 쓰이는 mach_port_t 를 위한건데.. 한단계 래핑했다기 보다는 그냥 같이 쓰인다

- CFMessagePort : 좀 쓸만하게 래핑된 프레임워크. 양방향 메세징을 지원하며 내가 주로 사용했다ㅋㅋ

- CPDistributedMessagingCenter : 마찬가지로 양방향 메세징을 지원하며, CFMessagePort 보다 한층 더 쓰기 쉬우나, 자질구레한 문제가 많이 일어난다. 내가 제대로 못써서 그런거일 수도 있고...BeeKeyboard 의 iOS5 시절에 사용했었다.


뭐 이정도..? 당연히 더 있지만 일단 지금 글에선 필요없다.



1. iOS7 의 강화된 샌드박스

그냥 더 많은게 안된다. 시스템 로그를 보면, 앱스토어 앱이나 시스템 기본 데몬들도 sandbox 에 의해 좌절하는 로그를 무수히 쏟아낸다...

특히 CFMessagePort 의 경우 본래 앱스토어 앱에선 사용 못할텐데 맥 레퍼런스에서

"This method is not available on iOS 7 and later—it will return NULL and log a sandbox violation in syslog."

라고 친절히 알려주기 까지 한다..  ( 출처: 애플 레퍼런스 )


2. 근데 왜 제목에서 with Evais0n??

사실 BeeKeyboard 는 본래 iOS7 탈옥이 나오기 전에 winocm의 opensn0w 를 이용한 아이폰4 반탈로, iOS7 에 대한 대응이 되어 있었다.

탈옥할 때 탈옥툴 개발자들이 sandbox 를 깨주는데(그래야 트윅이 사용가능 하므로) 이 작업을 evais0n 탈옥팀은 뭔가 반틈만 한게 아닌가.. 라고 생각하고 있다. 물론 1번의, Apple 의 강화된 샌드박스도 한 몫할 것이고.


3. 삽질의 시작.

진짜 처음엔 답이 없어보였다.. 자고 깨고 자고 깨고(일이 잘 안풀리면 급격히 졸려서 1~2시간쯤 자고 일어난다)를 반복하며 1주일 가까이를 헤맸었던 것으로 기억한다.(심지어 이땐 RocketBootstrap 이 없거나, 개발 중이였다)

요즘 오전 6~7시에 취침 하고 오후에 일어나는, 6시간쯤 비틀린 삶을 사는데 그 원인이 여기에 있나니...

여튼 sandbox 에 의해 막히지만, privileged process, 즉 backboardd 나, SpringBoard, 혹은 Preferences 같은 시스템 앱들은 정상 사용이 가능하다. 하지만 난 일반적인 앱 모두에서의 사용을 필요로 했다. 단방향이 아닌 양방향.


4. XPC, WaveMessaging!

처음으로 가능성의 빛을 본 건 xpc. iOS5 정도 부터 iOS 에 들어간 것으로 보이고, 맥에서 IPC 로 애플이 추천하는 방법이다.

양방향 통신도 가능하고... 데이터형을 모두 xpc_object_t 를 사용해서 변환작업이 있다는 것만 제외하면 쓸만했다.

하지만 이 방법의 단점은 plist 에 특정 이름을 쓸거라고 미리 지정을 해 줘야 한다. 그렇지 않으면 사용할 수 없음...

launchd 가 관리하기에 후킹도 불가능하다...

그래서 난 원래 시스템이 사용하는 xpc 를 빌려, 적절히 후킹해서 사용하기로 했다.

그 결과물이 WaveMessaging. 좀더 자세한 방법은 써봤자 모를테니 패스..

open source... 지만 쓸 일 없을거다.. 써서도 안되고. 혹시나 xpc 를 쓸 일이 있으면 참고가 되려나? 

소스: https://github.com/iolate/WaveMessaging


5. Optimo의 리젝...orz

SimulateTouch, BeeKeyboard, QuickKakao 프로토버전 등, IPC 가 필요한 앱에 모두 이식을 해 보았다.

결과는 대 성공! 모두 정상작동 하고 큰 문제도 없어보였다.

일단 WaveMessaging 패키지만 먼저 BigBoss 에 업로드 하였으나 돌아온 대답은 No!

이미 RocketBootstrap 이라는, 같은 목적의 패키지가 있으니 이것과 합치는 방향으로 가라는 것이다.

당시 RocketBootstrap 은 SpringBoard 를 기반으로만 작동하였고, LightMessaging 이 사용하는, mach_msg 와 이미 문제때문에 사용하지 않는 CPDistributedMessagingCenter..정도만 지원하였기에 나의 경우엔 쓸 수 없었다.

Optimo 에게 요구해 보았지만, 돌아온 대답은 "Ryan 과 잘 얘기해서 RocketBootstrap 으로 합치거라."


6. Ryan 의 CFMessagePort 지원

Ryan 은

1. CFMessagePort 혹은 xpc 를 지원하고 2. daemon 등에서도 사용가능 하게 해주겠다.

면서 몇일만 기다려 달라고 했다.

별 수 있으리.. 기다렸다. 며칠 후 RocketBootstrap 은 CFMessagePort 를 지원하였고, 잘 작동하는 듯 보였다.

하지만 일이 이렇게 쉽게 풀렸으면 이 글을 시작하지도 않았다..ㅠㅠ

일반 App -> SpringBoard, backboardd 는 정상적으로 사용이 가능해 졌지만, SpringBoard, backboardd -> App 은 사용이 불가능 했다!

App 위에서 CFMessagePort 의 서버를 돌렸는데 mach_lookup 뿐만 아니라 mach_register 까지 sandbox 에 의해 막히면서 크래시....

SimulateTouch 의 경우 이정도 만으로 충분히 사용이 가능했지만, BeeKeyboard 와 후에 만들 빠른 답장 트윅에선 곤란했다.

Ryan 에게 요구했으나 이건 자기도 어쩔 수 없다며, 다른 방법을 찾으라고 했다..


7. 결국 mach_msg

Ryan 이 나에게 왜 그런 기능들이 필요하냐고 물었다. 그래서 나는 BeeKeyboard 의 동작 원리와, 후에 만들 빠른 답장 류의 앱에 대해 간략히 설명해줬다. 음.. 여기는 말로 풀어내기 복잡하니 대충 건너뛰고 결론은

1. timeout 기능은 mach_msg 로 가능하다

2. xpc 또한 mach_msg 로 만들어 졌다. 니가 xpc 에서 사용하던건 mach_msg 로 구현이 가능하다


.... 별 수 있나.. mach_msg 로 시도했다.

xpc 를 떠올리며 최대한 흡사하게 흉내낸 후, 결과는 성공.

Ryan 의 LightMessaging 의 소스를 많이 참고하였지만, 그래도 생각보다 쉽게 해결되서 상당히 허탈했던 기억이....

Ryan 에게 확인을 구하니 접근은 정확하게 맞았고, 방법에 약간의 수정을 가하면 된다고 하였다.

이렇게 해서 mach_msg 로 내가 원하는 기능을 정확히 구현...

위에 말했듯이, mach_msg 를 그대로 사용하는건 토나오므로, BoatMessaging 이라는, CPDistributedMessagingCenter 와 CFMessagePort 의 사이.. 정도 수준으로 래핑한 스태틱 라이브러리를 만들었다.


이걸로 IPC 해결!!!



이렇게 까지 오는데 한달 넘게? 거의 두달? 여튼 엄청난 시간을 쏟아 부었던 것 같다..ㅠㅠ

BoatMessaging 의 소스나 하다못해 라이브러리라도 어디 올릴까 했지만,

살짝 꼼수스러운 라이브러리이기도 하고, 레퍼런스 등을 만들기 귀찮아서 일단은 그냥 나 혼자 쓰고 있다.


혹시나 필요하시면 연락주세요..ㅋㅋㅋ 기쁜 마음으로 공유해드립니다! ㅋㅋㅋ

이래봤자 요구할 만한 사람은 상당히 한정되어 있지만....

탈옥 개발자에, 한국어 사용자.. 몇이나 되겠어? 


만들 당시 Source Control 켜서 쓰고 있었길래 그냥 그대로 github 에 올렸다.

주소는 https://github.com/iolate/BoatMessaging

static library 에,, rocket bootstrap 을 필요로 하므로, 사용하려면 LDFLAGS 에 -lboatmessaging -lrocketbootstrap

둘다 해줘야 함...


여튼,, 수능이 끝난 잉여로운 고3이였기에 해결이 가능했던 것 같다..

간단하다.


Target -> Info (즉 Info.plist 수정) 에서

Application is agent (UIElement) 값을 YES 로 해주면 된다.

Raw key 값은 LSUIElement



근데 이렇게 하면 내가 원하는대로 수정이 안되거나,

수정을 하더라도 앱을 재실행 해줘야 한다.


그래서,, 아래와 같은 방법이 있다!!


위에서 설정하는 UIElement 값을 동적으로 바꿔주는 것 같다.


ProcessSerialNumber psn = { 0, kCurrentProcess };

TransformProcessType(&psn, kProcessTransformToForegroundApplication);


이건 독에 아이콘을 나타내는 코드



ProcessSerialNumber psn = { 0, kCurrentProcess };

TransformProcessType(&psn, kProcessTransformToUIElementApplication);


이건 다시 UIElement = TRUE, 즉 독에서 앱을 없애게 해준다.


type 인자만 다르지만,, 두줄밖에 안되니 그냥 두번 적어줬다.



여기에 앱을 등록하고, 확인하고, 제거하는 코드.

자신의 bundlePath 기준으로 등록, 검사, 제거한다.


코드를 그냥 붙여넣기 하려 했으나, 들여쓰기가 없어진다..ㅠ


그래서 걍 gist 로 올림.


https://gist.github.com/iolate/7963775


원래 추가/삭제만 있었는데 등록되어 있는지 검사하는 코드도 넣었다.(삭제에서 조금만 수정..ㅎㅎ)

그리고.. 메모리 관리 조금.. 근데 맞는지 모르겠다ㅋㅋㅋ


출처: http://cocoatutorial.grapewave.com/tag/mac-os-x/

%hook NSString

-(NSString *)stringWithString:(NSString *)str {

//....

%orig;

}

%end


THEOS 언어를 사용해봤다면, 메서드를 위와 같이 후킹해서 사용하게 된다.

근데 저건 탈옥해서 트윅을 사용할 때의 얘기이고,,,


맥용 앱을 제작할 때 메서드 후킹이 필요할 때가 있을 수 있다.

(iOS 에서는 확인 안해봤지만,, 안되겠지?)


일반적인 경우라면 새로운 클래스를 만들고 원래 클래스를 상속받아서 사용하면 되겠지만..




주저리주저리 하고 싶은 말이 많지만 생략하고, 오버라이딩(후킹)을 위해선 아래 함수를 사용하면 된다.

더보기 클릭!




사용방법은


1. 해당 클래스에 카테고리로 대체용 함수를 만든다.

2. MethodSwizzle 함수를 사용해 필요한 때에 바꿔준다.

3. 원래 메서드를 호출하고 싶다면, 카테고리로 만든 메서드를 호출해 주면 된다.


예시:


@interface NSString (Swizzle)

-(NSString *)alt_stringWithString:(NSString *)str;

@end


@implementation NSString (Swizzle)


-(NSString *)alt_stringWithString:(NSString *)str {

    //......

    

    return [self alt_stringWithString:str];

}


@end


...


-(void)somewhereInYourMind {

MethodSwizzle([NSString class], @selector(stringWithString:), @selector(alt_stringWithString:));


[NSString stringWithString:@""];

}


이렇게 하면 원래 메서드(여기선 stringWithString:) 를 호출하면

alt_stringWithString: 을 거친 다음 원래 메서드가 호출되고, 반환된다.



출처: http://cocoadev.com/MethodSwizzling

Activator 를 꺼려하는 사람이 은근히 많아서,

Activator 가 필수적이지 않은 BeeKeyboard 에서는 의존성을 없애야 겠다,, 는 생각만 하고 놔두다가

어제 weak link 로 바꿨다..ㅎ


잠깐 확인해 봤는데 잘됨!


1. Makefiles 의 LDFLAGS 를 아래처럼 사용한다

@@@@_LDFLAGS = -weak_library /opt/theos/lib/libactivator.dylib


내가 못하는건지, 원래 안되는 건지 -weak_library 로 할때는 경로 전체를 적어줘야 하더라..


2. 앱 내에선 클래스의 존재 유무로 라이브러리가 로드 됐는지 확인한다.

다른 방법도 있는 것 같지만, 난 간단하게 저렇게 했음.

확인 안하고 냅두면 크래쉬 남 ㅇㅇㅋ


Class LAE = NSClassFromString(@"LAEvent");

if (LAE != nil) {


}


요런식으로...



설정 부분 같으면 불러오는 부분은 PSListController 를 사용하고,

Activator 를 사용하는 컨트롤러에서는 내부 변수로 Activator 의 클래스를 사용하고 있어서,

PSListController 에서 tableView:didSelectRowAtIndexPath: 를 오버라이드 해서 체크하고 있다.

다른 부분에서 비슷한걸 구현해 사용하고 있기 때문에 쉽게 끝냈음 ㅋ


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    int group = [self indexOfGroup:indexPath.section];

    int pIndex = group + indexPath.row + 1;

    

    PSSpecifier* spec = [self specifierAtIndex:pIndex];

    if ([[spec propertyForKey:@"detail"] isEqualToString:@"GActivatorSettings"]) {

        Class LAE = NSClassFromString(@"LAEvent");

        if (LAE != nil) {

            [super tableView:tableView didSelectRowAtIndexPath:indexPath];

        }else{

            [tableView deselectRowAtIndexPath:indexPath animated:YES];

            NSString* message = LS(@"NEED_ACTIVATOR");

            UIAlertView* alert = [[UIAlertView alloc] initWithTitle:LS(@"BeeKeyboard") message:message delegate:self cancelButtonTitle:LS(@"OK") otherButtonTitles:nil];

            [alert show];

            [alert release];

            return;

        }

    }

    

    [super tableView:tableView didSelectRowAtIndexPath:indexPath];

}


단, 이 경우 PSListController 의 헤더에 tableView:didSelectRowAtIndexPath: 가 선언되어 있지 않기 때문에

선언해주고 써야 한다.


@interface PSListController (p)

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

@end



+ 확실하진 않은데, PSListController 에서 불러오는 plist 에서 index 0 의 항목이 PSGroupCell 이 아닐 경우 index 번호가 한개씩 밀릴 수도 있음... ㅎㅎ;;




여튼 이렇게 하면, Activator 와의 연계 부분을 사용하고 싶은 사람만 Activator 를 설치, 사용할 수 있게 된다!


다른 Dynamic Library 나 OS버젼 따라 다른 프레임워크 등도 이런식으로 사용하면 된다.

프레임워크의 경우엔 -weak_framework 임.

+ Recent posts