攻防世界mobile新手區之easyjava write up

0x00反編譯apk

拖進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);
                }
            }
        });
    }
}

0x01查看主要代碼:

/**
*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。

至此,上述兩個類的實例化分析清楚了。

------------------------------------------------------------分割線----------------------------------------------------------------------------------------------

0x03分析嵌套方法

接下來繼續分析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;
    }

0x04總結一下b.a()方法的作用:

在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
        }
    }

上述邏輯分析完畢。大概的描述就是根據傳入的字符串,逐個進行查表變換然後拼接成一個新的字符串。

0x05走流程看看變量怎麼變化

當然,上述分析都是扯淡,我們以輸入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'

0x06處理子串"abcd"流程分析

對於輸入字符 '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<chars.length;i++) {
			int alphbat_index = alphbatA.indexOf(chars[i]);  //查字母表下標
			//查字母表下標所在位置的下標
			int anum = -1;
			for(int j=0;j<a.length;j++) {
				if(a[j]==alphbat_index) {
					anum = j;
					break;
				}			
			}
			if(anum==-1) return "error1";
			//數表a表頭移動到表尾
			int mid = a[0];
			for(int j=1;j<a.length;j++) a[j-1] = a[j];
			a[a.length-1] = mid;
			//字母表A表頭移動到表尾
			alphbatA+=alphbatA.charAt(0);
			alphbatA = alphbatA.substring(1,27);
			//anum在數表b中查表
			int b_index = -1;
			for(int j=0;j<b.length;j++) {
				if(b[j]==anum) {
					b_index = j;
					break;
				}
			}
			if(b_index==-1) return "error2";
			//將下標b_index在字母表B中查表
			sb.append(alphbatB.charAt(b_index));
		}
		return sb.toString();
	}

執行這段方法可以看到輸出:

根據上述流程就可以得出逆推回去的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;i<input.length();i++) {
			int[] mid = new int[a.length];
			for(int j=0;j<mid.length-1;j++) mid[j] = a[j+1];
			mid[mid.length-1] = a[0];
			a = mid;
			alphbatA+=alphbatA.charAt(0);
			alphbatA = alphbatA.substring(1,27);
		}
		//開始逆推數表A\字母表A
		String nx = new StringBuffer(input).reverse().toString();
		char[] nxchar = nx.toCharArray();
		for(int i=0;i<nxchar.length;i++) {
			//查找字母表B下標7
			int nxc_index = alphbatB.indexOf(nxchar[i]);
			//根據nxc_index查找該下標所在的值9
			int num_B = b[nxc_index]; //num_B = 9
			//移動數表A\字母表A表尾到表頭
			int mid = a[a.length-1];
			for(int j=a.length-1;j>0;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的代碼一般靜下心來慢慢分析,一步步還原就好------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章