〇〇木一 发表于 2014-11-7 15:24

【分析】【吾爱破解2014CrackMe大赛】【第十三组】

android逆向什么的几乎没接触过啊{:1_906:},都是些基本的操作(看着大大们的文章完成的),分析也是简单的分析,感兴趣的可以看看。写的有什么问题就请见谅了。

一. 简单分析
先用apktools解开看,发现java层获取name和password后直接调用的libverify.so中的verify();
然后用ida打开libverify.so

unsigned __int64 __fastcall verify(unsigned int a1, int a2, int a3, unsigned int a4)
{
unsigned int v4; // r5@1
int v5; // r6@1
int v6; // r7@1
unsigned __int64 v8; // @1

v8 = __PAIR__(a4, a1);
v4 = a1;
v5 = a2;
v6 = a3;
if ( !sub_12A0() )
    ((void (__fastcall *)(unsigned int, int, int, _DWORD))*off_4F64)(v4, v5, v6, HIDWORD(v8));
return v8;
}

看见他先调用了sub_12A0,通过之才去真正验证的地方

signed int __fastcall sub_12A0()
{
void *v0; // r6@1
signed int v1; // r4@1
__pid_t v2; // r0@1
FILE *v3; // r5@1
char *v4; // r0@3
signed int result; // r0@9
int v6; // @4
char s; // @1
int v8; // @1

v0 = off_4F60;
v1 = 1;
v8 = *(_DWORD *)off_4F60;
memset(&s, 0, 0x64u);
v2 = getpid();
sprintf(&s, "/proc/%d/status", v2);
v3 = fopen(&s, "r");
if ( v3 )
{
    while ( fgets(&s, 99, v3) )
    {
      v4 = strstr(&s, "TracerPid:");
      if ( v4 )
      {
      sscanf(v4 + 10, "%d", &v6);
      v1 = v6;
      if ( !v6 )
          goto LABEL_8;
      break;
      }
    }
    v1 = 1;
LABEL_8:
    fclose(v3);
}
result = v1;
if ( v8 != *(_DWORD *)v0 )
    _stack_chk_fail(v1);
return result;
}


我对linux了解的也不多,但是看见TracerPid就知道是在检测调试器了(跟踪的进程嘛)。
然后去看看真正验证的地方,发现什么都没有。想想也知道,肯定是加密了。需要调试安卓,想想就头疼。
查了几篇看雪、吾爱上的文章,还是决定要试试,虽然没有可以用的手机,但虚拟机也完全没问题。


二.ida调试
需要下载android sdk(就用个虚拟机),还有就是看雪前辈给的ida6.5
用到的adb命令在sdk/platform-tools上有,调试用的android_server在ida的dbgsrv文件夹下。
先打开虚拟机,安装apk,可以用adb install CrackMe.apk 来安装。
之后打开cmd具体操作如下:


开启监听后,不要关闭cmd。打开CrackMe程序,打开ida,Debugger->Attcah->Remote ARMLinux/Android debugger



选择运行着的CrackMe52进程

ok,到这里就可以调试了。


三.动态跟踪

打开Modules选择libverify.so,找到verify,并下好断点。



运行到此处不让它跳(前面讲得检测调试器)



之后就进入真正验证的地方了



四.算法分析(截图或者代码是分几次搞的,地址不一样)

