当前位置: 首页 > news >正文

某尝准app请求体响应加密分析

包名: vz.com

版本:6.3.5 最新版

梆梆加固企业版

需求:我们只是分析针对 加密和解密返回的响应解密分析流程。更加具体的sign等需要亲自己去尝试,作者并未对此进行分

一.frida检测

首先我们Frida检测这一块 在 libDexHelper.so 里面 我们可以使用线程替换加hook libDexHelper.so 4B2E0函数更改返回值即可过掉!

为什么是 4B2E0这个函数呢 你用Stalker 去跑一下即可得出验证。

第二我们过掉检测 又没有完全过掉因为 还检测了 art::ArtMethod::PrettyMethod 所以我们自己可以加载一个 修改打包后的frida-java-bridge因为Java.use 等api都是他提供的。

代码就不提供了有需要加作者绿泡泡。

二.脱壳

这个博主直接用的现成的fart脱壳机。

通过网盘分享的文件:vz.com.zip 链接: 百度网盘 请输入提取码 提取码: a9nd --来自百度网盘超级会员v4的分享

三.抓包分析:

 

我们发现有所有的都有请求包加密和响应包加密了!

四.逆向分析:

hook bytes进行定位

密文 = 某算法(str -> bytes )

function hookbytes() {HideJava()Java.perform(function () {var base64 = Java.use("android.util.Base64");base64.encodeToString.overload('[B', 'int').implementation = function (a, b) {console.log("base64.encodeToString: ", JSON.stringify(a));var result = this.encodeToString(a, b);console.log("base64.encodeToString result: ", result)console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));return result;}var str = Java.use("java.lang.String");str.getBytes.overload().implementation = function () {console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));var result = this.getBytes();var newStr = str.$new(result);console.log("str.getBytes result: ", newStr);return result;}str.getBytes.overload('java.lang.String').implementation = function (a) {console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));var result = this.getBytes(a);var newStr = str.$new(result);console.log("str.getBytes result: ", newStr);return result;}})
}

定位到了 一些位置 我们上jadx去分析

at md.a.d(SecurityRequest.java:117) at kd.e.c(EncryptInterceptor.java:19) at kd.e.intercept(EncryptInterceptor.java:104) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:167)

md.a.d我们看到了一些 端倪

e()函数返回的 f()而f函数是:

 public static String f() {return "nyv2r7jbe048snnyr8t5h6m1re9hnu4k";}

e.b函数是base64:

所以我们重点去看VZCore.en函数:

我们从上一张图可以看到 我们 是从Natciml中进行加载so的,我们从apk中搞出来这个so看一眼 搜素JNI_OnUnload发现大概率是被加固了

what看不了!没关系~dump so fix一下:

SoFixer-Windows-64.exeGitHub - F8LEFT/SoFixer

fix:SoFixer-Windows-64.exe -s libNatciml.so_0x77e462e000_0x1b5000.so -o libNatcimlfix.so -m 0x0 -d

再次打开修复后的so ok ~~~看起来没啥问题

我们hook到的参数 如下:(我重新找的登录位置 不然太长了!!!)这里我们只是简单演示一下!当然你也可以hook到加密位置

加密解密通过hook bytes都可以得到堆栈的

这里是一个加密一个解密 我只是hook了解密

