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

快手新版本sig3参数算法还原

Frida Native层主动调用

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

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

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

export function callDoCommandNative(){ // jni方法复习

    Java.perform(function() {

        var base_addr = Module.findBaseAddress("libkwsgmain.so") || ptr(0x0);

        var real_addr = base_addr.add(0x41680)

        var docommand = new NativeFunction(real_addr, "pointer", ["pointer""pointer""int""pointer"]);

        var JNIEnv = Java.vm.getEnv();

        var Intger = Java.use("java.lang.Integer");

        var jstring = Java.use("java.lang.String");

        var Boolean = Java.use("java.lang.Boolean");

        var cla = JNIEnv.findClass("java/lang/Object");

        // args1:,d7b7d042-d4f2-4012-be60-d97ff2429c17,,,com.yxcorp.gifshow.App@27101bb,,'} data: None

        var currentApplication = Java.use("android.app.ActivityThread").currentApplication();

        var context = currentApplication.getApplicationContext();

        log("context"+context)

        var input_1 = JNIEnv.newStringUtf('d7b7d042-d4f2-4012-be60-d97ff2429c17');

        var argList_0 = JNIEnv.newObjectArray(7, cla, ptr(0x0));

        JNIEnv.setObjectArrayElement(argList_0, 0, ptr(0x0));

        JNIEnv.setObjectArrayElement(argList_0, 1, input_1);

        JNIEnv.setObjectArrayElement(argList_0, 2, ptr(0x0));

        JNIEnv.setObjectArrayElement(argList_0, 3, ptr(0x0));

        JNIEnv.setObjectArrayElement(argList_0, 4, context.$h);

        JNIEnv.setObjectArrayElement(argList_0, 5, ptr(0x0));

        JNIEnv.setObjectArrayElement(argList_0, 6, ptr(0x0));

        var point_0 = docommand(JNIEnv, ptr(0x0), 10412, argList_0); // 返回的是指针,通过cast转成java对象,从而读出来

        console.log("point_0: " + point_0);

        var s_0 = Java.cast(point_0, Java.use("java.lang.Object"));

        console.log("result: " + s_0);

        var argList = JNIEnv.newObjectArray(8, cla, ptr(0x0));

        var argList_1 = JNIEnv.newObjectArray(1, cla, ptr(0x0));

        var input0 = JNIEnv.newStringUtf('/rest/n/feed/selectionbb9caf23ee1fda57a6c167198aba919f');

        var input1 = JNIEnv.newStringUtf('d7b7d042-d4f2-4012-be60-d97ff2429c17');

        var input2 = Boolean.$new(false);

        var input2_2 = Boolean.$new(false);

        var input3 = Intger.$new(-1);

        var input5 = JNIEnv.newStringUtf("010a11c6-f2cb-4016-887d-0d958aef1534");

         

        JNIEnv.setObjectArrayElement(argList_1, 0, input0);

        JNIEnv.setObjectArrayElement(argList, 0, argList_1);

        JNIEnv.setObjectArrayElement(argList, 1, input1);

        JNIEnv.setObjectArrayElement(argList, 2, input3.$h);

        JNIEnv.setObjectArrayElement(argList, 3, input2.$h);

        JNIEnv.setObjectArrayElement(argList, 4, ptr(0x0));

        JNIEnv.setObjectArrayElement(argList, 5, ptr(0x0));

        JNIEnv.setObjectArrayElement(argList, 6, input2_2.$h);

        JNIEnv.setObjectArrayElement(argList, 7, input5);

        var point = docommand(JNIEnv, ptr(0x0), 10418, argList); // 返回的是指针,通过cast转成java对象,从而读出来

        var s = Java.cast(point, Java.use("java.lang.Object")); // $className : java.lang.String

        console.log("result: " + s);

        console.log("result: " + Java.vm.tryGetEnv().getStringUtfChars(point).readCString());

    })

}

     

