새소식

모의해킹/Android

Frida 표준 C 라이브러리 함수 후킹

  • -
반응형

 

여러 회사들의 모바일 앱을 분석해보다 보니 느낀거지만, 요즘은 금융권에 해당하지 않더라도 앱에 모바일 앱 보호 솔루션을 씌워놓은 회사들이 점점 늘어나고 있는 듯 하다.

 

그리고 대부분의 모바일 앱 보호 솔루션들이 C/C++ 로 컴파일된 라이브러리 파일(.so) 단에서 동작하는 경우가 많기 때문에 JEB, Jadx 등으로 Java 메소드만 분석하려고 하다가는 답도 찾지 못하고 솔루션한테 얻어맞기만 하다가 프로젝트가 끝날 수 있다.

 

대부분의 솔루션들은 open, fopen, access 등 의 내장된 C/C++ 표준 라이브러리 함수를 이용해 루팅/탈옥 여부를 탐지하곤 하는데 해당 함수들은 대개 안드로이드의 경우 libc.so 에, iOS 의 경우는 libSystem.B.dylib 에 내장되어 있다.

 

표준 C 라이브러리 함수들을 후킹하여 루팅 여부를 우회하기 위해서는 해당 함수의 원형(proto type)에 대해 알아야 한다.

예를 들어 open 함수를 이용해 '/bin/su' 와 '/system/app/Superuser.apk' 파일을 탐지하는 로직이 있다고 치자.

 

리눅스 매뉴얼 페이지 에서 open 함수의 원형을 확인해보면 된다.

open 함수의 첫번째 인자로 파일명이 넘겨지면 

 

해당 파일을 open 하고 나서 fd(file discriptor) 를 리턴하는데  접근하려는 파일이 존재하지 않거나 다른 이유로 해당 파일을 열지 못한다면 그 땐 -1 을 리턴하게 된다.

 

그럼 다시 본론으로 돌아가서 open 함수로 '/bin/su', '/system/app/Superuser.apk' 파일 존재 여부를 확인하는 보호 로직을 Frida 로 우회하기 위해서는 위 매뉴얼의 함수 원형을 참고하여 첫번째 파라미터로 넘겨받은 파일명이 루팅 체크 관련 문자열에 해당하면 -1 을 리턴하도록 변조하면 된다. (해당 파일이 존재하지 않는 것 처럼 속이는 것)

또는 onEnter 블록에서 파일명에 해당하는 파라미터를 변조하여 우회해도 된다.

 

그런데 모든 함수가 파일명을 첫 번째 파라미터에 전달하고, 파일을 찾지 못했을때 리턴값으로 -1 을 리턴하는 것은 아니기 때문에 잘 모르는 함수가 나오면 항상 해당 함수의 원형을 확인하는 습관을 가져야 한다.

 

예로 openat 함수는 파일명을 args[1] 로 넘겨받으며, fopen 함수는 에러 발생 시 0을 리턴한다.

 

그리고 자주 쓰이는 함수들은 미리 해당 함수의 원형에 따라 미리 우회 코드를 만들어 놓으면 그때마다 넣고 빼고 할 수 있어서 더 편하게 코드를 작성할 수 있다.

 

아래부터는 앱을 분석하면서 자주 보였던 표준 C 라이브러리 함수 몇 가지를 Frida 코드로 정리한 내용이다.


1. access


 

2. faccessat


 

3. fopen


 

4. fstatat


 

5. lstat


 

6. open


 

7. openat


 

8. readlink


 

9. readlinkat


 

10. stat



1. access

// Native function access
var flag_access=0;
Interceptor.attach(Module.findExportByName(null, 'access'), {
onEnter: function (args) {
	var str = Memory.readCString(args[0]);
	flag_access = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_access = 1;
		console.log("[N] Bypassed access: "+ str);
	} else {
		console.log("[N] access: "+ str);
	}
},
onLeave: function (retval) {
	if (flag_access) {
		retval.replace(-1);
	} 	
}
});

 

2. faccessat

// Native function faccessat
var flag_faccessat=0;
Interceptor.attach(Module.findExportByName(null, 'faccessat'), {
onEnter: function (args) {
	var str = args[1].readCString();
	flag_faccessat = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_faccessat = 1;
		console.log("[N] Bypassed faccessat: "+ str);
	} else {
		console.log("[N] faccessat: "+ str);
	}
},
onLeave: function (retval) {
	//console.log(retval);
	if (flag_faccessat) {
		retval.replace(-1);
	} 	
}
});

 

