【JS逆向学习】国家加密系列-SM算法实例

SM系列

1、国家加密算法介绍

事实上从 2010 年开始,我国国家密码管理局就已经开始陆续发布了一系列国产加密算法,其中SM1、SM4、SM7、祖冲之密码(ZUC)是对称算法;SM2、SM9是非对称算法;SM3是哈希算法。目前,这些算法已广泛应用于各个领域中。其中SM2、SM3、SM4 三种加密算法是比较常见的

2、算法分类
算法名称算法类别应用领域特点
SM1对称(分组)加密算法芯片分组长度、密钥长度均为 128 比特,算法安全保密强度及相关软硬件实现性能与AES相当
SM2非对称(基于椭圆曲线 ECC)加密算法数据加密ECC 椭圆曲线密码机制 256 位,相比 RSA 处理速度快,消耗更少,SM2算法在很多方面都优于RSA算法(RSA发展的早应用普遍,SM2领先也很正常)
SM3散列(hash)函数算法完整性校验安全性及效率与 SHA-256 相当,压缩函数更复杂, 此算法适用于商用密码应用中的数字签名和验证,消息认证的生成与验证以及随机函数的生成,可满足多种密码应用的安全需求。
SM4对称(分组)加密算法数据加密和局域网产品该算法的分组长度为128比特,密钥长度为128比特。加密算法与密钥扩展算法都采用32轮非线性迭代结构
SM7对称(分组)加密算法非接触式 IC 卡分组长度为128比特,密钥长度为128比特
SM9标识加密算法(IBE)端对端离线安全通讯SM9算法不需要申请数字证书,适用于互联网应用的各种新兴应用的安全保障
ZUC对称(序列)加密算法移动通信 4G 网络祖冲之序列密码算法是中国自主研究的流密码算法,是运用于移动通信4G网络中的国际标准密码算法,该算法包括祖冲之算法(ZUC)、加密算法(128-EEA3)和完整性算法(128-EIA3)三个部分。目前已有对ZUC算法的优化实现,有专门针对128-EEA3和128-EIA3的硬件实现与优化
3、算法实现
JavaScript实现

在 JavaScript 中已有比较成熟的实现库,这里推荐 sm-crypto[4],目前支持 SM2、SM3 和 SM4,需要注意的是,SM2 非对称加密的结果由 C1、C2、C3 三部分组成,其中 C1 是生成随机数的计算出的椭圆曲线点,C2 是密文数据,C3 SM3 的摘要值,最开始的国密标准的结果是按 C1C2C3 顺序的,新标准的是按 C1C3C2 顺序存放的,sm-crypto 支持设置 cipherMode,也就是 C1C2C3 的排列顺序。

// cnpm install sm-crypto --save
const sm2 = require('sm-crypto').sm2
// 1: C1C3C2,0: C1C2C3,默认为1
const cipherMode = 1
 
// 生成密钥对
let keypair = sm2.generateKeyPairHex()
let publicKey = keypair.publicKey   // 公钥
let privateKey = keypair.privateKey // 私钥
 
let msgString = "待加密数据"
let encryptData = sm2.doEncrypt(msgString, publicKey, cipherMode)    // 加密结果
let decryptData = sm2.doDecrypt(encryptData, privateKey, cipherMode) // 解密结果
 
console.log("encryptData: ", encryptData)
console.log("decryptData: ", decryptData)
Python实现

使用之前先安装三方库:pip install gmssl

from gmssl import sm2
 
# 16 进制的公钥和私钥
private_key = '2423c43b9d3a5a8aa41c983ef30231986ea38398b8a36f082057478f0ef23a13'
public_key = '045cfaca8c4fc5af189bbe36f831017daa51dd5116c1118affda2185aaec53a8cbc80762f1b7df97c7ac004194f721cb2862e493945f9dc86a53c06fe00fbd6273'
sm2_crypt = sm2.CryptSM2(public_key=public_key, private_key=private_key)
 
# 待加密数据和加密后数据为 bytes 类型
data = b"I love Python"
enc_data = sm2_crypt.encrypt(data)
dec_data = sm2_crypt.decrypt(enc_data)
 
print('enc_data: ', enc_data.hex())
print('dec_data: ', dec_data)

关于算法还原,大家尽量能用javascript实现尽量用javascript,当然这只是个人建议,毕竟不同的程序猿对语言的偏好不同

具体案例

