吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 17944|回复: 28
收起左侧

[第十三题] 【题目分析】【吾爱破解2014CrackMe大赛】

[复制链接]
codelive 发表于 2014-11-4 16:14
注意:只是分析,没有爆破或者注册机!


这个Android CM真的太让人头疼了,作者做了很足了反调试和静态分析处理,对so文件都进行了加密处理,分析的太累了,估计在今天20:00之前无法爆破完成,更不用说写注册机了,发此帖就算是记录一下分析过程吧,也供大家以后进行分析。

用到的工具: IDA ARM v6.5, Android逆向助手, Android开发环境Eclipse, 等等(工具使用方法和环境就不细说了)

App包含2个lib文件:libverify.so, libmodule.so

首先先解密so文件:

1. libverify.so
    导出了2个API : init_verify,  verify

    函数init_verify执行时会解密部分加密的数据,包括JNI_OnLoad函数,解密完成后再执行JNI_OnLoad进行初始化工作.
    加密的算法还没有研究,简单的办法是用IDA附加运行后的Android App,然后找到libverify.so模块起始地址,对内存进行DUMP,这样出来的so文件就是解密后的,可以用IDA静态分析了。

        下面是部分JNI_OnLoad的代码:
[C] 纯文本查看 复制代码
        int __fastcall JNI_OnLoad(int a1)
        {
            v21 = a1;
            v22 = 0;
            if(sub_12A0())
                return -1;
            v1 = sub_13CC();
            stat("/data/data/com.example.crackme52/lib/libmodule.so", (struct stat *)&v23);
            dword_5048 = ((((_DWORD)v24 << 20) - (((_DWORD)v24 << 20 >= 1u) + ((_DWORD)v24 << 20) - 1) + (unsigned int)(v24 / 4096)) << 12)
                + 4096;
            v2 = (void *)open("/data/data/com.example.crackme52/lib/libmodule.so", 0);
            dword_504C = (int)v2;
            if(v2 != (void *)-1)
            {
                v2 = mmap(0, dword_5048, 7, 34, 0, 0);
                if(v2 == (void *)-1)
                {
                    v3 = (int)"JNITag";
                    v4 = (int)"Map load error\n";
                    goto LABEL_6;
                }
                v5 = sub_12A0();
                if(!v5)
                {
                    v6 = *(_WORD *)(v1 + 44);
                    v7 = v1 + *(_DWORD *)(v1 + 28);
                    while(v5 < v6 && *(_DWORD *)v7 != 1)
                    {
                        v7 += 32;
                        ++v5;
                    }
                    if(v5 != v6)
                    {
                        v8 = *(_DWORD *)(v7 + 40) + *(_DWORD *)(v7 + 48) - 4 + v1;
                        v9 = (const void *)(v8 - *(_DWORD *)v8);
                        memcpy(v2, (const void *)(v8 - *(_DWORD *)v8), 0x174u);
                        v10 = 0;
                        do
                        {
                            *((_BYTE *)v2 + v10) = ~(*((_BYTE *)v2 + v10) - 3);
                            ++v10;
                        } while(v10 != 372);
                        v11 = 0;
                        v12 = (int)((char *)v2 + *((_DWORD *)v2 + 7));
                        while(v11 < *((_WORD *)v2 + 22) && *(_DWORD *)v12 != 1)
                        {
                            v12 += 32;
                            ++v11;
                        }
                        memcpy(v2, v9, *(_DWORD *)(v12 + 16));
                        for(i = 0; i < *(_DWORD *)(v12 + 16); ++i)
                            *((_BYTE *)v2 + i) = ~(*((_BYTE *)v2 + i) - 3);
                        memcpy((char *)v2 + *(_DWORD *)(v12 + 40), (char *)v9 + *(_DWORD *)(v12 + 36), *(_DWORD *)(v12 + 48));
                        v14 = 0;
                        while(v14 < *(_DWORD *)(v12 + 48))
                        {
                            *((_BYTE *)v2 + v14 + *(_DWORD *)(v12 + 40)) -= 3;
                            v15 = (int)((char *)v2 + v14++ + *(_DWORD *)(v12 + 40));
                            *(_BYTE *)v15 = ~*(_BYTE *)v15;
                        }
                    }
                    *(_DWORD *)"ng;Ljava/lang/String;)V" = v2;
                    sub_1190();
                    *(_DWORD *)off_4F64 = (char *)v2
                        + *(_DWORD *)(*(_DWORD *)"java/lang/String;)V" + 16 * sub_136C("begin_verify") + 4);
                    v16 = sub_136C("__stack_chk_guard");
                    *(_DWORD *)((char *)v2 + sub_127C(*(_DWORD *)"ring;)V", *(_DWORD *)";)V", v16)) = dword_5040;
                    v17 = sub_136C("memcpy");
                    *(_DWORD *)((char *)v2 + sub_127C(dword_5028, dword_502C, v17)) = dword_5044;
                    if(!(*(int(__fastcall **)(int, int *, unsigned int))(*(_DWORD *)v21 + 24))(v21, &v22, 0x10004u))
                    {
                        v18 = v22;
                        if(v22)
                        {
                            v19 = (*(int(__fastcall **)(int, _DWORD))(*(_DWORD *)v22 + 24))(v22, "com/example/crackme52/MainActivity");
                            if(v19)
                                return ((*(int(__fastcall **)(int, int, void **, signed int))(*(_DWORD *)v18 + 860))(
                                v18,
                                v19,
                                &off_5004,
                                1) >> 31) | 0x10004;
                        }
                    }
                }
                return -1;
            }
            v3 = (int)"JNITag";
            v4 = (int)"Open libmodule.so error\n";
        LABEL_6:
            _android_log_print(4, v3, v4);
            return (int)v2;
        }