export function jniOnload(){

    var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");

    if (android_dlopen_ext != null) {

        Interceptor.attach(android_dlopen_ext, {

            onEnter: function (args) {

                this.hook = false;

                var soName = args[0].readCString() || '';

                if (soName.indexOf("libkwsgmain.so") !== -1) {

                    this.hook = true;

                }

            },

            onLeave: function (retval) {

                if (this.hook) {

                    var jniOnload = Module.findExportByName("libkwsgmain.so""JNI_OnLoad") || ptr(0x0);

                    Interceptor.attach(jniOnload, {

                        onEnter: function (args) {

                            console.log("Enter Mtguard JNI OnLoad");

                        },

                        onLeave: function (retval) {

                            console.log("After Mtguard JNI OnLoad");

                            callDoCommandNative();

                            // hook_ks();

                        }

                    });

                }

            }

        });

    }

}

这里踩了一个坑,传入的对象数组里有个元素是context类型,打印出来是com.yxcorp.gifshow.App@b97f2c,所以想当然的就按这个类直接new了个对象传了进去,很快出现了报错:

对应汇编

 此处判断X23的值是否为0,正常打印出来是/data/app/com.smile.gifmaker-q14Fo0PSb77vTIOM1-iEqQ==/base.apk,调用了getPackageCodePath 方法。

 

这是一个context方法,那必须传入有效的context:

1

2

var currentApplication = Java.use("android.app.ActivityThread").currentApplication();

var context = currentApplication.getApplicationContext();

解决方法就是如此简单,基础,却让我绕了一大圈弯路,不得不感叹,基础真重要啊!

IDA静态分析

  • 花指令
    • 这块看龙哥的分析就好了,非常清晰

Unidbg模拟执行

  • 搭架子 & 补环境

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

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

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

package com.smile.gifmaker3;

import com.github.unidbg.*;

import com.github.unidbg.Module;

import com.github.unidbg.arm.backend.Backend;

import com.github.unidbg.arm.backend.CodeHook;

import com.github.unidbg.arm.backend.UnHook;

import com.github.unidbg.arm.backend.UnicornBackend;

import com.github.unidbg.arm.context.Arm32RegisterContext;

import com.github.unidbg.arm.context.Arm64RegisterContext;

import com.github.unidbg.file.FileResult;

import com.github.unidbg.file.IOResolver;

import com.github.unidbg.file.linux.AndroidFileIO;

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.api.AssetManager;

import com.github.unidbg.linux.android.dvm.array.ArrayObject;

import com.github.unidbg.linux.android.dvm.wrapper.DvmBoolean;

import com.github.unidbg.linux.android.dvm.wrapper.DvmInteger;

import com.github.unidbg.memory.Memory;

import com.github.unidbg.pointer.UnidbgPointer;

import com.github.unidbg.spi.SyscallHandler;

import com.github.unidbg.utils.Inspector;

import com.github.unidbg.virtualmodule.android.AndroidModule;

import com.github.unidbg.virtualmodule.android.JniGraphics;

import com.sun.jna.Pointer;

import king.trace.GlobalData;

import king.trace.KingTrace;

import unicorn.Unicorn;

import unicorn.UnicornConst;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.PrintStream;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.util.ArrayList;

import java.util.List;

public class kswgmain11420 extends AbstractJni implements IOResolver {

    private final AndroidEmulator emulator;

    private final VM vm;

    private final Module module;

    kswgmain11420() throws FileNotFoundException {

        // 创建模拟器实例,要模拟32位或者64位,在这里区分

        EmulatorBuilder<AndroidEmulator> builder = AndroidEmulatorBuilder.for64Bit().setProcessName("com.smile.gifmaker");

        emulator = builder.build();

        emulator.getSyscallHandler().setEnableThreadDispatcher(true);

        // 模拟器的内存操作接口

        final Memory memory = emulator.getMemory();

        // 设置系统类库解析

        memory.setLibraryResolver(new AndroidResolver(23));

        // 创建Android虚拟机

        // vm = emulator.createDalvikVM();

        vm = emulator.createDalvikVM(new File("unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\1142064wei.apk"));

        // 设置是否打印Jni调用细节

        vm.setVerbose(true);

        new JniGraphics(emulator, vm).register(memory);

        new AndroidModule(emulator, vm).register(memory);

        vm.setJni(this);

        SyscallHandler<AndroidFileIO> handler = emulator.getSyscallHandler();

        handler.addIOResolver(this);

        // 加载libttEncrypt.so到unicorn虚拟内存,加载成功以后会默认调用init_array等函数

        DalvikModule dm = vm.loadLibrary(new File("unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\libkwsgmain.so"), true);

        // 加载好的libttEncrypt.so对应为一个模块

        module = dm.getModule();

        // trace code

//        String traceFile = "unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\sig3_jniOnload.trc";

//        GlobalData.ignoreModuleList.add("libc.so");

//        GlobalData.ignoreModuleList.add("libhookzz.so");

//        GlobalData.ignoreModuleList.add("libc++_shared.so");

//        emulator.traceCode(module.base, module.base+module.size).setRedirect(new PrintStream(new FileOutputStream(traceFile), true));

        dm.callJNI_OnLoad(emulator);

    }

