ISCC2019——mobile01 之用android studio动态调试SO文件

参考:https://www.zhaoj.in/read-5629.html 内含该APK下载链接 感谢作者

  1. 安卓逆向第一步 拖进JEB
    翻到MainActivity函数
package com.iscc.crackme;

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

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }

    public MainActivity() {
        super();
    }

    static boolean access$000(MainActivity arg1, String arg2) {
        return arg1.checkFirst(arg2);
    }

    private boolean checkFirst(String arg5) {
        if(arg5.length() != 16) {
            return 0;
        }

        int v0 = 0;
        while(true) {
            if(v0 >= arg5.length()) {
                return 1;
            }

            if(arg5.charAt(v0) <= 56) {
                if(arg5.charAt(v0) < 49) {
                }
                else {
                    ++v0;
                    continue;
                }
            }

            return 0;
        }

        return 0;
    }

    public native boolean checkSecond(String arg1) {
    }

    protected void onCreate(Bundle arg4) {
        super.onCreate(arg4);
        this.setContentView(0x7F09001C);
        this.findViewById(0x7F070022).setOnClickListener(new View$OnClickListener(this.findViewById(0x7F070038)) {
            public void onClick(View arg5) {
                String v0 = this.val$editText.getText().toString().trim();
                if(!MainActivity.this.checkFirst(v0) || !MainActivity.this.checkSecond(v0)) {
                    Toast.makeText(MainActivity.this, "注册失败!", 0).show();
                }
                else {
                    Toast.makeText(MainActivity.this, "注册成功!", 0).show();
                }
            }
        });
    }
}
  1. 我们发现checkFirst函数简单的做了一个长度检查(16)和限制1~8的数字
  2. 但注册成功需要checkSecond函数也成功 我们注意到这里有一句public native boolean checkSecond(String arg1){}
    好,我们先来学习两个概念
  • 什么是native?
    答:Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。可以将native方法比作Java程序同C程序的接口。
  • 什么是so文件?
    so(shared object)
    这个so文件,有时候是直接被调用的,有时候是会参与到编译中的,也就是说,一个.so文件可能会被多个应用程序用到,因此取名叫share object。
  1. 让我们来找找SO文件
    ISCC2019——mobile01 之用android studio动态调试SO文件_第1张图片
    右键将其导出 我喜欢用IDA来反编译~
  2. 拖进IDA 函数很多 搜check 这三个是函数的主体 一个个看吧
    ISCC2019——mobile01 之用android studio动态调试SO文件_第2张图片
  3. 先看我们之前调用的checkSecond