2. libmodule.so, 整个文件都进行了加密
    导出了1个API : begin_verify

    开始猜测可能是异或算法,但测试下来解密不正确,后来经过对比分析,猜出了算法:
    解密算法是 :  文件的每个字节取反,然后再加1
                RR = (~BB) + 0x01;
    加密算法是 :  文件的每个字节先减1, 再取反
                BB = ~(RR - 0x01);

[C] 纯文本查看 复制代码
        解密代码如下:
        FILE *pIn  = fopen("D:/temp/vs/libmodule.so", "rb");
        FILE *pOut = fopen("D:/temp/vs/libmodule_out.so", "wb");
        int inSize = 13432; // 文件大小
        char *buffer = new char[inSize];

        while(true)
        {
            ret = fread(buffer, 1, inSize, pIn);
            if(ret <= 0)
            {
                break ;
            }
            for(int i = 0; i < ret; i ++)
            {
                buffer[i] = (~buffer[i]) + 0x01;
            }

            ret = fwrite(buffer, 1, ret, pOut);
        }
        delete []buffer;

        fclose(pIn);
        fclose(pOut);


        加密代码如下:
        FILE *pIn  = fopen("D:/temp/crack/crackme52/armeabi/libmodule.p.so", "rb");
        FILE *pOut = fopen("D:/temp/crack/crackme52/armeabi/libmodule.so", "wb");
        int inSize = 13432; // 文件大小
        char *buffer = new char[inSize];

        while(true)
        {
            ret = fread(buffer, 1, inSize, pIn);
            if(ret <= 0)
            {
                break ;
            }
            for(int i = 0; i < ret; i ++)
            {
                buffer[i] = ~(buffer[i] - 0x01);
            }

            ret = fwrite(buffer, 1, ret, pOut);
        }
        delete []buffer;

        fclose(pIn);
        fclose(pOut);


