flutter 加密安全

前言:数据安全

        数据的加密解密操作在 日常网络交互中经常会用到,现在密码的安全主要在于 秘钥的安全,如论 DES 3DES  AES 还是 RSA, 秘钥的算法(计算秘钥不固定) 和 保存,都决定了你的数据安全;但是常见的逆向操作 比如 hook 加密算法 都很容易拿到 秘钥; 这个时候我们可以 回溯到 之前的 古典密码学依赖算法本身),基本思路  置换 移位 编码 等等手段 来配合 加密算法一起使用,提高我们应用的安全

密码学概论_在传统的密码学中,加解密基础操作包括移位置换替换编码-CSDN博客文章浏览阅读201次。密码学基础_在传统的密码学中,加解密基础操作包括移位置换替换编码https://blog.csdn.net/nicepainkiller/article/details/132978492?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170902384916777224453245%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170902384916777224453245&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-132978492-null-null.nonecase&utm_term=%E5%AF%86%E7%A0%81&spm=1018.2226.3001.4450

android frida 逆向 自吐加密算法_frida 自吐算法 教程-CSDN博客文章浏览阅读1.8k次。frida hook android Android 逆向神器_frida 自吐算法 教程https://blog.csdn.net/nicepainkiller/article/details/132554698?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170902437216800182198144%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170902437216800182198144&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-132554698-null-null.nonecase&utm_term=hook&spm=1018.2226.3001.4450

 flutter 数据加密

如果是 android 原生应用,可以 hook 系统加密代码,来获取加密秘钥,而在 flutter 中,dart 是被直接编译为 .so 文件,也就是汇编,对于低级汇编语言 可读性大大降低。当然也是可以下断点动态调试的;但是相对于难度 大大增加,我们可以结合 古典密码学 主要特点 数据安全基于算法的保密,算法不公开, 设计繁琐的算法过程,增加汇编可读性难度;

