2019ISCC Mobile

2019-05-27  本文已影响0人  丿feng

0x01 Mobile1

用Android killer载入找到入口函数

package com.iscc.crackme;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity
  extends AppCompatActivity
{
  static
  {
    System.loadLibrary("native-lib");
  }
  
  private boolean checkFirst(String paramString)
  {
    if (paramString.length() != 16) {
      return false;
    }
    int i = 0;
    while (i < paramString.length()) {
      if (paramString.charAt(i) <= '8')
      {
        if (paramString.charAt(i) < '1') {
          return false;
        }
        i += 1;
      }
      else
      {
        return false;
      }
    }
    return true;
  }
  
  public native boolean checkSecond(String paramString);
  
  protected void onCreate(final Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2131296284);
    paramBundle = (EditText)findViewById(2131165240);
    ((Button)findViewById(2131165218)).setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        paramAnonymousView = paramBundle.getText().toString().trim();
        if ((MainActivity.this.checkFirst(paramAnonymousView)) && (MainActivity.this.checkSecond(paramAnonymousView)))
        {
          Toast.makeText(MainActivity.this, "注册成功!", 0).show();
          return;
        }
        Toast.makeText(MainActivity.this, "注册失败!", 0).show();
      }
    });
  }
}

代码非常简单,对注册码进行两次check,第一次check是

  private boolean checkFirst(String paramString)
  {
    if (paramString.length() != 16) {
      return false;
    }
    int i = 0;
    while (i < paramString.length()) {
      if (paramString.charAt(i) <= '8')
      {
        if (paramString.charAt(i) < '1') {
          return false;
        }
        i += 1;
      }
      else
      {
        return false;
      }
    }
    return true;
  }
  

功能是check注册码是否为16位,以及注册码是否为1~8的数字组合
第二个check函数在native层,分析so文件

 public native boolean checkSecond(String paramString);

用ida64载入64位的so文件,观察入口函数伪代码

char __fastcall Java_com_iscc_crackme_MainActivity_checkSecond(__int64 a1, __int64 a2, __int64 a3)
{
  char result; // al
  char v4; // [rsp+6h] [rbp-8Ah]
  char v5; // [rsp+13h] [rbp-7Dh]
  char v6; // [rsp+40h] [rbp-50h]
  char v7; // [rsp+58h] [rbp-38h]
  char v8; // [rsp+70h] [rbp-20h]
  unsigned __int64 v9; // [rsp+88h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  jstring2str(&v8, a1, a3);
  v5 = 0;
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string(&v7, &v8);
  v4 = 0;
  if ( checkfirst((__int64)&v7) & 1 )
  {
    std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string(&v6, &v8);
    v5 = 1;
    v4 = checkAgain(&v6);
  }
  if ( v5 & 1 )
    std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v6);
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v7);
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v8);
  result = v4 & 1;
  if ( __readfsqword(0x28u) == v9 )
    result = v4 & 1;
  return result;
}

发现也进行了两次check分别是checkfirst 以及 checkAgain,所以只要过了这两个check就完事了
值得注意的是jstring2str函数是将输入转换为字节数组
观察checkfirst函数

__int64 __fastcall checkfirst(__int64 a1)
{
  signed __int64 v2; // [rsp+0h] [rbp-118h]
  signed __int64 v3; // [rsp+18h] [rbp-100h]
  signed int i; // [rsp+30h] [rbp-E8h]
  char v5; // [rsp+37h] [rbp-E1h]

  for ( i = 1; i < 8; ++i )
  {
    if ( *(_BYTE *)a1 & 1 )
      v3 = *(_QWORD *)(a1 + 16);
    else
      v3 = a1 + 1;
    if ( *(_BYTE *)a1 & 1 )
      v2 = *(_QWORD *)(a1 + 16);
    else
      v2 = a1 + 1;
    if ( *(char *)(v3 + i) <= *(char *)(v2 + i - 1) )// 升序
    {
      v5 = 0;
      return v5 & 1;
    }
  }
  v5 = 1;
  return v5 & 1;
}