1、逆向目标
  • 目标:某某某
  • 主页:https://fuwu.nhsa.gov.cn/nationalHallSt/#/search/medical?code=90000&flag=false&gbFlag=true
  • 接口:
    https://fuwu.nhsa.gov.cn/ebus/fuwu/api/nthl/api/CommQuery/queryFixedHospital
  • 逆向参数:
    • 头部加密:X-Tif-Nonce、X-Tif-Paasid、X-Tif-Signature、X-Tif-Timestamp、X-Tingyun
    • pyaload:encData、signData
    • response: encData、signData
2、逆向过程

老规矩,直接翻页观察网络请求,然后比对两次的请求结果,哪些参数是加密的显而易见了
在这里插入图片描述

3、逆向分析
3.1 我们先来分析头部参数加密

对这种请求头加密,我们先挑一个比较有特点的加密参数搜索看下,这里我们选择 X-Tif-Signature 进行搜索尝试,发现只有两条结果
在这里插入图片描述
这就好办了,每个地方都打一个断点,然后翻页,发现断点断住了,如下
在这里插入图片描述

function f(t) {
  var r = n("6c27").sha256
    , s = Math.ceil((new Date).getTime() / 1e3)
    , h = Object(i.a)()
    , f = s + h + s;
  return t.headers["x-tif-paasid"] = l.paasId,
  t.headers["x-tif-signature"] = r(f),
  t.headers["x-tif-timestamp"] = s,
  t.headers["x-tif-nonce"] = h,
  t.headers.Accept = "application/json",
  t.headers.contentType = "application/x-www-form-urlencoded",
  t.data = {
      data: t.data || {}
  },
  t.data.appCode = l.appCode,
  t.data.version = l.version,
  t.data.encType = "SM4",
  t.data.signType = "SM2",
  t.data.timestamp = s,
  t.data.signData = function(t) {
      try {
          var n = m(t.data)
            , i = p(n);
          i.data = p(i.data);
          var r = v(i)
            , a = o.doSignature(r, d, {
              hash: !0
          });
          return e.from(a, "hex").toString("base64")
      } catch (e) {}
  }(t),
  t.data.data = {
      encData: function(e, t) {
          switch (e.toUpperCase()) {
          case "SM2":
              return function(e) {
                  try {
                      var t = o.generateKeyPairHex()
                        , n = t.publicKey
                        , i = e;
                      o.doEncrypt(i, n, 1)
                  } catch (e) {}
              }(t);
          case "SM3":
              return function(e) {
                  try {
                      var t = a(e);
                      return t
                  } catch (e) {}
              }(t);
          case "SM4":
              return function(e) {
                  try {
                      for (var t = e.data.data && JSON.stringify(e.data.data), n = "", i = 0; i < t.length; i++) {
                          var r = t.charAt(i)
                            , o = t.charCodeAt(i);
                          n += o > 127 ? "\\u" + o.toString(16).padStart(4, "0") : r
                      }
                      var a = A(n);
                      e.data.appCode && e.data.appCode !== u && (u = e.data.appCode);
                      var s = y(u, c)
                        , l = b(s, a);
                      return l.toUpperCase()
                  } catch (e) {}
              }(t)
          }
      }("SM4", t)
  },
  t.data = JSON.stringify({
      data: t.data
  }),
  t
}

请求头的参数加密逻辑可以说是一目了然了,如下

var s = Math.ceil((new Date).getTime() / 1e3), h = i(),f = s + h + s;
t.headers["x-tif-timestamp"] = s
t.headers["x-tif-nonce"] = h
t.headers["x-tif-signature"] = r(f)
从上面代码我们看出 r 应该是一个sha256的加密参数,我们直接用CryptoJS模块做下验证

用CryptoJS对 f 的值做 SHA256 加密

> var CryptoJS = require('crypto-js');
undefined
> CryptoJS.SHA256('1705906542B4qU1fDP1705906542').toString();
'43b186cd11580d55ba684f7fdecbf7f52c1c5b18d5df3a23e868aed2d13a8029'
> 

在这里插入图片描述
最后看下 X-Tingyun 参数,仍然先搜索这个加密参数,发现有两条结果,比对分析后排除了第二条结果在这里插入图片描述

然后我们再在文件里搜索 wo 这个参数,只有这一个地方使用,我们在这打个断点然后继续向下执行,发现在断点处断住了
在这里插入图片描述
我们直接根据代码分析加密逻辑