有个业务需求是相当于是一个签到功能,每天可以领取,这里防止脚本调用使用两层 基于算法加密 和 AES加密 相结合的方式;(当然还用到了第三方的的安全软件

以下是我上个版本加密算法设计:整体思路就是:

  • 按照规则 移动字符 替换字符
  • 阶段一主要是 按规则打乱字符(密文)位置
  • 阶段二主要是 指定位置(密文)插入无关字符

match_request_data.dart

import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart' as encrypt;

///匹配接口加密工具类
///整体加密思路:按照规则 移动字符 替换字符
///阶段一主要是 按规则打乱字符位置
///阶段二主要是 指定位置插入无关字符
class MatchRequestData {
  final String gameId;
  final String chatSign;
  final String nickName;
  late List<int> _gameIdSort;
  MatchRequestData(
      {required this.gameId, required this.chatSign, required this.nickName});

  String generateCode(String datum) {
    String idStr = '${int.parse(gameId)}';
    List<int> searchKeywords =
        List<int>.generate(idStr.length, (index) => int.parse(idStr[index]));
    searchKeywords.sort();
    _gameIdSort = searchKeywords;

    String base64 = _encodeBase64(datum);
    String base64Step0 =
        base64.substring(0, _gameIdSort[_gameIdSort.length - 1]) +
            _stepOne((base64.substring(
                _gameIdSort[_gameIdSort.length - 1],
                base64.length -
                    _gameIdSort[3] -
                    _gameIdSort[_gameIdSort.length - 1]))) +
            base64.substring(base64.length -
                _gameIdSort[3] -
                _gameIdSort[_gameIdSort.length - 1]);

    String _strHex = _strToHex(base64Step0);
    final key = encrypt.Key.fromUtf8(_generateMd5(chatSign + nickName));
    final iv = encrypt.IV.fromUtf8(_ivStepOne().substring(4, 20).toUpperCase());
    final encrypter =
        encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
    final encrypted = encrypter.encrypt(_strHex, iv: iv);
    final encryptCode = _stepTwo(encrypted.base64);
    idStr = _strHex = base64 = base64Step0 = '';
    return encryptCode;
  }

  String _stepOne(String str) {
    String res = '';
    str = _inverse(str);
    int lastPosition = _gameIdSort[_gameIdSort.length - 1];
    int count = 0;
    if (lastPosition % 2 == 0) {
      count = _gameIdSort[_gameIdSort.length - 2];
    } else {
      count = _gameIdSort[_gameIdSort.length - 3];
    }
    if (count == 0) {
      count = lastPosition;
    }
    int step = str.length ~/ count;
    List<String> base64Parts = [];
    for (int i = 0; i < count; i++) {
      if (i % 2 == 1) {
        base64Parts.add(_inverse(str.substring(step * i, step * (i + 1))));
      } else {
        base64Parts.add(str.substring(step * i, step * (i + 1)));
      }
    }
    if (step * count < str.length) {
      if (lastPosition % 2 == 0) {
        base64Parts.insert(0, str.substring(step * count));
      } else {
        base64Parts.insert(base64Parts.length, str.substring(step * count));
      }
    }
    if (lastPosition % 2 == 1) {
      for (int i = 0; i < base64Parts.length; i++) {
        res = res + base64Parts[i];
      }
    } else {
      for (int i = base64Parts.length - 1; i >= 0; i--) {
        res = res + base64Parts[i];
      }
    }
    str = '';
    lastPosition = count = step = -1;
    return res;
  }

  String _stepTwo(String data) {
    String res = "";
    String _strHex = _strToHex(data);
    String _code = _inverse(_strHex);
    final key = encrypt.Key.fromUtf8(_generateMd5(gameId + chatSign));
    final iv = encrypt.IV.fromUtf8(_ivStepTwo().substring(14, 30));
    final encrypter =
        encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
    final encrypted = encrypter.encrypt(_code, iv: iv);
    res = encrypted.base64;
    int maxLength = res.length;

    int indexSub = 0;
    int insertPos = 0;
    String insertStr = '';
    for (int i = 1; i < _gameIdSort.length; i++) {
      indexSub = _gameIdSort[i] + 1;
      insertPos = _magic(indexSub + i) + i * 11 + i - 1;
      // insertStr = chatSign.substring(1,indexSub);
      insertStr = chatSign[indexSub];
      //前面插入
      if (insertPos > res.length) {
        insertPos = maxLength;
      }
      res =
          '${res.substring(0, insertPos)}$insertStr${res.substring(insertPos)}';
    }
    _strHex = _code = '';
    return res;
  }

  String _stepThree(String str) {
    return str;
  }

  String _inverse(String tag) {
    String res = '';
    List<String> searchKeywords =
        List<String>.generate(tag.length, (index) => tag[index]);
    Iterable<String> array = searchKeywords.reversed;
    for (var e in array) {
      res = '$res$e';
    }
    return res;
  }

  String _ivStepOne() {
    String res = '';
    String map = _generateMd5(chatSign) + _generateMd5(nickName);
    int index = _gameIdSort[_gameIdSort.length - 2];
    while (res.length < 50) {
      res += map[index];
      index++;
    }
    index = 0;
    return res;
  }

  String _ivStepTwo() {
    String res = '';
    String map = _generateMd5(_inverse(chatSign)) + _generateMd5(chatSign);
    int index = _gameIdSort[_gameIdSort.length - 1];
    while (res.length < 50) {
      res += map[index];
      index++;
    }
    index = 0;
    return _inverse(res);
  }

  /// 字符串转 十六进制
  String _strToHex(String str) {
    List<int> charCodes = str.runes.toList();
    return charCodes.map((code) => code.toRadixString(16)).join('');
  }

  /// 字符串转 base64
  String _encodeBase64(String data) {
    return base64Encode(utf8.encode(data));
  }

  /// base64转 普通字符
  String _decodeBase64(String data) {
    return String.fromCharCodes(base64Decode(data));
  }

  String _generateMd5(String str) {
    return md5.convert(utf8.encode(str)).toString();
  }

  int _magic(int num) {
    if (num < 3) {
      return 1;
    } else {
      return _magic(num - 1) + _magic(num - 2);
    }
  }
}

调用的地方:

MatchRequestData data = MatchRequestData (
  gameId: userArray[i]['gameID'],
  chatSign: userArray[i]['chatSign'],
  nickName: userArray[i]['nickName'],
);

//需要传递给后台的 内容
Map datum = {
  'inTrust': 'TRUE',
  'time': DateTime.now().millisecondsSinceEpoch,
  'GameID': userArray[i]['gameID'],
  'nickName': userArray[i]['nickName'],
  'MachineCode':
      md5.convert(utf8.encode(userArray[i]['gameID'])).toString(),
  'sign': md5
      .convert(
          utf8.encode(userArray[i]['gameID'] + userArray[i]['chatSign']))
      .toString(),
};


String res = data.generateCode(jsonEncode(datum));
 

服务端的数据解密:

服务端为 .net 框架:

对应于加密算法写的解密算法:

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace ToMatch
{
    public class MatchEncrypt
    {
        private string gameID;
        private string chatSign;
        private string nickName;
        private List<int> idSort;

        /// <summary>
        /// 匹配构造函数
        /// </summary>
        /// <param name="gameId">GameID</param>
        /// <param name="chatSign">签名</param>
        /// <param name="nickName">昵称</param>
        public MatchEncrypt(string gameId, string chatSign, string nickName) {
            this.gameID = gameId;
            this.chatSign = chatSign;
            this.nickName = nickName;

            this.idSort = new List<int>();
            string idStr = int.Parse(this.gameID).ToString();
            for (int i = 0; i < idStr.Length; i++)
            {
                this.idSort.Add((int)Char.GetNumericValue(idStr[i]));
            }
            this.idSort.Sort();
        }




        private String IvStepOne {
            get {
                String res = "";
                String map = Md5Hash(chatSign) + Md5Hash(nickName);
                int index = idSort[idSort.Count - 2];
                while (res.Length < 50)
                {
                    res += map[index];
                    index++;
                }
                return res;
            }
        }

        private String IvStepTwo
        {
            get
            {
                String res = "";
                String map = Md5Hash(AESHelper.Inverse(chatSign),false) + Md5Hash(chatSign,false);
                int index = idSort[idSort.Count - 1];
                while (res.Length < 50)
                {
                    res += map[index];
                    index++;
                }
                return AESHelper.Inverse(res);
            }
        }


        /// <summary>
        /// 解密客户端内容
        /// </summary>
        /// <param name="code">密文</param>
        /// <returns></returns>
        public string Resolver(string code) {

            //第一阶段解密内容
            string resStepOne = StepOne(code);

            if (resStepOne.Length > 0)
            {
                //Console.WriteLine("第一解密 result:" + resStepOne);
                //第二阶段解密
                string resSteptwo = Steptwo(resStepOne);
                //Console.WriteLine("第二解密 result:" + resSteptwo);
                //Console.WriteLine(AESHelper.FromBase64(resSteptwo));
                if (resSteptwo.Length > 0)
                {
                    return AESHelper.FromBase64(resSteptwo);
                }
                else 
                {
                    return "解密失败——请记录日志.Step-2";
                }       
            }
            else {
                return "解密失败——请记录日志.Step-1";
            }            
        }


        private string StepOne(string code)
        {
            // 1.先移除插入的字符
            // 2.再进行解密操作
            int maxlength = code.Length - idSort.Count - 1;
            int indexSub = 0;
            int insertPos = 0;
   
            for (int i = 1; i < idSort.Count; i++)
            {
                indexSub = idSort[i] + 1;
                insertPos = magic(indexSub + i) + i * 11;    
                //前面插入
                //Console.WriteLine("前面    索引:" + i);
                //Console.WriteLine("前面插入位置:" + insertPos);
                //Console.WriteLine("前面插入字符:" + insertStr + "");
  
                if (insertPos > code.Length) {
                    //Console.WriteLine("修正Length:" + code.Length);
                    Console.WriteLine("修正insertPos:" + insertPos);
                    //Console.WriteLine("----code.Length:" + (code.Length -maxlength  ));
                    //Console.WriteLine("----code.Length:" + (  idSort.Count-1 - i));
                    //Console.WriteLine("----code.Length:" + ((code.Length - maxlength - (idSort.Count - 1 - i))+1));
                    insertPos = maxlength -4;
                    insertPos = maxlength - ((code.Length - maxlength - (idSort.Count - 1 - i)) + 1);
                    //Console.WriteLine("*******code.Length:" + ((code.Length - maxlength - (idSort.Count - 1 - i)) + 1));
                    //Console.WriteLine("*******code.Length:" + (code.Length - maxlength - (idSort.Count - i)) );
                    //Console.WriteLine("*******code.Length:" + (code.Length - maxlength - idSort.Count  - i ));
                    //Console.WriteLine("code.Length:" + code.Length);
                    //Console.WriteLine("maxlength:" + maxlength);
                    //Console.WriteLine("idSort.Count:" + idSort.Count);
                    //Console.WriteLine("idSort.Count - i:" + (idSort.Count - i));
                    //Console.WriteLine("修正插入位置i:" + i);
                    //Console.WriteLine("修正插入位置:" + insertPos);
                    insertPos = maxlength - ((code.Length - maxlength - (idSort.Count - 1 - i)) + 1);
                }              
                code = code.Substring(0, insertPos) + code.Substring(insertPos + 1);
            }

            //Console.WriteLine("整理后的:" + code);
            //Console.WriteLine("整理后的Length:" + code.Length);
            string key = Md5Hash(this.gameID + this.chatSign, false);
            string iv = IvStepTwo.Substring(14, 16);

            //第一次解密是 16进制字符串
            string result = AESHelper.Decrypt(code, key, iv);
            return AESHelper.HexStringToString(AESHelper.Inverse(result), Encoding.UTF8);
        }

        private string Steptwo(string code) {

            string key = Md5Hash(this.chatSign + this.nickName, false);
            string iv = IvStepOne.Substring(4, 16);

      

            string base64 = AESHelper.HexStringToString(AESHelper.Decrypt(code, key, iv), Encoding.UTF8);
            
            string source = base64.Substring(0, idSort[idSort.Count - 1]) + generateMid(base64.Substring(idSort[idSort.Count - 1], base64.Length - idSort[3] - idSort[idSort.Count - 1] * 2)) + base64.Substring(base64.Length - idSort[3] - idSort[idSort.Count - 1]);

            //第二次解密是 base64
            return source ;
        }


        private string generateMid(string str) {

            string res = "";
            List<String> base64Parts = new List<string>();
            int lastPosition = this.idSort[this.idSort.Count - 1];
            string subBefore = "";
            int count = 0;
            if (lastPosition % 2 == 0)
            {
                count = idSort[idSort.Count - 2];
            }
            else
            {
                count = idSort[idSort.Count - 3];
            }
            if (count == 0) {
                count = lastPosition;
            }
            int step = str.Length / count;

            int subLength = str.Length - step * count;

            if (lastPosition % 2 == 0)
            {
                for (int i = 0; i < count; i++)
                {
                    if (i % 2 == 1)
                    {
                        base64Parts.Add(AESHelper.Inverse(str.Substring(step * (count - i - 1), step)));
                    }
                    else
                    {
                        //base64Parts.Add(v.Substring(step * i, step));
                        //Console.WriteLine(i + "不需要翻转原始:" + str.Substring(step * (count - i - 1), step));
                        base64Parts.Add(str.Substring(step * (count - i - 1), step));
                    }
                }

                for (int i = 0; i < base64Parts.Count; i++)
                {
                    //Console.WriteLine("偶数项目:" + i + " " + base64Parts[i]);
                    res = res + base64Parts[i];
                }
                subBefore = str.Substring(step * count);
                res += subBefore;
            }
            else
            {
                for (int i = 0; i < count; i++)
                {
                    if (i % 2 == 1)
                    {
                        base64Parts.Add(AESHelper.Inverse(str.Substring(step * i, step)));          
                    }
                    else
                    {
                        base64Parts.Add(str.Substring(step * i, step));
                    }
                }
                subBefore = str.Substring(step * count);
                base64Parts.Add(subBefore);
                for (int i = 0; i < base64Parts.Count; i++)
                {
                    //Console.WriteLine("奇数项目:" + i + " " + base64Parts[i]);
                    res = res + base64Parts[i];
                }
            }

            return AESHelper.Inverse(res);
        }

        private static int magic(int num)
        {

            if (num < 3)
            {
                return 1;
            }
            else
            {
                return magic(num - 1) + magic(num - 2);
            }
        }

        private string Md5Hash(string sourceText, bool toUpper = true)
        {
            StringBuilder result = new StringBuilder();
            using (MD5 md5 = new MD5CryptoServiceProvider())
            {
                byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(sourceText));
                if (toUpper)
                    for (int i = 0; i < data.Length; i++)
                        result.Append(data[i].ToString("X2"));
                else
                    for (int i = 0; i < data.Length; i++)
                        result.Append(data[i].ToString("x2"));
            }
            return result.ToString();
        }
    }
}

 总结

