당분간 모바일 해킹을 할 시간이 부족해 당장 기억나는 부분들로만 정리한 내용 추가 예정

Android 구조 및 동작이해

Android 진단 환경 구축

ADB

SDK 설치 (Android Studio 설치하면 알아서 깔림)

For Mac 터미널에서 code ~/.zshrc 입력 후 아래와 같이 환경 변수 등록

KeyStore

Android Studio

[File -> New Project -> Build] 메뉴에서 아래와 같이 진행

KeyTool

keytool -genkey -v -keystore dogyun.keystore -alias dogyun -keyalg RSA -keysize 2048 -validity 10000 명령어 입력 후 생성

Proxy

낮은 버전의 경우

단순히 127.0.0.1:burp_port 들어가서 인증서 다운로드 후 Astro에서 인증서 설치 진행

높은 버전의 경우

  1. Burp Suite [Proxy] - [Options] - [Import/export certificate] 기능을 통해 인증서 파일을 추출
  2. openssl x509 -inform DER -in cacert.cer -out cacert.pem pem 변환
  3. openssl x509 -inform PEM -subject_hash_old -in cacert.pem 해쉬값 추출
  4. mv cacert.pem 9a5ba575.0 파일명 해쉬값으로 변경
  5. adb push 9a5ba575.0 /data/local/tmp
  6. mount -o rw,remount / 쓰기권한 마운트 시키기
  7. cp 9a5ba575.0 /system/etc/security/cacerts/
    1. ls -l /system/etc/security/cacerts/ | grep 9a5ba575
  8. mount -o ro,remount /
  9. [설정] - [생체 인식 및 보안] - [기타 보안 설정] - [인증서 확인] 에서 확인

정적 분석

APK 추출

  1. ASTRO 백업 기능 이용
  2. 플레이스토어에서 APK 다운로드
  3. APK Extractor 이용

디컴파일

  1. jadx APK 파일 그냥 jadx에 넣으면 됨
  2. APK Tool 이용 https://github.com/iBotPeaches/Apktool/releases/tag/v2.8.0
    java -jar apktool_2.8.0.jar d "디컴파일 할 앱 이름"
    

  3. dex2jar 이용 https://github.com/pxb1988/dex2jar/releases/tag/v2.1
    # Windows의 경우 bat 파일로 돌리면 됨
    ./d2j-dex2jar.sh apk
    

동적 분석

Python3 Android.py --type 1 --app Test --script hook.js

import argparse
import frida
import sys
import time

from pynput import keyboard


def on_message(message, data):
    if message['type'] == 'send':
        print(message['payload'])
    elif message['type'] == 'error':
        print(message['stack'])

def get_messages_from_js(message, data):
    print(message['payload'])

def get_script(script_name):
    with open("./"+script_name, 'r') as f:
        script = f.read()
    return script

help_script = """
HELP
"""

parser = argparse.ArgumentParser(description=help_script)
parser.add_argument('--script', required=True, help='JS File to Inject')
parser.add_argument('--app', required=True, help='JS File to Inject')
parser.add_argument('--type', required=False, help='JS File to Inject')
args = parser.parse_args()

# 대상 앱의 패키지 이름 또는 식별값
target_app_identifier = args.app.split('/')[0]
target_app_name = args.app.split('/')[1]
target_script = args.script

# Frida 디바이스에 연결
device = frida.get_usb_device()

# 스크립트 로드 함수
def load_script(target_script):
    global process
    global waiting_for_key
    # 스크립트 로드
    script = process.create_script(get_script(target_script))
    script.on('message', on_message)
    script.load()
    
    time.sleep(3)
    waiting_for_key = False

# 앱이 이미 실행 중인지 확인하는 함수
def is_app_running(app_identifier):
    processes = device.enumerate_processes()
    for process in processes:
        # if process.name == "카카오페이" or process.name == "com.kakaopay.app":
        #     print(process.name)
        if process.name == app_identifier:
            return process.pid
    return False

# 앱이 이미 실행 중인 경우 중지하고 spawn하는 함수
def spawn_app(app_identifier, app_name):
    global process
    isRun = is_app_running(app_name)
    if isRun:
        print("앱이 이미 실행 중입니다. 중지시킵니다.")
        device.kill(isRun)
        time.sleep(2)  # 2초 동안 앱 초기화 대기
    
    print("앱을 spawn합니다.")
    pid = device.spawn([app_identifier], timeout=10)
    time.sleep(2)  # 2초 동안 앱 초기화 대기
    process = device.attach(pid)

    # Process resume (실행시킴.)
    device.resume(pid)

    # 스크립트 로드
    load_script(target_script)

    sys.stdin.read()

# 앱 attach 하기
def attach_app(app_identifier):
    global process
    pid = is_app_running(app_identifier)
    print("pid : ", pid, "에 붙습니다.")
    process = device.attach(pid)
        
    # 스크립트 로드
    
    load_script(target_script)

    sys.stdin.read()

### 메인 함수 ###
# 메인 함수에서 비동기 이벤트 루프 실행
def main():
    frida_type = args.type

    if frida_type == '1' or frida_type == 'spawn':
        spawn_app(target_app_identifier, target_app_name)
    elif frida_type == '2' or frida_type == 'attach':
        attach_app(target_app_identifier)
    else:
        print('Injection Type error: ' + frida_type)

if __name__ == "__main__":
    main()

메모리 관련

fridump

# string 뽑기
Python3 fridump3.py -u pid -s

AM

  1. Manifest에서 debugging이 되는지 확인
  2. App pid 정보 확인 (ps | grep diva) 및 am dump

실시간 메모리 후킹

