android frida 逆向 自吐加密算法

news2025/1/20 5:54:28

前言:

♛  frida  hook   android   Android  逆向神器

前几天在学习 Android 逆向的时候发现了一个神器:通过  frida hook  我们可以 “劫持”  一些函数 为我们所用, 今天就和大家上手一个  加密函数的劫持 让打印出:

  • 加密秘钥
  • 密文

效果如下:可以很直观的看出 加密算法以及秘钥

 

 

准备工作:

安装运行环境:(因为我这里都装过来 你们自己对照着来)

  1. python  环境
    C:\Users\Administrator>python --version
    Python 3.10.6
  2. frida
    C:\Users\Administrator>pip install frida
    Requirement already satisfied: frida in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (16.1.3)
    Requirement already satisfied: typing-extensions in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (from frida) (4.7.1)
    
    [notice] A new release of pip available: 22.2.1 -> 23.2.1
    [notice] To update, run: python.exe -m pip install --upgrade pip
    
    C:\Users\Administrator>frida
    usage: frida [options] target
    frida: error: target must be specified
  3. firda-tools
    C:\Users\Administrator>pip install frida-tools
    Requirement already satisfied: frida-tools in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (12.2.1)
    Requirement already satisfied: prompt-toolkit<4.0.0,>=2.0.0 in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (from frida-tools) (3.0.39)
    Requirement already satisfied: colorama<1.0.0,>=0.2.7 in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (from frida-tools) (0.4.6)
    Requirement already satisfied: pygments<3.0.0,>=2.0.2 in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (from frida-tools) (2.15.1)
    Requirement already satisfied: frida<17.0.0,>=16.0.9 in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (from frida-tools) (16.1.3)
    Requirement already satisfied: typing-extensions in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (from frida<17.0.0,>=16.0.9->frida-tools) (4.7.1)
    Requirement already satisfied: wcwidth in c:\users\administrator\appdata\local\programs\python\python310\lib\site-packages (from prompt-toolkit<4.0.0,>=2.0.0->frida-tools) (0.2.6)
    
    [notice] A new release of pip available: 22.2.1 -> 23.2.1
    [notice] To update, run: python.exe -m pip install --upgrade pip
  4.  下载一个 frida server    Releases · frida/frida · GitHub
    下载地址: https://github.com/frida/frida/releases

    因为 我用的是模拟器所有下载这个版本,可以使用 命令查看对于的类型

    C:\Users\Administrator>adb shell getprop ro.product.cpu.abi
    x86_64
  5. adb
    C:\Users\Administrator>adb devices
    List of devices attached
    emulator-5558   device

实操上手:

⒈建立注入环境:运行 frida server   (不要关闭)

把刚刚下载的 解压发送到 手机  /data/local/ 目录下, 我这里重命名为 f86(应为一些加壳软件会监测是否使用 frifa)

 

 执行 f86,   记得要给权限!chmod 777 f86

C:\Users\Administrator>adb shell
star2qltechn:/ $ su
star2qltechn:/ # cd /data/lcoal
/system/bin/sh: cd: /data/lcoal: No such file or directory
2|star2qltechn:/ # cd /data/local
star2qltechn:/data/local # ls
cfg-zeiag f86 file-cache temp-zeiag tmp traces
star2qltechn:/data/local # chmod 777 f86
star2qltechn:/data/local # su
star2qltechn:/data/local # ./f86

2 建立端口转发  一共两个端口  (不要关闭)

  • adb forward tcp:27043 tcp:27043
  • adb forward tcp:27042 tcp:27042
C:\Users\Administrator>adb forward tcp:27043 tcp:27043
27043

C:\Users\Administrator>adb forward tcp:27042 tcp:27042
27042

3  运行我们的 hook.js 代码  (不要关闭)

frida -U -F -l C:\Users\Administrator\Desktop\py\hook.js
  • -U 指的的我们的 USB设备
  • -F 指的是我们最手机 最上层运行的APP
  • -l 指定我们脚本路径

 运行这App的时候 跑起来我们的脚本

 

 

4  奇迹就发生了

 

 

hook.js 脚本

var N_ENCRYPT_MODE = 1
var N_DECRYPT_MODE = 2