数据安全不是绝对的,只能说我们多设置些障碍,对于逆向的难度对增大,你挖的坑多远。逆向时候就越困难,当然也可以借助一些第三方安全软件来增加我们数据的安全性。在数据安全的道路上 始终是此消彼长的状态

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mycat核心教程--Mycat 监控工具【四】

Mycat核心教程--Mycat 监控工具 九、Mycat 监控工具9.1.Mycat-web 简介9.2.Mycat-web 配置使用9.2.1.ZooKeeper 安装【上面有】9.2.2.Mycat-web 安装9.2.2.1.下载安装包9.2.2.2.安装包拷贝到Linux系统/opt目录下&#xff0c;并解压9.2.2.3.拷贝mycat-web文件夹到/usr/local目录…

如何使用程序通过OCR识别解析PDF中的表格

https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7/ppstructure/table/README_ch.md#41-%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B Paddle-structure是目前我们能找到的可以做中英文版面分析较好的一个基础模型&#xff0c;其开源版可以识别十类页面元素。这篇文章介绍…

负载均衡.

简介: 将请求/数据【均匀】分摊到多个操作单元上执行&#xff0c;负载均衡的关键在于【均匀】。 负载均衡的分类: 网络通信分类 四层负载均衡:基于 IP 地址和端口进行请求的转发。七层负载均衡:根据访问用户的 HTTP 请求头、URL 信息将请求转发到特定的主机。 载体维度分类 硬…

