文章目录
201807 test.class Time4.class
第一小题:test.class
首先,明确一下题目的意思和我们目的:
题目其实输出了两个部分,第一个就是由“201807”对应的“Welcome”:
第二个是判断当前时间是否在“2018-7-1 00:00:00”和“2018-7-31 23:59:59”之间,如果是则输出当前时间并等待输入,如果不是则输出当前时间并退出。
方法有很多种,这里就没有用第一题的解法了。尝试了另外两种不同的解法。
方法一:直接用二进制编辑器修改.class文件
先用编译器打开test.class查看源代码:
查看源代码,可以发现两个日期格式的参数是String类型,且本身没有类型检查,而我们的目的是使代码输出“Welcome”,而源代码只有在当前日期为“201807”时输出“Welcome”,所以我们把“yyyyMM”字符串直接改为“201807”字符串即可输出“Welcome”。
用编辑器以16进制打开test.class,找到“yyyyMM”字符串所在的那一行:
把“yyyyMM”字符串直接改为“201807”字符串(16进制是修改对应的ASCII码):
保存test.class,运行:
这里我们只是解决了第一个问题输出Welcome,还有第二个问题使当前时间符合规范不退出。我们没有跟上面一样把“yyyy-MM-dd HH:mm:ss”字符串直接改为“2018-07-24 12:12:12”字符串,因为源代码中使用的SimpleDateFormat类的parse()函数需要正则表达式来分割时间,我们把“yyyy-MM-dd HH:mm:ss”字符串直接改为“2018-07-24 12:12:12”字符串会报错。
所以我们只能修改其他的地方了。既然是比较当前时间与“2018-7-1 00:00:00”和“2018-7-31 23:59:59”的大小,我们没法把时间改在这里面,但我们可以把时间改成无限大,即把“2018-7-1 00:00:00”改为“0000-0-0 00:00:00”、把“2018-7-31 23:59:59”和“9999-12-31 23:59:59”。
修改:这里因为内存已经限定了7的位数只有一位,所以我们只能把它改成最大的9了,最多将把“2018-7-1 00:00:00”改为“0000-0-0 00:00:00”、把“2018-7-31 23:59:59”和“9999-9-31 23:59:59”。但其实还可以把31、23、59也改成99,日期类即使超过了一般的限制也没什么关系,但反正已经改成9999年之后了,差几个月也没什么关系了:
保存test.class,运行:
成功输出“Welcome”且没有退出,进入了程序。
方法二:用Bytecode字节码查看器和JClassLib包的代码修改.class文件
用编译器打开.class文件,查看.class源码:
发现代码的逻辑是当输入201807时,先将201807转换成大整数,然后201807乘以445再加上43597487644106,再将这个大整数转换成字符串就“是Welcome”了。而我们要使所有的时间都可以输入“Welcome”,所以关键就是修改445和43597487644106。
我的想法就是将445改为0,然后将43597487644106改为“Welcome”对应的大整数24599839172947301。
求Welcome对应大整数的源代码:
import java.math.BigInteger;
/**
* 文件注释:
*
* @Auther: Legends
* @Date: /22 18:44
* @Description: 求Welecome对应的大整数
*/
public class New {
public static void main(String[] args) {
String test = "Welcome";
//字符串转16进制字符串
String s4 = str2HexStr(test);
//再转大整数
BigInteger biginteger = HexStr2int(s4);
System.out.println("Welcome对应的大整数为:"+biginteger);
}
public static BigInteger HexStr2int(String s)
{
BigInteger biginteger = new BigInteger(s, 16);
return biginteger;
}
public static String str2HexStr(String s)
{
char ac[] = "0123456789ABCDEF".toCharArray();
StringBuilder stringbuilder = new StringBuilder("");
byte abyte0[] = s.getBytes();
for(int j = 0; j < abyte0.length; j++)
{
int i = (abyte0[j] & 0xf0) >> 4;
stringbuilder.append(ac[i]);
i = abyte0[j] & 0xf;
stringbuilder.append(ac[i]);
}
return stringbuilder.toString().trim();
}
}
代码结果截图:
这样不管输入的日期时什么因为是乘以0,所以消除了日期的影响,直接将大整数“24599839172947301”转换成对应的字符串“Welcome”即可。这里我们之所以没有像方法一一样直接用16进制编辑器修改,是因为在16进制编辑器中的“43597487644106”是占14位,而“24599839172947301”是占17位,无法直接通过编辑器直接修改16进制代码扩容。所以这就是我们用代码来修改的原因。
先用java字节码查看软件Bytecode查看test.class对应的字节码:
我们要修改的常量字符串有:“2018-7-1 00:00:00”、“2018-7-31 23:59:59”、“445”、“43597487644106”(也可以修改“yyyyMM”,但修改这个已经在方法一中实现了,就不重复修改了),找到对应的常量字节码在常量池中的位置:
“2018-7-1 00:00:00”:
“2018-7-31 23:59:59”:
“445”:
“43597487644106”:
78、79、89、92、CONSTANT_Utf8_info这些都是字符串常量的标识,我们待会就通过这些标识找到它们,再修改。
源代码:
/**
* 文件注释:
*
* @Auther: Legends
* @Date: /22 21:48
* @Description: 修改.class的方法
*/
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.gjt.jclasslib.io.ClassFileWriter;
import org.gjt.jclasslib.structures.CPInfo;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.structures.InvalidByteCodeException;
import org.gjt.jclasslib.structures.constants.ConstantUtf8Info;
public class ChangeClass
{
public static void main(String[] args){
//用文件输入流将.class文件的内容读入
String filePath = "D:\\test.class";
FileInputStream fis = null;
try {
fis = new FileInputStream(filePath);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//数据输入流
DataInput di = new DataInputStream(fis);
ClassFile cf = new ClassFile();
try {
//即将数据流读入ClassFile类的对象
cf.read(di);
} catch (InvalidByteCodeException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//获取常量池数组
CPInfo[] infos = cf.getConstantPool();
//遍历常量池
int count = infos.length;
for (int i = 0; i < count; i++) {
//找到第78个常量字符串,修改“2018-7-1 00:00:00”为“0000-0-0 00:00:00”
if(i == 78){
ConstantUtf8Info uInfo = (ConstantUtf8Info)infos[i];
uInfo.setBytes("0000-0-0 00:00:00".getBytes());
infos[i]=uInfo;
}
//找到第79个常量字符串,修改“2018-7-31 23:59:59”为“9999-9-31 23:59:59”
if(i == 79){
ConstantUtf8Info uInfo = (ConstantUtf8Info)infos[i];
uInfo.setBytes("9999-9-31 23:59:59".getBytes());
infos[i]=uInfo;
}
//找到第79个常量字符串,修改“445”为“000”
if(i == 89){
ConstantUtf8Info uInfo = (ConstantUtf8Info)infos[i];
uInfo.setBytes("000".getBytes());
infos[i]=uInfo;
}
//找到第79个常量字符串,修改“43597487644106”为“24599839172947301”
if(i == 92){
ConstantUtf8Info uInfo = (ConstantUtf8Info)infos[i];
uInfo.setBytes("24599839172947301".getBytes());
infos[i]=uInfo;
}
//逐个输出常量池中的常量和类型
if (infos[i] != null) {
System.out.print(i);
System.out.print(" = ");
try {
System.out.print(infos[i].getVerbose());
} catch (InvalidByteCodeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.print(" = ");
System.out.println(infos[i].getTagVerbose());
if(i == 92){
break;
}
}
}
cf.setConstantPool(infos);
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
File f = new File(filePath);
try {
ClassFileWriter.writeToFile(f, cf);
} catch (InvalidByteCodeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
实验结果截图:
修改后的.class文件:
运行test.class文件:
成功输出结果Welecome,且没有退出!!!
第二小题:Time4.class
先用编译器反编译查看.class源代码:
题目是比较了当前日期与“2018-07”是否相等,又比较了当前的年份、“_”、当前月份连接而成的字符串与“2018-07”是否相等,我们在使用本机时间进入程序的时候是走的第三个分支:JOptionPane.showMessageDialog(var9, var3 + var10.m(var3) + var10.u(var8));所以关键就在于m函数和u函数。m函数是将p = "070097105108101100033"转成字符串“Failed!”。而u函数是一个字符自身求与的函数,返回结果为6个空格!!!
题目比较简单,所以我们也没有用第一题和第二题第一问的复杂方法了。
方法一:直接将当前日期的初始化格式“yyyy-MM”改为“2018-07”:
这样无论当前时间为多少,程序都会认为我们的时间是“2018-07”。用编辑器以16进制打开Time4.class,找到“yyyy-MM”:
修改为“2018-07”:
保存,运行Time4.classs:
果然所有的当前时间都被初始化为“2018-07”了,并成功输出了Welcome。
方法二:将p = “070097105108101100033”(对应字符串Failed!)改为p=”087101108099111109101”(对应字符串Welcome):
这里刚好因为Failed!后面加了个!才与Welcome长度相等,否则长度不相等就得用第一问的方法修改常量池了。直接修改后:
保存,运行Time4.class:
成功在当前时间输出Welcome。
第三种方法是,将时间变量var1 = “2018-07”,修改为当前时间“2019-03”:
使得程序判定进入s函数,再将d = “%5;+B=.”(对应Failed!)修改为“[KEU>CR”(对应Welcome),但这并不是个一劳永逸的办法,每个月都得修改一次当前时间,就不使用了。