function showStacks() {
    var Exception = Java.use("java.lang.Exception");
    var ins = Exception.$new("Exception");
    var straces = ins.getStackTrace();

    if (undefined == straces || null == straces) {
        return;
    }

    console.log("============================= Stack strat=======================");
    console.log("");

    for (var i = 0; i < straces.length; i++) {
        var str = "   " + straces[i].toString();
        console.log(str);
    }

    console.log("");
    console.log("============================= Stack end=======================\r\n");
    Exception.$dispose();
}

//工具相关函数
var base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
    base64DecodeChars = new Array((-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), (-1), 62, (-1), (-1), (-1), 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, (-1), (-1), (-1), (-1), (-1), (-1), (-1), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, (-1), (-1), (-1), (-1), (-1), (-1), 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, (-1), (-1), (-1), (-1), (-1));

function stringToBase64(e) {
    var r, a, c, h, o, t;
    for (c = e.length, a = 0, r = ''; a < c;) {
        if (h = 255 & e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4),
                r += '==';
            break
        }
        if (o = e.charCodeAt(a++), a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
                r += base64EncodeChars.charAt((15 & o) << 2),
                r += '=';
            break
        }
        t = e.charCodeAt(a++),
            r += base64EncodeChars.charAt(h >> 2),
            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
            r += base64EncodeChars.charAt(63 & t)
    }
    return r
}
function base64ToString(e) {
    var r, a, c, h, o, t, d;
    for (t = e.length, o = 0, d = ''; o < t;) {
        do
            r = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && r == -1);
        if (r == -1)
            break;
        do
            a = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && a == -1);
        if (a == -1)
            break;
        d += String.fromCharCode(r << 2 | (48 & a) >> 4);
        do {
            if (c = 255 & e.charCodeAt(o++), 61 == c)
                return d;
            c = base64DecodeChars[c]
        } while (o < t && c == -1);
        if (c == -1)
            break;
        d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2);
        do {
            if (h = 255 & e.charCodeAt(o++), 61 == h)
                return d;
            h = base64DecodeChars[h]
        } while (o < t && h == -1);
        if (h == -1)
            break;
        d += String.fromCharCode((3 & c) << 6 | h)
    }
    return d
}
function hexToBase64(str) {
    return base64Encode(String.fromCharCode.apply(null, str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")));
}
function base64ToHex(str) {
    for (var i = 0, bin = base64Decode(str.replace(/[ \r\n]+$/, "")), hex = []; i < bin.length; ++i) {
        var tmp = bin.charCodeAt(i).toString(16);
        if (tmp.length === 1)
            tmp = "0" + tmp;
        hex[hex.length] = tmp;
    }
    return hex.join("");
}
function hexToBytes(str) {
    var pos = 0;
    var len = str.length;
    if (len % 2 != 0) {
        return null;
    }
    len /= 2;
    var hexA = new Array();
    for (var i = 0; i < len; i++) {
        var s = str.substr(pos, 2);
        var v = parseInt(s, 16);
        hexA.push(v);
        pos += 2;
    }
    return hexA;
}
function bytesToHex(arr) {
    var str = '';
    var k, j;
    for (var i = 0; i < arr.length; i++) {
        k = arr[i];
        j = k;
        if (k < 0) {
            j = k + 256;
        }
        if (j < 16) {
            str += "0";
        }
        str += j.toString(16);
    }
    return str;
}
function stringToHex(str) {
    var val = "";
    for (var i = 0; i < str.length; i++) {
        if (val == "")
            val = str.charCodeAt(i).toString(16);
        else
            val += str.charCodeAt(i).toString(16);
    }
    return val
}
function stringToBytes(str) {
    var ch, st, re = [];
    for (var i = 0; i < str.length; i++) {
        ch = str.charCodeAt(i);
        st = [];
        do {
            st.push(ch & 0xFF);
            ch = ch >> 8;
        }
        while (ch);
        re = re.concat(st.reverse());
    }
    return re;
}
//将byte[]转成String的方法
function bytesToString(arr) {
    var str = '';
    arr = new Uint8Array(arr);
    for (var i in arr) {
        str += String.fromCharCode(arr[i]);
    }
    return str;
}
function bytesToBase64(e) {
    var r, a, c, h, o, t;
    for (c = e.length, a = 0, r = ''; a < c;) {
        if (h = 255 & e[a++], a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4),
                r += '==';
            break
        }
        if (o = e[a++], a == c) {
            r += base64EncodeChars.charAt(h >> 2),
                r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
                r += base64EncodeChars.charAt((15 & o) << 2),
                r += '=';
            break
        }
        t = e[a++],
            r += base64EncodeChars.charAt(h >> 2),
            r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4),
            r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6),
            r += base64EncodeChars.charAt(63 & t)
    }
    return r
}
function base64ToBytes(e) {
    var r, a, c, h, o, t, d;
    for (t = e.length, o = 0, d = []; o < t;) {
        do
            r = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && r == -1);
        if (r == -1)
            break;
        do
            a = base64DecodeChars[255 & e.charCodeAt(o++)];
        while (o < t && a == -1);
        if (a == -1)
            break;
        d.push(r << 2 | (48 & a) >> 4);
        do {
            if (c = 255 & e.charCodeAt(o++), 61 == c)
                return d;
            c = base64DecodeChars[c]
        } while (o < t && c == -1);
        if (c == -1)
            break;
        d.push((15 & a) << 4 | (60 & c) >> 2);
        do {
            if (h = 255 & e.charCodeAt(o++), 61 == h)
                return d;
            h = base64DecodeChars[h]
        } while (o < t && h == -1);
        if (h == -1)
            break;
        d.push((3 & c) << 6 | h)
    }
    return d
}
//stringToBase64 stringToHex stringToBytes
//base64ToString base64ToHex base64ToBytes
//               hexToBase64  hexToBytes    
// bytesToBase64 bytesToHex bytesToString

