주의사항 : 이 포스팅은 개인 학습 및 교육적 목적으로 작성되었으며, 제공하는 정보를 악용하여 불법적인 행위를 하는 것은 엄격히 금지되어 있습니다. 타인의 시스템에 대한 접근 권한을 얻기 위해 명시적인 동의를 받아야 하며, 이러한 기술을 사용하여 발생하는 모든 결과에 대한 책임은 사용자에게 있습니다.
: 탈옥되지 않은 기기에서는 존재할 수 없는 파일 또는 디렉터리 존재여부로 탈옥 여부를 판단하는 방법
: 앱 샌드박스 외부의 위치에 파일 생성을 시도하는 것으로 탈옥 여부를 판단하는 방법
: 프로토콜 핸들러 사용여부를 기반으로 탈옥 여부를 판단하는 방법
: 시스템 API를 호출하는 것으로 탈옥 여부를 판단하는 방법
:OpenSSH 서비스가 동작중인지를 확인하는 것으로 탈옥 여부를 판단하는 방법
이하 Test 들의 탐지 우회에서 탈옥되었다고 변경하겠다.
: 탈옥 탐지 우회는 두 단계로 나눠서 진행
Ghidra 로 탈옥 탐지 로직 파악
파악된 로직을 Ghidra 또는 Frida 를 이용하여 탈옥 탐지 기능 우회
1001cc17c 더블클릭 시 해당위치로 이동
레지스터 w0 의 값이 0x0 일 때와 아닐 때의 결과가 달라진다.
/* (1) 사전 준비
(1-1) DVIA-v2 바이너리가 메모리에 올라갔을때 실제 할당된 주소를 변수(realBase)에 담는다
var realBase = Module.findBaseAddress('DVIA-v2') <-- 실제 바이너리 이름 또는 프로세스 이름
(1-2) 주소 출력하여 확인
console.log(realBase)
(1-3) 주소값을 더한다
var Jailbreak_address = realBase.add('0x1cbdd0')
(2) 변조 시도 (interceptor - 반환값 또는 특정 무언가를 변조시 타겟 선언하여 사용)
Interceptor.attach(Jailbreak_address, { target 설정은 첫 번째 인자, 다음으로 콜백 지정
onEnter:function(args){ 콜백은 onEnter:function(args) <-- 기본 구조, onLeave:(반환값 변조시 사용) 여기서는 사용 안함
console.log(this.context) context는 할당된 레지스터 값을 받는다
console.log(JSON.stringify(this.context)) JSON 형태로 출력 - 앱 실행 시 레지스터 값 확인 가능
}
}) */
-------------------------------------
// jailBreak_test1.js
var realBase = Module.findBaseAddress('DVIA-v2')
console.log(realBase)
var jailBreak_address = realBase.add('0x1cbdd0')
console.log(jailBreak_address)
Interceptor.attach(jailBreak_address, {
onEnter:function(args){
console.log(JSON.stringify(this.context))
}
})
0x104a18000 : DVIA-v2 바이너리가 실제 할당된 주소
0x104be3dd0 : DVIA-v2 바이너리 내의 변조할 주소
// jailBreak_test1.js
var realBase = Module.findBaseAddress('DVIA-v2')
console.log(realBase)
var jailBreak_address = realBase.add('0x1cbdd0')
console.log(jailBreak_address)
Interceptor.attach(jailBreak_address, {
onEnter:function(args){
console.log(JSON.stringify(this.context))
// 아래부터 추가
// 레지스터 값 변조 (레지스터 받을 때는 this.context)
this.context.x0 = 0x1 // 현재 코드 블럭내 레지스터 값은 x0 : 0x0 인데 이를 강제 변경
console.log(JSON.stringify(this.context)) // 출력
}
})
// jailBreak_test2.js
if(ObjC.available){ // 해당 코드가 실행 가능한 환경이라면,
for(var classname in ObjC.classes) // ObjC.classes > 메모리에 올라와 있는 오브젝트 맵핑 클래스를 가져온다.
console.log(classname) //가져온 클래스의 이름을 출력
}
3, 프리다로 파일 로드 후 클래스 조회
frida -U -l "js 파일 경로" DVIA-v2 > classes.txt
// jailBreak_test2_2.js
if(ObjC.available){
var classname = "JailbreakDetection" //분석하고자 하는 클래스 이름을 지정
var methods = ObjC.classes[classname].$ownMethods //클래스가 가지고 있는 기본 메소드를 배열 형식으로 가져온다
console.log(methods) //가져온 메소드정보 출력
}
- 는 클래스에 대한 메소드, - 는 인스턴스에 대한 메소드
// jailBreak_test2_3.js
if(ObjC.available){
var classname = "JailbreakDetection"
var methodname = "isJailbroken" // 메소드 이름을 변수에 저장
var hook = ObjC.classes[classname][methodname] // 클래스명과, 메소드명을 배열형식으로 넣어준다.
Interceptor.attach(hook.implementation,{ // 탈취하기위해 준비, implementation : 해당 메소드를 실행한다.
onLeave:function(retval){ // 반환값을 확인하기 위해 사용.
console.log("[*] Class Name : " + classname) // 클래스명 출력
console.log("[*] Method Name : " + methodname) // 메소드명 출력
console.log("[*] Type of return value : " + hook.returnType) // 반환대는 값의 형식을 출력한다.
console.log("[*] Return Value : " + retval) // 반환값을 출력한다.
}
})
}
// jailBreak_test2_4.js
if(ObjC.available){
var classname = "JailbreakDetection"
var methodname = "isJailbroken"
var hook = ObjC.classes[classname][methodname]
Interceptor.attach(hook.implementation,{
onLeave:function(retval){
console.log("[*] Class Name : " + classname)
console.log("[*] Method Name : " + methodname)
console.log("[*] Type of return value : " + hook.returnType)
console.log("[*] Return Value : " + retval)
var new_retval = ptr("0x1") // 변조하고자 하는 값을 변수에 저장
retval.replace(new_retval) // 변수에 저장한 값으로 반환값을 변조
console.log("[*] New Return Value : " + retval) // 변조된 반환값을 출력
}
})
}
search - For Strings - 탈옥 탐지됐을 때 나왔을 문구인 Device is Jailbroken, the application will now exit 검색 - 조회할 행 더블클릭
Listing 창 - XREF[1]: __T07DVIA_v232JailbreakDetection... 더블클릭 - 음영표시된 칸 선택 - Window - Function Graph - 노란색 부분 확대
// jailBreak_test3.js
var realbase_address = Module.findBaseAddress('DVIA-v2') // DVIA-v2 바이너리가 실행되어 메모리에 올라갔을때 할당된 주소를 realbase_address 에 저장
console.log('realbase address : ' + realbase_address) // realbase_address에 저장된 주소를 화면에 출력
var jailbreak_address = realbase_address.add('0x1959dc') // realbase_address에 분기점 오프셋을 더하면 실제 분기점 메모리 주소가 jailbreak_address 에 저장됨
console.log('jailbreak address : ' + jailbreak_address) // jailbreak_address에 저장된 주소를 화면에 출력
Interceptor.attach(jailbreak_address, { // jailbreak_address에서 호출되는 값을 가로채기 위해 준비
onEnter: function(args){ // 호출되는 레지스터 값을 받아옴
console.log(JSON.stringify(this.context)) // 받아온 레지스터 값을 JSON 형태로 화면에 출력
}
})
w8, #0x0, 0x100195da8 우클릭 - Patch Instruction - 0x0 값을 0x1 로 수정 ( 조건문의 w8 의 값 #0x0 을 #0x1 로 수정하여 조건문이 반대가 되도록 만든다. )
File - Export Program - Format : Binary (Raw Bytes) - OK
파일 명의 확장자를 삭제하여 DVIA-v2 로 수정 후 아이폰의 DVIA-v2 파일 교체
DVIA 앱 재실행 후 Jailbreak Test 3 터치 시 탈옥되었다고 뜰 것이다.
디버깅 방지
디버깅 탐지
출처
https://hagsig.tistory.com/148
https://cybersecurity.asee.io/blog/what-is-jailbreaking/
https://takudaddy.tistory.com/592
https://takudaddy.tistory.com/594
https://takudaddy.tistory.com/595