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的代碼一般靜下心來慢慢分析,一步步還原就好------------