효투의 세상 로딩중...
효투의 세상 로딩중...
반응형

lldb의 사용법을 어느정도 익혀서 정리해봄

2023.08.11 - [모바일/Ios] - [iOS] LLDB를 이용한 동적 디버깅 분석

 

[iOS] LLDB를 이용한 동적 디버깅 분석

ios에서 대표적인 동적 디버깅 도구는 LLDB라고 할 수있다. LLDB를 사용하기위해서 일단 Cydia나 Sileo에 Repo 추가가 필요하다 https://apt.procurs.us/ Index of / apt.procurs.us 옛날에는 lldb를 설치후에 쉘에서 바

hyotwo.tistory.com

 

첫 글에는 LLDB 사용법을 다뤘다면 이번 포스팅에선 사용법을 정리하면서

어느정도 활용하는 법을 적어보려한다.

 

 

LLDB 실행하기

 

lldb로 프로세스에 붙으면 아래 사진과 같은 모습이 나온다

lldb -p <pid> 명령어로 붙거나 lldb를 실행한 후 attach --name <실행파일명> 으로 붙을 수 있다.

※ 실행파일은 당연 Mach-O 파일임

 

help 명령을 입력하게되면

모든 명령어와 설명이 출력되고

굳이 쓸 일은 없을 것 같지만

gui를 입력하면 아래 사진처럼 gui 형태로도 사용할 수 있다. 

 

help <명령어> 를 입력하면 해당 명령어에 사용할 수 있는 기능이나 옵션을 또 친절히 설명해주기 때문에

읽어보고 사용하면 될 듯하다.

 

프로세스 Offset 구하기 및 BP(BreakPoint) 생성

 

먼저 LLDB를 목적에 맞게 사용하기위해선 디버깅도구인 만큼

메모리에서 실제 동작하는 주소에 브레이크포인트를 걸어서 디버깅을 해야한다.

 

최근 ios 포스팅에서도 여러번 언급했듯이

실행파일의 Offset + 메소드나 펑션의 시작주소가 실제 메모리의 시작주소가 된다

 

실행파일의 Offset은 그 프로세스가 실행될 때마다 계속 변경되며

 

Offset값을 구하는 법은

 

image dump sections <실행파일명>

image list 명령어 후 실행파일 확인

 

그럼 현재 실행한 프로세스의 Offset값은 1009d8000 이며

 

내가 디버깅을 시작하고싶은 부분의 주소는 100192CB0 이다

 

 

두 주소를 더해주면

100B6ACB0 이고 이 주소가 브레이크포인트를 걸 지점이다

 

lldb에서는 br옵션이나 b 옵션으로 브레이크 포인트를 줄 수있다.

 

b 옵션으로 100B6ACB0 주소 자체에 바로 브레이크 포인트를 걸거나

br 옵션으로 br set -a(Address 옵션) 주소로 BP 생성 가능

 

br명령과 b 명령은 기능은 동일하지만 브레이크포인트를 설정하는 방식이 다르다

그래서 첫 포스팅의 ptrace 자체에 BP를 거는 방식이나 아래처럼 주소를 다이렉트로 지정해주는 경우

b 명령어로만 가능함

브레이크 포인트가 어떤게 걸려있나 확인할 땐

br l

br list 등의 명령어로 확인이 가능하다

 

지울땐 delete 명령어나 수정할 땐 modify 등의 옵션을 줘서 가능

 

bp를 원하는 함수나 메소드에 걸어뒀다면

원하는 지점에서 앱이 브레이크포인트에 걸려서 아래 영상과 같이 멈추게된다

 

 

 

 

동적 디버깅을 통한 어셈블리 분석(Reversing)

 

BP에 걸리게되면 LLDB단에는 멈춘 지점과 그 다음에 실행될 어셈블리 명령들이 출력된다.

제대로 BP지점을 설정했다면

 

그 부분의 어셈블리 명령은 IDA와 같은 디스어셈블러 도구에서 정적분석을 해도

 

어셈블리 명령어들은 동일해야한다.

 

LLDB로 원하는 포인트에 디버깅이 시작됐다면

 

좌측에 → 화살표로 현재 BP가 걸려있는 지점을 확인할 수 있고

 

n 명령어나

ni 명령어로 다음 코드로 진행할 수 있다.

 

 

계속 레지스터를 이동하고 복사하고 덮어쓰는 sub, stp, mov 등등의 스택관리 어셈블리 명령들을 건너서

다음 코드를 실행하다보면

 

BL이라는 명령을 만날 수 있는데 Branch with Link 의 약자로 특정 함수나 서브루틴을 호출하는 역할을 한다

 

그럼 A라는 서브루틴이나 함수가 호출될 때 

A 함수가 어떤 역할을 하는지 또 봐줘야 동작을 이해할 수 있다

 

이 때는 분석하고싶은 BL문을 만났을 때 lldb에 s 옵션을 줘서

아래 그림처럼 해당 함수나 서브루틴에 들어갈 수 있다.

 

그리고 다시 n으로 코드를 진행하며

해당 함수의 기능이 끝나면

 

원래 진행되던 메소드로 돌아온다 아래 사진과 같음

 

반응형

 

test1Tapped 메소드에선

아래 사진의 BL 호출이 핵심적이다

 

그래서 lldb로 코드를 진행시키며 4번째 호출 함수에 들어가서 코드를 확인해줌

 

__T07DVIA_v232JailbreakDetectionViewControllerC20jailbreakTest1Tappedyyp 라는 펑션 함수를 타고 들어오면

또 역시 어셈블리 명령은 IDA에서 보는것과 동일하다.

 

