吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8725|回复: 14
收起左侧

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

  [复制链接]
〇〇木一 发表于 2014-11-7 15:24
android逆向什么的几乎没接触过啊,都是些基本的操作(看着大大们的文章完成的),分析也是简单的分析,感兴趣的可以看看。写的有什么问题就请见谅了。

一. 简单分析
先用apktools解开看,发现java层获取name和password后直接调用的libverify.so中的verify();
然后用IDA打开libverify.so
[C++] 纯文本查看 复制代码
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; // [sp+0h] [bp-20h]@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,通过之才去真正验证的地方
[C++] 纯文本查看 复制代码
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; // [sp+4h] [bp-7Ch]@4
  char s; // [sp+8h] [bp-78h]@1
  int v8; // [sp+6Ch] [bp-14h]@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具体操作如下:
QQ截图20141107140522.jpg

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

QQ截图20141107141328.jpg

选择运行着的CrackMe52进程
QQ截图20141107141556.jpg
ok,到这里就可以调试了。


三.动态跟踪

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

QQ截图20141107130347.jpg

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

QQ截图20141107130927.jpg

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

QQ截图20141107143309.jpg

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

验证部分:
[C++] 纯文本查看 复制代码
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; // [sp+8h] [bp-80h]@1
  int jPassword; // [sp+Ch] [bp-7Ch]@1
  int v23; // [sp+10h] [bp-78h]@1
  unsigned int v24; // [sp+18h] [bp-70h]@1
  char v25; // [sp+1Ch] [bp-6Ch]@1
  char NamePad[12]; // [sp+20h] [bp-68h]@4
  char charTable[64]; // [sp+2Ch] [bp-5Ch]@1
  int v28; // [sp+6Ch] [bp-1Ch]@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[v9] = *(_BYTE *)(Name + v9);
          ++v9;
        }
        v10 = (char *)&v24 - v7;
        while ( v7 != 12 )                      // ,.?! pad
        {
          NamePad[v7] = v10[v7];
          ++v7;
        }
      }
      else
      {
        do
        {
          NamePad[v5] = *(_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[v13];//累加
v14=62;
        result = ((int (__fastcall *)(int, signed int))sub_492EEF2C)(v11, v14);
        if ( (unsigned __int8)charTable[v14] != *(_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

[C++] 纯文本查看 复制代码
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是余数。
所以这个算法就是
[C++] 纯文本查看 复制代码
int sum=0;
bool ret=true;
for(i = 11 to 0)
{
  sum+=name[i];
  if(charTable[sum%62]!=password[i])
  {
     ret=false;
     break;
  }
}



注册机也就好些了:
[C++] 纯文本查看 复制代码
#include<iostream>
using namespace std;

char *charTable="0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
char *pad=",.?!";
char name[13];
char password[13];
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[i]=pad[i-8];
		}
		name[12]=0;
		for(i=11;i>=0;i--)
		{
			sum+=name[i];
			password[i]=charTable[sum%62];
		}
		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
活到老,学到老!!加油。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-24 10:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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