일반적인 ios 후킹 코드

console.log("[*] Started: Hooking"); 
if (ObjC.available) {
    try {
        var className = "Class 명";
        var funcName = "메소드 명";
        var hook = eval('ObjC.classes.' + className + '["' + funcName + '"]');
        var newretval = ptr('0x0');

        Interceptor.attach(hook.implementation,{
            onLeave : function(retval) {console.log("[*] Class Name : " + className);
            console.log("[*] Function Name : " + funcName);
            console.log("[+] type of return value : " + typeof retval);
            console.log("[-] return value : " + retval);
            retval.replace(newretval);
            console.log("[-] new return value : " + retval);
            
        }});}
    catch(err) { console.log("[!] Exception2: " + err.message); } } 
else { console.log("not aVailable!")}

Ida를 이용한 메모리 주소를 추가하여 해당 반환값을 변조하는 코드

var targetMobile = 'DVIA-v2';
var addr = ptr(0x1949A8);
var moduleBase = Module.getBaseAddress(targetMobile);
var targetAddress = moduleBase.add(addr);


Interceptor.attach(targetAddress, {
    onEnter: function(args) {
        console.log('Address Entered: ' +addr + 'args: '+ args[2]);
    },
    onLeave: function(retval){
        console.log('retval: '+retval);
        retval.replace(0x0);
        console.log('modify: '+ retval);
    }
});

UI dump 출처 : https://hackcatml.tistory.com/57

frida -U DVIA-v2 -l UI_dump.js

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");

 

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

palera1n ios 15~16 탈옥방법 with 맥북  (0) 2023.06.18
Tweak setting  (0) 2023.04.23
iOS Setting  (0) 2023.04.03
iOS 정적 분석  (0) 2023.03.30
iOS 진단 코드  (0) 2023.03.21

Info.plist : 번들 ID, 버전번호, 메타데이터

- iOS 앱의 중요 정보 포함

- 앱에 대한 필수 구성정보 설명

- 키-값의 구조

- 모든 번들 실행 파일 정보 포함

- XML 또는 bplist 형식

- url scheme도 존재

- 앱 권한 (UsageDescription)

-  App Transport Security(ATS)설정 ➔ NSAppTransportSecurity

 

Library/ ➔ 캐시, 환경설정, 쿠키 및 속성 목록 (plist) 구성파일

Library/Caches/ ➔ 반영구 캐시 파일로 사용불가 및 백업 없음, 저장공간 정리 시 제거

Library/Application Support/ ➔ 앱 실행에 필요한 파일, 사용자에게 안보임 컨텐츠 백업(NSURLIsExcludedFromBackupKey)

 Library/Preferences ➔ 앱 재시작시에도 유지되는 속성 BUNDLEID.plist에 암호화 되지 않은 상태로 저장 NSUserDefaults 존재

tmp/ ➔ 앱 실행에 유지할 필요가 없는 임시 파일

/private/var/mobile/Containers/Data/Application/[app uuid]/Libaray/Cookies ➔ 쿠키 경로

 

디버깅 로그 내 중요정보 노출 여부

➔ 3uTools Realtime-log

➔ /var/mobile/Containers/Data/Application/[앱uuid]

➔ sdsiosloginfo.exe -d [로그파일명]

 

Keychain

➔ 앱 실행 시 민감한 정보를 저장하는 sqlite

➔ 주요정보(인증토큰, 비밀번호) 평문 저장

https://github.com/ptoomey3/Keychain-Dumper/releases

 

Releases · ptoomey3/Keychain-Dumper

A tool to check which keychain items are available to an attacker once an iOS device has been jailbroken - ptoomey3/Keychain-Dumper

github.com

Objection

# objection을 이용한 keychain dump
pip install objection
frida-ps -Uai
objection -g [packagename] explore
ios keychain dump

++
# 추가기능
# 탈옥 탐지 로직 우회
ios jailbreak disable
# ssl pinning
ios sslpinning disable

백그라운드 이미지 내 중요 정보 저장 여부

