美文网首页
2019ISCC Mobile

2019ISCC Mobile

作者: 丿feng | 来源:发表于2019-05-27 14:45 被阅读0次

    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

    相关文章

      网友评论

          本文标题:2019ISCC Mobile

          本文链接:https://www.haomeiwen.com/subject/pvsnoqtx.html