mirror of
https://github.com/stulle123/kakaotalk_analysis.git
synced 2024-11-26 07:22:12 +00:00
Fix Frida scripts
This commit is contained in:
parent
a770a0b587
commit
c50e2eb113
|
@ -13,7 +13,7 @@ This is how one can run the PoC:
|
|||
- Assumption: You've already set up your test environment (see setup description [here](./SETUP.md))
|
||||
- Wipe all entries in the `public_key_info` and `secret_key_info` tables from the `KakaoTalk.db` database
|
||||
- Start `mitmproxy`: `$ mitmdump -m wireguard -s mitm_secret_chat.py`
|
||||
- Start `Frida`: `$ frida -U -l trace_loco.js -f com.kakao.talk`
|
||||
- Start `Frida`: `$ frida -U -l debug_secret_chat.js -f com.kakao.talk`
|
||||
- Create a new *Secret Chat* room in the KakaoTalk app and send a message
|
||||
- View message in `mitmproxy` terminal window
|
||||
|
||||
|
|
|
@ -2,52 +2,88 @@
|
|||
Hook various Secret Chat methods of KakaoTalk 10.4.3.
|
||||
*/
|
||||
|
||||
import { printStacktrace, dumpByteArray } from "./utils.js";
|
||||
const locoKey = Java.array(
|
||||
"byte",
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
]
|
||||
);
|
||||
const patchLocoKey = true;
|
||||
const locoFileNames = [
|
||||
"V2SLSink.kt",
|
||||
"V2SLSource.kt",
|
||||
"V2SLHandshake.kt",
|
||||
"LocoV2SLSocket.kt",
|
||||
];
|
||||
const enableStacktracePrinting = false;
|
||||
var StringCls = null;
|
||||
|
||||
Java.perform(function () {
|
||||
hookLocoCipherHelper();
|
||||
hookLocoCipherHelper_2();
|
||||
StringCls = Java.use("java.lang.String");
|
||||
hookKeyGeneratorGenerateKey();
|
||||
hookSharedSecretStore();
|
||||
hookLocoCipherHelper_GenerateRSAPrivateKey();
|
||||
hookLocoCipherHelper_GenerateRSAPublicKey();
|
||||
hookAESCTRHelper_GenerateIV();
|
||||
hookSecretChatHelper();
|
||||
hookLocoPubKeyInfo();
|
||||
hookTalkLocoPKStore();
|
||||
hookTalkLocoPKStore_2();
|
||||
hookAESCTRHelper_GenerateIV();
|
||||
printAESCTRKeySet();
|
||||
});
|
||||
|
||||
const printStacktrace = false;
|
||||
function hookKeyGeneratorGenerateKey() {
|
||||
var generateKey = Java.use("javax.crypto.KeyGenerator")[
|
||||
"generateKey"
|
||||
].overload();
|
||||
|
||||
function hookLocoCipherHelper() {
|
||||
var locoCipherHelper = Java.use("com.kakao.talk.secret.LocoCipherHelper")[
|
||||
"s"
|
||||
].overload("com.kakao.talk.secret.LocoCipherHelper$c", "[B", "[B");
|
||||
locoCipherHelper.implementation = function (arg0, arg1, arg2) {
|
||||
console.log("hookLocoCipherHelper2 called!");
|
||||
generateKey.implementation = function () {
|
||||
var tmp = this.generateKey();
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log(caller.getFileName());
|
||||
var ret = locoCipherHelper.call(this, arg0, arg1, arg2);
|
||||
console.log(ret);
|
||||
return locoCipherHelper.call(this, arg0, arg1, arg2);
|
||||
const secretKeySpec = Java.cast(
|
||||
tmp,
|
||||
Java.use("javax.crypto.spec.SecretKeySpec")
|
||||
);
|
||||
const encodedKey = secretKeySpec.getEncoded();
|
||||
|
||||
if (locoFileNames.includes(caller.getFileName())) {
|
||||
// console.log("[KeyGenerator.generateKey()]: Object: " + tmp);
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
// dumpByteArray("[KeyGenerator.generateKey()]: Key", encodedKey);
|
||||
var base64_key = Java.use("android.util.Base64").encodeToString(
|
||||
encodedKey,
|
||||
0
|
||||
);
|
||||
console.log("Generated key: " + base64_key);
|
||||
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (patchLocoKey) {
|
||||
dumpByteArray("Patching LOCO AES key with key", locoKey);
|
||||
const SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
|
||||
var fakeKey = SecretKeySpec.$new(locoKey, "AES");
|
||||
tmp = fakeKey;
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
||||
return tmp;
|
||||
};
|
||||
}
|
||||
|
||||
function hookLocoCipherHelper_2() {
|
||||
var locoCipherHelper = Java.use("com.kakao.talk.secret.LocoCipherHelper$b")[
|
||||
function hookSharedSecretStore() {
|
||||
var locoCipherHelper = Java.use("com.kakao.talk.secret.LocoCipherHelper$e")[
|
||||
"$init"
|
||||
].overload(
|
||||
"com.kakao.talk.secret.LocoCipherHelper$d",
|
||||
"com.kakao.talk.secret.LocoCipherHelper$c"
|
||||
);
|
||||
].overload("java.lang.String", "long");
|
||||
locoCipherHelper.implementation = function (arg0, arg1) {
|
||||
var tmp = this.$init(arg0, arg1);
|
||||
console.log("hookLocoCipherHelper5 called!");
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log(caller.getFileName());
|
||||
console.log(arg0);
|
||||
console.log(arg1);
|
||||
console.log("Secret Chat shared secret: " + arg0);
|
||||
console.log("Secret Chat seed for nonce: " + arg1);
|
||||
console.log(this.toString());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("##############################################");
|
||||
};
|
||||
}
|
||||
|
@ -59,10 +95,10 @@ function hookLocoCipherHelper_GenerateRSAPrivateKey() {
|
|||
locoCipherHelper.implementation = function (arg0) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
// var private_key = locoCipherHelper.call(this, arg0);
|
||||
// var encoded_key = Java.use("android.util.Base64").encodeToString(private_key.getEncoded(), 0);
|
||||
console.log("Generate RSA private key from string: " + arg0);
|
||||
// console.log(encoded_key)
|
||||
console.log("Generated RSA private key: " + arg0);
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
return locoCipherHelper.call(this, arg0);
|
||||
};
|
||||
|
@ -74,13 +110,9 @@ function hookLocoCipherHelper_GenerateRSAPublicKey() {
|
|||
].overload("java.lang.String");
|
||||
locoCipherHelper.implementation = function (arg0) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
var ret = locoCipherHelper.call(this, arg0);
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("Generate RSA public key from string: " + arg0);
|
||||
var public_key = locoCipherHelper.call(this, arg0);
|
||||
// var encoded_key = Java.use("android.util.Base64").encodeToString(public_key.getEncoded(), 0);
|
||||
// console.log(encoded_key);
|
||||
if (printStacktrace) {
|
||||
console.log("Generated RSA public key: " + arg0);
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -96,7 +128,7 @@ function hookLocoPubKeyInfo() {
|
|||
var tmp = this.$init(locoBody);
|
||||
console.log("locoPubKeyInfo called!");
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log(locoBody);
|
||||
console.log("##############################################");
|
||||
};
|
||||
|
@ -117,24 +149,11 @@ function hookSecretChatHelper() {
|
|||
}
|
||||
|
||||
function hookTalkLocoPKStore() {
|
||||
var talkLocoPKStore = Java.use("yl1.x3")["toString"].overload();
|
||||
talkLocoPKStore.implementation = function () {
|
||||
console.log("talkLocoPKStore called!");
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log(caller.getFileName());
|
||||
var ret = talkLocoPKStore.call(this);
|
||||
console.log(ret);
|
||||
console.log("##############################################");
|
||||
return talkLocoPKStore.call(this);
|
||||
};
|
||||
}
|
||||
|
||||
function hookTalkLocoPKStore_2() {
|
||||
var talkLocoPKStore = Java.use("yl1.x3$a")["toString"].overload();
|
||||
talkLocoPKStore.implementation = function () {
|
||||
console.log("talkLocoPKStore2 called!");
|
||||
console.log("TalkLocoPKStore class called!");
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
var ret = talkLocoPKStore.call(this);
|
||||
console.log(ret);
|
||||
console.log("##############################################");
|
||||
|
@ -150,6 +169,9 @@ function hookAESCTRHelper_GenerateIV() {
|
|||
"javax.crypto.spec.PBEKeySpec"
|
||||
);
|
||||
AESCTRHelper.implementation = function (arg0, arg1, arg2, arg3) {
|
||||
console.log("AESCTRHelper class called!");
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
dumpByteArray("Generated IV", arg1);
|
||||
console.log("##############################################");
|
||||
return AESCTRHelper.call(this, arg0, arg1, arg2, arg3);
|
||||
|
@ -159,6 +181,9 @@ function hookAESCTRHelper_GenerateIV() {
|
|||
function printAESCTRKeySet() {
|
||||
var AESCTRKeySet = Java.use("d20.b")["$init"].overload("[B", "[B", "[B");
|
||||
AESCTRKeySet.implementation = function (arg0, arg1, arg2) {
|
||||
console.log("AESCTRKeySet class called!");
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
dumpByteArray("Secret key", arg0);
|
||||
dumpByteArray("IV", arg1);
|
||||
dumpByteArray("arg2", arg2);
|
||||
|
@ -166,3 +191,69 @@ function printAESCTRKeySet() {
|
|||
return AESCTRKeySet.call(this, arg0, arg1, arg2);
|
||||
};
|
||||
}
|
||||
|
||||
function printStacktrace() {
|
||||
var stacktrace = Java.use("android.util.Log")
|
||||
.getStackTraceString(Java.use("java.lang.Exception").$new())
|
||||
.replace("java.lang.Exception", "");
|
||||
console.log(stacktrace);
|
||||
}
|
||||
|
||||
function dumpByteArray(title, byteArr) {
|
||||
if (byteArr != null) {
|
||||
try {
|
||||
var buff = new ArrayBuffer(byteArr.length);
|
||||
var dtv = new DataView(buff);
|
||||
for (var i = 0; i < byteArr.length; i++) {
|
||||
/*
|
||||
Frida sucks sometimes and returns different byteArr.length between ArrayBuffer(byteArr.length) and for(..; i < byteArr.length;..).
|
||||
It occurred even when Array.copyOf was done to work on copy.
|
||||
*/
|
||||
dtv.setUint8(i, byteArr[i]);
|
||||
}
|
||||
console.log(title + ":\n");
|
||||
console.log(_hexdumpJS(dtv.buffer, 0, byteArr.length));
|
||||
} catch (error) {
|
||||
console.log("Exception has occured in hexdump");
|
||||
}
|
||||
} else {
|
||||
console.log("byteArr is null!");
|
||||
}
|
||||
}
|
||||
|
||||
function _hexdumpJS(arrayBuffer, offset, length) {
|
||||
var view = new DataView(arrayBuffer);
|
||||
offset = offset || 0;
|
||||
length = length || arrayBuffer.byteLength;
|
||||
|
||||
var out =
|
||||
_fillUp("Offset", 8, " ") +
|
||||
" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n";
|
||||
var row = "";
|
||||
for (var i = 0; i < length; i += 16) {
|
||||
row += _fillUp(offset.toString(16).toUpperCase(), 8, "0") + " ";
|
||||
var n = Math.min(16, length - offset);
|
||||
var string = "";
|
||||
for (var j = 0; j < 16; ++j) {
|
||||
if (j < n) {
|
||||
var value = view.getUint8(offset);
|
||||
string += value >= 32 && value < 128 ? String.fromCharCode(value) : ".";
|
||||
row += _fillUp(value.toString(16).toUpperCase(), 2, "0") + " ";
|
||||
offset++;
|
||||
} else {
|
||||
row += " ";
|
||||
string += " ";
|
||||
}
|
||||
}
|
||||
row += " " + string + "\n";
|
||||
}
|
||||
out += row;
|
||||
return out;
|
||||
}
|
||||
|
||||
function _fillUp(value, count, fillWith) {
|
||||
var l = count - value.length;
|
||||
var ret = "";
|
||||
while (--l > -1) ret += fillWith;
|
||||
return ret + value;
|
||||
}
|
||||
|
|
|
@ -2,36 +2,34 @@
|
|||
Debug WebViews.
|
||||
*/
|
||||
|
||||
import { printStacktrace, printMap } from "./utils.js";
|
||||
const enableStacktracePrinting = false;
|
||||
|
||||
Java.perform(function () {
|
||||
enableWebviewDebugging();
|
||||
});
|
||||
|
||||
const printStacktrace = false;
|
||||
|
||||
function enableWebviewDebugging() {
|
||||
var Webview = Java.use("android.webkit.WebView");
|
||||
|
||||
Webview.loadUrl.overload("java.lang.String").implementation = function (url) {
|
||||
console.log("\n[+]Loading URL from", url);
|
||||
console.log("\n[+] Loading URL from", url);
|
||||
console.log(
|
||||
"[+]Setting the value of setWebContentsDebuggingEnabled() to TRUE"
|
||||
"[+] Setting the value of setWebContentsDebuggingEnabled() to TRUE"
|
||||
);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
var js = this.getSettings().getJavaScriptEnabled();
|
||||
console.log("[+]JS enabled: " + js);
|
||||
console.log("[+] JS enabled: " + js);
|
||||
|
||||
var mw = this.getSettings().supportMultipleWindows();
|
||||
console.log("[+]Mutliple windows?: " + mw);
|
||||
console.log("[+] Multiple windows?: " + mw);
|
||||
|
||||
var fa = this.getSettings().getAllowFileAccess();
|
||||
console.log("[+]File access: " + fa);
|
||||
console.log("[+] File access: " + fa);
|
||||
|
||||
var uf = this.getSettings().getAllowUniversalAccessFromFileURLs();
|
||||
console.log("[+]Universal file access: " + uf);
|
||||
console.log("[+] Universal file access: " + uf);
|
||||
|
||||
this.setWebContentsDebuggingEnabled(true);
|
||||
this.loadUrl.overload("java.lang.String").call(this, url);
|
||||
|
@ -39,29 +37,29 @@ function enableWebviewDebugging() {
|
|||
|
||||
Webview.loadUrl.overload("java.lang.String", "java.util.Map").implementation =
|
||||
function (url, additionalHttpHeaders) {
|
||||
console.log("\n[+]Loading URL from", url);
|
||||
console.log("[+]Additional Headers:");
|
||||
console.log("\n[+] Loading URL from", url);
|
||||
console.log("[+] Additional Headers:");
|
||||
var headers = Java.cast(additionalHttpHeaders, Java.use("java.util.Map"));
|
||||
printMap(headers);
|
||||
console.log(
|
||||
"[+]Setting the value of setWebContentsDebuggingEnabled() to TRUE"
|
||||
"[+] Setting the value of setWebContentsDebuggingEnabled() to TRUE"
|
||||
);
|
||||
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
|
||||
var js = this.getSettings().getJavaScriptEnabled();
|
||||
console.log("[+]JS enabled: " + js);
|
||||
console.log("[+] JS enabled: " + js);
|
||||
|
||||
var mw = this.getSettings().supportMultipleWindows();
|
||||
console.log("[+]Multiple windows?: " + mw);
|
||||
console.log("[+] Multiple windows?: " + mw);
|
||||
|
||||
var fa = this.getSettings().getAllowFileAccess();
|
||||
console.log("[+]File access: " + fa);
|
||||
console.log("[+] File access: " + fa);
|
||||
|
||||
var uf = this.getSettings().getAllowUniversalAccessFromFileURLs();
|
||||
console.log("[+]Universal file access: " + uf);
|
||||
console.log("[+] Universal file access: " + uf);
|
||||
|
||||
this.setWebContentsDebuggingEnabled(true);
|
||||
this.loadUrl
|
||||
|
@ -71,7 +69,7 @@ function enableWebviewDebugging() {
|
|||
|
||||
Webview.addJavascriptInterface.implementation = function (object, name) {
|
||||
console.log(
|
||||
"[+]Javascript interface:" +
|
||||
"[+] Javascript interface:" +
|
||||
object.$className +
|
||||
" instantiated as: " +
|
||||
name
|
||||
|
@ -86,7 +84,7 @@ function enableWebviewDebugging() {
|
|||
"android.graphics.Bitmap"
|
||||
).implementation = function (view, url, favicon) {
|
||||
console.log("onPageStarted URL: " + url);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
this.onPageStarted
|
||||
|
@ -124,3 +122,17 @@ function enableWebviewDebugging() {
|
|||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
function printStacktrace() {
|
||||
var stacktrace = Java.use("android.util.Log")
|
||||
.getStackTraceString(Java.use("java.lang.Exception").$new())
|
||||
.replace("java.lang.Exception", "");
|
||||
console.log(stacktrace);
|
||||
}
|
||||
|
||||
function printMap(map) {
|
||||
var mapIter = map.entrySet().iterator();
|
||||
while (mapIter.hasNext()) {
|
||||
console.log(mapIter.next());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,31 @@
|
|||
Hook most of Android's Crypto APIs.
|
||||
*/
|
||||
|
||||
import {
|
||||
printStacktrace,
|
||||
dumpByteArray,
|
||||
decodeMode,
|
||||
charArrayToString,
|
||||
} from "./utils.js";
|
||||
/*
|
||||
const doNotHookFileNames = [
|
||||
"SimpleCipher.kt",
|
||||
"AccountUpdater.kt",
|
||||
"DataBaseResourceCrypto.kt",
|
||||
"CookieContentEncryptor.java",
|
||||
"Aes256Cipher.kt",
|
||||
"TiaraEncrypt.java",
|
||||
];
|
||||
*/
|
||||
const doNotHookFileNames = [];
|
||||
const hookAllClasses = false;
|
||||
const dummyKey = Java.array(
|
||||
"byte",
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
]
|
||||
);
|
||||
const patchKey = true;
|
||||
const enableStacktracePrinting = false;
|
||||
var StringCls = null;
|
||||
|
||||
Java.perform(function () {
|
||||
StringCls = Java.use("java.lang.String");
|
||||
hookCipherGetInstance();
|
||||
hookCipherGetInstance2();
|
||||
hookCipherGetInstance3();
|
||||
|
@ -48,29 +65,6 @@ Java.perform(function () {
|
|||
hookKeyPairGeneratorGetInstance();
|
||||
});
|
||||
|
||||
/*
|
||||
const doNotHookFileNames = [
|
||||
"SimpleCipher.kt",
|
||||
"AccountUpdater.kt",
|
||||
"DataBaseResourceCrypto.kt",
|
||||
"CookieContentEncryptor.java",
|
||||
"Aes256Cipher.kt",
|
||||
"TiaraEncrypt.java",
|
||||
];
|
||||
*/
|
||||
|
||||
const doNotHookFileNames = [];
|
||||
const dummyKey = Java.array(
|
||||
"byte",
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
]
|
||||
);
|
||||
const patchKey = true;
|
||||
const hookAllClasses = false;
|
||||
const printStacktrace = false;
|
||||
|
||||
/*
|
||||
.overload("java.lang.String")
|
||||
.overload("java.lang.String", "java.security.Provider")
|
||||
|
@ -84,10 +78,10 @@ function hookCipherGetInstance() {
|
|||
var tmp = this.getInstance(type);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("[Cipher.getInstance()]: type: " + type);
|
||||
console.log("[Cipher.getInstance()]: cipherObj: " + tmp);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -181,7 +175,7 @@ function hookCipherInit2() {
|
|||
// dumpByteArray("Secret key", key);
|
||||
var key_base64 = Java.use("android.util.Base64").encodeToString(key, 0);
|
||||
console.log("Base64 encoded key: " + key_base64);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -221,7 +215,7 @@ function hookCipherInit4() {
|
|||
var tmp = this.init(mode, secretKey, spec);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log(
|
||||
"[Cipher.init4()]: mode: " +
|
||||
decodeMode(mode) +
|
||||
|
@ -239,7 +233,7 @@ function hookCipherInit4() {
|
|||
Java.use("javax.crypto.spec.IvParameterSpec")
|
||||
);
|
||||
dumpByteArray("IV", ivParameterSpec.getIV());
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -298,7 +292,7 @@ function hookCipherInit6() {
|
|||
0
|
||||
);
|
||||
console.log("Secret key: " + secret_key_base64);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -397,7 +391,7 @@ function hookDoFinal2() {
|
|||
0
|
||||
);
|
||||
// console.log("Result in Base64: " + result_base64)
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -542,10 +536,10 @@ function hookPBEKeySpec3() {
|
|||
console.log(
|
||||
"[PBEKeySpec.PBEKeySpec3()]: iter: " + iter + " key length: " + keyLength
|
||||
);
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
dumpByteArray("Password", charArrayToString(pass).getBytes());
|
||||
dumpByteArray("Salt", salt);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -564,9 +558,9 @@ function hookIVParameterSpecDefInit1() {
|
|||
ivParameterSpecDef.implementation = function (arr) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
dumpByteArray("IV", arr);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -582,9 +576,9 @@ function hookIVParameterSpecDefInit2() {
|
|||
ivParameterSpecDef.implementation = function (arr, off, len) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
dumpByteArray("IV", arr);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -604,9 +598,9 @@ function hookSecretKeySpecDefInit1() {
|
|||
secretKeySpecDef.implementation = function (arr, alg) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
dumpByteArray(alg + " Secret Key", arr);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -622,9 +616,9 @@ function hookSecretKeySpecDefInit2() {
|
|||
secretKeySpecDef.implementation = function (arr, off, len, alg) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
dumpByteArray(alg + " Secret Key", arr);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -738,10 +732,10 @@ function hookKeyGeneratorGetInstance() {
|
|||
var tmp = this.getInstance(type);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("[KeyGenerator.getInstance()]: type: " + type);
|
||||
console.log("[KeyGenerator.getInstance()]: cipherObj: " + tmp);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -758,10 +752,10 @@ function hookKeyGeneratorGetInstance2() {
|
|||
var tmp = this.getInstance(alg, provider);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("[KeyGenerator.getInstance2()]: Algorithm: " + alg);
|
||||
console.log("[KeyGenerator.getInstance2()]: Provider: " + provider);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -778,10 +772,10 @@ function hookKeyGeneratorGetInstance3() {
|
|||
var tmp = this.getInstance(alg, provider);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("[KeyGenerator.getInstance2()]: Algorithm: " + alg);
|
||||
console.log("[KeyGenerator.getInstance2()]: Provider: " + provider);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -802,14 +796,14 @@ function hookKeyGeneratorInit() {
|
|||
var tmp = this.init(length, secureRandom);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (!doNotHookFileNames.includes(caller.getFileName()) || hookAllClasses) {
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log(
|
||||
"[KeyGenerator.init()]: secureRandom:" +
|
||||
secureRandom +
|
||||
" , cipherObj: " +
|
||||
this
|
||||
);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
|
@ -841,7 +835,7 @@ function hookKeyGeneratorGenerateKey() {
|
|||
);
|
||||
console.log("Generated key: " + base64_key);
|
||||
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
}
|
||||
|
@ -867,12 +861,90 @@ function hookKeyPairGeneratorGetInstance() {
|
|||
].overload("java.lang.String");
|
||||
keyPairGetInstance.implementation = function (alg) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log(caller.getFileName());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("[KeyPairGenerator.getInstance()]: Algorithm:" + alg);
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("##############################################");
|
||||
return this.getInstance(alg);
|
||||
};
|
||||
}
|
||||
|
||||
function printStacktrace() {
|
||||
var stacktrace = Java.use("android.util.Log")
|
||||
.getStackTraceString(Java.use("java.lang.Exception").$new())
|
||||
.replace("java.lang.Exception", "");
|
||||
console.log(stacktrace);
|
||||
}
|
||||
|
||||
function dumpByteArray(title, byteArr) {
|
||||
if (byteArr != null) {
|
||||
try {
|
||||
var buff = new ArrayBuffer(byteArr.length);
|
||||
var dtv = new DataView(buff);
|
||||
for (var i = 0; i < byteArr.length; i++) {
|
||||
/*
|
||||
Frida sucks sometimes and returns different byteArr.length between ArrayBuffer(byteArr.length) and for(..; i < byteArr.length;..).
|
||||
It occurred even when Array.copyOf was done to work on copy.
|
||||
*/
|
||||
dtv.setUint8(i, byteArr[i]);
|
||||
}
|
||||
console.log(title + ":\n");
|
||||
console.log(_hexdumpJS(dtv.buffer, 0, byteArr.length));
|
||||
} catch (error) {
|
||||
console.log("Exception has occured in hexdump");
|
||||
}
|
||||
} else {
|
||||
console.log("byteArr is null!");
|
||||
}
|
||||
}
|
||||
|
||||
function _hexdumpJS(arrayBuffer, offset, length) {
|
||||
var view = new DataView(arrayBuffer);
|
||||
offset = offset || 0;
|
||||
length = length || arrayBuffer.byteLength;
|
||||
|
||||
var out =
|
||||
_fillUp("Offset", 8, " ") +
|
||||
" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n";
|
||||
var row = "";
|
||||
for (var i = 0; i < length; i += 16) {
|
||||
row += _fillUp(offset.toString(16).toUpperCase(), 8, "0") + " ";
|
||||
var n = Math.min(16, length - offset);
|
||||
var string = "";
|
||||
for (var j = 0; j < 16; ++j) {
|
||||
if (j < n) {
|
||||
var value = view.getUint8(offset);
|
||||
string += value >= 32 && value < 128 ? String.fromCharCode(value) : ".";
|
||||
row += _fillUp(value.toString(16).toUpperCase(), 2, "0") + " ";
|
||||
offset++;
|
||||
} else {
|
||||
row += " ";
|
||||
string += " ";
|
||||
}
|
||||
}
|
||||
row += " " + string + "\n";
|
||||
}
|
||||
out += row;
|
||||
return out;
|
||||
}
|
||||
|
||||
function _fillUp(value, count, fillWith) {
|
||||
var l = count - value.length;
|
||||
var ret = "";
|
||||
while (--l > -1) ret += fillWith;
|
||||
return ret + value;
|
||||
}
|
||||
|
||||
function decodeMode(mode) {
|
||||
if (mode == 1) return "Encrypt mode";
|
||||
else if (mode == 2) return "Decrypt mode";
|
||||
else if (mode == 3) return "Wrap mode";
|
||||
else if (mode == 4) return "Unwrap mode";
|
||||
}
|
||||
|
||||
function charArrayToString(charArray) {
|
||||
if (charArray == null) return "(null)";
|
||||
else return StringCls.$new(charArray);
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
Print all Strings.
|
||||
*/
|
||||
|
||||
import { printStacktrace } from "./utils.js";
|
||||
|
||||
Java.perform(function () {
|
||||
hookStrings();
|
||||
});
|
||||
|
||||
const printStacktrace = false;
|
||||
var StringCls = null;
|
||||
|
||||
Java.perform(function () {
|
||||
StringCls = Java.use("java.lang.String");
|
||||
});
|
||||
|
||||
function hookStrings() {
|
||||
let StringBuilder = Java.use("java.lang.StringBuilder");
|
||||
StringBuilder.toString.overload().implementation = function () {
|
||||
let StringBuilderResult = this.toString.call(this);
|
||||
|
||||
if (
|
||||
StringBuilderResult !== null &&
|
||||
StringBuilderResult.indexOf("file:") != -1
|
||||
) {
|
||||
if (printStacktrace) {
|
||||
printStacktrace();
|
||||
}
|
||||
console.log("[+] StringBuilder:\t", StringBuilderResult);
|
||||
}
|
||||
return StringBuilderResult;
|
||||
};
|
||||
|
||||
let StringBuffer = Java.use("java.lang.StringBuffer");
|
||||
StringBuffer.toString.overload().implementation = function () {
|
||||
let StringBufferResult = this.toString.call(this);
|
||||
|
||||
if (
|
||||
StringBufferResult !== null &&
|
||||
StringBufferResult.indexOf("http") != -1
|
||||
) {
|
||||
console.log("[+] StringBuffer:\t", StringBufferResult);
|
||||
}
|
||||
return StringBufferResult;
|
||||
};
|
||||
}
|
|
@ -2,14 +2,12 @@
|
|||
Debug Deep Links.
|
||||
*/
|
||||
|
||||
import { printStacktrace } from "./utils.js";
|
||||
const enableStacktracePrinting = false;
|
||||
|
||||
Java.perform(function () {
|
||||
deepLinkSniffer();
|
||||
});
|
||||
|
||||
const printStacktrace = false;
|
||||
|
||||
function deepLinkSniffer() {
|
||||
var Intent = Java.use("android.content.Intent");
|
||||
Intent.getData.implementation = function () {
|
||||
|
@ -17,11 +15,13 @@ function deepLinkSniffer() {
|
|||
this.getAction() !== null ? this.getAction().toString() : false;
|
||||
if (action) {
|
||||
console.log("[*] Intent.getData() was called");
|
||||
console.log("[*] Activity: " + this.getComponent().getClassName());
|
||||
if (this.getComponent()) {
|
||||
console.log("[*] Activity: " + this.getComponent().getClassName());
|
||||
}
|
||||
console.log("[*] Action: " + action);
|
||||
var uri = this.getData();
|
||||
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
|
||||
|
@ -45,3 +45,10 @@ function deepLinkSniffer() {
|
|||
return this.getData();
|
||||
};
|
||||
}
|
||||
|
||||
function printStacktrace() {
|
||||
var stacktrace = Java.use("android.util.Log")
|
||||
.getStackTraceString(Java.use("java.lang.Exception").$new())
|
||||
.replace("java.lang.Exception", "");
|
||||
console.log(stacktrace);
|
||||
}
|
||||
|
|
|
@ -2,15 +2,6 @@
|
|||
Decrypt and print LOCO traffic of KakaoTalk 10.4.3.
|
||||
*/
|
||||
|
||||
import { dumpByteArray, printStacktrace } from "./utils.js";
|
||||
|
||||
Java.perform(function () {
|
||||
hookDoFinal2();
|
||||
hookKeyGeneratorGenerateKey();
|
||||
hookSharedSecretStore();
|
||||
printLocoBody();
|
||||
});
|
||||
|
||||
const locoKey = Java.array(
|
||||
"byte",
|
||||
[
|
||||
|
@ -18,16 +9,20 @@ const locoKey = Java.array(
|
|||
0x00, 0x00, 0x00, 0x00,
|
||||
]
|
||||
);
|
||||
|
||||
const patchLocoKey = true;
|
||||
const locoFileNames = [
|
||||
"V2SLSink.kt",
|
||||
"V2SLSource.kt",
|
||||
"V2SLHandshake.kt",
|
||||
"LocoV2SLSocket.kt",
|
||||
];
|
||||
const enableStacktracePrinting = false;
|
||||
|
||||
const patchLocoKey = true;
|
||||
const printStacktrace = false;
|
||||
Java.perform(function () {
|
||||
hookDoFinal2();
|
||||
hookKeyGeneratorGenerateKey();
|
||||
printLocoBody();
|
||||
});
|
||||
|
||||
function hookDoFinal2() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload("[B");
|
||||
|
@ -44,7 +39,7 @@ function hookDoFinal2() {
|
|||
// var result_base64 = Java.use("android.util.Base64").encodeToString(tmp, 0);
|
||||
// console.log("Result in Base64: " + result_base64)
|
||||
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
|
||||
|
@ -79,7 +74,7 @@ function hookKeyGeneratorGenerateKey() {
|
|||
);
|
||||
console.log("Generated key: " + base64_key);
|
||||
|
||||
if (printStacktrace) {
|
||||
if (enableStacktracePrinting) {
|
||||
printStacktrace();
|
||||
}
|
||||
}
|
||||
|
@ -96,21 +91,6 @@ function hookKeyGeneratorGenerateKey() {
|
|||
};
|
||||
}
|
||||
|
||||
function hookSharedSecretStore() {
|
||||
var locoCipherHelper = Java.use("com.kakao.talk.secret.LocoCipherHelper$e")[
|
||||
"$init"
|
||||
].overload("java.lang.String", "long");
|
||||
locoCipherHelper.implementation = function (arg0, arg1) {
|
||||
var tmp = this.$init(arg0, arg1);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
console.log("Secret Chat shared secret: " + arg0);
|
||||
console.log("Secret Chat seed for nonce: " + arg1);
|
||||
console.log(this.toString());
|
||||
console.log("Caller: " + caller.getFileName());
|
||||
console.log("##############################################");
|
||||
};
|
||||
}
|
||||
|
||||
function printLocoBody() {
|
||||
Java.choose("com.kakao.talk.loco.protocol.LocoBody", {
|
||||
onMatch: function (instance) {
|
||||
|
@ -121,3 +101,69 @@ function printLocoBody() {
|
|||
onComplete: function () {},
|
||||
});
|
||||
}
|
||||
|
||||
function printStacktrace() {
|
||||
var stacktrace = Java.use("android.util.Log")
|
||||
.getStackTraceString(Java.use("java.lang.Exception").$new())
|
||||
.replace("java.lang.Exception", "");
|
||||
console.log(stacktrace);
|
||||
}
|
||||
|
||||
function dumpByteArray(title, byteArr) {
|
||||
if (byteArr != null) {
|
||||
try {
|
||||
var buff = new ArrayBuffer(byteArr.length);
|
||||
var dtv = new DataView(buff);
|
||||
for (var i = 0; i < byteArr.length; i++) {
|
||||
/*
|
||||
Frida sucks sometimes and returns different byteArr.length between ArrayBuffer(byteArr.length) and for(..; i < byteArr.length;..).
|
||||
It occurred even when Array.copyOf was done to work on copy.
|
||||
*/
|
||||
dtv.setUint8(i, byteArr[i]);
|
||||
}
|
||||
console.log(title + ":\n");
|
||||
console.log(_hexdumpJS(dtv.buffer, 0, byteArr.length));
|
||||
} catch (error) {
|
||||
console.log("Exception has occured in hexdump");
|
||||
}
|
||||
} else {
|
||||
console.log("byteArr is null!");
|
||||
}
|
||||
}
|
||||
|
||||
function _hexdumpJS(arrayBuffer, offset, length) {
|
||||
var view = new DataView(arrayBuffer);
|
||||
offset = offset || 0;
|
||||
length = length || arrayBuffer.byteLength;
|
||||
|
||||
var out =
|
||||
_fillUp("Offset", 8, " ") +
|
||||
" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n";
|
||||
var row = "";
|
||||
for (var i = 0; i < length; i += 16) {
|
||||
row += _fillUp(offset.toString(16).toUpperCase(), 8, "0") + " ";
|
||||
var n = Math.min(16, length - offset);
|
||||
var string = "";
|
||||
for (var j = 0; j < 16; ++j) {
|
||||
if (j < n) {
|
||||
var value = view.getUint8(offset);
|
||||
string += value >= 32 && value < 128 ? String.fromCharCode(value) : ".";
|
||||
row += _fillUp(value.toString(16).toUpperCase(), 2, "0") + " ";
|
||||
offset++;
|
||||
} else {
|
||||
row += " ";
|
||||
string += " ";
|
||||
}
|
||||
}
|
||||
row += " " + string + "\n";
|
||||
}
|
||||
out += row;
|
||||
return out;
|
||||
}
|
||||
|
||||
function _fillUp(value, count, fillWith) {
|
||||
var l = count - value.length;
|
||||
var ret = "";
|
||||
while (--l > -1) ret += fillWith;
|
||||
return ret + value;
|
||||
}
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
export function printStacktrace() {
|
||||
var stacktrace = Java.use("android.util.Log")
|
||||
.getStackTraceString(Java.use("java.lang.Exception").$new())
|
||||
.replace("java.lang.Exception", "");
|
||||
console.log(stacktrace);
|
||||
}
|
||||
|
||||
export function printMap(map) {
|
||||
var mapIter = map.entrySet().iterator();
|
||||
while (mapIter.hasNext()) {
|
||||
console.log(mapIter.next());
|
||||
}
|
||||
}
|
||||
|
||||
export function decodeMode(mode) {
|
||||
if (mode == 1) return "Encrypt mode";
|
||||
else if (mode == 2) return "Decrypt mode";
|
||||
else if (mode == 3) return "Wrap mode";
|
||||
else if (mode == 4) return "Unwrap mode";
|
||||
}
|
||||
|
||||
export function charArrayToString(charArray) {
|
||||
if (charArray == null) return "(null)";
|
||||
else return StringCls.$new(charArray);
|
||||
}
|
||||
|
||||
export function dumpByteArray(title, byteArr) {
|
||||
if (byteArr != null) {
|
||||
try {
|
||||
var buff = new ArrayBuffer(byteArr.length);
|
||||
var dtv = new DataView(buff);
|
||||
for (var i = 0; i < byteArr.length; i++) {
|
||||
/*
|
||||
Frida sucks sometimes and returns different byteArr.length between ArrayBuffer(byteArr.length) and for(..; i < byteArr.length;..).
|
||||
It occurred even when Array.copyOf was done to work on copy.
|
||||
*/
|
||||
dtv.setUint8(i, byteArr[i]);
|
||||
}
|
||||
console.log(title + ":\n");
|
||||
console.log(_hexdumpJS(dtv.buffer, 0, byteArr.length));
|
||||
} catch (error) {
|
||||
console.log("Exception has occured in hexdump");
|
||||
}
|
||||
} else {
|
||||
console.log("byteArr is null!");
|
||||
}
|
||||
}
|
||||
|
||||
function _fillUp(value, count, fillWith) {
|
||||
var l = count - value.length;
|
||||
var ret = "";
|
||||
while (--l > -1) ret += fillWith;
|
||||
return ret + value;
|
||||
}
|
||||
|
||||
function _hexdumpJS(arrayBuffer, offset, length) {
|
||||
var view = new DataView(arrayBuffer);
|
||||
offset = offset || 0;
|
||||
length = length || arrayBuffer.byteLength;
|
||||
|
||||
var out =
|
||||
_fillUp("Offset", 8, " ") +
|
||||
" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n";
|
||||
var row = "";
|
||||
for (var i = 0; i < length; i += 16) {
|
||||
row += _fillUp(offset.toString(16).toUpperCase(), 8, "0") + " ";
|
||||
var n = Math.min(16, length - offset);
|
||||
var string = "";
|
||||
for (var j = 0; j < 16; ++j) {
|
||||
if (j < n) {
|
||||
var value = view.getUint8(offset);
|
||||
string += value >= 32 && value < 128 ? String.fromCharCode(value) : ".";
|
||||
row += _fillUp(value.toString(16).toUpperCase(), 2, "0") + " ";
|
||||
offset++;
|
||||
} else {
|
||||
row += " ";
|
||||
string += " ";
|
||||
}
|
||||
}
|
||||
row += " " + string + "\n";
|
||||
}
|
||||
out += row;
|
||||
return out;
|
||||
}
|
Loading…
Reference in New Issue
Block a user