SD-WAN技术:优化国内外服务器访问的关键

在全球化的商业环境中&#xff0c;企业经常需要在国内访问国外的服务器。然而&#xff0c;由于地理位置和网络架构的限制&#xff0c;这种跨国访问往往会遇到速度慢、延迟高等问题。SD-WAN&#xff08;软件定义广域网&#xff09;技术的兴起&#xff0c;为企业提供了一种新的解…

人像背景分割SDK,智能图像处理

美摄科技人像背景分割SDK解决方案&#xff1a;引领企业步入智能图像处理新时代 随着科技的不断进步&#xff0c;图像处理技术已成为许多行业不可或缺的一部分。为了满足企业对于高质量、高效率人像背景分割的需求&#xff0c;美摄科技推出了一款领先的人像背景分割SDK&#xf…

fastjson序列化MessageExt对象问题(1.2.78之前版本)

前言 无论是kafka&#xff0c;还是RocketMq&#xff0c;消费者方法参数中的MessageExt对象不能被 fastjson默认的方式序列化。 一、查看代码 Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,ConsumeConcurrentlyContext context) {t…

阿里云定价_ECS产品价格_云服务器收费标准 - 阿里云官方活动

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

jvm常用参数配置

一、 常用参数 -Xms JVM启动时申请的初始Heap值&#xff0c;默认为操作系统物理内存的1/64但小于1G。默认当空余堆内存大于70%时&#xff0c;JVM会减小heap的大小到-Xms指定的大小&#xff0c;可通过-XX:MaxHeapFreeRation来指定这个比列。Server端JVM最好将-Xms和-Xmx设为相同…