function memoryScan() {
    try {
	  var search_string = ['']; // 찾을 패턴의 문자열 Find string ex) ['a'] Or ['a','b']
        var Modify_string = ['']; // 변조할 문자열 Modify string ex) ['a'] Or ['a','b']
        var patched = false;
		
        search_string.forEach(function (patt, index) {
            var pattern = patt
                .split('')
                .map(char => char.charCodeAt(0).toString(16))
                .join(' ');
			
            Process.enumerateRanges('rw-', {
                onMatch: function (range) {

                    var result = Memory.scanSync(range.base, range.size, pattern); // 패턴 직전 메모리를 원하면 4번째 인자 추가
                    if (result.length > 0) {
					
                        result.forEach(function (match) {
							console.log("");
					console.log("\x1b[31m" + '[*] Scan String: ' + patt + "\x1b[0m");
                            console.log("\x1b[36m" + '[*] String Address: ' + match.address + "\x1b[0m");									                     
                            console.log(hexdump(match.address, { offset: 0, length: 128 }));
                            var mempatch = Modify_string[index];

                            var hexData = [];
                            for (var i = 0; i < mempatch.length; i++) {
                                var hexChar = '0x' + mempatch.charCodeAt(i).toString(16);
                                hexData.push(hexChar);
                            }

                            var nullBytesToAdd = patt.length - mempatch.length;

                            for (var i = 0; i < nullBytesToAdd; i++) {
                                hexData.push('0x00');
                            }

                            Memory.writeByteArray(match.address, hexData);
                            console.log("\x1b[33m" + '[*] Patch Address: ' + match.address + "\x1b[0m");
                            console.log("\x1b[32m[*] Before Patch: " + patt + '   \x1b[0m------>  ' + "\x1b[32mAfter Patch: " + mempatch + "\x1b[0m");
                            console.log(hexdump(match.address, { offset: 0, length: 32 }));
                            patched = true; 
                        });
                    }
                },
                onComplete: function () {}
            });
        });
    } catch (e) {
    }
}
setInterval(memory, 1000);

Trace

Function Trace

function trace(pattern)
{
	var type = (pattern.toString().indexOf("!") === -1) ? "java" : "module";

	if (type === "module") {

		// trace Module
		var res = new ApiResolver("module");
		var matches = res.enumerateMatchesSync(pattern);
		var targets = uniqBy(matches, JSON.stringify);
		targets.forEach(function(target) {
			traceModule(target.address, target.name);
		});

	} else if (type === "java") {

		// trace Java Class
		var found = false;
		Java.enumerateLoadedClasses({
			onMatch: function(aClass) {
				if (pattern.test(aClass)) {
					found = true;
					// console.log(aClass)
					// var className = aClass.match(/[L](.*);/)[1].replace(/\//g, ".");
					if(aClass.includes("."))traceClass(aClass);
				}
			},
			onComplete: function() {}
		});

		// trace Java Method
		if (!found) {
			try {
				traceMethod(pattern);
			}
			catch(err) { // catch non existing classes/methods
				console.error(err);
			}
		}
	}
}

// find and trace all methods declared in a Java Class
function traceClass(targetClass)
{
	var hook = Java.use(targetClass);
	var methods = hook.class.getDeclaredMethods();
	hook.$dispose;

	var parsedMethods = [];
	methods.forEach(function(method) {
		// console.log(method)
		parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
	});

	var targets = uniqBy(parsedMethods, JSON.stringify);
    // console.log("\n[*] " + targetClass)
	targets.forEach(function(targetMethod) {
        // console.log("\t[*] " + targetMethod)
		if(!targetMethod.includes("getGidForName")&&!targetMethod.includes("myPid"))traceMethod(targetClass + "." + targetMethod);
	});
}

// trace a specific Java Method
function traceMethod(targetClassMethod)
{
	var delim = targetClassMethod.lastIndexOf(".");
	if (delim === -1) return;

	var targetClass = targetClassMethod.slice(0, delim)
	var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)

	var hook = Java.use(targetClass);
	var overloadCount = 0;
    try {
        overloadCount = hook[targetMethod].overloads.length;
    } catch (error) {
        overloadCount = 0;
    }

	console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");

	for (var i = 0; i < overloadCount; i++) {
        hook[targetMethod].overloads[i].implementation = function() {
            console.warn("\n*** entered " + targetClassMethod);
            // console.log(hook.$new().label.value)
            // print backtrace
            // Java.perform(function() {
            //	var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
            //	console.log("\nBacktrace:\n" + bt);
            // });   

            // print args
            if (arguments.length) console.log();
            for (var j = 0; j < arguments.length; j++) {
                // if(typeof(arguments[j])=="object"){
                // 	// console.log(Java.cast(arguments[j].value, Java.use("java.util.List")));
                // 	console.log("arg[" + j + "]: " + JSON.stringify(arguments[j]));
                // 	// console.log(arguments[j].$className)
                // 	// if(arguments[j].$className=="b.f4sQ"){
                // 	// 	console.log(arguments[j].toString())
                // 	// }
                // }else{
                // 	console.log("arg[" + j + "]: " + arguments[j].value);
                // }
                console.log("arg[" + j + "]: " + arguments[j]);
            }

            // print retval
            // var retval = this[targetMethod].apply(this, arguments);
            if(/blockingGetToken/.test(targetMethod)){
                // retval='cyP0v_0qRAqLtt5N11szK2:APA91bGGUKTmB0YADn-QOIjyoLJV1o1ZxB5PNQ7bxMhqc-rkP0ADx-gYr3d7vbfW_6-zs6THyUWDvQzTmxoYJwTgJzWFwsZDPf3xI5evZHzRVgL0pOo0LYKWYpRgeSxxl4slPehpmJoV'
            }
         // rare crash (Frida bug?)
            // if(typeof(retval)=="object"){
            // 	console.log("\nretval: " + JSON.stringify(retval));
            // }else{
            // 	console.log("\nretval: " + retval);
            // }
            // console.log("\nretval: " + retval);
            console.warn("\n*** exiting " + targetClassMethod);
            // return retval;
        }
	}
}

// trace Module functions
function traceModule(impl, name)
{
	console.log("Tracing " + name);

	Interceptor.attach(impl, {

		onEnter: function(args) {

			// debug only the intended calls
			this.flag = false;
			// var filename = Memory.readCString(ptr(args[0]));
			// if (filename.indexOf("XYZ") === -1 && filename.indexOf("ZYX") === -1) // exclusion list
			// if (filename.indexOf("my.interesting.file") !== -1) // inclusion list
				this.flag = true;

			if (this.flag) {
				console.warn("\n*** entered " + name);

				// print backtrace
				console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE)
						.map(DebugSymbol.fromAddress).join("\n"));
			}
		},

		onLeave: function(retval) {

			if (this.flag) {
				// print retval
				console.log("\nretval: " + retval);
				console.warn("\n*** exiting " + name);
			}
		}

	});
}

// remove duplicates from array
function uniqBy(array, key)
{
        var seen = {};
        return array.filter(function(item) {
                var k = key(item);
                return seen.hasOwnProperty(k) ? false : (seen[k] = true);
        });
}

// ctrace("/com.test.test.class*/")
function ctrace(pattern){
    setTimeout(function() {
        Java.perform(function () {
            trace(pattern)
        })
    },0)
}

Stack Trace

function arrange(stack) {
	var ret = "";
	for(var i = 0; i < stack.length; ++i)
		ret += stack[i].toString() + "\n";  
	
	return ret;
}

