Crypto知识

Crypto知识

在逆向中常常会遇到各种加密算法。虽然Reverse中对Crypto的要求不算很高,但了解常见加密方法的基本原理,有助于识别加密算法中的魔改,也能够自己编写exp而不是死板地套用他人的脚本。

BaseX

Base系列编码方法,思想基本一致,就是将密文转换为二进制再分组,将每一组二进制用新的字符代替。

Base16

将二进制分割为4位一组,并将二进制映射为十六进制的16个字符。位数不够4的倍数可以填充0。

Base32

将二进制分割为5位一组,映射为32个字符,不够5的倍数填"="。

映射表为A~Z,2~7

Base58

1.(可选)添加版本前缀,计算校验和。

2.将二进制数据视为一个大整数,使用Base58字符集的字符作为数字的基数。

3.重复地将大整数除以58,记录余数生成字符,直到商为0。

4.反转生成的字符顺序

Base64

最常见。

Base85 91 100

少见。遇到填坑。

识别

找码表。

解码

  • 在线网站。Cyberchef支持换表。

  • python脚本。

    encode:

    # coding=utf-8
    import base64
    
    #转换为utf-8
    string = 'hello'.encode("utf-8") #encode()不填时默认为utf-8
    
    #baseX编码
    base16 = base64.b16encode(string)
    
    print(base16)
    

    decode:

    # coding=utf-8
    import base64
    
    string = 'NBSWY3DP'
    
    #baseX解码
    base32 = base64.b32decode(string)
    
    print(base32)
    

    常见魔改

    换表。有时候会隐藏换表算法,注意寻找。

    分组位数或许也会有魔改?目前还没遇到。

    RC4

    对称加密。

    RC4的关键变量

    1.密钥流:根据明文密钥生成密钥流,密钥流长度和明文长度对应,明文长度=密钥流长度=密文长度。因为密文第i字节=明文第i字节xor密钥流第i字节

    2.状态向量S:长度为256,s[0]~s[255]。每个单元都是一个字节。

    3.临时向量T:长度为256,每个单元也是一个字节。如果密钥长度为256字节,就直接将密钥的值赋予T,否则循环赋予。

    4.密钥K:长度为1-256字节,通常取16字节(其实不怎么通常)。

    加密原理(C/C++)

    1.初始化S&T

    //初始化S和T,S[i]=i,使用密钥对T循环赋值
    	void initial() {
    		for(int i=0;i<256;++i){
    			S[i]=i;
    			T[i]=K[i%keylen];
    		}
    	}
    

    2.初始排列S

    //对S进行初始化排序
    	void rangeS() {
    		int j=0;
    		for(int i=0;i<256;++i){
    			j=(j+S[i]+T[i])%256;
    			swap(S[i],S[j]);
    		}
    	}
    

    3.产生密钥流

    //根据明文长度和S生成密钥流k len为明文的字节数
    void RC4::keyStream(int len) {
    	initial();
    	rangeS();
     
    	int i=0,j=0,t;
    	while(len--){
    		i=(i+1)%256;
    		j=(j+S[i])%256;
            swap(S[i],S[j]);
            t=(S[i]+S[j])%256;
    		k.push_back(S[t]);
    	}
    }
    

    4.将k的每一位与原文进行异或得到密文

    for(int i=0;i<lenFile;++i){
    		out<<(unsigned char)(bits[i]^k[i]);
    

    解密

    通过对加密算法的分析我们得知:只要知道明文长度和密钥,就能够确定密钥流,而且加密方法又是可逆性很好的异或算法。因此解密的思路比较明确:

    1.获得密钥流

    2.将密文与密钥流每一位进行异或

    魔改

    RC4的魔改无非在生成密钥流的过程中进行,比如对key偷偷操作一下,改一改操作轮数之类的。但我们生成密钥流的办法其实就是完全照搬原来的算法,所以这个过程中魔改了什么其实不是很重要,主要是要看出来并在exp中体现。

    题外话

    在看相关exp中注意到一些小tips记录一下。

    1.不引入新变量tmp实现swap(可能会遇到~~(其实OI竞赛里学过但早忘了我是RJ,5555)~)

    a=a+b;
    b=a-b;//b=a(ori)
    a=a-b;//a=b(ori)
    

    2.unsinged char与char

    参考:(https://blog.csdn.net/huangyimo/article/details/82980364)

    “程序实现时,需要注意的是,状态向量数组S和临时向量数组T的类型应设为unsigned char,而不是char。因为在一些机器下,将char默认做为signed char看待,在算法中计算下标i,j的时候,会涉及char转int,如果是signed的char,那么将char的8位拷贝到int的低8位后,还会根据char的符号位,在int的高位补0或1。由于密钥是随机产生的,如果遇到密钥的某个字节的高位为1的话,那么计算得到的数组下标为负数,就会越界。”

    总结:涉及到char转int问题时,最好使用unsigned char而非char。

    RSA

    基于大数分解难题的加密算法,非对称。

    相关数论知识

    同余

    给定整数a,b,若有正整数m,(a-b)%m==0,则称a,b对于模m同余,记作a≡b(mod m)。

    其意义是a,b对m取模结果相等。

    乘法逆元

    如果ax≡1(mod m),且gcd(a,m)=1,则称a关于模m的乘法逆元为x。

    只有a,m互质时,a才有关于p的逆元。

    若将a关于模p的逆元记作inv(a),则有应用:(a/b)%p≡(a*inv(b))%p

    例子:当a=3,m=5,那么a关于模5的逆元为2,因为有:

    3*2≡1(mod 5)

    费马小定理Fermat's little theorem是求解逆元的一个重要方法:

    如果p是一个质数,而a不是p的倍数,则有ap-1 ≡ 1 (mod p)

    所以当p为质数的时候,ap-2 就是a关于模p的逆元

    扩展欧几里得算法Extended euclidean algorithm也可求解逆元。

    欧拉函数

    OI学过euler筛法筛质数。

    φ(n)表示小于等于n的正整数中与n互质的数的数目。

    密钥生成步骤

    1.找两个大素数p,q

    2.n=pq

    3.Euler Formula: φ(n)=(p-1)(q-1)

    4.选定e,使得1<e<φ(n),且e与φ(n)互质。

    5.计算e关于φ(n)的模逆元d。

    得到公钥pk=(e,n),私钥sk(d,n)

    加解密步骤

    给定明文M,加密过程如下:

    • 计算C≡Me (mod n),得到的C就是密文。

    给定密文C,解密过程如下:

    • 计算M≡Cd (mod n),得到的M就是解密后的明文。

    例题

    来自buuctf reverse rsa

    压缩包里有flag.enc(密文),pub.key(密钥)

    把密钥扔进在线网站解析可以得到e,n

    在线大数分解n可以得到:

    p=285960468890451637935629440372639283459
    q=304008741604601924494328155975272418463

    然后可以计算φ(n),d,获得私钥key,随后利用python的rsa库进行解密,打开flag.enc作为密文。

    import gmpy2
    import rsa
    e=65537
    p=285960468890451637935629440372639283459
    q=304008741604601924494328155975272418463
    n=p*q
    eul=(p-1)*(q-1)
    d=gmpy2.invert(e,eul)
    key = rsa.PrivateKey(n, e, int(d), p, q)
    
    with open("C:\\Users\\accel\\Desktop\\output\\flag.enc","rb+") as f:
        f=f.read()
        print(rsa.decrypt(f,key))
    #b'flag{decrypt_256}\n'
    

    End

    还有美好的TEA大家族,AES等,特别是TEA,得额外再写一篇。

    贴一张5.5周年荒芜拉普兰德大头照(物理意义上的大头)。

posted @ 2024-10-31 17:08  brs7  阅读(3)  评论(0编辑  收藏  举报