특정 시점에서 변조포인트를 찾아야겠다는 생각이 들면은

 

LDR 명령이나 MOV 명령과 같은 특정 레지스터의 값이 바뀌는 명령에서

아래 명령으로 레지스터 값을 확인할 수 있다.

reg read <레지스터주소>

reg read(전체 레지스터 확인)

register read 

 

레지스터 주소를 특정하지하고 reg read만 했을 때는 전체 레지스터가 나오며

 

 현재 어떤 값이 담겨있는지 lr, sp, pc 등등 모든 메모리 영역도 확인가능하다

 

아래 사진처럼 ldr 명령 한줄이 실행되면

x9 레지스터의 값이 0에서 특정 주소값이 담기게 된 것을 볼 수 있다.

 

 

이런 방식으로 코드를 분석하는 것인데

아까 위에서 bl이 특정 함수를 호출하는 중요한 명령어라고 말했다

 

그럼 blr로 뭔가 비슷하니까 중요해보인다

 

blr은 Branch with Link Register 의 약자이다

그러니까 X9 레지스터에 담긴 주소를 호출한 뒤 그 결과값을 x9 레지스터에 담게된다

 

아래사진에서 보듯이 x9 레지스터를 살펴보면

 

100e509a8이라는 주소값이 담겨있고 그 오른쪽엔 어떤 함수인지 정보가 들어있다

 

그리고 해당 blr 명령이 끝나면 x9레지스터에 1값이 담긴다

 

이게 왜 이렇게 된거냐??

살펴보면

 

x9 레지스터에서 

호출하는 주소는 100E509A8 이다

 

그리고 현재 프로세스의 오프셋은 100cbc000이다 (중간에 끊겨가주고 오프셋 변경됨...)

그리고 아래 사진을 보면

호출된 isJailbroken 함수의 시작주소는 1001949A8이고

 

그 함수 내부는 아래 사진처럼 Cydia.app을 검증하고 

그 아래로 쭉쭉 탈옥탐지 로직이 길게 있다.

 

 

 그럼 함수의 시작주소가 되는 1001949A8과 오프셋은 100cbc000 을 더하면

x9 레지스터에 담긴 100e509a8 주소가 나온다

 

 

그래서 저 위의 isJailbroken 함수가 호출되며 여러 탈옥탐지 로직을 거쳐

그 결과값인 0x1 = True = 1 값이 x9 레지스터에 저장되서

이렇게 나온 것임

 

그리고 코드를 계속 또 진행시키다 보면

위에서 설명한 같은 원리로 

 

__T07DVIA_v213DVIAUtilitiesC9showAlertySb28forJailbreakTestIsJailbroken_So16UIViewControllerC04viewL0tFZ

라는 함수가 호출되는것을 볼 수 있다.

 

IDA에서 그 코드를 살펴도되고 s를 통해 진입해도된다

 

IDA에서 보는 코드

LLDB에서 step으로 들어온 코드

두개가 어차피 똑같지만 아무튼 보면 w0 레지스터에 담긴 값이 0인지 아닌지 비교하는 분기문

 

TBZ에 의해 탈옥 탐지 알림창을 띄우고 있다.

 

LLDB를 이용한 레지스터 변조

이렇게 이제 어떤 포인트를 변조해야하는지 찾았으면

 

바로 변조를 해주면된다.

 

기본적인 명령어는 아래 사진처럼

 

reg write <레지스터주소> <변환할 값>

 

x0 레지스터에 0값을 씌워주면

 

test 1 로직이 우회가 가능하게된다

 

0이든 0x0이든 0x00 이든 다 똑같은 0으로 받아들인다.

그 결과는 아래 영상과 같음

 

 

 

LLDB로 할 수 있는 다양한 변조

 

위와 같은 레지스터 변조 뿐만 아니라

 

리턴 변조, Hex값 변조 등

다양한 변조가 가능하며

 

다양한 기능을 가진 Frida와 비슷하게 기능을 가진다.

 

먼저 Frida의 리턴변조와 같이

 

특정 메소드에 진입하게되었을 때 리턴변조를 사용할 수 있다.

DVIA에서는 test2에서 가능함

 

먼저 test2에서 호출되는 isJailbroken 메소드에 BP를 걸어준다

 

브레이크포인트 셋팅

test2를 누르게되면 메소드가 호출되며 프로세스가 멈춤

여기서

thread return 이라는 명령어를 통해서 리턴 변조를 수행할 수 있다.

 

thread return 0x0 을 실행하면

 

Frida처럼 리턴 자체가 0x1에서 0x0으로 변조되며 메소드의 탈옥탐지 로직 우회가 가능하다.

 

 

이런 리턴변조 뿐만 아니라

특정 분기문을 변조할 때 Hex 데이터를 변조하는데 그 또한 LLDB에서 수행할 수 있다

 

아래 사진처럼 isJailbroken 메소드 아래에 TBZ라는 분기문을 TBNZ로 변경해야할 때

 

mem read <주소>

memory read <주소>

 

mem write <주소> <Hex>

memory write <주소> <Hex>

명령어를 사용한다

 

mem read명령어로 TBZ 분기문의 주소를 읽게되면 

아래 사진과 같이 HxD에서 보든 IDA에서 보든 같은 결과가 나오게된다.

 

해당 분기문을 변조하려하면

 

mem write <주소> <HEX>

아래 사진처럼 분기문 자체를 변조할 수 있다.

 

 

이 외에도 다양한 LLDB의 기능들이 있고

 

help 기능을 이용하여 공부하며 분석하는것도 좋은 접근 방법이라고 생각한다.

 

반응형
  • hyotwo7658@gmail.com

복사 완료 👍