var/mobile/Containers/Data/Application/[Bundle_id]/Library/SplashBoard/Snapshots/sceneID:com.test.test-default/*.ktx
var/mobile/Containers/Data/Application/[Bundle_id]/Library/SplashBoard/Snapshots/sceneID:com.test.test-default/downscaled/*.ktx

 

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

palera1n ios 15~16 탈옥방법 with 맥북  (0) 2023.06.18
Tweak setting  (0) 2023.04.23
iOS Setting  (0) 2023.04.03
iOS 탈옥 탐지 우회 예시 코드 [추가 업로드예정]  (0) 2023.03.30
iOS 진단 코드  (0) 2023.03.21

iOS도 기본적으로 안드로이드와 동일한 방식으로 후킹한다.

둘 다 특정 함수가 호출되는 시점을 잡기 떄문이다.

 

Hooking  시 사용하는 JAVA  API에 간략하게 설명하겠다.

setImmediate(fn): Hooking이나 함수 Holding으로 앱 실행이 늦어질 때 timeout으로 종료되는 것을 방지한다.
Java.Perform(fn): 스레드가 가상머신에 연결되었는지 확인하고 함수(fn)를 호출한다.
Java.androidVersion : 사용하고 있는 안드로이드 버전의 기기를 반환함
Java.enumerateLoadedClasses(callback): 
- onMatch(이름, 핸들):에 지정된 개체가 자바스크립트 래퍼를 가져오기 위해 use()할 수 있는 이름을 가진 로드된 각
클래스에 대해 호출되는 클래스를 열거.
- onComplete(): 모든 클래스가 열거되면 호출

 

예시1 ➔ 안드로이드 내 고유 함수 후킹

Java.perform(function(){
    const objS = Java.use("java.lang.String");
    console.log("[*] Hooking Start");

    objS.equals.overload('java.lang.String').implementation = function(arg0){
        console.log(arg0);
        var data = this.equals(arg0);
        return data;
    };
    console.log("[*] Hooking End");
});

예시 2 ➔ 클래스 메소드 순차 후킹

setTimeout(function() { // avoid java.lang.ClassNotFoundException

	Java.perform(function() {

		var hook = Java.use("루팅 탐지 Class 명"); // 루팅 탐지 클래스
		console.log("");
		console.log("info: hooking target class");
	
		// 아래에 메소드
		hook.isEmulator.overload().implementation = function() {
			console.log("info: entered target method");
			
			// obtain old retval
			var retval = this.isRooted.overload().call(this);
			console.log("old ret value: " + retval);

			// set new retval
			var retnew = false;
			console.log("new ret value: " + retnew);
			return retnew;
		}

	});   

}, 0);

예시 3 ➔ 해당 Class가 생성한 인스턴스가 생길 시 후킹

Java.perform(function() {
    Java.choose("com.examplePackage.exampleClass",
    {
        /* when instance founded, 'onMatch` function will called */
        onMatch: function(instance)
        {
            console.log("[+] Instance Found! Hook Start");
        },    

        /* when their action is finished, 'onComplete' function will called */
        onComplete: function()
        {
            console.log("[*] Instance Finished");
        }
    });
});

메모리 단 후킹 예시

libnative-lib.so 라이브러리 내에 있는 Jniint() 메소드 후킹하여 해당 메소드가 동작시 메모리 주소에 접근하여 변조한다.

후킹 메소드의 메소드만 알 경우 findExportByname(null , "fopen") 를 써도 된다.

Interceptor.attach(Module.getExportByName('libnative-lib.so', 'Jniint'),{
onEnter: function(args) {
this.first = args[0].toInt32();
console.log("on enter with : " + this.first)
},
onLeave : functin(retval){
const dstAddr = Java.vm.getEnv().newIntArray(117878);
console.log("dstAddr is : " + dstAddr.toInt32())
retval.replace(dstAddr)
}
});

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

루팅 탐지 우회 모듈  (0) 2023.04.11
Scrcpy 안드로이드 앱 화면 공유  (0) 2023.04.11
Burp 인증서 설치  (0) 2023.03.28
안드로이드 무결성 검증  (0) 2023.03.28
Activity 강제실행  (0) 2023.03.28

MITM(Man In the Middle Attack)
Android 7(API 24) 이전, Proxy(Burp) 인증서를 단말기에 설치만 하면 인증서 신뢰하였다. 하지만 Android 7 이후, OS 정책 변경으로 사용자가 설치한 루트 인증서는 신뢰하지 않음 

 

이를 해결하기 위해

루팅된 기기에서 시스템 인증서 경로로 버프 인증서 강제로 밀어넣기

openssl설치

https://slproweb.com/products/Win32OpenSSL.html

 

Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions

Minimum system requirements: Windows XP or later 32MB RAM 200MHz CPU 30MB hard drive space Recommended system requirements: Windows XP or later 128MB RAM 500MHz CPU 300MB hard drive space March 14, 2023 - OpenSSL 3.1 is available. Also, Happy Pi Day. Users

slproweb.com

Burp > Proxy > Options > Import/export CA certificate
Certificate in DER format > Next > 인증서 저장

OpenSSL 이용하여 pem 파일로 변환
openssl x509 -inform DER -in cacert.der -out cacert.pem
openssl x509 -inform PEM -subject_hash_old -in cacert.pem

#확인한 해쉬값으로 파일명 변경 후 확장자 .0 으로 변경
mv cacert.pem 9a5ba575.0

# adb remount
mount -o rw,remount /system
chmod 644 9a5ba575.0
adb push 9a5ba575.0 /system/etc/security/cacerts/
Reboot

버프 인증서 만료 날짜 360로 설정 변경

//Win64 OpenSSL v1.1.1u 설치할 것

openssl req -x509 -days 360 -nodes -newkey rsa:2048 -outform der -keyout server.key -out ca.der

openssl rsa -in server.key -inform pem -out server.key.der -outform der

openssl pkcs8 -topk8 -in server.key.der -inform der -out server.key.pkcs8.der -outform der -nocrypt

# burp에 업로드한 인증서 export로 다운로드

openssl x509 -inform DER -in cacert.der -out cacert.pem

openssl x509 -inform PEM -subject_hash_old -in cacert.pem

adb push 9a5ba575.0 /sdcard/

adb remount

mount -o rw,remount /system

chmod 644 9a5ba575.0

mv 9a5ba575.0 /system/etc/security/cacerts/


방법 2. APP 리패키징하여 사용자 인증서를 신뢰하도록 하는 옵션 추가 하여 설치

 

 


방법 3. SSL-Pinning(with. frida) (앱에서 인증서 검증을 하는 경우)

https://codeshare.frida.re/

 

Frida CodeShare

Unleash the power of Frida. "If I have seen further, it is by standing on the shoulders of giants."      -Sir Issac Newton The Frida CodeShare project is comprised of developers from around the world working together with one goal - push Frida to its l

codeshare.frida.re

들어가면 ssl pinning 관련 코드가 존재한다. 그 중에서 frida-mutiple-unpinning이 범용성이 좋다.