Java.perform(function () {
    var secretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');
    secretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function (a, b) {
        showStacks();
        var result = this.$init(a, b);
        console.log("======================================");
        console.log("算法名:" + b + "|str密钥:" + bytesToString(a));
        console.log("算法名:" + b + "|Hex密钥:" + bytesToHex(a));
        return result;
    }

    var DESKeySpec = Java.use('javax.crypto.spec.DESKeySpec');
    DESKeySpec.$init.overload('[B').implementation = function (a) {
        showStacks();
        var result = this.$init(a);
        console.log("======================================");
        var bytes_key_des = this.getKey();
        console.log("des密钥  |str " + bytesToString(bytes_key_des));
        console.log("des密钥  |hex " + bytesToHex(bytes_key_des));
        return result;
    }

    DESKeySpec.$init.overload('[B', 'int').implementation = function (a, b) {
        showStacks();
        var result = this.$init(a, b);
        console.log("======================================");
        var bytes_key_des = this.getKey();
        console.log("des密钥  |str " + bytesToString(bytes_key_des));
        console.log("des密钥  |hex " + bytesToHex(bytes_key_des));
        return result;
    }

    var mac = Java.use('javax.crypto.Mac');
    mac.getInstance.overload('java.lang.String').implementation = function (a) {
        showStacks();
        var result = this.getInstance(a);
        console.log("======================================");
        console.log("算法名:" + a);
        return result;
    }
    mac.update.overload('[B').implementation = function (a) {
        //showStacks();
        this.update(a);
        console.log("======================================");
        console.log("update:" + bytesToString(a))
    }
    mac.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
        //showStacks();
        this.update(a, b, c)
        console.log("======================================");
        console.log("update:" + bytesToString(a) + "|" + b + "|" + c);
    }
    mac.doFinal.overload().implementation = function () {
        //showStacks();
        var result = this.doFinal();
        console.log("======================================");
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }
    mac.doFinal.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.doFinal(a);
        console.log("======================================");
        console.log("doFinal参数: |str  :"     + bytesToString(a));
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }

    var md = Java.use('java.security.MessageDigest');
    md.getInstance.overload('java.lang.String', 'java.lang.String').implementation = function (a, b) {
        //showStacks();
        console.log("======================================");
        console.log("算法名:" + a);
        return this.getInstance(a, b);
    }
    md.getInstance.overload('java.lang.String').implementation = function (a) {
        //showStacks();
        console.log("======================================");
        console.log("算法名:" + a);
        return this.getInstance(a);
    }
    md.update.overload('[B').implementation = function (a) {
        //showStacks();
        console.log("======================================");
        console.log("update:" + bytesToString(a))
        return this.update(a);
    }
    md.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
        //showStacks();
        console.log("======================================");
        console.log("update:" + bytesToString(a) + "|" + b + "|" + c);
        return this.update(a, b, c);
    }
    md.digest.overload().implementation = function () {
        //showStacks();
        console.log("======================================");
        var result = this.digest();
        console.log("digest结果:" + bytesToHex(result));
        console.log("digest结果:" + bytesToBase64(result));
        return result;
    }
    md.digest.overload('[B').implementation = function (a) {
        //showStacks();
        console.log("======================================");
        console.log("digest参数:" + bytesToString(a));
        var result = this.digest(a);
        console.log("digest结果:" + bytesToHex(result));
        console.log("digest结果:" + bytesToBase64(result));
        return result;
    }

    var ivParameterSpec = Java.use('javax.crypto.spec.IvParameterSpec');
    ivParameterSpec.$init.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.$init(a);
        console.log("======================================");
        console.log("iv向量: |str:" + bytesToString(a));
        console.log("iv向量: |hex:" + bytesToHex(a));
        return result;
    }

    var cipher = Java.use('javax.crypto.Cipher');
    cipher.getInstance.overload('java.lang.String').implementation = function (a) {
        //showStacks();
        var result = this.getInstance(a);
        console.log("======================================");
        console.log("模式填充:" + a);
        return result;
    }
    cipher.init.overload('int', 'java.security.Key').implementation = function (a, b) {
        //showStacks();
        var result = this.init(a, b);
        console.log("======================================");
        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str密钥:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function (a, b) {
        //showStacks();
        var result = this.init(a, b);
        console.log("======================================");

        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        console.log("======================================");

        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str密钥:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key));

        return result;
    }
    cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

         var bytes_key = b.getEncoded();
        console.log("init key:" + "|str密钥:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function (a, b, c) {
        //showStacks();
        var result = this.init(a, b, c);
        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str密钥:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function (a, b, c, d) {
        //showStacks();
        var result = this.init(a, b, c, d);
        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

        var bytes_key = b.getEncoded();
        console.log("init key:" + "|str密钥:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key));
        return result;
    }
    cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function (a, b, c, d) {
        //showStacks();
        var result = this.update(a, b, c, d);
        if (N_ENCRYPT_MODE == a)
        {
            console.log("init  | 加密模式");    
        }
        else if(N_DECRYPT_MODE == a)
        {
            console.log("init  | 解密模式");    
        }

         var bytes_key = b.getEncoded();
        console.log("init key:" + "|str密钥:" + bytesToString(bytes_key));
        console.log("init key:" + "|Hex密钥:" + bytesToHex(bytes_key));
        return result;
    }

    cipher.update.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.update(a);
        console.log("======================================");
        console.log("update:" + bytesToString(a));
        return result;
    }
    cipher.update.overload('[B', 'int', 'int').implementation = function (a, b, c) {
        //showStacks();
        var result = this.update(a, b, c);
        console.log("======================================");
        console.log("update:" + bytesToString(a) + "|" + b + "|" + c);
        return result;
    }
    cipher.doFinal.overload().implementation = function () {
        //showStacks();
        var result = this.doFinal();
        console.log("======================================");
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }
    cipher.doFinal.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.doFinal(a);
        console.log("======================================");
        console.log("doFinal参数: |str  :"     + bytesToString(a));
        console.log("doFinal结果: |str  :"     + bytesToString(result));
        console.log("doFinal结果: |hex  :"     + bytesToHex(result));
        console.log("doFinal结果: |base64  :"  + bytesToBase64(result));
        return result;
    }

    var x509EncodedKeySpec = Java.use('java.security.spec.X509EncodedKeySpec');
    x509EncodedKeySpec.$init.overload('[B').implementation = function (a) {
        //showStacks();
        var result = this.$init(a);
        console.log("======================================");
        console.log("RSA密钥:" + bytesToBase64(a));
        return result;
    }

    var rSAPublicKeySpec = Java.use('java.security.spec.RSAPublicKeySpec');
    rSAPublicKeySpec.$init.overload('java.math.BigInteger', 'java.math.BigInteger').implementation = function (a, b) {
        //showStacks();
        var result = this.$init(a, b);
        console.log("======================================");
        //console.log("RSA密钥:" + bytesToBase64(a));
        console.log("RSA密钥N:" + a.toString(16));
        console.log("RSA密钥E:" + b.toString(16));
        return result;
    }

    var KeyPairGenerator = Java.use('java.security.KeyPairGenerator');
    KeyPairGenerator.generateKeyPair.implementation = function ()
    {
        //showStacks();
        var result = this.generateKeyPair();
        console.log("======================================");

        var str_private = result.getPrivate().getEncoded();
        var str_public = result.getPublic().getEncoded();
        console.log("公钥  |hex" + bytesToHex(str_public));
        console.log("私钥  |hex" + bytesToHex(str_private));

        return result;
    }

    KeyPairGenerator.genKeyPair.implementation = function ()
    {
        //showStacks();
        var result = this.genKeyPair();
        console.log("======================================");

        var str_private = result.getPrivate().getEncoded();
        var str_public = result.getPublic().getEncoded();
        console.log("公钥  |hex" + bytesToHex(str_public));
        console.log("私钥  |hex" + bytesToHex(str_private));

        return result;
    }
});

