android frida 逆向 自吐加密算法

前言:

♛  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 pipC:\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
27043C:\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 = 2function 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;) {dor = base64DecodeChars[255 & e.charCodeAt(o++)];while (o < t && r == -1);if (r == -1)break;doa = 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);elseval += 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;) {dor = base64DecodeChars[255 & e.charCodeAt(o++)];while (o < t && r == -1);if (r == -1)break;doa = 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 bytesToStringJava.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.mzph.cn/news/64369.shtml

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

相关文章

开箱报告,Simulink Toolbox库模块使用指南(五)——S-Fuction模块(C MEX S-Function)

文章目录 前言 C MEX S-Function 算法原理 原始信号创建 编写S函数 仿真验证 Tips 分析和应用 总结 前言 见《开箱报告&#xff0c;Simulink Toolbox库模块使用指南&#xff08;一&#xff09;——powergui模块》 见《开箱报告&#xff0c;Simulink Toolbox库模块使用…

PhpStorm安装篇

PhpStorm安装篇: 下载地址 : 进入官网下PhpStorm: PHP IDE and Code Editor from JetBrains 下载完之后&#xff0c;安装包 安装目录建议不要放C盘&#xff0c;放其他盘&#xff0c;其他直接一直点 next &#xff0c;到结束 安装完&#xff0c;打开编辑器 注册账号并登录后…

Android投屏总结

#android手机投屏 ####导语 至于手机投屏的实现方法可谓五花八门&#xff0c;今天小袁就说下以开发人员的角度来说下当今手机的主流投屏方法。目前这种将终端信号经由WiFi传输到电视、电视盒的技术有三种&#xff1a;DLNA、AirPlay、Miracast、Google Cast。 ##手机投屏智能电…

python 深度学习 解决遇到的报错问题3

目录 一、AttributeError: The vocab attribute was removed from KeyedVector in Gensim 4.0.0. 二、ImportError: cannot import name logsumexp 三、FutureWarning: Passing (type, 1) or 1type as a synonym of type is deprecated; in a future version of numpy, it w…

VBA:对Excel单元格进行合并操作

Sub hb()Dim nn 3For i 3 To 18If Range("b" & i) <> Range("b" & i 1) ThenRange("b" & n & ":b" & i).Mergen i 1End IfNextEnd Sub

记录--前端使用a链接下载内容增加loading效果

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 问题描述&#xff1a;最近工作中出现一个需求&#xff0c;纯前端下载 Excel 数据&#xff0c;并且有的下载内容很多&#xff0c;这时需要给下载增加一个 loading 效果。 代码如下&#xff1a; // util…

Maven入门教程(三):Maven语法

视频教程&#xff1a;Maven保姆级教程 Maven入门教程(一)&#xff1a;安装Maven环境 Maven入门教程(二)&#xff1a;idea/Eclipse使用Maven Maven入门教程(三)&#xff1a;Maven语法 Maven入门教程(四)&#xff1a;Nexus私服 Maven入门教程(五)&#xff1a;自定义脚手架 6.Mav…

Loki日志系统

1、Loki是什么&#xff1f; Loki是一个开源的日志聚合系统&#xff0c;由Grafana Labs开发和维护。它旨在帮助用户收集、存储和查询大规模的日志数据&#xff0c;帮助用户更好地理解和监控他们的应用程序和系统。 Loki的设计灵感来自于Prometheus&#xff0c;它采用了类似的标…

【小沐学Unity3d】3ds Max 骨骼动画制作(蒙皮修改器skin)

文章目录 1、简介2、蒙皮修改器3.1 骨骼对象测试3.2 Biped对象测试 3、动画制作4、FBX导出结语 1、简介 “蒙皮”修改器是一种骨骼变形工具&#xff0c;主要设计用于通过另一个对象对一个对象进行变形来创建角色动画。可使用骨骼、样条线和其他对象变形网格、面片和 NURBS 对象…

python3+requests:接口自动化测试(二)

前言&#xff1a;上篇文章python3requestsunittest&#xff1a;接口自动化测试&#xff08;一&#xff09;&#xff1a;已经介绍了基于unittest框架的实现接口自动化&#xff0c;但是也存在一些问题&#xff0c;比如最明显的测试数据和业务没有区分开&#xff0c;接口用例不便于…

【机器学习】线性回归

Model Representation 1、问题描述2、表示说明3、数据绘图4、模型函数5、预测总结附录 1、问题描述 一套 1000 平方英尺 (sqft) 的房屋售价为300,000美元&#xff0c;一套 2000 平方英尺的房屋售价为500,000美元。这两点将构成我们的数据或训练集。面积单位为 1000 平方英尺&a…

2010-2021年上市公司和讯网社会责任评级CSR数据/和讯网上市公司社会责任数据

2010-2021年上市公司和讯网社会责任评级CSR数据 1、时间&#xff1a;2010-2021年 2、指标&#xff1a;股票名称、股票代码、年份、总得分、等级、股东责任、员工责任、供应商、客户和消费者权益责任、环境责任、社会责任、所属年份 3、样本量&#xff1a;4万 4、来源&#…

数据结构与算法基础-学习-31-交换排序之冒泡排序、快速排序

排序的其他相关知识点和源码分享可以参考之前的博客&#xff1a; 《数据结构与算法基础-学习-30-插入排序之直接插入排序、二分插入排序、希尔排序》 一、交换排序基本思想 两两比较&#xff0c;如果发生逆序则交换位置&#xff0c;直到所有数据记录都排好序为止。 二、冒…

大模型理解之CLIP

前言 2021年2月份&#xff0c;CLIP模型被提出&#xff0c;想法很简单&#xff0c;性能高效&#xff0c;而且具备很好的泛化性。我在这里简单谈论下我对CLIP模型的理解&#xff0c;以及发现的一些问题。 我是在沐神的视频中了解的CLIP, 里面提到CLIP最大的贡献在于打破了固定类…

四轴飞行器的电池研究(MatlabSimulink仿真)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

全球免费编程教育网站:Code.org

全球免费编程教育网站&#xff1a;Code.org 官网地址注册使用 你还在为小朋友的编程教育而发愁吗&#xff1f; 你还在为小朋友放假无聊而头疼吗&#xff1f; 他来了他来了&#xff0c;全球免费编程教育网站来了。 2013年成立的Code.org是一个非营利组织。 它致力于为年轻女子、…

Xilinx UltraScale架构之可配置逻辑块CLB

目录 一、概览 二、UltraScale架构 2.1 UltraScale/UltraScale特点 2.2 与7系列CLB差异 三、 CLB结构 3.1 LUT 3.2 FF 3.3 多路选择器Multiplexers 3.4 进位链Carry Chain 四、应用 4.1 分布式RAM 4.2 移位寄存器 4.3 进位链Carry Chain 五、参考资料 一、概览 二…

专门针对开发人员,攻击者利用Rust获取操作系统信息

近日&#xff0c;研究人员在 Rust 编程语言的 crate 注册表中发现了一些恶意软件包&#xff0c;专门针对开发人员。 Phylum 在上周发布的一份报告中称&#xff0c;这些库是由一个名为 "amaperf "的用户在 2023 年 8 月 14 日至 16 日之间上传的。现已删除的软件包名…

【LeetCode-中等题】114. 二叉树展开为链表

文章目录 题目方法一&#xff1a;前序遍历&#xff08;构造集合&#xff09; 集合&#xff08;构造新树&#xff09;方法二&#xff1a;原地构建方法三&#xff1a;前序遍历--迭代&#xff08;构造集合&#xff09; 集合&#xff08;构造新树&#xff09; 题目 方法一&#x…

el-select 选择一条数据后,把其余数据带过来

1. 案例&#xff1a; ps: 票号是下拉框选择&#xff0c;风险分类、场站名称以及开始时间是选择【票号】后带过来的。 2. 思路: 使用官网上给的方法&#xff0c;选择之后&#xff0c;触发change方法从而给其余字段赋值 3. 代码 <el-form-itemlabel"票号&#xff1a;&…