function hookdemo() {HideJava()Java.perform(function () {let a = Java.use("md.a");a["a"].implementation = function (str) {console.log(`a.a is called: str=${str}`);let result = this["a"](str);console.log(`a.a result=${result}`);return result;};let VZCore = Java.use("com.feeyo.vz.core.VZCore");
VZCore["de"].implementation = function (str, bArr) {console.log(`VZCore.de is called: str=${str}, bArr=${bArr}`);let result = this["de"](str, bArr);console.log(`VZCore.de result=${result}`);return result;
};})
}[Pixel 3::vz.com ]-> hookdemo()
[Pixel 3::vz.com ]-> a.a is called: str=utsRJDK2xZuf6n/NWfiIsxCD0+KBVaUGov6b2XtINB+cLqeqdiKGuolYAg+ueUBKB93gRPREhcA91GPh4ii3CzW+2dYLiRRVzz+UIGxyjewIgeKOAkskc8fHorduTPhNformvOkIzabceMKTzSU27iIBTjSpSctAwk7k9A+vJUzMZGfeznO9EalSNhZulAjDnVdVWkjiXtvjm+lOPasFEQ==
VZCore.de is called: str=nyv2r7jbe048snnyr8t5h6m1re9hnu4k, bArr=-70,-37,17,36,50,-74,-59,-101,-97,-22,127,-51,89,-8,-120,-77,16,-125,-45,-30,-127,85,-91,6,-94,-2,-101,-39,123,72,52,31,-100,46,-89,-86,118,34,-122,-70,-119,88,2,15,-82,121,64,74,7,-35,-32,68,-12,68,-123,-64,61,-44,99,-31,-30,40,-73,11,53,-66,-39,-42,11,-119,20,85,-49,63,-108,32,108,114,-115,-20,8,-127,-30,-114,2,75,36,115,-57,-57,-94,-73,110,76,-8,77,126,-118,-26,-68,-23,8,-51,-90,-36,120,-62,-109,-51,37,54,-18,34,1,78,52,-87,73,-53,64,-62,78,-28,-12,15,-81,37,76,-52,100,103,-34,-50,115,-67,17,-87,82,54,22,110,-108,8,-61,-99,87,85,90,72,-30,94,-37,-29,-101,-23,78,61,-85,5,17
VZCore.de result=123,34,99,111,100,101,34,58,51,44,34,109,115,103,34,58,34,-24,-76,-90,-27,-113,-73,-28,-72,-115,-27,-83,-104,-27,-100,-88,-17,-68,-116,-26,-126,-88,-24,-65,-10
4,-26,-100,-119,56,-26,-84,-95,-26,-100,-70,-28,-68,-102,-23,-121,-115,-24,-81,-107,34,44,34,100,97,116,97,34,58,123,34,116,105,112,115,34,58,34,-24,-76,-90,-27,-113,-73,-28,-7
2,-115,-27,-83,-104,-27,-100,-88,-17,-68,-116,-26,-126,-88,-24,-65,-104,-26,-100,-119,56,-26,-84,-95,-26,-100,-70,-28,-68,-102,-23,-121,-115,-24,-81,-107,34,44,34,115,116,97,116,117,115,34,58,48,44,34,105,115,89,122,109,34,58,48,44,34,121,122,109,85,114,108,34,58,34,34,125,125
a.a result={"code":3,"msg":"账号不存在,您还有8次机会重试","data":{"tips":"账号不存在,您还有8次机会重试","status":0,"isYzm":0,"yzmUrl":""}}

五.unidbg辅助分析

搭架子:

