Android 逆向入门

Android Re 入门

必备工具:

IDA : 反编译.so文件

AndroidKiller: 反编译apk文件及再次编译为apk

jd-gui : 将.jar文件反编译为java代码

dex2jar: 反编译.dex文件

apktool: 能够反编译及回编译apk

夜神模拟器: 运行apk文件

例题 1 [easy-so]

下载

来源:攻防世界

使用夜神模拟器运行该apk文件, 发现需要输入flag

zip解压apk文件, 用 dex2jar反编译 classes.dex文件得到classes-dex2jar.jar,

d2j-dex2jar.bat classes.dex

再用jd-gui打开classes-dex2jar.jar反编译为java代码.

public class MainActivity extends AppCompatActivity {
  protected void onCreate(Bundle paramBundle) {
    super.onCreate(paramBundle);
    setContentView(2131296283);
    ((Button)findViewById(2131165218)).setOnClickListener(new View.OnClickListener() {
          public void onClick(View param1View) {
            if (cyberpeace.CheckString(((EditText)MainActivity.this.findViewById(2131165233)).getText().toString()) == 1) {
              Toast.makeText((Context)MainActivity.this, ", 1).show();
              return;
            } 
            Toast.makeText((Context)MainActivity.this, ", 1).show();
          }
        });
  }
}

在这里可以发现, cyberpeace加载了'cyberpeace'动态库, CheckString函数是native层的, 这个函数的实现就在libcyberpeace.so文件中

package com.testjava.jack.pingan2;

public class cyberpeace {
  static {
    System.loadLibrary("cyberpeace");
  }
  
  public static native int CheckString(String paramString);
}

使用ida打开lib/x86/libcyberpeace.so文件, 找到 _BOOL4 __cdecl Java_com_testjava_jack_pingan2_cyberpeace_CheckString(int a1, int a2, int a3)函数, 该函数实现如下:

_BOOL4 __cdecl Java_com_testjava_jack_pingan2_cyberpeace_CheckString(int a1, int a2, int a3)
{
  const char *get_str; // ST1C_4
  size_t len; // edi
  char *str; // esi
  size_t i; // edi
  char v7; // al
  char v8; // al
  size_t v9; // edi
  char v10; // al

  get_str = (const char *)(*(int (__cdecl **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0); // 从java层获取所输入的字符串
  len = strlen(get_str);
  str = (char *)malloc(len + 1);
  memset(&str[len], 0, len != -1);
  memcpy(str, get_str, len);
  if ( strlen(str) >= 2 ) // 加密1
  {
    i = 0;
    do
    {
      v7 = str[i];
      str[i] = str[i + 16];
      str[i++ + 16] = v7;
    }
    while ( i < strlen(str) >> 1 );
  }
  // 加密2
  v8 = *str;
  if ( *str )
  {
    *str = str[1];
    str[1] = v8;
    if ( strlen(str) >= 3 )
    {
      v9 = 2;
      do
      {
        v10 = str[v9];
        str[v9] = str[v9 + 1];
        str[v9 + 1] = v10;
        v9 += 2;
      }
      while ( v9 < strlen(str) );
    }
  }
  return strcmp(str, "f72c5a36569418a20907b55be5bf95ad") == 0; // 与字符串作比较
}

从以上发现, 对我们所输入的字符串进行了加密, 然后再与f72c5a36569418a20907b55be5bf95ad进行比较. 现在只需逆一下以上代码即可得到flag, exp代码如下

#include <stdio.h>
#include <string.h>

int main(void) {
	char str[] = "f72c5a36569418a20907b55be5bf95ad";
	int i, v7, v8, v9, v10;
	v8 = *str;
	if ( *str ) {
		*str = str[1];
		str[1] = v8;
		if ( strlen(str) >= 3 ) {
      		v9 = 2;
      		do {
        		v10 = str[v9];
        		str[v9] = str[v9 + 1];
        		str[v9 + 1] = v10;
        		v9 += 2;
      		} while ( v9 < strlen(str) );
		}
	}
	
	// 交换 
	if ( strlen(str) >= 2 )	{
   	 i = 0;
    	do {
      		v7 = str[i];
      		str[i] = str[i + 16];
     		str[i++ + 16] = v7;
   	 	} while ( i < strlen(str) >> 1 );
	}
	printf("%s", str);
  
	return 0;
}

运行以上代码即可获取flag

例题 2 [app2]

下载

来源:攻防世界

对输入的账号和密码在SecondActivity类中进行加密判断, 而加密调用了native层的加密函数

protected void onCreate(Bundle paramBundle) {
    super.onCreate(paramBundle);
    setContentView(2130903041);
    Intent intent = getIntent();
    String str1 = intent.getStringExtra("ili");
    String str2 = intent.getStringExtra("lil");
    if (Encryto.doRawData(this, str1 + str2).equals("VEIzd/V2UPYNdn/bxH3Xig==")) {
      intent.setAction("android.test.action.MoniterInstallService");
      intent.setClass((Context)this, MoniterInstallService.class);
      intent.putExtra("company", "tencent");
      intent.putExtra("name", "hacker");
      intent.putExtra("age", 18);
      startActivity(intent);
      startService(intent);
    } 
    SharedPreferences.Editor editor = getSharedPreferences("test", 0).edit();
    editor.putString("ilil", str1);
    editor.putString("lili", str2);
    editor.commit();
  }

IDA反编译doRawData函数, 因为a为对象, 选择a按下y 键 然后输入 JNIEnv*就可以显示对象的函数调用, 如下

int __cdecl doRawData(JNIEnv *a1, int a2, int a3, int a4)
{
  char *v4; // esi
  const char *v5; // ST10_4
  int result; // eax
  char *v7; // esi
  jstring (*v8)(JNIEnv *, const jchar *, jsize); // ST10_4
  size_t v9; // eax
  int v10; // [esp+4h] [ebp-28h]
  int v11; // [esp+8h] [ebp-24h]
  int v12; // [esp+Ch] [ebp-20h]
  int v13; // [esp+10h] [ebp-1Ch]
  char v14; // [esp+14h] [ebp-18h]
  unsigned int v15; // [esp+18h] [ebp-14h]

  v15 = __readgsdword(0x14u);
  if ( checkSignature((int)a1, a2, a3) == 1 )
  {
    v14 = 0;
    v13 = 0x3D3D7965;
    v12 = 0x6B747365;
    v11 = 0x74617369;
    v10 = 0x73696874;
    v4 = (char *)(*a1)->GetStringUTFChars(a1, (jstring)a4, 0);
    v5 = (const char *)AES_128_ECB_PKCS5Padding_Encrypt(v4, (int)&v10);
    (*a1)->ReleaseStringUTFChars(a1, (jstring)a4, v4);
    result = (int)(*a1)->NewStringUTF(a1, v5);
  }
  else
  {
    v7 = UNSIGNATURE[0];
    v8 = (*a1)->NewString;
    v9 = strlen(UNSIGNATURE[0]);
    result = (int)v8(a1, (const jchar *)v7, v9);
  }
  return result;
}

可以发现, 加密方式为aes加密, key 为 v10中的内容.为thisisatestkey==

对VEIzd/V2UPYNdn/bxH3Xig== 解密为aimagetencent, 发现提交flag错误, 重新找另一个字符串,在FileDataActivity类中找到如下.

public class FileDataActivity extends a {
  private TextView c;
  
  protected void onCreate(Bundle paramBundle) {
    super.onCreate(paramBundle);
    setContentView(2130903042);
    this.c = (TextView)findViewById(2131165184);
    this.c.setText(Encryto.decode(this, "9YuQ2dk8CSaCe7DTAmaqAA=="));
  }
}

调用了decode函数, 而decode函数与doRawData实现一样, 直接与之前一样的AES ecb解密, 得到flag

Ph0en1x-100

下载

来源:攻防世界

程序流程, 输入flag

jd-gui反编译如下:

 public void onGoClick(View paramView)
  {
    paramView = this.etFlag.getText().toString();
    if (getSecret(getFlag()).equals(getSecret(encrypt(paramView)))) {
      Toast.makeText(this, "Success", 1).show();
    }
    for (;;)
    {
      return;
      Toast.makeText(this, "Failed", 1).show();
    }
  }

getFlag函数与encrypt函数是native层

  static
  {
    System.loadLibrary("phcm");
  }
  
  public native String encrypt(String paramString);
  
  public native String getFlag();

反编译libphcm.so文件

int __cdecl Java_com_ph0en1x_android_1crackme_MainActivity_encrypt(JNIEnv *a1, int a2, int a3)
{
  size_t i; // esi
  const char *s; // edi

  i = 0;
  for ( s = (*a1)->GetStringUTFChars(a1, (jstring)a3, 0); i < strlen(s); --s[i++] )
    ;
  return (*a1)->NewStringUTF(a1, s);
}

以上加密就是对字符串中的每个字符-1

int __cdecl Java_com_ph0en1x_android_1crackme_MainActivity_getFlag(JNIEnv *a1)
{
  signed int v1; // esi
  char *v2; // edi
  char v3; // al
  int result; // eax
  int v5; // [esp+26h] [ebp-46h]
  int v6; // [esp+2Ah] [ebp-42h]
  int v7; // [esp+2Eh] [ebp-3Eh]
  __int16 v8; // [esp+32h] [ebp-3Ah]
  int v9; // [esp+34h] [ebp-38h]
  int v10; // [esp+38h] [ebp-34h]
  int v11; // [esp+3Ch] [ebp-30h]
  int v12; // [esp+40h] [ebp-2Ch]
  int v13; // [esp+44h] [ebp-28h]
  int v14; // [esp+48h] [ebp-24h]
  int v15; // [esp+4Ch] [ebp-20h]
  int v16; // [esp+50h] [ebp-1Ch]
  int v17; // [esp+54h] [ebp-18h]
  int v18; // [esp+58h] [ebp-14h]
  unsigned int v19; // [esp+5Ch] [ebp-10h]

  v1 = 38;
  v2 = (char *)&v18 + 2;
  v9 = 1279407662;
  v10 = 987807583;
  v19 = __readgsdword(0x14u);
  v11 = 1663091624;
  v12 = 482391945;
  v13 = 683820061;
  v14 = 235072895;
  v15 = 2559534685;
  v16 = 382777269;
  v17 = 4227367757;
  v18 = 4670209;
  v5 = 1819043144;
  v6 = 1750081647;
  v7 = 829318448;
  v8 = 120;
  do
  {
    v3 = *v2--;
    v2[1] = (*((_BYTE *)&v5 + v1-- % 13) ^ (v3 + 1 - *v2)) - 1;
  }
  while ( v1 );
  LOBYTE(v9) = (v9 ^ 0x48) - 1;
  result = (int)(*a1)->NewStringUTF(a1, (const char *)&v9);
  if ( __readgsdword(0x14u) != v19 )
    sub_4B0();
  return result;
}

这个加密稍微有点复杂. 若想获取flag, 先对上面这给逆出来, 在对encrypt加密加密函数给再逆出来即可获得flag, 但是, 我写了一个c脚本, 上面这个有问题, 主要是LOBYTE(v9) = (v9 ^ 0x48) - 1;这个语句不好写.换另一种思路.动态调试(瞎弄半天, 啥也没弄出来), 再换另一种, 就是使用Android killer修改smali源码, 将其getFlag函数的字符串给打印出来, 只需将打印失败逻辑添加一下getFlag函数, 将getFlag字符串覆盖为打印失败的字符串, 复制上面调用getFlag的即可, 再更变一下变量, 如下

 .line 37
    :cond_0
    const-string v1, "Failed"
    
    invoke-virtual {p0}, Lcom/ph0en1x/android_crackme/MainActivity;->getFlag()Ljava/lang/String;

    move-result-object v1   // getFlag()函数的返回值,(字符串)
    
    invoke-static {p0, v1, v3}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v1

    invoke-virtual {v1}, Landroid/widget/Toast;->show()V

然后点击Android->编译, 即可再次编译为apk文件, 安装再nox中运行, 随便输入就会出现ekfz@q2^x/t^fn0mF^6/^rbqanqntfg^E`hq|

再次让每个字符+1就可得到flag

#include <stdio.h>
#include <string.h>

int main(void) {
	char flag[] = "ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|";
	for(int i = 0; i < strlen(flag); ++i) {
		putchar(++flag[i]);
	}

	return 0;
}

app1

 public void onClick(View paramView)
  {
    for (;;)
    {
      try
      {
        str = this.this$0.text.getText().toString();
        PackageInfo localPackageInfo = this.this$0.getPackageManager().getPackageInfo("com.example.yaphetshan.tencentgreat", 16384);
        paramView = localPackageInfo.versionName;
        int i = localPackageInfo.versionCode;
        j = 0;
        if ((j >= str.length()) || (j >= paramView.length())) {
          continue;
        }
        if (str.charAt(j) != (paramView.charAt(j) ^ i))
        {
          Toast.makeText(this.this$0, "再接再励~", 1).show();
          return;
        }
      }
      catch (PackageManager.NameNotFoundException paramView)
      {
        String str;
        int j;
        Toast.makeText(this.this$0, "不要玩小聪明", 1).show();
        continue;
      }
      j++;
      continue;
      if (str.length() != paramView.length()) {
        continue;
      }
      Toast.makeText(this.this$0, "恭喜开启芝麻之门", 1).show();
    }
  }

在BuildConfig class中找到versionName和versionCode

package com.example.yaphetshan.tencentgreat;

public final class BuildConfig
{
  public static final String APPLICATION_ID = "com.example.yaphetshan.tencentgreat";
  public static final String BUILD_TYPE = "debug";
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String FLAVOR = "";
  public static final int VERSION_CODE = 15;
  public static final String VERSION_NAME = "X<cP[?PHNB<P?aj";
}

解密脚本

#include <stdio.h>
#include <string.h>
int main(void) {
	char name[] = "X<cP[?PHNB<P?aj";
	for(int i = 0; i < strlen(name); ++i)
		putchar(name[i] ^15);

	return 0;
}

posted @ 2020-09-05 09:58  i0gan  阅读(659)  评论(0编辑  收藏  举报