0x00拖进模拟器查看:
0x01上dex2jar:
查看MainActivity.class:
package com.tencent.testvuln;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
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;
import com.tencent.testvuln.c.SignatureTool;
@SuppressLint({"ShowToast"})
public class MainActivity extends Activity implements View.OnClickListener {
private Button a;
private Handler b = null;
private EditText c;
private EditText d;
public void onClick(View paramView) {
switch (paramView.getId()) {
default:
return;
case 2131165187:
break;
}
if (this.c.getText().length() == 0 || this.d.getText().length() == 0) {
Toast.makeText((Context)this, ", 1).show();
return;
}
String str1 = this.c.getText().toString();
String str2 = this.d.getText().toString();
Log.e("test", str1 + " test2 = " + str2);
Intent intent = new Intent((Context)this, SecondActivity.class);
intent.putExtra("ili", str1);
intent.putExtra("lil", str2);
startActivity(intent);
}
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(2130903040);
this.a = (Button)findViewById(2131165187);
this.a.setOnClickListener(this);
this.c = (EditText)findViewById(2131165185);
this.d = (EditText)findViewById(2131165186);
SharedPreferences.Editor editor = getSharedPreferences("test", 0).edit();
editor.putLong("ili", System.currentTimeMillis());
editor.commit();
Log.d("hashcode", SignatureTool.getSignature((Context)this) + "");
}
public boolean onCreateOptionsMenu(Menu paramMenu) {
getMenuInflater().inflate(2131099648, paramMenu);
return true;
}
public boolean onOptionsItemSelected(MenuItem paramMenuItem) { return (paramMenuItem.getItemId() == 2131165188) ? true : super.onOptionsItemSelected(paramMenuItem); }
}
我们查看onclick方法:
public void onClick(View paramView) {
switch (paramView.getId()) {
default:
return;
case 2131165187:
break;
}
//c和d分别是两个输入框
//获取两个输入框的内容后做是否为空校验
if (this.c.getText().length() == 0 || this.d.getText().length() == 0) {
Toast.makeText((Context)this, ", 1).show();
return;
}
String str1 = this.c.getText().toString();
String str2 = this.d.getText().toString();
Log.e("test", str1 + " test2 = " + str2);
//空校验通过,通过startActivity()启动一个新的activity:SecondActivity
Intent intent = new Intent((Context)this, SecondActivity.class);
intent.putExtra("ili", str1);
intent.putExtra("lil", str2);
startActivity(intent);
}
一波分析之后我们查看SecondActivity.class:,同时注意,SecondActivity初始化之后带入两个参数ili和lli,ili是变量c,lli是d。
package com.tencent.testvuln;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.tencent.testvuln.c.Encryto;
public class SecondActivity extends a {
private BroadcastReceiver c = new BroadcastReceiver(this) {
public void onReceive(Context param1Context, Intent param1Intent) {
Toast.makeText(param1Context, "myReceiver receive", 0).show();
String str = param1Intent.getAction();
if (param1Context.getPackageName().equals(str));
}
};
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();
}
public boolean onCreateOptionsMenu(Menu paramMenu) {
getMenuInflater().inflate(2131099648, paramMenu);
return true;
}
public boolean onOptionsItemSelected(MenuItem paramMenuItem) { return (paramMenuItem.getItemId() == 2131165188) ? true : super.onOptionsItemSelected(paramMenuItem); }
}
老规矩,查看onCreate方法:
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(2130903041);
Intent intent = getIntent(); //获取到传入的intent
String str1 = intent.getStringExtra("ili"); //读取c
String str2 = intent.getStringExtra("lil"); //读取d
//判断条件是c+d经过本地函数处理之后内容是否等于VEIzd/V2UPYNdn/bxH3Xig==
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();
}
这里有一个判断:判断条件是c+d经过本地函数处理之后内容是否等于VEIzd/V2UPYNdn/bxH3Xig==,如果判断结果为真则会开启一个新的activity: MoniterInstallService
我们先来研究这个判断条件内的函数,看看假如输入的c+d等于"VEIzd/V2UPYNdn/bxH3Xig=="会发生什么:
查看Encryto.doRawData(),发现是一个JNI方法。
上IDA,将so文件拖进IDA后找到j_doRawData之后无脑F5:
int __fastcall doRawData(int a1, int a2, int a3, int a4)
{
int v4; // r4
int v5; // r9
int v6; // r6
int v7; // r8
int result; // r0
int (__fastcall *v9)(int, char *, size_t); // r6
char *v10; // r5
size_t v11; // r2
int v12; // [sp+0h] [bp-28h]
int v13; // [sp+4h] [bp-24h]
int v14; // [sp+8h] [bp-20h]
int v15; // [sp+Ch] [bp-1Ch]
char v16; // [sp+10h] [bp-18h]
int v17; // [sp+18h] [bp-10h]
v4 = a1;
v5 = a4;
if ( j_checkSignature(a1) != 1 )
goto LABEL_7;
v12 = *(_DWORD *)"thisisatestkey==";
v13 = *(_DWORD *)"isatestkey==";
v14 = *(_DWORD *)"estkey==";
v15 = *(_DWORD *)"ey==";
v16 = aThisisatestkey[16];
v6 = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 676))(v4, v5, 0);
v7 = j_AES_128_ECB_PKCS5Padding_Encrypt(v6, (int)&v12);
(*(void (__fastcall **)(int, int, int))(*(_DWORD *)v4 + 680))(v4, v5, v6);
result = (*(int (__fastcall **)(int, int))(*(_DWORD *)v4 + 668))(v4, v7);
if ( _stack_chk_guard != v17 )
{
LABEL_7:
do
{
v9 = *(int (__fastcall **)(int, char *, size_t))(*(_DWORD *)v4 + 652);
v10 = UNSIGNATURE[0];
v11 = strlen(UNSIGNATURE[0]);
}
while ( _stack_chk_guard != v17 );
result = v9(v4, v10, v11);
}
return result;
}
我们可以大胆的猜测(具体是为什么我也不知道),字符指针v6就是传入的字符串。
然后可以看到,输出是v7=j_AES_128_ECB_PKCS5Padding_Encrypt(v6, (int)&v12);
根据目测函数名,推测是128位的AES加密,模式是ECB,填充方式是PKCS5Padding。
然后我们观察到这个函数的v12是密钥,且前面有操作strcpy(v12,"thisisatestkey=="),因此可以知道密钥是v12.
然后打开在线解密网站:在线AES加解密
输入:
拿到明文为aimagetencent。
于是根据之前分析的,c+d拼接成aimagetencent。
不妨大胆假设c="aimage" ,d="tencent",输入一下试试看会发生什么。
我们在第一个输入:tencent
第二个输入框输入:aimage,
神奇的一幕出现了:
开始我认为是我的模拟器版本只有4.4过低,于是用我的真机测试,依然闪退。
事情到此陷入了僵局。
分析一波判断结果为true之后发生的事情:
开启一个新的MoniterInstallService,点击进去这个MoniterInstallService看下代码:
package com.tencent.testvuln;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
public class MoniterInstallService extends Service {
private long a = 0L;
private String b = null;
private String c = null;
private c d = null;
private BroadcastReceiver e = new BroadcastReceiver(this) {
public void onReceive(Context param1Context, Intent param1Intent) {
this.a.b("");
try {
Thread.sleep(1000L);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
param1Intent = new Intent("android.setting.word");
param1Intent.putExtra("1", MoniterInstallService.a(this.a));
param1Intent.putExtra("2", MoniterInstallService.b(this.a));
param1Context.sendBroadcast(param1Intent);
}
};
private Handler f = new Handler(this) {
public void handleMessage(Message param1Message) {
switch (param1Message.what) {
default:
return;
case 0:
break;
}
this.a.a("");
}
};
void a(String paramString) {
paramString = Environment.getExternalStorageDirectory().getPath() + "/crash/";
if (this.d == null)
this.d = new c(paramString);
this.d.startWatching();
}
void b(String paramString) {
if (this.d != null)
this.d.stopWatching();
}
public IBinder onBind(Intent paramIntent) { return null; }
public void onCreate() {
super.onCreate();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.tencent.testvul.service.MoniterFile");
intentFilter.setPriority(2147483647);
registerReceiver(this.e, intentFilter);
}
public void onDestroy() { super.onDestroy(); }
public void onStart(Intent paramIntent, int paramInt) {
super.onStart(paramIntent, paramInt);
this.b = paramIntent.getStringExtra("ili");
this.c = paramIntent.getStringExtra("lil");
(new Thread(this) {
public void run() {
Message message = new Message();
message.what = 0;
MoniterInstallService.c(this.a).sendMessage(message);
}
}).start();
}
}
大概扫了扫发现是一个广播开启和接收的Activity,和本次CTF关系应该不大,因此这个暂时不管它,继续往下分析SecondActivity的代码。
SharedPreferences.Editor editor = getSharedPreferences("test", 0).edit();
editor.putString("ilil", str1);
editor.putString("lili", str2);
editor.commit();
.............发现这代码它也没干啥事啊,就存储了一下输入的变量。
事情再次陷入了僵局。。。。。。。
然后发现在一个从未被使用过的类(之所以说它从未被使用过是因为如下:search搜索该类只找到它自己): FileDataActivity中发现了如下神秘代码:
package com.tencent.testvuln;
import android.os.Bundle;
import android.widget.TextView;
import com.tencent.testvuln.c.Encryto;
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=="));
}
}
然后我们拿着串神秘代码去AES解密,就拿到了flag:
-------end----------