某物登录表单加密
之前分析过某物h5的以及小程序的搜索接口,就是一个aes,秘钥不固定,表单里把秘钥以及密文一起发过去,服务器解密后再把数据加密返回,客户端解密展示到页面上.
这期是关于app的登录,密码登录
声明
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!
我创建了一个js 安卓 ios逆向的技术交流群,刚兴趣的可以加一下,在最后面.
抓包
可以看到表单里有newSign,password,userName加密了,都是32位的.并且响应也是加密的.
再来看看头部,框中的那几个都是可能需要分析的,其中有一个shumeiid,这个是数美sdk里的.这个难度估计有点大,这个我们下期分析,SK和edk和ltk自行研究,后面看了下so的结构,就算是so的难度应该也不会太大.这几个貌似都没变,但是ltk是一直变化的.这个也留着后面分析.
反编译
先查下壳,没加固,总算碰到一个没加固的,前几期都是加固的,看来是对自己的产品比较有信心.
尝试搜索下字符串username
有点多,这样一个个点过去太麻烦了,尝试hook hashmap
var hashMap = Java.use("java.util.HashMap");
hashMap.put.implementation = function (a, b) {if(a!=null && a.equals("userName")){console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))console.log("hashMap.put: ", a, b);}return this.put(a, b);
}
frida反调试
frida启动后以attach注入
直接断开,同时app会重启,这里用的已经是去过特征版的frida了,葫芦娃编译的,还是被检测到了.
以spawn方式启动试试
直接断开并没有重启,来看看它加载了哪些so.肯定是so的检测
var dlopen = Module.findExportByName(null, "dlopen");
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");Interceptor.attach(dlopen, {onEnter: function (args) {var path_ptr = args[0];var path = ptr(path_ptr).readCString();console.log("[dlopen:]", path);},onLeave: function (retval) {}
});
Interceptor.attach(android_dlopen_ext, {onEnter: function (args) {var path_ptr = args[0];var path = ptr(path_ptr).readCString();console.log("[dlopen_ext:]", path);},onLeave: function (retval) {}
});
libmsaoaidsec.so mobile security alliance open aid security,字面意思,看着就不是什么好东西,网上有分析libmsaoaidsec.so的文章,自行检索,我这里是直接把它删掉,有些app可以这样过,有些删了会闪退,闪退的话只能把它过掉.
删掉后frida可以正常使用,直接attach即可.
看到mobileLogin字眼,jadx中点过去看看
f.c方法点过去看看
看到了AES ECB字眼,应该是java层的,这样的话其他的几个也有可能是java层的,但是在上面图中的hashmap中没有看到newsign字眼,这个app用的okhttp3框架,极有可能是用的okhttp3.Interceptor添加的.因为有可能是java层的,有3个参数,当然优先考虑java通杀方案.
Java.perform(function () {function showStacks() {console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));}var ByteString = Java.use("com.android.okhttp.okio.ByteString");function toBase64(tag, data) {//logOutPut(tag + " Base64: " + ByteString.of(data).base64());console.log(tag + " Base64: " + ByteString.of(data).base64());}function toHex(tag, data) {//logOutPut(tag + " Hex: " + ByteString.of(data).hex());console.log(tag + " Hex: " + ByteString.of(data).hex());}function toUtf8(tag, data) {//logOutPut(tag + " Utf8: " + ByteString.of(data).utf8());console.log(tag + " Utf8: " + ByteString.of(data).utf8());}var messageDigest = Java.use("java.security.MessageDigest");messageDigest.update.overload('byte').implementation = function (data) {console.log("MessageDigest.update('byte') is called!");showStacks();return this.update(data);}messageDigest.update.overload('java.nio.ByteBuffer').implementation = function (data) {console.log("MessageDigest.update('java.nio.ByteBuffer') is called!");showStacks();return this.update(data);}messageDigest.update.overload('[B').implementation = function (data) {console.log("MessageDigest.update('[B') is called!");showStacks();var algorithm = this.getAlgorithm();var tag = algorithm + " update data";toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);console.log("=======================================================");return this.update(data);}messageDigest.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {console.log("MessageDigest.update('[B', 'int', 'int') is called!");showStacks();var algorithm = this.getAlgorithm();var tag = algorithm + " update data";toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);console.log("=======================================================", start, length);return this.update(data, start, length);}messageDigest.digest.overload().implementation = function () {console.log("MessageDigest.digest() is called!");showStacks();var result = this.digest();var algorithm = this.getAlgorithm();var tag = algorithm + " digest result";toHex(tag, result);toBase64(tag, result);console.log("=======================================================");return result;}messageDigest.digest.overload('[B').implementation = function (data) {console.log("MessageDigest.digest('[B') is called!");showStacks();var algorithm = this.getAlgorithm();var tag = algorithm + " digest data";toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);var result = this.digest(data);var tags = algorithm + " digest result";toHex(tags, result);toBase64(tags, result);console.log("=======================================================");return result;}messageDigest.digest.overload('[B', 'int', 'int').implementation = function (data, start, length) {console.log("MessageDigest.digest('[B', 'int', 'int') is called!");showStacks();var algorithm = this.getAlgorithm();var tag = algorithm + " digest data";toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);var result = this.digest(data, start, length);var tags = algorithm + " digest result";toHex(tags, result);toBase64(tags, result);console.log("=======================================================", start, length);return result;}var mac = Java.use("javax.crypto.Mac");mac.init.overload('java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function (key, AlgorithmParameterSpec) {console.log("Mac.init('java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!");return this.init(key, AlgorithmParameterSpec);}mac.init.overload('java.security.Key').implementation = function (key) {console.log("Mac.init('java.security.Key') is called!");var algorithm = this.getAlgorithm();var tag = algorithm + " init Key";var keyBytes = key.getEncoded();toUtf8(tag, keyBytes);toHex(tag, keyBytes);toBase64(tag, keyBytes);console.log("=======================================================");return this.init(key);}mac.update.overload('byte').implementation = function (data) {console.log("Mac.update('byte') is called!");return this.update(data);}mac.update.overload('java.nio.ByteBuffer').implementation = function (data) {console.log("Mac.update('java.nio.ByteBuffer') is called!");return this.update(data);}mac.update.overload('[B').implementation = function (data) {console.log("Mac.update('[B') is called!");var algorithm = this.getAlgorithm();var tag = algorithm + " update data";toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);console.log("=======================================================");return this.update(data);}mac.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {console.log("Mac.update('[B', 'int', 'int') is called!");var algorithm = this.getAlgorithm();var tag = algorithm + " update data";toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);console.log("=======================================================", start, length);return this.update(data, start, length);}mac.doFinal.overload().implementation = function () {console.log("Mac.doFinal() is called!");var result = this.doFinal();var algorithm = this.getAlgorithm();var tag = algorithm + " doFinal result";toHex(tag, result);toBase64(tag, result);console.log("=======================================================");return result;}var cipher = Java.use("javax.crypto.Cipher");cipher.init.overload('int', 'java.security.cert.Certificate').implementation = function () {console.log("Cipher.init('int', 'java.security.cert.Certificate') is called!");return this.init.apply(this, arguments);}cipher.init.overload('int', 'java.security.Key', 'java.security.SecureRandom').implementation = function () {console.log("Cipher.init('int', 'java.security.Key', 'java.security.SecureRandom') is called!");return this.init.apply(this, arguments);}cipher.init.overload('int', 'java.security.cert.Certificate', 'java.security.SecureRandom').implementation = function () {console.log("Cipher.init('int', 'java.security.cert.Certificate', 'java.security.SecureRandom') is called!");return this.init.apply(this, arguments);}cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom').implementation = function () {console.log("Cipher.init('int', 'java.security.Key', 'java.security.AlgorithmParameters', 'java.security.SecureRandom') is called!");return this.init.apply(this, arguments);}cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom').implementation = function () {console.log("Cipher.init('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec', 'java.security.SecureRandom') is called!");return this.init.apply(this, arguments);}cipher.init.overload('int', 'java.security.Key', 'java.security.AlgorithmParameters').implementation = function () {console.log("Cipher.init('int', 'java.security.Key', 'java.security.AlgorithmParameters') is called!");return this.init.apply(this, arguments);}cipher.init.overload('int', 'java.security.Key').implementation = function () {console.log("Cipher.init('int', 'java.security.Key') is called!");var algorithm = this.getAlgorithm();var tag = algorithm + " init Key";var className = JSON.stringify(arguments[1]);if(className.indexOf("OpenSSLRSAPrivateKey") === -1){var keyBytes = arguments[1].getEncoded();toUtf8(tag, keyBytes);toHex(tag, keyBytes);toBase64(tag, keyBytes);}console.log("=======================================================");return this.init.apply(this, arguments);}cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function () {console.log("Cipher.init('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec') is called!");var algorithm = this.getAlgorithm();var tag = algorithm + " init Key";var keyBytes = arguments[1].getEncoded();toUtf8(tag, keyBytes);toHex(tag, keyBytes);toBase64(tag, keyBytes);var tags = algorithm + " init iv";var iv = Java.cast(arguments[2], Java.use("javax.crypto.spec.IvParameterSpec"));var ivBytes = iv.getIV();toUtf8(tags, ivBytes);toHex(tags, ivBytes);toBase64(tags, ivBytes);console.log("=======================================================");return this.init.apply(this, arguments);}cipher.doFinal.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function () {console.log("Cipher.doFinal('java.nio.ByteBuffer', 'java.nio.ByteBuffer') is called!");showStacks();return this.doFinal.apply(this, arguments);}cipher.doFinal.overload('[B', 'int').implementation = function () {console.log("Cipher.doFinal('[B', 'int') is called!");showStacks();return this.doFinal.apply(this, arguments);}cipher.doFinal.overload('[B', 'int', 'int', '[B').implementation = function () {console.log("Cipher.doFinal('[B', 'int', 'int', '[B') is called!");showStacks();return this.doFinal.apply(this, arguments);}cipher.doFinal.overload('[B', 'int', 'int', '[B', 'int').implementation = function () {console.log("Cipher.doFinal('[B', 'int', 'int', '[B', 'int') is called!");showStacks();return this.doFinal.apply(this, arguments);}cipher.doFinal.overload().implementation = function () {console.log("Cipher.doFinal() is called!");showStacks();return this.doFinal.apply(this, arguments);}cipher.doFinal.overload('[B').implementation = function () {console.log("Cipher.doFinal('[B') is called!");showStacks();var algorithm = this.getAlgorithm();var tag = algorithm + " doFinal data";var data = arguments[0];toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);var result = this.doFinal.apply(this, arguments);var tags = algorithm + " doFinal result";toHex(tags, result);toBase64(tags, result);console.log("=======================================================");return result;}cipher.doFinal.overload('[B', 'int', 'int').implementation = function () {console.log("Cipher.doFinal('[B', 'int', 'int') is called!");showStacks();var algorithm = this.getAlgorithm();var tag = algorithm + " doFinal data";var data = arguments[0];toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);var result = this.doFinal.apply(this, arguments);var tags = algorithm + " doFinal result";toHex(tags, result);toBase64(tags, result);console.log("=======================================================", arguments[1], arguments[2]);return result;}var signature = Java.use("java.security.Signature");signature.update.overload('byte').implementation = function (data) {console.log("Signature.update('byte') is called!");return this.update(data);}signature.update.overload('java.nio.ByteBuffer').implementation = function (data) {console.log("Signature.update('java.nio.ByteBuffer') is called!");return this.update(data);}signature.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {console.log("Signature.update('[B', 'int', 'int') is called!");var algorithm = this.getAlgorithm();var tag = algorithm + " update data";toUtf8(tag, data);toHex(tag, data);toBase64(tag, data);console.log("=======================================================", start, length);return this.update(data, start, length);}signature.sign.overload('[B', 'int', 'int').implementation = function () {console.log("Signature.sign('[B', 'int', 'int') is called!");return this.sign.apply(this, arguments);}signature.sign.overload().implementation = function () {console.log("Signature.sign() is called!");var result = this.sign();var algorithm = this.getAlgorithm();var tag = algorithm + " sign result";toHex(tag, result);toBase64(tag, result);console.log("=======================================================");return result;}
});
不过我比较喜欢用算法助手,原理是一样的.先在lsp里把目标app勾上,算法助手页面把这个app打开
全部勾上后重启app,登录一下,抓包同时开着,这个包里的
{"cipherParam": "userName","countryCode": 86,"loginToken": "","newSign": "614a0f4d2ef049ff1d0145b0a96d2ef0","password": "ca8f119a27ec17f98b463807cd0b6b62","platform": "android","timestamp": "1711714586437","type": "pwd","userName": "83c7358c94e3a24e11f0fde1cbf5d559_1","v": "5.38.5"
}
userName
日志里搜一下83c7358c94e3a24e11f0fde1cbf5d559,后面的_1自己添加上去的.
有结果
aes ecb nopadding,无填充是因为已经填充过了,看明文后面那一串,直接用pkcs7就可以了.userName解决
password
直接搜ca8f119a27ec17f98b463807cd0b6b62
密码加一个盐值,没什么好说的.
newSign
搜一下614a0f4d2ef049ff1d0145b0a96d2ef0
md5签的一串base64值.搜索这个值有没有结果,搜一部分dWWoXlbR3K87j2N27Dkv4uOPUnOsh0xrJ5t
没有搜到,这个值是base64过的,尝试hook一下base64的encodeToString方法
var base64 = Java.use("android.util.Base64");
base64.encodeToString.overload('[B', 'int').implementation = function (a, b) {
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
console.log("base64.encodeToString: ", JSON.stringify(a));
var result = this.encodeToString(a, b);
console.log("base64.encodeToString result: ", result)
return result;}
没有hook到,估计是so base64后返回的,所以没有hook到.
先看下先前的堆栈
let i0 = Java.use("gf.i0");
i0["c"].implementation = function (map, j, str) {console.log(`i0.c is called: map=${map}, j=${j}, str=${str}`);let result = this["c"](map, j, str);console.log(`i0.c result=${result}`);return result;
};
hook一下这个c方法,入参的str是空值
中间添加了些东西,这里直接跟这个aesencrypt.encode方法,一路跟到
encodebyte方法,写这个方法的主动调用
function call2(){Java.perform(function (){let AESEncrypt = Java.use("com.duapp.aesjni.AESEncrypt");var str='010110100010001010010010000011000111001011101010101000101110111010011010101101101010001000101100010110100010001010011010110011001111001011100010101000100100110010110010100010101011110010111100'var bArr = [99,105,112,104,101,114,80,97,114,97,109,117,115,101,114,78,97,109,101,99,111,117,110,116,114,121,67,111,100,101,56,54,108,111,103,105,110,84,111,107,101,110,112,97,115,115,119,111,114,100,99,97,56,102,49,49,57,97,50,55,101,99,49,55,102,57,56,98,52,54,51,56,48,55,99,100,48,98,54,98,54,50,112,108,97,116,102,111,114,109,97,110,100,114,111,105,100,116,105,109,101,115,116,97,109,112,49,55,49,49,55,48,52,48,56,49,52,55,54,116,121,112,101,112,119,100,117,115,101,114,78,97,109,101,56,51,99,55,51,53,56,99,57,52,101,51,97,50,52,101,49,49,102,48,102,100,101,49,99,98,102,53,100,53,53,57,95,49,117,117,105,100,49,48,49,51,48,48,102,98,51,57,48,51,48,56,52,101,118,53,46,51,56,46,53]var res = AESEncrypt["encodeByte"](bArr, str)console.log(res)})
}
des 3des aes关系
第一个字符串转16进制后是24字节,写文章之前不记得前面有个aes了,以为这个是3des,3des秘钥就是24字节,3des由des进化过来的,因为des不安全,那个时候aes还没出来,aes中的a是advanced,高级的,由des进化而来,aes的原始名称是Rijndael算法,是由创造这个算法的两位作者名字组成的.因为是在des基础上改进的,所以大家都称它aes.
des秘钥8字节太短了,aes有16字节,24字节,32字节的,密钥的长度在一定程度上决定着算法的安全性,而DES密钥长度过短,也就导致了它的安全性较低;而AES则更加安全,这也是AES能够取代DES的重要原因之一.
在aes还没出之前为了解决des安全性的问题,有专家推出了3des,cyberchef中名称是TripleDES,Triple有3的意思,秘钥24字节,由3把秘钥组成,每8个字节算一把秘钥,加密流程如图所示,如果前两把秘钥相同,就相当于只有一次des加密,3des目前已被证明安全性不足,目前用的都是aes,如果没记错的话某书的x-s就是用的3des,好吧扯得有点远了,回到正文.
用3des加密一遍结果不对,只能看so了.
第一个数组转utf-8
import base64
byte_list = [99,105,112,104,101,114,80,97,114,97,109,117,115,101,114,78,97,109,101,99,111,117,110,116,114,121,67,111,100,101,56,54,108,111,103,105,110,84,111,107,101,110,112,97,115,115,119,111,114,100,99,97,56,102,49,49,57,97,50,55,101,99,49,55,102,57,56,98,52,54,51,56,48,55,99,100,48,98,54,98,54,50,112,108,97,116,102,111,114,109,97,110,100,114,111,105,100,116,105,109,101,115,116,97,109,112,49,55,49,49,55,48,52,48,56,49,52,55,54,116,121,112,101,112,119,100,117,115,101,114,78,97,109,101,56,51,99,55,51,53,56,99,57,52,101,51,97,50,52,101,49,49,102,48,102,100,101,49,99,98,102,53,100,53,53,57,95,49,117,117,105,100,49,48,49,51,48,48,102,98,51,57,48,51,48,56,52,101,118,53,46,51,56,46,53]
num_list = bytearray()
for item in byte_list:if item < 0:item = item + 256num_list.append(item)
utf8_string = num_list.decode()
print('utf8_string:',utf8_string)
结果就是表单里除了newsign的东西cipherParamuserNamecountryCode86loginTokenpasswordca8f119a27ec17f98b463807cd0b6b62platformandroidtimestamp1711704081476typepwduserName83c7358c94e3a24e11f0fde1cbf5d559_1uuid101300fb3903084ev5.38.5,再拼了一个uuid和版本号.
so加固
上图中so的名字是libJNIEncrypt.so,只有64位的,用ida64打开
提示文件结果被破坏,点yes
警告这个so有无意义或者无效的节.点ok.
刚进来两处爆红,上面一条都是灰色的,没有代码段,不用想,加固了.
so dump
dump内存中的so,yang神的脚本
function dump_so(so_name) {Java.perform(function () {var currentApplication = Java.use("android.app.ActivityThread").currentApplication();var dir = currentApplication.getApplicationContext().getFilesDir().getPath();var libso = Process.getModuleByName(so_name);console.log("[name]:", libso.name);console.log("[base]:", libso.base);console.log("[size]:", ptr(libso.size));console.log("[path]:", libso.path);var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";var file_handle = new File(file_path, "wb");if (file_handle && file_handle != null) {Memory.protect(ptr(libso.base), libso.size, 'rwx');var libso_buffer = ptr(libso.base).readByteArray(libso.size);file_handle.write(libso_buffer);file_handle.flush();file_handle.close();console.log("[dump]:", file_path);}});
}
dump_so("libJNIEncrypt.so")
dump下来拉到电脑上用sofixer修复一下.
dump下来的没有前面的警告了,上面的"进度条"也变成了蓝色,同时看到了Jnionload,动态注册,以及base64和aes128,难怪之前hook不到base64,原来是so base64的
接下来找动态注册函数.hook libart
var addrRegisterNatives = null;
var symbols = Module.enumerateSymbolsSync("libart.so");
for (var i = 0; i < symbols.length; i++) {var symbol = symbols[i];if (symbol.name.indexOf("art") >= 0 &&symbol.name.indexOf("JNI") >= 0 &&symbol.name.indexOf("RegisterNatives") >= 0 &&symbol.name.indexOf("CheckJNI") < 0) {addrRegisterNatives = symbol.address;console.log("RegisterNatives is at ", symbol.address, symbol.name);break}
}
if (addrRegisterNatives) {// RegisterNatives(env, 类型, Java和C的对应关系,个数)Interceptor.attach(addrRegisterNatives, {onEnter: function (args) {var env = args[0]; // jni对象var java_class = args[1]; // 类var class_name = Java.vm.tryGetEnv().getClassName(java_class);var taget_class = "com.duapp.aesjni.AESEncrypt"; //111 某个类中动态注册的soif (class_name === taget_class) {//只找我们自己想要类中的动态注册关系console.log("\n[RegisterNatives] method_count:", args[3]);var methods_ptr = ptr(args[2]);var method_count = parseInt(args[3]);for (var i = 0; i < method_count; i++) {// Java中函数名字的var name_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3));// 参数和返回值类型var sig_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize));// C中的函数内存地址var fnPtr_ptr = Memory.readPointer(methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2));var name = Memory.readCString(name_ptr);var sig = Memory.readCString(sig_ptr);var find_module = Process.findModuleByAddress(fnPtr_ptr);// 地址、偏移量、基地址var offset = ptr(fnPtr_ptr).sub(find_module.base);console.log("name:", name, "sig:", sig,'module_name:',find_module.name ,"offset:", offset);}}}});
}
以spawn启动
0x174c,ida中跳过去看看,然后转换一下jnienv对象
看返回值,来自v18,v18来自j_AES_128_ECB_PKCS5Padding_Encrypt,很明显了,ecb无iv 16字节秘钥,pkcs5填充,直接frida hook这个函数打印两个入参.
var soAddr = Module.findBaseAddress("libJNIEncrypt.so");
var funcAddr = soAddr.add(0x182C) //32位的话记得+1Interceptor.attach(funcAddr,{onEnter: function(args){console.log('onEnter arg[0]: ',hexdump(args[0]))console.log('onEnter arg[1]: ',hexdump(args[1]))this.arg0 = args[0]},onLeave: function(retval){// console.log('onLeave arg[]: ')console.log('onLeave result: ',retval)}});
入参1是java层传来的明文,参数2是key d245a0ba8d678a61,验证一下,和主动调用结果一致,最后md5就是newsign了,至此,newsign分析完毕,没什么难度的.
技术交流+lyaoyao__i(两个_),群聊7天有效
微信公众号
知识星球
相关文章:
某物登录表单加密
之前分析过某物h5的以及小程序的搜索接口,就是一个aes,秘钥不固定,表单里把秘钥以及密文一起发过去,服务器解密后再把数据加密返回,客户端解密展示到页面上. 这期是关于app的登录,密码登录 声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,…...
2024java面试题
题目:反转一个单链表描述:给定一个单链表的头节点,将其反转,返回反转后的链表的头节点。 题目:合并两个有序链表描述:给定两个有序链表的头节点,将它们合并成一个有序链表,返回合并…...
FebHost:什么是哈萨克斯坦.KZ域名?
哈萨克斯坦,作为中亚地区重要的一员,其国家域名”.kz”正成为这个独立国家在网络世界中的代表。作为一个经济快速发展的国家,哈萨克斯坦的互联网基础设施和网络应用也在蓬勃发展。而.kz域名正是哈萨克斯坦网络身份的重要体现。 作为注册和管理.kz域名的主要机构,哈…...
python(一)网络爬取
在爬取网页信息时,需要注意网页爬虫规范文件robots.txt eg:csdn的爬虫规范文件 csdn.net/robots.txt User-agent: 下面的Disallow规则适用于所有爬虫(即所有用户代理)。星号*是一个通配符,表示“所有”。 Disallow&…...
港大新工作 HiGPT:一个模型,任意关系类型 !
论文标题: HiGPT: Heterogeneous Graph Language Model 论文链接: https://arxiv.org/abs/2402.16024 代码链接: https://github.com/HKUDS/HiGPT 项目网站: https://higpt-hku.github.io/ 1. 导读 异质图在各种领域…...
Git版本管理使用手册 - 5 - Git的.ignore文件语法
Git的.ignore文件 1.使用 .ignore文件可以忽略指定文件的版本控制。 2.语法: (1)#开头表示注释 (2)!开头表示不忽略匹配文件 (3)* 表示除/外,任何字符串 (4)?表示除/外,任何一个字符 (5)/ 如果模式的结尾有分割符/&am…...
使用Spring Cloud Gateway构建API网关,实现路由、过滤、流量控制等功能。
使用Spring Cloud Gateway构建API网关,实现路由、过滤、流量控制等功能。 使用Spring Cloud Gateway可以轻松地构建API网关,实现路由、过滤、流量控制等功能。下面是一个简单的示例,演示如何在Spring Boot应用程序中集成Spring Cloud Gatewa…...
Matlab|电动汽车充放电V2G模型
目录 1 主要内容 1.1 模型背景 1.2 目标函数 1.3 约束条件 2 部分代码 3 效果图 4 下载链接 1 主要内容 本程序主要建立电动汽车充放电V2G模型,采用粒子群算法,在保证电动汽车用户出行需求的前提下,为了使工作区域电动汽车尽可能多的消…...
<QT基础(4)>QLabel使用笔记
Label 前面的文章里面把QLabel批量引入ScrollArea作为预览窗口,这篇把图像填充到QLable的PixelMap展示指定图像。 参数设置 设置QLabel的大小格式 QWidget* widget new QWidget; widget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); widget->…...
2016年认证杯SPSSPRO杯数学建模C题(第二阶段)如何有效的抑制校园霸凌事件的发生全过程文档及程序
2016年认证杯SPSSPRO杯数学建模 C题 如何有效的抑制校园霸凌事件的发生 原题再现: 近年来,我国发生的多起校园霸凌事件在媒体的报道下引发了许多国人的关注。霸凌事件对学生身体和精神上的影响是极为严重而长远的,因此对于这些情况我们应该…...
前端理论总结(css3)——css优化的方法
1:首推的是合并css文件,如果页面加载10个css文件,每个文件1k,那么也要比只加载一个100k的css文件慢 2:减少css嵌套,最好不要套三层以上 3:不要在ID选择器前面进行嵌套,ID本来就是唯一…...
项目立项管理
目录 1.概述 2.项目建议与立项申请 3.项目可行性研究 3.1.内容 3.2.初步可行性研究 3.3.详细可行性研究 4.项目评估与决策 5.总结 1.概述 本文的目录结构参考了《信息系统项目管理师教程(第四版)》。 项目立项管理是一项全面评估准备投资工程的多…...
QT的学习
代码练习 完成一个使用qss的登陆窗口界面。 使用手动连接,将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中,在槽函数中判断ui界面上输入的账号是否…...
redis在docker安装并启动流程
1、启动server docker run -d -p 6379:6379 --name redis01 redis:7.2.4以上命令,每次启动新的Redis容器,数据会丢失。 我们需要挂载数据文件,在宿主机上面,这样就可以持久化数据. 2、挂载数据文件(可根据需求选择…...
Spring高频面试题
(一些来源于GitCode AI) 什么是Spring框架? Spring是一个开源的Java平台,它简化了企业级应用的开发。它提供了IOC(Inversion of Control)/DI(Dependency Injection)容器,…...
Qt篇——Qt无法翻译tr()里面的字符串
最近遇到使用Qt语言家翻译功能时,ui界面中的中文都能够翻译成英文,但是tr("测试")这种动态设置给控件的中文,无法翻译(lang_English.ts文件中的翻译已经正确添加了tr()字符串的翻译)。 上网搜了很多资料&am…...
农村分散式生活污水分质处理及循环利用技术指南
标准已完成意见征集: 本文件给出了农村分散式生活污水分质处理及循环利用的总则、污水收集、污水分质处理、资源化利用、利用模式、运维管理等的指导。 本文件适用于农村分散式生活污水分质处理及循环利用的设施新建、扩建和改建工程的设计、施工与运维。 注:本文件…...
深圳区块链交易所app系统开发,撮合交易系统开发
随着区块链技术的迅速发展和数字资产市场的蓬勃发展,区块链交易所成为了数字资产交易的核心场所之一。在这个快速发展的领域中,区块链交易所App系统的开发和撮合交易系统的建设至关重要。本文将探讨区块链交易所App系统开发及撮合交易系统的重要性&#…...
使用Shell脚本进行MySql权限修改
背景:原先数据配置文件中有bind-address127.0.0.1,注释掉此配置后,原数据库中默认带%root的权限,现在需要通过脚本实现白名单列表中的ip添加权限允许访问数据库,白名单之外的ip没有权限访问数据库。 以下是过程中记录的…...
项目中线程池的应用
1、首先我们需要在配置类中将线程池作为单例bean配置 Configuration public class ThreadPoolExecutorConfig {BeanExecutorService executorService(){return new ThreadPoolExecutor(2,3,0,TimeUnit.MICROSECONDS,new ArrayBlockingQueue<>(3),(r)->new Thread(r,&…...
前端 JS 压缩图片的思路(附源码)
前言 相信大家都做过图片上传相关的功能,在图片上传的过程中,不知道大家有没有考虑过文件体积的问题,如果我们直接将原图片上传,可以图片体积比较大,一是上传速度较慢,二是前端进行渲染时速度也比较慢,比较影响客户的体验感。所以在不影响清晰度的情况下,前端可以在上…...
C语言结合体和枚举的魅力展现
前言 ✨✨欢迎👍👍点赞☕️☕️收藏✍✍评论 个人主页:秋邱’博客 所属栏目:人工智能 (感谢您的光临,您的光临蓬荜生辉) 引言: 前面我们已经讲了结构体的声明,自引用,内存…...
基于STC12C5A60S2系列1T 8051单片机通过单个按键单击次数实现开关机应用
基于STC12C5A60S2系列1T 8051单片机通过单个按键单击次数实现开关机应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍基于STC12C5A60S2系列1T 8051单片机通过单个按…...
静态住宅IP优缺点,究竟要怎么选?
在进行海外 IP 代理时,了解动态住宅 IP 和静态住宅 IP 的区别以及如何选择合适的类型非常重要。本文将介绍精态住宅 IP 特点和,并提供选择建议,帮助您根据需求做出明智的决策。 静态住宅 IP 的特点 静态住宅 IP 是指 IP 地址在一段时间内保…...
day07-缓存商品、购物车
1. 缓存菜品 1.1 问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大。 结果: 系统响应慢、用户体验差 1.2 实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓…...
平台介绍-搭建赛事运营平台(3)
上文介绍了品牌隔离的基本原理,就是通过不同的前端和微服务来实现。但是确实很多功能是类似的,所以从编程角度还是有些管理手段的。 前端部分:前端部分没有什么特别手段,就是两个独立的项目工程,分别维护。相同的部分复…...
数值分析复习:逼近理论的应用——最小二乘问题、解超定、欠定方程组
文章目录 逼近理论的应用——最小二乘问题、解超定、欠定方程组离散平方逼近最小二乘解 本篇文章适合个人复习翻阅,不建议新手入门使用 本专栏:数值分析复习 的前置知识主要有:数学分析、高等代数、泛函分析 逼近理论的应用——最小二乘问题、…...
设计模式-设配器模式
目录 🎊1.适配器模式介绍 🎃2.适配器类型 🎏3.接口适配器 🎐4.类的适配器 🎎5.优缺点 1.适配器模式介绍 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设…...
BEVFormer v2论文阅读
摘要 本文工作 提出了一种具有透视监督(perspective supervision)的新型鸟瞰(BEV)检测器,该检测器收敛速度更快,更适合现代图像骨干。现有的最先进的BEV检测器通常与VovNet等特定深度预训练的主干相连,阻碍了蓬勃发展…...
FFMPEG C++封装(二)
4 详细设计 这章是FFMPEG C封装库的详细设计。 4.1 Init 该模块初始化FFMPEG库。 4.1.1 Init定义 namespace media { namespace sdk { void MEDIASDK_EXPORT Init(); } }函数说明: Init 初始化FFMPEG库,该函数可调用多次。 4.1.2 Init实现 name…...
58同城网招聘找工作建筑工程/佛山seo外包平台
磁盘阵列df -h看文件系统使用率 : format看可用磁盘 : cfgadm -al看总线连接...
我要找人做网站的主页/手机百度网盘网页版登录入口
目录(TOC)是Word文档的重要组成部分。它提供了文档内容的概述,并允许您快速导航到所需的部分。您可能会遇到需要以编程方式从Word文档中添加,提取,更新或删除目录的情况。为此,本文将教您如何使用C 处理Wor…...
如何用wordpress站群/江苏seo技术教程
教材学习内容总结 信息的表示和处理 通过使用标准的字符码能够对文档中的字母和符号进行编码。 三种重要的数字表现形式: 1、 无符号数:编码基于传统的二进制表示法表示大于或等于零的数字。 2、 补码:编码是表示有符号整数的最常见方法&…...
网站代建设费用吗/日本域名注册
1 //遍历服务器指定文件夹下的所有文件2 string path "uploads/Image/";3 string serverPath Server.MapPath(path);4 5 //创建临时文件夹6 string tempName DateTime.Now.ToString("yyyyMMddH…...
产品外观工业设计公司/小吴seo博客
读书笔记--第5篇--《公司绝不会告诉你的50大秘密》 0.法律解救不了您。 1.聪明过头并非明智之举。 2.年龄和性别歧视是活生生的现实。 3.公司并非畅所欲言的好地方。 4.如果你与老板作对,必然会被逐出公司大门。 5.与人力部门的闲谈,会使你面临失业的危险…...
企业网站设计经典案例/在线代理浏览网址
题目 力扣 思路 暴力 根据题意,在sx和sy不大于tx和ty时,遍历所有情况,结果会超时。 代码 class Solution { public:bool reachingPoints(int sx, int sy, int tx, int ty) {return dfs(sx, sy, tx, ty);}bool dfs(int sx, int sy, int t…...