分析算法不擅长,只能先尝试爆破, 从分析可以知道,最终验证函数是 libmodule.so的导出函数 begin_verify,那么就从这个函数进行分析
[C] 纯文本查看 复制代码
        int __fastcall begin_verify(int a1, int a2, int a3, int a4)
        {
            v20 = a2;
            v19 = a4;
            v4 = a1;
            v18 = a3;
            v25 = _stack_chk_guard;
            memcpy(dest, "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789", 0x3Fu);
            v5 = 0;
            v21 = 557788716;
            v22 = 0;
            result = (*(int(__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, v19);
            if(result == 10) // 此处条件成立
            {
                result = (*(int(__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, v18) - 8;
                if((unsigned int)result <= 0xC)
                {
                    v7 = (*(int(__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, v18);
                    v8 = (*(int(__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 676))(v4, v18, 0);
                    if(v7 <= 12)
                    {
                        while(v5 < v7)
                        {
                            v23[v5] = *(_BYTE *)(v8 + v5);
                            ++v5;
                        }
                        v10 = (char *)&v21 - v7;
                        while(v7 != 12)
                        {
                            v23[v7] = v10[v7];
                            ++v7;
                        }
                    }
                    else
                    {
                        for(i = v7 - 12; i != v7; ++i)
                            v23[i] = *(_BYTE *)(v8 + i);
                    }
                    v11 = 0;
                    v12 = (*(int(__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 676))(v4, v19, 0);
                    v13 = 0;
                    while(1)
                    {
                        v11 += (unsigned __int8)v23[v13];
                        result = v11 / 62;
                        if(dest[v11 % 62] != *(_BYTE *)(v12 + v13))  
                            break;  // 此处不能被执行
                        ++v13;
                        if(v13 == 12)
                        {
                            result = (*(int(__fastcall **)(int, _DWORD))(*(_DWORD *)v4 + 24))(v4, "android/widget/Toast");
                            v14 = result;
                            if(result)
                            {
                                result = (*(int(__fastcall **)(int, int, _DWORD, _DWORD))(*(_DWORD *)v4 + 452))(
                                    v4,
                                    result,
                                    "makeText",
                                    "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
                                v15 = result;
                                if(result)
                                {
                                    v16 = *(int(__fastcall **)(_DWORD, _DWORD, _DWORD, _DWORD))(*(_DWORD *)v4 + 456);
                                    (*(void(__fastcall **)(int, _DWORD))(*(_DWORD *)v4 + 668))(v4, "You got it!");
                                    result = v16(v4, v14, v15, v20);
                                    v17 = result;
                                    if(result)
                                    {
                                        result = (*(int(__fastcall **)(int, int, _DWORD, char[4]))(*(_DWORD *)v4 + 132))(
                                            v4,
                                            v14,
                                            "show",
                                            "()V");
                                        if(!v18) // 此处条件成立
                                            result = (*(int(__fastcall **)(int, int, int))(*(_DWORD *)v4 + 244))(v4, v17, result);
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
            }
            if(v25 != _stack_chk_guard)
                _stack_chk_fail(result);
            return result;
        }


        把几个有判断的部分进行了跳转,使其不论什么条件都可以执行到  
        (*(void(__fastcall **)(int, _DWORD))(*(_DWORD *)v4 + 668))(v4, "You got it!");

        经过多次测试和修改,程序还是不能显示Cracked信息,郁闷,如果不是爆破的问题,或许是文件有检验.

        自己对Android方面调试方面接触的太少,经验欠缺,在使用IDA动态调试的时候,IDA经常会崩溃或者异常,或许是作者的反调试处理,所以一直没办法用IDA进行动态跟踪,希望有这方面经验的能够一同探讨。

        因为工作上还有事情要忙,只能先分析到这里,后面有进展再继续吧。。。

免费评分

参与人数 1热心值 +1 收起 理由
杨问天 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

L4Nce 发表于 2014-11-7 17:07
感谢分享分析心得。虽然没有在比赛时完成破解,但期待赛后更完整的分析,52破解有你更精彩。
currwin 发表于 2014-11-7 17:35
开始猜测可能是异或算法,但测试下来解密不正确,后来经过对比分析,猜出了算法:
    解密算法是 :  文件的每个字节取反,然后再加1
                RR = (~BB) + 0x01;
    加密算法是 :  文件的每个字节先减1, 再取反
                BB = ~(RR - 0x01);
  这是求补运算啊
 楼主| codelive 发表于 2014-11-7 17:49
currwin 发表于 2014-11-7 17:35
开始猜测可能是异或算法,但测试下来解密不正确,后来经过对比分析,猜出了算法:
    解密算法是 :  文件 ...

没错,只不过又进行了加1处理。
ThomasKing 发表于 2014-11-7 17:51
额,楼主真心不意思告诉你。 被我fake到了。

免费评分

参与人数 1威望 +5 热心值 +1 收起 理由
L4Nce + 5 + 1 cm大赛防御组一等奖奖励

查看全部评分

 楼主| codelive 发表于 2014-11-7 17:55
ThomasKing 发表于 2014-11-7 17:51
额,楼主真心不意思告诉你。 被我fake到了。

这个CM是不是libmodule.so解密了也没用?因为最终不是调用libmodule.so中的begin_verify,而调用函数是在libverify.so中的数据,对数据进行解密,然后来调用的吧? libmodule.so 就是陷阱,对吧?
 楼主| codelive 发表于 2014-11-7 17:58
ThomasKing 发表于 2014-11-7 17:51
额,楼主真心不意思告诉你。 被我fake到了。

v2 = (void *)open("/data/data/com.example.crackme52/lib/libmodule.so", 0);
后来我分析了,其实这个返回值  v2,一直没有被用到。
所以libmodule.so是陷阱。
不知道是不是我的IDA 配置问题,无法对App进行调试?还请多多指教。
currwin 发表于 2014-11-7 20:40
codelive 发表于 2014-11-7 17:49
没错,只不过又进行了加1处理。

不不,求补的定义就是 按位取反+1
ThomasKing 发表于 2014-11-7 21:08
codelive 发表于 2014-11-7 17:58
v2 = (void *)open("/data/data/com.example.crackme52/lib/libmodule.so", 0);
后来我分析了,其实这个 ...

嗯嗯,大概是这样子的。
ThomasKing 发表于 2014-11-7 21:11
codelive 发表于 2014-11-7 17:55
这个CM是不是libmodule.so解密了也没用?因为最终不是调用libmodule.so中的begin_verify,而调用函数是在l ...

不知道。 防御组能不能把APK的实现原理放出来,如果可以的话,我在详细的写写吧
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 17:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表