验证部分:
int __fastcall sub_492EECD4(int a1, int a2, int a3, int a4)
{
int v4; // r5@1
int v5; // r6@1
int result; // r0@1
int v7; // r4@3
int Name; // r0@3
int v9; // r3@3
char *v10; // r3@8
int v11; // r6@11
int Password; // r7@11
signed int v13; // r4@11
int v14; // r1@12
char v15; // cf@13
int v16; // r6@14
int v17; // r4@15
int (__fastcall *v18)(_DWORD, _DWORD, _DWORD, _DWORD); // r7@16
int v19; // r7@16
int v20; // r0@17
int jName; // @1
int jPassword; // @1
int v23; // @1
unsigned int v24; // @1
char v25; // @1
char NamePad; // @4
char charTable; // @1
int v28; // @1

v23 = a2;
jPassword = a4;
v4 = a1;
jName = a3;
v28 = _stack_chk_guard;
memcpy_0(charTable, "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm", 63);
v5 = 0;
v24 = 0x213F2E2Cu;
v25 = 0;
result = (*(int (__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, jPassword);// GetStringLength
if ( result == 12 )
{
    result = (*(int (__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, jName) - 8;// GetStringLength
    if ( (unsigned int)result <= 0xC )
    {
      v7 = (*(int (__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, jName);// GetStringLength
      Name = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 676))(v4, jName, 0);// GetStringUTFChars
      v9 = 0;
      if ( v7 <= 12 )
      {
      while ( v9 < v7 )
      {
          NamePad = *(_BYTE *)(Name + v9);
          ++v9;
      }
      v10 = (char *)&v24 - v7;
      while ( v7 != 12 )                      // ,.?! pad
      {
          NamePad = v10;
          ++v7;
      }
      }
      else
      {
      do
      {
          NamePad = *(_BYTE *)(Name + v5);
          ++v5;
      }
      while ( v5 != 12 );
      }
      v11 = 0;
      Password = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 676))(v4, jPassword, 0);// GetStringUTFChars
      v13 = 11;//从最后开始循环
      while ( 1 )
      {
      v11 += (unsigned __int8)NamePad;//累加
v14=62;
      result = ((int (__fastcall *)(int, signed int))sub_492EEF2C)(v11, v14);
      if ( (unsigned __int8)charTable != *(_BYTE *)(Password + v13) )
          break;
      v15 = (unsigned int)v13-- >= 1;
      if ( !v15 )
      {
          result = (*(int (__fastcall **)(int, _DWORD))(*(_DWORD *)v4 + 24))(v4, "android/widget/Toast");
          v16 = 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;");
            v17 = result;
            if ( result )
            {
            v18 = *(int (__fastcall **)(_DWORD, _DWORD, _DWORD, _DWORD))(*(_DWORD *)v4 + 456);
            (*(void (__fastcall **)(int, _DWORD))(*(_DWORD *)v4 + 668))(v4, "Congratulation! You crack it!");
            result = v18(v4, v16, v17, v23);
            v19 = result;
            if ( result )
            {
                v20 = (*(int (__fastcall **)(int, int, _DWORD, _UNKNOWN *))(*(_DWORD *)v4 + 132))(
                        v4,
                        v16,
                        "show",
                        &unk_492F053C);
                result = (*(int (__fastcall **)(int, int, int))(*(_DWORD *)v4 + 244))(v4, v19, v20);
            }
            }
          }
          break;
      }
      }
    }
}
if ( v28 != _stack_chk_guard )
    result = ((int (__fastcall *)(int))unk_492EEC68)(result);
return result;
}

其中需要知道sub_492EEF2C是干什么的,就跟进去看了下,里面还会调用sub_492EEE58

int __fastcall sub_492EEE58(int result, unsigned int a2)
{
char v2; // nf@0
int v3; // r12@1
int v4; // r3@4
char v5; // r0@8
unsigned int v6; // r1@8
unsigned int v7; // r2@8
char v8; // zf@17

v3 = result ^ a2;
if ( v2 )
    a2 = -a2;
if ( a2 == 1 )
{
    if ( (v3 ^ result) < 0 )
      result = -result;
}
else
{
    v4 = result;
    if ( result < 0 )
      v4 = -result;
    if ( v4 <= a2 )
    {
      if ( v4 < a2 )
      result = 0;
      if ( v4 == a2 )
      result = (v3 >> 31) | 1;
    }
    else if ( a2 & (a2 - 1) )
    {
      v5 = __clz(a2) - __clz(v4);
      v6 = a2 << v5;
      v7 = 1 << v5;
      result = 0;
      while ( 1 )
      {
      if ( v4 >= v6 )
      {
          v4 -= v6;
          result |= v7;
      }
      if ( v4 >= v6 >> 1 )
      {
          v4 -= v6 >> 1;
          result |= v7 >> 1;
      }
      if ( v4 >= v6 >> 2 )
      {
          v4 -= v6 >> 2;
          result |= v7 >> 2;
      }
      if ( v4 >= v6 >> 3 )
      {
          v4 -= v6 >> 3;
          result |= v7 >> 3;
      }
      v8 = v4 == 0;
      if ( v4 )
      {
          v7 >>= 4;
          v8 = v7 == 0;
      }
      if ( v8 )
          break;
      v6 >>= 4;
      }
      if ( v3 < 0 )
      result = -result;
    }
    else
    {
      result = (unsigned int)v4 >> (31 - __clz(a2));
      if ( v3 < 0 )
      result = -result;
    }
}
return result;
}

好吧,这个怎么看都想是稍微有点复杂的算法。可是...
分析了下发现这个就是个a/b,tm就是个除法,在百度了下ARM汇编没有除,就是这么干的...
之后就知道了sub_492EEF2C也就是个除法,返回之后R1是商,R2是余数。
所以这个算法就是
int sum=0;
bool ret=true;
for(i = 11 to 0)
{
sum+=name;
if(charTable!=password)
{
   ret=false;
   break;
}
}


注册机也就好些了:
#include<iostream>
using namespace std;

char *charTable="0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
char *pad=",.?!";
char name;
char password;
int main()
{
        memset(name,0,13);
        memset(password,0,13);
        cout<<"请输入用户名:"<<endl;
        cin>>name;
        int len=strlen(name);
        if(len>=8 && len<=12)
        {
                int i=0,sum=0;
                for(i=len;i<12;i++)
                {
                        name=pad;
                }
                name=0;
                for(i=11;i>=0;i--)
                {
                        sum+=name;
                        password=charTable;
                }
                cout<<"注册码为:"<<endl<<password<<endl;
        }
        else
        {
                cout<<"长度必须为8~12"<<endl;
        }

        system("pause");
        return 0;
}

两组key:
12345678
dmWDVyfx0ONB
52pojie0
4RHwg6Hd0ONB

以上仅是简单的分析,对什么加壳脱壳的还一概不了解(题主大大在看雪有几篇精华文章,可惜还看不懂)。













bess 发表于 2014-11-13 11:00

@〇〇木一   用IDA打开libverify.so文件,之后你写的代码是你自己分析写出来的,还是IDA反编译出来的,我知道IDA可以进行反编译,但是这种LOAD:000012A0               PUSH    {R4-R6,LR}也可以进行反编译吗?我的F5都不可以进行这个的反编译,求指导

〇〇木一 发表于 2014-11-13 18:52

bess 发表于 2014-11-13 11:00
@〇〇木一   用IDA打开libverify.so文件,之后你写的代码是你自己分析写出来的,还是IDA反编译出来的,我知 ...

http://bbs.pediy.com/showthread.php?t=191169

小清子、 发表于 2016-3-13 22:56

楼主好强大,学习一下

1294279706 发表于 2016-3-14 12:38

感谢分享!!!!!!!

joshdexipi 发表于 2016-3-19 13:30

多看看,再学学

isoul 发表于 2016-3-30 22:45

验证的那一段不是太懂

wincorry 发表于 2016-4-28 12:20

楼主好强大,学习一下

wincorry 发表于 2016-4-28 20:18

膜拜大神

o6o7o5 发表于 2016-5-2 19:02

活到老,学到老!!加油。
页: [1] 2
查看完整版本: 【分析】【吾爱破解2014CrackMe大赛】【第十三组】