关键代码是

 if ( *(char *)(v3 + i) <= *(char *)(v2 + i - 1) )// 升序

由于输入只有16位,由此我们可以大胆猜测*(_BYTE *)a1 & 1的值为0

check first
*flag&1==0 && *(flag+i) > *f(lag+i-1) (1<=i<=7) 即前八位为 1 2 3 4 5 6 7 8 

故可以得到注册码前八位是升序的又因注册码是1~8所以可以得到注册码为
12345678********
进入checkAgain函数

char __fastcall checkAgain(__int64 a1)
{
  char result; // al
  signed __int64 v2; // [rsp+10h] [rbp-170h]
  signed __int64 v3; // [rsp+20h] [rbp-160h]
  signed int l; // [rsp+3Ch] [rbp-144h]
  signed int k; // [rsp+40h] [rbp-140h]
  int j; // [rsp+44h] [rbp-13Ch]
  signed int i; // [rsp+48h] [rbp-138h]
  char v8; // [rsp+4Fh] [rbp-131h]
  int v9; // [rsp+130h] [rbp-50h]
  int v10; // [rsp+134h] [rbp-4Ch]
  int v11; // [rsp+148h] [rbp-38h]
  int v12; // [rsp+14Ch] [rbp-34h]
  int v13[10]; // [rsp+150h] [rbp-30h]
  unsigned __int64 v14; // [rsp+178h] [rbp-8h]

  v14 = __readfsqword(0x28u);
  for ( i = 0; i < 8; ++i )
  {
    if ( *(_BYTE *)a1 & 1 )
      v3 = *(_QWORD *)(a1 + 16);
    else
      v3 = a1 + 1;
    v13[i] = *(char *)(v3 + i) - 49;
  }
  for ( j = 0; j < 8; ++j )
  {
    if ( *(_BYTE *)a1 & 1 )
      v2 = *(_QWORD *)(a1 + 16);
    else
      v2 = a1 + 1;
    *(&v9 + j) = *(char *)(v2 + j + 8) - 49;
  }
  if ( v12 + v9 == 5 )
  {
    if ( v11 + v10 == 12 )
    {
      if ( v9 < v12 )
      {
        for ( k = 1; k < 8; ++k )
        {
          for ( l = 0; l < k; ++l )
          {
            if ( v13[l] == v13[k] )
            {
              v8 = 0;
              goto LABEL_34;
            }
            if ( *(&v9 + l) == *(&v9 + k) )
            {
              v8 = 0;
              goto LABEL_34;
            }
            if ( v13[k] - v13[l] == *(&v9 + k) - *(&v9 + l) )
            {
              v8 = 0;
              goto LABEL_34;
            }
            if ( v13[k] - v13[l] == *(&v9 + l) - *(&v9 + k) )
            {
              v8 = 0;
              goto LABEL_34;
            }
          }
        }
        v8 = 1;
      }
      else
      {
        v8 = 0;
      }
    }
    else
    {
      v8 = 0;
    }
  }
  else
  {
    v8 = 0;
  }
LABEL_34:
  result = v8;
  if ( __readfsqword(0x28u) == v14 )
    result = v8 & 1;
  return result;
}

代码颇长,逐步分析
check主要进行以下功能

check again
i -> range(0,8)
(int)v13[i] = *(flag+i) - 49  
(int)v9[i] = *(flag+i) - 49
v9[7] + v9[0] == 5 && v9[6] + v9[1] == 12 && v9[0] < v9[7]
for k in range(1,8)
    for l in range(0,k)
        v13[l] != v13[k]
        v9[l] != v9[k]
        v13[k] - v13[l] != abs(v9[k] - v9[l])

易得注册码后八位也是不相同的
v9[7] + v9[0] == 5 && v9[6] + v9[1] == 12 && v9[0] < v9[7]结合
v13[k] - v13[l] != abs(v9[k] - v9[l])可以约束求解注册码后八位
最终解得注册码为 1234567836275184

Screenshot_2019-05-09-19-46-51.png
上一篇下一篇

猜你喜欢

热点阅读