安卓JNI动态注册分析

打开某ctf的apk运行结果如下
安卓JNI动态注册分析_第1张图片
使用jadx打开apk后,查看关键代码如下:

package org.isclab.iscc;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
    private Button button;
    private EditText editFlag;

    public native int checkFlag(byte[] bArr, int i);

    static {
        System.loadLibrary("ISCC");
    }

    /* access modifiers changed from: protected */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.editFlag = (EditText) findViewById(R.id.textView3);
        this.button = (Button) findViewById(R.id.button);
        this.button.setOnClickListener(new View.OnClickListener() {
            /* class org.isclab.iscc.MainActivity.AnonymousClass1 */

            public void onClick(View v) {
                String flag = Digest.encode(MainActivity.this.editFlag.getText().toString()).trim();
                Log.i("ISCC", flag);
                if (MainActivity.this.checkFlag(flag.getBytes(), flag.length()) == 1) {
                    Toast.makeText(MainActivity.this, "Flag验证成功!", 0).show();
                } else {
                    Toast.makeText(MainActivity.this, "Flag验证失败!", 0).show();
                }
            }
        });
    }

    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            return true;
        }

去看checkflag函数

package org.isclab.iscc;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
    private Button button;
    private EditText editFlag;

    public native int checkFlag(byte[] bArr, int i);

    static {
        System.loadLibrary("ISCC");
    }

    /* access modifiers changed from: protected */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.editFlag = (EditText) findViewById(R.id.textView3);
        this.button = (Button) findViewById(R.id.button);
        this.button.setOnClickListener(new View.OnClickListener() {
            /* class org.isclab.iscc.MainActivity.AnonymousClass1 */

            public void onClick(View v) {
                String flag = Digest.encode(MainActivity.this.editFlag.getText().toString()).trim();
                Log.i("ISCC", flag);
                if (MainActivity.this.checkFlag(flag.getBytes(), flag.length()) == 1) {
                    Toast.makeText(MainActivity.this, "Flag验证成功!", 0).show();
                } else {
                    Toast.makeText(MainActivity.this, "Flag验证失败!", 0).show();
                }
            }
        });
    }

    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

出现native明显so层 IDA看下
安卓JNI动态注册分析_第2张图片
IDA载入so文件,发现没有函数名类似“org_isclab_iscc”,那就是动态注册了。查看F5之后的JNI_OnLoad,修正参数后代码如下:
安卓JNI动态注册分析_第3张图片

signed int __cdecl JNI_OnLoad(int a1)
{
  int v1; // eax
  signed int v2; // edx
  int v4; // [esp+1Ch] [ebp-10h]

  v4 = 0;
  v1 = (*(int (__cdecl **)(int, int *, signed int))(*(_DWORD *)a1 + 24))(a1, &v4, 65540);
  v2 = -1;
  if ( !v1 )
  {
    register_ndk_load(v4);
    v2 = 65540;
  }
  return v2;
}

去看register_ndk_load(v4);
安卓JNI动态注册分析_第4张图片
安卓JNI动态注册分析_第5张图片
看下地址调用了这个:

off_2020

安卓JNI动态注册分析_第6张图片
可以看到调用了Checkflag 函数
安卓JNI动态注册分析_第7张图片
整体程序到了这里
安卓JNI动态注册分析_第8张图片
安卓JNI动态注册分析_第9张图片
F5c代码如下

BOOL4 __cdecl native_checkFlag(int a1, int a2, int a3, size_t size)
{
  char *v4; // ebp
  unsigned int v5; // edi
  bool v6; // cf
  bool v7; // zf
  unsigned int v8; // eax
  size_t v9; // edx
  char v10; // ST2B_1
  signed int v11; // ecx
  const char *v12; // edi
  _BYTE *v13; // esi
  int v14; // esi
  void *src; // [esp+2Ch] [ebp-20h]

  src = malloc(size);
  (*(void (__cdecl **)(int, int, _DWORD, size_t, void *))(*(_DWORD *)a1 + 800))(a1, a3, 0, size, src);
  v4 = (char *)malloc(size + 1);
  memset(v4, 0, size + 1);
  memcpy(v4, src, size);
  v5 = (signed int)size / 2;
  v6 = 0;
  v7 = (signed int)size / 2 == 0;
  if ( (signed int)size / 2 > 0 )
  {
    v8 = 0;
    v9 = (size_t)&v4[size];
    do
    {
      v10 = v4[v8] - 5;
      v4[v8++] = *(_BYTE *)(v9 - 1);
      v6 = v8 < v5;
      v7 = v8 == v5;
      *(_BYTE *)(v9-- - 1) = v10;
    }
    while ( v8 != v5 );
  }
  v11 = 29;
  v12 = "=0HWYl1SE5UQWFfN?I+PEo.UcshU";
  v13 = v4;
  v4[size] = 0;
  do
  {
    if ( !v11 )
      break;
    v6 = *v13 < (const unsigned __int8)*v12;
    v7 = *v13++ == *v12++;
    --v11;
  }
  while ( v7 );
  v14 = (char)((!v6 && !v7) - v6);
  free(v4);
  free(src);
  return v14 == 0;
}

逻辑很简单,遍历前一半的数据,每一个加5,然后跟对称的数据进行交换。我们还原的代码就是从后面开始遍历,遍历后一半,然后跟对称的数据进行交换,编写java代码如下:

public class decode{    
public static void main(String[] args) {
        char Key[] = "=0HWYl1SE5UQWFfN?I+PEo.UcshU".toCharArray();
        int len = Key.length;
        for (int i = len - 1; i >= len / 2; i--) {
            Key[i] += 5;
            char temp = Key[len - i - 1];
            Key[len - i - 1] = Key[i];
            Key[i] = temp;
        }
        System.out.println(Key);
    }
}

在这里插入图片描述
安卓JNI动态注册分析_第10张图片
安卓JNI动态注册分析_第11张图片

你可能感兴趣的:(CTF,安卓逆向,安卓动态注册逆向分析,安卓逆向,动态注册实战分析)