逆向Leetcode Runtime
LeetCode是一个算法做题狗平台,可以让做题狗们在上面进行刷在充满大厂免费Claude生成出来的框架的实际开发场景大概两三辈子都不会用到的算法题,平台支持多种编程语言,提供在线代码运行与测评,这个功能的本质上是一个沙盒。
笔者某一天在LeetCode上游玩时候注意到Java执行时是通过一个叫__Driver__的神秘物质调用的。

攻读过JVM基础大全的同学都知道,ClassLoader.loadClass本质上也是从classpath读取字节流,然后拖到虚拟机里执行。
如果LeetCode的Classpath可以被读取,那么就能逆向他的调用和判分过程,但是LeetCode实际上不会让你看到stdout,但是会让你看到Exception里的信息,所以我们尝试直接读取Driver的class,并打印到异常。
java
import java.io.InputStream;
import java.util.Arrays;
class Solution {
public int[] twoSum(int[] nums, int target) {
try {
String name = "__Driver__";
InputStream is = ClassLoader.getSystemResourceAsStream(name + ".class");
byte[] bytecode = is.readAllBytes();
throw new RuntimeException(Arrays.toString(bytecode));
}catch (Throwable e){
throw new RuntimeException(e);
}
}
}很显然由于LeetCode并没有限制Exception显示的长度,导致他的执行器的Class直接输出了。

我们使用Base64编码之后即可获得执行器的Class文件,然后使用一个开源免费不要钱的反编译器进行反编译即可获得执行器源码。
java
class __Driver__ {
private static String zeroString;
private static final String SEPARATOR;
__Driver__() {
super();
}
public static void main(String[] stringArray) throws IOException {
Object object;
String string;
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long l = 0L;
PrintWriter printWriter = new PrintWriter((Writer)new FileWriter((String)"user.out"), (boolean)true);
BufferedReader bufferedReader = new BufferedReader((Reader)new InputStreamReader((InputStream)System.in));
while ((string = bufferedReader.readLine()) != null) {
object = __Deserializer__.toIntArray((String)string);
string = bufferedReader.readLine();
int n = __Deserializer__.toInteger((String)string);
long l2 = threadMXBean.getCurrentThreadCpuTime();
new __DriverSolution__();
int[] nArray = __DriverSolution__.__helper__((int[])object, (int)n);
l += threadMXBean.getCurrentThreadCpuTime() - l2;
printWriter.println((String)__Serializer__.serialize((int[])nArray));
System.out.print((String)SEPARATOR);
}
printWriter.close();
object = (Object)new PrintWriter((Writer)new FileWriter((String)"display_runtime.txt"), (boolean)true);
object.println((long)(l /= 1000000L));
object.close();
}
static {
zeroString = __Serializer__.serialize((int)0);
SEPARATOR = "" + '\u001b' + '\t' + '\u001d';
}
}简单解读源码,通过__Deserializer__传入参数然后执行,然后算出一个时间塞到display_runtime.txt,参数是从System.in传入一个Json,然后解析到Array。
简单测试了一下,只要这个display_runtime.txt不是负数,他就是完全相信的,不止测试,包括提交也是完全相信的,在装逼这一领域已经完全秒杀打表,你甚至可以自定义自己想要的时间,你只要在程序退出之前Hook给这个文件用你想要的成绩覆盖了即可。

示例代码:
java
class Solution {
public int[] twoSum(int[] nums, int target) {
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
try {
java.nio.file.Files.write(java.nio.file.Path.of("display_runtime.txt"),"0".getBytes(java.nio.charset.StandardCharsets.UTF_8));
} catch (java.io.IOException e) {
}
}
});
int n = nums.length;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[0];
}
}订阅更新
如果您喜欢我的文章,输入您的邮箱订阅更新,有新的文章会通知您
登录 GitHub 后参与讨论
加载评论中…