首页
社区
课程
招聘
[原创]恶意代码分析之 RC4 算法学习
发表于: 2019-12-16 02:02 8008

[原创]恶意代码分析之 RC4 算法学习

2019-12-16 02:02
8008
RC4(Rivest Cipher 4)是一种基于密钥流的加密算法,它的密钥长度可变,密钥长度在 1-256 字节范围。它的特点是算法简单、运算效率高,而且非线性度良好。它加解密使用相同的密钥,假设定义 RC4 的运算过程是RC4(key,data),那么,密文=RC4(key,明文),明文=RC4(key,密文)。因此也属于对称加密算法,WEP 里也用到了 RC4 算法。
近期分析多个勒索软件时,会遇到使用 RC4 解密相应字符串或者配置的过程,加之之前阅读过相关的分析报告后也发现存在勒索软件使用 RC4 解密相应的内容的行为,由于笔者之前并未学习过密码学,于是对 RC4 算法进行学习,并相应记录笔记。
RC4 由伪随机数生成器和异或运算组成,RC4 一个字节一个字节地加解密。给定一个密钥,伪随机数生成器接受密钥并产生一个 S 盒。S 盒用来加密数据,而且在加密过程中 S 盒会变化。

3.1 伪代码

出现的几个变量如下:S、K、T、k
S  S 盒,是一个 256 个字节大小的 char 类型数组,char S[256]
K  密钥 K,长度限定在 1-256 个字节
T  临时变量 T,大小也是 256 个字节的数组
k  输出的密钥流,大小与实际需要加密的内容一致。
S 盒与临时变量 T 初始化,伪代码如下:
for i=0 to 255 do
   S[i]=i;
   T[i]=K[ I % K_len ];

S 盒打乱过程会使用到上述初始化后的 S 与 T 变量,伪代码如下:
j=0;
for i=0 to 255 do
   j= ( j+S[i]+T[i]) % 256;
   swap(S[i],S[j]); //交换

密钥流的长度与需要加密的明文长度一致,输出密钥流 k[r],伪代码如下:
i,j=0;
for r=0 to r_len do  //r为明文
   i=(i+1) % 256;
   j=(j+S[i]) % 256;
   swap(S[i],S[j]);
   t=(S[i]+S[j]) % 256;
   k[r]=S[t];

加密与解密是一致的,k[r]为之前输出的密钥流,d[r]为加密后的内容,伪代码如下:
for r=0 to r_len do
   d[r] ^= k[r]

4 C 语言例子

4.1 主文件

#include <stdio.h>
#include<Windows.h>
#include "RC4.h"

