拖进JEB2看看:
点击MainActivity.class
package com.a.easyjava;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.c;
import android.view.View$OnClickListener;
import android.view.View;
import android.widget.Toast;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends c {
public MainActivity() {
super();
}
private static char a(String arg1, b arg2, a arg3) {
return arg3.a(arg2.a(arg1));
}
static Boolean a(String arg1) {
return MainActivity.b(arg1);
}
private static Boolean b(String arg8) {
Boolean v0_1;
int v0 = 0;
if(!arg8.startsWith("flag{")) {
v0_1 = Boolean.valueOf(false);
}
else if(!arg8.endsWith("}")) {
v0_1 = Boolean.valueOf(false);
}
else {
String v2 = arg8.substring(5, arg8.length() - 1);
b v4 = new b(Integer.valueOf(2));
a v5 = new a(Integer.valueOf(3));
StringBuilder v3 = new StringBuilder();
int v1 = 0;
while(v0 < v2.length()) {
v3.append(MainActivity.a(v2.charAt(v0) + "", v4, v5));
Integer v6 = Integer.valueOf(v4.b().intValue() / 25);
if(v6.intValue() > v1 && v6.intValue() >= 1) {
++v1;
}
++v0;
}
v0_1 = Boolean.valueOf(v3.toString().equals("wigwrkaugala"));
}
return v0_1;
}
protected void onCreate(Bundle arg3) {
super.onCreate(arg3);
this.setContentView(2130968603);
this.findViewById(2131427446).setOnClickListener(new View$OnClickListener(((Context)this)) {
public void onClick(View arg5) {
if(MainActivity.a(this.a.findViewById(2131427445).getText().toString()).booleanValue()) {
Toast.makeText(this.a, "You are right!", 1).show();
}
else {
Toast.makeText(this.a, "You are wrong! Bye~", 1).show();
new Timer().schedule(new TimerTask() {
public void run() {
System.exit(1);
}
}, 2000);
}
}
});
}
}
/**
*arg8:输入的flag
**/
private static Boolean b(String arg8) {
Boolean v0_1; //判断结果
int v0 = 0;
if(!arg8.startsWith("flag{")) { //若不以flag{开头则返回false
v0_1 = Boolean.valueOf(false);
}
else if(!arg8.endsWith("}")) { //若不以}结尾则返回false
v0_1 = Boolean.valueOf(false);
}
else {
String v2 = arg8.substring(5, arg8.length() - 1); //arg截取一定长度的子串,分析结构知是截取flag{}花括号内部的子串
b v4 = new b(Integer.valueOf(2)); //v4 = 2
a v5 = new a(Integer.valueOf(3)); //v5 = 3
StringBuilder v3 = new StringBuilder();
int v1 = 0;
while(v0 < v2.length()) { //循环遍历子串的每个字符
v3.append(MainActivity.a(v2.charAt(v0) + "", v4, v5));
Integer v6 = Integer.valueOf(v4.b().intValue() / 25);
if(v6.intValue() > v1 && v6.intValue() >= 1) {
++v1;
}
++v0;
}
v0_1 = Boolean.valueOf(v3.toString().equals("wigwrkaugala"));
}
return v0_1;
}
虽然代码被混淆了,但我并不想去混淆,直接分析被混淆的代码。毕竟头铁就是刚。
先分析两个类的实例化过程:
b v4 = new b(Integer.valueOf(2));
a v5 = new a(Integer.valueOf(3));
类a、b的代码如下:
package com.a.easyjava;
import java.util.ArrayList;
public class b {
public static ArrayList a;
static String b;
Integer[] c;
static Integer d;
static {
b.a = new ArrayList();
b.b = "abcdefghijklmnopqrstuvwxyz";
b.d = Integer.valueOf(0);
}
public b(Integer arg9) {
super();
this.c = new Integer[]{Integer.valueOf(8), Integer.valueOf(25), Integer.valueOf(17), Integer.valueOf(23), Integer.valueOf(7), Integer.valueOf(22), Integer.valueOf(1), Integer.valueOf(16), Integer.valueOf(6), Integer.valueOf(9), Integer.valueOf(21), Integer.valueOf(0), Integer.valueOf(15), Integer.valueOf(5), Integer.valueOf(10), Integer.valueOf(18), Integer.valueOf(2), Integer.valueOf(24), Integer.valueOf(4), Integer.valueOf(11), Integer.valueOf(3), Integer.valueOf(14), Integer.valueOf(19), Integer.valueOf(12), Integer.valueOf(20), Integer.valueOf(13)};
int v0;
for(v0 = arg9.intValue(); v0 < this.c.length; ++v0) {
b.a.add(this.c[v0]);
}
for(v0 = 0; v0 < arg9.intValue(); ++v0) {
b.a.add(this.c[v0]);
}
}
public Integer a(String arg5) {
int v0 = 0;
Integer v1 = Integer.valueOf(0);
if(b.b.contains(arg5.toLowerCase())) {
Integer v2 = Integer.valueOf(b.b.indexOf(arg5));
while(v0 < b.a.size() - 1) {
if(b.a.get(v0) == v2) {
v1 = Integer.valueOf(v0);
}
++v0;
}
}
else {
if(arg5.contains(" ")) {
v1 = Integer.valueOf(-10);
goto label_24;
}
v1 = Integer.valueOf(-1);
}
label_24:
b.a();
return v1;
}
public static void a() {
int v0 = b.a.get(0).intValue();
b.a.remove(0);
b.a.add(Integer.valueOf(v0));
b.b = b.b + "" + b.b.charAt(0);
b.b = b.b.substring(1, 27);
b.d = Integer.valueOf(b.d.intValue() + 1);
}
public Integer b() {
return b.d;
}
}
package com.a.easyjava;
import java.util.ArrayList;
public class a {
public static ArrayList a;
static String b;
Integer[] c;
static Integer d;
static {
a.a = new ArrayList();
a.b = "abcdefghijklmnopqrstuvwxyz";
a.d = Integer.valueOf(0);
}
public a(Integer arg8) {
super();
this.c = new Integer[]{Integer.valueOf(7), Integer.valueOf(14), Integer.valueOf(16), Integer.valueOf(21), Integer.valueOf(4), Integer.valueOf(24), Integer.valueOf(25), Integer.valueOf(20), Integer.valueOf(5), Integer.valueOf(15), Integer.valueOf(9), Integer.valueOf(17), Integer.valueOf(6), Integer.valueOf(13), Integer.valueOf(3), Integer.valueOf(18), Integer.valueOf(12), Integer.valueOf(10), Integer.valueOf(19), Integer.valueOf(0), Integer.valueOf(22), Integer.valueOf(2), Integer.valueOf(11), Integer.valueOf(23), Integer.valueOf(1), Integer.valueOf(8)};
int v0;
for(v0 = arg8.intValue(); v0 < this.c.length; ++v0) {
a.a.add(this.c[v0]);
}
for(v0 = 0; v0 < arg8.intValue(); ++v0) {
a.a.add(this.c[v0]);
}
}
public char a(Integer arg5) {
char v0_1;
int v0 = 0;
Integer v1 = Integer.valueOf(0);
if(arg5.intValue() == -10) {
a.a();
v0_1 = " ".charAt(0);
}
else {
while(v0 < a.a.size() - 1) {
if(a.a.get(v0) == arg5) {
v1 = Integer.valueOf(v0);
}
++v0;
}
a.a();
v0_1 = a.b.charAt(v1.intValue());
}
return v0_1;
}
public static void a() {
a.d = Integer.valueOf(a.d.intValue() + 1);
if(a.d.intValue() == 25) {
int v0 = a.a.get(0).intValue();
a.a.remove(0);
a.a.add(Integer.valueOf(v0));
a.d = Integer.valueOf(0);
}
}
}
v4实例化之后,其包含的
变量a=数组{17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,17,23},
变量b="abcdefghijklmnopqrstuvwxyz";
变量c={8,25,17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13},
变量d=0。
其实a就是根据传入构造器的整数截取的数组c的一部分,这里传入的是2,则截取的是c[2-c.length]的部分。
然后再做一个循环,再加入c的[0-2]两个整型。
相当于是将c的头两个数字截断后放到尾巴上。
v5实例化过程:
变量a={21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8,7,14,16}
变量b="abcdefghijklmnopqrstuvwxyz";
变量c={7,14,16,21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8}
变量d=0。
至此,上述两个类的实例化分析清楚了。
------------------------------------------------------------分割线----------------------------------------------------------------------------------------------
接下来继续分析MainActivity.a()方法,发现指向一个新方法arg3.a(arg2.a(arg1)):
来分析这个嵌套:MainActivity.a(v2.charAt(v0) + "", v4, v5);
先看arg2.a(String arg1),这是对传入的flag的字符逐一处理的方法:
public Integer a(String arg5) {
int v0 = 0; //v0=0
Integer v1 = Integer.valueOf(0); //v1=0
if(b.b.contains(arg5.toLowerCase())) { //判断条件:arg5全部转为小写形式,数字不受影响,若类b的变量b包含该字符则进入花括号内的代码,其中,变量b是小写字母表
//b.b="abcdefghijklmnopqrstuvwxyz";
Integer v2 = Integer.valueOf(b.b.indexOf(arg5)); //v2=arg5所在的字母表的索引位置
while(v0 < b.a.size() - 1) { //遍历b.a,其中b.a是一个数组
//在a中查找v2的位置
if(b.a.get(v0) == v2) {
v1 = Integer.valueOf(v0);
}
++v0;
}
} //如果传入的字符串不是字母,而是数字或者其他字符
else {
if(arg5.contains(" ")) { //如果传入的是空格,跳转到label_24
v1 = Integer.valueOf(-10);
goto label_24;
}
//传入的不是空格,返回-1
v1 = Integer.valueOf(-1);
}
label_24:
b.a(); //传入的是空格,执行一个同名无参的重载方法a()
return v1;
}
在b中有一个数表a={17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,17,23},a方法接收到传入的单个字符后,会对该字符检查是否是字母
1.是字母:读取该字符的字母序,根据这个字母序从数表a中返回对应下标的数字。比如传入的是"a",则字母序为0,返回a[0]中的值=17。
2.不是字母:
2.1是空格,执行一个重载方法a().返回 -10
2.2不是空格:返回-1
这里来看看label_24底下的这个重载方法a()做了些什么:
public static void a() {
int v0 = b.a.get(0).intValue(); //v0=数表a的第一位的值
b.a.remove(0); //a数组移除开头第一位,此行代码结束后a[1]取代a[0]的位置
b.a.add(Integer.valueOf(v0)); //数表a添加v0到表尾
b.b = b.b + "" + b.b.charAt(0); //字符串b+=b的第一个字符
b.b = b.b.substring(1, 27); //b.b截取下标为1-27的子串
b.d = Integer.valueOf(b.d.intValue() + 1); //变量d自增1
}
接下来看外层函数arg3.a(),该方法是a类中的a()方法,代码如下:
public char a(Integer arg5) {
char v0_1; //定义处理结果
int v0 = 0;
Integer v1 = Integer.valueOf(0);
if(arg5.intValue() == -10) { //如果传入的值为-10,即char是空格,执行一个同名无参的重载方法a()
a.a();
v0_1 = " ".charAt(0); //然后返回一个空格
}
else { //传入的值不是空格
while(v0 < a.a.size() - 1) { //遍历数表a
if(a.a.get(v0) == arg5) { //若数表中有传入的数值,则v1=数表中该数值的下标
v1 = Integer.valueOf(v0);
}
++v0;
}
a.a(); //执行重载方法a()
v0_1 = a.b.charAt(v1.intValue()); //返回a类的成员变量b中v1下标的字符
}
return v0_1;
}
该方法做了什么:
对传入的整型参数()就是上述分析的数表的值或者0或者-1做如下处理:
1.若传入的值为-10:执行无参的重载方法a();然后返回一个空格。
2.传入的值不是-10:遍历数组a,查找是否有该整型参数,如果有,则返回a类的成员变量b中该整型下标的对应字符,然后执行重载方法a().
2.1如果没有该整型参数,则返回变量b中的第一个字符。因为最开始v1=0。
现在来看看a类中这个重载方法a()做了些什么:
public static void a() {
a.d = Integer.valueOf(a.d.intValue() + 1); //变量d自增1
if(a.d.intValue() == 25) { //若d自增1后等于25
int v0 = a.a.get(0).intValue(); //数组a将头部第一个元素移动到数组尾部
a.a.remove(0);
a.a.add(Integer.valueOf(v0));
a.d = Integer.valueOf(0); //d重置为0
}
}
上述逻辑分析完毕。大概的描述就是根据传入的字符串,逐个进行查表变换然后拼接成一个新的字符串。
当然,上述分析都是扯淡,我们以输入flag{abcd}为例子,解析这串字符串是怎么变化的:
1.将花括号内的子串"abcd"提取出来,丢进去处理:
2.此时,v4,v5实例化完成。
3.四轮循环第一轮,取子串"abcd"的第一个字符‘a’:v4.a(a)
此时,v4中变量如下:
变量a=数组{17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,8,25},
变量b="abcdefghijklmnopqrstuvwxyz";
变量c={8,25,17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13},
变量d=0。
当v4.a('a')执行完,v4中变量如下:
变量a={23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,8,25,17}
变量b="bcdefghijklmnopqrstuvwxyza";
变量c={8,25,17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13}
变量d=0。
函数返回值为9
将执行结果作为输入,执行v5.a(9)
代码执行前,v5中变量如下:
变量a={21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8,7,14,16}
变量b="abcdefghijklmnopqrstuvwxyz";
变量c={7,14,16,21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8}
变量d=0。
代码执行完毕后,v5中变量如下:
变量a={21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8,7,14,16}
变量b="abcdefghijklmnopqrstuvwxyz";
变量c={7,14,16,21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8}
变量d=1。
返回值为'h'
一轮循环走完,输出了字符'h'
对于输入字符 'a':
1.在字母表A"abcdefghijklmnopqrstuvwxyz"中查表,查找a所在的下标0
2.将下标0在数表a[17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,8,25]中查表,查找到0所在的下标9
3.查表完毕将数表a表头移动到表尾
4.字母表A表头第一个元素移动到表尾
5.将该下标9在数表b[21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8,7,14,16]中查表,查找到9所在的下标7
6.将该下标7在字母表B"abcdefghijklmnopqrstuvwxyz"中查表,查到第7位为'h'
7.返回'h'
如此循环4次。
为了便于理解,给出化简重命名版的字符变换代码:
public static String encrypt(String input) {
int[] a = {17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,8,25};
int[] b = {21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8,7,14,16};
String alphbatA = "abcdefghijklmnopqrstuvwxyz";
String alphbatB = "abcdefghijklmnopqrstuvwxyz";
char[] chars = input.toCharArray();
StringBuffer sb = new StringBuffer();
for(int i=0;i
执行这段方法可以看到输出:
根据上述流程就可以得出逆推回去的Java代码:
public static String decrypt(String input) {
if(input.length()>25) return "此代码不考虑字符长度大于25的情况";
int[] a = {17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,8,25};
int[] b = {21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8,7,14,16};
String alphbatA = "abcdefghijklmnopqrstuvwxyz";
String alphbatB = "abcdefghijklmnopqrstuvwxyz";
StringBuffer sb = new StringBuffer();
//将数表A\字母表A移动至处理结束前的状态
for(int i=0;i0;j--) a[j] = a[j-1];
a[0] = mid;
char last = alphbatA.charAt(alphbatA.length()-1);
String mids = alphbatA.substring(0,25);
alphbatA = last+mids;
//根据num_B查数表a
int num_A = a[num_B];
sb.append(alphbatA.charAt(num_A));
}
return sb.reverse().toString();
}
执行这段代码就可以得到flag{}内部的内容:
这段字符串加上flag{}就是flag了。
-----end------------
-----------菜得真实,这种纯Java的代码一般静下心来慢慢分析,一步步还原就好------------