Java.perform(function() {
	var Thread = Java.use("java.lang.Thread");
	var ThreadInstance = Thread.$new();
	var stack = ThreadInstance.currentThread().getStackTrace();
	
	YourClass.Method.implementation = function() {
		var full_call_stack = arrange(stack);
		console.log("Call Stack is : " + full_call_stack); 
	}
})
var ThreadDef = Java.use('java.lang.Thread');
var ThreadObj = ThreadDef.$new();
function stackTrace() {
    var stack = ThreadObj.currentThread().getStackTrace();
    for (var i = 0; i < stack.length; i++) {
        console.log(i + " => " + stack[i].toString());
    }
    console.log("-------------------------------------");
}

Java.perform(function() {
	YourClass.Method.implementation = function() {
		stackTrace();
	}
})

ClassLoader

Basic

Java.perform(function() {
	for(var i=0;i<Java.enumerateClassLoadersSync().length;i++){
		var classLoaderToUse = Java.enumerateClassLoadersSync()[i];
		console.log(`[${i+1}] ${classLoaderToUse}`)
		Java.classFactory.loader = classLoaderToUse;
		try{
			// Hooking Code
		}catch (error){
		}
	}
})

InMemoryDexDump

function InMemoryDexDump(){
    console.log("[*] In Memory Dex Dump v0.1 - @cryptax");
    var count = 0
    Java.perform(function() {
        var memoryclassLoader = Java.use("dalvik.system.InMemoryDexClassLoader");
        memoryclassLoader.$init.overload('java.nio.ByteBuffer', 'java.lang.ClassLoader').implementation = function(dexbuffer, loader) {
            console.log("[*] Hooking InMemoryDexClassLoader");
            count += 1
            var object = this.$init(dexbuffer, loader);
            /* dexbuffer is a Java ByteBuffer
               you cannot dump to /sdcard unless the app has rights to
             */
            var remaining = dexbuffer.remaining();
            const filename = `/data/data/com.dunamu.ustockplus.android/dump${count}.dex`;
            
            console.log("[*] Opening file name=" + filename + " to write " + remaining + " bytes");
            const f = new File(filename, 'wb');
            var buf = new Uint8Array(remaining);
            for (var i = 0; i < remaining; i++) {
                buf[i] = dexbuffer.get();
                //debug: console.log("buf["+i+"]="+buf[i]);
            }
            console.log("[*] Writing " + remaining + " bytes...");
            f.write(buf);
            f.close();
            // checking
            remaining = dexbuffer.remaining();
            if (remaining > 0) {
                console.log("[-] Error: There are " + remaining + " remaining bytes!");
            } else {
                console.log("[+] Dex dumped successfully in " + filename);
            }

            return object;
        }
    });
}

SSL Pinning