    public static void main(String[] args) throws FileNotFoundException {

        kswgmain11420 kk = new kswgmain11420();

        kk.init_native();

        kk.get_NS_sig3();

    }

    public void init_native() throws FileNotFoundException {

        // trace code

//        String traceFile = "unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\sig3_init_native.trc";

//        GlobalData.ignoreModuleList.add("libc.so");

//        GlobalData.ignoreModuleList.add("libhookzz.so");

//        GlobalData.ignoreModuleList.add("libc++_shared.so");

//        emulator.traceCode(module.base, module.base+module.size).setRedirect(new PrintStream(new FileOutputStream(traceFile), true));

        List<Object> list = new ArrayList<>(10);

        list.add(vm.getJNIEnv()); // 第一个参数是env

        DvmObject<?> thiz = vm.resolveClass("com/kuaishou/android/security/internal/dispatch/JNICLibrary").newObject(null);

        list.add(vm.addLocalObject(thiz)); // 第二个参数,实例方法是jobject,静态方法是jclass,直接填0,一般用不到。

        DvmObject<?> context = vm.resolveClass("com/yxcorp/gifshow/App").newObject(null); // context

        vm.addLocalObject(context);

        list.add(10412); //参数1

        StringObject appkey = new StringObject(vm,"d7b7d042-d4f2-4012-be60-d97ff2429c17"); // SO文件有校验

        vm.addLocalObject(appkey);

        DvmInteger intergetobj = DvmInteger.valueOf(vm, 0);

        vm.addLocalObject(intergetobj);

        list.add(vm.addLocalObject(new ArrayObject(intergetobj,appkey,intergetobj,intergetobj,context,intergetobj,intergetobj)));

        // 直接通过地址调用

        Number numbers = module.callFunction(emulator, 0x41680, list.toArray());

        System.out.println("numbers:"+numbers);

        DvmObject<?> object = vm.getObject(numbers.intValue());

        String result = (String) object.getValue();

        System.out.println("result:"+result);

    }

    @Override

    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {

        switch (signature) {

            case "com/yxcorp/gifshow/App->getPackageCodePath()Ljava/lang/String;": {

                return new StringObject(vm, "/data/app/com.smile.gifmaker-q14Fo0PSb77vTIOM1-iEqQ==/base.apk");

            }

            case "com/yxcorp/gifshow/App->getAssets()Landroid/content/res/AssetManager;": {

//                return new Long(vm, "3817726272");

                return new AssetManager(vm, signature);

            }

            case "com/yxcorp/gifshow/App->getPackageName()Ljava/lang/String;": {

                return new StringObject(vm, "com.smile.gifmaker");

            }

            case "com/yxcorp/gifshow/App->getPackageManager()Landroid/content/pm/PackageManager;": {

                DvmClass clazz = vm.resolveClass("android/content/pm/PackageManager");

                return clazz.newObject(signature);

            }

        }

        return super.callObjectMethodV(vm, dvmObject, signature, vaList);

    }

    @Override

    public boolean callBooleanMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {

        switch (signature) {

            case "java/lang/Boolean->booleanValue()Z":

                DvmBoolean dvmBoolean = (DvmBoolean) dvmObject;

                return dvmBoolean.getValue();

        }

        return super.callBooleanMethodV(vm, dvmObject, signature, vaList);

    }