docker 容器修改端口

一般在运行容器时&#xff0c;我们都会通过参数 -p&#xff08;使用大写的-P参数则会随机选择宿主机的一个端口进行映射&#xff09;来指定宿主机和容器端口的映射&#xff0c;例如 docker run -it -d --name [container-name] -p 8088:80 [image-name]这里是将容器内的80端口…

wu-framework-parent 项目明细

wu-framework-parent 介绍 springboot 版本3.2.1 wu-framework-parent 是一款由Java语言开发的框架&#xff0c;目标不写代码但是却能完成功能。 框架涵盖无赖ORM( wu-database-lazy-starter)、仿生组件 、easy框架系列【Easy-Excel、easy-listener、easy-upsert】 授权框架(…

WPF 【十月的寒流】学习笔记(3):DataGrid分页

文章目录 前言相关链接代码仓库项目配置&#xff08;省略&#xff09;项目初始配置xamlviewModel Filter过滤详细代码展示结果问题 Linq过滤CollectionDataxamlviewModel sql&#xff0c;这里用到数据库&#xff0c;就不展开了 总结 前言 我们这次详细了解一下列表通知的底层是…

ubuntu20下使用 torchviz可视化计算图

安装 torchviz&#xff1a; pip install torchviz示例代码&#xff1a;下面是一个简单的示例代码&#xff0c;展示如何使用 torchviz 可视化计算图&#xff1a; python import torch from torchviz import make_dot# 创建一个简单的模型 model torch.nn.Sequential(torch.nn…

