admin 管理员组

文章数量: 887007

base64编码解码中的问题及思考

base64中遇到的问题及解决方案
第一段代码是我的复制,来源于

[/

#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MAX 100const char *base64payload = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";int find_pos(char c)
{for(int i = 0; i < 65; i++)if(c == base64payload[i])return i;return -1;
}unsigned char *base64_encode(const char *s, const int len)
{unsigned int sign = len % 3;unsigned int res_len = len % 3 ? ((len) / 3 + 1) * 4 : (len) / 3 * 4;unsigned int i = 0, j = 0;unsigned char *res = (unsigned char *)malloc(res_len + 1);memset(res, 0, res_len + 1);for(i = 0, j = 0; i < len; i += 3, j += 4){if(i + 2 >= len){res[j] = (s[i] >> 2) & 0x3F;if(sign == 1)//因为只多出一个数据,故需要补两个'='{res[j + 1] = ((s[i] & 0x03) << 4) & 0x3F;res[j + 2] = 0x40;//0x40对应数字64,即为base64payload中下标为64的'='res[j + 3] = 0x40;}else if(sign == 2)//因为多出两个数据,故需要补一个'='{res[j + 1] = (((s[i] & 0x03) << 4) | ((s[i + 1] >> 4) & 0x0F));res[j + 2] = ((s[i + 1] & 0x0F) << 2) & 0x3F;res[j + 3] = 0x40;break;}}res[j] = (s[i] >> 2) & 0x3F;//取第一个字节的前六位,最高两位补零res[j + 1] = (((s[i] & 0x03) << 4) | ((s[i + 1] >> 4) & 0x0F));//取第一个字节的最后两位和第二个字节的前四位,最高两位补零res[j + 2] = (((s[i + 1] & 0x0F) << 2) | ((s[i + 2] >> 6) & 0x03));//取第二个字节的后四位和第三个字节的前两位,最高两位补零res[j + 3] = (s[i + 2] & 0x3F);//取第三个字节的最后六位,最高两位补零}for(j = 0; j < res_len; j++)res[j] = base64payload[res[j]];return res;
}unsigned char *base64_decode(const char *s, const int len)
{unsigned int res_len = len / 4 * 3;unsigned int i = 0, j = 0;unsigned char *res = (unsigned char *)malloc(res_len + 1);memset(res, 0, res_len + 1);int count = len / 4;for(i = 0; i < count; i++){int s_index = i * 4;int res_index = i * 3;int buffer[4];//四个字节一组存入int sign = 0;for(j = 0; j < 4; j++)buffer[j] = find_pos(s[s_index + j]);if(i == count - 1)for(j = 0; j < 4; j++)if(buffer[j] == 0x40)//如果为'=',sign的值加一sign++;res[res_index] = ((buffer[0] & 0x3F) << 2 | (buffer[1] & 0x30) >> 4);if(sign == 2)break;res[res_index + 1] = ((buffer[1] & 0x0f) << 4 | (buffer[2] & 0x3C) >> 2);if(sign == 1)break;res[res_index + 2] = ((buffer[2] & 0x03) << 6 | (buffer[3] & 0x3F));}return res;
}int main()
{unsigned char s1[MAX];scanf("%s", s1);unsigned char *s2 = base64_encode((const char *)s1, strlen((const char *)s1));printf("%s\n", s2);unsigned char *s3 = base64_decode((const char *)s2, strlen((const char *)s2));printf("%s\n", s3);free(s2);free(s3);return 0;
}
  1. 条件运算符: ? 带三个运算对象的运算符称为三元运算符, :条件运算符是C中唯一的三元运算符。通用形式:expression?expression2:expression3解释:如果expression1为真(非0),那么整个条件表达式的值与expression2的值相同;如果expression1为假(0),那么整个条件表达式的值与expression3 相同。int是有符号类型,占用4byte,32bit,int a=1,a的二进制需要整理的问题:
    1. 位操作(unsigned,int)
    2. malloc的使用
    3. C函数
    4. const限定符,指针

先来一波感想吧,这个base64谁玩谁知道,在我做这个的时候一般都是这样的状态:woc!我做出来了,我加密成功了!??woc,为什么只能加密3个,多了就加载不了?为什么加载的程序在输入字符超过11个后就会崩溃?…等等一些列的bug让我的心情经常处于高峰和低谷的切换状态,当时真的爽成弟弟了。依稀记得跟天语姐姐装b的时候,以为自己真的完工了,果然到了下午输入了超过11个英文字符的时候我发现居然崩溃了!我的天?于是硬着头皮,凭着我强大的心理素质硬是继续debug,不过效果也很明显,没有成功就对了:) 等我吐槽到这里的时候,当年崔万志说的话仿佛来到了我的耳边:抱怨没有用,一切靠自己。(但是吐槽出来感觉好多了啊哈哈哈哈) 好的,我闭嘴好好写总结。首先,我先把自己借鉴和原创的代码部分粘贴上 ?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 100const char *base64words="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";//base64的编码运算
unsigned char *base64_encode(const char *source,const int len)
{unsigned int more_word=len%3;//判断是否为3的倍数字符unsigned int base64_len;//base64_len为编码后的字符长度if(more_word==0)//通过多余字节base64_len=len/3*4;elsebase64_len=(len/3+1)*4;unsigned int i=0,j=0;//开辟以长度为base64_len+1长度的字符空间,并把首地址赋给base64_codeunsigned char *base64_code=(unsigned char *)malloc(base64_len+1);memset(base64_code,0,base64_len+1);//初始化开辟的空间for(i=0,j=0;i<len;i+=3,j+=4)//每次源代码前进3个字符,base64_code前进4个字符{if(i+2>=len){base64_code[j]=(source[i]>>2)&0x3f;//开辟后的第一个字符不受影响if(more_word==1){	//question1 并且这里也可以用另一种方式改写;base64_code[j+1]=((source[i]&0x3)<<4)&0x30;base64_code[j+2]=0x40;base64_code[j+3]=0x40;}if(more_word==2){base64_code[j+1]=(((source[i]&0x3)<<4)|((source[i+1]>>4)&0x3f));base64_code[j+2]=((source[i+1]&0x3f)<<2)&0x3c;base64_code[j+3]=0x40;}}else{base64_code[j]=(source[i]>>2)&0x3f;base64_code[j+1]=(((source[i]&0x3)<<4)|((source[i+1]>>4)&0xf));base64_code[j+2]=(((source[i+1]&0xf)<<2)|((source[i+2]>>6)&0x3));base64_code[j+3]=(source[i+2])&0x3f;}}for(j=0;j<base64_len;j++)base64_code[j]=base64words[base64_code[j]];//perfect!!//base64_code[]中存储的是不是字符吗question2return base64_code;
}//base64的解码运算unsigned char *base64_decode(int *base64_code_num,int base64_len)
{int len=base64_len/4*3;int coun=base64_len/4;unsigned char *source=(unsigned char *)malloc(len+1);memset(source,0,len+1);unsigned int i=0,j=0,k;//unsigned int *base64_code_num=get_num(base64_code,base64_len);for(i=0,j=0,k=0;k<coun;k++,j+=4,i+=3){if(base64_code_num[j+2]==64){source[i]=(((base64_code_num[j]&0x3f)<<2)|((base64_code_num[j+1]>>4)&0x3));}else if(base64_code_num[j+3]==64){source[i]=(((base64_code_num[j]&0x3f)<<2)|((base64_code_num[j+1]>>4)&0x3));source[i+1]=(((base64_code_num[j+1]&0xf)<<4)|((base64_code_num[j+2]>>2)&0xf));}else{source[i]=(((base64_code_num[j]&0x3f)<<2)|((base64_code_num[j+1]>>4)&0x3));source[i+1]=(((base64_code_num[j+1]&0xf)<<4)|((base64_code_num[j+2]>>2)&0xf));source[i+2]=(((base64_code_num[j+2]&0x3)<<6)|(base64_code_num[j+3]&0x3f));}}//free(base64_code_num);return source;
}int main()
{while(1){unsigned int num[MAX*2];int j;unsigned char source[MAX];scanf("%s",source);unsigned char *base64_code=base64_encode((const char *)source,strlen((const char *)source));printf("%s\n",base64_code);for(int i=0;i<strlen(base64_code);i++){for(j=0;j<65;j++){if(base64_code[i]==base64words[j]){num[i]=j;break;}}}unsigned char *source1=base64_decode(num,strlen(base64_code));//此处strlen不能适用于num数组,wtfprintf("%s\n",source1);free(source1);free(num);}return 0;
}

想要看懂,可能需要上文提到的需要解决的问题的答案。(好绕是吗,那我就把上文提到的问题复制下来)需要整理的问题:The most important question:base64编码解码的原理(这个必须弄明白,因为下面四个问题是解决编码解码的工具知识,是工具而已,并不是实现目的)其他问题:
1. 位操作(unsigned,int)
2. malloc的使用
3. C函数
4. const限定符,指针

我们一个个来:首先从最重要的问题说起,base64编码解码的过程:先放个小程序

#include <stdio.h>
#include <stdlib.h>int main()
{printf("The size of char is %d",sizeof(char));return 0;
}

这是预先你应该知道的char占位数,一个字符占一个字节的位置,一字节即1byte=8bit,bit就是二进制位数,也就是一个char类型的字符占8个bit位,计算机存储数据的时候便是将char类型字符以8bit位数保存,即ASCII编码方式。上面程序编译后,会出现:

The size of char is 1
那么base64是什么呢,简单来说呢, 它是用64个可打印字符表示二进制所有数据方法。具体适用场景不再赘述,这里只谈原理。
先放张图看一下base64编码过程是什么样子的:

转换的时候,将三个byte的数据(也可以不够三个,上图就是两个字符),先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲区中剩下的bit用0补足。然后,每次取出6个bit,按照其值选择(最后的结果就是3、2、1个字符对应4个base64编码字符)
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。
如果最后剩下两个输入数据,在编码结果后加1个“=”;如果最后剩下一个输入数据,编码结果后加2个“=”;如果没有剩下任何数据,就什么都不要加。base64编码过程就这么简单。(网络摘抄 .html)
上面的编码过程中位数不够三位,所以第三个base64码‘I’的最后两位就需要补2位0,然后利用索引找到相应的字符‘I’。索引(编号)和base64的对应关系如下:

编码过程明白了,解码过程也比较容易了。首先将编码后的字符以4个为一组进行解码,解码过程就是编码过程的逆,最后一组解码的时候如果有=,则证明源字符不是3的倍数,所以分为有一个‘=’则缺一个字符,就是最后一组位两个字符,两个‘=’则缺两个字符,也就是说最后一组只有一个字符。进行讨论解决就ok。
那么以上就是base64编码解码的过程,即讲明原理,以下将是我们在编程过程中会用到的知识。
1.位操作(unsigned,int)

2.malloc的使用

3.C函数

4.const限定符,指针

1:位操作
既然要取8位二进制编码中的6位,就需要用到位操作,来获取并保存6位二进制数据。

那么既然有现成的博客,我就挂一篇一位大佬写的 ;
在这里着重区别有无符号类型的左右移动出现的问题。
2:malloc的使用
malloc()函数接受一个参数:所需要的内存字节数。malloc函数会找到合适的空闲内存块,这样的内存是匿名的。也就是说,malloc()分配内存,但是不会为其赋名。然而,它确实返回动态分配内存块的首字节地址。因此,可以把该地址赋给一个指针变量,并使用指针访问这块内存。从ANSI C标准开始,C使用一个新的malloc()函数返回值类型:指向void的指针。该类型相当于一个“通用指针”。malloc()函数可用于返回指向数组的指针、指向结构的指针等,所以通常该函数的返回值会被强制转化成匹配的类型。在ANSI C中,应该坚持使用强制类型转换,提高代码的可读性。下面是一个实例:

double * ptr;
ptr=(double ) malloc(30sizeof(double));
以上代码为30个double类型的值请求内存空间,并设置ptr指向该位置。注意,ptr被声明为指向一个double类型,而不是指向内含30个double类型值的块。回忆一下数组的知识,数组名是该数组首元素的地址。因此,如果让ptr指向这个块的首元素,便可像使用数组名一样使用它。所以可使用数组名表示指针,也可以用指针来表示数组。
malloc()使用完毕了,那么与其配套的free()就得用上。free()函数的参数是之前malloc()返回的地址,该函数释放之前malloc()分配的内存。因此动态分配内存的存储期从调用malloc()分配内存到调用free()释放内存为止。设想malloc()和free()管理着一个内存池,每次调用malloc()分配内存给程序使用,每次调用free()把内存归还内存池中,这样便可重复使用这些内存。free()的参数应该是一个指针,指向由malloc()分配的一块内存。不能用free()释放通过其他方式(如,声明一个数组)分配的内存。malloc()和free()的原型都在stdlib.h头文件中。
3:C函数
等我啥时候有心情再来吧,因为涉及的指针问题解决不了,所以我就没用第一个代码里的int find_pos(char c)函数。因为我想把所有的大型计算过程(for循环)放到函数中,这样main()函数可能会清爽一点,但是效果很明显,我记得当时老是出现输入两三个字符就崩溃的情况。以后有机会和时间再来讨论这个问题吧,因为现在我只想好好做做ctf中的逆向。第九章和第十章的Cprimerplus还是得时常更新阅读。:)果然第一遍读完啥印象也没了。

4:const限定符,指针
复习后再正儿八经整理,C primer plus
最后
kgnb!?

本文标签: base64编码解码中的问题及思考