안드로이드 보안 강의를 들으며 루팅탐지와 우회하는 방법에 대해 알아보자.
관리자 권한(루트 권한)을 얻는 것
장점 : 커스텀 테마 적용, 통신사 어플 삭제, CPU 오버클럭으로 성능 최대화 가능
단점 : AS불가, 벽돌폰, 보안 취약
부트 로더(OS가 시동되기 전에 관련 작업을 세팅하는 과정)를 언락하여 기기 내부 메모리에 기록이 가능하도록 세팅
안드로이드 폰에 리커버리 프로그램이 내장되어 있는 것을 다른 프로그램을 대체한다. 메모리나 캐시를 조작하기 위함이다.
위 과정을 거치고 나서 관리자 권한을 얻는다.
sudo는 root가 아닌 사용자가 root 권한으로 실행하는 것.
su는 현재 계정에서 root 계정으로 전환하는 것
루팅된 디바이스의 경우, su파일 존재 여부를 확인하여 루팅 여부를 판별 할 수 있음.
busybox는 루팅된 장치에 종종 설치된다.
이 것은 linux 명령어를 제공하는 바이너리 파일이고, busybox가 실행되면 루팅된 디바이스라고 생각할 수 있다.
/// su와 busybox 탐지되는 경로 리스트
private String[] binaryPaths= {
"/data/local/",
"/data/local/bin/",
"/data/local/xbin/",
"/sbin/",
"/su/bin/",
"/system/bin/",
"/system/bin/.ext/",
"/system/bin/failsafe/",
"/system/sd/xbin/",
"/system/usr/we-need-root/",
"/system/xbin/",
"/system/app/Superuser.apk",
"/cache",
"/data",
"/dev"
};
/// su 사용 확인
private boolean checkForSuBinary() {
return checkForBinary("su"); // function is available below
}
/// su binary 파일 체크
private boolean checkForBinary(String filename) {
for (String path : binaryPaths) {
File f = new File(path, filename);
boolean fileExists = f.exists();
if (fileExists) {
return true;
}
}
return false;
}
/// busybox 사용 확인
private boolean checkForSuBinary() {
return checkForBinary("su"); // function is available below
}
/// busybox binary 파일 체크
private boolean checkSuExists() {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]
{"/system /xbin/which", "su"});
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line = in.readLine();
process.destroy();
return line != null;
} catch (Exception e) {
if (process != null) {
process.destroy();
}
return false;
}
}
루팅 관련 프로세스가 동작하고 있는지 혹은, 루팅 관련 패키지나 앱이 설치되어 있는지 확인
private void getInstallApllList(){
PackageManager packageManager = mContext.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> AppInfos = packageManager.queryIntentActivities(intent, 0);
for (ResolveInfo info : AppInfos) {
ActivityInfo ai = info.activityInfo;
// 앱 이름 탐지
Log.d("app name", ai.loadLabel(packageManager).toString());
// 앱 패키지 탐지
Log.d("app package Name",""+ai.packageName);
int resId = ai.applicationInfo.icon;
//icon
}
}
private boolean detectTestKeys() {
String buildTags = android.os.Build.TAGS;
return buildTags != null && buildTags.contains("test-keys");
}
앱을 구글 플레이 스토어를 통해 다운받고, 앱을 디컴파일하여 루팅 탐지부분을 제거하고 앱을 사용할 수도 있다. 그러므로 플레이 스토어에서 받은 상태 그대로인지 확인하는 방법이다.
void getInstaller() {
// 플레이 스토어에서 설ㅣ했을 경우, "[설치한 앱의 패키지 명]" 으로 프린트
String installer = this.getPackageManager().getInstallerPackageName(this.getPackageName());
if (installer == null) {
// developer
Log.d(TAG, "Installer package is null");
System.exit(0);
} else {
Log.d(TAG, "Installer package is not null : " + installer);
switch (checkContains(installer)) {
case "com.android.vending":// Google Play Store
case "samsungapps":// Galaxy Apps
case "packageinstaller":// Android Package Installer
case "com.skt.skaf.A000Z00040":// ONE STORE
break;
default: // 위 스토어들 제외 앱 종료
System.exit(0);
break;
}
return;
}
}
null - developer
com.android.vending - google play
com.amazon.venezia - amazon app
com.sec.android.app.samsungapps - samsung app store
samsungapps://ProductDetail/com.sec.chaton
1) Google Play Store(구글 플레이스토어)com.android.vending
2) ONE STORE(원스토어)com.skt.skaf.A000Z00040
3) Galaxy Apps(갤럭시 앱스)com.sec.android.app.samsungapps
4) Samsung Smart Switchcom.sec.android.easyMover.Agent
5) Android Package Installercom.google.android.packageinstaller
6) Samsung Mate Agentcom.samsung.android.mateagent
파일 이름을 바꿈으로써 쉽게 우회 가능
'su' -> 다른 이름으로 변경
scriptable 한 DBI(Dynamic Binary Instrumentation) 프레임워크로 JS Injection 을 통해 후킹한다.
frida 설치는 별도의 글에 정리하겠음!
설치하다 실패함 ㅎ
apk 파일로 빌드된 앱을 소스코드로 변환하는 프로그램. 컴파일의 반대.
코드 난독화 및 안드로이드 앱 용량을 줄여주는 프로그램
프로가드보다 강력한 기능을 제공하는 유료 서비스
앱에서의 무결성 검사란 앱이 누군가에 의해서 조작되었는지 검사하는 것을 뜻한다.
원본 apk파일과 비교해서 용량이 변동되지 않았는지 체크하는 방법도 있습니다.
앱을 배포하기 위해 Signing을 하게 된다. Signing시에 사용되는 키스토어 파일이 갖고 있는 키와, 이 키스토어로 서명된 앱이 가지고 있는 키를 비교하는 방법으로 무결성을 검증할 수 있다.
같은 서명 키로 앱은 서명키에 대한 Hash값이 동일하기 때문에 이를 비교하여 무결성을 검증할 수 있다. 누군가 임의로 수정 후 다시 컴파일 하였다면 키가 달라지기 때문이다.
구글 플레이 스토어 -> 앱 선택 -> 설정 -> 앱 서명 메뉴에 SHA 인증서가 있다
최초 배포시 사용한 서명 키를 추출하여 사용한다. 서명키와 업로드 키는 한 쌍이다!
private void getSignature(){
@SuppressLint("PackageManagerGetSignatures")
PackageInfo packageInfo = null;
TextView pkgInfoTxt = (TextView) findViewById(R.id.pkg_text_view);
try{
packageInfo = this.getPackageManager().getPackageInfo(this.getPackageName(), PackageManager.GET_SIGNATURE)
String sigString = "";
for(Signature sig : packageInfo.signatures){
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(sig.toByteArray());
String currentSignature = Base64.encodeToString(md.digest(), Base64.DEFAULT);
sigString += "["+currentSignature+"], ";
}
pkgInfoTxt.setText("sigs : "+sigString);
}catch(e){
e.printStackTrace();
}
}
@SuppressLint("PackageManagerGetSignatures")
private void getSignature() {
PackageInfo packageInfo = null;
//TextView pkgInfoTxt = findViewById(R.id.pkg_text_view); // 레이아웃에 정의된 TextView ID 사용
try {
packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURE);
String sigString = "";
for (Signature sig : packageInfo.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(sig.toByteArray());
String currentSignature = Base64.encodeToString(md.digest(), Base64.DEFAULT);
sigString += "[" + currentSignature + "], ";
}
Log.d(TAG, "sigs : " + sigString);
} catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
Future<void> getSignature() async {
// 키스토어 파일 경로
String keystoreFilePath = '/경로/키스토어.jks'; // 실제 경로로 변경
// 키스토어 비밀번호 및 키 비밀번호
String keystorePassword = '키스토어_비밀번호';
String keyPassword = '키_비밀번호';
// keystore 파일 읽기
File keystoreFile = File(keystoreFilePath);
List<int> keystoreBytes = await keystoreFile.readAsBytes();
// SHA-256 해시 생성
List<int> sha256Bytes = sha256.convert(keystoreBytes).bytes;
// SHA-256 해시를 문자열로 변환
String sha256String = base64.encode(sha256Bytes);
// 결과 출력
print('SHA-256 해시: $sha256String');
}