티스토리 뷰
lldb를 사용해 IOS의 Application 동적 디버깅을 진행하는 방법에는 두가지가 있다.
1) macOS의 xcode를 사용해 원격으로 디버깅을 진행하는 방법
2) lldb-10 tweak을 설치해 로컬에서 진행하는 방법
(별다른 Source를 추가하지 않고 그냥 검색을 하면 나온다.)
나는 후자를 선택해서 진행했으며 로컬연결은 OpenSSH를 설치해 터미널상에서 진행했다.
IOS Version : IOS 12.4.8
IOS Device : IPhone 6
Jailbreak : Yes (unc0ver)
lldb-10 을 사용해 Process를 Attach하는 방법
1. ps -ef 를 사용해 Attach하고 싶은 Proccess의 PID를 찾는다. (당연히 App을 실행한 이후에)
2. lldb-10 명령어를 사용해서 해당 PID를 사용해 대상 Process에 Attach한다.
3. continue(c) 명령어를 사용해 Stop되어있는 Process를 resume한다.
공유라이브러리의 API에 BreakPoint 설정하는 방법
-[NSFileManager fileExistsAtPath:] 와 같은 API에 BreakPoint를 설정하고 싶을 때 사용한다. 주의해야 할 것은 사용자함수에 대해서는 안된다. 이 경우는 밑에서 추가적인 방법을 소개하도록 한다.
"b -[NSFileManager fileExistsAtPath]" 명령어를 사용해 BreakPoint를 설정한다.
사용자 함수에 BreakPoint 설정하는 방법
사용자 함수는 공유라이브러리(dylib)의 API처럼 모든 Process에 존재하는 것이 아니기 때문에 lldb에서 함수 이름(Symbol)을 알고있지 않다. ___lldb_unnamed_symbol587$$[APPNAME] 와 같은 형태로 알고있긴 하지만 우리가 가진 정보로 이 존재를 알아내기란 쉽지 않다. (루틴을 일일이 따라가보며 찾아낼 순 있겠지만ㅎㅎ)
1. image list 명령어로 Process에 올라온 Library를 확인하고, Binary의 BaseAddress를 찾는다.
Binary에 대한 정보는 보통 가장 위에 있으며 여기서는 BaseAddress는 0x000000010058c000이다.
2. IDA에 Binary File을 올려 대상 함수의 Offset을 찾는다.
간단하게 찾을 수 있으며, Offset은 BaseAddress를 뺀 0x19A0C 인 것을 확인할 수 있다.
Binary File을 뽑아내는 방법은 앞 포스팅을 참고하자.
3. BaseAddress와 Offset을 더해서 대상 함수의 Address를 구한다.
4. "b [Address]" 명령어를 사용해 해당 함수에 BreakPoint를 설정한다.
제대로 BreakPoint가 걸린 것을 확인할 수 있다. 보다시피 함수명이 ___lldb_unnamed_symbol580$$[APPNAME] 으로 되어있는 것을 확인할 수 있다. 물론 이걸 직접 "b ___lldb_unnamed_symbol580$$[APPNAME]" 해도 BreakPoint는 걸린다. 그러나 우리가 이걸 알아내는 것이 매우 어려울 수 있다.
Tip !!!
(IOS Application Dynamic Debugging을 진행하면서 유용했던 것들에 대한 정리)
1. 해당 API호출시 BreakPoint에서 Process가 Stop하는데 이 때 Argument정보 확인하기.
BreakPoint로 설정된 API 호출시 위와같이 Process가 Stop한다. 이 때 "po $arg1" 명령어를 사용해 이 API가 호출될 때 전달된 Argument를 확인할 수 있다.
물론 ARM 함수 호출규약에 의해서 $x0, $x1, $x2 ... Register를 통해서도 확인이 가능하다.
그리고 당연하게도 "register write x2 [Input]" 명령어로 이 값의 변경도 가능하다.
2. Process의 Register 정보를 확인하고 변조하기. (반환값 변조라던가.. 뭐 그럴때)
우선 "ni" 명령어를 계속 사용해 해당 함수를 빠져나가는 ret까지 이동했다.
(next instruction 명령어로 gdb와 같은 명령어이므로 설명은 생략한다.)
"register read" 명령어를 사용해 모든 Register에 대한 정보를 확인할 수 있다. 그리고 반환 값이 저장되는 Register $x0을 보니 0x01이 저장된 것을 확인할 수 있다. 이 값을 이제 0x00으로 변경해보도록 하자.
"register write [target] [value]" 명령어를 사용해 해당 Register의 값을 0x00으로 수정한다. 그리고 다시 Register를 확인해보니 정상적으로 값이 변경된 것을 확인할 수 있다.
(깨알같이 특정 Register의 정보만 확인하는 방법에 대해서 소개해봤다 ㅎㅎㅎ)
3. 특정 함수로 BreakPoint를 걸고 Stop됐을 때 반환 값만 바꿔서 바로 종료하기.
(굉장히 유용할 것으로 예상된다.. 감히? - 사실 이 동작은 FRIDA로 더 간단하게 가능하긴 하다.)
BreakPoint에 의해서 ___lldb_unnamed_symbol580$$DamnVulnerableIOSApp 의 시작부분에서 Process가 Stop된 것을 확인할 수 있다. 이 때 "thread return 0x00" 명령어를 사용해 해당 함수의 반환 값을 0x00으로 주고 바로 함수를 종료시킬 수 있다.
신경써줘야할 부분은 "continue" 명령어를 사용해 resume을 해줘야 한다는 점이다. 함수를 빠져나가기만 하지 바로 이어서 실행되는게 아니다.
4. 특정 코드를 동적으로 내가 원하는 코드로 바꾸기 (Dynamic Code Patch)
"mov x22, x0" 을 통해 특정 함수의 반환 값을 x22 레지스터에 저장한다. 확인해보니 x0에 저장된 값은 0x01이다. 이 값을 레지스터를 직접 바꿔도 되지만 여기서는 0x100d29a68 주소의 코드를 "mov x22, #0x00" 으로 변경해 반환 값을 변조해보도록 한다.
memory write -s 4 0x100d29a68 0xd2800016 명령어를 사용해 0x100d29a68 주소의 코드를 0xd2800016으로 덮어쓴다. 이 코드의 Assembly는 ARM to HEX 웹페이지에서 가져왔다.
160080d2지만 이걸 엔디안 전환을 해주게되면 0xd2800016이 된다.
dis -p 명령어를 사용해 정상적으로 코드가 수정된 것을 확인할 수 있고 이 후 흐름이 내가 원하는 방향으로 진행된다는 것까지 확인할 수 있다.
여기서는 정말 간단한 예제로 테스트 했지만 Dynamic Code Patch라는 것은 사용하기에 따라서 정말 다양하게 활용할 수 있다고 생각한다.
이외에도 굉장히 다양한 명령어와 편리한 기능들이 존재한다. 사용하게되면 계속해서 추가하도록 한다. 자세한 명령어들을 알고싶으면 LLDB 홈페이지를 참고하도록 하자.
- GDB, LLDB의 명령어 비교
https://lldb.llvm.org/use/map.html
- LLDB의 튜토리얼
https://lldb.llvm.org/use/tutorial.html
- 나보다 더 잘작성되어 있는 참고 사이트들
https://www.letmecompile.com/xcode-lldb-%EB%94%94%EB%B2%84%EA%B9%85-%ED%85%8C%ED%81%AC%EB%8B%89/
'IOS > Hacking' 카테고리의 다른 글
[IOS] Local Data Storage 종류와 저장 위치 (+API) (Feat. objection) (0) | 2020.08.20 |
---|---|
[IOS] objection 사용하기 (Application Runtime Analyze Tool) (0) | 2020.08.12 |
[IOS] 탈옥 탐지(Jailbreak) 코드 예시. (2) | 2020.07.10 |
[IOS] IPA 파일 Binary Patch. (2) | 2020.07.09 |
[IOS] IPA 추출방법. (IOS 12.4.7/12.4.8) (0) | 2020.07.08 |