k8s部署java微服务程序时,关于配置conusl acl token的方法总结

一、背景 java微服务程序使用consul作为服务注册中心&#xff0c;而consul集群本身的访问是需要acl token的&#xff0c;以增强服务调用的安全性。 本文试着总结下&#xff0c;有哪些方法可以配置consul acl token&#xff0c;便于你根据具体的情况选择。 个人认为&#xff…

蓝桥杯题练习:平地起高楼

题目要求 function convertToTree(regions, rootId "0") {// TODO: 在这里写入具体的实现逻辑// 将平铺的结构转化为树状结构&#xff0c;并将 rootId 下的所有子节点数组返回// 如果不存在 rootId 下的子节点&#xff0c;则返回一个空数组}module.exports convert…

from tensorflow.keras.layers import Dense,Flatten,Input报错无法引用

from tensorflow.keras.layers import Dense,Flatten,Input 打印一下路径&#xff1a; import tensorflow as tf import keras print(tf.__path__) print(keras.__path__) [E:\\开发工具\\pythonProject\\studyLL\\venv\\lib\\site-packages\\keras\\api\\_v2, E:\\开发工具\\…

jenkins+kubernetes+git+dockerhub构建devops云平台

Devops简介 k8s助力Devops在企业落地实践 传统方式部署项目为什么发布慢&#xff0c;效率低&#xff1f; 上线一个功能&#xff0c;有多少时间被浪费了&#xff1f; 如何解决发布慢&#xff0c;效率低的问题呢&#xff1f; 什么是Devops&#xff1f; 敏捷开发 提高开发效率&…

《剑指Offer》--查缺补漏

C sizeof 赋值运算符函数 在C中可以用 struct 和 class 来定义类型。这两种类型有什么区别&#xff1f; 数组 面试题4&#xff1a;二维数组中的查找 暂留 字符串 C/C中每个字符串都以字符

在TMP中计算书名号《》高度的问题

1&#xff09;在TMP中计算书名号《》高度的问题 2&#xff09;FMOD设置中关于Virtual Channel Count&Real Channel Count的参数疑问 3&#xff09;Unity 2021.3.18f1 ParticleSystemTrailGeometryJob粒子拖尾系统崩溃 4&#xff09;XLua打包Lua文件粒度问题 这是第375篇UWA…

NR 2-STEP RA Absolute Timing Advance Command MAC CE的应用场景

3 GPP在 R2-2002413中将2-step RA引入&#xff0c;进而R16 38.321出现了 Absolute TAC MAC CE&#xff0c;在 NR Timing Advance(TA)_ntn rrc-CSDN博客 有提到这个MAC CE&#xff0c;当时以“absolute timing advance command MAC CE 在2-step RA的某个场景下使用”一笔带过&am…

前端架构: 脚手架命令行交互核心实现之inquirer和readline的应用教程

命令行交互核心实现 核心目标&#xff1a;实现命令行行交互&#xff0c;如List命令行的交互呢比命令行的渲难度要更大&#xff0c;因为它涉及的技术点会会更多它涉及以下技术点 键盘输入的一个监听 (这里通过 readline来实现)计算命令行窗口的尺寸清屏光标的移动输出流的静默 …
最新文章