    public String get_NS_sig3() throws FileNotFoundException {

        // trace code

//        String traceFile = "unidbg-android\\src\\test\\java\\com\\smile\\gifmaker3\\sig3_new.trc";

//        GlobalData.ignoreModuleList.add("libc.so");

//        GlobalData.ignoreModuleList.add("libhookzz.so");

//        GlobalData.ignoreModuleList.add("libc++_shared.so");

//        emulator.traceCode(module.base, module.base+module.size).setRedirect(new PrintStream(new FileOutputStream(traceFile), true));

        System.out.println("_NS_sig3 start");

        List<Object> list = new ArrayList<>(10);

        list.add(vm.getJNIEnv()); // 第一个参数是env

        DvmObject<?> thiz = vm.resolveClass("com/kuaishou/android/security/internal/dispatch/JNICLibrary").newObject(null);

        list.add(vm.addLocalObject(thiz)); // 第二个参数,实例方法是jobject,静态方法是jclass,直接填0,一般用不到。

        DvmObject<?> context = vm.resolveClass("com/yxcorp/gifshow/App").newObject(null); // context

        vm.addLocalObject(context);

        list.add(10418); //参数1

        StringObject urlObj = new StringObject(vm, "/rest/app/eshop/ks/live/item/byGuest6bcab0543b7433b6d0771892528ef686");

        vm.addLocalObject(urlObj);

        ArrayObject arrayObject = new ArrayObject(urlObj);

        StringObject appkey = new StringObject(vm,"d7b7d042-d4f2-4012-be60-d97ff2429c17");

        vm.addLocalObject(appkey);

        DvmInteger intergetobj = DvmInteger.valueOf(vm, -1);

        vm.addLocalObject(intergetobj);

        DvmBoolean boolobj = DvmBoolean.valueOf(vm, false);

        vm.addLocalObject(boolobj);

        StringObject appkey2 = new StringObject(vm,"7e46b28a-8c93-4940-8238-4c60e64e3c81");

        vm.addLocalObject(appkey2);

        list.add(vm.addLocalObject(new ArrayObject(arrayObject,appkey,intergetobj,boolobj,context,null,boolobj,appkey2)));

        // 直接通过地址调用

        Number numbers = module.callFunction(emulator, 0x41680, list.toArray());

        System.out.println("numbers:"+numbers);

        DvmObject<?> object = vm.getObject(numbers.intValue());

        String result = (String) object.getValue();

        System.out.println("result:"+result);

        return result;

    }

    @Override

    public FileResult resolve(Emulator emulator, String pathname, int oflags) {

        System.out.println("fuck:"+pathname);

        return null;

    }

    public String readStdString(Pointer strptr){

        Boolean isTiny = (strptr.getByte(0) & 1) == 0;

        if(isTiny){

            return strptr.getString(1);

        }

        return strptr.getPointer(emulator.getPointerSize()* 2L).getString(0);

    }

    @Override

    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {

        switch (signature) {

            case "com/kuaishou/android/security/internal/common/ExceptionProxy->getProcessName(Landroid/content/Context;)Ljava/lang/String;":

                return new StringObject(vm, "com.smile.gifmaker");

            case "com/meituan/android/common/mtguard/NBridge->getSecName()Ljava/lang/String;":

                return new StringObject(vm, "ppd_com.sankuai.meituan.xbt");

            case "com/meituan/android/common/mtguard/NBridge->getAppContext()Landroid/content/Context;":

                return vm.resolveClass("android/content/Context").newObject(null);

            case "com/meituan/android/common/mtguard/NBridge->getMtgVN()Ljava/lang/String;":

                return new StringObject(vm, "4.4.7.3");

            case "com/meituan/android/common/mtguard/NBridge->getDfpId()Ljava/lang/String;":

                return new StringObject(vm, "");

        }

        return super.callStaticObjectMethodV(vm, dvmClass, signature,vaList);

    }

}

