这个比赛结束没多久,个人也就随便做了玩玩,两道题其实都不难,尤其是看上去比较难的第二道题,是需要靠投机取巧的
题目一:
不得不说界面真渣,不过现在真正搞安卓的都是c大牛,都不玩java的
接下来就需要自己去分析了,第一步当然看看有没有壳,没有就直接反编译看吧,个人比较喜欢jadx,下面都用jadx作为java反编译工具
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView((int) R.layout.activity_main);
ApplicationInfo applicationInfo = getApplicationInfo();
int i = applicationInfo.flags & 2;
applicationInfo.flags = i;
if (i != 0) {
p();
((Button) findViewById(R.id.sureButton)).setOnClickListener(new d(this)); //d方法为触发点击事件的函数
} else {
p();
((Button) findViewById(R.id.sureButton)).setOnClickListener(new d(this));
}
}
private void p() {
try {
InputStream open = getResources().getAssets().open("url.png"); //读取一张图片的值,从144位开始,读取16位数据,保存到全局变量v中
int available = open.available();
Object obj = new byte[available];
open.read(obj, 0, available);
Object obj2 = new byte[16];
System.arraycopy(obj, 144, obj2, 0, 16);
this.v = new String(obj2, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean a(String str, String str2) {
return new c().a(str, str2).equals(new String(new byte[]{(byte) 21, (byte) -93, (byte) -68, (byte) -94, (byte) 86, (byte) 117, (byte) -19, (byte) -68, (byte) -92, (byte) 33, (byte) 50, (byte) 118, (byte) 16, (byte) 13, (byte) 1, (byte) -15, (byte) -13, (byte) 3, (byte) 4, (byte) 103, (byte) -18, (byte) 81, (byte) 30, (byte) 68, (byte) 54, (byte) -93, (byte) 44, (byte) -23, (byte) 93, (byte) 98, (byte) 5, (byte) 59})); //这里暂时看不出来什么东西,等分析完了就知道了
}
看下d方法做了什么
public void onClick(View view) {
if (this.a.a(this.a.v, ((EditText) this.a.findViewById(R.id.passCode)).getText().toString())) {
//拿到了输入框的内容,和之前的v变量,传入到a方法中进行了处理,这里的a方法就是上面的boolean类型函数
TextView textView = (TextView) this.a.findViewById(R.id.textView);
Toast.makeText(this.a.getApplicationContext(), "Congratulations!", 1).show();
textView.setText(R.string.nice);
return;
}
Toast.makeText(this.a.getApplicationContext(), "Oh no.", 1).show();
}
返回到a函数中看到又调用了c类中的a方法,将两个值传入,跟入
public String a(String str, String str2) {
String a = a(str); //调用a方法
String str3 = "";
a aVar = new a(); //实例化一个a类,跟入发现是个aes加密
aVar.a(a.getBytes());
try {
return new String(aVar.b(str2.getBytes()), "utf-8");
} catch (Exception e) {
e.printStackTrace();
return str3;
}
}
private String a(String str) {
//这里就是将所有的字符以两位隔开,并交换两个字符的位置,通过for循环可以看出
try {
str.getBytes("utf-8");
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i stringBuilder.append(str.charAt(i + 1));
stringBuilder.append(str.charAt(i));
}
return stringBuilder.toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
aes加密代码:用用户输入数据作为加密数据,图片数据作为加密key
protected void a(byte[] bArr) {
if (bArr == null) {
try {
this.a = new SecretKeySpec(MessageDigest.getInstance("MD5").digest("".getBytes("utf-8")), "AES");
this.b = Cipher.getInstance("AES/ECB/PKCS5Padding");
return;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return;
} catch (NoSuchAlgorithmException e2) {
e2.printStackTrace();
return;
} catch (NoSuchPaddingException e3) {
e3.printStackTrace();
return;
}
}
this.a = new SecretKeySpec(bArr, "AES");
this.b = Cipher.getInstance("AES/ECB/PKCS5Padding");
}
protected byte[] b(byte[] bArr) {
this.b.init(1, this.a);
return this.b.doFinal(bArr);
}
最后拿到加密后的数据与
new String(new byte[]{(byte) 21, (byte) -93, (byte) -68, (byte) -94, (byte) 86, (byte) 117, (byte) -19, (byte) -68, (byte) -92, (byte) 33, (byte) 50, (byte) 118, (byte) 16, (byte) 13, (byte) 1, (byte) -15, (byte) -13, (byte) 3, (byte) 4, (byte) 103, (byte) -18, (byte) 81, (byte) 30, (byte) 68, (byte) 54, (byte) -93, (byte) 44, (byte) -23, (byte) 93, (byte) 98, (byte) 5, (byte) 59})
进行对比,知道了key和加密算法,这里已经理清思路就知道怎么办了
写个解密方法,反响解密下或者比较省事的方法就是在先插桩打印出c类中对v变量进行位置交换的方法的返回值,这样拿到了aes的key,最后直接将上面的bytes数据直接解密就ok
题目二:
拿到后安装到模拟器上是无法打开的,判断是检测了模拟器
看了下也是没有壳的,直接反编译
入口处的主要代码如下:
((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
TextView textView = (TextView) MainActivity.this.findViewById(R.id.editText);
MainActivity.this.this_is_your_flag = textView.getText().toString();
if (MainActivity.this.this_is_your_flag.length() Process.killProcess(Process.myPid());
} else if (MainActivity.this.this_is_your_flag.length() > 39) {判断用户数据是否大于35位
Process.killProcess(Process.myPid());
}
//这里数据输入长度错误都会直接结束进程
Format format = new Format();
MainActivity.this.this_is_your_flag = format.form(MainActivity.this.this_is_your_flag); //实例化了Format类对字符串进行了截取,代码如下
if (MainActivity.this.this_is_your_flag.length() Toast.makeText(MainActivity.this.getApplicationContext(), "No,more.", 1).show();
} else if (new Check().check(MainActivity.this.this_is_your_flag)) { //这里就是关键点了,调用了check方法
Toast.makeText(MainActivity.this.getApplicationContext(), "Congratulations!You got it.", 1).show();
} else {
Toast.makeText(MainActivity.this.getApplicationContext(), "Oh no.Come on!", 1).show();
}
}
});
protected String form(String input) {
return input.substring(5, 38);
}
protected String fo1m(String input) {
return input.substring(5, 36);
}
protected String forn(String input) {
return input.substring(5, 39);
}
protected String f0rm(String input) {
return input.substring(5, 37);
}
//这里都是简单的对字符串进行截取,只是长度不一样
private static String[] known_pipes = new String[]{"/dev/socket/qemud", "/dev/qemu_pipe"};
String emulator = checkPipes();
private native boolean checkPasswd(String str);
protected native boolean checkEmulator(String str);
public static String checkPipes() {
for (String pipes : known_pipes) {
if (new File(pipes).exists()) {
return "true";
}
}
return "false";
}
boolean check(String pass) {
if (checkEmulator(this.emulator)) {
return false;
}
return checkPasswd(pass);
}
主要的check方法中第一步调用了checkEmulator,emulator在checkPipes中进行了初始化,这里简单的判断了下是否是模拟器,是的话直接返回false,所以做这类东西的时候还是在手机上做比较好,坑比较少
当然checkEmulator是个native方法,还需要看下so,如果过掉模拟器检测,最后就会真正的检测密码
ida打开下so
.text:00005074 EXPORT Java_com_example_ring_wantashell_Check_checkEmulator
.text:00005074 Java_com_example_ring_wantashell_Check_checkEmulator
.text:00005074 PUSH {R4,R6,R7,LR}
.text:00005076 ADD R7, SP, #8
.text:00005078 MOVS R1, #0x2A4
.text:0000507C LDR R3, [R0]
.text:0000507E LDR R3, [R3,R1]
.text:00005080 MOVS R4, #0
.text:00005082 PUSH {R2}
.text:00005084 POP {R1}
.text:00005086 PUSH {R4}
.text:00005088 POP {R2}
.text:0000508A BLX R3
.text:0000508C LDR R1, =(aTrue - 0x5092)
.text:0000508E ADD R1, PC ; "true"
.text:00005090 BL j_j_strcmp //这里就简单的判断下传入值和true是否相等,相等等于0,否则等于1
.text:00005094 PUSH {R0}
.text:00005096 POP {R1}
.text:00005098 MOVS R0, #1
.text:0000509A CMP R1, #0
.text:0000509C BEQ locret_50A2
.text:0000509E PUSH {R4}
.text:000050A0 POP {R0}
由于原文较长,微信中无法全部发布,剩下内容请点击左下角“原文”进行阅读。
--官方论坛
www.52pojie.cn
--推荐给朋友
公众微信号:吾爱破解论坛
或搜微信号:pojie_52