这是Crypto方向的笔记

以下大多是主播在网站CryptoHack上做题总结的知识点以及WP

0x01

一个python代码,可以输出flag

#!/usr/bin/env python3

import sys
# import this

ords = [81, 64, 75, 66, 70, 93, 73, 72, 1, 92, 109, 2, 84, 109, 66, 75, 70, 90, 2, 92, 79]

print("Here is your flag:")
print("".join(chr(o ^ 0x32) for o in ords))
import this

下面是逐行讲解

#!/usr/bin/env python3

在linux/mac系统中指明编译器,在windows中会被当成注释而无效。

import sys

导入system模块,其中有许多与系统相关的功能(但是这里好像没有用到)。

ords创建并初始化了一个数组ords,用于存放密文。

print("".join(chr(o ^ 0x32) for o in ords))

这段代码处理并输出了flag。for o in ords代表将ords中的元素存储到了临时变量o中,相当于将ords中的元素依次处理。join()方法可以用于拼接,将解密的文字拼接起来。 chr()函数用于将数字按照ASCII码表转换为对应的字符。经过o ^ 0x32的异或处理后解密为需要的明文,而前面的""是连接符,表示收尾相接,不插入任何东西。

CryptoHack – CryptoHack 简介 - Great Snakes

0x02

十六进制(hex):将十六进制转换为字节bytes.fromhex(),将字节转换为十六进制hex()例如:hes(255)->0x63,以及.hex()例如:b"Hello".hex()→ '48656c6c6f'

同时还可以这么用:

b"crypto{test}".hex(sep=" ", bytes_per_sep=2)
# → '6372 7970 746f 7b74 6573 747d'

可以用于分组,每2个字节一组。

base64:导入库import base64,函数编码base64base64.b64encode()

字节和大整数:导入库from Crypto.Util.number import *,字节转longbytes_to_long, 以及long_to_bytes

异或:XOR 是一个位向算符,如果位相同则返回 0,否则返回 1。在教材中,XOR 算子用 ⊕ 表示,但在大多数挑战和编程语言中,你会看到用 caret 代替。

有关ASCII码:为了将crypto转化为一个ASCII码组成的数组,实现方法:

label = "crypto"
ords = [ord(c) for c in label]
print(ords)
# → [99, 114, 121, 112, 116, 111]

以及:

label = "crypto"
ords = []
for c in label:
    ords.append(ord(c))
print(ords)
# → [99, 114, 121, 112, 116, 111]

chr()可以用于将整数转换为ASCII对应的字符

0x03

利用XOR的各个属性(同一律等)进行解密的一道题

题目:

KEY1 = a6c8b6733c9b22de7bc0253266a3867df55acde8635e19c73313
KEY2 ^ KEY1 = 37dcb292030faa90d07eec17e3b1c6d8daf94c35d4c9191a5e1e
KEY2 ^ KEY3 = c1545756687e7573db23aa1c3452a098b71a7fbf0fddddde5fc1
FLAG ^ KEY1 ^ KEY3 ^ KEY2 = 04ee9855208a2cd59091d04767ae47963170d1660df7f56f5faf

使用python实现,下面是我的做法:

from Crypto.Util.number import *
key1=bytes.fromhex("a6c8b6733c9b22de7bc0253266a3867df55acde8635e19c73313")
key1_key2 = bytes.fromhex("37dcb292030faa90d07eec17e3b1c6d8daf94c35d4c9191a5e1e")
key2_key3 = bytes.fromhex("c1545756687e7573db23aa1c3452a098b71a7fbf0fddddde5fc1")
flag_key1_key2_key3=bytes.fromhex("04ee9855208a2cd59091d04767ae47963170d1660df7f56f5faf")
key2 =bytes_to_long(key1)^bytes_to_long(key1_key2)
key3 = key2^bytes_to_long(key2_key3)
flag = long_to_bytes(bytes_to_long(flag_key1_key2_key3)^bytes_to_long(key1)^key2^key3)
print(flag)

及其粗暴,但做出来还是挺有成就感的

下面是网络大蛇的做法:

from pwn import xor
k1=bytes.fromhex('a6c8b6733c9b22de7bc0253266a3867df55acde8635e19c73313')
k2_3=bytes.fromhex('c1545756687e7573db23aa1c3452a098b71a7fbf0fddddde5fc1')
flag=bytes.fromhex('04ee9855208a2cd59091d04767ae47963170d1660df7f56f5faf')
print(xor(k1,k2_3,flag))  

哇xor()刚学的就忘,你不用打ctf了

0x04

扩展欧几里得算法

首先是裴蜀定理,定义如下:
$$
pu+qv=gcd(p,q)
$$
是否有解?使用python实现。

首先是一个结论:
$$
gcd(a,b)=gcd(b,a % b )
$$
这意味着可以使用递归实现函数

RSA算法

非对称加密算法

原理:

1.两个大素数p和q,假设两数长度一样

2.计算n=pq

3.计算欧拉函数:
$$
\varphi(n)=(p-1)(q-1)
$$
4.选取加密密钥e,其与phi(n)互质,合适的e可以加快加解密速度.e的常用值有3,17,65537(2^16+1)

