吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 468|回复: 2
收起左侧

[学习记录] RC4-c语言实现

  [复制链接]
youzipii 发表于 2024-3-21 16:51
参考:网络安全实验——RC4的实现
[C] 纯文本查看 复制代码
#include<stdio.h>
//交换
void Swap(unsigned char *S1, unsigned char *S2) {
	unsigned char tmp = *S1;
	*S1 = *S2;
	*S2 = tmp; 
}

//获取字符串长度 
int my_strlen(unsigned char *S) {
	int i = 0;
	int res = 0;
	while(*S++ != '\0') {
		res += 1;
		i++;
	}
	return res;
} 

//初始化S盒
void init_S_Box(unsigned char *S, unsigned char *Key, int len) {
	int i = 0, j = 0;
	unsigned char T[256];
	unsigned char tmp = 0;
	for (i = 0; i < 256; i++) 
	{
		S[i] = i;			 // S_box升序置为0~255 
		T[i] = Key[i % len]; // T使用key来进行填充的
	}
	//初始置换 
	for (i = 0; i<256; i++)
	{
		j = (j + S[i] + T[i]) % 256; // 取一个s[i]+k[i]+j的索引
		//交换S[i]和S[j]
		Swap(&S[i], &S[j]);
	}
}

//加解密函数,因为是异或的方式所以加密也是解密 
void rc4(unsigned char *S, unsigned char *Str, int len) {
	//PRGA(伪随机数生成)
	int i = 0, j = 0, k = 0, t = 0;
	
	for (k = 0; k < len; k++) {
		i = (i + 1) % 256;
		j = (j + S[i]) % 256;
		Swap(&S[i], &S[j]);
		t = (S[i] + S[j]) % 256;
		Str[k] = Str[k] ^ S[t]; //以活加密 
	}
	
} 
 
int main(){
	//用无符号字符型,因为一个char的大小刚好是一字节,无符号是为了防止出现负数 
	unsigned char S_box[256] = {};
	unsigned char Key[256] = { "hello world"};
	unsigned char Str[512] = "hello world!";
	int len = my_strlen(Key);
	printf("*************原始信息*************\n"); 
	printf("message = %s\n", Str);
	printf("Key = %s \nKey_length = %d\n\n", Key, len);
	init_S_Box(S_box, Key, len);   //初始化 
	printf("*************加密*************\n"); 
	rc4(S_box, Str, len);//加密
	printf("cipher = %s\n\n", Str);
	printf("*************解密*************\n"); 
	init_S_Box(S_box, Key, len);   //初始化 
	rc4(S_box, Str, len);
	printf("message = %s\n\n", Str);

}

这个不懂是不是字节码的问题,有些编码一个汉字是三个字节,有的又是两个,数据编码的转换有点复杂,用C语言可能会好点

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

爱飞的猫 发表于 2024-3-22 08:49
本帖最后由 爱飞的猫 于 2024-3-22 08:51 编辑

这个不懂是不是字节码的问题,有些编码一个汉字是三个字节,有的又是两个,数据编码的转换有点复杂,用C语言可能会好点

因为编码后的汉字是多字符的。UTF-8 的话是可以通过第一个字节的高位来判断长度,一个字符可以用多个字节来表示。

int utf8_chr_len(char utf8_chr) {
    // 最高位未设置,单字节长度
    if ((utf8_chr & 0x80) == 0) {
        return 1;
    }

    // 之后数高位有多少个 1 直到遇到 0。目前的标准,单字符最高 6 字节,即二进制的 "1111110?"
    int result = {1};
    for(int shifts = 6; shifts >= 1; shifts--) {
        if (utf8_chr & (1 << shifts)) {
            result ++;
        } else {
            break;
        }
    }
    return result;
}

// 测试案例,输出 1 和 3
printf("utf8_chr_len: %d, %d\n", utf8_chr_len('A'), utf8_chr_len("中"[0]));

  • 可以引入 string.h,这样就能直接使用 strlen 函数了。
  • i (不包括初始化时) 和 j 的类型其实可以使用 uint8_t,超过 255 会自动回到 0。
  • 初始化 S 表的时候的 i 不可以,因为要表示 256
  • 从 C99 标准开始(即支持使用 // 注释的版本开始),不需要在函数头定义所有需要的变量了,可以在需要的地方直接插入。

另外 RC4 如果需要分段加解密,则需要记录 ij 的值。例如加解密一个大文件,可以分批次读取到缓冲区操作。

最后,如果真的有加解密的使用需求,建议使用成熟的、测试过的库,例如 openssl


修改后的代码:

#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

// 交换
void Swap(uint8_t *S1, uint8_t *S2) {
    uint8_t tmp = *S1;
    *S1 = *S2;
    *S2 = tmp;
}

struct rc4_ctx_t {
    uint8_t i;
    uint8_t j;
    uint8_t S[256];
};

// 初始化S盒
void rc4_init(struct rc4_ctx_t *ctx, const uint8_t *key, int key_len) {
    ctx->i = 0;
    ctx->j = 0;
    for (int i = 0; i < 256; i++) {
        ctx->S[i] = i;
    }

    uint8_t j = {0};
    for (int i = 0; i < 256; i++) {
        j = j + ctx->S[i] + key[i % key_len];
        Swap(&ctx->S[i], &ctx->S[j]);
    }
}

// RC4 随机数生成与加解密
void rc4_cipher(struct rc4_ctx_t *ctx, uint8_t *Str, int len) {
    for (int k = 0; k < len; k++) {
        ctx->i++;
        ctx->j += ctx->S[ctx->i];

        Swap(&ctx->S[ctx->i], &ctx->S[ctx->j]);

        // key 就是本轮生成的字节。
        uint8_t key = ctx->S[ctx->i] + ctx->S[ctx->j];

        // 异或处理
        Str[k] ^= key;
    }
}

// 摘抄自 https://stackoverflow.com/a/29865
void hexdump(void *ptr, int buflen) {
    unsigned char *buf = (unsigned char *) ptr;
    int i, j;
    for (i = 0; i < buflen; i += 16) {
        printf("%06x: ", i);
        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%02x ", buf[i + j]);
            else
                printf("   ");
        printf(" ");
        for (j = 0; j < 16; j++)
            if (i + j < buflen)
                printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
        printf("\n");
    }
}

int main() {
    struct rc4_ctx_t rc4 = {};
    const char* key = "hello world";
    char message[512] = {"hello world!"};

    printf("*************原始信息*************\n");
    printf("message = %s\n", message);
    printf("Key = %s \n\n", key);

    printf("*************加密*************\n");
    rc4_init(&rc4, (const uint8_t*)key, strlen(key));
    rc4_cipher(&rc4, message, sizeof(message));
    hexdump(message, sizeof(message));
    printf("*************解密*************\n");
    rc4_init(&rc4, (const uint8_t*)key, strlen(key));
    rc4_cipher(&rc4, message, sizeof(message));
    hexdump(message, sizeof(message));
    printf("*************结束*************\n");
}

上文中的 hexdump 方法渲染效果如下:



 楼主| youzipii 发表于 2024-3-23 22:51
爱飞的猫 发表于 2024-3-22 08:49
[md]> 这个不懂是不是字节码的问题,有些编码一个汉字是三个字节,有的又是两个,数据编码的转换有点复杂, ...

感谢指点
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-16 00:10

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表