// 前文中比对了翻页后的两次网络请求,判断Gu.key是一个固定值:4Nl_NnGbjwY
Gu.key = '4Nl_NnGbjwY';
var a = Ir(), i = "c=B|" + Gu.key;
function Ir() {
  try {
         return Zi().substring(0, 16)
     } catch (t) {}
 }
 Zi = (e.location.protocol,
    function() {
        function t(t) {
            return 0 > t ? NaN : 30 >= t ? 0 | Math.random() * (1 << t) : 53 >= t ? (0 | Math.random() * (1 << 30)) + (0 | Math.random() * (1 << t - 30)) * (1 << 30) : NaN
        }
        function e(t, e) {
            for (var n = t.toString(16), r = e - n.length, a = "0"; r > 0; r >>>= 1,
            a += a)
                1 & r && (n = a + n);
            return n
        }
        return function(n) {
            return n || (n = ""),
            e(t(32), 8) + n + e(t(16), 4) + n + e(16384 | t(12), 4) + n + e(32768 | t(14), 4) + n + e(t(48), 12)
        }
    }());

Zi 函数改写如下

function Zi(){
    function t(t) {
        return 0 > t ? NaN : 30 >= t ? 0 | Math.random() * (1 << t) : 53 >= t ? (0 | Math.random() * (1 << 30)) + (0 | Math.random() * (1 << t - 30)) * (1 << 30) : NaN
    }
    function e(t, e) {
        for (var n = t.toString(16), r = e - n.length, a = "0"; r > 0; r >>>= 1,
        a += a)
            1 & r && (n = a + n);
        return n
    }
    var ret = function(n) {
        return n || (n = ""),
        e(t(32), 8) + n + e(t(16), 4) + n + e(16384 | t(12), 4) + n + e(32768 | t(14), 4) + n + e(t(48), 12)
    }();
    console.log(ret.substring(0, 16));
}

还有一个 appCode 是一个固定字符串
在这里插入图片描述

至此,所有的请求头加密参数全部分析完毕

3.2 payload加密参数

先来分析 signData,可以看到使用的是 sm2 加密算法,

t.data.signData = function(t) {
  try {
      var n = m(t.data)
        , i = p(n);
      i.data = p(i.data);
      var r = v(i)
        , a = o.doSignature(r, d, {
          hash: !0
      });
      return e.from(a, "hex").toString("base64")
  } catch (e) {}
}(t)

在这里插入图片描述