5.使用扩展欧几里得算法(辗转相除法)求出e模phi(n)的逆元d,即:
$$
ed \equiv 1 mod \varphi(n)
$$
指路:欧几里得算法

6.m明文,c密文,加密公式为:
$$
c=m^emodn
$$
相应的,解密公式反过来即可。

但是由于数字太大,一般使用快速幂取模,分布计算减小计算量

而python自带的pow()可以自动实现快速幂取模,上式的加密公式也可以写成:

pow(m,e,n)

讲解一下扩展欧几里得算法:

首先是mod符号,意思是"按模......",比如:
$$
a\equiv b (mod m)
$$
读作a与b模m同余,定义为m整除(a-b)

对于上述的
$$
ed \equiv 1 mod \varphi(n)
$$
意为:

(ed-1)能被phi(n)整除,或是ed 和 1 除以 φ(n) 的余数相同

即存在k,使得:
$$
(ed-1)/\varphi(n)=k
$$

$$
k·\varphi(n)=ed-1
$$

顺便附上一个c语言求最大公约数的程序:

#include<stdio.h>
int main(void)
{
	int a, b, c;
	int temp1,temp2,min,max;
	scanf("%d %d %d", &a, &b, &c);// 即min/max
	temp1 = a > b ? a : b;
	temp2 = a < b ? a : b;
	max = temp1 > c ? temp1 : c;
	min = temp2 < c ? temp2 : c;
	int t;//此处利用 辗转相除法(欧几里得算法) 得到max和min的最大公约数
	int m = max;
	int n = min;
	while (n != 0)
	{
		t = m % n;
		m = n;
		n = t;
	}
	printf("%d/%d", min/m, max/m);
	return 0;
}

同时附上一个欧几里得算法的实例(来源于百度百科):

假如需要求 1997 和 615 两个正整数的最大公约数,用欧几里得算法,是这样进行的:

1997 ÷ 615 = 3 (余 152)

615 ÷ 152 = 4(余7)

152 ÷ 7 = 21(余5)

7 ÷ 5 = 1 (余2)

5 ÷ 2 = 2 (余1)

2 ÷ 1 = 2 (余0)

以除数和余数反复做除法运算,当余数为 0 时(即除数可以整除被除数),可得当前中的被除数、除数的最大公约数是除数,根据欧几里得算法原理,就得出了 1997 和 615 的最大公约数 1。

先用大数a除以小数b(算式1),得到的余数作为算式2的除数,而算式1的除数作为算式2的被除数;循环直到余数为0,得到的除数就是最大公约数,也就是gcb(a,b)

0x05

对负数取模:

What is the output when this statement executed: System.out.printf(-7 % 3)

而余数的定义是:

如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = qd+ r 且0 ≤ r < d。其中,q 被称为商,r 被称为余数。

即:$-7=3\times(-3)+2$

因此余数是2

二次剩余

在模 n 的意义下,如果一个整数 a 能写成某个整数的平方,那么 a 就叫模 n 的二次剩余;否则叫二次非剩余。

定义是:设n为正整数,gcd(a,n)=1.如果存在整数x,使得
$$
x^2\equiv,a,(mod,n)
$$
则称a是模n的二次剩余,而x就是a在模n下的平方根

也就是说,二次剩余就是“模某个数下能开平方的数。

这些内容在《初等数论》p195中有提到

Legendre符号

用于判断是否为二次剩余(quadratic residue)

Quadratic Residue * Quadratic Residue = Quadratic Residue
Quadratic Residue * Quadratic Non-residue = Quadratic Non-residue
Quadratic Non-residue * Quadratic Non-residue = Quadratic Residue

如果觉得麻烦,其实可以转换一下:

quadratic residue为1,而quadratic non-residue为-1,同样成立

而legender symbol的主要内容是:
$$
(a/p)\equiv a^{(p-1)/2}mod,p
$$

(a/p)=1  | a is a quadratic residue and a 恒等 0 mod p
(a/p)=-1 | a is a quadratic non-restdue 
(a/p)=0  | a 恒等 0 modp

关于Tonelli-Shanks算法_python-CSDN博客

在sympy库中有关二次剩余的函数

is_quad_residue(a,p)#判断是否是二次剩余
quadratic_residues(a)#枚举模p的二次剩余
sqrt_mod(a,p)#求满足 x² ≡ a (mod p) 的整数 x
#其中的第三个参数:all_roots=True 会返回所有原根
nthroot_mod()#见下
nthroot_mod(a, n, p, all_roots=False)

nthroot_mod()用于求解于 $n ^{index}\equiv x\pmod p$的同余式

给定 a, n, p,找出所有满足 xⁿ ≡ a (mod p) 的 x

x\pmod p -> $x\pmod p$

有关线性同余

中国剩余定理

用于求解一组模数互质的同余方程组

在知道一个数除以几个两两互质的数的余数,就可以唯一确定这个数除以他们乘积的余数

例题:

![image-20260923201812733](C:\Users\Administrator\Desktop\笔记 博客\ctf\assets\image-20260923201812733.png)

懒人做法直接使用sympy库中的solve_congruence()函数解决:

result=solve_congruence((2, 3), (3, 5), (2, 7)
print(result)