function SSLPinning(){
    /**@@@+++@@@@******************************************************************
     **
    ** Android SSL Pinning frida script v1.0 hyugogirubato
    **
    ** frida -D "DEVICE" -l "pinning.js" -f "PACKAGE"
    **
    ** Update: Dynamic error support.
    **
    ***@@@---@@@@******************************************************************
    */

    // Custom params
    const MODE = {
        SSLPeerUnverifiedException: true,
        HttpsURLConnection: true,
        SSLContext: true,
        TrustManagerImpl: true,
        OkHTTPv3: true,
        Trustkit: true,
        TitaniumPinningTrustManager: true,
        FabricPinningTrustManager: true,
        ConscryptOpenSSLSocketImpl: true,
        ConscryptOpenSSLEngineSocketImpl: true,
        ApacheOpenSSLSocketImpl: true,
        PhoneGapsslCertificateChecker: true,
        IBMMobileFirst: true,
        IBMWorkLight: true,
        ConscryptCertPinManager: true,
        NetsecurityCertPinManager: true,
        AndroidgapWorkLight: true,
        NettyFingerprintTrustManagerFactory: true,
        SquareupCertificatePinner: true,
        SquareupOkHostnameVerifier: true,
        AndroidWebViewClient: true,
        ApacheWebViewClient: true,
        BoyeAbstractVerifier: true,
        ApacheAbstractVerifier: true,
        Appmattus: true,
        ChromiumCronet: true,
        Flutter: true
    };


    let index = 0; // color index
    const COLORS = {
        red: '\x1b[31m',
        green: '\x1b[32m',
        yellow: '\x1b[33m',
        blue: '\x1b[34m',
        magenta: '\x1b[35m',
        cyan: '\x1b[36m',
        reset: '\x1b[0m'
    };

    const randomColor = () => {
        const colorKeys = Object.keys(COLORS).filter(key => key !== "reset" && key !== "red");
        index = (index + 1) % colorKeys.length;
        return COLORS[colorKeys[index]];
    }


    const rudimentaryFix = (typeName) => {
        if (typeName === "boolean") {
            return true;
        } else if (typeName !== "void") {
            return null;
        }
    }

    const loadJava = (library) => {
        try {
            return Java.use(library);
        } catch (e) {
            return undefined;
        }
    }


    setTimeout(function () {
        console.log("---");
        console.log("Capturing Android app...");
        if (Java.available) {
            console.log("[*] Java available");
            Java.perform(function () {

                    if (MODE.SSLPeerUnverifiedException) {
                        const colorKey = randomColor();
                        try {
                            const UnverifiedCertError = Java.use("javax.net.ssl.SSLPeerUnverifiedException");
                            UnverifiedCertError.$init.implementation = function (str) {
                                console.log(`${COLORS.red}[!] Unexpected SSLPeerUnverifiedException occurred, trying to patch it dynamically...${COLORS.reset}`);

                                let className, methodName, callingMethod, returnTypeName;
                                try {
                                    const stackTrace = Java.use("java.lang.Thread").currentThread().getStackTrace();
                                    const exceptionStackIndex = stackTrace.findIndex(stack =>
                                        stack.getClassName() === "javax.net.ssl.SSLPeerUnverifiedException"
                                    );

                                    if (exceptionStackIndex === -1) {
                                        console.log(`${COLORS.yellow}[-] SSLPeerUnverifiedException not found in the stack trace.${COLORS.reset}`);
                                        return this.$init(str);
                                    }

                                    // Retrieve the method raising the SSLPeerUnverifiedException
                                    const callingFunctionStack = stackTrace[exceptionStackIndex + 1];
                                    className = callingFunctionStack.getClassName();
                                    methodName = callingFunctionStack.getMethodName();
                                    const callingClass = Java.use(className);
                                    callingMethod = callingClass[methodName];

                                    // Skip it when already patched by Frida
                                    if (callingMethod.implementation) {
                                        return;
                                    }

                                    // Trying to patch the uncommon SSL Pinning method via implementation
                                    returnTypeName = callingMethod.returnType.type;
                                    callingMethod.implementation = function () {
                                        rudimentaryFix(returnTypeName);
                                    };
                                    console.log(`${colorKey}  --> SSLPeerUnverifiedException [${className}.${methodName}]${COLORS.reset}`);
                                } catch (e) {
                                    // Dynamic patching via implementation does not works, then trying via function overloading
                                    console.log(`${COLORS.red}[!] The uncommon SSL Pinning method has more than one overload${COLORS.reset}`);
                                    if (String(e).includes(".overload")) {
                                        const splittedList = String(e).split(".overload");
                                        for (let i = 2; i < splittedList.length; i++) {
                                            const 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)
                                                const argList = extractedOverload.split(", ");

                                                // Overload the method based on the number of arguments
                                                callingMethod.overload(...argList).implementation = function (...args) {
                                                    rudimentaryFix(returnTypeName);
                                                };
                                                // Go here if overloaded method has a single argument
                                            } else {
                                                callingMethod.overload(extractedOverload).implementation = function (a) {
                                                    rudimentaryFix(returnTypeName);
                                                };
                                            }
                                        }
                                        console.log(`${colorKey}  --> SSLPeerUnverifiedException [${className}.${methodName}]${COLORS.reset}`);
                                    } else {
                                        console.log(`${COLORS.red}[!] Failed to dynamically patch SSLPeerUnverifiedException ${e}${COLORS.reset}`);
                                    }
                                }
                                return this.$init(str);
                            }
                        } catch (e) {
                            console.log(`${COLORS.red}[!] Failed to dynamically patch SSLPeerUnverifiedException ${e}${COLORS.reset}`);
                        }
                    }

                    if (MODE.HttpsURLConnection) {
                        const colorKey = randomColor();
                        const HttpsURLConnection = loadJava("javax.net.ssl.HttpsURLConnection");
                        try {
                            HttpsURLConnection.setDefaultHostnameVerifier.implementation = function (hostnameVerifier) {
                                console.log(`${colorKey}  --> HttpsURLConnection [DefaultHostnameVerifier]${COLORS.reset}`);
                            };
                            console.log("[+] HttpsURLConnection [DefaultHostnameVerifier]");
                        } catch (e) {
                            console.log("[ ] HttpsURLConnection [DefaultHostnameVerifier]");
                        }

                        try {
                            HttpsURLConnection.setSSLSocketFactory.implementation = function (SSLSocketFactory) {
                                console.log(`${colorKey}  --> HttpsURLConnection [SSLSocketFactory]${COLORS.reset}`);
                            };
                            console.log("[+] HttpsURLConnection [SSLSocketFactory]");
                        } catch (e) {
                            console.log("[ ] HttpsURLConnection [SSLSocketFactory]");
                        }

                        try {
                            HttpsURLConnection.setHostnameVerifier.implementation = function (hostnameVerifier) {
                                console.log(`${colorKey}  --> HttpsURLConnection [HostnameVerifier]${COLORS.reset}`);
                            };
                            console.log("[+] HttpsURLConnection [HostnameVerifier]");
                        } catch (e) {
                            console.log("[ ] HttpsURLConnection [HostnameVerifier]");
                        }

                    }

                    if (MODE.SSLContext) {
                        // TrustManager (Android < 7)
                        const colorKey = randomColor();
                        try {
                            const X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
                            const SSLContext = Java.use("javax.net.ssl.SSLContext");

                            const 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()
                            const TrustManagers = [TrustManager.$new()];
                            // Get a handle on the init() on the SSLContext class
                            const SSLContext_init = SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom");
                            // Override the init method, specifying the custom TrustManager
                            SSLContext_init.implementation = function (keyManager, trustManager, secureRandom) {
                                console.log(`${colorKey}  --> TrustManager [SSLContext] (Android < 7)${COLORS.reset}`);
                                SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
                            };
                            console.log("[+] TrustManager [SSLContext] (Android < 7)");
                        } catch (e) {
                            console.log("[ ] TrustManager [SSLContext] (Android < 7)");
                        }
                    }

                    if (MODE.TrustManagerImpl) {
                        // TrustManagerImpl (Android > 7)
                        const colorKey = randomColor();
                        const TrustManagerImpl = loadJava("com.android.org.conscrypt.TrustManagerImpl");
                        try {
                            const ArrayList = Java.use("java.util.ArrayList");
                            TrustManagerImpl.checkTrustedRecursive.implementation = function (certs, ocspData, tlsSctData, host, clientAuth, untrustedChain, trustAnchorChain, used) {
                                console.log(`${colorKey}  --> TrustManagerImpl [TrustedRecursive] (Android > 7): ${host}${COLORS.reset}`);
                                return ArrayList.$new();
                            };
                            console.log("[+] TrustManagerImpl [TrustedRecursive] (Android > 7)");
                        } catch (e) {
                            console.log("[ ] TrustManagerImpl [TrustedRecursive] (Android > 7)");
                        }

                        try {
                            TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
                                console.log(`${colorKey}  --> TrustManagerImpl [verifyChain] (Android > 7): ${host}${COLORS.reset}`);
                                return untrustedChain;
                            };
                            console.log("[+] TrustManagerImpl [verifyChain] (Android > 7)");
                        } catch (e) {
                            console.log("[ ] TrustManagerImpl [verifyChain] (Android > 7)");
                        }
                    }

                    if (MODE.OkHTTPv3) {
                        const colorKey = randomColor();
                        const CertificatePinner = loadJava("okhttp3.CertificatePinner");
                        try {
                            CertificatePinner.check.overload("java.lang.String", "java.util.List").implementation = function (a, b) {
                                console.log(`${colorKey}  --> OkHTTPv3 [List]: ${a}${COLORS.reset}`);
                            };
                            console.log("[+] OkHTTPv3 [List]");
                        } catch (e) {
                            console.log("[ ] OkHTTPv3 [List]");
                        }

                        try {
                            CertificatePinner.check.overload("java.lang.String", "java.security.cert.Certificate").implementation = function (a, b) {
                                console.log(`${colorKey}  --> OkHTTPv3 [Certificate]: ${a}${COLORS.reset}`);
                            };
                            console.log("[+] OkHTTPv3 [Certificate]");
                        } catch (e) {
                            console.log("[ ] OkHTTPv3 [Certificate]");
                        }

                        try {
                            CertificatePinner.check.overload("java.lang.String", "[Ljava.security.cert.Certificate;").implementation = function (a, b) {
                                console.log(`${colorKey}  --> OkHTTPv3 [Array]: ${a}${COLORS.reset}`);
                            };
                            console.log("[+] OkHTTPv3 [Array]");
                        } catch (e) {
                            console.log("[ ] OkHTTPv3 [Array]");
                        }

                        try {
                            CertificatePinner.check$okhttp.overload("java.lang.String", "kotlin.jvm.functions.Function0").implementation = function (a, b) {
                                console.log(`${colorKey}  --> OkHTTPv3 [Function]: ${a}${COLORS.reset}`);
                            };
                            console.log("[+] OkHTTPv3 [Function]");
                        } catch (e) {
                            console.log("[ ] OkHTTPv3 [Function]");
                        }
                    }

                    if (MODE.Trustkit) {
                        const colorKey = randomColor();
                        const OkHostnameVerifier = loadJava("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier");
                        const PinningTrustManager = loadJava("com.datatheorem.android.trustkit.pinning.PinningTrustManager");
                        try {
                            OkHostnameVerifier.verify.overload("java.lang.String", "javax.net.ssl.SSLSession").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Trustkit OkHostnameVerifier [SSLSession]: ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Trustkit OkHostnameVerifier [SSLSession]");
                        } catch (e) {
                            console.log("[ ] Trustkit OkHostnameVerifier [SSLSession]");
                        }

                        try {
                            OkHostnameVerifier.verify.overload("java.lang.String", "java.security.cert.X509Certificate").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Trustkit OkHostnameVerifier [X509Certificate]: ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Trustkit OkHostnameVerifier [X509Certificate]");
                        } catch (e) {
                            console.log("[ ] Trustkit OkHostnameVerifier [X509Certificate]");
                        }

                        try {
                            PinningTrustManager.checkServerTrusted.overload("[Ljava.security.cert.X509Certificate;", "java.lang.String").implementation = function (chain, authType) {
                                console.log(`${colorKey}  --> Trustkit PinningTrustManager${COLORS.reset}`);
                            };
                            console.log("[+] Trustkit PinningTrustManager");
                        } catch (e) {
                            console.log("[ ] Trustkit PinningTrustManager");
                        }
                    }

                    if (MODE.TitaniumPinningTrustManager) {
                        const colorKey = randomColor();
                        const PinningTrustManager = loadJava("appcelerator.https.PinningTrustManager");
                        try {
                            PinningTrustManager.checkServerTrusted.implementation = function (chain, authType) {
                                console.log(`${colorKey}  --> Titanium [PinningTrustManager]${COLORS.reset}`);
                            };
                            console.log("[+] Titanium [PinningTrustManager]");
                        } catch (e) {
                            console.log("[ ] Titanium [PinningTrustManager]");
                        }
                    }

                    if (MODE.FabricPinningTrustManager) {
                        const colorKey = randomColor();
                        const PinningTrustManager = loadJava("io.fabric.sdk.android.services.network.PinningTrustManager");
                        try {
                            PinningTrustManager.checkServerTrusted.implementation = function (chain, authType) {
                                console.log(`${colorKey}  --> Fabric [PinningTrustManager]${COLORS.reset}`);
                            };
                            console.log("[+] Fabric [PinningTrustManager]");
                        } catch (e) {
                            console.log("[ ] Fabric [PinningTrustManager]");
                        }
                    }

                    if (MODE.ConscryptOpenSSLSocketImpl) {
                        const colorKey = randomColor();
                        const OpenSSLSocketImpl = loadJava("com.android.org.conscrypt.OpenSSLSocketImpl");
                        try {
                            OpenSSLSocketImpl.verifyCertificateChain.implementation = function (certRefs, JavaObject, authMethod) {
                                console.log(`${colorKey}  --> Conscrypt [OpenSSLSocketImpl] (Refs)${COLORS.reset}`);
                            };
                            console.log("[+] Conscrypt [OpenSSLSocketImpl] (Refs)");
                        } catch (e) {
                            console.log("[ ] Conscrypt [OpenSSLSocketImpl] (Refs)");
                        }

                        try {
                            OpenSSLSocketImpl.verifyCertificateChain.implementation = function (certChain, authMethod) {
                                console.log(`${colorKey}  --> Conscrypt [OpenSSLSocketImpl] (Chain)${COLORS.reset}`);
                            };
                            console.log("[+] Conscrypt [OpenSSLSocketImpl] (Chain)");
                        } catch (e) {
                            console.log("[ ] Conscrypt [OpenSSLSocketImpl] (Chain)");
                        }
                    }

                    if (MODE.ConscryptOpenSSLEngineSocketImpl) {
                        const colorKey = randomColor();
                        const OpenSSLEngineSocketImpl = loadJava("com.android.org.conscrypt.OpenSSLEngineSocketImpl");
                        try {
                            OpenSSLEngineSocketImpl.verifyCertificateChain.overload("[Ljava.lang.Long;", "java.lang.String").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Conscrypt [OpenSSLEngineSocketImpl]: ${b}${COLORS.reset}`);
                            };
                            console.log("[+] Conscrypt [OpenSSLEngineSocketImpl]");
                        } catch (e) {
                            console.log("[ ] Conscrypt [OpenSSLEngineSocketImpl]");
                        }
                    }

                    if (MODE.ApacheOpenSSLSocketImpl) {
                        const colorKey = randomColor();
                        const OpenSSLSocketImpl = loadJava("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
                        try {
                            OpenSSLSocketImpl.verifyCertificateChain.implementation = function (asn1DerEncodedCertificateChain, authMethod) {
                                console.log(`${colorKey}  --> Apache [OpenSSLSocketImpl]${COLORS.reset}`);
                            };
                            console.log("[+] Apache [OpenSSLSocketImpl]");
                        } catch (e) {
                            console.log("[ ] Apache [OpenSSLSocketImpl]");
                        }
                    }

                    if (MODE.PhoneGapsslCertificateChecker) {
                        const colorKey = randomColor();
                        const sslCertificateChecker = loadJava("nl.xservices.plugins.sslCertificateChecker");
                        try {
                            sslCertificateChecker.execute.overload("java.lang.String", "org.json.JSONArray", "org.apache.cordova.CallbackContext").implementation = function (a, b, c) {
                                console.log(`${colorKey}  --> PhoneGap [sslCertificateChecker]: ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] PhoneGap [sslCertificateChecker]");
                        } catch (e) {
                            console.log("[ ] PhoneGap [sslCertificateChecker]");
                        }
                    }

                    if (MODE.IBMMobileFirst) {
                        const colorKey = randomColor();
                        const MobileFirst = loadJava("com.worklight.wlclient.api.WLClient");
                        try {
                            MobileFirst.getInstance().pinTrustedCertificatePublicKey.overload("java.lang.String").implementation = function (cert) {
                                console.log(`${colorKey}  --> IBM [MobileFirst] (String): ${cert}${COLORS.reset}`);
                            };
                            console.log("[+] IBM [MobileFirst] (String)");
                        } catch (e) {
                            console.log("[ ] IBM [MobileFirst] (String)");
                        }

                        try {
                            MobileFirst.getInstance().pinTrustedCertificatePublicKey.overload("[Ljava.lang.String;").implementation = function (cert) {
                                console.log(`${colorKey}  --> IBM [MobileFirst] (Array): ${cert}${COLORS.reset}`);
                            };
                            console.log("[+] IBM [MobileFirst] (Array)");
                        } catch (e) {
                            console.log("[ ] IBM [MobileFirst] (Array)");
                        }
                    }

                    if (MODE.IBMWorkLight) {
                        const colorKey = randomColor();
                        const WorkLight = loadJava("com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning");
                        try {
                            WorkLight.verify.overload("java.lang.String", "javax.net.ssl.SSLSocket").implementation = function (a, b) {
                                console.log(`${colorKey}  --> IBM [WorkLight] (SSLSocket): ${a}${COLORS.reset}`);
                            };
                            console.log("[+] IBM [WorkLight] (SSLSocket)");
                        } catch (e) {
                            console.log("[ ] IBM [WorkLight] (SSLSocket)");
                        }

                        try {
                            WorkLight.verify.overload("java.lang.String", "java.security.cert.X509Certificate").implementation = function (a, b) {
                                console.log(`${colorKey}  --> IBM [WorkLight] (X509Certificate): ${a}${COLORS.reset}`);
                            };
                            console.log("[+] IBM [WorkLight] (X509Certificate)");
                        } catch (e) {
                            console.log("[ ] IBM [WorkLight] (X509Certificate)");
                        }

                        try {
                            WorkLight.verify.overload("java.lang.String", "[Ljava.lang.String;", "[Ljava.lang.String;").implementation = function (a, b) {
                                console.log(`${colorKey}  --> IBM [WorkLight] (String): ${a}${COLORS.reset}`);
                            };
                            console.log("[+] IBM [WorkLight] (String)");
                        } catch (e) {
                            console.log("[ ] IBM [WorkLight] (String)");
                        }

                        try {
                            WorkLight.verify.overload("java.lang.String", "javax.net.ssl.SSLSession").implementation = function (a, b) {
                                console.log(`${colorKey}  --> IBM [WorkLight] (SSLSession): ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] IBM [WorkLight] (SSLSession)");
                        } catch (e) {
                            console.log("[ ] IBM [WorkLight] (SSLSession)");
                        }
                    }

                    if (MODE.ConscryptCertPinManager) {
                        const colorKey = randomColor();
                        const CertPinManager = loadJava("com.android.org.conscrypt.CertPinManager");
                        try {
                            CertPinManager.checkChainPinning.overload("java.lang.String", "java.util.List").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Conscrypt [CertPinManager] (List): ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Conscrypt [CertPinManager] (List)");
                        } catch (e) {
                            console.log("[ ] Conscrypt [CertPinManager] (List)");
                        }

                        try {
                            CertPinManager.isChainValid.overload("java.lang.String", "java.util.List").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Conscrypt [CertPinManager] (Legacy): ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Conscrypt [CertPinManager] (Legacy)");
                        } catch (e) {
                            console.log("[ ] Conscrypt [CertPinManager] (Legacy)");
                        }
                    }

                    if (MODE.NetsecurityCertPinManager) {
                        const colorKey = randomColor();
                        const CertPinManager = loadJava("com.commonsware.cwac.netsecurity.conscrypt.CertPinManager");
                        try {
                            CertPinManager.isChainValid.overload("java.lang.String", "java.util.List").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Netsecurity [CertPinManager]: ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Netsecurity [CertPinManager]");
                        } catch (e) {
                            console.log("[ ] Netsecurity [CertPinManager]");
                        }
                    }

                    if (MODE.AndroidgapWorkLight) {
                        const colorKey = randomColor();
                        const Worklight = loadJava("com.worklight.androidgap.plugin.WLCertificatePinningPlugin");
                        try {
                            Worklight.execute.overload("java.lang.String", "org.json.JSONArray", "org.apache.cordova.CallbackContext").implementation = function (a, b, c) {
                                console.log(`${colorKey}  --> Android [WorkLight]: ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Android [WorkLight]");
                        } catch (e) {
                            console.log("[ ] Android [WorkLight]");
                        }
                    }

                    if (MODE.NettyFingerprintTrustManagerFactory) {
                        const colorKey = randomColor();
                        const FingerprintTrustManagerFactory = loadJava("io.netty.handler.ssl.util.FingerprintTrustManagerFactory");
                        try {
                            FingerprintTrustManagerFactory.checkTrusted.implementation = function (type, chain) {
                                console.log(`${colorKey}  --> Netty [FingerprintTrustManagerFactory]${COLORS.reset}`);
                            };
                            console.log("[+] Netty [FingerprintTrustManagerFactory]");
                        } catch (e) {
                            console.log("[ ] Netty [FingerprintTrustManagerFactory]");
                        }
                    }

                    if (MODE.SquareupCertificatePinner) {
                        // OkHTTP < v3
                        const colorKey = randomColor();
                        const CertificatePinner = loadJava("com.squareup.okhttp.CertificatePinner");
                        try {
                            CertificatePinner.check.overload("java.lang.String", "java.security.cert.Certificate").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Squareup [CertificatePinner] (Certificate): ${a}${COLORS.reset}`);
                            };
                            console.log("[+] Squareup [CertificatePinner] (Certificate)");
                        } catch (e) {
                            console.log("[ ] Squareup [CertificatePinner] (Certificate)");
                        }

                        try {
                            CertificatePinner.check.overload("java.lang.String", "java.util.List").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Squareup [CertificatePinner] (List): ${a}${COLORS.reset}`);
                            };
                            console.log("[+] Squareup [CertificatePinner] (List)");
                        } catch (e) {
                            console.log("[ ] Squareup [CertificatePinner] (List)");
                        }
                    }

                    if (MODE.SquareupOkHostnameVerifier) {
                        // OkHTTP v3
                        const colorKey = randomColor();
                        const OkHostnameVerifier = loadJava("com.squareup.okhttp.internal.tls.OkHostnameVerifier");
                        try {
                            OkHostnameVerifier.verify.overload("java.lang.String", "java.security.cert.X509Certificate").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Squareup [OkHostnameVerifier] (X509Certificate): ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Squareup [OkHostnameVerifier] (X509Certificate)");
                        } catch (e) {
                            console.log("[ ] Squareup [OkHostnameVerifier] (X509Certificate)");
                        }

                        try {
                            OkHostnameVerifier.verify.overload("java.lang.String", "javax.net.ssl.SSLSession").implementation = function (a, b) {
                                console.log(`${colorKey}  --> Squareup [OkHostnameVerifier] (SSLSession): ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Squareup [OkHostnameVerifier] (SSLSession)");
                        } catch (e) {
                            console.log("[ ] Squareup [OkHostnameVerifier] (SSLSession)");
                        }
                    }

                    if (MODE.AndroidWebViewClient) {
                        const colorKey = randomColor();
                        const WebViewClient = loadJava("android.webkit.WebViewClient");

                        try {
                            WebViewClient.onReceivedSslError.overload("android.webkit.WebView", "android.webkit.SslErrorHandler", "android.net.http.SslError").implementation = function (obj1, obj2, obj3) {
                                console.log(`${colorKey}  --> Android [WebViewClient] (SslErrorHandler)${COLORS.reset}`);
                            };
                            console.log("[+] Android [WebViewClient] (SslErrorHandler)");
                        } catch (e) {
                            console.log("[ ] Android [WebViewClient] (SslErrorHandler)");
                        }

                        try {
                            WebViewClient.onReceivedSslError.overload("android.webkit.WebView", "android.webkit.WebResourceRequest", "android.webkit.WebResourceError").implementation = function (obj1, obj2, obj3) {
                                console.log(`${colorKey}  --> Android [WebViewClient] (SSLWebResourceError)${COLORS.reset}`);
                            };
                            console.log("[+] Android [WebViewClient] (SSLWebResourceError)");
                        } catch (e) {
                            console.log("[ ] Android [WebViewClient] (SSLWebResourceError)");
                        }

                        try {
                            WebViewClient.onReceivedError.overload("android.webkit.WebView", "int", "java.lang.String", "java.lang.String").implementation = function (obj1, obj2, obj3, obj4) {
                                console.log(`${colorKey}  --> Android [WebViewClient] (String)${COLORS.reset}`);
                            };
                            console.log("[+] Android [WebViewClient] (String)");
                        } catch (e) {
                            console.log("[ ] Android [WebViewClient] (String)");
                        }

                        try {
                            WebViewClient.onReceivedError.overload("android.webkit.WebView", "android.webkit.WebResourceRequest", "android.webkit.WebResourceError").implementation = function (obj1, obj2, obj3) {
                                console.log(`${colorKey}  --> Android [WebViewClient] (WebResourceError)${COLORS.reset}`);
                            };
                            console.log("[+] Android [WebViewClient] (WebResourceError)");
                        } catch (e) {
                            console.log("[ ] Android [WebViewClient] (WebResourceError)");
                        }
                    }

                    if (MODE.ApacheWebViewClient) {
                        const colorKey = randomColor();
                        const CordovaWebViewClient = loadJava("org.apache.cordova.CordovaWebViewClient");
                        try {
                            CordovaWebViewClient.onReceivedSslError.overload("android.webkit.WebView", "android.webkit.SslErrorHandler", "android.net.http.SslError").implementation = function (obj1, obj2, obj3) {
                                console.log(`${colorKey}  --> Apache [WebViewClient]${COLORS.reset}`);
                                obj3.proceed();
                            };
                            console.log("[+] Apache [WebViewClient]");
                        } catch (e) {
                            console.log("[ ] Apache [WebViewClient]");
                        }
                    }

                    if (MODE.BoyeAbstractVerifier) {
                        const colorKey = randomColor();
                        const AbstractVerifier = loadJava("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
                        try {
                            AbstractVerifier.verify.implementation = function (host, ssl) {
                                console.log(`${colorKey}  --> Boye [AbstractVerifier]: ${host}${COLORS.reset}`);
                            };
                            console.log("[+] Boye [AbstractVerifier]");
                        } catch (e) {
                            console.log("[ ] Boye [AbstractVerifier]");
                        }
                    }

                    if (MODE.ApacheAbstractVerifier) {
                        const colorKey = randomColor();
                        const AbstractVerifier = loadJava("org.apache.http.conn.ssl.AbstractVerifier");
                        try {
                            AbstractVerifier.verify.implementation = function (a, b, c, d) {
                                console.log(`${colorKey}  --> Apache [AbstractVerifier]: ${a}${COLORS.reset}`);
                            };
                            console.log("[+] Apache [AbstractVerifier]");
                        } catch (e) {
                            console.log("[ ] Apache [AbstractVerifier]");
                        }
                    }

                    if (MODE.Appmattus) {
                        const colorKey = randomColor();
                        const Transparency = loadJava("com.appmattus.certificatetransparency.internal.verifier.CertificateTransparencyInterceptor");
                        try {
                            Transparency.intercept.implementation = function (a) {
                                console.log(`${colorKey}  --> Appmattus [Transparency]${COLORS.reset}`);
                                return a.proceed(a.request());
                            };
                            console.log("[+] Appmattus [Transparency]");
                        } catch (e) {
                            console.log("[ ] Appmattus [Transparency]");
                        }
                    }

                    if (MODE.ChromiumCronet) {
                        const colorKey = randomColor();
                        const CronetEngineBuilderImpl = loadJava("org.chromium.net.impl.CronetEngineBuilderImpl");
                        try {
                            CronetEngineBuilderImpl.enablePublicKeyPinningBypassForLocalTrustAnchors.overload("boolean").implementation = function (a) {
                                console.log(`${colorKey}  --> Chromium [CronetEngineBuilderImpl] (LocalTrustAnchors)${COLORS.reset}`);
                                return CronetEngineBuilderImpl.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true);
                            };
                            console.log("[+] Chromium [CronetEngineBuilderImpl] (LocalTrustAnchors)");
                        } catch (e) {
                            console.log("[ ] Chromium [CronetEngineBuilderImpl] (LocalTrustAnchors)");
                        }

                        try {
                            CronetEngineBuilderImpl.addPublicKeyPins.overload("java.lang.String", "java.util.Set", "boolean", "java.util.Date").implementation = function (hostName, pinsSha256, includeSubdomains, expirationDate) {
                                console.log(`${colorKey}  --> Chromium [CronetEngineBuilderImpl] (PublicKey): ${hostName}${COLORS.reset}`);
                                return CronetEngineBuilderImpl.addPublicKeyPins.call(this, hostName, pinsSha256, includeSubdomains, expirationDate);
                            };
                            console.log("[+] Chromium [CronetEngineBuilderImpl] (PublicKey)");
                        } catch (e) {
                            console.log("[ ] Chromium [CronetEngineBuilderImpl] (PublicKey)");
                        }
                    }

                    if (MODE.Flutter) {
                        const colorKey = randomColor();
                        const HttpCertificatePinning = loadJava("diefferson.http_certificate_pinning.HttpCertificatePinning");
                        const SslPinningPlugin = loadJava("com.macif.plugin.sslpinningplugin.SslPinningPlugin");
                        try {
                            HttpCertificatePinning.checkConnexion.overload("java.lang.String", "java.util.List", "java.util.Map", "int", "java.lang.String").implementation = function (a, b, c, d, e) {
                                console.log(`${colorKey}  --> Flutter [HttpCertificatePinning]: ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Flutter [HttpCertificatePinning]");
                        } catch (e) {
                            console.log("[ ] Flutter [HttpCertificatePinning]");
                        }

                        try {
                            SslPinningPlugin.checkConnexion.overload("java.lang.String", "java.util.List", "java.util.Map", "int", "java.lang.String").implementation = function (a, b, c, d, e) {
                                console.log(`${colorKey}  --> Flutter [SslPinningPlugin]: ${a}${COLORS.reset}`);
                                return true;
                            };
                            console.log("[+] Flutter [SslPinningPlugin]");
                        } catch (e) {
                            console.log("[ ] Flutter [SslPinningPlugin]");
                        }
                    }

                }
            );
        } else {
            console.log(`${COLORS.red}[!] Java unavailable${COLORS.reset}`);
        }

        console.log("Capturing setup completed");
        console.log("---");
    }, 0);
}