int main()
{
    unsigned char text[] = "UOIzVDP2Vzs=";
    unsigned char key[] = "flag{this_is_not_the_flag_hahaha}";
    unsigned int i;
    printf("加密前的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("加密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%X", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("解密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    system("pause");

    return 0;
}

4.2 RC4.c

#include <string.h>

static void rc4_init(unsigned char* s_box, unsigned char* key, unsigned int key_len)
{
    unsigned char Temp[256];
    int i;
    for (i = 0; i < 256; i++)
    {
        s_box[i] = i;//顺序填充S盒
        Temp[i] = key[i%key_len];//生成临时变量T
    }
    int j = 0;
    for (i = 0; i < 256; i++)//打乱S盒
    {
        j = (j + s_box[i] + Temp[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
    }
}

void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len)
{
    unsigned char s_box[256];
    rc4_init(s_box, key, key_len);
    unsigned int i = 0, j = 0, t = 0;
    unsigned int Temp;
    for (Temp = 0; Temp < data_len; Temp++)
    {
        i = (i + 1) % 256;
        j = (j + s_box[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
        t = (s_box[i] + s_box[j]) % 256;
        data[Temp] ^= s_box[t];
    }
}

4.3 RC4.h

#ifndef RC4_H
#define RC4_H

/*
导出rc4_crypt函数,参数为要加密的数据、数据长度、密码、密码长度
*/
void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len);

#endif

5 逆向分析

5.1 静态分析

上述代码编译后,得到 RC4.exe,直接载入 IDA 中进行分析,为了效果,使用 release 模式编译,不加载 pdb 文件。先找到 main 函数,如下:


双击进入 main 函数后,只关注 RC4 加密函数,如下:


再次双击进入,逻辑非常清晰,存在 3 个循环,如下:


首先第一个循环是初始化 S 盒与临时变量 T。


第二个循环是打乱上面初始化的 S 盒


最后一个循环是输出加密密钥流,最后与明文进行逐字节异或,得到加密的密文。


5.2 动态分析

ollydbg 调试 debug 版本,如下:






6 实际案例

这里选择一个勒索样本(64a1fbb51ab62f1aa012172b45c8ca15)作为例子,IDA 打开后分析如下:


从最开始的两个函数可以发现极有可能会解密资源,进入第一个函数看看,如下:



两处循环,次数都为 256 次,第一次循环赋值是按照顺序进行赋值的,相当于之前的初始化 S 盒。


第二个函数进入后发现也是循环后进行赋值,最后有一处是对之前第一个函数得到的值进行相应的异或操作,最后得到一个值。动态调试如下, 0x0040E000 开始的 128 个字节在函数执行后保持不变,而之后的字节数值已经被更改,猜测这 128 个字节是密钥。



执行完该函数后,相关的数据区域确实发生了变化,根据之前的分析猜测是 RC4 算法,本地提取相关区域数据后对其进行手工解密如下,发现结果确实是使用地址 0x0040E000 前 128 个字节(0x80)作为密钥,然后采用 RC4 算法解密后的结果如下,与上图执行完后得到的数据是一致的。


7 总结

识别点:存在 3 个循环,前两次循环的次数为 256 次,最后一次循环的次数以某个变量的值为限,实质是需要加密(解密)的内容长度,每次循环的最后有一个异或的操作代表加密该内容。
出现的几个变量如下:S、K、T、k
S  S 盒,是一个 256 个字节大小的 char 类型数组,char S[256]
K  密钥 K,长度限定在 1-256 个字节
T  临时变量 T,大小也是 256 个字节的数组
k  输出的密钥流,大小与实际需要加密的内容一致。
S 盒与临时变量 T 初始化,伪代码如下:
for i=0 to 255 do
   S[i]=i;
   T[i]=K[ I % K_len ];
for i=0 to 255 do
   S[i]=i;
   T[i]=K[ I % K_len ];

S 盒打乱过程会使用到上述初始化后的 S 与 T 变量,伪代码如下:
j=0;
for i=0 to 255 do
   j= ( j+S[i]+T[i]) % 256;
   swap(S[i],S[j]); //交换

j=0;
for i=0 to 255 do
   j= ( j+S[i]+T[i]) % 256;
   swap(S[i],S[j]); //交换

密钥流的长度与需要加密的明文长度一致,输出密钥流 k[r],伪代码如下:
i,j=0;
for r=0 to r_len do  //r为明文
   i=(i+1) % 256;
   j=(j+S[i]) % 256;
   swap(S[i],S[j]);
   t=(S[i]+S[j]) % 256;
   k[r]=S[t];

i,j=0;
for r=0 to r_len do  //r为明文
   i=(i+1) % 256;
   j=(j+S[i]) % 256;
   swap(S[i],S[j]);
   t=(S[i]+S[j]) % 256;
   k[r]=S[t];

加密与解密是一致的,k[r]为之前输出的密钥流,d[r]为加密后的内容,伪代码如下:
for r=0 to r_len do
   d[r] ^= k[r]

4 C 语言例子

4.1 主文件

#include <stdio.h>
#include<Windows.h>
#include "RC4.h"

int main()
{
    unsigned char text[] = "UOIzVDP2Vzs=";
    unsigned char key[] = "flag{this_is_not_the_flag_hahaha}";
    unsigned int i;
    printf("加密前的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("加密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%X", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("解密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    system("pause");

    return 0;
}

4.2 RC4.c

#include <string.h>

static void rc4_init(unsigned char* s_box, unsigned char* key, unsigned int key_len)
{
    unsigned char Temp[256];
    int i;
    for (i = 0; i < 256; i++)
    {
        s_box[i] = i;//顺序填充S盒
        Temp[i] = key[i%key_len];//生成临时变量T
    }
    int j = 0;
    for (i = 0; i < 256; i++)//打乱S盒
    {
        j = (j + s_box[i] + Temp[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
    }
}

void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len)
{
    unsigned char s_box[256];
    rc4_init(s_box, key, key_len);
    unsigned int i = 0, j = 0, t = 0;
    unsigned int Temp;
    for (Temp = 0; Temp < data_len; Temp++)
    {
        i = (i + 1) % 256;
        j = (j + s_box[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
        t = (s_box[i] + s_box[j]) % 256;
        data[Temp] ^= s_box[t];
    }
}

4.3 RC4.h

#ifndef RC4_H
#define RC4_H

/*
导出rc4_crypt函数,参数为要加密的数据、数据长度、密码、密码长度
*/
void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len);

#endif

5 逆向分析

5.1 静态分析

上述代码编译后,得到 RC4.exe,直接载入 IDA 中进行分析,为了效果,使用 release 模式编译,不加载 pdb 文件。先找到 main 函数,如下:


双击进入 main 函数后,只关注 RC4 加密函数,如下:


再次双击进入,逻辑非常清晰,存在 3 个循环,如下:


首先第一个循环是初始化 S 盒与临时变量 T。


第二个循环是打乱上面初始化的 S 盒


最后一个循环是输出加密密钥流,最后与明文进行逐字节异或,得到加密的密文。


5.2 动态分析

ollydbg 调试 debug 版本,如下:






6 实际案例

这里选择一个勒索样本(64a1fbb51ab62f1aa012172b45c8ca15)作为例子,IDA 打开后分析如下:


从最开始的两个函数可以发现极有可能会解密资源,进入第一个函数看看,如下:



两处循环,次数都为 256 次,第一次循环赋值是按照顺序进行赋值的,相当于之前的初始化 S 盒。


第二个函数进入后发现也是循环后进行赋值,最后有一处是对之前第一个函数得到的值进行相应的异或操作,最后得到一个值。动态调试如下, 0x0040E000 开始的 128 个字节在函数执行后保持不变,而之后的字节数值已经被更改,猜测这 128 个字节是密钥。



执行完该函数后,相关的数据区域确实发生了变化,根据之前的分析猜测是 RC4 算法,本地提取相关区域数据后对其进行手工解密如下,发现结果确实是使用地址 0x0040E000 前 128 个字节(0x80)作为密钥,然后采用 RC4 算法解密后的结果如下,与上图执行完后得到的数据是一致的。


7 总结

识别点:存在 3 个循环,前两次循环的次数为 256 次,最后一次循环的次数以某个变量的值为限,实质是需要加密(解密)的内容长度,每次循环的最后有一个异或的操作代表加密该内容。
for r=0 to r_len do
   d[r] ^= k[r]

4 C 语言例子

4.1 主文件

#include <stdio.h>
#include<Windows.h>
#include "RC4.h"

int main()
{
    unsigned char text[] = "UOIzVDP2Vzs=";
    unsigned char key[] = "flag{this_is_not_the_flag_hahaha}";
    unsigned int i;
    printf("加密前的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("加密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%X", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("解密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    system("pause");

    return 0;
}

4.2 RC4.c

#include <stdio.h>
#include<Windows.h>
#include "RC4.h"

int main()
{
    unsigned char text[] = "UOIzVDP2Vzs=";
    unsigned char key[] = "flag{this_is_not_the_flag_hahaha}";
    unsigned int i;
    printf("加密前的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("加密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%X", text[i]);
    printf("\n");
    rc4_crypt(text, strlen((const char*)text), key, strlen((const char*)key));
    printf("解密后的数据:");
    for (i = 0; i < strlen((const char*)text); i++)
        printf("%c", text[i]);
    printf("\n");
    system("pause");

    return 0;
}
#include <string.h>

static void rc4_init(unsigned char* s_box, unsigned char* key, unsigned int key_len)
{
    unsigned char Temp[256];
    int i;
    for (i = 0; i < 256; i++)
    {
        s_box[i] = i;//顺序填充S盒
        Temp[i] = key[i%key_len];//生成临时变量T
    }
    int j = 0;
    for (i = 0; i < 256; i++)//打乱S盒
    {
        j = (j + s_box[i] + Temp[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
    }
}

void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len)
{
    unsigned char s_box[256];
    rc4_init(s_box, key, key_len);
    unsigned int i = 0, j = 0, t = 0;
    unsigned int Temp;
    for (Temp = 0; Temp < data_len; Temp++)
    {
        i = (i + 1) % 256;
        j = (j + s_box[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
        t = (s_box[i] + s_box[j]) % 256;
        data[Temp] ^= s_box[t];
    }
}

4.3 RC4.h

#ifndef RC4_H
#define RC4_H

/*
导出rc4_crypt函数,参数为要加密的数据、数据长度、密码、密码长度
*/
void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len);

#endif

5 逆向分析

5.1 静态分析

上述代码编译后,得到 RC4.exe,直接载入 IDA 中进行分析,为了效果,使用 release 模式编译,不加载 pdb 文件。先找到 main 函数,如下:


双击进入 main 函数后,只关注 RC4 加密函数,如下:


再次双击进入,逻辑非常清晰,存在 3 个循环,如下:


首先第一个循环是初始化 S 盒与临时变量 T。


第二个循环是打乱上面初始化的 S 盒


最后一个循环是输出加密密钥流,最后与明文进行逐字节异或,得到加密的密文。


5.2 动态分析

ollydbg 调试 debug 版本,如下:






6 实际案例

这里选择一个勒索样本(64a1fbb51ab62f1aa012172b45c8ca15)作为例子,IDA 打开后分析如下:


从最开始的两个函数可以发现极有可能会解密资源,进入第一个函数看看,如下:



两处循环,次数都为 256 次,第一次循环赋值是按照顺序进行赋值的,相当于之前的初始化 S 盒。


第二个函数进入后发现也是循环后进行赋值,最后有一处是对之前第一个函数得到的值进行相应的异或操作,最后得到一个值。动态调试如下, 0x0040E000 开始的 128 个字节在函数执行后保持不变,而之后的字节数值已经被更改,猜测这 128 个字节是密钥。



执行完该函数后,相关的数据区域确实发生了变化,根据之前的分析猜测是 RC4 算法,本地提取相关区域数据后对其进行手工解密如下,发现结果确实是使用地址 0x0040E000 前 128 个字节(0x80)作为密钥,然后采用 RC4 算法解密后的结果如下,与上图执行完后得到的数据是一致的。


7 总结

识别点:存在 3 个循环,前两次循环的次数为 256 次,最后一次循环的次数以某个变量的值为限,实质是需要加密(解密)的内容长度,每次循环的最后有一个异或的操作代表加密该内容。
#include <string.h>

static void rc4_init(unsigned char* s_box, unsigned char* key, unsigned int key_len)
{
    unsigned char Temp[256];
    int i;
    for (i = 0; i < 256; i++)
    {
        s_box[i] = i;//顺序填充S盒
        Temp[i] = key[i%key_len];//生成临时变量T
    }
    int j = 0;
    for (i = 0; i < 256; i++)//打乱S盒
    {
        j = (j + s_box[i] + Temp[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
    }
}

void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len)
{
    unsigned char s_box[256];
    rc4_init(s_box, key, key_len);
    unsigned int i = 0, j = 0, t = 0;
    unsigned int Temp;
    for (Temp = 0; Temp < data_len; Temp++)
    {
        i = (i + 1) % 256;
        j = (j + s_box[i]) % 256;
        unsigned char tmp = s_box[i];
        s_box[i] = s_box[j];
        s_box[j] = tmp;
        t = (s_box[i] + s_box[j]) % 256;
        data[Temp] ^= s_box[t];
    }
}
#ifndef RC4_H
#define RC4_H

/*
导出rc4_crypt函数,参数为要加密的数据、数据长度、密码、密码长度
*/
void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len);

#endif

5 逆向分析

5.1 静态分析

上述代码编译后,得到 RC4.exe,直接载入 IDA 中进行分析,为了效果,使用 release 模式编译,不加载 pdb 文件。先找到 main 函数,如下:


双击进入 main 函数后,只关注 RC4 加密函数,如下:


再次双击进入,逻辑非常清晰,存在 3 个循环,如下:


首先第一个循环是初始化 S 盒与临时变量 T。


第二个循环是打乱上面初始化的 S 盒


最后一个循环是输出加密密钥流,最后与明文进行逐字节异或,得到加密的密文。


5.2 动态分析

ollydbg 调试 debug 版本,如下:






6 实际案例

这里选择一个勒索样本(64a1fbb51ab62f1aa012172b45c8ca15)作为例子,IDA 打开后分析如下:


从最开始的两个函数可以发现极有可能会解密资源,进入第一个函数看看,如下:



两处循环,次数都为 256 次,第一次循环赋值是按照顺序进行赋值的,相当于之前的初始化 S 盒。


第二个函数进入后发现也是循环后进行赋值,最后有一处是对之前第一个函数得到的值进行相应的异或操作,最后得到一个值。动态调试如下, 0x0040E000 开始的 128 个字节在函数执行后保持不变,而之后的字节数值已经被更改,猜测这 128 个字节是密钥。



执行完该函数后,相关的数据区域确实发生了变化,根据之前的分析猜测是 RC4 算法,本地提取相关区域数据后对其进行手工解密如下,发现结果确实是使用地址 0x0040E000 前 128 个字节(0x80)作为密钥,然后采用 RC4 算法解密后的结果如下,与上图执行完后得到的数据是一致的。


7 总结

识别点:存在 3 个循环,前两次循环的次数为 256 次,最后一次循环的次数以某个变量的值为限,实质是需要加密(解密)的内容长度,每次循环的最后有一个异或的操作代表加密该内容。
#ifndef RC4_H
#define RC4_H

/*
导出rc4_crypt函数,参数为要加密的数据、数据长度、密码、密码长度
*/
void rc4_crypt(unsigned char* data, unsigned int data_len, unsigned char* key, unsigned int key_len);

#endif

5 逆向分析

5.1 静态分析

上述代码编译后,得到 RC4.exe,直接载入 IDA 中进行分析,为了效果,使用 release 模式编译,不加载 pdb 文件。先找到 main 函数,如下:


双击进入 main 函数后,只关注 RC4 加密函数,如下:


再次双击进入,逻辑非常清晰,存在 3 个循环,如下:


首先第一个循环是初始化 S 盒与临时变量 T。


第二个循环是打乱上面初始化的 S 盒


最后一个循环是输出加密密钥流,最后与明文进行逐字节异或,得到加密的密文。


5.2 动态分析

ollydbg 调试 debug 版本,如下:






6 实际案例

这里选择一个勒索样本(64a1fbb51ab62f1aa012172b45c8ca15)作为例子,IDA 打开后分析如下:


从最开始的两个函数可以发现极有可能会解密资源,进入第一个函数看看,如下:



两处循环,次数都为 256 次,第一次循环赋值是按照顺序进行赋值的,相当于之前的初始化 S 盒。


第二个函数进入后发现也是循环后进行赋值,最后有一处是对之前第一个函数得到的值进行相应的异或操作,最后得到一个值。动态调试如下, 0x0040E000 开始的 128 个字节在函数执行后保持不变,而之后的字节数值已经被更改,猜测这 128 个字节是密钥。



执行完该函数后,相关的数据区域确实发生了变化,根据之前的分析猜测是 RC4 算法,本地提取相关区域数据后对其进行手工解密如下,发现结果确实是使用地址 0x0040E000 前 128 个字节(0x80)作为密钥,然后采用 RC4 算法解密后的结果如下,与上图执行完后得到的数据是一致的。


7 总结

识别点:存在 3 个循环,前两次循环的次数为 256 次,最后一次循环的次数以某个变量的值为限,实质是需要加密(解密)的内容长度,每次循环的最后有一个异或的操作代表加密该内容。
上述代码编译后,得到 RC4.exe,直接载入 IDA 中进行分析,为了效果,使用 release 模式编译,不加载 pdb 文件。先找到 main 函数,如下:


双击进入 main 函数后,只关注 RC4 加密函数,如下:


再次双击进入,逻辑非常清晰,存在 3 个循环,如下:


首先第一个循环是初始化 S 盒与临时变量 T。



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 5
支持
分享
最新回复 (6)
雪    币: 30
活跃值: (1307)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
最后截图中,算法工具叫什么名字?
2019-12-16 05:06
0
雪    币: 9934
活跃值: (2554)
能力值: ( LV6,RANK:87 )
在线值:
发帖
回帖
粉丝
3
写的很好,希望能有其他算法的后续
2019-12-16 08:45
0
雪    币: 8427
活跃值: (5021)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
4
学习了
2019-12-16 13:26
0
雪    币: 17428
活跃值: (5009)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
5
暴强 最后截图中,算法工具叫什么名字?
CryptoTester,可以搜一下,是国外安全人员自己写的工具。
2019-12-17 18:26
0
雪    币: 17428
活跃值: (5009)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
6
Lixinist 写的很好,希望能有其他算法的后续
年末了,忙起来了,以后遇到其余算法,学习后也会分享的。
2019-12-17 18:28
0
雪    币: 9934
活跃值: (2554)
能力值: ( LV6,RANK:87 )
在线值:
发帖
回帖
粉丝
7
jishuzhain CryptoTester,可以搜一下,是国外安全人员自己写的工具。[em_13]
能上传一下吗?google没找到啊
2019-12-17 20:19
0
游客
登录 | 注册 方可回帖
返回
//