本文由CSDN-蚍蜉撼青松 【主页:http://blog.csdn.net/howeverpf】原创,转载请注明出处!
在分析网络数据包或者研究安全问题时,经常会遇到变换后的字符序列。而能否准确识别密文的变换算法,对进一步的分析工作很关键。常用的变换算法包括但不限于:Base64、URL编码、HTML编码、MD5散列、DES加密、RSA加密等。要求:
1)在理解算法原理及密文特点的基础上,使用任意编程语言实现,当输入一段密文时,返回可能的变换算法。
2)能准确识别5种以上变换或加密算法。
首先分析这七种变换算法的特点,分别提出判别依据与函数实现:
URL编码判定函数实现如下:
// 判定是否存在URL编码 //参数 pszBuff, 待判定字符序列 //返回 若一切正常,返回 true bool IsEncodedbyURL(char *pszBuff) { int i,j; // 百分号 数量上是否正常 if (g_nCountPC == ARRAY_MAX_SIZE) DbgMsgPrint("Please make the buffer size bigger for '%'."); else if (g_nCountPC == 0) return true; // 百分号 数量正常的话, 遍历待判定字符序列中所有的百分号【已由预处理的时候取得】 //若每个百分号后紧跟的两个字符不都是十六进制值,则不存在URL编码 for (i=0; i<g_nCountPC; i++) { j = g_arrnPosPC[i]; if ( !(IsHex(pszBuff+j+1) && IsHex(pszBuff+j+2)) ) { // 只要有一个百分号的字符不是十六进制值,都判定为不存在URL编码 DbgMsgPrint("the char after % isn't a hex!"); g_nIsEncodedbyURL = IDENTIFIY_RES_FALSE; return true; } } // 所有的百分号后面紧跟的都为2个十六进制数,才能基本肯定使用了URL编码 g_nIsEncodedbyURL = IDENTIFIY_RES_CERTAIN; return true; }
QP编码判定函数实现如下:
// 判定是否存在QP编码 //参数 pszBuff, 待判定字符序列 //返回 一切正常,返回 true bool IsEncodedbyQP(char *pszBuff) { int i,j; // 等于号 数量上是否正常 if (g_nCountEQ == ARRAY_MAX_SIZE) DbgMsgPrint("Please make the buffer size bigger for '='."); else if (g_nCountEQ == 0) return true; // 等于号 数量正常的话, 遍历待判定字符序列中所有的等于号【已由预处理的时候取得】 for (i=0; i<g_nCountEQ; i++) { j = g_arrnPosEQ[i]; // 对于QP编码,等于号后面的两个字符要么是十六进制值(字母的话需大写),要么是 \r\n if ( RegnizeChar(pszBuff+j+1)<=0 || RegnizeChar(pszBuff+j+2)<=0) { if( *(pszBuff+j+1)!='\r' || *(pszBuff+j+2)!='\n' ) { // 只要有一个等于号后面的字符不符合,就不是QP编码 g_nIsEncodedbyQP = IDENTIFIY_RES_FALSE; return true; } } } // 所有的等于号后面紧跟的字符都符合,才能说可能使用了QP编码 g_nIsEncodedbyQP = IDENTIFIY_RES_MAYBE; for (i=0; i<g_nconutcr-1 i="" qp="" r="" n="" 76="" if="" g_arrnposcr="" 1="" -=""> LINE_MAX_LENGTH+1) return true; } // 行长度都小于限定值,则能基本肯定使用了QP编码 g_nIsEncodedbyQP = IDENTIFIY_RES_CERTAIN; return true; }
Unicode编码判定函数实现如下:
// 判定是否存在Unicode编码 //参数 pszBuff, 待判定字符序列 //返回 一切正常,返回 true bool IsEncodedbyUnicode(char *pszBuff) { int i,j; // 反斜杆 数量上是否正常 if (g_nCountBS == ARRAY_MAX_SIZE) DbgMsgPrint("Please make the buffer size bigger for '\'."); else if (g_nCountBS == 0) return true; // 反斜杆 数量正常的话, 遍历待判定字符序列中所有的反斜杆【已由预处理的时候取得】 for (i=0; i<g_nCountBS; i++) { j = g_arrnPosBS[i]; if (*(pszBuff+j+1)=='u') // 反斜杆后面紧跟的字符是否为’u’ { // ”\u”后紧跟的四个字符是否为十六进制数 if ( IsHex(pszBuff+j+2) && IsHex(pszBuff+j+3) && IsHex(pszBuff+j+4) && IsHex(pszBuff+j+5) ) { // 只要有一个符合条件的,就可以认为一定存在Unicode编码 g_nIsEncodedbyUnicode = IDENTIFIY_RES_CERTAIN; return true; } } } // 所有的反斜杆后面的字符都不符合条件,才确认不存在Unicode编码 g_nIsEncodedbyUnicode = IDENTIFIY_RES_FALSE; return true; }
HTML特殊实体编码判定函数实现如下:
// 判定是否存在HTML特殊字符编码 //参数 pszBuff, 待判定字符序列 //返回 一切正常,返回 true bool IsEncodedbyHtml(char *pszBuff) { int i,j,nTemp; bool bFindSC; // 取址符与分号 数量上是否正常 if (g_nCountAMP == ARRAY_MAX_SIZE) DbgMsgPrint("Please make the buffer size bigger for '&'."); if (g_nCountSC == ARRAY_MAX_SIZE) DbgMsgPrint("Please make the buffer size bigger for ';'."); if (g_nCountAMP==0 || g_nCountSC==0) return true; nTemp = 0; for (i=0; i<g_nCountAMP; i++) { bFindSC = false; for (j=nTemp; j<g_nCountSC; j++) { if ( g_arrnPosSC[j]>g_arrnPosAMP[i] ) { bFindSC = true; break; } } if (!bFindSC) continue; nTemp = j; if (*(pszBuff+g_arrnPosAMP[i]+1) == '#') { for (j=g_arrnPosAMP[i]+2; j<g_arrnPosSC[nTemp]; j++) { if (RegnizeChar(pszBuff+j) != 2) { g_nIsEncodedbyHtml = IDENTIFIY_RES_FALSE; return true; } } } else { for (j=g_arrnPosAMP[i]+1; j<g_arrnPosSC[nTemp]; j++) { if ( *(pszBuff+j)>='A' && *(pszBuff+j)<='Z') continue; else if ( *(pszBuff+j)>='a' && *(pszBuff+j)<='z') continue; g_nIsEncodedbyHtml = IDENTIFIY_RES_FALSE; return true; } } g_nIsEncodedbyHtml = IDENTIFIY_RES_CERTAIN; break; } return true; }
Base64编码判定【兼带判定是否为 MD5/SHA1 散列】的函数实现如下:
// 判定是否为Base64编码 // 顺带判定是否为 MD5/SHA1 散列 //参数 pszBuff, 待判定字符序列 //返回 一切正常,返回 true bool IsEncodedbyBase64(void) { int nRealLength,i; // 先检查预处理的时候有没有遇到非法字符【对于Base64编码】 if ( g_nIsEncodedbyBase64 == IDENTIFIY_RES_FALSE ) { return true; } else if (g_nCountEQ > 2) { g_nIsEncodedbyBase64 = IDENTIFIY_RES_FALSE; return true; } // 当数据源为文件,且完整的数据都已被读进来来时,检查长度是否为4的倍数 if ( (g_pszEncodedFilePath != NULL) && (g_nEncodedCotentLength >= BUFFER_MAX_SIZE) ) { DbgMsgPrint("The buffer is too small for the whole file, can't regnize the Base64/MD5/SHA1!"); return true; } nRealLength = g_nEncodedCotentLength - g_nConutCR - g_nCountLF; // 获取字符序列的实际长度(除去回车、换行) if ( nRealLength%4 != 0 ) { // 基于Base64、MD5、SHA1的输出字符序列的特点,真实长度不为4的倍数的话,不可能是Base64/MD5/SHA1 g_nIsEncodedbyBase64 = IDENTIFIY_RES_FALSE; g_nIsEncodedbyMD5 = IDENTIFIY_RES_FALSE; g_nIsEncodedbySHA1 = IDENTIFIY_RES_FALSE; return true; } // 经Base64编码的字符序列,其最后一个或最后两个字符可能是等号; 而MD5、SHA1的输出字符序列中不含等号 if ( g_nCountEQ > 0) { for (i=0; i<g_nCountEQ; i++) { // 等号是否在最后一个或两个 if (g_arrnPosEQ[i] < g_nEncodedCotentLength-2) { g_nIsEncodedbyBase64 = IDENTIFIY_RES_FALSE; return true; } } g_nIsEncodedbyBase64 = IDENTIFIY_RES_MAYBE; } else { g_nIsEncodedbyBase64 = IDENTIFIY_RES_MAYBE; // MD5散列输出的长度一般是128bits,一般使用32个十六进制数表示 // SHA1散列输出的长度一般是160bits,一般使用40个十六进制数表示 // 可通过长度对二者进行简单的区分 if (nRealLength == 32) { g_nIsEncodedbyMD5 = IDENTIFIY_RES_MAYBE; g_nIsEncodedbySHA1 = IDENTIFIY_RES_FALSE; return true; } else if (nRealLength == 40) { g_nIsEncodedbySHA1 = IDENTIFIY_RES_MAYBE; g_nIsEncodedbyMD5 = IDENTIFIY_RES_FALSE; return true; } } return true; }
前面提到的七种变换算法的识别方法中,提到了两个名词,存在型判定、是非型判定。对于存在型判定,我们判定的是输入中是否存在某种变换算法;而对于是非型判定,我们判定的是整个输入是否整体使用了某种变换算法。由于这种区别,我们在这实现是非型判定的时候,就要求输入必须是完整的。(我在程序中用读入的长度是否小于输入缓存size来简单地判定)。
在前文提到的七种变换算法的识别方法中,很多都需要对一些特殊字符进行定位,例如:百分号’%’(URL编码),等于号’=’(QP编码、Base64编码),反斜线’\’(Unicode编码),等等。而这些特殊字符在原始输入字符序列中的位置和出现次数,都由一个预处理函数完成【前文的几个判定函数实现中,也有提到】,其实现如下:
// 对待判定字符序列的预处理 //参数 pszBuff, 待判定字符序列 //参数 nBuffSize, 待判定字符序列的长度 //返回 若一切正常,返回 true bool PreProcess(char *pszBuff, int nBuffSize) { int i; for (i=0; i<nBuffSize; i++) { if (IsHex(pszBuff+i)) continue; else { // 出现非十六进制值,则必然不可能是 MD5/SHA1 散列编码 g_nIsEncodedbyMD5 = IDENTIFIY_RES_FALSE; g_nIsEncodedbySHA1 = IDENTIFIY_RES_FALSE; } if ((*(pszBuff+i)>='G' && *(pszBuff+i)<='Z') || (*(pszBuff+i)>='g' && *(pszBuff+i)<='z') || *(pszBuff+i)=='+' || *(pszBuff+i)=='-') { continue; } else if (*(pszBuff+i)=='\r') { if (g_nConutCR < ARRAY_MAX_SIZE) g_arrnPosCR[g_nConutCR++] = i; // 记录回车符 \r 出现的位置与次数 continue; } else if (*(pszBuff+i)=='\n') { if (g_nCountLF < ARRAY_MAX_SIZE) g_arrnPosLF[g_nCountLF++] = i; // 记录换行符 \n 出现的位置与次数 continue; } else if (*(pszBuff+i)=='=') { if (g_nCountEQ < ARRAY_MAX_SIZE) g_arrnPosEQ[g_nCountEQ++] = i; // 记录等于号 = 出现的位置与次数 } else { // 超出了 BASE64 编码结果集的范围 g_nIsEncodedbyBase64 = IDENTIFIY_RES_FALSE; } if (*(pszBuff+i)=='\\') { if (g_nCountBS < ARRAY_MAX_SIZE) g_arrnPosBS[g_nCountBS++] = i; // 记录反斜杆 \\ 出现的位置与次数 } else if (*(pszBuff+i)=='%') { if (g_nCountPC < ARRAY_MAX_SIZE) g_arrnPosPC[g_nCountPC++] = i; // 记录百分号 % 出现的位置与次数 } else if (*(pszBuff+i)=='&') { if (g_nCountAMP < ARRAY_MAX_SIZE) g_arrnPosAMP[g_nCountAMP++] = i; // 记录取址符 & 出现的位置与次数 } else if (*(pszBuff+i)==';') { if (g_nCountSC < ARRAY_MAX_SIZE) g_arrnPosSC[g_nCountSC++] = i; // 记录分号 ; 出现的位置与次数 } } return true; }
main.cpp 主程序文件内容
#include "base.h" char *g_pszExecPath = NULL; int g_nWorkMode = 0; char *g_pszEncodedFilePath = NULL; char *g_pszEncodedCotent = NULL; int g_nEncodedCotentLength = 0; int g_arrnPosCR[ARRAY_MAX_SIZE]; //回车符 \r 0D 13 位置记录 int g_arrnPosLF[ARRAY_MAX_SIZE]; //换行符 \n 0A 10 位置记录 int g_arrnPosBS[ARRAY_MAX_SIZE]; //反斜线 \\ 5C 92 位置记录 int g_arrnPosEQ[ARRAY_MAX_SIZE]; //等于号 = 3D 61 位置记录 int g_arrnPosPC[ARRAY_MAX_SIZE]; //百分号 % 25 37 位置记录 int g_arrnPosAMP[ARRAY_MAX_SIZE];//取地址符& 26 38 位置记录 int g_arrnPosSC[ARRAY_MAX_SIZE]; //分号 ; 3b 59 位置记录 int g_nConutCR = 0; int g_nCountLF = 0; int g_nCountBS = 0; int g_nCountEQ = 0; int g_nCountPC = 0; int g_nCountAMP= 0; int g_nCountSC = 0; // 识别结果 int g_nIsEncodedbyURL = IDENTIFIY_RES_UNKNOW; int g_nIsEncodedbyQP = IDENTIFIY_RES_UNKNOW; int g_nIsEncodedbyUnicode = IDENTIFIY_RES_UNKNOW; int g_nIsEncodedbyHtml = IDENTIFIY_RES_UNKNOW; int g_nIsEncodedbyBase64 = IDENTIFIY_RES_UNKNOW; int g_nIsEncodedbyMD5 = IDENTIFIY_RES_UNKNOW; int g_nIsEncodedbySHA1 = IDENTIFIY_RES_UNKNOW; // 函数声明 bool GetParam(char* argv[]); bool GetExecPath(void); int RegnizeChar(char*); bool IsHex(char*); bool PreProcess(char*, int); bool IsEncodedbyURL(char*); bool IsEncodedbyQP(char*); bool IsEncodedbyUnicode(char*); bool IsEncodedbyHtml(char*); bool IsEncodedbyBase64(void); bool FreeGloabl(char*); int main(int argc, char* argv[]) { int nFileHandle; char szReadTemp[BUFFER_MAX_SIZE]; size_t nReadSize; const char *pszPrintInfo[4]; pszPrintInfo[0] = "IDENTIFIY_RES_FALSE"; pszPrintInfo[1] = "IDENTIFIY_RES_UNKNOW"; pszPrintInfo[2] = "IDENTIFIY_RES_MAYBE"; pszPrintInfo[3] = "IDENTIFIY_RES_CERTAIN"; // 参数个数合法性判定 if (argc != 3) { DbgErrPrint("Invalid count of parameter! \nUse this commnd like:\n \tIdentify -s [encoded content]\n or\n \tIdentify -f [filename]"); return -1; } // 获取参数值 if (!GetParam(argv)) { DbgErrPrint("Invalid value of parameter! \nUse this commnd like:\n \tIdentify -s [encoded content]\n or\n \tIdentify -f [filename]"); return -1; } // 若待判定的是一个文件,则需将文件内容读出 if (g_nWorkMode == IDENTIFIY_SRC_FILE) { nFileHandle = open (g_pszEncodedFilePath, O_RDONLY); if (nFileHandle == -1) { DbgSysErrPrint("Open encoded file failed!"); return -1; } nReadSize = read(nFileHandle, szReadTemp, BUFFER_MAX_SIZE); close(nFileHandle); switch(nReadSize) { case -1: DbgSysErrPrint("Read from encoded file failed!"); return -1; case 0: DbgErrPrint("Empty encoded file!"); return -1; default: g_pszEncodedCotent = szReadTemp; g_nEncodedCotentLength = nReadSize; } } // 预处理 if (!PreProcess(g_pszEncodedCotent, g_nEncodedCotentLength)) { DbgErrPrint("PreProcess failed!"); return -1; } // 依次判定各种编码的可能性 IsEncodedbyURL(g_pszEncodedCotent); IsEncodedbyUnicode(g_pszEncodedCotent); IsEncodedbyQP(g_pszEncodedCotent); IsEncodedbyHtml(g_pszEncodedCotent); IsEncodedbyBase64(); // 打印结果 DbgMsgPrint("URL/URI encode identification result: %s", pszPrintInfo[g_nIsEncodedbyURL+1]); DbgMsgPrint("Quoted-Printable encode identification result: %s", pszPrintInfo[g_nIsEncodedbyQP+1]); DbgMsgPrint("Unicode encode identification result: %s", pszPrintInfo[g_nIsEncodedbyUnicode+1]); DbgMsgPrint("Html special encode identification result: %s", pszPrintInfo[g_nIsEncodedbyHtml+1]); DbgMsgPrint("Base64 encode identification result: %s", pszPrintInfo[g_nIsEncodedbyBase64+1]); DbgMsgPrint("MD5 hash encode identification result: %s", pszPrintInfo[g_nIsEncodedbyMD5+1]); DbgMsgPrint("SHA1 hash encode identification result: %s", pszPrintInfo[g_nIsEncodedbySHA1+1]); FreeGloabl(argv[2]); return 0; } // 获取程序的启动参数 //参数 argv, 程序启动参数列表 bool GetParam(char* argv[]) { if (strncmp(argv[1], "-s", 2)==0) // 要判定的字符序列直接通过参数传进来 { g_nWorkMode = IDENTIFIY_SRC_STRING; g_pszEncodedCotent = argv[2]; g_nEncodedCotentLength = strlen(g_pszEncodedCotent); DbgMsgPrint("Identify string: %s", g_pszEncodedCotent); return true; } else if (strncmp(argv[1], "-f", 2)==0) //要判定的是一个文件 { g_nWorkMode = IDENTIFIY_SRC_FILE; if (argv[2][0] == '/') { g_pszEncodedFilePath = argv[2]; } else { if (!GetExecPath()) { DbgErrPrint("Get the path of Identify failed!"); return false; } g_pszEncodedFilePath = new char[strlen(g_pszExecPath)+strlen(argv[2])+1]; ASSERT(g_pszEncodedFilePath!=NULL); strcpy(g_pszEncodedFilePath, g_pszExecPath); strcat(g_pszEncodedFilePath, argv[2]); } DbgMsgPrint("Identify file: %s", g_pszEncodedFilePath); return true; } return false; } // 获取可执行文件所在的目录路径 //返回 成功返回true bool GetExecPath(void) { char szExecPath[512]; int nCount; int i; //先获取可执行文件的路径 nCount = readlink("/proc/self/exe", szExecPath, sizeof(szExecPath)); if (nCount == -1) { DbgErrPrint("Readlink failed!"); return false; } //提取可执行文件所在的目录路径 szExecPath[nCount] = '\0'; // 字符数组转换成C字符串 for (i=nCount-1; i>0; i--) { if (szExecPath[i-1] == '/') break; } if (i == 0) //不存在目录分隔符 { DbgErrPrint("Get exec path failed!"); return false; } szExecPath[i] ='\0'; g_pszExecPath = new char[i+1]; ASSERT(g_pszExecPath != NULL); strcpy(g_pszExecPath, szExecPath); return true; } // 识别输入字符的类型 //参数 pszValue, 指向待识别的字符 //返回 若为数字,返回 2; 若为大写英文字符,返回 1; 若为小写英文字符,返回 0; 若都不是,返回 -1. int RegnizeChar(char *pszValue) { if (*pszValue>='0' && *pszValue<='9') return 2; else if (*pszValue>='A' && *pszValue<='F') return 1; else if (*pszValue>='a' && *pszValue<='f') return 0; else return -1; } // 判定输入字符是否可能是十六进制值 //参数 pszValue, 指向待识别的字符 //返回 若识别为非16进制值,返回 false; 反之,返回 true. bool IsHex(char *pszValue) { if (RegnizeChar(pszValue)<0) return false; return true; } /*此处略去预处理函数与几个判定函数的定义,参看前文 */ // 释放全局变量 //参数 pszParam【输入】,最后一个启动参数 //返回 成功返回true bool FreeGloabl(char *pszParam) { if (g_pszExecPath != NULL) { delete [] g_pszExecPath; g_pszExecPath = NULL; } // 仅当启动参数为相对路径时,才需要释放 if (g_pszEncodedFilePath != NULL && g_pszEncodedFilePath != pszParam) { delete [] g_pszEncodedFilePath; g_pszEncodedFilePath = NULL; } return true; }
#ifndef ENCODEIDENTIFIY_BASE_H #define ENCODEIDENTIFIY_BASE_H #include <stdio.h> //ANSI标准 输入输出 #include <stdlib.h> //ANSI标准 常用函数库 #include <time.h> //ANSI标准 日期与时间处理 #include <signal.h> //ANSI标准 信号处理 #include <errno.h> //ANSI标准 错误码定义 #include <assert.h> //ANSI标准 验证程序断言 #include <unistd.h> //POSIX 符号常量( 系统常用API的封装 ) #include <strings.h> //POSIX 字符串处理( 基本类似标准的string.h ) #include <fcntl.h> //POSIX 文件操作 #include <sys/stat.h> //POSIX 文件属性 #include <sys/types.h> //POSIX 基本系统数据类型 #include <string> using namespace std; // 定义是否使用调试模式 #define DEBUG // 定义调试宏 #ifdef DEBUG #define ASSERT(_x_) assert(_x_) #define DbgSysErrPrint(_x_) perror(_x_) #define DbgMsgPrint(format,...) printf(format"\n", ##__VA_ARGS__) #define DbgErrPrint(format,...) printf("File: "__FILE__", Line: %05d, Func: %s ---> "format"\n", __LINE__, __FUNCTION__, ##__VA_ARGS__) #else #define ASSERT(_x_) #define DbgSysErrPrint(_x_) #define DbgMsgPrint(format,...) #define DbgErrPrint(format,...) #endif #define ARRAY_MAX_SIZE 512 #define BUFFER_MAX_SIZE 2048 #define LINE_MAX_LENGTH 76 // 定义识别来源 #define IDENTIFIY_SRC_STRING 1 // 识别字符串 #define IDENTIFIY_SRC_FILE 2 // 识别文件 // 定义识别结果 #define IDENTIFIY_RES_CERTAIN 2 // 确认是某种编码 #define IDENTIFIY_RES_MAYBE 1 // 可能是某种编码 #define IDENTIFIY_RES_UNKNOW 0 // 无法确认 #define IDENTIFIY_RES_FALSE -1 // 确认不是某种编码 // 可供外部文件调用的全局变量声明 extern char *g_pszExecPath; extern int g_nWorkMode; extern char *g_pszEncodedFilePath; extern char *g_pszEncodedCotent; extern int g_nEncodedCotentLength; #endif // ENCODEIDENTIFIY_BASE_H
Linux工具开发---2编程实现对输入字符序列变换(编码/加密/散列)方式的智能判定,布布扣,bubuko.com
Linux工具开发---2编程实现对输入字符序列变换(编码/加密/散列)方式的智能判定
原文:http://blog.csdn.net/howeverpf/article/details/37729451