Util

String <-> Binary

function bin2String(bArr) {
	var JString = Java.use('java.lang.String');
	var str = JString.$new(bArr).toString();

	return str
}

function string2Bin(str) {
    var result = [];
    for (var i = 0; i < str.length; i++) {
      result.push(str.charCodeAt(i));
    }
    return result;
}

ByteString <-> String

// if(byteString!=null) Bytes = byteString.getData$okio();
var Bytes = "okio.ByteString의 Byte 배열" 
var JavaByte = Java.use("[B");
var buffer = Java.cast(Bytes, JavaByte);
var result1 = Java.array('byte', buffer);
var JString = Java.use('java.lang.String');
var str = JString.$new(result1);

let result = "";

// String -> Bytearray
var newStr = `{"pin_txid":"string"}`
var newbyteString = string2Bin(newStr)

var java_bytestr = Java.use('okio.ByteString').$new(newbyteString)
result = this[method](mediaType, java_bytestr);

OnClickListen

function OnClickListener() {
    Java.perform(function () {
        Java.use("android.view.View").setOnClickListener.implementation = function (
            listener
            ) {
            if (listener != null) {
                watch(listener, "onClick");
            }
            return this.setOnClickListener(listener);
        };

        Java.choose("android.view.View$ListenerInfo", {
            onMatch: function (instance) {
                instance = instance.mOnClickListener.value;
                if (instance) {
                console.log("mOnClickListener name is :" + getObjClassName(instance));
                watch(instance, "onClick");
                }
            },
            onComplete: function () {},
        });
    });
}

