题目
查壳
先查壳(PKiD等),有壳脱壳,没壳用AndroidKiller等反编译工具打开查看JAVA代码
无壳APK
Java Decompiler - MainActivity-onCreate
答案
flag{05397c42f9b6da593a3644162d36eb01}
Writeup
解题
运行
解题思路
搜索字符串
使用反编译工具的字符串搜索功能,搜索运行后失败的提示字符串“验证失败”,搜索结果为0:
将“搜索内容”下拉,选择“转换为Unicode”:
再次搜索,找到字符串所在路径:
在Java Decompiler中,找到该文件:
MainActivity
搜不出来或者没有思路时,应该先看Android程序的入口——也就是MainActivity函数。
代码逻辑
MainActivity
主要代码为一个if+equals判断。
将输入的字符串(“getText().toString()”)传递给函数“Base64New().Base64Encode”后与“5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=”对比是否相等:
public class MainActivity
extends AppCompatActivity
{
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130968603);
((Button)findViewById(2131427446)).setOnClickListener(new View.OnClickListener()
{
public void onClick(View paramAnonymousView)
{
paramAnonymousView = ((EditText)MainActivity.this.findViewById(2131427445)).getText().toString();
if (new Base64New().Base64Encode(paramAnonymousView.getBytes()).equals("5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs="))
{
Toast.makeText(MainActivity.this, "验证通过!", 1).show();
}
for (;;)
{
return;
Toast.makeText(MainActivity.this, "验证失败!", 1).show();
}
}
});
}
}public class MainActivity
extends AppCompatActivity
{
protected void onCreate(Bundle paramBundle)
{
super.onCreate(paramBundle);
setContentView(2130968603);
((Button)findViewById(2131427446)).setOnClickListener(new View.OnClickListener()
{
public void onClick(View paramAnonymousView)
{
paramAnonymousView = ((EditText)MainActivity.this.findViewById(2131427445)).getText().toString();
if (new Base64New().Base64Encode(paramAnonymousView.getBytes()).equals("5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs="))
{
Toast.makeText(MainActivity.this, "验证通过!", 1).show();
}
for (;;)
{
return;
Toast.makeText(MainActivity.this, "验证失败!", 1).show();
}
}
});
}
}
Base64New().Base64Encode
很明显是一个与Base64编码有关的函数,并且加上文字“Base64New”是明示了——Base64换(码)表:
package com.testjava.jack.pingan1;
public class Base64New
{
private static final char[] Base64ByteToStr = { 118, 119, 120, 114, 115, 116, 117, 111, 112, 113, 51, 52, 53, 54, 55, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 121, 122, 48, 49, 50, 80, 81, 82, 83, 84, 75, 76, 77, 78, 79, 90, 97, 98, 99, 100, 85, 86, 87, 88, 89, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 56, 57, 43, 47 };
private static final int RANGE = 255;
private static byte[] StrToBase64Byte = new byte['?'];
public String Base64Encode(byte[] paramArrayOfByte)
{
StringBuilder localStringBuilder = new StringBuilder();
for (int i = 0; i <= paramArrayOfByte.length - 1; i += 3)
{
byte[] arrayOfByte = new byte[4];
int j = 0;
int k = 0;
if (k <= 2)
{
if (i + k <= paramArrayOfByte.length - 1)
{
arrayOfByte[k] = ((byte)(byte)((paramArrayOfByte[(i + k)] & 0xFF) >>> k * 2 + 2 | j));
}
for (j = (byte)(((paramArrayOfByte[(i + k)] & 0xFF) << (2 - k) * 2 + 2 & 0xFF) >>> 2);; j = 64)
{
k++;
break;
arrayOfByte[k] = ((byte)j);
}
}
arrayOfByte[3] = ((byte)j);
j = 0;
if (j <= 3)
{
if (arrayOfByte[j] <= 63)
{
localStringBuilder.append(Base64ByteToStr[arrayOfByte[j]]);
}
for (;;)
{
j++;
break;
localStringBuilder.append('=');
}
}
}
return localStringBuilder.toString();
}
}
Base64
此时需要对Base64算法有一个基础的了解。
数据特点
- 出现64个字节的字符串(其实应该叫做表)作为数组取值,并且(经常,不是一定)出现“/”和“+”符号
-
Base64算法特征
Base64表格
位移
等号补位
代码
def base(string:str)->str: oldstr = '' newstr = [] base = '' base64_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'] #原始字符串转换为二进制,用bin转换后是0b开头的,所以把b替换了,首位补0补齐8位 for i in string: oldstr += '{:08}'.format(int(str(bin(ord(i))).replace('0b', ''))) #转换好的二进制按照6位一组分好,最后一组不足6位的后面补0 for j in range(0, len(oldstr), 6): newstr.append('{:<06}'.format(oldstr[j:j + 6])) #在base_list中找到对应的字符,拼接 for l in range(len(newstr)): base += base64_list[int(newstr[l], 2)] #判断base字符结尾补几个‘=’ if len(string) % 3 == 1: base += '==' elif len(string) % 3 == 2: base += '=' return base
结论
Base64变体 - 换表
需要解码的字符串 = "5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=" 新码表 = { 118, 119, 120, 114, 115, 116, 117, 111, 112, 113, 51, 52, 53, 54, 55, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 121, 122, 48, 49, 50, 80, 81, 82, 83, 84, 75, 76, 77, 78, 79, 90, 97, 98, 99, 100, 85, 86, 87, 88, 89, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 56, 57, 43, 47 } 码表ASCII字符串化 = "vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/"
Base64New函数
把原本Base64使用的码表“
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
”换成“vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/
后进行Base64编码处理”。Base64变体 - 题外话
标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。
为解决此问题,可采用一种用于URL的改进Base64编码,它在末尾填充’=’号,并将标准Base64中的“+”和“/”分别改成了“-”和“”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。 另有一种用于正则表达式的改进Base64变种,它将“+”和“/”改成了“!”和“-”,因为“+”,“*”以及前面在IRCu中用到的“[”和“]”在正则表达式中都可能具有特殊含义。 此外还有一些变种,它们将“+/”改为“-”或“.”(用作编程语言中的标识符名称)或“.-”(用于XML中的Nmtoken)甚至“:”(用于XML中的Name)。
解法
【🐷偷懒高效推荐💤】在线编码转化
- 使用CyberChef的Base64解码功能“From Base64”
- 自定义Base64码表“vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/”
- 输入需要Base64解码的字符串“5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=”
- 得flag&input=NXJGZjdFMks2cnFON0hwaXl1c2g3RTZTNWZKZzZyc2k1TkJmNk5HVDVycz0)“05397c42f9b6da593a3644162d36eb01”:
【👩💻练Python推荐⌨】Python脚本
这里有一个小插曲是,当我把解码出的字符串“05397c42f9b6da593a3644162d36eb01”输入后在APP里面是“验证通过”:
但是在XCTF里面却是失败,百度查答案,竟然是要加上“flag{”和“}”😅
import base64
string2Base64 = "5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs="
tableBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
tableNew = "vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/"
'''
maketrans():用于创建字符映射的转换表,对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标;
translate():法根据参数table给出的表(包含 256 个字符)转换字符串的字符, 要过滤掉的字符放到 del 参数中;
decode():以encoding指定的编码格式解码字符串。
'''
'1.换表'
maketrans = str.maketrans(tableNew, tableBase64)
'2.使用新表转换字符串'
translate = string2Base64.translate(maketrans)
'2.Base64解码'
flag = base64.b64decode(translate).decode("utf-8")
'''
三合一操作:
flag = base64.b64decode(string2Base64.translate(str.maketrans(tableNew, tableBase64)))
'''
print("flag{" + flag + "}")