网络协议分析中常见编码的原理及其解码函数
(基于C/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】原创,转载请注明出处!------