Project: Universal Android SSL Pinning Bypass with Frida 사용시 아래 과정으로 사용

adb push cacert.der /data/local/tmp/cert-der.crt

chmod 777 cert-der.crt

frida -U -l ssl_bypass.js [pid]
/* 
   Android SSL Re-pinning frida script v0.2 030417-pier 

   $ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt
   $ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause

   https://techblog.mediaservice.net/2017/07/universal-android-ssl-pinning-bypass-with-frida/
   
   UPDATE 20191605: Fixed undeclared var. Thanks to @oleavr and @ehsanpc9999 !
*/

setTimeout(function(){
    Java.perform(function (){
    	console.log("");
	    console.log("[.] Cert Pinning Bypass/Re-Pinning");

	    var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
	    var FileInputStream = Java.use("java.io.FileInputStream");
	    var BufferedInputStream = Java.use("java.io.BufferedInputStream");
	    var X509Certificate = Java.use("java.security.cert.X509Certificate");
	    var KeyStore = Java.use("java.security.KeyStore");
	    var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
	    var SSLContext = Java.use("javax.net.ssl.SSLContext");

	    // Load CAs from an InputStream
	    console.log("[+] Loading our CA...")
	    var cf = CertificateFactory.getInstance("X.509");
	    
	    try {
	    	var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");
	    }
	    catch(err) {
	    	console.log("[o] " + err);
	    }
	    
	    var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
	  	var ca = cf.generateCertificate(bufferedInputStream);
	    bufferedInputStream.close();

		var certInfo = Java.cast(ca, X509Certificate);
	    console.log("[o] Our CA Info: " + certInfo.getSubjectDN());

	    // Create a KeyStore containing our trusted CAs
	    console.log("[+] Creating a KeyStore for our CA...");
	    var keyStoreType = KeyStore.getDefaultType();
	    var keyStore = KeyStore.getInstance(keyStoreType);
	    keyStore.load(null, null);
	    keyStore.setCertificateEntry("ca", ca);
	    
	    // Create a TrustManager that trusts the CAs in our KeyStore
	    console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
	    var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
	    var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
	    tmf.init(keyStore);
	    console.log("[+] Our TrustManager is ready...");

	    console.log("[+] Hijacking SSLContext methods now...")
	    console.log("[-] Waiting for the app to invoke SSLContext.init()...")

	   	SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) {
	   		console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
	   		SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
	   		console.log("[+] SSLContext initialized with our custom TrustManager!");
	   	}
    });
},0);

Project: frida-multiple-unpinning code

frida ssl_pinning.js -U -f [app package 명]
/*  Android ssl certificate pinning bypass script for various methods
	by Maurizio Siddu
	
	Run with:
	frida -U -f [APP_ID] -l frida_multiple_unpinning.js --no-pause
*/