这里也有一个小tips,让unidbg加载指定SO文件,如果这个SO有依赖其他的SO库,那么很有可能会加载失败。这个情况不同于直接加载apk文件里的so文件,那种情况下unidbg会帮我们自动去寻找需要的SO文件。这个情况下,我们只要把需要的SO文件提取出来,放在同一目录下即可。

  • trace code

    用去花后的so文件,trace关键函数,很快的。

SHA256还原

结合ida伪代码和sha256的伪代码,还原

  • 思路:先看iv和table,然后看明文编排,最后比对具体运算 ———— 龙哥语录
  • sha256算法基础

确定调用栈

  • [确定调用堆栈]
  • 找到sig3最先出现的地方
  • sub_2636c 编排地址:
  • sub_2BD20
  • sub_25938
  • sub_120C4

至此,明文输入字符串的加密结果拿到

拼接过程

  • 固定字符串
  • 随机字符串
    • 猜测是随机,进一步验证,追踪,在JNI_OnLoad里:

 

相关文章:

快手新版本sig3参数算法还原

Frida Native层主动调用 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 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81…...

Linux 安全 - LSM机制

文章目录 前言一、LSM起源二、LSM简介2.1 MAC2.2 LSM特征 三、Major and Minor LSMs3.1 Major LSMs3.2 Minor LSMs3.3 BPF LSM 四、LSM 框架五、LSM Capabilities Module六、LSM hooks 说明参考资料 前言 在这两篇文章中介绍了 Linux 安全机制 Credentials &#xff1a; Linu…...

uni-app:实现简易自定义下拉列表

效果 代码 <template><view><view class"dropdown-trigger" tap"showDropdown">{{ selectedItem }}</view><view class"dropdown-list" v-if"showList"><view class"dropdown-item" v-f…...

排序算法——直接插入排序

一、介绍 插入排序就是将前两个元素排好&#xff0c;再将第三个元素通过与前边的元素比较后插入适当的位置&#xff0c;再将第四个元素插入&#xff0c;不断重复插入与前边元素比较的操作&#xff0c;直到将元素都排列好。 演示如下&#xff1a; 视频演示&#xff1a;…...

手动抄表和自动抄表优缺点对比

随着科技的发展&#xff0c;自动抄表技术已经越来越成熟&#xff0c;被广泛应用于各个领域。然而&#xff0c;手动抄表在一些特定场景下仍然具有一定的优势。本文将从手动抄表和自动抄表的优缺点入手&#xff0c;对比分析它们的应用场景和使用价值。 1.成本低&#xff1a;手动抄…...

HiSilicon352 android9.0 emmc添加新分区

添加新分区 从emmc中单独划分出一个分区&#xff0c;用来存储相关数据&#xff08;可用于存储照片&#xff0c;视频&#xff0c;音乐和文档等&#xff09;或者系统日志log&#xff0c;从而不影响到其他分区。 实现方法&#xff1a; device/hisilicon/Hi3751V350/etc/Hi3751V3…...

networkX-04-查找k短路

文章目录 1.构建图2.使用networkX查找最短路径3.自己构建方法 教程仓库地址&#xff1a;github networkx_tutorial import networkx as nx import matplotlib.pyplot as plt1.构建图 # 创建有向图 G nx.DiGraph()# 添加带权重的边 edges [(0, 1, 1), (0, 2, 2), (1, 2, 1), …...

Linux虚拟机搭建RabbitMQ集群

普通集群模式&#xff0c;意思就是在多台机器上启动多个 RabbitMQ 实例&#xff0c;每台机器启动一个。创建的 queue&#xff0c;只会放在一个 RabbitMQ 实例上&#xff0c;但是每个实例都同步 queue 的元数据&#xff08;元数据可以认为是 queue 的一些配置信息&#xff0c;通…...

C之fopen/fclose/fread/fwrite/flseek

一、C中文件操作简介 c中的文件操作大致和linux的文件操作类似&#xff0c;但是毕竟是不同的API&#xff0c;所以会有些差异。部分差异会在下面的案例中体验 二、fopen open的参数有两个一个是文件名&#xff0c;一个是模式选择&#xff0c;不同open函数&#xff0c;open中的模…...

3D机器视觉:解锁未来的立体视野

