【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)))} elset.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、逆向总结

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django框架二

一、模型层及ORM 1.模型层定义 负责跟数据库之间进行通信 2.Django配置mysql 安装mysqlclient&#xff0c;mysqlclient版本最好在13.13以上 pip3 install mysqlclient DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: "mysite1",USER:root,PASSWO…

pip安装之后还是无法使用问题处理

最近由于需要使用到Python 相关功能&#xff0c; 记录下一些入门小技巧 1 python 下载安装 在window10 环境下载免安装版本&#xff0c; 并解压 安装包下载地址&#xff1a; https://www.python.org/ftp/python/3.12.1/python-3.12.1-embed-amd64.zip 2. 安装pip, 由于是内嵌…

【立创EDA-PCB设计基础】5.布线设计规则设置

前言&#xff1a;本文详解布线前的设计规则设置。经过本专栏中的【立创EDA-PCB设计基础】前几节已经完成了布局&#xff0c;接下来开始进行布线&#xff0c;在布线之前&#xff0c;要设置设计规则。 目录 1.间距设置 1.1 安全间距设置 1.2 其它间距设置 2.物理设置 2.1 导…

力扣hot100 合并两个有序链表 递归 双指针

Problem: 21. 合并两个有序链表 文章目录 &#x1f496; 递归思路 &#x1f496; 双指针 &#x1f496; 递归 思路 &#x1f468;‍&#x1f3eb; 参考地址 n , m n,m n,m 分别为 list1 和 list2 的元素个数 ⏰ 时间复杂度: O ( n m ) O(nm) O(nm) &#x1f30e; 空间复杂…

是谁说网工这行是小众行业?我帮你搜了搜……

有人说网工这行是个小众行业&#xff0c;很多朋友在喊岗位不够多&#xff0c;我帮你搜了搜&#xff0c;其实招聘岗位的需求真的很多&#xff1a; 可能你换着搜索下关键词&#xff0c;善用不同类型的渠道&#xff0c;你会有新的惊喜。 能达到这要求的&#xff0c;这位朋友&#…

线程池--JAVA

虽然线程是轻量级进程&#xff0c;但是如果当创建和销毁的的频率非常之高&#xff0c;那么它也就会消耗很多的资源。 而线程池就是用来优化线程频繁创建和销毁的场景&#xff0c;减少线程创建、销毁的频率。 ExecutorService JAVA标准库为我们实现了线程池&#xff0c;Execu…

华而有实,维乐Prevail Glide带你领略风景线,成为风景线~

大家都知道呢&#xff01;骑行&#xff0c;不仅是一种运动&#xff0c;更是一种生活态度。在骑行装备的世界里&#xff0c;一个好的坐垫对于骑行的舒适度和安全性至关重要。那今天&#xff0c;我要为大家推荐一款备受赞誉的坐垫——维乐坐垫美学系列-Prevail Glide。    为…

基于springboot+vue的甘肃非物质文化网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

pycharm创建vue项目idealTree:npm: sill idealTree buildDeps,换taobao源后还不好使?那就再换一个

心血来潮打算写个小项目&#xff0c;前后端分离&#xff0c;flask/fastapivue&#xff08;具体用哪个后端还没想好&#xff09;&#xff0c;里面的功能大概就是目前所有热门的应用的合集&#xff0c;一键出结果的那种&#xff0c;然后跟随着科技趋势&#xff0c;不断去更新维护…

CSGO搬砖项目还能火多久?

最近放假回到老家&#xff0c;见了不少亲戚朋友&#xff0c;大家不约而同都在感叹今年大环境不好&#xff0c;工作不顺&#xff0c;生意效益不好&#xff0c;公司状况不佳&#xff0c;反问我们生意如何&#xff1f;为了让他们心里好受一点&#xff0c;我也假装附和道:也不咋地&…

JavaScript DOM表单相关操作之表单相关事件

1、焦点事件 焦点事件就是鼠标的光标事件&#xff0c;点到输入框中&#xff0c;叫做获得焦点事件&#xff0c;当鼠标离开这个输入框时叫做失去焦点事件。 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>知数SEO_专注搜…

Rust 程序设计语言学习——基础语法

Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率&#xff0c;它的执行效率也是令人称赞的&#xff0c;是一种少有的兼顾开发效率和执行效率的语言。 Rust 语言由 Mozilla 开发&#xff0c;最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apach…

基于SpringBoot+vue的在线视频教育平台的设计与实现,附源码,数据库

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【文件处理】spring boot 文件处理

接收文件 PostMappingpublic result<String> add(MultipartFile file) throws IOException {// 得到目标文件夹File directory new File("file");//如果文件夹不存在就创建if(!directory.exists()){directory.mkdirs();}//文件名称String fileName file.getO…

性能进阶:使用JMeter进行websocket测试【建议收藏】

本次测试案例主要是分享如何使用JMeter进行websocket协议下的聊天接口性能测试。 包含websocket插件的下载安装、线程组及sampler的设置、csv参数化和组建分布式测试的方法、如何通过调整参数来获得发压机的最大并发数以及对测试过程的总结。 整篇文章只侧重介绍进行websocke…

Linux中的软件包管理器yum

目录 1.什么是软件包 2.关于 rzsz 3.查看软件包 4.如何安装软件 5.如何卸载软件 1.什么是软件包 ● 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. ● 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理…

阿里云优惠券领取入口、使用教程,2024优惠券更新

阿里云优惠代金券领取入口&#xff0c;阿里云服务器优惠代金券、域名代金券&#xff0c;在领券中心可以领取当前最新可用的满减代金券&#xff0c;阿里云百科aliyunbaike.com分享阿里云服务器代金券、领券中心、域名代金券领取、代金券查询及使用方法&#xff1a; 阿里云优惠券…

Linux下修改系统的运行级别

借助命令ll /etc/system/system/default.target可以查看当前的系统运行的级别&#xff1a;以下图为例运行级别就是3 但如果系统运行的级别默认为图形时&#xff0c;要将图形级别改为文本级别&#xff0c;可以按照下边两种方法运行&#xff1a; 1、重新设置链接文件 这个方法需…

03-常用编程概念

上一篇&#xff1a;02-编程猜谜游戏 本章介绍几乎所有编程语言中都会出现的概念&#xff0c;以及它们在 Rust 中的工作原理。许多编程语言的核心都有许多共同点。本章介绍的概念都不是 Rust 独有的&#xff0c;但我们会在 Rust 的上下文中讨论这些概念&#xff0c;并解释使用这…

Pymol-电子密度图展示方法-PDB数据库已发表结构和自己晶体解析得到的结构密度图

简单来说&#xff0c;想要用PyMol展示电子密度图可以归为以下两种&#xff1a; 一是展示PDB数据库中已发表数据的结构和Map的方式 以6sps.pdb为例&#xff0c;在pymol中导入该数据密度图时&#xff0c;可以无需下载对应的密度文件&#xff0c;直接用fetch即可&#xff1a; Py…