int __cdecl Java_com_iscc_crackme_MainActivity_checkSecond(int a1, int a2, int a3)
{
  bool v3; // zf@5
  signed int v5; // [sp-4h] [bp-80h]@1
  int v6; // [sp+0h] [bp-7Ch]@1
  int v7; // [sp+4h] [bp-78h]@1
  unsigned __int8 v8; // [sp+10h] [bp-6Ch]@5
  char v9; // [sp+11h] [bp-6Bh]@2
  char v10; // [sp+12h] [bp-6Ah]@1
  char v11; // [sp+13h] [bp-69h]@1
  int v12; // [sp+14h] [bp-68h]@1
  int v13; // [sp+18h] [bp-64h]@1
  int v14; // [sp+1Ch] [bp-60h]@1
  int *v15; // [sp+20h] [bp-5Ch]@1
  signed int v16; // [sp+24h] [bp-58h]@1
  char v17; // [sp+2Bh] [bp-51h]@1
  unsigned __int8 v18; // [sp+37h] [bp-45h]@3
  int v19; // [sp+38h] [bp-44h]@2
  int v20; // [sp+48h] [bp-34h]@1
  int v21; // [sp+58h] [bp-24h]@1
  int v22; // [sp+68h] [bp-14h]@1

  v5 = 35617;
  v22 = *MK_FP(__GS__, 20);//MK_FP是一个宏。功能是做段基址加上偏移地址的运算,也就是取实际地址
  v16 = 208460;
  *(int *)((char *)&dword_32E54 + (_DWORD)&v6 - 208460) = a3;
  *(int *)((char *)&dword_32E50 + (_DWORD)&v6 - 208460) = a1;
  v15 = &v21;
  v14 = a3;
  v13 = a2;
  v12 = a1;
  jstring2str(&v21);
  v17 = 0;
  *(int *)((char *)&dword_32E50 + (_DWORD)&v6 - 208460) = (int)v15;
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string(&v20, v7);
  v11 = checkfirst(&v20);//从上一句看v20是一个字符串 通过checkfirst函数对这个字符串做个检查  注意到是小写的first
  v10 = 0;
  if ( v11 & 1 )//如果checkfirst通过继续checkAgain
  {
    *(int *)((char *)&dword_32E50 + (_DWORD)&v6 - 208460) = (int)&v21;
    std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::basic_string(&v19, v7);
    v17 = 1;
    v9 = checkAgain((int)&v19);//跟进去
    v10 = v9;
  }
  v18 = v10 & 1;
  if ( v17 & 1 )
    std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v19);
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v20);
  std::__ndk1::basic_string<char,std::__ndk1::char_traits<char>,std::__ndk1::allocator<char>>::~basic_string(&v21);
  v3 = *MK_FP(__GS__, 20) == v22;
  v8 = v18;
  if ( !v3 )
    JUMPOUT(*(_DWORD *)algn_8D04);
  return v8;
}
int __cdecl checkfirst(int a1)
{
  int v2; // [sp+4h] [bp-94h]@7
  int v3; // [sp+10h] [bp-88h]@4
  signed int i; // [sp+1Ch] [bp-7Ch]@1
  char v5; // [sp+23h] [bp-75h]@10

  for ( i = 1; i < 8; ++i )
  {
    if ( *(_BYTE *)a1 & 1 )//第一字节是奇数
      v3 = *(_DWORD *)(a1 + 8);//移动到下一个字节
    else//否则会偏移1位 很麻烦 所以我们只要让上面这个条件成立也是可以的
      v3 = a1 + 1;
    if ( *(_BYTE *)a1 & 1 )
      v2 = *(_DWORD *)(a1 + 8);
    else
      v2 = a1 + 1;
    //很难理解v2 v3的操作 但有一点可以肯定 经过这个if else之后两者的值是一样的
    //然后我们注意到下面做了一个字符的比较要求后一个大于前一个 否则返回false 
    //比较了7次 为 12345678
    if ( *(_BYTE *)(v3 + i) <= (signed int)*(_BYTE *)(v2 + i - 1) )
    {
      v5 = 0;
      return v5 & 1;//即return 0;
    }
  }
  v5 = 1;
  return v5 & 1;//即return 1;
}
  1. 大致逻辑有了 检查字符串前8(也有可能是后8,代码逻辑看不出)为12345678 之后会在checkAgain函数中再次做一个复杂的检查 静态调试继续深入十分费力 有什么好的办法呢? 就是动态调试了
  2. 逻辑:构造一个12345678+长度为8(每位1-8)的字符串让checkAgain检查如果通过就是答案 8^8=1.7kw次运算 合理!
  3. 我们用android studio做这件事情 配置详情可直接参考原文章
    MainActivity.java代码如下
package com.iscc.crackme;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
    static
    {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toast.makeText(this,"123",Toast.LENGTH_SHORT).show();
        int cnt = 0;
        int flag = 0;
        for(int i=1;i<=8;++i){
            if(flag == 1) break;
            for(int j=1;j<=8;++j){
                for(int q=1;q<=8;++q){
                    for(int k=1;k<=8;++k){
                        for(int x=1;x<=8;++x){
                            for(int y=1;y<=8;++y){
                                for(int z=1;z<=8;++z){
                                    for(int m=1;m<=8;++m){
                                        final String paramString = "12345678" + i + j + q + k + x + y + z + m;
                                        if(checkSecond(paramString)){
                                            System.out.println(paramString+":"+checkSecond(paramString));
                                            flag = 1;
                                        }
                                        cnt += 1;
                                        if(cnt%100000==0){//用来观测进行到哪里了
                                            System.out.println(paramString);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    public native boolean checkSecond(String paramString);
}

可以在log日志中找到答案

你可能感兴趣的:(安卓)