网络协议分析中常见编码的原理及其解码
(基于C++语言实现)
本文由CSDN-蚍蜉撼青松 【主页:http://blog.csdn.net/howeverpf】原创,转载请注明出处!
一、Base64解码
1.1 简介与编码原理
在MIME格式的电子邮件中,base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定base64。使用的字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。完整的base64定义可见RFC 1421和RFC 2045。
----------------------------本段引自*:http://zh.wikipedia.org/wiki/Base64
Base64编码的过程是把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后对于每个6Bit,在其高位再添两位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。由此可以估算编码后数据长度大约为原长的135.1%。
Base64编码的规则:
- 对输入的原始字节序列,按每3个8bit的字节分为一组,每组共24bit;【三三分组】
- 针对每组分别处理,将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节;【四变三】
- 将这4个8bit的字节分别转换成10进制,对照Base64编码表 (见图1-1),得到对应编码后的可打印字符;【查表】
- 每76个字符加一个换行符;【换行处理】
- 当输入的原始字节序列长度不是3的整数倍时,需在输入字节序列最后用零补齐。在这种情况下,编码后结果必然不足四个字节,需在编码结果最后用‘=‘补齐;这就保证编码后字节序列的长度一定是4的倍数。【结束处理】
图1-1 Base64编码表(截自*)
1.2 Base64解码函数
/***********************************************************/ /* 输入参数说明: */ /* src为指向存放输入内容的缓冲区指针 */ /* 输出参数说明: */ /* 返回值为指向存放输出内容的缓冲区指针 */ /***********************************************************/ char * Base64_Decode(char *src) { int n,i,j; int equal_count=0;//等号的个数 char *p=NULL; char *dst=NULL; char ch64[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; n=strlen(src); if (n%4!=0)//输入不对 返回空指针 { debug(stderr,"Base64_Decode input error"); return dst; } if (*(src + n - 1) == ‘=‘) { equal_count += 1; } if (*(src + n - 2) == ‘=‘) { equal_count += 1; } for (i=0;i<n;i++) { p=strchr(ch64,src[i]);//p指向src[i]在ch64中的位置 if (!p) //没找到,为"=",不转换 break; src[i]=p-ch64;//将src[i]转换为0-63的数字 } dst=new char[n*3/4+1]; memset(dst,0,n*3/4+1); for (i=0,j=0;i<n-4;i+=4,j+=3) { dst[j]=(src[i]<<2) | ((src[i+1]&0x30)>>4); dst[j+1]=((src[i+1]&0x0F)<<4) | ((src[i+2]&0x3C)>>2); dst[j+2]=((src[i+2]&0x03)<<6) | src[i+3]; } switch (equal_count)//最后四个字符单独处理 { case 0: dst[j]=(src[n-4]<<2) | ((src[n-3]&0x30)>>4); dst[j+1]=((src[n-3]&0x0F)<<4) | ((src[n-2]&0x3C)>>2); dst[j+2]=((src[n-2]&0x03)<<6) | src[n-1]; break; case 1: dst[j]=(src[n-4]<<2) | ((src[n-3]&0x30)>>4); dst[j+1]=((src[n-3]&0x0F)<<4) | ((src[n-2]&0x3C)>>2); break; case 2: dst[j]=(src[n-4]<<2) | ((src[n-3]&0x30)>>4); break; default: break; } return dst; }
二、Quoted-Printable解码
2.1 简介与编解码原理
Quoted-Printable编码,通常缩写为"Q"编码,其原理是把一个8bit的字符用两个16进制数值表示,然后在前面加“=”。所以我们看到经过QP编码后的文件通常是这个样子:=B3=C2=BF=A1=C7=E5=A3= AC=C4=FA=BA=C3=A3=A1。同Base64一样,Quoted-Printable也是MIME邮件中常用的编码方式之一,一般用在电子邮件的标题。
Quoted-Printable的编码规则:
- QP编码根据输入字符的数值范围进行编码:输入字符在33-60、62-126范围内的,不需编码直接输出;否则先输出‘=‘,后面跟随两个十六进制数字【0–9 或 A–F (必须使用大写)】表示输入字符的十六进制字节值;
- ASCII的水平制表符(tab)与空格符, 十进制为9和32,如果不出现在行尾则可以用其ASCII字符编码直接表示;如果这两个字符出现在行尾,必须QP编码表示为"=09" (tab)或"=20" (space);
- 如果数据中包含有意义的行结束标志,必须转换为ASCII回车(CR)换行(LF)序列,既不能用原来的ASCII字符也不能用QP编码的"="转义字符序列。 相反,如果字节值13与10有其它的不是行结束的含义,它们必须QP编码为=0D与=0A;
- QP编码后的数据要求每行长度不能超过76个字符。为满足此要求又不改变被编码文本,在QP编码结果的每行末尾加上软换行(soft line break). 即在每行末尾加上一个"=\r\n"序列, 但并不会出现在解码得到的文本中。
Quoted-Printable的解码规则:
- 碰到 ‘=‘ 并且后面2个字符均处于0-9 或 A-F 中时,为编码字符序列,否则为未编码字符;【判定是否为编码后字符】
- 对于未编码字符,直接输出;
- 对于编码字符序列,要将‘=‘后的两个字符转为十六进制数值,然后求出该十六进制数值对应的十进制值,将得到的十进制值转为对应的编码前字节。
本小节部分内容参考:
《Quote Printable编码原理(Delphi)》http://blog.csdn.net/fengsh998/article/details/8663046
《QP(Quote-Printable) 编码》http://love-love-l.blog.163.com/blog/static/2107830420113593541737/
2.2 Quoted-Printable解码函数:
/***********************************************************/ /* 输入参数说明: */ /* pSrc为指向存放输入内容的缓冲区指针 */ /* pDst为指向存放输出内容的缓冲区指针 */ /* nSrcLen为输入缓冲区的大小 */ /* 输出参数说明: */ /* 返回值为输出内容的大小 */ /***********************************************************/ int Quoted_Decode(char* pszSrc, char* pszDst, int nSrclen) { int i=0; int nDstLen; // 输出的字符计数 while (i< nSrclen) { if (strncmp(pszSrc, "=\r\n", 3) == 0) // 软回车,跳过 { pszSrc += 3; i+= 3; } else { if (*pszSrc == ‘=‘) // 编码字节 { sscanf(pszSrc, "=%02X", (unsigned int*)pszDst); pszDst++; pszSrc += 3; i+= 3; } else // 非编码字节 { *pszDst++ = *pszSrc++; i++; } nDstLen++; } } *pszDst = ‘\0‘; // 输出加个结束符 return nDstLen; }
三、URL编码解码
3.1 简介与编解码原理
这种编码方式由于最初用于使URL符合RFC 1738规范【RFC 1738中规定: “只有字母和数字[0-9,a-z,A-Z]、一些特殊符号“$-_.+!*‘(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL。”】而被称为URL编码,应该算是HTTP请求报文中最常见的编码了。
URL编码的编码规则:
- 当输入字节为字母和数字[0-9,a-z,A-Z]、一些特殊符号“$-_.+!*‘(),”【不包括双引号】、以及某些保留字时,不需编码直接输出;
- 否则先输出‘%‘,后面跟随两个十六进制数字【0–9 或 A–F (大小写皆可) 】表示输入字节的十六进制字节值;
URL编码的解码规则:
- 碰到 ‘%‘ 并且后面2个字符均处于0-9 或 A-F(大小写皆可)中时,为编码字符序列,否则为未编码字符;【判定是否为编码后字符】
- 对于未编码字符,直接输出;
- 对于编码字符序列,要将‘%‘后的两个字符转为十六进制数值,然后求出该十六进制数值对应的十进制值,将得到的十进制值转为对应的编码前字节。
3.2 URL解码函数
/***********************************************************/ /* 输入参数说明: */ /* pSrc为指向存放输入内容的缓冲区指针 */ /* pDst为指向存放输出内容的缓冲区指针 */ /* nSrcLen为输入缓冲区的大小 */ /* 输出参数说明: */ /* 返回值为输出内容的大小 */ /***********************************************************/ int Url_Decode(const char *pSrc, char *pDst, int nSrcLen) { int nDstLen; // 输出的字符计数 int i=0; while(i < nSrcLen) { if(*pSrc == ‘%‘) // 是编码字节 { sscanf(pSrc, "%%%02x", (unsigned short *)pDst); pDst++; pSrc += 3; i += 3; } else // 非编码字节 { *pDst++ = (unsigned char )*pSrc++; i++; } nDstLen++; } *pDst = ‘\0‘; // 输出加个结束符 return nDstLen; }
------本文由CSDN-蚍蜉撼青松【主页:http://blog.csdn.net/howeverpf】原创,转载请注明出处!------