var i = n("1602")
   , r = n("68b2")
   , o = r.sm2
   , a = r.sm3
   , s = r.sm4
   , l = (n("94f8"),
 {
     appCode: "T98HPCGN5ZVVQBS8LZQNOAEXVI9GYHKQ",
     version: "1.0.0",
     appSecret: "NMVFVILMKT13GEMD3BKPKCTBOQBPZR2P",
     publicKey: "BEKaw3Qtc31LG/hTPHFPlriKuAn/nzTWl8LiRxLw4iQiSUIyuglptFxNkdCiNXcXvkqTH79Rh/A2sEFU6hjeK3k=",
     privateKey: "AJxKNdmspMaPGj+onJNoQ0cgWk2E3CYFWKBJhpcJrAtC",
     publicKeyType: "base64",
     privateKeyType: "base64"
 }

sm2 加密所涉及的 publicKey、privateKey,都已在代码中明文给出,所以我们只需要弄清楚用于加密的明文是什么即可了,我们先来看下 t 的值

{
	"transformRequest": {},
	"transformResponse": {},
	"timeout": 30000,
	"xsrfCookieName": "XSRF-TOKEN",
	"xsrfHeaderName": "X-XSRF-TOKEN",
	"maxContentLength": -1,
	"headers": {
		"common": {
			"Accept": "application/json, text/plain, */*"
		},
		"delete": {},
		"get": {},
		"head": {},
		"post": {
			"Content-Type": "application/x-www-form-urlencoded"
		},
		"put": {
			"Content-Type": "application/x-www-form-urlencoded"
		},
		"patch": {
			"Content-Type": "application/x-www-form-urlencoded"
		},
		"Accept": "application/json",
		"Content-Type": "application/json",
		"channel": "web",
		"x-tif-signature": "43c7aa1773742fc396f5cd85086c0b0c353a63fe6c35138087c0fafb34a7ea0a",
		"x-tif-timestamp": 1705908253,
		"x-tif-nonce": "J6YWpOnL",
		"contentType": "application/x-www-form-urlencoded"
	},
	"withCredentials": false,
	"baseURL": "/ebus/fuwu/api",
	"method": "post",
	"url": "/nthl/api/CommQuery/queryFixedHospital",
	"data": {
		"data": {
			"addr": "",
			"regnCode": "320100",
			"medinsName": "",
			"medinsLvCode": "",
			"medinsTypeCode": "",
			"openElec": "",
			"pageNum": 4,
			"pageSize": 10,
			"queryDataSource": "es"
		},
		"appCode": "T98HPCGN5ZVVQBS8LZQNOAEXVI9GYHKQ",
		"version": "1.0.0",
		"encType": "SM4",
		"signType": "SM2",
		"timestamp": 1705908253
	}
}

然后按照以下代码逻辑处理后就是最终用于计算加密的值

var n = m(t.data), 
i = p(n);
i.data = p(i.data);
var r = v(i), 
a = o.doSignature(r, d, {
	hash: !0
});
// 可以直接把这几个函数的源码 copy 过来直接用
function m(e) {
	   var t = {}
	      , n = ["signData", "encData", "extra"];
	    for (var i in e)
	        e.hasOwnProperty(i) && !n.includes(i) && null != e[i] && (t[i] = e[i]);
	    return t
}

function p(e) {
     var t = new Array
       , n = 0;
     for (var i in e)
         t[n] = i,
         n++;
     var r = [].concat(t).sort()
       , o = {};
     for (var a in r)
         o[r[a]] = e[r[a]];
     return o
 }
 
function v(e) {
    var t = [];
    for (var n in e)
        if (e.hasOwnProperty(n) && (e[n] || "".concat(e[n])))
            if ("data" === n) {
                var i = Object.assign({}, e[n]);
                for (var r in i) {
                    if ("number" != typeof i[r] && "boolean" != typeof i[r] || (i[r] = "" + i[r]),
                    Array.isArray(i[r]) && !i[r].length && delete i[r],
                    Array.isArray(i[r]) && i[r].length > 0)
                        for (var o = 0; o < i[r].length; o++)
                            i[r][o] = p(i[r][o]);
                    null != i[r] && i[r] || delete i[r]
                }
                var a = p(i);
                t.push("".concat(n, "=").concat(JSON.stringify(a)))
            } else
                t.push("".concat(n, "=").concat(e[n]));
    return t.push("key=".concat(c)),
    t.join("&")
}

加密的逻辑我们已经很清楚了,现在直接做下验证

const sm2 = require('sm-crypto').sm2;
var privatekey = '009c4a35d9aca4c68f1a3fa89c93684347205a4d84dc260558a049869709ac0b42';
var data = 'appCode=T98HPCGN5ZVVQBS8LZQNOAEXVI9GYHKQ&data={"pageNum":"4","pageSize":"10","queryDataSource":"es","regnCode":"320100"}&encType=SM4&signType=SM2&timestamp=1705908253&version=1.0.0&key=NMVFVILMKT13GEMD3BKPKCTBOQBPZR2P';

var result = sm2.doSignature(data, privatekey, {hash: 1});
console.log(result);
result: d3fc3a2ef0ca66500ee1988ec8ca366a11c29b3a4853984258dc3e8a1caa27bf7caca8346eac0f4f1288e5016c05bf306b94fa58bc7d6961cb96cc21b16d96cf
# 后面应该还要做个 base64,这里我们就不验证了,大家感兴趣的可以去补全一下

继续看 encData 加密,从前面接口参数分析中可以看到数据加密方式是 SM4 方式
在这里插入图片描述
我们简单分析下 encData 的加密逻辑

try {
	for (var t = e.data.data && JSON.stringify(e.data.data), n = "", i = 0; i < t.length; i++) {
       var r = t.charAt(i)
         , o = t.charCodeAt(i);
       n += o > 127 ? "\\u" + o.toString(16).padStart(4, "0") : r
   }
   var a = A(n);
   e.data.appCode && e.data.appCode !== u && (u = e.data.appCode);
   var s = y(u, c)
     , l = b(s, a);
   return l.toUpperCase()
} catch (e) {}

function y(e, t) {
    return A(b(A(e.substr(0, 16)), A(t)).toUpperCase().substr(0, 16))
}
function b(t, n) {
    var i = 16 - parseInt(n.length % 16);
    n = n.concat(new Array(i).fill(i));
    var r = s.encrypt(n, t);
    return e.from(r).toString("hex")
}
function A(e) {
    var t, n, i = new Array;
    t = e.length;
    for (var r = 0; r < t; r++)
        (n = e.charCodeAt(r)) >= 65536 && n <= 1114111 ? (i.push(n >> 18 & 7 | 240),
        i.push(n >> 12 & 63 | 128),
        i.push(n >> 6 & 63 | 128),
        i.push(63 & n | 128)) : n >= 2048 && n <= 65535 ? (i.push(n >> 12 & 15 | 224),
        i.push(n >> 6 & 63 | 128),
        i.push(63 & n | 128)) : n >= 128 && n <= 2047 ? (i.push(n >> 6 & 31 | 192),
        i.push(63 & n | 128)) : i.push(255 & n);
    return i
}

上述代码中我们只需要关心 var r = s.encrypt(n, t); 中的 s 是哪来的,我们把思维向前放一放就发现了,如下图,很明显是一个 SM4 加密
在这里插入图片描述
至此,payload 的所有加密参数我们分析完毕,response 的参数加密分析逻辑和 payload 完全一致,大家感兴趣的可以自行分析

4、逆向总结

回顾整个流程,其实没有什么难度,入口也很好找,参数的加密逻辑也不复杂

原创声明:未经许可,不得转载。
如有侵权,请联系作者删除删除。

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

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

相关文章

大数据导论(4)---大数据应用

文章目录 1. 在互联网中的应用1.1 推荐系统1.2 长尾理论1.3 推荐方法与模型1.4 推荐系统应用 2. 在其他领域的应用2.1 企业营销2.2 智慧交通 1. 在互联网中的应用 1.1 推荐系统 1. 推荐系统产生&#xff1a;  (1) 互联网的飞速发展使我们进入了信息过载的时代&#xff0c;搜索…

二叉树题目:二叉树的序列化与反序列化

文章目录 题目标题和出处难度题目描述要求示例数据范围 前言解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;二叉树的序列化与反序列化 出处&#xff1a;297. 二叉树的序列化与反序列化 难度 8 级 题目描述 要求 序列…

数据结构:堆与堆排序

目录 堆的定义&#xff1a; 堆的实现&#xff1a; 堆的元素插入&#xff1a; 堆元素删除&#xff1a; 堆初始化与销毁&#xff1a; 堆排序&#xff1a; 堆的定义&#xff1a; 堆是一种完全二叉树&#xff0c;完全二叉树定义如下&#xff1a; 一棵深度为k的有n个结点的二…

微信小程序的nodejs+vue课堂在线学习系统教学辅助平台PHP设计与实现

小程序主要实现功能&#xff1a;一、用户的登录与实现 二、课程页面。学生们可以观看课程视频【课程视频有章程】&#xff0c;搜索课程&#xff0c;课程签到&#xff0c;评论课程&#xff0c;课后答题&#xff08;课后成绩&#xff09;&#xff0c;课程互动&#xff08;在视频下…

【深度学习】手把手教你使用 Auto DL 远程服务器连接 PyCharm

前言 文章性质&#xff1a;实操记录 &#x1f4bb; 主要内容&#xff1a;主要记录了如何租用 Auto DL 服务器&#xff0c;以及如何在 PyCharm 中连接远程服务器。 相关文档&#xff1a;如何使用 Auto DL 远程服务器连接 PyCharm 运行代码 - 知乎 冷知识1&#xff1a;小伙伴们不…

c++:string相关的oj题(把字符串转换成整数、344.反转字符串、387. 字符串中的第一个唯一字符、917. 仅仅反转字母)

文章目录 1.把字符串转换成整数题目详情代码思路 2. 344.反转字符串题目详情代码1思路1代码2思路 3. 387. 字符串中的第一个唯一字符题目详情代码思路 4. 917. 仅仅反转字母题目详情代码思路 1.把字符串转换成整数 传送门 题目详情 代码 class Solution { public:int StrToI…

提升用户体验的利器——TTS语音合成软件盘点

提升用户体验的利器——TTS语音合成软件盘点 在当今信息爆炸的时代&#xff0c;人们每天都要处理大量的文本信息。因此&#xff0c;将文本信息转化为语音信息&#xff0c;使得信息能够以更自然、更方便的方式传达给人们&#xff0c;就显得尤为重要。这就是TTS&#xff08;Text…

【C++修行之道】竞赛常用库函数(sort,min和max函数,min_element和max_element、nth_element)

目录 一、sort 1.1sort简介 语法 参数 功能 适用容器 1.2sort的用法 1.3自定义比较函数 示例 1265蓝桥题 —— 排序 二、min和max函数 三、min_element和max_element 497蓝桥题 —— 成绩分析 四、nth_element 一、sort 1.1sort简介 sort函数包含在头文件<a…

手机软件的测试主要有哪些方面去测试,性能测试用什么去测试好?

手机App软件与Web软件系统的架构是不一样的&#xff0c;手机是基于CS架构&#xff0c;而Web系统是基于BS架构的&#xff0c;所以测试手机App软件那么要考虑的东西会更多一些。 分析题主的问题包含两块&#xff1a; 1、手机软件(App)测试主要有哪些方面&#xff1f; 2、手机软件…

【C/C++】C/C++编程——为什么学习 C++?

当提到C的时候&#xff0c;很多人会觉得语法复杂、学习曲线陡峭&#xff0c;并且好像与C语言还有点"纠缠不清"。尽管如此&#xff0c;C仍然是当今世界上最受欢迎和最有影响力的编程语言之一。特别是在当今快速发展的人工智能&#xff08;AI&#xff09;领域&#xff…

java数据结构与算法刷题-----LeetCode645. 错误的集合(位运算解法需要重点掌握)

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 法一&#xff1a;桶排序思想法二&#xff1a;位运算 法一&#x…

gdip-yolo项目解读:gdip模块 |mdgip模块 |GDIP regularizer模块的使用分析

gdip-yolo是2022年提出了一个端到端的图像自适应目标检测框架&#xff0c;其论文中的效果展示了良好的图像增强效果。其提出了gdip模块 |mdgip模块 |GDIP regularizer模块等模块&#xff0c;并表明这是效果提升的关键。为此对gdip-yolo的项目进行深入分析。 gdip-yolo的论文可以…

ARM 驱动 1.22

linux内核等待队列wait_queue_head_t 头文件 include <linux/wait.h> 定义并初始化 wait_queue_head_t r_wait; init_waitqueue_head(&cm_dev->r_wait); wait_queue_head_t 表示等待队列头&#xff0c;等待队列wait时&#xff0c;会导致进程或线程被休眠&…

springsecurity集成kaptcha功能

前端代码 本次采用简单的html静态页面作为演示&#xff0c;也可结合vue前后端分离开发&#xff0c;复制就可运行测试 项目目录 登录界面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</…

详谈c++智能指针!!!

文章目录 前言一、智能指针的发展历史1.C 98/03 的尝试——std::auto_ptr2.std::unique_ptr3.std::shared_ptr4.std::weak_ptr5.智能指针的大小6.智能指针使用注意事项 二、智能指针的模拟实现三、C11和boost中智能指针的关系 前言 C/C 语言最为人所诟病的特性之一就是存在内存…

Quartus II使用小技巧

工程结构&#xff1a; 在建立完某项设计的文件后&#xff0c;依次在其里面新建四个文件夹&#xff0c;分别为&#xff1a;rtl、qprj、msim、doc。 rtl文件夹用于存放设计的源文件。 doc文件夹用于存放设计的一些文档性的资料。 qprj文件夹用于存放quaruts 工程以及quartus生…

陪玩系统:最新商业版游戏陪玩语音聊天系统3.0商业升级独立版本源码

首发价值29800元的最新商业版游戏陪玩语音聊天系统3.0商业升级独立版本源码 &#xff08;价值29800&#xff09;最新陪玩3.0独立版本 &#xff0c;文件截图 结尾将会附上此系统源码以及详细搭建教程包含素材图仅用于学习使用 陪玩系统3.0独立升级版正式发布&#xff0c;此版本…

项目管理中如何有效沟通?项目管理有效沟通指南

无论是少数人的小型企业还是拥有数十名员工的大公司&#xff0c;有效的沟通对于确保每个人都参与并准备好在项目中实现相同的目标至关重要。 然而&#xff0c;由于沟通不畅&#xff0c;似乎在翻译中总是丢失一些东西。事实上&#xff0c;根据布兰迪斯大学的一项研究&#xff0c…

【复现】SpringBlade SQL 注入漏洞_22

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 SpringBlade 是由一个商业级项目升级优化而来的SpringCloud微服务架构&#xff0c;采用Java8 API重构了业务代码&#xff0c;完全…

一文梳理Windows自启动位置

不同版本的Windows开机自启动的位置略有出入&#xff0c;一般来说&#xff0c;Windows自启动的位置有&#xff1a;自启动文件夹、注册表子键、自动批处理文件、系统配置文件等。如果计算机感染了木马&#xff0c;很有可能就潜伏于其中&#xff01;本文将说明这些常见的Windows开…