package com.fan;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;import java.io.File;
import java.util.Base64;public class FZC1 extends AbstractJni implements IOResolver {@Overridepublic FileResult resolve(Emulator emulator, String pathname, int oflags) {System.out.println("pathName:" + pathname);return null;}public static AndroidEmulator emulator;public static Memory memory;public static VM vm;public static DalvikModule dm;public static Module module;public FZC1() {// 1.创建设备(32位或64位模拟器), 具体看so文件在哪个目录。 在armeabi-v7a就选择32位emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("vz.com").build();// 2.获取内存对象(可以操作内存)memory = emulator.getMemory();// 3.设置安卓sdk版本(只支持19、23)memory.setLibraryResolver(new AndroidResolver(23));// 4.创建虚拟机(运行安卓代码需要虚拟机,就想运行py代码需要python解释器一样)vm = emulator.createDalvikVM(new File("apks/FCZ/fcz.apk"));vm.setJni(this); // 后续设置补环境vm.setVerbose(true); //是否展示调用过程的细节emulator.getSyscallHandler().addIOResolver(this);// 5.加载so文件DalvikModule dm = vm.loadLibrary("Natciml", true);dm.callJNI_OnLoad(emulator);// 6.dm代表so文件,dm.getModule()得到module对象,基于module对象可以访问so中的成员。module = dm.getModule();}public static String bytesTohexString(byte[] bytes) {StringBuffer sb = new StringBuffer();for (int i = 0; i < bytes.length; i++) {String hex = Integer.toHexString(bytes[i] & 0xFF);if (hex.length() < 2) {sb.append(0);}sb.append(hex);}return sb.toString();}public static void main(String[] args) {FZC1 result = new FZC1();;}
}//ce77c4ff557fb4b1
//77ecff4cf7551b4b

/proc/self/maps这里我们需要补一个maps

进入自己的手机运行:

ps -ef | grep vz.com 

cat /proc/21293/maps

然后把他保存下来:

unidbg补一下:

 @Overridepublic FileResult resolve(Emulator emulator, String pathname, int oflags) {System.out.println("pathName:" + pathname);if (pathname.equals("/proc/self/maps")) {return FileResult.success(new SimpleFileIO(oflags, new File("apks/FCZ/maps"), pathname));}return null;}

顺便补一点环境:

 @Overridepublic DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {switch (signature){case "com/feeyo/vz/application/VZApplication->mContext:Lcom/feeyo/vz/application/VZApplication;":{return vm.resolveClass("com/feeyo/vz/application/VZApplication").newObject(null);}}return super.getStaticObjectField(vm, dvmClass, signature);}@Overridepublic DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {switch (signature){case "com/feeyo/vz/application/VZApplication->getPackageName()Ljava/lang/String;":{return ProxyDvmObject.createObject(vm,"vz.com");}case "com/feeyo/vz/application/VZApplication->getPackageManager()Landroid/content/pm/PackageManager;":{return vm.resolveClass("android/content/pm/PackageManager").newObject(null);}}return super.callObjectMethodV(vm, dvmObject, signature, vaList);}

再去调用:str 是上面说的hook en这个函数得到的 读者可以自行去hook。

 public static void sign(){String str1 = "nyv2r7jbe048snnyr8t5h6m1re9hnu4k";DvmClass VZCore = vm.resolveClass("com.feeyo.vz.core.VZCore");String str = "{\"api\":\"\\/user\\/login\",\"params\":{\"signature\":\"41fa04f8a30da58c0026e298b6123782\",\"language\":\"zh\",\"pwdstr\":\"9dc030dff5a31ff83e2f296e41e458d4\",\"reqid\":\"1-1734185667-1734185728726\",\"uid\":\"\",\"userLevel\":\"0\",\"phoneCode\":\"86\",\"appKey\":\"OJsfFe79\",\"uniqueID\":\"823dfbd2-cf03-41a4-afef-127e40d1c575\",\"oaid\":\"\",\"memberLevel\":\"0\",\"appVer\":\"202411290944\",\"devicetimezone\":\"28800\",\"mobile\":\"18222065655\",\"matetype\":\"Pixel 3\",\"deviceVersion\":\"13\",\"version\":\"6.3.5\",\"deviceID\":\"057f79e269167815b8b3e31e453bb2c5\",\"isVip\":\"0\",\"isYn\":\"0\",\"token\":\"\",\"pwd\":\"123456\",\"device\":\"1\",\"androidID\":\"f2af7b841da58f3d\",\"channelID\":\"feeyo_026\"},\"timestamp\":1734185728,\"nonce\":\"fe50b64954720ccb97ff36a6bd105b6c6018565eea17352a8e1c095204ff4e62\"}";
//        String str = "miRen";ByteArray byteValues = VZCore.callStaticJniMethodObject(emulator,"en(Ljava/lang/String;[B)[B",str1,new ByteArray(vm, str.getBytes()));byte[] info = byteValues.getValue();System.out.println(bytesTohexString(info));String data = new String(Base64.getEncoder().encode(info));System.out.println(data);}

整体代码如下

package com.fan;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.jni.ProxyDvmObject;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;import java.io.File;
import java.util.Base64;public class FZC1 extends AbstractJni implements IOResolver {@Overridepublic FileResult resolve(Emulator emulator, String pathname, int oflags) {System.out.println("pathName:" + pathname);if (pathname.equals("/proc/self/maps")) {return FileResult.success(new SimpleFileIO(oflags, new File("apks/FCZ/maps"), pathname));}return null;}public static AndroidEmulator emulator;public static Memory memory;public static VM vm;public static DalvikModule dm;public static Module module;public FZC1() {// 1.创建设备(32位或64位模拟器), 具体看so文件在哪个目录。 在armeabi-v7a就选择32位emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("vz.com").build();// 2.获取内存对象(可以操作内存)memory = emulator.getMemory();// 3.设置安卓sdk版本(只支持19、23)memory.setLibraryResolver(new AndroidResolver(23));// 4.创建虚拟机(运行安卓代码需要虚拟机,就想运行py代码需要python解释器一样)vm = emulator.createDalvikVM(new File("apks/FCZ/fcz.apk"));vm.setJni(this); // 后续设置补环境vm.setVerbose(true); //是否展示调用过程的细节emulator.getSyscallHandler().addIOResolver(this);// 5.加载so文件DalvikModule dm = vm.loadLibrary("Natciml", true);dm.callJNI_OnLoad(emulator);// 6.dm代表so文件,dm.getModule()得到module对象,基于module对象可以访问so中的成员。module = dm.getModule();}public static String bytesTohexString(byte[] bytes) {StringBuffer sb = new StringBuffer();for (int i = 0; i < bytes.length; i++) {String hex = Integer.toHexString(bytes[i] & 0xFF);if (hex.length() < 2) {sb.append(0);}sb.append(hex);}return sb.toString();}public static void sign(){String str1 = "nyv2r7jbe048snnyr8t5h6m1re9hnu4k";DvmClass VZCore = vm.resolveClass("com.feeyo.vz.core.VZCore");String str = "{\"api\":\"\\/user\\/login\",\"params\":{\"signature\":\"41fa04f8a30da58c0026e298b6123782\",\"language\":\"zh\",\"pwdstr\":\"9dc030dff5a31ff83e2f296e41e458d4\",\"reqid\":\"1-1734185667-1734185728726\",\"uid\":\"\",\"userLevel\":\"0\",\"phoneCode\":\"86\",\"appKey\":\"OJsfFe79\",\"uniqueID\":\"823dfbd2-cf03-41a4-afef-127e40d1c575\",\"oaid\":\"\",\"memberLevel\":\"0\",\"appVer\":\"202411290944\",\"devicetimezone\":\"28800\",\"mobile\":\"18222065655\",\"matetype\":\"Pixel 3\",\"deviceVersion\":\"13\",\"version\":\"6.3.5\",\"deviceID\":\"057f79e269167815b8b3e31e453bb2c5\",\"isVip\":\"0\",\"isYn\":\"0\",\"token\":\"\",\"pwd\":\"123456\",\"device\":\"1\",\"androidID\":\"f2af7b841da58f3d\",\"channelID\":\"feeyo_026\"},\"timestamp\":1734185728,\"nonce\":\"fe50b64954720ccb97ff36a6bd105b6c6018565eea17352a8e1c095204ff4e62\"}";
//        String str = "miRen";ByteArray byteValues = VZCore.callStaticJniMethodObject(emulator,"en(Ljava/lang/String;[B)[B",str1,new ByteArray(vm, str.getBytes()));byte[] info = byteValues.getValue();System.out.println(bytesTohexString(info));String data = new String(Base64.getEncoder().encode(info));System.out.println(data);}public static void main(String[] args) {FZC1 result = new FZC1();result.sign();}@Overridepublic DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {switch (signature){case "com/feeyo/vz/application/VZApplication->mContext:Lcom/feeyo/vz/application/VZApplication;":{return vm.resolveClass("com/feeyo/vz/application/VZApplication").newObject(null);}}return super.getStaticObjectField(vm, dvmClass, signature);}@Overridepublic DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {switch (signature){case "com/feeyo/vz/application/VZApplication->getPackageName()Ljava/lang/String;":{return ProxyDvmObject.createObject(vm,"vz.com");}case "com/feeyo/vz/application/VZApplication->getPackageManager()Landroid/content/pm/PackageManager;":{return vm.resolveClass("android/content/pm/PackageManager").newObject(null);}}return super.callObjectMethodV(vm, dvmObject, signature, vaList);}
}

我们先修改入参:为什么要修改入参?因为有些加密是分组加密 所以我们缩短自己的入参 更容易分析,这里我们先剧透一下是aes 加密,所以修改了入参“miRen”。

多次运行得到的结果是 hex : b75c99641fa056d004498d0a5ccb7b66

    string:      t1yZZB+gVtAESY0KXMt7Zg==

ok!接下来去用一下 DalvikVM64 类设置日志级别为 DEBUG 直接来说就是显示更多信息:

        Logger.getLogger(DalvikVM64.class).setLevel(Level.DEBUG);

首先我们可以观察到的信息是函数注册在:Java_com_feeyo_vz_core_VZCore_en => RX@0x4007fa48[libNatciml.so]0x7fa48

去ida中看下这个位置0x7fa48

改下JNIEnv * a1

我们回到idea unidbg再次观察有没有有用的信息:

最后生成的位置在buf=RW@0x404e4000

我们去用 emulator.traceWrite(0x404e4000L,0x404e4000L+0x16);

好的 第一个字节生成写入的位置在0x97728 我们ida跳过去看一眼:

跳到这个函数我们发现竟然是AES_encrypt 这么好符号都没去 这就对应了我们上文为啥要修改入参长度了 ,就是因为他是aes 分组加密。

好接下来我们用Unidbg的断点调试一下:

// AESencrypt emulator.attach().addBreakPoint(module.base+0x97510);

blr 跳到函数结束 Add breakpoint: 0x40081c54 in libNatciml.so [0x81c54]

b75c99641fa056d004498d0a5ccb7b66 不就是我们最后的结果吗对吧

但是这里的 mx2中的 77ecff4cf7551b4b 我们去尝试并不是我们的key

aes加密条件

既然他是aes加密 那么我们就要清楚一些事情:

第一:加密模式(ecb还是cbc或者是别的) 我们常见的就是ecb和cbc所以遇到样本优先从这两个角度去考虑

第二:填充方式 从上面那幅图我们已经可以得出是 pkcs7填充了

第三:key 或者 iv 的值

补充:

PKCS7 填充

PKCS7 是一种常见的块加密填充方式,目的是确保数据长度是块大小的倍数。每个填充字节的值表示填充的字节数。例如:

  • 如果最后一个块的大小不足以填充整个块,它会用 0x01 来填充一个字节,0x02 来填充两个字节,依此类推,直到填充满整个块。

在 AES 中,块大小通常是 16 字节。如果明文长度不是 16 字节的倍数,PKCS7 填充会使用多个字节的值来填充,使得最终的数据长度是 16 字节的倍数。

好我们接下来接着去分析:回到主函数0x7fa48: 看下这些函数sub_xxx都是些什么 大概浏览一遍

这个0x7fbe8 是最后返回的位置

也就是

所以我们可以去看下sub_7A070()这个函数:

我们可以看到是个这个玩意,大概率是为了增加控制流混淆我们的,没关系unidbg我们可以处理。

我们切换到到汇编 然后去断到:emulator.attach().addBreakPoint(module.base+0x7070A);

我们看x17的值:0x83b60在这里

我们跳过去:0x83b60 发现是这个玩意 EVP_CIPHER_CTX_free

我们按照这个方案去改下名字 给函数 由于本人比较懒就改了一些:

我们可以尝试chat一下:

或者看下openssl github官网openssl/crypto/aes/aes_ecb.c at master · openssl/openssl

在 OpenSSL 中,使用 EVP 库来进行 AES 加密的具体流程包括以下几个步骤:初始化加密上下文、设置加密算法和密钥、加密数据并处理加密剩余部分,最后清理加密上下文。EVP 提供了一组高层次的接口,简化了加密操作。

OpenSSL EVP

下面是使用 OpenSSL EVP 库进行 AES 加密的详细步骤和函数说明。

  • EVP_CIPHER_CTX_new(): 创建一个新的加密上下文。

  • EVP_EncryptInit_ex(): 初始化加密上下文,设置加密算法、密钥和初始化向量(IV)。

  • EVP_EncryptUpdate(): 加密数据块,可以多次调用来加密多个数据块。

  • EVP_EncryptFinal_ex(): 完成加密过程,确保所有数据都被正确加密。

  • EVP_CIPHER_CTX_free(): 释放加密上下文,清理资源。

这里的key并不是我们设置的key 而是处理的key所以我们上面aes_encrypt那个位置得到的key并不能得到正确结果:

经过对比我们确定了 我们的加密方式是aes ecb模式 如果你还不能确定可以去 hook 一下 断到哪里就是那里结果肯定是aes_ecb模式

好 截止现在我们三个aes 条件已经得到了2个分别是 填充模式 pkcs7 和 ecb模式加密,我们去找最后一个key

我们要思考我们传参的时候为什么要传入一个str1

String str1 = "nyv2r7jbe048snnyr8t5h6m1re9hnu4k";

会不会是从这里处理得到key的呢 答案是肯定的。这里是我处理了字符名之后的ida代码:

hook一下

x0=0x401e2000 x1=0x20e2cbe0 x2=0x0 x3=0x68be2bc2 x4=0x3 x5=0x12 x6=0x13 x7=0x403a6000 x8=0xbffff698

确实入参是我们的st1: 

blr再次查看:0xbffff698 得到了 ce77c4ff557b4b

上CyberChef验证:确实是b75c99641fa056d004498d0a5ccb7b66

验证一下 解密

a.a is called: str=utsRJDK2xZuf6n/NWfiIsxCD0+KBVaUGov6b2XtINB+cLqeqdiKGuolYAg+ueUBKB93gRPREhcA91GPh4ii3CzW+2dYLiRRVzz+UIGxyjewIgeKOAkskc8fHorduTPhNformvOkIzabceMKTzSU27iIBTjSpSctAwk7k9A+vJUzMZGfeznO9EalSNhZulAjDnVdVWkjiXtvjm+lOPasFEQ==

三个aes加密条件齐全完结:

补充:

我们也可以直接去hook setkey

六.结尾

我们从零开始 完成了一个app的响应加密和解密 ,其中有借助chat和百度,相信大家看完次文章会有一些收货。有写的不足的地方希望大家斧正,蟹蟹大家。


http://www.mrgr.cn/news/81176.html

相关文章:

  • RK3588 Android平台部署DeepSeek-R1教程
  • 两种交换排序算法--冒泡,快速
  • 探索从传统检索增强生成(RAG)到缓存增强生成(CAG)的转变
  • WebAssembly:前后端开发的未来利器
  • 【C语言系列】深入理解指针(5)
  • ubuntu 下使用deepseek
  • 多行为级联24|多行为推荐的超图增强级联图卷积网络
  • HashMap源码深度解析
  • CentOS HTTPS自签证书访问失败问题的排查与解决全流程
  • SpringCloud 运用(2)—— 跨服务调度
  • 访谈积鼎科技总经理:国产CFD软件发展与未来趋势展望
  • GitCode 光引计划投稿|JavaVision:引领全能视觉智能识别新纪元
  • Centos7安装k8s集群
  • node.js的异步工作之---回调函数与回调地狱
  • Pandas系列|第三期:Pandas中访问数据
  • 2024年A股最新退市规则
  • linux上抓包RoCEv2
  • Day1 苍穹外卖前端 Vue基础、Vue基本使用方式、Vue-router、Vuex、TypeScript
  • 【学术小白的学习之路】基于情感词典的中文句子情感分析(代码词典获取在结尾)
  • FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
  • YoloV8改进策略:Head改进|DynamicHead,利用注意力机制统一目标检测头部|即插即用
  • sqlite 自定以脚本解释器
  • 时空信息平台架构搭建:基于netty封装TCP通讯模块(IdleStateHandler网络连接监测,处理假死)
  • UE5 渲染管线 学习笔记
  • 【Rust自学】6.1. 定义枚举
  • 设计模式七大原则