주의사항 : 이 포스팅은 개인 학습 및 교육적 목적으로 작성되었으며, 제공하는 정보를 악용하여 불법적인 행위를 하는 것은 엄격히 금지되어 있습니다. 타인의 시스템에 대한 접근 권한을 얻기 위해 명시적인 동의를 받아야 하며, 이러한 기술을 사용하여 발생하는 모든 결과에 대한 책임은 사용자에게 있습니다.
: 런타임 조작이란 디버깅 툴을 이용해 앱이 실행 중인 상태에서 흐름을 조작하여 보안 기능을 우회하는 것을 말한다.
// RuntimeManipuation1.js
// 클래스 이름 출력
if(ObjC.available){
for(var classname in ObjC.classes)
console.log(classname)
}
// RuntimeManipuation1_2.js
// 메소드 이름 출력
if(ObjC.available){
var classname = "LoginValidate"
var methods = ObjC.classes[classname].$ownMethods
for(var i=0; i<methods.length; i++){
console.log(methods[i])
}
}
// RuntimeManipuation1_3.js
if(ObjC.available){
var classname = "LoginValidate"
var methodname = "isLoginValidated"
var hook = ObjC.classes[classname][methodname]
Interceptor.attach(hook.implementation,{
onLeave:function(retval){
console.log("Runtime 조작")
console.log("[+] 클래스 명: " + classname)
console.log("[+] 메소드 명: " + methodname)
console.log("[+] 반환 값 타입: " + hook.returnType)
console.log("[+] 반환 값: " + retval)
}
})
}
// RuntimeManipuation1_4.js
if(ObjC.available){
var classname = "LoginValidate"
var methodname = "isLoginValidated"
var hook = ObjC.classes[classname][methodname]
Interceptor.attach(hook.implementation,{
onLeave:function(retval){
console.log("Runtime 조작")
console.log("[+] 클래스 명: " + classname)
console.log("[+] 메소드 명: " + methodname)
console.log("[+] 반환 값 타입: " + hook.returnType)
console.log("[+] 반환 값: " + retval)
var new_retval = ptr("0x1")
retval.replace(new_retval)
console.log("[+] 변조 시킨 반환 값 : " + new_retval)
}
})
}
// RuntimeManipuation2.js
//(1) 실 주소 확인 후 변조
var realBase = Module.findBaseAddress('DVIA-v2')
console.log("[+] ASLR로 바뀐 바이너리 실제 메모리 주소 : " + realBase)
var jailBreak_address = realBase.add('0x1bded4')
console.log("[+] 분기점 실제 주소 : " + jailBreak_address)
//(2) 반환 값 변조
Interceptor.attach(jailBreak_address,{
onEnter:function(args){
console.log("[+] 변조 전 반환 값 : ")
console.log(JSON.stringify(this.context))
this.context.x8 = 0x1
console.log("[+] 변조 후 반환 값 : ")
console.log(JSON.stringify(this.context))
}
})
A-1. x8 에는 0x2290 값을 고정으로 가지고, x9 에는 사용자 입력 값 local_28 을 가진다.
A-2. 주소 확인
A-3. Brute force 실행
// RuntimeManipuation3.js
var realBase = Module.findBaseAddress('DVIA-v2')
//console.log("[+] 앱 실제 메모리 주소 : " + realBase)
var runtime_address = realBase.add('0x15e3e0')
//console.log("[+] 분기점 실제 주소 : " + runtime_address)
// [+] 네 자릿수 맞춰주는 함수 정의
function padToFour(number){
if(number<=9999){
number = ("000" + number).slice(-4) // slice로 4자리 맞춰주면 됨
return String(number) // 위에서 "000"이 스트링으로 묶였는데 혹시 몰라 한번 더 형 변환을 해준다.
}
}
Interceptor.attach(runtime_address,{
onEnter:function(args){
console.log("")
console.log("[+] x8, x9 값 : ")
console.log(JSON.stringify(this.context))
console.log("x8 값 :" + this.context.x8)
console.log("x9 값 :" + this.context.x9)
// [+] 비교가 가능하도록 16진수 x8 값을 정수형(10진수)로 변환한다.
var hex_pin = parseInt(this.context.x8, 16) // 데이터 형식 주의! 여기선 정수(Int)형으로 반환된다.
hex_pin = String(hex_pin) // padToFour 함수가 스트링 타입이기에 여기도 동일하게 형 변환시켜 줘야한다.
// [+] Brute Force 반복문
for(var i=0;i<10000;i++){
console.log("[+] Brute force attack : " + padToFour(i))
if(hex_pin == padToFour(i)){
this.context.x9 = padToFour(i)
console.log("new x8 값 :" + this.context.x8)
console.log("new x9 값 :" + this.context.x9)
console.log("x8의 정수 코드는 : " + hex_pin)
break
}
}
}
})
B-1. x8 과 x9 에 같은 값을 넣어 준다.
B-2. 비교 값들에 동일한 값을 대입
// RuntimeManipuation3_2.js
var realBase = Module.findBaseAddress('DVIA-v2')
//console.log("[+] 앱 실제 메모리 주소 : " + realBase)
var runtime_address = realBase.add('0x15e3e0')
//console.log("[+] 분기점 실제 주소 : " + runtime_address)
Interceptor.attach(runtime_address,{
onEnter:function(args){
console.log("")
console.log("x8 값 :" + this.context.x8)
console.log("x9 값 :" + this.context.x9)
this.context.x8 = 0x1
this.context.x9 = 0x1
// x9 를 x8 에 담아줘도 된다.
console.log("new x8 값 :" + this.context.x8)
console.log("new x9 값 :" + this.context.x9)
}
})