原创 | 文 BFT机器人 机器视觉领域一直在不断演进&#xff0c;从最初的二维图像处理&#xff0c;逐渐扩展到了更复杂的三维领域&#xff0c;形成了3D机器视觉。3D机器视觉技术的涌现为计算机系统带来了全新的感知和理解能力&#xff0c;这一领域的发展正日益受到广泛关注。本文…...

大端字节序存储 | 小端字节序存储介绍

为什么存储的顺序有些变动呢&#xff1f; 大小端的介绍 我们在创建变量时&#xff0c;操作系统就会给你分配空间&#xff0c;比如你创建了【short/int/double/float】的变量&#xff0c;这些变量的类型都是大于1个字节的&#xff0c;操作系统会根据你这个变量的类型&#xff…...

ASP.Core3.1 WebAPI 发布到IIS

本篇文章简述如何在IIS中发布你的.Core 程序 1.打包 首先你要打包好你程序&#xff0c;如果你是Visual Studio开发的程序&#xff0c;那你右击你的项目点击发布 如果你是Visual Code 开发的&#xff0c;那你在你的终端切换到你的目录然后执行命令 dotnet publish --config…...

MyBatisPlus属性自动填充和乐观锁插件+查询删除操作+整合SpringBoot出现问题解决

属性字段自动填充 一、实体类和数据表添加两个字段&#xff08;属性&#xff09; 表&#xff1a;create_tiem/update_time 实体类&#xff1a;createTime/updateTime 二、实体类中属性进行注解添加 TableField(fillFieldFill.INSERT) private Date createTime;TableField(f…...

软件测试/测试开发丨App自动化—CSS 定位与原生定位

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27684 一、原生定位 元素属性定位组合定位 # ID 定位 driver.find_element_by_android_uiautomator(\new UiSelector().resourceId("<element-I…...

c语言:通讯录管理系统(文件版本)

前言&#xff1a;在大多数高校内&#xff0c;都是通过设计一个通讯录管理系统来作为c语言课程设计&#xff0c;通过一个具体的系统设计将我们学习过的结构体和函数等知识糅合起来&#xff0c;可以很好的锻炼学生的编程思维&#xff0c;本文旨在为通讯录管理系统的设计提供思路和…...

Android Studio 配置Git SVN忽略文件

在使用Android Studio进行版本控制时&#xff0c;经常会遇到需要忽略某些文件的情况&#xff0c;比如临时文件、编译生成的文件等。这些文件虽然在项目中存在&#xff0c;但不希望被加入到版本控制中。 在Android Studio中设置忽略文件 在Android Studio中&#xff0c;我们可…...

独享IP地址的层级划分和管理:打造稳定高效的网络架构

在网络架构设计中&#xff0c;独享地址的层级划分和管理是一项关键任务。它不仅能提供更好的网络性能和安全性&#xff0c;还能帮助企业实现更高效的资源管理。本文将为您详细介绍独享地址的层级划分和管理的重要性&#xff0c;并提供具体的方案和实际操作建议。 第一部分&…...

js中async的作用

async是JavaScript中的关键字&#xff0c;用于表示函数是异步的。 当函数被标记为async时&#xff0c;该函数会自动返回一个Promise对象&#xff0c;这个Promise对象的状态可能为resolved或rejected&#xff0c;具体取决于函数内部的操作和返回值。 使用async关键字时&#x…...

什么是信创测试?信创测试工具有哪些?

信创全称是“信息技术应用创新”&#xff0c;旨在实现信息技术自主可控&#xff0c;规避外部技术制裁和风险&#xff0c;其涉及产业链包括硬件、基础软件、应用软件、云服务、数据安全等领域。 信创测试是指对信创工程项目中的产品、系统等进行测试和验证&#xff0c;以确保其…...

健康医疗类APP在高需求快速发展背景下,商业化如何快速破局增收?

随着人口老龄化、亚健康人群的增加&#xff0c;人们健康意识的觉醒&#xff0c;健康医疗成为的大众重点关注的领域。同时&#xff0c;伴随互联网技术的飞速发展&#xff0c;为医疗行业促生了大量创新产品和衍生品&#xff0c;在此背景下&#xff0c;我国移动医疗产品正从萌芽走…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...