setTimeout(function() {
	Java.perform(function() {
		console.log('');
		console.log('======');
		console.log('[#] Android Bypass for various Certificate Pinning methods [#]');
		console.log('======');


		var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
		var SSLContext = Java.use('javax.net.ssl.SSLContext');
		
		// TrustManager (Android < 7) //
		////////////////////////////////
		var TrustManager = Java.registerClass({
			// Implement a custom TrustManager
			name: 'dev.asd.test.TrustManager',
			implements: [X509TrustManager],
			methods: {
				checkClientTrusted: function(chain, authType) {},
				checkServerTrusted: function(chain, authType) {},
				getAcceptedIssuers: function() {return []; }
			}
		});
		// Prepare the TrustManager array to pass to SSLContext.init()
		var TrustManagers = [TrustManager.$new()];
		// Get a handle on the init() on the SSLContext class
		var SSLContext_init = SSLContext.init.overload(
			'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
		try {
			// Override the init method, specifying the custom TrustManager
			SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
				console.log('[+] Bypassing Trustmanager (Android < 7) pinner');
				SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
			};
		} catch (err) {
			console.log('[-] TrustManager (Android < 7) pinner not found');
			//console.log(err);
		}



	
		// OkHTTPv3 (quadruple bypass) //
		/////////////////////////////////
		try {
			// Bypass OkHTTPv3 {1}
			var okhttp3_Activity_1 = Java.use('okhttp3.CertificatePinner');    
			okhttp3_Activity_1.check.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {                              
				console.log('[+] Bypassing OkHTTPv3 {1}: ' + a);
				return;
			};
		} catch (err) {
			console.log('[-] OkHTTPv3 {1} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass OkHTTPv3 {2}
			// This method of CertificatePinner.check is deprecated but could be found in some old Android apps
			var okhttp3_Activity_2 = Java.use('okhttp3.CertificatePinner');    
			okhttp3_Activity_2.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function(a, b) {
				console.log('[+] Bypassing OkHTTPv3 {2}: ' + a);
				return;
			};
		} catch (err) {
			console.log('[-] OkHTTPv3 {2} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass OkHTTPv3 {3}
			var okhttp3_Activity_3 = Java.use('okhttp3.CertificatePinner');    
			okhttp3_Activity_3.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(a, b) {
				console.log('[+] Bypassing OkHTTPv3 {3}: ' + a);
				return;
			};
		} catch(err) {
			console.log('[-] OkHTTPv3 {3} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass OkHTTPv3 {4}
			var okhttp3_Activity_4 = Java.use('okhttp3.CertificatePinner');    
			//okhttp3_Activity_4['check$okhttp'].implementation = function(a, b) {
			okhttp3_Activity_4.check$okhttp.overload('java.lang.String', 'kotlin.jvm.functions.Function0').implementation = function(a, b) {		
				console.log('[+] Bypassing OkHTTPv3 {4}: ' + a);
				return;
			};
		} catch(err) {
			console.log('[-] OkHTTPv3 {4} pinner not found');
			//console.log(err);
		}

	

	
		// Trustkit (triple bypass) //
		//////////////////////////////
		try {
			// Bypass Trustkit {1}
			var trustkit_Activity_1 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');
			trustkit_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(a, b) {
				console.log('[+] Bypassing Trustkit {1}: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Trustkit {1} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass Trustkit {2}
			var trustkit_Activity_2 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');
			trustkit_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(a, b) {
				console.log('[+] Bypassing Trustkit {2}: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Trustkit {2} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass Trustkit {3}
			var trustkit_PinningTrustManager = Java.use('com.datatheorem.android.trustkit.pinning.PinningTrustManager');
			trustkit_PinningTrustManager.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String').implementation = function(chain, authType) {
				console.log('[+] Bypassing Trustkit {3}');
				//return;
			};
		} catch (err) {
			console.log('[-] Trustkit {3} pinner not found');
			//console.log(err);
		}
		
	
	
  
		// TrustManagerImpl (Android > 7) //
		////////////////////////////////////
		try {
			// Bypass TrustManagerImpl (Android > 7) {1}
			var array_list = Java.use("java.util.ArrayList");
			var TrustManagerImpl_Activity_1 = Java.use('com.android.org.conscrypt.TrustManagerImpl');
			TrustManagerImpl_Activity_1.checkTrustedRecursive.implementation = function(certs, ocspData, tlsSctData, host, clientAuth, untrustedChain, trustAnchorChain, used) {
				console.log('[+] Bypassing TrustManagerImpl (Android > 7) checkTrustedRecursive check: '+ host);
				return array_list.$new();
			};
		} catch (err) {
			console.log('[-] TrustManagerImpl (Android > 7) checkTrustedRecursive check not found');
			//console.log(err);
		}  
		try {
			// Bypass TrustManagerImpl (Android > 7) {2} (probably no more necessary)
			var TrustManagerImpl_Activity_2 = Java.use('com.android.org.conscrypt.TrustManagerImpl');
			TrustManagerImpl_Activity_2.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
				console.log('[+] Bypassing TrustManagerImpl (Android > 7) verifyChain check: ' + host);
				return untrustedChain;
			};   
		} catch (err) {
			console.log('[-] TrustManagerImpl (Android > 7) verifyChain check not found');
			//console.log(err);
		}

  
  
		

		// Appcelerator Titanium PinningTrustManager //
		///////////////////////////////////////////////
		try {
			var appcelerator_PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');
			appcelerator_PinningTrustManager.checkServerTrusted.implementation = function(chain, authType) {
				console.log('[+] Bypassing Appcelerator PinningTrustManager');
				return;
			};
		} catch (err) {
			console.log('[-] Appcelerator PinningTrustManager pinner not found');
			//console.log(err);
		}




		// Fabric PinningTrustManager //
		////////////////////////////////
		try {
			var fabric_PinningTrustManager = Java.use('io.fabric.sdk.android.services.network.PinningTrustManager');
			fabric_PinningTrustManager.checkServerTrusted.implementation = function(chain, authType) {
				console.log('[+] Bypassing Fabric PinningTrustManager');
				return;
			};
		} catch (err) {
			console.log('[-] Fabric PinningTrustManager pinner not found');
			//console.log(err);
		}




		// OpenSSLSocketImpl Conscrypt (double bypass) //
		/////////////////////////////////////////////////
		try {
			var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
			OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, JavaObject, authMethod) {
				console.log('[+] Bypassing OpenSSLSocketImpl Conscrypt {1}');
			};
		} catch (err) {
			console.log('[-] OpenSSLSocketImpl Conscrypt {1} pinner not found');
			//console.log(err);        
		}
		try {
			var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');
			OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certChain, authMethod) {
				console.log('[+] Bypassing OpenSSLSocketImpl Conscrypt {2}');
			};
		} catch (err) {
			console.log('[-] OpenSSLSocketImpl Conscrypt {2} pinner not found');
			//console.log(err);        
		}




		// OpenSSLEngineSocketImpl Conscrypt //
		///////////////////////////////////////
		try {
			var OpenSSLEngineSocketImpl_Activity = Java.use('com.android.org.conscrypt.OpenSSLEngineSocketImpl');
			OpenSSLEngineSocketImpl_Activity.verifyCertificateChain.overload('[Ljava.lang.Long;', 'java.lang.String').implementation = function(a, b) {
				console.log('[+] Bypassing OpenSSLEngineSocketImpl Conscrypt: ' + b);
			};
		} catch (err) {
			console.log('[-] OpenSSLEngineSocketImpl Conscrypt pinner not found');
			//console.log(err);
		}




		// OpenSSLSocketImpl Apache Harmony //
		//////////////////////////////////////
		try {
			var OpenSSLSocketImpl_Harmony = Java.use('org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl');
			OpenSSLSocketImpl_Harmony.verifyCertificateChain.implementation = function(asn1DerEncodedCertificateChain, authMethod) {
				console.log('[+] Bypassing OpenSSLSocketImpl Apache Harmony');
			};
		} catch (err) {
			console.log('[-] OpenSSLSocketImpl Apache Harmony pinner not found');
			//console.log(err);      
		}




		// PhoneGap sslCertificateChecker //
		////////////////////////////////////
		try {
			var phonegap_Activity = Java.use('nl.xservices.plugins.sslCertificateChecker');
			phonegap_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function(a, b, c) {
				console.log('[+] Bypassing PhoneGap sslCertificateChecker: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] PhoneGap sslCertificateChecker pinner not found');
			//console.log(err);
		}




		// IBM MobileFirst pinTrustedCertificatePublicKey (double bypass) //
		////////////////////////////////////////////////////////////////////
		try {
			// Bypass IBM MobileFirst {1}
			var WLClient_Activity_1 = Java.use('com.worklight.wlclient.api.WLClient');
			WLClient_Activity_1.getInstance().pinTrustedCertificatePublicKey.overload('java.lang.String').implementation = function(cert) {
				console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {1}: ' + cert);
				return;
			};
			} catch (err) {
			console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {1} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass IBM MobileFirst {2}
			var WLClient_Activity_2 = Java.use('com.worklight.wlclient.api.WLClient');
			WLClient_Activity_2.getInstance().pinTrustedCertificatePublicKey.overload('[Ljava.lang.String;').implementation = function(cert) {
				console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {2}: ' + cert);
				return;
			};
		} catch (err) {
			console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {2} pinner not found');
			//console.log(err);
		}




		// IBM WorkLight (ancestor of MobileFirst) HostNameVerifierWithCertificatePinning (quadruple bypass) //
		///////////////////////////////////////////////////////////////////////////////////////////////////////
		try {
			// Bypass IBM WorkLight {1}
			var worklight_Activity_1 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
			worklight_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function(a, b) {
				console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {1}: ' + a);                
				return;
			};
		} catch (err) {
			console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {1} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass IBM WorkLight {2}
			var worklight_Activity_2 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
			worklight_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(a, b) {
				console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {2}: ' + a);
				return;
			};
		} catch (err) {
			console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {2} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass IBM WorkLight {3}
			var worklight_Activity_3 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
			worklight_Activity_3.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;').implementation = function(a, b) {
				console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {3}: ' + a);
				return;
			};
		} catch (err) {
			console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {3} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass IBM WorkLight {4}
			var worklight_Activity_4 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');
			worklight_Activity_4.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(a, b) {
				console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {4}: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {4} pinner not found');
			//console.log(err);
		}




		// Conscrypt CertPinManager //
		//////////////////////////////
		try {
			var conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager');
			conscrypt_CertPinManager_Activity.checkChainPinning.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
				console.log('[+] Bypassing Conscrypt CertPinManager: ' + a);
				//return;
				return true;
			};
		} catch (err) {
			console.log('[-] Conscrypt CertPinManager pinner not found');
			//console.log(err);
		}
		
		


		// Conscrypt CertPinManager (Legacy) //
		///////////////////////////////////////
		try {
			var legacy_conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager');
			legacy_conscrypt_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
				console.log('[+] Bypassing Conscrypt CertPinManager (Legacy): ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Conscrypt CertPinManager (Legacy) pinner not found');
			//console.log(err);
		}

			   


		// CWAC-Netsecurity (unofficial back-port pinner for Android<4.2) CertPinManager //
		///////////////////////////////////////////////////////////////////////////////////
		try {
			var cwac_CertPinManager_Activity = Java.use('com.commonsware.cwac.netsecurity.conscrypt.CertPinManager');
			cwac_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
				console.log('[+] Bypassing CWAC-Netsecurity CertPinManager: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] CWAC-Netsecurity CertPinManager pinner not found');
			//console.log(err);
		}




		// Worklight Androidgap WLCertificatePinningPlugin //
		/////////////////////////////////////////////////////
		try {
			var androidgap_WLCertificatePinningPlugin_Activity = Java.use('com.worklight.androidgap.plugin.WLCertificatePinningPlugin');
			androidgap_WLCertificatePinningPlugin_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function(a, b, c) {
				console.log('[+] Bypassing Worklight Androidgap WLCertificatePinningPlugin: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Worklight Androidgap WLCertificatePinningPlugin pinner not found');
			//console.log(err);
		}




		// Netty FingerprintTrustManagerFactory //
		//////////////////////////////////////////
		try {
			var netty_FingerprintTrustManagerFactory = Java.use('io.netty.handler.ssl.util.FingerprintTrustManagerFactory');
			//NOTE: sometimes this below implementation could be useful 
			//var netty_FingerprintTrustManagerFactory = Java.use('org.jboss.netty.handler.ssl.util.FingerprintTrustManagerFactory');
			netty_FingerprintTrustManagerFactory.checkTrusted.implementation = function(type, chain) {
				console.log('[+] Bypassing Netty FingerprintTrustManagerFactory');
			};
		} catch (err) {
			console.log('[-] Netty FingerprintTrustManagerFactory pinner not found');
			//console.log(err);
		}




		// Squareup CertificatePinner [OkHTTP<v3] (double bypass) //
		////////////////////////////////////////////////////////////
		try {
			// Bypass Squareup CertificatePinner  {1}
			var Squareup_CertificatePinner_Activity_1 = Java.use('com.squareup.okhttp.CertificatePinner');
			Squareup_CertificatePinner_Activity_1.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function(a, b) {
				console.log('[+] Bypassing Squareup CertificatePinner {1}: ' + a);
				return;
			};
		} catch (err) {
			console.log('[-] Squareup CertificatePinner {1} pinner not found');
			//console.log(err);
		}
		try {
			// Bypass Squareup CertificatePinner {2}
			var Squareup_CertificatePinner_Activity_2 = Java.use('com.squareup.okhttp.CertificatePinner');
			Squareup_CertificatePinner_Activity_2.check.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
				console.log('[+] Bypassing Squareup CertificatePinner {2}: ' + a);
				return;
			};
		} catch (err) {
			console.log('[-] Squareup CertificatePinner {2} pinner not found');
			//console.log(err);
		}




		// Squareup OkHostnameVerifier [OkHTTP v3] (double bypass) //
		/////////////////////////////////////////////////////////////
		try {
			// Bypass Squareup OkHostnameVerifier {1}
			var Squareup_OkHostnameVerifier_Activity_1 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');
			Squareup_OkHostnameVerifier_Activity_1.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(a, b) {
				console.log('[+] Bypassing Squareup OkHostnameVerifier {1}: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Squareup OkHostnameVerifier check not found');
			//console.log(err);
		}    
		try {
			// Bypass Squareup OkHostnameVerifier {2}
			var Squareup_OkHostnameVerifier_Activity_2 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');
			Squareup_OkHostnameVerifier_Activity_2.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(a, b) {
				console.log('[+] Bypassing Squareup OkHostnameVerifier {2}: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Squareup OkHostnameVerifier check not found');
			//console.log(err);
		}


		

		// Android WebViewClient (quadruple bypass) //
		//////////////////////////////////////////////
		try {
			// Bypass WebViewClient {1} (deprecated from Android 6)
			var AndroidWebViewClient_Activity_1 = Java.use('android.webkit.WebViewClient');
			AndroidWebViewClient_Activity_1.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function(obj1, obj2, obj3) {
				console.log('[+] Bypassing Android WebViewClient check {1}');
			};
		} catch (err) {
			console.log('[-] Android WebViewClient {1} check not found');
			//console.log(err)
		}
		try {
			// Bypass WebViewClient {2}
			var AndroidWebViewClient_Activity_2 = Java.use('android.webkit.WebViewClient');
			AndroidWebViewClient_Activity_2.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function(obj1, obj2, obj3) {
				console.log('[+] Bypassing Android WebViewClient check {2}');
			};
		} catch (err) {
			console.log('[-] Android WebViewClient {2} check not found');
			//console.log(err)
		}
		try {
			// Bypass WebViewClient {3}
			var AndroidWebViewClient_Activity_3 = Java.use('android.webkit.WebViewClient');
			AndroidWebViewClient_Activity_3.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function(obj1, obj2, obj3, obj4) {
				console.log('[+] Bypassing Android WebViewClient check {3}');
			};
		} catch (err) {
			console.log('[-] Android WebViewClient {3} check not found');
			//console.log(err)
		}
		try {
			// Bypass WebViewClient {4}
			var AndroidWebViewClient_Activity_4 = Java.use('android.webkit.WebViewClient');
			AndroidWebViewClient_Activity_4.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function(obj1, obj2, obj3) {
				console.log('[+] Bypassing Android WebViewClient check {4}');
			};
		} catch (err) {
			console.log('[-] Android WebViewClient {4} check not found');
			//console.log(err)
		}
		



		// Apache Cordova WebViewClient //
		//////////////////////////////////
		try {
			var CordovaWebViewClient_Activity = Java.use('org.apache.cordova.CordovaWebViewClient');
			CordovaWebViewClient_Activity.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function(obj1, obj2, obj3) {
				console.log('[+] Bypassing Apache Cordova WebViewClient check');
				obj3.proceed();
			};
		} catch (err) {
			console.log('[-] Apache Cordova WebViewClient check not found');
			//console.log(err);
		}




		// Boye AbstractVerifier //
		///////////////////////////
		try {
			var boye_AbstractVerifier = Java.use('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier');
			boye_AbstractVerifier.verify.implementation = function(host, ssl) {
				console.log('[+] Bypassing Boye AbstractVerifier check: ' + host);
			};
		} catch (err) {
			console.log('[-] Boye AbstractVerifier check not found');
			//console.log(err);
		}




		// Apache AbstractVerifier //
		/////////////////////////////
		try {
			var apache_AbstractVerifier = Java.use('org.apache.http.conn.ssl.AbstractVerifier');
			apache_AbstractVerifier.verify.implementation = function(a, b, c, d) {
				console.log('[+] Bypassing Apache AbstractVerifier check: ' + a);
				return;
			};
		} catch (err) {
			console.log('[-] Apache AbstractVerifier check not found');
			//console.log(err);
		}




		// Chromium Cronet //
		/////////////////////    
		try {
			var CronetEngineBuilderImpl_Activity = Java.use("org.chromium.net.impl.CronetEngineBuilderImpl");
			// Setting argument to TRUE (default is TRUE) to disable Public Key pinning for local trust anchors
			CronetEngine_Activity.enablePublicKeyPinningBypassForLocalTrustAnchors.overload('boolean').implementation = function(a) {
				console.log("[+] Disabling Public Key pinning for local trust anchors in Chromium Cronet");
				var cronet_obj_1 = CronetEngine_Activity.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true);
				return cronet_obj_1;
			};
			// Bypassing Chromium Cronet pinner
			CronetEngine_Activity.addPublicKeyPins.overload('java.lang.String', 'java.util.Set', 'boolean', 'java.util.Date').implementation = function(hostName, pinsSha256, includeSubdomains, expirationDate) {
				console.log("[+] Bypassing Chromium Cronet pinner: " + hostName);
				var cronet_obj_2 = CronetEngine_Activity.addPublicKeyPins.call(this, hostName, pinsSha256, includeSubdomains, expirationDate);
				return cronet_obj_2;
			};
		} catch (err) {
			console.log('[-] Chromium Cronet pinner not found')
			//console.log(err);
		}



		// Flutter Pinning packages http_certificate_pinning and ssl_pinning_plugin (double bypass) //
		//////////////////////////////////////////////////////////////////////////////////////////////
		try {
			// Bypass HttpCertificatePinning.check {1}
			var HttpCertificatePinning_Activity = Java.use('diefferson.http_certificate_pinning.HttpCertificatePinning');
			HttpCertificatePinning_Activity.checkConnexion.overload("java.lang.String", "java.util.List", "java.util.Map", "int", "java.lang.String").implementation = function (a, b, c ,d, e) {
				console.log('[+] Bypassing Flutter HttpCertificatePinning : ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Flutter HttpCertificatePinning pinner not found');
			//console.log(err);
		}
		try {
			// Bypass SslPinningPlugin.check {2}
			var SslPinningPlugin_Activity = Java.use('com.macif.plugin.sslpinningplugin.SslPinningPlugin');
			SslPinningPlugin_Activity.checkConnexion.overload("java.lang.String", "java.util.List", "java.util.Map", "int", "java.lang.String").implementation = function (a, b, c ,d, e) {
				console.log('[+] Bypassing Flutter SslPinningPlugin: ' + a);
				return true;
			};
		} catch (err) {
			console.log('[-] Flutter SslPinningPlugin pinner not found');
			//console.log(err);
		}



		
		// Dynamic SSLPeerUnverifiedException Patcher                                //
		// An useful technique to bypass SSLPeerUnverifiedException failures raising //
		// when the Android app uses some uncommon SSL Pinning methods or an heavily //
		// code obfuscation. Inspired by an idea of: https://github.com/httptoolkit  //
		///////////////////////////////////////////////////////////////////////////////
		function rudimentaryFix(typeName) {
			// This is a improvable rudimentary fix, if not works you can patch it manually
			if (typeName === undefined){
				return;
			} else if (typeName === 'boolean') {
				return true;
			} else {
				return null;
			}
		}
		try {
			var UnverifiedCertError = Java.use('javax.net.ssl.SSLPeerUnverifiedException');
			UnverifiedCertError.$init.implementation = function (str) {
				console.log('\x1b[36m[!] Unexpected SSLPeerUnverifiedException occurred, trying to patch it dynamically...\x1b[0m');
				try {
					var stackTrace = Java.use('java.lang.Thread').currentThread().getStackTrace();
					var exceptionStackIndex = stackTrace.findIndex(stack =>
						stack.getClassName() === "javax.net.ssl.SSLPeerUnverifiedException"
					);
					// Retrieve the method raising the SSLPeerUnverifiedException
					var callingFunctionStack = stackTrace[exceptionStackIndex + 1];
					var className = callingFunctionStack.getClassName();
					var methodName = callingFunctionStack.getMethodName();
					var callingClass = Java.use(className);
					var callingMethod = callingClass[methodName];
					console.log('\x1b[36m[!] Attempting to bypass uncommon SSL Pinning method on: '+className+'.'+methodName+'\x1b[0m');					
					// Skip it when already patched by Frida
					if (callingMethod.implementation) {
						return; 
					}
					// Trying to patch the uncommon SSL Pinning method via implementation
					var returnTypeName = callingMethod.returnType.type;
					callingMethod.implementation = function() {
						rudimentaryFix(returnTypeName);
					};
				} catch (e) {
					// Dynamic patching via implementation does not works, then trying via function overloading
					//console.log('[!] The uncommon SSL Pinning method has more than one overload); 
					if (String(e).includes(".overload")) {
						var splittedList = String(e).split(".overload");
						for (let i=2; i<splittedList.length; i++) {
							var extractedOverload = splittedList[i].trim().split("(")[1].slice(0,-1).replaceAll("'","");
							// Check if extractedOverload has multiple arguments
							if (extractedOverload.includes(",")) {
								// Go here if overloaded method has multiple arguments (NOTE: max 6 args are covered here)
								var argList = extractedOverload.split(", ");
								console.log('\x1b[36m[!] Attempting overload of '+className+'.'+methodName+' with arguments: '+extractedOverload+'\x1b[0m');
								if (argList.length == 2) {
									callingMethod.overload(argList[0], argList[1]).implementation = function(a,b) {
										rudimentaryFix(returnTypeName);
									}
								} else if (argNum == 3) {
									callingMethod.overload(argList[0], argList[1], argList[2]).implementation = function(a,b,c) {
										rudimentaryFix(returnTypeName);
									}
								}  else if (argNum == 4) {
									callingMethod.overload(argList[0], argList[1], argList[2], argList[3]).implementation = function(a,b,c,d) {
										rudimentaryFix(returnTypeName);
									}
								}  else if (argNum == 5) {
									callingMethod.overload(argList[0], argList[1], argList[2], argList[3], argList[4]).implementation = function(a,b,c,d,e) {
										rudimentaryFix(returnTypeName);
									}
								}  else if (argNum == 6) {
									callingMethod.overload(argList[0], argList[1], argList[2], argList[3], argList[4], argList[5]).implementation = function(a,b,c,d,e,f) {
										rudimentaryFix(returnTypeName);
									}
								} 
							// Go here if overloaded method has a single argument
							} else {
								callingMethod.overload(extractedOverload).implementation = function(a) {
									rudimentaryFix(returnTypeName);
								}
							}
						}
					} else {
						console.log('\x1b[36m[-] Failed to dynamically patch SSLPeerUnverifiedException '+e+'\x1b[0m');
					}
				}
				//console.log('\x1b[36m[+] SSLPeerUnverifiedException hooked\x1b[0m');
				return this.$init(str);
			};
		} catch (err) {
			//console.log('\x1b[36m[-] SSLPeerUnverifiedException not found\x1b[0m');
			//console.log('\x1b[36m'+err+'\x1b[0m');
		}
		


	 
	});
	
}, 0);

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

Scrcpy 안드로이드 앱 화면 공유  (0) 2023.04.11
Frida Hooking  (0) 2023.03.29
안드로이드 무결성 검증  (0) 2023.03.28
Activity 강제실행  (0) 2023.03.28
Snapshot  (0) 2023.03.23

무결성 검증

앱에 보안을 테스트 할 때 중요한 것을 뽑으라고 한다면 아마 코드 변조를 탐지하는 위변조 탐지를 뽑을 수 있을 것이다.

위변조 탐지는 루팅 우회등 앱을 변조하여 악성 앱을 만들 수 있기 때문에 위변조 테스트가 필요하다.

이를 위해서는 가장 먼저 해볼 수 있는 부분은

앱 이름 변조 혹은 Toast 메세지를 띄우는 것이다.

앱 이름의 경우는 AndroidManifest.xml 내 label를 수정하거나 아니면 다른 곳에서 String을 가져올 경우 String 값을 변조하면 된다.

 

두번째는 Smali Code 변조이다

Smali Code 란 Smali는 dalvik에서 사용하는 dex 형식의 어셈블리어이다. 즉 Dex는 기계어로 구성되어 있기 때문에 이것을 쉽게 읽기 해주는 것이 Smali Code이다.

이를 아래와 같이 코드를 앱이 처음 실행되는 oncreate 부분을 찾아 삽입하게 되면 위변조 테스트가 가능하다.

const/4 v0, 0x1
 
const-string v1, "Integrity Check"
 
invoke-static {p0, v1, v0}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
 
move-result-object v0
 
invoke-virtual {v0}, Landroid/widget/Toast;->show()V

진단

MainActivity 뿐만 아니라 모든 실행 로직에서 무결성 검증을 실시 해야한다.

앱 내에서 함수 실행을 통한 무결성 검증은 변조가 가능함으로 고정된 hash 값 계산 및 서버의 값과 비교하는 것이

안전하다.

대응방안

코드 난독화, 암호화
Signing Key 확인, 런타임 시 Signing 인증서를 확인하여 위변조된 앱인지 확인
Apk 파일의 해쉬(hash) 계산하여 서버의 값과 비교로 확인
중요 로직 실행 전, 후 메소드 해쉬(hash) 비교로 확인

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

Frida Hooking  (0) 2023.03.29
Burp 인증서 설치  (0) 2023.03.28
Activity 강제실행  (0) 2023.03.28
Snapshot  (0) 2023.03.23
burp 인증서 안드로이드 with NOX  (0) 2023.03.22

 

 

안드로이드 경우 각 화면마다 Activity가 존재한다.

웹처럼 각각의 페이지 번호라고 생각하면 편할 것이다.

그럼 만약 ACtivity 강제실행이 왜 취약하다고 할 수 있을까.

만약 루팅을 한 폰이 있는 경우 앱 개발자는 루팅 폰이 앱에 접근하는 것을 막을 것이다.

왜냐하면 높은 권한으로 앱 변조나 앱에 올라간 메모리의 데이터에 직접적으로 간접하여

문제를 일으킬 가능성이 있기 때문이다.

그래서 루팅 감지나 디버깅 감지를 하게되는데 이를 앱이 실행할떄 바로 시작하도록 한다.

그런데 만약 첫번째(루팅탐지)를 건너뛰고 다음 단계로 나아가게 된다면 문제가 발생하게 된다.

루팅 탐지가 간단히 우회되는 것이다.

 

 

우선 jadx로 apk 파일을 열면 코드의 구성을 보여주는데 맨처음 AnroidManifest.xml이 열릴 것이다.

해당 코드는 맨처음 어떤 Activity가 실행되는지 나오는 곳이다.

보게 되면 uk.rossmarks.fridalab.MainActivity가 먼저 사용되게 된다.

여기서는 간단한 앱이기 떄문에 activity가 하나 뿐이지만 다른데에는 여러개가 존재한다.

+ 추가적으로 Activity  내 True로 exported가 설정되어있다면 강제 호출이 가능하다. false더라도 실행되는 경우가 있으니 실행 해볼 것.

android:exported="true"

그러면 adb 내 있는 activity 강제 호출 명령어를 쓰면 1번단계를 거치지 않고도 다음 2단계를 넘어갈 수 있다.

 

현재 실행중인 Activity 호출 명령어

adb shell "dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'"

adb shell dumpsys window windows | grep FocusedApp

Activity 강제 호출 (둘 중 하나 사용해 보면 될겁니다. 저는 첫번째만 써지네요)

Activity 강제호출
adb shell am start -n [com.package.name]/[com.Activity.Name]

adb shell su -c am start -n [com.package.name]/[com.Activity.Name]

Activity 강제종료
adb shell am force-stop [com.package.name]

 

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

Burp 인증서 설치  (0) 2023.03.28
안드로이드 무결성 검증  (0) 2023.03.28
Snapshot  (0) 2023.03.23
burp 인증서 안드로이드 with NOX  (0) 2023.03.22
[Android] 안드로이드 디컴파일 및 Smali 코드 분석  (0) 2023.03.09

+ Recent posts