당분간 모바일 해킹을 할 시간이 부족해 당장 기억나는 부분들로만 정리한 내용 추가 예정
Android 구조 및 동작이해
Android 진단 환경 구축
ADB
SDK 설치 (Android Studio 설치하면 알아서 깔림)
- https://developer.android.com/studio/releases/platform-tools
- SDK 설치 후 platform-tools 디렉토리 환경 변수 등록
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에서 인증서 설치 진행
높은 버전의 경우
- Burp Suite [Proxy] - [Options] - [Import/export certificate] 기능을 통해 인증서 파일을 추출
-
openssl x509 -inform DER -in cacert.cer -out cacert.pem
pem 변환 -
openssl x509 -inform PEM -subject_hash_old -in cacert.pem
해쉬값 추출 -
mv cacert.pem 9a5ba575.0
파일명 해쉬값으로 변경 adb push 9a5ba575.0 /data/local/tmp
-
mount -o rw,remount /
쓰기권한 마운트 시키기 -
cp 9a5ba575.0 /system/etc/security/cacerts/
ls -l /system/etc/security/cacerts/ | grep 9a5ba575
mount -o ro,remount /
- [설정] - [생체 인식 및 보안] - [기타 보안 설정] - [인증서 확인] 에서 확인
정적 분석
APK 추출
- ASTRO 백업 기능 이용
- 플레이스토어에서 APK 다운로드
- APK Extractor 이용
디컴파일
- jadx APK 파일 그냥 jadx에 넣으면 됨
- APK Tool 이용
https://github.com/iBotPeaches/Apktool/releases/tag/v2.8.0
java -jar apktool_2.8.0.jar d "디컴파일 할 앱 이름"
- 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
- Manifest에서 debugging이 되는지 확인
- 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));
}