3. fopen

// Native function fopen
var flag_fopen=0;
Interceptor.attach(Module.findExportByName(null, 'fopen'), {
onEnter: function (args) {
	var str = args[0].readCString();
	flag_fopen = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_fopen = 1;
		console.log("[N] Bypassed fopen: "+ str);
	} else {
		console.log("[N] fopen: "+ str);
	}
},
onLeave: function (retval) {
	//console.log(retval);
	if (flag_fopen) {
		retval.replace(0x0);
	} 	
}
});

 

4. fstatat

// Native function fstatat
var flag_fstatat=0;
Interceptor.attach(Module.findExportByName(null, 'fstatat'), {
onEnter: function (args) {
	var str = args[1].readCString();
	flag_fstatat64 = 0;

	if (arr.indexOf(str) > -1) {
		flag_fstatat64 = 1;
		console.log("[N] Bypassed fstatat: "+ str);
	} else {
		console.log("[N] fstatat: "+ str);
	}
},
onLeave: function (retval) {
	//console.log(retval);
	if (flag_fstatat64) {
		retval.replace(-1);
	} 	
}
});

 

5. lstat

// Native function lstat
var flag_lstat=0;
Interceptor.attach(Module.findExportByName(null, 'lstat'), {
onEnter: function (args) {
	var str = args[0].readCString();
	flag_lstat = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_lstat = 1;
		console.log("[N] Bypassed lstat: "+ str);
	} else {
		console.log("[N] lstat: "+ str);
	}
},
onLeave: function (retval) {
	//console.log(retval);
	if (flag_lstat) {
		retval.replace(-1);
	} 	
}
});

 

6. open 

// Native function open
var flag_open=0;
Interceptor.attach(Module.findExportByName(null, 'open'), {
onEnter: function (args) {
	var str = Memory.readCString(args[0]);
	flag_open = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_open = 1;
		console.log("[N] Bypassed open: "+ str);
	} else {
		console.log("[N] open: "+ str);
	}
},
onLeave: function (retval) {
	if (flag_open) {
		retval.replace(-1);
	} 	
}
});

 

7. openat

// Native function openat
var flag_openat=0;
Interceptor.attach(Module.findExportByName(null, 'openat'), {
onEnter: function (args) {
	var str = Memory.readCString(args[1]);
	flag_openat = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_openat = 1;
		console.log("[N] Bypassed openat: "+ str);
	} else {
		console.log("[N] openat: "+ str);
	}
},
onLeave: function (retval) {
	//console.log(retval);
	if (flag_openat) {
		retval.replace(-1);
	} 	
}
});

 

8. readlink

// Native function readlink
var flag_readlink=0;
Interceptor.attach(Module.findExportByName(null, 'readlink'), {
onEnter: function (args) {
	var str = args[0].readCString();
	flag_readlink = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_readlink = 1;
		console.log("[N] Bypassed readlink: "+ str);
	} else {
		console.log("[N] readlink: "+ str);
	}
},
onLeave: function (retval) {
	//console.log(retval);
	if (flag_readlink) {
		retval.replace(-1);
	} 	
}
});

 

9. readlinkat

// Native function readlinkat
var flag_readlinkat=0;
Interceptor.attach(Module.findExportByName(null, 'readlinkat'), {
onEnter: function (args) {
	var str = args[1].readCString();
	flag_readlinkat = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_readlinkat = 1;
		console.log("[N] Bypassed readlinkat: "+ str);
	} else {
		console.log("[N] readlinkat: "+ str);
	}
},
onLeave: function (retval) {
	//console.log(retval);
	if (flag_readlinkat) {
		retval.replace(-1);
	} 	
}
});

 

 

10. stat

// Native function stat
var flag_stat=0;
Interceptor.attach(Module.findExportByName(null, 'stat'), {
onEnter: function (args) {
	var str = args[0].readCString();
	flag_stat = 0;
	
	if (arr.indexOf(str) > -1) {
		flag_stat = 1;
		console.log("[N] Bypassed stat: "+ str);
	} else {
		console.log("[N] stat: "+ str);
	}
},
onLeave: function (retval) {
	 if (flag_stat) {
		retval.replace(-1);
	} 	
}
});

 

 

 

 

 

 

 

반응형

'모의해킹 > Android' 카테고리의 다른 글

분할 APK(Split APKs) 병합  (1) 2023.03.12
Android 7 Burp 인증서 자동 삽입 스크립트  (7) 2023.02.17
JEB Frida Code Generator  (0) 2023.02.09
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.