mirror of
https://github.com/stulle123/kakaotalk_analysis.git
synced 2025-02-06 06:55:29 +00:00
Add scripts
This commit is contained in:
parent
b294ef501f
commit
eb549efe0b
713
scripts/frida/loco-tracer.js
Normal file
713
scripts/frida/loco-tracer.js
Normal file
|
@ -0,0 +1,713 @@
|
|||
/*
|
||||
TODO:
|
||||
- Hook Ed25519 (verify and sign)
|
||||
- AESCTRHelper
|
||||
- CipherSpec
|
||||
- Aes256Cipher
|
||||
- SimpleCipher
|
||||
- Hook Java Crypto APIs:
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import lc2.EdDSAEngine;
|
||||
import lc2.EdDSAPrivateKey;
|
||||
import lc2.EdDSAPublicKey;
|
||||
import oc2.EdDSANamedCurveSpec;
|
||||
import oc2.EdDSANamedCurveTable;
|
||||
import oc2.EdDSAParameterSpec;
|
||||
import oc2.EdDSAPrivateKeySpec;
|
||||
import oc2.EdDSAPublicKeySpec;
|
||||
*/
|
||||
|
||||
Java.perform(function () {
|
||||
/*
|
||||
hookCipherGetInstance(); // Kakaotalk
|
||||
hookCipherGetInstance2();
|
||||
hookCipherGetInstance3();
|
||||
hookCipherInit();
|
||||
hookCipherInit2(); // Kakaotalk
|
||||
hookCipherInit3();
|
||||
hookCipherInit4(); // Kakaotalk
|
||||
hookCipherInit5();
|
||||
hookCipherInit6(); // Kakaotalk
|
||||
hookCipherInit7();
|
||||
hookCipherInit8();
|
||||
hookDoFinal();
|
||||
hookDoFinal2(); // Kakaotalk
|
||||
hookDoFinal3();
|
||||
hookDoFinal4();
|
||||
hookDoFinal5();
|
||||
hookDoFinal6();
|
||||
hookDoFinal7();
|
||||
hookUpdate();
|
||||
hookUpdate2();
|
||||
hookUpdate3();
|
||||
hookUpdate4();
|
||||
hookUpdate5();
|
||||
hookIVParameterSpecDefInit1(); // Kakaotalk
|
||||
hookIVParameterSpecDefInit2(); // Kakaotalk
|
||||
hookSecretKeySpecDefInit1(); // Kakaotalk
|
||||
hookSecretKeySpecDefInit2(); // Kakaotalk
|
||||
hookKeyGeneratorGetInstance(); // Kakaotalk
|
||||
hookKeyGeneratorGetInstance2();
|
||||
hookKeyGeneratorGetInstance3();
|
||||
hookKeyGeneratorInit(); // Kakaotalk
|
||||
hookKeyGeneratorGenerateKey(); // Kakaotalk
|
||||
hookKeyPairGeneratorGetInstance(); // Kakaotalk
|
||||
hookPBEKeySpec();
|
||||
hookPBEKeySpec2();
|
||||
hookPBEKeySpec3(); // Kakaotalk
|
||||
// hookV2SLSinkInit(); // Kakaotalk
|
||||
*/
|
||||
hookDoFinal2(); // Kakaotalk
|
||||
hookKeyGeneratorGenerateKey(); // Kakaotalk
|
||||
// hookPBEKeySpec3(); // Kakaotalk
|
||||
});
|
||||
|
||||
const locoClasses = ["c41.c", "cv.a", "cv.b", "mw0.a", "mw0.c", "mw0.d", "mw0.e", "com.android.org.conscrypt.KeyGeneratorImpl"];
|
||||
const secretChatClasses = ["xa1.a", "xa1.i", "xa1.k", "LocoCipherHelper"];
|
||||
const locoKey = Java.array("byte", [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||
const patchKey = true;
|
||||
const printStacktrace = false;
|
||||
const hookAllClasses = false
|
||||
|
||||
var StringCls = null;
|
||||
Java.perform(function () {
|
||||
StringCls = Java.use('java.lang.String');
|
||||
});
|
||||
|
||||
/*
|
||||
.overload("java.lang.String")
|
||||
.overload("java.lang.String", "java.security.Provider")
|
||||
.overload("java.lang.String", "java.lang.String")
|
||||
*/
|
||||
function hookCipherGetInstance() {
|
||||
var cipherGetInstance = Java.use("javax.crypto.Cipher")["getInstance"].overload("java.lang.String");
|
||||
cipherGetInstance.implementation = function (type) {
|
||||
var tmp = this.getInstance(type);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[Cipher.getInstance()]: type: " + type);
|
||||
console.log("[Cipher.getInstance()]: cipherObj: " + tmp);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherGetInstance2() {
|
||||
var cipherGetInstance = Java.use("javax.crypto.Cipher")["getInstance"].overload("java.lang.String", "java.security.Provider");
|
||||
cipherGetInstance.implementation = function (transforamtion, provider) {
|
||||
console.log("[Cipher.getInstance2()]: transforamtion: " + transforamtion + ", provider: " + provider);
|
||||
var tmp = this.getInstance(transforamtion, provider);
|
||||
console.log("[Cipher.getInstance2()]: cipherObj: " + tmp);
|
||||
cipherList.push(tmp);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherGetInstance3() {
|
||||
var cipherGetInstance = Java.use("javax.crypto.Cipher")["getInstance"].overload("java.lang.String", "java.lang.String");
|
||||
cipherGetInstance.implementation = function (transforamtion, provider) {
|
||||
console.log("[Cipher.getInstance3()]: transforamtion: " + transforamtion + ", provider: " + provider);
|
||||
var tmp = this.getInstance(transforamtion, provider);
|
||||
console.log("[Cipher.getInstance3()]: cipherObj: " + tmp);
|
||||
cipherList.push(tmp);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("int", "java.security.cert.Certificate")
|
||||
.overload("int", "java.security.Key")
|
||||
.overload("int", "java.security.Key", "java.security.AlgorithmParameters")
|
||||
.overload("int", "java.security.Key", "java.security.spec.AlgorithmParameterSpec")
|
||||
.overload("int", "java.security.cert.Certificate", "java.security.SecureRandom")
|
||||
.overload("int", "java.security.Key", "java.security.SecureRandom")
|
||||
.overload("int", "java.security.Key", "java.security.spec.AlgorithmParameterSpec", "java.security.SecureRandom")
|
||||
.overload("int", "java.security.Key", "java.security.AlgorithmParameters", "java.security.SecureRandom")
|
||||
*/
|
||||
function hookCipherInit() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.cert.Certificate");
|
||||
cipherInit.implementation = function (mode, cert) {
|
||||
console.log("[Cipher.init()]: mode: " + decodeMode(mode) + ", cert: " + cert + " , cipherObj: " + this);
|
||||
var tmp = this.init(mode, cert);
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherInit2() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.Key");
|
||||
cipherInit.implementation = function (mode, secretKey) {
|
||||
var tmp = this.init(mode, secretKey);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[Cipher.init2()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " , cipherObj: " + this);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherInit3() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.Key", "java.security.AlgorithmParameters");
|
||||
cipherInit.implementation = function (mode, secretKey, alParam) {
|
||||
console.log("[Cipher.init3()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " alParam:" + alParam + " , cipherObj: " + this);
|
||||
var tmp = this.init(mode, secretKey, alParam);
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherInit4() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.Key", "java.security.spec.AlgorithmParameterSpec");
|
||||
cipherInit.implementation = function (mode, secretKey, spec) {
|
||||
var tmp = this.init(mode, secretKey, spec);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[Cipher.init4()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " spec:" + spec + " , cipherObj: " + this);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherInit5() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.cert.Certificate", "java.security.SecureRandom");
|
||||
cipherInit.implementation = function (mode, cert, secureRandom) {
|
||||
console.log("[Cipher.init5()]: mode: " + decodeMode(mode) + ", cert: " + cert + " secureRandom:" + secureRandom + " , cipherObj: " + this);
|
||||
var tmp = this.init(mode, cert, secureRandom);
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherInit6() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.Key", "java.security.SecureRandom");
|
||||
cipherInit.implementation = function (mode, secretKey, secureRandom) {
|
||||
var tmp = this.init(mode, secretKey, secureRandom);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[Cipher.init6()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " secureRandom:" + secureRandom + " , cipherObj: " + this);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherInit7() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.Key", "java.security.spec.AlgorithmParameterSpec", "java.security.SecureRandom");
|
||||
cipherInit.implementation = function (mode, secretKey, spec, secureRandom) {
|
||||
console.log("[Cipher.init7()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " spec:" + spec + " secureRandom: " + secureRandom + " , cipherObj: " + this);
|
||||
var tmp = this.init(mode, secretKey, spec, secureRandom);
|
||||
}
|
||||
}
|
||||
|
||||
function hookCipherInit8() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["init"].overload("int", "java.security.Key", "java.security.AlgorithmParameters", "java.security.SecureRandom");
|
||||
cipherInit.implementation = function (mode, secretKey, alParam, secureRandom) {
|
||||
console.log("[Cipher.init8()]: mode: " + decodeMode(mode) + ", secretKey: " + secretKey.$className + " alParam:" + alParam + " secureRandom: " + secureRandom + " , cipherObj: " + this);
|
||||
var tmp = this.init(mode, secretKey, alParam, secureRandom);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload()
|
||||
.overload("[B")
|
||||
.overload("[B", "int")
|
||||
.overload("java.nio.ByteBuffer", "java.nio.ByteBuffer")
|
||||
.overload("[B", "int", "int")
|
||||
.overload("[B", "int", "int", "[B")
|
||||
.overload("[B", "int", "int", "[B", "int")
|
||||
*/
|
||||
function hookDoFinal() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload();
|
||||
cipherInit.implementation = function () {
|
||||
console.log("[Cipher.doFinal()]: " + " cipherObj: " + this);
|
||||
var tmp = this.doFinal();
|
||||
dumpByteArray("Result", tmp);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookDoFinal2() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload("[B");
|
||||
cipherInit.implementation = function (byteArr) {
|
||||
var tmp = this.doFinal(byteArr);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[Cipher.doFinal2()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
dumpByteArray("Result", tmp);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookDoFinal3() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload("[B", "int");
|
||||
cipherInit.implementation = function (byteArr, a1) {
|
||||
console.log("[Cipher.doFinal3()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.doFinal(byteArr, a1);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookDoFinal4() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload("java.nio.ByteBuffer", "java.nio.ByteBuffer");
|
||||
cipherInit.implementation = function (a1, a2) {
|
||||
console.log("[Cipher.doFinal4()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", a1.array());
|
||||
var tmp = this.doFinal(a1, a2);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", a2.array());
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookDoFinal5() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload("[B", "int", "int");
|
||||
cipherInit.implementation = function (byteArr, a1, a2) {
|
||||
console.log("[Cipher.doFinal5()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.doFinal(byteArr, a1, a2);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", tmp);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookDoFinal6() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload("[B", "int", "int", "[B");
|
||||
cipherInit.implementation = function (byteArr, a1, a2, outputArr) {
|
||||
console.log("[Cipher.doFinal6()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.doFinal(byteArr, a1, a2, outputArr);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", outputArr);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookDoFinal7() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["doFinal"].overload("[B", "int", "int", "[B", "int");
|
||||
cipherInit.implementation = function (byteArr, a1, a2, outputArr, a4) {
|
||||
console.log("[Cipher.doFinal7()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.doFinal(byteArr, a1, a2, outputArr, a4);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", outputArr);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("[B")
|
||||
.overload("java.nio.ByteBuffer", "java.nio.ByteBuffer")
|
||||
.overload("[B", "int", "int")
|
||||
.overload("[B", "int", "int", "[B")
|
||||
.overload("[B", "int", "int", "[B", "int")
|
||||
*/
|
||||
function hookUpdate() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["update"].overload("[B");
|
||||
cipherInit.implementation = function (byteArr) {
|
||||
console.log("[Cipher.update()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.update(byteArr);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", tmp);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookUpdate2() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["update"].overload("java.nio.ByteBuffer", "java.nio.ByteBuffer");
|
||||
cipherInit.implementation = function (byteArr, outputArr) {
|
||||
console.log("[Cipher.update2()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr.array());
|
||||
var tmp = this.update(byteArr, outputArr);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", outputArr.array());
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookUpdate3() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["update"].overload("[B", "int", "int");
|
||||
cipherInit.implementation = function (byteArr, a1, a2) {
|
||||
console.log("[Cipher.update3()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.update(byteArr, a1, a2);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", tmp);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookUpdate4() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["update"].overload("[B", "int", "int", "[B");
|
||||
cipherInit.implementation = function (byteArr, a1, a2, outputArr) {
|
||||
console.log("[Cipher.update4()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.update(byteArr, a1, a2, outputArr);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", outputArr);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookUpdate5() {
|
||||
var cipherInit = Java.use("javax.crypto.Cipher")["update"].overload("[B", "int", "int", "[B", "int");
|
||||
cipherInit.implementation = function (byteArr, a1, a2, outputArr, a4) {
|
||||
console.log("[Cipher.update5()]: " + " cipherObj: " + this);
|
||||
dumpByteArray("In buffer (cipher: " + this.getAlgorithm() + ")", byteArr);
|
||||
var tmp = this.update(byteArr, a1, a2, outputArr, a4);
|
||||
dumpByteArray("Out buffer (cipher: " + this.getAlgorithm() + ")", outputArr);
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("[B")
|
||||
.overload("[B", "int", "int")
|
||||
*/
|
||||
function hookIVParameterSpecDefInit1() {
|
||||
var ivParameterSpecDef = ivParameterSpecDef = Java.use("javax.crypto.spec.IvParameterSpec").$init.overload("[B");
|
||||
ivParameterSpecDef.implementation = function (arr) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
dumpByteArray("IV", arr)
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
return ivParameterSpecDef.call(this, arr);
|
||||
}
|
||||
}
|
||||
|
||||
function hookIVParameterSpecDefInit2() {
|
||||
var ivParameterSpecDef = ivParameterSpecDef = Java.use("javax.crypto.spec.IvParameterSpec").$init.overload("[B", "int", "int");
|
||||
ivParameterSpecDef.implementation = function (arr, off, len) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
dumpByteArray("IV", arr)
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
return ivParameterSpecDef.call(this, arr, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("[B", java.lang.String)
|
||||
.overload("[B", "int", "int", "java.lang.String")
|
||||
*/
|
||||
function hookSecretKeySpecDefInit1() {
|
||||
var secretKeySpecDef = Java.use("javax.crypto.spec.SecretKeySpec").$init.overload("[B", "java.lang.String");
|
||||
secretKeySpecDef.implementation = function (arr, alg) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
dumpByteArray("Original " + alg + " Secret Key", arr);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
return secretKeySpecDef.call(this, arr, alg);
|
||||
}
|
||||
}
|
||||
|
||||
function hookSecretKeySpecDefInit2() {
|
||||
var secretKeySpecDef = Java.use("javax.crypto.spec.SecretKeySpec").$init.overload("[B", "int", "int", "java.lang.String");
|
||||
secretKeySpecDef.implementation = function (arr, off, len, alg) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
dumpByteArray(alg + " Secret Key", arr);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
return secretKeySpecDef.call(this, arr, off, len, alg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("java.lang.String")
|
||||
.overload("java.lang.String", "java.lang.String")
|
||||
.overload("java.lang.String", "java.security.Provider")
|
||||
*/
|
||||
function hookKeyGeneratorGetInstance() {
|
||||
var keyGeneratorInit = Java.use("javax.crypto.KeyGenerator")["getInstance"].overload("java.lang.String");
|
||||
keyGeneratorInit.implementation = function (type) {
|
||||
var tmp = this.getInstance(type);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[KeyGenerator.getInstance()]: type: " + type);
|
||||
console.log("[KeyGenerator.getInstance()]: cipherObj: " + tmp);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################");
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookKeyGeneratorGetInstance2() {
|
||||
var keyGeneratorInit = Java.use("javax.crypto.KeyGenerator")["getInstance"].overload("java.lang.String", "java.lang.String");
|
||||
keyGeneratorInit.implementation = function (alg, provider) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[KeyGenerator.getInstance2()]: Algorithm: " + alg);
|
||||
console.log("[KeyGenerator.getInstance2()]: Provider: " + provider);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################");
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
function hookKeyGeneratorGetInstance3() {
|
||||
var keyGeneratorInit = Java.use("javax.crypto.KeyGenerator")["getInstance"].overload("java.lang.String", "java.security.Provider");
|
||||
keyGeneratorInit.implementation = function (alg, provider) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[KeyGenerator.getInstance2()]: Algorithm: " + alg);
|
||||
console.log("[KeyGenerator.getInstance2()]: Provider: " + provider);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################");
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("int", "java.security.SecureRandom")
|
||||
*/
|
||||
function hookKeyGeneratorInit() {
|
||||
var keyGeneratorInit = Java.use("javax.crypto.KeyGenerator")["init"].overload("int", "java.security.SecureRandom");
|
||||
keyGeneratorInit.implementation = function (length, secureRandom) {
|
||||
var tmp = this.init(length, secureRandom);
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[KeyGenerator.init()]: secureRandom:" + secureRandom + " , cipherObj: " + this);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload()
|
||||
*/
|
||||
function hookKeyGeneratorGenerateKey() {
|
||||
var generateKey = Java.use("javax.crypto.KeyGenerator")["generateKey"].overload();
|
||||
generateKey.implementation = function () {
|
||||
var tmp = this.generateKey();
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
const secretKeySpec = Java.cast(tmp, Java.use("javax.crypto.spec.SecretKeySpec"));
|
||||
const encodedKey = secretKeySpec.getEncoded();
|
||||
/*
|
||||
console.log("[KeyGenerator.generateKey()]: Object: " + tmp);
|
||||
dumpByteArray("[KeyGenerator.generateKey()]: Key:", encodedKey);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
*/
|
||||
if (patchKey) {
|
||||
const SecretKeySpec = Java.use("javax.crypto.spec.SecretKeySpec");
|
||||
var fakeKey = SecretKeySpec.$new(locoKey, "AES");
|
||||
tmp = fakeKey
|
||||
}
|
||||
console.log("##############################################");
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("java.lang.String")
|
||||
*/
|
||||
function hookKeyPairGeneratorGetInstance() {
|
||||
var keyPairGetInstance = Java.use("java.security.KeyPairGenerator")["getInstance"].overload("java.lang.String");
|
||||
keyPairGetInstance.implementation = function (alg) {
|
||||
var caller = Java.use("java.lang.Exception").$new().getStackTrace()[1];
|
||||
if (locoClasses.includes(caller.getClassName()) || hookAllClasses) {
|
||||
console.log("[KeyPairGenerator.getInstance()]: Algorithm:" + alg);
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
}
|
||||
return this.getInstance(alg);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload('[C')
|
||||
.overload('[C', '[B', 'int')
|
||||
.overload('[C', '[B', 'int', 'int')
|
||||
*/
|
||||
function hookPBEKeySpec() {
|
||||
var PBEKeySpec = Java.use('javax.crypto.spec.PBEKeySpec')['$init'].overload('[C');
|
||||
PBEKeySpec.implementation = function (pass) {
|
||||
console.log("[PBEKeySpec.PBEKeySpec()]: password: " + charArrayToString(pass));
|
||||
return this.$init(pass);
|
||||
}
|
||||
}
|
||||
|
||||
function hookPBEKeySpec2() {
|
||||
var PBEKeySpec = Java.use('javax.crypto.spec.PBEKeySpec')['$init'].overload('[C', '[B', 'int');
|
||||
PBEKeySpec.implementation = function (pass, salt, iter) {
|
||||
console.log("[PBEKeySpec.PBEKeySpec2()]: password: " + charArrayToString(pass) + " iter: " + iter);
|
||||
dumpByteArray("Salt", salt)
|
||||
return this.$init(pass, salt, iter);
|
||||
}
|
||||
}
|
||||
|
||||
function hookPBEKeySpec3() {
|
||||
var PBEKeySpec = Java.use('javax.crypto.spec.PBEKeySpec')['$init'].overload('[C', '[B', 'int', 'int');
|
||||
PBEKeySpec.implementation = function (pass, salt, iter, keyLength) {
|
||||
console.log("[PBEKeySpec.PBEKeySpec3()]: password: " + charArrayToString(pass) + " iter: " + iter + " key length: " + keyLength);
|
||||
dumpByteArray("Salt", salt)
|
||||
if (printStacktrace) {
|
||||
var stacktrace = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()).replace("java.lang.Exception", "")
|
||||
console.log(stacktrace);
|
||||
}
|
||||
console.log("##############################################")
|
||||
return this.$init(pass, salt, iter, keyLength);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
.overload("xc2.b0", "java.security.Key")
|
||||
*/
|
||||
function hookV2SLSinkInit() {
|
||||
var V2SLSinkInit = Java.use("mw0.d")["$init"].overload("xc2.b0", "java.security.Key");
|
||||
V2SLSinkInit.implementation = function (arg0, arg1) {
|
||||
var tmp = this.$init(arg0, arg1);
|
||||
const secretKeySpec = Java.cast(arg1, Java.use("javax.crypto.spec.SecretKeySpec"));
|
||||
const encodedKey = secretKeySpec.getEncoded();
|
||||
dumpByteArray("V2SLSinkInit called with AES Key", encodedKey);
|
||||
console.log("##############################################")
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* All below is hexdump implementation*/
|
||||
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++) {
|
||||
dtv.setUint8(i, byteArr[i]); // Frida sucks sometimes and returns different byteArr.length between ArrayBuffer(byteArr.length) and for(..; i < byteArr.length;..). It occured even when Array.copyOf was done to work on copy.
|
||||
}
|
||||
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;
|
||||
};
|
36
scripts/mitmproxy/README.md
Normal file
36
scripts/mitmproxy/README.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# MITM Kakaotalk LOCO Packets
|
||||
|
||||
This is a simple script to man-in-the-middle LOCO packets with mitmproxy.
|
||||
|
||||
Setup on your MITM host:
|
||||
|
||||
```bash
|
||||
$ python3 -m venv venv
|
||||
$ source venv/bin/activate
|
||||
(venv) $ python3 -m pip install mitmproxy bson cryptography
|
||||
(venv) $ mitmdump --mode wireguard --rawtcp -s loco_mitm.py
|
||||
```
|
||||
|
||||
Android emulator setup:
|
||||
|
||||
- Install the Kakaotalk app if not done already
|
||||
- Install the WireGuard app
|
||||
- Change the IP address in mitmproxy's generated WireGuard config to `10.0.2.2`. Example:
|
||||
```
|
||||
[Interface]
|
||||
PrivateKey = MCCAFVMZQk+k+sbdXx0B4LG+Mij/UO7qyWa7IRqv/nA=
|
||||
Address = 10.0.0.1/32
|
||||
DNS = 10.0.0.53
|
||||
|
||||
[Peer]
|
||||
PublicKey = K+t/qiGO8tlA9L7wjAOb8wqjnu/NuthHgLs2gOCIDgY=
|
||||
AllowedIPs = 0.0.0.0/0
|
||||
Endpoint = 10.0.2.2:51820
|
||||
```
|
||||
- Import the config in the WireGuard app
|
||||
|
||||
Back on your MITM host start Frida (see [setup instructions](../../README.md#setup-frida-to-disable-certificate-pinning)):
|
||||
|
||||
```bash
|
||||
$ frida -U -l loco-tracer.js -f com.kakao.talk
|
||||
```
|
15
scripts/mitmproxy/lib/crypto_utils.py
Normal file
15
scripts/mitmproxy/lib/crypto_utils.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
||||
_KEY = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
|
||||
def aes_encrypt(plaintext, iv):
|
||||
cipher = Cipher(algorithms.AES(_KEY), modes.CFB(iv))
|
||||
encryptor = cipher.encryptor()
|
||||
return encryptor.update(plaintext) + encryptor.finalize()
|
||||
|
||||
|
||||
def aes_decrypt(ciphertext, iv):
|
||||
cipher = Cipher(algorithms.AES(_KEY), modes.CFB(iv))
|
||||
decryptor = cipher.decryptor()
|
||||
return decryptor.update(ciphertext) + decryptor.finalize()
|
191
scripts/mitmproxy/lib/loco_parser.py
Normal file
191
scripts/mitmproxy/lib/loco_parser.py
Normal file
|
@ -0,0 +1,191 @@
|
|||
import bson, logging, struct, io
|
||||
from .crypto_utils import aes_encrypt, aes_decrypt
|
||||
|
||||
|
||||
class LocoPacket:
|
||||
def __init__(
|
||||
self,
|
||||
id=0,
|
||||
status_code=0,
|
||||
loco_command="",
|
||||
body_type=0,
|
||||
body_length=0,
|
||||
body_payload=b"",
|
||||
):
|
||||
self.id = id
|
||||
self.status_code = status_code
|
||||
self.loco_command = loco_command
|
||||
self.body_type = body_type
|
||||
self.body_length = body_length
|
||||
self.body_payload = body_payload
|
||||
|
||||
# TODO: Add exception handling
|
||||
def get_packet_bytes(self) -> bytes:
|
||||
try:
|
||||
f = io.BytesIO()
|
||||
f.write(struct.pack("<I", self.id))
|
||||
f.write(struct.pack("<H", self.status_code))
|
||||
f.write(self.loco_command.encode("utf-8"))
|
||||
f.write(b"\x00" * (11 - len(self.loco_command)))
|
||||
f.write(struct.pack("<b", self.body_type))
|
||||
f.write(struct.pack("<i", self.body_length))
|
||||
f.write(self.body_payload)
|
||||
return f.getvalue()
|
||||
except Exception as e:
|
||||
logging.error(f"Could not create LOCO packet: {e}")
|
||||
return None
|
||||
|
||||
def get_packet_as_dict(self) -> dict:
|
||||
loco_dict = vars(self)
|
||||
|
||||
try:
|
||||
if loco_dict["body_payload"]:
|
||||
loco_dict["body_payload"] = bson.loads(self.body_payload)
|
||||
except:
|
||||
logging.error("Could not decode BSON body.")
|
||||
|
||||
return loco_dict
|
||||
|
||||
|
||||
class LocoEncryptedPacket:
|
||||
def __init__(self, length=0, iv=b"", payload=b""):
|
||||
self.length = length
|
||||
self.iv = iv
|
||||
self.payload = payload
|
||||
|
||||
# TODO: Add exception handling
|
||||
def get_packet_bytes(self, loco_packet: LocoPacket) -> bytes:
|
||||
# new_iv = os.urandom(16)
|
||||
|
||||
if not loco_packet:
|
||||
logging.error(
|
||||
f"Could not create LOCO encrypted packet: Loco packet data is None."
|
||||
)
|
||||
return None
|
||||
|
||||
encrypted_packet = aes_encrypt(loco_packet, self.iv)
|
||||
|
||||
if not encrypted_packet:
|
||||
logging.error(f"Could not encrypt LOCO packet.")
|
||||
return None
|
||||
|
||||
try:
|
||||
f = io.BytesIO()
|
||||
f.write(struct.pack("<I", len(encrypted_packet) + len(self.iv)))
|
||||
f.write(self.iv)
|
||||
f.write(encrypted_packet)
|
||||
return f.getvalue()
|
||||
except Exception as e:
|
||||
logging.error(f"Could not create LOCO encrypted packet: {e}")
|
||||
return None
|
||||
|
||||
|
||||
class LocoHandshakePacket:
|
||||
def __init__(self, length=256, type=0, block_cipher_mode=0, payload=b""):
|
||||
self.length = length
|
||||
self.type = type
|
||||
self.block_cipher_mode = block_cipher_mode
|
||||
self.payload = payload
|
||||
|
||||
self.cipher_mode_map = {1: "CBC", 2: "AES/CFB/NoPadding", 3: "OFB"}
|
||||
self.encryption_mode_map = {15: "RSA/NONE/OAEPWithSHA1AndMGF1Padding"}
|
||||
|
||||
|
||||
class LocoParser:
|
||||
def __init__(self):
|
||||
self.loco_packet = LocoPacket()
|
||||
self.loco_encrypted_packet = LocoEncryptedPacket()
|
||||
self.handshake_packet = LocoHandshakePacket()
|
||||
|
||||
# TODO: Add exception handling
|
||||
def parse_loco_packet(self, data):
|
||||
if not data:
|
||||
logging.error(
|
||||
f"Could not parse LOCO encrypted packet: Packet data is None."
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
id = struct.unpack("<I", data[:4])[0]
|
||||
status_code = struct.unpack("<H", data[4:6])[0]
|
||||
loco_command = data[6:17].decode().replace("\0", "")
|
||||
body_type = struct.unpack("<b", data[17:18])[0]
|
||||
body_length = struct.unpack("<i", data[18:22])[0]
|
||||
body_payload = data[22:]
|
||||
return LocoPacket(
|
||||
id, status_code, loco_command, body_type, body_length, body_payload
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f"Could not parse LOCO packet: {e}")
|
||||
return None
|
||||
|
||||
# TODO: Add exception handling
|
||||
def parse_loco_encrypted_packet(self, data):
|
||||
if not data:
|
||||
logging.error(
|
||||
f"Could not parse LOCO encrypted packet: Packet data is None."
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
length = struct.unpack("<I", data[0:4])[0]
|
||||
iv = data[4:20]
|
||||
payload = data[20:]
|
||||
return LocoEncryptedPacket(length, iv, payload)
|
||||
except Exception as e:
|
||||
logging.error(f"Could not parse LOCO encrypted packet: {e}")
|
||||
return None
|
||||
|
||||
# TODO: Add exception handling
|
||||
def parse_handshake_packet(self, data):
|
||||
if not data:
|
||||
logging.error(
|
||||
f"Could not parse LOCO handshake packet: Packet data is None."
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
type = struct.unpack("<I", data[4:8])[0]
|
||||
block_cipher_mode = struct.unpack("<I", data[8:12])[0]
|
||||
payload = data[22:]
|
||||
return LocoHandshakePacket(type, block_cipher_mode, payload)
|
||||
except Exception as e:
|
||||
logging.error(f"Could not parse LOCO handshake packet: {e}")
|
||||
return None
|
||||
|
||||
def parse(self, data):
|
||||
self.loco_encrypted_packet = self.parse_loco_encrypted_packet(data)
|
||||
|
||||
if self.loco_encrypted_packet.length == 256:
|
||||
self.handshake_packet = self.parse_handshake_packet(data)
|
||||
else:
|
||||
decrypted_payload = aes_decrypt(
|
||||
self.loco_encrypted_packet.payload, self.loco_encrypted_packet.iv
|
||||
)
|
||||
self.loco_packet = self.parse_loco_packet(decrypted_payload)
|
||||
|
||||
def inject_message(self, trigger_message, payload) -> bytes:
|
||||
if not self.loco_packet:
|
||||
return None
|
||||
|
||||
if not self.loco_packet.loco_command == "MSG":
|
||||
return None
|
||||
|
||||
body_json = self.bson_decode(self.loco_packet.body_payload)
|
||||
|
||||
if (
|
||||
"chatLog" in body_json
|
||||
and body_json["chatLog"]["message"] == trigger_message
|
||||
):
|
||||
body_json["chatLog"]["message"] = payload
|
||||
self.loco_packet.body_payload = self.bson_encode(body_json)
|
||||
self.loco_packet.body_length = len(self.loco_packet.body_payload)
|
||||
return self.loco_encrypted_packet.get_packet_bytes(
|
||||
self.loco_packet.get_packet_bytes()
|
||||
)
|
||||
|
||||
def bson_encode(self, data):
|
||||
return bson.dumps(data)
|
||||
|
||||
def bson_decode(self, data):
|
||||
return bson.loads(data)
|
41
scripts/mitmproxy/loco_mitm.py
Normal file
41
scripts/mitmproxy/loco_mitm.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import logging
|
||||
|
||||
from mitmproxy import connection
|
||||
from mitmproxy import tcp
|
||||
from mitmproxy import tls
|
||||
from mitmproxy.utils import human
|
||||
from mitmproxy.utils import strutils
|
||||
|
||||
from lib.loco_parser import LocoParser
|
||||
|
||||
|
||||
class LocoMitm:
|
||||
@staticmethod
|
||||
def get_addr(server: connection.Server):
|
||||
return server.peername or server.address
|
||||
|
||||
def tls_clienthello(self, data: tls.ClientHelloData):
|
||||
server_address = self.get_addr(data.context.server)
|
||||
logging.info(f"Skip TLS intercept for {human.format_address(server_address)}.")
|
||||
data.ignore_connection = True
|
||||
|
||||
def tcp_message(self, flow: tcp.TCPFlow):
|
||||
message = flow.messages[-1]
|
||||
parser = LocoParser()
|
||||
parser.parse(message.content)
|
||||
tampered_packet = parser.inject_message("foo", "bar")
|
||||
|
||||
if tampered_packet:
|
||||
message.content = tampered_packet
|
||||
|
||||
if parser.loco_packet:
|
||||
logging.info(
|
||||
f"from_client={message.from_client}, content={parser.loco_packet.get_packet_as_dict()}"
|
||||
)
|
||||
else:
|
||||
logging.info(
|
||||
f"from_client={message.from_client}), content={strutils.bytes_to_escaped_str(message.content)}]"
|
||||
)
|
||||
|
||||
|
||||
addons = [LocoMitm()]
|
Loading…
Reference in New Issue
Block a user