AlertDialog Prevent

Java.perform(function(){
    var AD = Java.use('android.app.AlertDialog')
    AD.show.implementation = function (){
        console.log("Prevent Alert")
    }
})

Shared Preferences

function notifyNewSharedPreference() {
	Java.use('android.app.SharedPreferencesImpl$EditorImpl').putString.overload('java.lang.String', 'java.lang.String').implementation = function(k, v) {
        console.log('[SharedPreferencesImpl]', k, '=', v);
        return this.putString(k, v);
    }
}   

SQLite Log

Interceptor.attach(Module.findExportByName('libsqlite.so', 'sqlite3_prepare16_v2'), {
      onEnter: function(args) {
          console.log('DB: ' + Memory.readUtf16String(args[0]) + '\tSQL: ' + Memory.readUtf16String(args[1]));
      }
});

Hook First Instance

Java.perform(function() {
    Java.choose("com.examplePackage.exampleClass",
    {
        onMatch: function(instance)
        {
            console.log("[+] Instance Found! Hook Start");
        },    
        onComplete: function()
        {
            console.log("[*] Instance Finished");
        }
    });
});

Reflection

function reflectFunctionCall(){
    Java.perform(function() {
        Java.use("java.lang.Class").forName.overload('java.lang.String').implementation = function (param0) {
            console.log("forName hook : " + param0);
            return this.forName(param0);
        };
        
        Java.use("java.lang.Class").getMethod.overload('java.lang.String', '[Ljava.lang.Class;').implementation = function (param0, param1) {
            console.log("getMethod hook : " + param0);
            return this.getMethod(param0, param1);
        };
        
        var jlrmethod = Java.use("java.lang.reflect.Method")
        jlrmethod.invoke.implementation = function(object, parameters){
            var retvalue = this.invoke(object, parameters)
        
            var type = Object.prototype.toString.call(parameters)
            if(type == '[object Array]'){
                var arrayLength = parameters.length;
                // console.log(arrayLength)
                for (var i = 0; i < arrayLength; i++) {
                    var parameter = parameters[i]
                    var paramstring = String(parameter)
                    if(paramstring.startsWith("[Ljava.lang.String;@")){
                        console.log("------------")
                        console.log("string array")
                        console.log(type + JSON.stringify(parameter))
                        
                        var result = parameter
                        console.log(type + result)
                        console.log("------------")
                    }
                    else{
                        console.log(type + parameter)
                    }
                }
            }
            else{
                console.log(type + parameters)
            }
            console.log("Method.invoke(object, parameters): '" + this.toString() + "' = '" + object + "', '" + parameters + "' = '" + retvalue + "'")
            if(retvalue=='Invalid'){
                return 'Invalid'
            }
            else if(this.toString().includes('bE23766.a()')&& retvalue=='INVALID'){
                console.log("###################"+JSON.stringify(retvalue))
            }
            
            return retvalue
        }
        
    })
}

Describe Class

function describeJavaClass(className) {
  var jClass = Java.use(className);
  console.log(JSON.stringify({
    _name: className,
    _methods: Object.getOwnPropertyNames(jClass.__proto__).filter(m => {
      return !m.startsWith('$') // filter out Frida related special properties
         || m == 'class' || m == 'constructor' // optional
    }), 
    _fields: jClass.class.getFields().map(f => {
      return f.toString()
    })  
  }, null, 2));
}