总结:

使用 frida hook 的原理, 是直接劫持java加密函数, 不需要去逆向代码直接, 效率很高!

对于使用 java 原生开发的应用 效率很高! 但是相对于 flutter 开发的应用效果就比较差了

 

后面抽空研究研究  fiida 在 flutter 身上的新姿势

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/943472.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Ansys Zemax | 手机镜头设计 - 第 2 部分:使用 OpticsBuilder 实现光机械封装

本文是3篇系列文章的一部分&#xff0c;该系列文章将讨论智能手机镜头模块设计的挑战&#xff0c;从概念、设计到制造和结构变形的分析。本文是三部分系列的第二部分。概括介绍了如何在 CAD 中编辑光学系统的光学元件以及如何在添加机械元件后使用 Zemax OpticsBuilder 分析系统…

MySQL 存储引擎详解

文章目录 0.前言1.基础介绍2.1. InnoDB存储引擎底层原理InnoDB记录存储结构和索引页结构InnoDB记录存储结构&#xff1a;InnoDB索引页结构&#xff1a; 3. MVCC 详解3.1. 版本号分配&#xff1a;3.2. 数据读取&#xff1a;3.3. 数据写入&#xff1a;3.4. 事务隔离级别&#xff…

剑指offer(C++)-JZ29:顺时针打印矩阵(算法-模拟)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 输入一个矩阵&#xff0c;按照从外向里以顺时针的顺序依次打印出每一个数字&#xff0c;例如&#xff0c;…

