用户
搜索
  • TA的每日心情
    慵懒
    昨天 07:31
  • 签到天数: 203 天

    连续签到: 9 天

    [LV.7]常住居民III

    版主

    Rank: 7Rank: 7Rank: 7

    27

    主题

    109

    帖子

    4080

    魔法币
    收听
    2
    粉丝
    3
    注册时间
    2015-11-20

    i春秋签约作者春秋文阁

    发表于 2017-12-29 10:47:19 35153
    本帖最后由 icq5f7a075d 于 2018-1-2 21:40 编辑

    本文原创作者:icq5f7a075d,本文属i春秋原创奖励计划,未经许可禁止转载!
    本文如果出现错误,欢迎指出,感激不尽!
    本文中的所有程序请在虚拟机中运行。
    参考资料:
    《加密与解密实战攻略》
    《应用密码学》 曹天杰著

    1. 算法介绍
    MD5:消息摘要算法第五版,是一种散列算法,用以提供消息的完整性保护。
    MD5加密流程:
    (1)数据填充:
    ①消息填充:在要处理的信息后面补上0x80(实际上是先补上一位1,紧跟着补0),然后后面补充0直到消息长度模512等于448;填充后的消息长度是512的某一倍数减64;
    ②添加消息长度:在消息后面补上64bit消息长度,如果消息长度大于2^64,则以2^64取模。
    将消息按照长度512进行分组,每一个512bit分组是16个字(32bit)。
    (2)初始化散列值/缓冲区
    MD5算法使用128bit长的缓冲区以存储中间结果和最终Hash值。缓冲区可表示为4个32位长的寄存器(A、B、C、D),其初始值:
    A=0x67452301
    B=0xefcdab89
    C=0x98badcfe
    D=0x10325476
    (3)计算散列值
    以分组为单位进行消息处理。整个计算过程,分别对每一块信息块进行MD5计算;每次计算4轮,每轮16次操作;
    图片1.png
    这4个函数为:
    F(X,Y,Z) =(X&Y)|((~X)&Z)
    G(X,Y,Z) =(X&Z)|(Y&(~Z))
    H(X,Y,Z) =X^Y^Z
    I(X,Y,Z)=Y^(X|(~Z))
    四轮具体操作:
    FF(a, b, c, d, Mj, s, ti)表示 a = b + ((a + F(b, c, d) + Mj + ti) <<< s)
    GG(a, b, c, d, Mj, s, ti)表示 a = b + ((a + G(b, c, d) + Mj + ti) <<< s)
    HH(a, b, c, d, Mj, s, ti)表示 a = b + ((a + H(b, c, d) + Mj + ti) <<< s)
    II(a, b, c, d, Mj, s, ti)表示 a = b + ((a + I(b, c, d) + Mj + ti) <<<s)
    Mj是信息块的第j个分组;<<<表示循环左移;+是模232加法;tii是4294967896(0x1 0000 0000)*abs(sin(i))的整数部分(即常数T),i的单位是弧度。
    常数表T
    [C++] 纯文本查看 复制代码
    T[1]=0xd76aa478;         T[17]=0xf61e2562;            T[33]=0xfffa3942;         T[49]=0xf4292244;
    T[2]=0xe8c7b756;         T[18]=0xc040b340;            T[34]=0x8771f681;        T[50]=0x432aff97;
    T[3]=0x242070db;         T[19]=0x265e5a51;            T[35]=0x6d9d6122;        T[51]=0xab9423a7;
    T[4]=0xc1bdceee;         T[20]=0xe9b6c7aa;            T[36]=0xfde5380c;        T[52]=0xfc93a039;
    T[5]=0xf57c0faf;         T[21]=0xd62f105d;            T[37]=0xa4beea44;        T[53]=0x655b59c3;
    T[6]=0x4787c62a;         T[22]=0x02441453;            T[38]=0x4bdecfa9;        T[54]=0x8f0ccc92;
    T[7]=0xa8304613;         T[23]=0xd8a1e681;            T[39]=0xf6bb4b60;        T[55]=0xffeff47d;
    T[8]=0xfd469501;         T[24]=0xe7d3fbc8;            T[40]=0xbebfbc70;        T[56]=0x85845dd1;
    T[9]=0x698098d8;         T[25]=0x21e1cde6;            T[41]=0x289b7ec6;        T[57]=0x6fa87e4f;
    T[10]=0x8b44f7af;         T[26]=0xc33707d6;            T[42]=0xeaa127fa;        T[58]=0xfe2ce6e0;
    T[11]=0xffff5bb1;         T[27]=0xf4d50d87;            T[43]=0xd4ef3085;        T[59]=0xa3014314;
    T[12]=0x895cd7be;         T[28]=0x455a14ed;            T[44]=0x04881d05;        T[60]=0x4e0811a1;
    T[13]=0x6b901122;         T[29]=0xa9e3e905;            T[45]=0xd9d4d039;        T[61]=0xf7537e82;
    T[14]=0xfd987193;         T[30]=0xfcefa3f8;            T[46]=0xe6db99e5;        T[62]=0xbd3af235;
    T[15]=0xa679438e;         T[31]=0x676f02d9;            T[47]=0x1fa27cf8;        T[63]=0x2ad7d2bb;
    T[16]=0x49b40821;         T[32]=0x8d2a4c8a;            T[48]=0xc4ac5665;        T[64]=0xeb86d391;
    最后得到它的64步如下:
    第一轮
            FF(a, b, c, d, M0, 7, 0xd76aa478)
            FF(d, a, b, c, M1, 12, 0xe8c7b756)
            FF(c, d, a, b, M2, 17, 0x242070db)
            FF(b, c, d, a, M3, 22, 0xc1bdceee)
            FF(a, b, c, d, M4, 7, 0xf57c0faf)
            FF(d, a, b, c, M5, 12, 0x4787c62a)
            FF(c, d, a, b, M6, 17, 0xa8304613)
            FF(b, c, d, a, M7, 22, 0xfd469501)
            FF(a, b, c, d, M8, 7, 0x698098d8)
            FF(d, a, b, c, M9, 12, 0x8b44f7af)
            FF(c, d, a, b, M10, 17, 0xffff5bb1)
            FF(b, c, d, a, M11, 22, 0x895cd7be)
            FF(a, b, c, d, M12, 7, 0x6b901122)
            FF(d, a, b, c, M13, 12, 0xfd987193)
            FF(c, d, a, b, M14, 17, 0xa679438e)
            FF(b, c, d, a, M15, 22, 0x49b40821)
            第二轮
      GG(a, b, c, d, M1, 5, 0xf61e2562)
      GG(d, a, b, c, M6, 9, 0xc040b340)
      GG(c, d, a, b, M11, 14, 0x265e5a51)
      GG(b, c, d, a, M0, 20, 0xe9b6c7aa)
      GG(a, b, c, d, M5, 5, 0xd62f105d)
      GG(d, a, b, c, M10, 9, 0x02441453)
      GG(c, d, a, b, M15, 14, 0xd8a1e681)
      GG(b, c, d, a, M4, 20, 0xe7d3fbc8)
      GG(a, b, c, d, M9, 5, 0x21e1cde6)
      GG(d, a, b, c, M14, 9, 0xc33707d6)
      GG(c, d, a, b, M3, 14, 0xf4d50d87)
      GG(b, c, d, a, M8, 20, 0x455a14ed)
      GG(a, b, c, d, M13, 5, 0xa9e3e905)
      GG(d, a, b, c, M2, 9, 0xfcefa3f8)
      GG(c, d, a, b, M7, 14, 0x676f02d9)
      GG(b, c, d, a, M12, 20, 0x8d2a4c8a)
            第三轮
      HH(a, b, c, d, M5, 4, 0xfffa3942)
      HH(d, a, b, c, M8, 11, 0x8771f681)
      HH(c, d, a, b, M11, 16, 0x6d9d6122)
      HH(b, c, d, a, M14, 23, 0xfde5380c)
      HH(a, b, c, d, M1, 4, 0xa4beea44)
      HH(d, a, b, c, M4, 11, 0x4bdecfa9)
      HH(c, d, a, b, M7, 16, 0xf6bb4b60)
      HH(b, c, d, a, M10, 23, 0xbebfbc70)
      HH(a, b, c, d, M13, 4, 0x289b7ec6)
      HH(d, a, b, c, M0, 11, 0xeaa127fa)
      HH(c, d, a, b, M3, 16, 0xd4ef3085)
      HH(b, c, d, a, M6, 23, 0x04881d05)
      HH(a, b, c, d, M9, 4, 0xd9d4d039)
      HH(d, a, b, c, M12, 11, 0xe6db99e5)
      HH(c, d, a, b, M15, 16, 0x1fa27cf8)
      HH(b, c, d, a, M2, 23, 0xc4ac5665)
            第四轮
      II(a, b, c, d, M0, 6, 0xf4292244)
      II(d, a, b, c, M7, 10, 0x432aff97)
      II(c, d, a, b, M14, 15, 0xab9423a7)
      II(b, c, d, a, M5, 21, 0xfc93a039)
      II(a, b, c, d, M12, 6, 0x655b59c3)
      II(d, a, b, c, M3, 10, 0x8f0ccc92)
      II(c, d, a, b, M10, 15, 0xffeff47d)
      II(b, c, d, a, M1, 21, 0x85845dd1)
      II(a, b, c, d, M8, 6, 0x6fa87e4f)
      II(d, a, b, c, M15, 10, 0xfe2ce6e0)
      II(c, d, a, b, M6, 15, 0xa3014314)
      II(b, c, d, a, M13, 21, 0x4e0811a1)
      II(a, b, c, d, M4, 6, 0xf7537e82)
      II(d, a, b, c, M11, 10, 0xbd3af235)
      II(c, d, a, b, M2, 15, 0x2ad7d2bb)
      II(b, c, d, a, M9, 21, 0xeb86d391)
    说明:
    A. 四个轮运算的结构相同,但各轮使用不同的基本逻辑函数:F、G、H、I;
    B. 每轮的输入都是当前要处理的512bit的分组和128bit缓冲区的当前值A、B、C、D的内容,输出仍然是放在缓冲区中以产生新的A、B、C、D;
    C. 每轮的处理过程还需要使用常数表T中元素。第4轮的输出再与第1轮的输入相加,相加时看作4个32bit的字按模232相加,相加结果就是本轮压缩函数HMD5的输出。
    (4)输出散列值
    消息的所有L个分组被处理完以后,最后一个HMD5的输出就是MD5散列值。
    2. 程序逆向
    该程序中和《算法逆向2》中的程序一脉相承,很多地方使用了相同的方法。
    2.1. 定位算法关键位置
    运行程序,随机输入NameCode,弹出错误对话框:
    图片2.png
    IDA中查找MessageBox的引用,找到弹出错误对话框和正确对话框的位置:
    图片3.png
    简单的crackme修改上面的几个跳转语句就要让程序弹出正确对话框。这里我们要详细分析算法,该段代码所在的函数过程sub_404898就是我们要分析的关键算法。
    2.2. 算法逆向
    ①获取并处理输入数据,MD5初始化
    程序首先进行了一些初始化工作,初始化MD5中的ABCD寄存器,并初始化一个变量赋值为0xC3D2E1F0,我们将其命名为E,这个值用于处理MD5后的数据。开辟64字节的缓冲区,接着获取NameCode数据,并在Name字符串前添加“DiKeN”,Code字符串前添加“0”,获取和添加字符串方式与《算法逆向2》中的方法相同:
    图片4.png
    判断Name数据的长度,长度不大于0x1E
    图片11.png
    ②消息填充:
    首先将消息(处理后的Name数据)存入buffer缓冲区,并在消息尾填充0x80,
    图片5.png
    接着将消息长度填充进buffer最后8字节中,这里是小端存储,buffer[63]是最高位,buffer[56]是最低位,填充时逐字节填充。
    图片6.png
    消息填充完毕后,应该进行消息分组,但本程序对Name的长度进行了限制,不需要考虑多个分组的情况,消息只有一个分组,所以不需要分组。
    ③计算散列值
    sub_403E88是MD5中4*16轮运算的过程,运算过程比较简单,直接硬编码将常数表T写进函数。
    图片7.png
    图片8.png
    ④处理MD5数据
    经过4*16次运算,我们计算出A、B、C、D的值,成功得到MD5。程序又对MD5的结果进行了进一步处理,处理的过程A=A^B^C^D^E:
    图片9.png
    ⑤校验数据
    将输入的Code转换为整数(过程和《算法逆向2》中相同),比较CodeA的值,如果相同就通过校验。
    2.3. 注册机
    程序流程已经分析的很详细,直接上源码:
    [C++] 纯文本查看 复制代码
    #include<iostream>
    #include<string.h>
    using namespace std;
    unsigned T[65];
    
    void MD5Updata(unsigned &A,unsigned &B,unsigned &C,unsigned &D,char *buffer);
    
    int main()
    {
            T[1]=0xd76aa478;         T[17]=0xf61e2562;            T[33]=0xfffa3942;         T[49]=0xf4292244;
            T[2]=0xe8c7b756;         T[18]=0xc040b340;            T[34]=0x8771f681;        T[50]=0x432aff97;
            T[3]=0x242070db;         T[19]=0x265e5a51;            T[35]=0x6d9d6122;        T[51]=0xab9423a7;
            T[4]=0xc1bdceee;         T[20]=0xe9b6c7aa;            T[36]=0xfde5380c;        T[52]=0xfc93a039;
            T[5]=0xf57c0faf;         T[21]=0xd62f105d;            T[37]=0xa4beea44;        T[53]=0x655b59c3;
            T[6]=0x4787c62a;         T[22]=0x02441453;            T[38]=0x4bdecfa9;        T[54]=0x8f0ccc92;
            T[7]=0xa8304613;         T[23]=0xd8a1e681;            T[39]=0xf6bb4b60;        T[55]=0xffeff47d;
            T[8]=0xfd469501;         T[24]=0xe7d3fbc8;            T[40]=0xbebfbc70;        T[56]=0x85845dd1;
            T[9]=0x698098d8;         T[25]=0x21e1cde6;            T[41]=0x289b7ec6;        T[57]=0x6fa87e4f;
            T[10]=0x8b44f7af;         T[26]=0xc33707d6;            T[42]=0xeaa127fa;        T[58]=0xfe2ce6e0;
            T[11]=0xffff5bb1;         T[27]=0xf4d50d87;            T[43]=0xd4ef3085;        T[59]=0xa3014314;
            T[12]=0x895cd7be;         T[28]=0x455a14ed;            T[44]=0x04881d05;        T[60]=0x4e0811a1;
            T[13]=0x6b901122;         T[29]=0xa9e3e905;            T[45]=0xd9d4d039;        T[61]=0xf7537e82;
            T[14]=0xfd987193;         T[30]=0xfcefa3f8;            T[46]=0xe6db99e5;        T[62]=0xbd3af235;
            T[15]=0xa679438e;         T[31]=0x676f02d9;            T[47]=0x1fa27cf8;        T[63]=0x2ad7d2bb;
            T[16]=0x49b40821;         T[32]=0x8d2a4c8a;            T[48]=0xc4ac5665;        T[64]=0xeb86d391;
            unsigned A=0x67452301,B=0xEFCDAB89,C= 0x98BADCFE,D= 0x10325476;
        unsigned E= 0xC3D2E1F0;
        char buffer[64]={'\0'};
        char t_Name[64],t_Code[64],Name[64]="DiKeN",Code[64]="0";
        unsigned len_tName,len_tCode,len_Name,len_Code;
        long len_byte_Name;
        cout<<"Name:";
        cin>>t_Name;
        len_tName=strlen(t_Name);
        strcat(Name,t_Name);
        if(Name[0]=='\0'||len_tName>0x1E){
                printf("%x,%x\n",(unsigned)Name[0],len_tName) ;
                cout<<"Error!"<<endl;
                return 1;
        }
        
        len_Name=strlen(Name);len_Code=strlen(Code);
        //消息填充 
            memcpy(&buffer,&Name,len_Name);
        buffer[len_Name]='\x80';
            len_byte_Name =len_Name<<3;//计算bit数 
            if(len_byte_Name>0xFF){
                    buffer[56]=(char)(len_byte_Name&0x000000FF);
                    buffer[57]=(char)(len_byte_Name&0x0000FF00);
            }
            else
                    buffer[56]=(char)len_byte_Name;
            MD5Updata(A,B,C,D,buffer);
            A=A+0x67452301;
            B=B+0xEFCDAB89;
            C=C+0x98BADCFE;
            D=D+0x10325476;
            A=A^B^C^D^E;         
            printf("Code:%u",(unsigned)A);
        return 0;
    } 
    
    unsigned md5_ROL(unsigned m,unsigned A,int size){
            return A+((m >> (32 - size)) | (m << size) );
    }
    void MD5Updata(unsigned &A,unsigned &B,unsigned &C,unsigned &D,char *buffer){
            unsigned c[64];
            c[0]=c[4]=c[8]=c[12]=0x7;
            c[1]=c[5]=c[9]=c[13]=0xC;
            c[2]=c[6]=c[10]=c[14]=0x11;
            c[3]=c[7]=c[11]=c[15]=0x16;
            c[16]=c[20]=c[24]=c[28]=0x5;
            c[17]=c[21]=c[25]=c[29]=0x9;
            c[18]=c[22]=c[26]=c[30]=0xe;
            c[19]=c[23]=c[27]=c[31]=0x14;
            c[32]=c[36]=c[40]=c[44]=0x4;
            c[33]=c[37]=c[41]=c[45]=0xb;
            c[34]=c[38]=c[42]=c[46]=0x10;
            c[35]=c[39]=c[43]=c[47]=0x17;
            c[48]=c[52]=c[56]=c[60]=0x6;
            c[49]=c[53]=c[57]=c[61]=0xa;
            c[50]=c[54]=c[58]=c[62]=0xf;
            c[51]=c[55]=c[59]=c[63]=0x15;
            
            //FF();
            int i,j;
            i=0;j=0;
            for(;i<16;i++)
            {
                    j=4*i;
                    unsigned m_a=((unsigned)buffer[j]&0x000000FF)+((unsigned)buffer[j+1]*0x100&0x0000FF00)+((unsigned)buffer[j+2]*0x10000&0x00FF0000)+((unsigned)buffer[j+3]*0x1000000&0xFF000000);
                    if(i%4==0)
                    {
                            A=md5_ROL(A+m_a+T[i+1]+((C^D)&B^D),B,c);
                    }
                            
                    else if(i%4==1)
                    {
                            D=md5_ROL(D+m_a+T[i+1]+((B^C)&A^C),A,c);
                    }        
                    else if(i%4==2)
                    {
                            C=md5_ROL(C+m_a+T[i+1]+((A^B)&D^B),D,c);
                    }
                    else
                    {
                    B=md5_ROL(B+m_a+T[i+1]+((D^A)&C^A),C,c);
                    }
                            
            }
            //GG()
            i=0;j=0;
            for(;i<16;i++)
            {
                    j=4*(((5*i)+1)%16);
                    unsigned m_a=((unsigned)buffer[j]&0x000000FF)+((unsigned)buffer[j+1]*0x100&0x0000FF00)+((unsigned)buffer[j+2]*0x10000&0x00FF0000)+((unsigned)buffer[j+3]*0x1000000&0xFF000000);
    
                    if(i%4==0)
                    {
                            A=md5_ROL(A+m_a+T[i+17]+((C^B)&D^C),B,c[i+16]);
                    }
                            
                    else if(i%4==1)
                    {
                            D=md5_ROL(D+m_a+T[i+17]+((B^A)&C^B),A,c[i+16]);
                    }        
                    else if(i%4==2)
                    {
                            C=md5_ROL(C+m_a+T[i+17]+((A^D)&B^A),D,c[i+16]);
                    }
                    else
                    {
                            B=md5_ROL(B+m_a+T[i+17]+((D^C)&A^D),C,c[i+16]);
                    }                
            }
            
            //HH()
            i=0;j=0;
            for(;i<16;i++)
            {
                    j=4*((3*i+5)%16);
                    unsigned m_a=((unsigned)buffer[j]&0x000000FF)+((unsigned)buffer[j+1]*0x100&0x0000FF00)+((unsigned)buffer[j+2]*0x10000&0x00FF0000)+((unsigned)buffer[j+3]*0x1000000&0xFF000000);
    
                    if(i%4==0)
                    {
                            A=md5_ROL(A+m_a+T[i+33]+(C^B^D),B,c[i+32]);
                    }
                            
                    else if(i%4==1)
                    {
                            D=md5_ROL(D+m_a+T[i+33]+(B^A^C),A,c[i+32]);
                    }        
                    else if(i%4==2)
                    {
                            C=md5_ROL(C+m_a+T[i+33]+(A^D^B),D,c[i+32]);
                    }
                    else
                    {
                            B=md5_ROL(B+m_a+T[i+33]+(D^C^A),C,c[i+32]);
                    }                
            }
            
            //II()
            i=0;j=0;
            for(;i<16;i++)
            {
                    j=4*((7*i)%16);
                    unsigned m_a=((unsigned)buffer[j]&0x000000FF)+((unsigned)buffer[j+1]*0x100&0x0000FF00)+((unsigned)buffer[j+2]*0x10000&0x00FF0000)+((unsigned)buffer[j+3]*0x1000000&0xFF000000);
                    if(i%4==0)
                    {
                            A=md5_ROL(A+m_a+T[i+49]+(((~D)|B)^C),B,c[i+48]);
                    }                        
                    else if(i%4==1)
                    {
                            D=md5_ROL(D+m_a+T[i+49]+(((~C)|A)^B),A,c[i+48]);
                    }        
                    else if(i%4==2)
                    {
                            C=md5_ROL(C+m_a+T[i+49]+(((~B)|D)^A),D,c[i+48]);
                    }
                    else
                    {
                            B=md5_ROL(B+m_a+T[i+49]+(((~A)|C)^D),C,c[i+48]);
                    }                
            }
    }

    图片10.png
    3. 最后再说一点
    本程序实现的MD5是极其简单和容易识别的,对输入数据长度进行了限制,所以不需要考虑多个分组的情况,常数直接通过硬编码写入程序,非常容易识别。现实中,我们往往会遇到变形的MD5算法,MD5的变形无非就是变一下四个常数,变一下用于填充的字符,变一下hash的处理过程

    嗯,本系列未完待续...

    md5crackme.rar

    11.55 KB, 下载次数: 1, 下载积分: 魔法币 -3

    售价: 5 魔法币  [记录]

    逆向程序

    本帖被以下淘专辑推荐:

    看到算法逆向,还以为是构造碰撞。。。
    使用道具 举报 回复
    发表于 2017-12-30 12:48:27
    Ph0enix 发表于 2017-12-30 09:47
    看到算法逆向,还以为是构造碰撞。。。

    你说的有道理,我标题起的有问题
    使用道具 举报 回复
    66666666666666666666666666
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册