zoukankan      html  css  js  c++  java
  • MD5 详解

    MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2、MD3和MD4发展而来。MD5算法的使用不需要支付任何版权费用。

    MD5功能:
    输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
    不同的输入得到的不同的结果(唯一性);
    根据128位的输出结果不可能反推出输入的信息(不可逆);

    MD5属不属于加密算法:

    认为不属于的人是因为他们觉得不能从密文(散列值)反过来得到原文,即没有解密算法,所以这部分人认为MD5只能属于算法,不能称为加密算法;
    认为属于的人是因为他们觉得经过MD5处理后看不到原文,即已经将原文加密,所以认为MD5属于加密算法;我个人支持后者。

    MD5用途:
    1、防止被篡改:
    1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。3)SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.

    2、防止直接看到明文:
    现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码(其实这样是不安全的,后面我会提到)。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)

    3、防止抵赖(数字签名):
    这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。

    MD5算法过程:
    对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

    第一步、填充:如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的方法是填充一个1和n个0。填充完后,信息的长度就为N*512+448(bit);

    第二步、记录信息长度:用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N*512+448+64=(N+1)*512位。

    第三步、装入标准的幻数(四个整数):标准的幻数(物理顺序)是(A=(01234567)16,B=(89ABCDEF)16,C=(FEDCBA98)16,D=(76543210)16)。

    如果在程序中定义应该是(A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L)。有点晕哈,其实想一想就明白了。

    第四步、四轮循环运算:循环的次数是分组的个数(N+1)

    1)将每一512字节细分成16个小组,每个小组32位(4个字节)

    2)先认识四个线性函数(&是与,|是或,~是非,^是异或)

    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))

     3)设Mj表示消息的第j个子分组(从0到15),<<<s表示循环左移s位,则四种操作为:

    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)

    4)四轮运算

    第一轮
    a=FF(a,b,c,d,M0,7,0xd76aa478)
    b=FF(d,a,b,c,M1,12,0xe8c7b756)
    c=FF(c,d,a,b,M2,17,0x242070db)
    d=FF(b,c,d,a,M3,22,0xc1bdceee)
    a=FF(a,b,c,d,M4,7,0xf57c0faf)
    b=FF(d,a,b,c,M5,12,0x4787c62a)
    c=FF(c,d,a,b,M6,17,0xa8304613)
    d=FF(b,c,d,a,M7,22,0xfd469501)
    a=FF(a,b,c,d,M8,7,0x698098d8)
    b=FF(d,a,b,c,M9,12,0x8b44f7af)
    c=FF(c,d,a,b,M10,17,0xffff5bb1)
    d=FF(b,c,d,a,M11,22,0x895cd7be)
    a=FF(a,b,c,d,M12,7,0x6b901122)
    b=FF(d,a,b,c,M13,12,0xfd987193)
    c=FF(c,d,a,b,M14,17,0xa679438e)
    d=FF(b,c,d,a,M15,22,0x49b40821)

    第二轮
    a=GG(a,b,c,d,M1,5,0xf61e2562)
    b=GG(d,a,b,c,M6,9,0xc040b340)
    c=GG(c,d,a,b,M11,14,0x265e5a51)
    d=GG(b,c,d,a,M0,20,0xe9b6c7aa)
    a=GG(a,b,c,d,M5,5,0xd62f105d)
    b=GG(d,a,b,c,M10,9,0x02441453)
    c=GG(c,d,a,b,M15,14,0xd8a1e681)
    d=GG(b,c,d,a,M4,20,0xe7d3fbc8)
    a=GG(a,b,c,d,M9,5,0x21e1cde6)
    b=GG(d,a,b,c,M14,9,0xc33707d6)
    c=GG(c,d,a,b,M3,14,0xf4d50d87)
    d=GG(b,c,d,a,M8,20,0x455a14ed)
    a=GG(a,b,c,d,M13,5,0xa9e3e905)
    b=GG(d,a,b,c,M2,9,0xfcefa3f8)
    c=GG(c,d,a,b,M7,14,0x676f02d9)
    d=GG(b,c,d,a,M12,20,0x8d2a4c8a)

    第三轮
    a=HH(a,b,c,d,M5,4,0xfffa3942)
    b=HH(d,a,b,c,M8,11,0x8771f681)
    c=HH(c,d,a,b,M11,16,0x6d9d6122)
    d=HH(b,c,d,a,M14,23,0xfde5380c)
    a=HH(a,b,c,d,M1,4,0xa4beea44)
    b=HH(d,a,b,c,M4,11,0x4bdecfa9)
    c=HH(c,d,a,b,M7,16,0xf6bb4b60)
    d=HH(b,c,d,a,M10,23,0xbebfbc70)
    a=HH(a,b,c,d,M13,4,0x289b7ec6)
    b=HH(d,a,b,c,M0,11,0xeaa127fa)
    c=HH(c,d,a,b,M3,16,0xd4ef3085)
    d=HH(b,c,d,a,M6,23,0x04881d05)
    a=HH(a,b,c,d,M9,4,0xd9d4d039)
    b=HH(d,a,b,c,M12,11,0xe6db99e5)
    c=HH(c,d,a,b,M15,16,0x1fa27cf8)
    d=HH(b,c,d,a,M2,23,0xc4ac5665)

    第四轮
    a=II(a,b,c,d,M0,6,0xf4292244)
    b=II(d,a,b,c,M7,10,0x432aff97)
    c=II(c,d,a,b,M14,15,0xab9423a7)
    d=II(b,c,d,a,M5,21,0xfc93a039)
    a=II(a,b,c,d,M12,6,0x655b59c3)
    b=II(d,a,b,c,M3,10,0x8f0ccc92)
    c=II(c,d,a,b,M10,15,0xffeff47d)
    d=II(b,c,d,a,M1,21,0x85845dd1)
    a=II(a,b,c,d,M8,6,0x6fa87e4f)
    b=II(d,a,b,c,M15,10,0xfe2ce6e0)
    c=II(c,d,a,b,M6,15,0xa3014314)
    d=II(b,c,d,a,M13,21,0x4e0811a1)
    a=II(a,b,c,d,M4,6,0xf7537e82)
    b=II(d,a,b,c,M11,10,0xbd3af235)
    c=II(c,d,a,b,M2,15,0x2ad7d2bb)
    d=II(b,c,d,a,M9,21,0xeb86d391)

    5)每轮循环后,将A,B,C,D分别加上a,b,c,d,然后进入下一循环。

    网络上找的标准MD5算法(c语言版):

    复制代码
    #include <STRING.H>
    #include <STDIO.H>
    
    //! 定义MD5状态数据结构类型
    typedef struct{
        unsigned long int    state[4];        // 初始链接变量;保存16字节摘要
        unsigned long int    count[2];        // 明文位数
        unsigned char        PADDING[64];    // 填充位,最大64*8位
        unsigned char        buffer[64];        // 输入缓冲
    }MD5_State;
    
    //! MD5转换常量
    #define S11 7
    #define S12 12
    #define S13 17
    #define S14 22
    #define S21 5
    #define S22 9
    #define S23 14
    #define S24 20
    #define S31 4
    #define S32 11
    #define S33 16
    #define S34 23
    #define S41 6
    #define S42 10
    #define S43 15
    #define S44 21
    
    //! F, G, H and I 基本MD5函数
    #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
    #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
    #define H(x, y, z) ((x) ^ (y) ^ (z))
    #define I(x, y, z) ((y) ^ ((x) | (~z)))
    
    //! 将x循环左移n位
    #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
    
    //! 4轮运算中FF(第1轮), GG(第2轮), HH(第3轮), and II(第4轮)转换
    #define FF(a, b, c, d, x, s, ac) { 
        (a) += F ((b), (c), (d)) + (x) + (unsigned long int)(ac); 
        (a) = ROTATE_LEFT ((a), (s)); 
        (a) += (b); 
        }
    #define GG(a, b, c, d, x, s, ac) { 
        (a) += G ((b), (c), (d)) + (x) + (unsigned long int)(ac); 
        (a) = ROTATE_LEFT ((a), (s)); 
        (a) += (b); 
        }
    #define HH(a, b, c, d, x, s, ac) { 
        (a) += H ((b), (c), (d)) + (x) + (unsigned long int)(ac); 
        (a) = ROTATE_LEFT ((a), (s)); 
        (a) += (b); 
        }
    #define II(a, b, c, d, x, s, ac) { 
        (a) += I ((b), (c), (d)) + (x) + (unsigned long int)(ac); 
        (a) = ROTATE_LEFT ((a), (s)); 
        (a) += (b); 
        }
    
    /******************************************************************************/
    //    名称:MD5_memcpy
    //    功能:输入输出字节拷贝
    //  参数:output:    指向unsigned char类型输出缓冲区
    //          input:    指向unsigned char类型输入缓冲区
    //          len:        输入数据长度(字节)
    //    返回:无
    
    /******************************************************************************/
    void MD5_memcpy( unsigned char *output, unsigned char *input, unsigned int len )
    {
        unsigned int i;
    
        for( i = 0; i < len; i++ )
            output[i] = input[i];
    }
    
    /******************************************************************************/
    //    名称:MD5_memset
    //    功能:对MD5运算缓冲区中填充数据
    //  参数:output:    指向unsigned char类型缓冲区
    //          value:    数据
    //          len:        填充大小
    //    返回:无
    
    /******************************************************************************/
    void MD5_memset( unsigned char *output, int value, unsigned int len )
    {
        unsigned int i;
    
        for( i = 0; i < len; i++ )
            ((char *)output)[i] = (char)value;
    }
    
    /******************************************************************************/
    //    名称:Encode
    //    功能:数据类型轮换(unsigned long char -> unsigned char)
    //  参数:output:    指向unsigned char类型输出缓冲区
    //          input:    指向unsigned long int类型输入缓冲区
    //          len:        输入数据长度(字节)
    //    返回:无
    
    /******************************************************************************/
    void Encode( unsigned char *output, unsigned long int *input, unsigned int len )
    {
        unsigned int i, j;
    
        for(i = 0, j = 0; j < len; i++, j += 4) 
        {
            output[j] = (unsigned char)(input[i] & 0xff);
            output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
            output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
            output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
        }
    }
    
    /******************************************************************************/
    //    名称:Decode
    //    功能:数据类型轮换(unsigned char -> unsigned long int)
    //  参数:output:    指向unsigned long int类型输出缓冲区
    //          input:    指向unsigned char类型输入缓冲区
    //          len:        输入数据长度(字节)
    //    返回:无
    
    /******************************************************************************/
    void Decode( unsigned long int *output, unsigned char *input, unsigned int len )
    {
        unsigned int i, j;
    
        for( i=0, j=0; j<len; i++, j+=4 )
            output[i] = ((unsigned long int)input[j]) | (((unsigned long int)input[j+1]) << 8) |
            (((unsigned long int)input[j+2]) << 16) | (((unsigned long int)input[j+3]) << 24);
    }
    
    /******************************************************************************/
    //    名称:MD5Init
    //    功能:初始链接变量赋值;初始化填充位
    //  参数:指向MD5状态数据变量
    //    返回:无
    //  备注:填充位第1位为1,其余位为0
    
    /******************************************************************************/
    void MD5_Init( MD5_State *s )
    {
        s->count[0] = s->count[1] = 0;
        //! 初始链接变量
        s->state[0] = 0x67452301;
        s->state[1] = 0xefcdab89;
        s->state[2] = 0x98badcfe;
        s->state[3] = 0x10325476;
    
        //! 初始填充位(目标形式: 0x80000000......,共计512位)
        MD5_memset( s->PADDING, 0, sizeof(s->PADDING) );
        *(s->PADDING)=0x80;
        //  s->PADDING = {
        //    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        //    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        //    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    };
    }
    
    /******************************************************************************/
    //    名称:MD5Transform
    //    功能:MD5 4轮运算
    //  参数:state: 链接变量;block: 子明文分组
    //    返回:无
    //  备注:4轮共计64步运算
    
    /******************************************************************************/
    void MD5_Transform( unsigned long int state[4], unsigned char block[64] )
    {
        unsigned long int a = state[0], b = state[1], c = state[2], d = state[3], x[16];
    
        Decode( x, block, 64 );
    
        //! 第1轮
    
        FF (a, b, c, d, x[0], S11, 0xd76aa478); // 1
        FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); // 2
        FF (c, d, a, b, x[ 2], S13, 0x242070db); // 3
        FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); // 4
        FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); // 5
        FF (d, a, b, c, x[ 5], S12, 0x4787c62a); // 6
        FF (c, d, a, b, x[ 6], S13, 0xa8304613); // 7
        FF (b, c, d, a, x[ 7], S14, 0xfd469501); // 8
        FF (a, b, c, d, x[ 8], S11, 0x698098d8); // 9
        FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); // 10
        FF (c, d, a, b, x[10], S13, 0xffff5bb1); // 11
        FF (b, c, d, a, x[11], S14, 0x895cd7be); // 12
        FF (a, b, c, d, x[12], S11, 0x6b901122); // 13
        FF (d, a, b, c, x[13], S12, 0xfd987193); // 14
        FF (c, d, a, b, x[14], S13, 0xa679438e); // 15
        FF (b, c, d, a, x[15], S14, 0x49b40821); // 16
    
        //! 第2轮
        GG (a, b, c, d, x[ 1], S21, 0xf61e2562); // 17
        GG (d, a, b, c, x[ 6], S22, 0xc040b340); // 18
        GG (c, d, a, b, x[11], S23, 0x265e5a51); // 19
        GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); // 20
        GG (a, b, c, d, x[ 5], S21, 0xd62f105d); // 21
        GG (d, a, b, c, x[10], S22,  0x2441453); // 22
        GG (c, d, a, b, x[15], S23, 0xd8a1e681); // 23
        GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); // 24
        GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); // 25
        GG (d, a, b, c, x[14], S22, 0xc33707d6); // 26
        GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); // 27
        GG (b, c, d, a, x[ 8], S24, 0x455a14ed); // 28
        GG (a, b, c, d, x[13], S21, 0xa9e3e905); // 29
        GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); // 30
        GG (c, d, a, b, x[ 7], S23, 0x676f02d9); // 31
        GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); // 32
    
        //! 第3轮
        HH (a, b, c, d, x[ 5], S31, 0xfffa3942); // 33
        HH (d, a, b, c, x[ 8], S32, 0x8771f681); // 34
        HH (c, d, a, b, x[11], S33, 0x6d9d6122); // 35
        HH (b, c, d, a, x[14], S34, 0xfde5380c); // 36
        HH (a, b, c, d, x[ 1], S31, 0xa4beea44); // 37
        HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); // 38
        HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); // 39
        HH (b, c, d, a, x[10], S34, 0xbebfbc70); // 40
        HH (a, b, c, d, x[13], S31, 0x289b7ec6); // 41
        HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); // 42
        HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); // 43
        HH (b, c, d, a, x[ 6], S34,  0x4881d05); // 44
        HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); // 45
        HH (d, a, b, c, x[12], S32, 0xe6db99e5); // 46
        HH (c, d, a, b, x[15], S33, 0x1fa27cf8); // 47
        HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); // 48
    
        //! 第4轮
        II (a, b, c, d, x[ 0], S41, 0xf4292244); // 49
        II (d, a, b, c, x[ 7], S42, 0x432aff97); // 50
        II (c, d, a, b, x[14], S43, 0xab9423a7); // 51
        II (b, c, d, a, x[ 5], S44, 0xfc93a039); // 52
        II (a, b, c, d, x[12], S41, 0x655b59c3); // 53
        II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); // 54
        II (c, d, a, b, x[10], S43, 0xffeff47d); // 55
        II (b, c, d, a, x[ 1], S44, 0x85845dd1); // 56
        II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); // 57
        II (d, a, b, c, x[15], S42, 0xfe2ce6e0); // 58
        II (c, d, a, b, x[ 6], S43, 0xa3014314); // 59
        II (b, c, d, a, x[13], S44, 0x4e0811a1); // 60
        II (a, b, c, d, x[ 4], S41, 0xf7537e82); // 61
        II (d, a, b, c, x[11], S42, 0xbd3af235); // 62
        II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); // 63
        II (b, c, d, a, x[ 9], S44, 0xeb86d391); // 64
    
        state[0] += a;
        state[1] += b;
        state[2] += c;
        state[3] += d;
    
        MD5_memset( (unsigned char*)x, 0, sizeof (x) );
    }
    
    /******************************************************************************/
    //    名称:MD5_Update
    //    功能:明文填充,明文分组,16个子明文分组
    //  参数:指向SHA状态变量
    //    返回:无
    
    /******************************************************************************/
    void MD5_Update( MD5_State *s, unsigned char *input, unsigned int inputLen )
    {
        unsigned int i, index, partLen;
    
        //! 明文填充
    
        //! 字节数 mod 64
        index = (unsigned int)((s->count[0] >> 3) & 0x3F);
    
        //! 更新位数
        if((s->count[0] += ((unsigned long int)inputLen << 3))
            < ((unsigned long int)inputLen << 3))
            s->count[1]++;
        s->count[1] += ((unsigned long int)inputLen >> 29);
    
        partLen = 64 - index;
    
        //! MD5 4轮运算
        if (inputLen >= partLen)
        {
            MD5_memcpy( (unsigned char*)&s->buffer[index],  (unsigned char*)input, partLen );
            MD5_Transform( s->state, s->buffer );
    
            for( i = partLen; i + 63 < inputLen; i += 64 )
                MD5_Transform( s->state, &input[i] );
    
            index = 0;
        }
        else
            i = 0;
    
        MD5_memcpy ((unsigned char*)&s->buffer[index], (unsigned char*)&input[i], inputLen-i);
    }
    
    /******************************************************************************/
    //    名称:MD5_Final
    //    功能:MD5最后变换
    //  参数:strContent:指向文件内容缓冲区; iLength:文件内容长度; output:摘要输出缓冲区
    //    返回:无
    
    /******************************************************************************/
    void MD5_Final( MD5_State *s, unsigned char digest[16] )
    {
        unsigned char bits[8];
        unsigned int index, padLen;
    
        Encode (bits, s->count, 8);
    
        //! 长度小于448位(mod 512),对明文进行填充
        index = (unsigned int)((s->count[0] >> 3) & 0x3f);
        padLen = (index < 56) ? (56 - index) : (120 - index);
        MD5_Update( s, s->PADDING, padLen );
    
        MD5_Update( s, bits, 8);
        Encode( digest, s->state, 16 );
    
        MD5_memset ((unsigned char*)s, 0, sizeof (*s));
        MD5_Init( s );
    }
    
    
    /******************************************************************************/
    //    名称:SHA_digest
    //    功能:生成文件摘要
    //  参数:strContent:指向文件内容缓冲区; iLength:文件内容长度; output:摘要输出缓冲区
    //    返回:无
    
    /******************************************************************************/
    void md5_digest( void const *strContent, unsigned int iLength, unsigned char output[16] )
    {
        unsigned char *q = (unsigned char*)strContent;
        MD5_State s;
    
        MD5_Init( &s );
        MD5_Update( &s, q, iLength );
        MD5_Final( &s, output );
    }
    
    int main(int argc, char* argv[])
    {
        unsigned char output[16] = {0};
        int i = 0;
    
        md5_digest("Hello World!", strlen("Hello World!"), output);
    
        for (i = 0; i < 16; i++)
        {
            printf("%02x", output[i]);
        }
    
        printf("
    ");
        return 0;
    }
    复制代码

    ed076287532e86365e841e92bfc50d8c

  • 相关阅读:
    LeetCode "Super Ugly Number" !
    LeetCode "Count of Smaller Number After Self"
    LeetCode "Binary Tree Vertical Order"
    LeetCode "Sparse Matrix Multiplication"
    LeetCode "Minimum Height Tree" !!
    HackerRank "The Indian Job"
    HackerRank "Poisonous Plants"
    HackerRank "Kundu and Tree" !!
    LeetCode "Best Time to Buy and Sell Stock with Cooldown" !
    HackerRank "AND xor OR"
  • 原文地址:https://www.cnblogs.com/DeeLMind/p/7581423.html
Copyright © 2011-2022 走看看