JDK配置环境变量(超详细)

1.打开高级系统设置 电脑右键--属性&#xff0c;或者搜索“高级系统设置” 找到高级系统设置 2.环境变量 高级--环境变量 3.配置JAVA_HOME JAVA_HOME 4.添加到Path中 %JAVA_HOME%\bin 5.测试是否配置成功 快捷键winR,输入cmd&#xff0c;回车 打开黑屏命令窗口。 如果配…

windows11显示未激活怎么办

遇到的问题 本人遇到的问题&#xff0c;win11家庭中文版 reset后&#xff0c;系统正常使用&#xff0c;但是系统状态是未激活&#xff0c;未激活就意味着某些功能不可用&#xff0c;比如我就是想改一下系统主题&#xff0c;就是不能改&#xff0c;因为系统没有激活&#xff0c…

开学季触控笔买哪种好?ipad可以用的性价比手写笔

虽然苹果的平板电脑价格不菲&#xff0c;但如果你有足够的资金&#xff0c;还是可以考虑购买一台。而且&#xff0c;这款iPad配上了一支电容笔&#xff0c;不仅可以用来绘画&#xff0c;还可以用来做笔记。苹果的原装电容笔&#xff0c;虽然功能很强&#xff0c;但价格很贵&…

当摩托不再“摩登”,谁又在风口狂飙?

【潮汐商业评论/文】 “你看&#xff0c;这女生骑摩托车真是泰酷辣。” Jessica举着手机里一个女生骑摩托的视频对闺蜜说。 对于很多人来说&#xff0c;摩托车都是一个极具独特魅力的出行工具。硬朗的机车、酷帅的骑行服&#xff0c;配上发动机的轰鸣声&#xff0c;似乎看到…

上海港股通开通条件是什么?港股通交易佣金最低多少?

上海港股通权限开通条件有&#xff1a; 1、申请权限开通前20个交易日证券账户日均资产不低于50万元; 2、进行港股知识测试&#xff0c;且测试分数不低于80分; 3、风险承受能力等级需要匹配&#xff0c;无投资经验期限的门槛 港股通的股票范围是香港联合交易所恒生综合大型股…

[ES]二基础 |

一、索引库操作 1、mapping属性 mapping是对索引库中文档的约束&#xff0c;常见的mapping属性包括&#xff1a; 1)type&#xff1a;字段数据类型&#xff0c;常见的简单类型有&#xff1a; ①字符串&#xff1a;text(可分词的文本)、keyword&#xff08;精确值&#xff0c…

直播预约丨《实时湖仓实践五讲》第一讲:企业为什么需要实时湖仓?

如今&#xff0c;大规模、高时效、智能化数据处理已是“刚需”&#xff0c;企业需要更强大的数据平台&#xff0c;来应对数据查询、数据处理、数据挖掘、数据展示以及多种计算模型并行的挑战&#xff0c;湖仓一体方案应运而生。 《实时湖仓实践五讲》是袋鼠云打造的系列直播活…

详解转置卷积 代码实现

转置卷积 计算输出的形状公式 输出矩阵的高 (输入的高-1) x 高方向的步长 - 2 x 高方向的填充 卷积核的高 输出矩阵的宽 (输入的宽-1) x 宽方向的步长 - 2 x 宽方向的填充 卷积核的宽举例计算&#xff1a;当输入为2x2&#xff0c;转置卷积核形状为2x2的情况 代入公式验证…

传统企业如何进行数字化转型升级?

传统企业通过一系列战略和运营变革进行数字化转型升级&#xff0c;利用技术增强业务流程、改善客户体验&#xff0c;在数字化时代保持竞争力。以下是他们通常如何处理此过程的分步概述&#xff1a; 1.评估和战略制定&#xff1a; 了解当前状态&#xff1a;评估现有业务流程、…

【Cesium创造属于你的地球】实现地球展示、灵活进行坐标转换、视角切换

大家好&#xff0c;我是AIC山鱼&#xff01;&#x1f449;这是我的主页 &#x1f40b;作为CSDN博主和前端优质创作者✍&#xff0c;我致力于为大家带来新颖、脱俗且有趣的内容。 &#x1f431;我还创建了山鱼社区&#xff0c;这是一个独特的社区&#x1f3e0;&#xff0c;&…

windows服务器查看网络带宽

windows服务器查看网络带宽&#xff1f; 鼠标右键单击win标志&#xff0c;进入计算机管理 另外一个方法&#xff1a;

前端须知名词解释

目录 一、多维转一维 二、一维转多维 一维转多维——使用场景&#xff1a;分页 三、判断当前元素是否为数组 四、判断当前元素是否是空对象 五、数字分割符&#xff1a;提高数字可读性 六、模糊盒子&#xff08;怪异盒子&#xff09;与标准盒模型 七、css的filter属性 …

代码随想录算法训练营day42 | 01背包问题,416. 分割等和子集

目录 01背包问题 416. 分割等和子集 01背包问题 416. 分割等和子集 类型&#xff1a;动态规划&#xff0c;01背包 难度&#xff1a;medium 思路: 经典的01背包问题&#xff0c;背包容量为sum/2, 每个物品的重量为nums[i],其价值也为nums[i]。 需要注意的是&#xff0c;如果…

数字世界的不朽之旅,探索人类意识的未来归宿!

在莎士比亚名剧《哈姆雷特》中&#xff0c;有一句经典的独白“生存还是死亡&#xff0c;这是一个值得考虑的问题”&#xff0c;令人沉思。数百年后的今天&#xff0c;随着人工智能技术的不断发展&#xff0c;数字永生正在改变着生死的边界&#xff0c;使得“有的人死了&#xf…

Ctenos7安装mysql-8.1.0/tomcat-9.0.80/LNMT部署

目录 一、实验拓扑 二、部署mysql 三、部署Tomcat 四、配置NGINX 五、 配置NGINX的双机热备提高可用性 一、实验拓扑 二、部署mysql 官网下载地址https://dev.mysql.com/downloads/mysql/ 1、移除mariadb&#xff0c;安装所需应用 mysql-8.1.0 社区版 安装说明官网下载地址…

【高等数学基础知识篇】——函数,极限与连续

本文仅用于个人学习记录&#xff0c;使用的教材为汤家凤老师的《高等数学辅导讲义》。本文无任何盈利或者赚取个人声望的目的&#xff0c;如有侵权&#xff0c;请联系删除&#xff01; 文章目录 一、函数基础知识1.1 基本初等函数和初等函数1.2 函数的初等特性1.3 特殊函数 二、…

网络综合布线专业技能人才培养方案

一、网络综合布线专业技能人才培养方案概述 网络综合布线专业技能人才培养方案旨在培养具备综合布线系统设计、安装与维护的全面技能和知识的专业人才。以下是一个概述&#xff1a; 培养目标&#xff1a;培养具备综合布线系统的理论基础和实践操作技能的专业人才&#xff0c;能…