commit e1347af8b118fecd2a3c288040860040232e98f2 Author: sirjonasxx Date: Thu Mar 29 19:28:59 2018 +0200 Initial commit diff --git a/src/main/Main.java b/src/main/Main.java new file mode 100644 index 0000000..03cb07b --- /dev/null +++ b/src/main/Main.java @@ -0,0 +1,45 @@ +package main; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.stage.StageStyle; +import main.ui.GEarthController; + +import java.util.Arrays; + +// run as root issue Invalid MIT-MAGIC-COOKIE-1 key fix: https://stackoverflow.com/questions/48139447/invalid-mit-magic-cookie-1-key + +public class Main extends Application { + + @Override + public void start(Stage primaryStage) throws Exception{ + + FXMLLoader loader = new FXMLLoader(GEarthController.class.getResource("G-Earth.fxml")); + Parent root = loader.load(); + + GEarthController companion = loader.getController(); + companion.setStage(primaryStage); + + primaryStage.setResizable(false); + //primaryStage.initStyle(StageStyle.UNDECORATED); + primaryStage.setTitle("G-Earth"); + primaryStage.setScene(new Scene(root, 620, 295)); + primaryStage.show(); + + primaryStage.getScene().getStylesheets().add(GEarthController.class.getResource("bootstrap3.css").toExternalForm()); + + primaryStage.setOnCloseRequest( event -> { + companion.abort(); + }); + + } + + + public static void main(String[] args) { + launch(args); + } +} diff --git a/src/main/OSValidator.java b/src/main/OSValidator.java new file mode 100644 index 0000000..913efdf --- /dev/null +++ b/src/main/OSValidator.java @@ -0,0 +1,54 @@ +package main; + +public class OSValidator { + + private static String OS = System.getProperty("os.name").toLowerCase(); + + public static void main(String[] args) { + + System.out.println(OS); + + if (isWindows()) { + System.out.println("This is Windows"); + } else if (isMac()) { + System.out.println("This is Mac"); + } else if (isUnix()) { + System.out.println("This is Unix or Linux"); + } else if (isSolaris()) { + System.out.println("This is Solaris"); + } else { + System.out.println("Your OS is not support!!"); + } + } + + public static boolean isWindows() { + return (OS.contains("win")); + } + + public static boolean isMac() { + return (OS.contains("mac")); + } + + public static boolean isUnix() { + return (OS.contains("nix") || OS.contains("nux") || OS.indexOf("aix") > 0); + } + + public static boolean isSolaris() { + return (OS.contains("sunos")); + } + + public static String getOS() { + if (isWindows()) { + return "win"; + } else if (isMac()) { + return "osx"; + } else if (isUnix()) { + return "uni"; + } else if (isSolaris()) { + return "sol"; + } else { + return "err"; + } + } + +} diff --git a/src/main/irrelevant/Timer.java b/src/main/irrelevant/Timer.java new file mode 100644 index 0000000..8792cf4 --- /dev/null +++ b/src/main/irrelevant/Timer.java @@ -0,0 +1,11 @@ +package main.irrelevant; + +public class Timer { + private long time0 = System.nanoTime(), time1 = time0; + private boolean ended = false; + public void start() { time0 = System.nanoTime(); ended = false; } + public void end() { time1 = System.nanoTime(); ended = true; } + public double delta() { + if ( ! ended) { end(); } + return 1e-6 * (time1 - time0); } +} \ No newline at end of file diff --git a/src/main/protocol/HConnection.java b/src/main/protocol/HConnection.java new file mode 100644 index 0000000..a5b3319 --- /dev/null +++ b/src/main/protocol/HConnection.java @@ -0,0 +1,399 @@ +package main.protocol; + +import main.OSValidator; +import main.protocol.packethandler.Handler; +import main.protocol.packethandler.IncomingHandler; +import main.protocol.packethandler.OutgoingHandler; +import sun.plugin2.util.SystemUtil; + +import java.io.*; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +public class HConnection { + + public enum State { + NOT_CONNECTED, + PREPARING, // DOMAIN AND PORT BEEN PASSED + PREPARED, // FOUND IP ADDRESS OF DOMAIN + WAITING_FOR_CLIENT, // WAITING FOR CORRECT TCP CONNECTION TO BE SET UP + CONNECTED // CONNECTED + } + + private static final String hostsFileLocation; + static { + if (OSValidator.isWindows()) hostsFileLocation = System.getenv("WinDir") + "\\system32\\drivers\\etc\\hosts"; + else hostsFileLocation = "/etc/hosts"; // confirmed location on linux & mac + } + + private volatile boolean hostFileEdited = false; + private volatile Object[] trafficListeners = {new ArrayList(), new ArrayList(), new ArrayList()}; + private volatile List stateChangeListeners = new ArrayList<>(); + private volatile State state = State.NOT_CONNECTED; + private volatile String input_domain = null; // given string representation + + private volatile String actual_domain; // actual ip representation + private volatile int port = -1; + + private volatile ServerSocket proxy; + private volatile Handler inHandler = null; + private volatile Handler outHandler = null; + + public final static boolean DEBUG = false; + + public State getState() { + return state; + } + private String detourIP() { + return "127.0.0.1"; + } + + public void prepare(String domain, int port) { + setState(State.PREPARING); + + this.actual_domain = domain; + this.port = port; + + if (hostFileEdited) { + removeFromHostsFile(detourIP() + " " + domain); + } + + try { + InetAddress address = InetAddress.getByName(domain); + actual_domain = address.getHostAddress(); + if (DEBUG) System.out.println("found dom:" + actual_domain); + input_domain = domain; + setState(State.PREPARED); + + } catch (UnknownHostException e) { + e.printStackTrace(); + setState(State.NOT_CONNECTED); + } + } + public void start() throws IOException { + if (state == State.PREPARED) { + if (DEBUG) System.out.println("waiting for client on port: " + port); + + setState(State.WAITING_FOR_CLIENT); + if (!hostFileEdited) { + addToHostsFile(detourIP() + " " + input_domain); + } + + proxy = new ServerSocket(port); + + try { + while ((state == State.WAITING_FOR_CLIENT) && !proxy.isClosed()) { + try { + Socket client = proxy.accept(); + if (DEBUG) System.out.println("accepted a proxy"); + new Thread(() -> { + try { + startProxyThread(client); + } catch (InterruptedException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + }).start(); + + + } catch (IOException e1) { + // TODO Auto-generated catch block + //e1.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + if (DEBUG) System.out.println("done waiting for clients with: " + this.state ); + } + } + private void startProxyThread(Socket client) throws InterruptedException, UnknownHostException, IOException { + final boolean[] datastream = new boolean[1]; + + Socket habbo_server = new Socket(actual_domain, port); + + OutputStream client_out = client.getOutputStream(); + InputStream client_in = client.getInputStream(); + OutputStream habbo_server_out = habbo_server.getOutputStream(); + InputStream habbo_server_in = habbo_server.getInputStream(); + + if (DEBUG) System.out.println(habbo_server.getLocalAddress().getHostAddress() + ": " + habbo_server.getLocalPort()); + + final boolean[] aborted = new boolean[1]; + + // wachten op data van client + new Thread(() -> { + try { + OutgoingHandler handler = new OutgoingHandler(habbo_server_out); + + while (!client.isClosed() && (state == State.WAITING_FOR_CLIENT || state == State.CONNECTED)) { + byte[] buffer; + while (client_in.available() > 0) { + client_in.read(buffer = new byte[client_in.available()]); + + handler.act(buffer, trafficListeners); + if (!datastream[0] && handler.isDataStream()) { + datastream[0] = true; + setState(State.CONNECTED); + onConnect(); + + outHandler = handler; + //client_outputStream = client_out; + //server_outputStream = habbo_server_out; + } + + } + Thread.sleep(1); + + } + } + catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + finally { + if (DEBUG) System.out.println("abortclient"); + try { + if (habbo_server_out != null) habbo_server_out.close(); + if (habbo_server_in != null) habbo_server_in.close(); + if (client_in != null) client_in.close(); + if (client_out != null) client_out.close(); + if (habbo_server != null && !habbo_server.isClosed()) habbo_server.close(); + if (client != null && !client.isClosed()) client.close(); + aborted[0] = true; + } catch (IOException e) { + e.printStackTrace(); + } + if (datastream[0]) { + setState(State.NOT_CONNECTED); + outHandler = null; + inHandler = null; + }; + } + }).start(); + // wachten op data van server + new Thread(() -> { + try { + IncomingHandler handler = new IncomingHandler(client_out); + + while (!habbo_server.isClosed() && (state == State.CONNECTED || state == State.WAITING_FOR_CLIENT)) { + byte[] buffer; + while (habbo_server_in.available() > 0) { + habbo_server_in.read(buffer = new byte[habbo_server_in.available()]); + if (!handler.isDataStream() && datastream[0]) { + handler.setAsDataStream(); + inHandler = handler; + } + handler.act(buffer, trafficListeners); + } + Thread.sleep(1); + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } finally { + try { + if (habbo_server_out != null) habbo_server_out.close(); + if (habbo_server_in != null) habbo_server_in.close(); + if (client_in != null) client_in.close(); + if (client_out != null) client_out.close(); + if (!habbo_server.isClosed()) habbo_server.close(); + if (!client.isClosed()) client.close(); + aborted[0] = true; + } catch (IOException e) { + e.printStackTrace(); + } + } + }).start(); + + while(!aborted[0]) + { + Thread.sleep(50); + } + + try { + if (!habbo_server.isClosed()) habbo_server.close(); + if (!client.isClosed()) client.close(); + if (DEBUG) System.out.println("STOP"); + } + catch (IOException e) { + e.printStackTrace(); + } + } + private void onConnect() { + if (hostFileEdited) { + removeFromHostsFile(detourIP() + " " + input_domain); + } + + if (proxy != null && !proxy.isClosed()) { + try { + proxy.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + public void abort() { + if (hostFileEdited) { + removeFromHostsFile(detourIP() + " " + input_domain); + } + port = -1; + setState(State.NOT_CONNECTED); + actual_domain = null; + if (proxy != null && !proxy.isClosed()) { + try { + proxy.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + private void addToHostsFile(String text) { + if (DEBUG) System.out.println("try add hostsfile: " + text); + try + { + ArrayList lines = new ArrayList(); + File f1 = new File(hostsFileLocation); + FileReader fr = new FileReader(f1); + BufferedReader br = new BufferedReader(fr); + String line = null; + boolean containmmm = false; + while ((line = br.readLine()) != null) + { + if (line.equals(text)) + containmmm = true; + lines.add(line); + + } + fr.close(); + br.close(); + + FileWriter fw = new FileWriter(f1); + BufferedWriter out = new BufferedWriter(fw); + + if (!containmmm) { + out.write(text); + } + + for (int i = 0; i < lines.size(); i++) { + out.write("\n"+ lines.get(i)); + } + + out.flush(); + out.close(); + hostFileEdited = true; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + private void removeFromHostsFile(String text) { + try + { + if (DEBUG) System.out.println("try remove hostsfile: " + text); + ArrayList lines = new ArrayList(); + File f1 = new File(hostsFileLocation); + FileReader fr = new FileReader(f1); + BufferedReader br = new BufferedReader(fr); + String line = null; + while ((line = br.readLine()) != null) + { + if (!line.contains(text)) + lines.add(line); + + } + fr.close(); + br.close(); + + FileWriter fw = new FileWriter(f1); + BufferedWriter out = new BufferedWriter(fw); + + for (int i = 0; i < lines.size(); i++) { + out.write(lines.get(i)); + if (i != lines.size() - 1) out.write("\n"); + } + out.flush(); + out.close(); + hostFileEdited = false; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + + private void setState(State state) { + if (state != this.state) { + State buffer = this.state; + this.state = state; + for (StateChangeListener listener : stateChangeListeners) { + listener.stateChanged(buffer, state); + } + } + + } + + /** + * 3 orders: + * 0 = before modification ¹ + * 1 = modification + * 2 = after modification ¹ + * + * ¹don't modificate (block, replace) + */ + public void addTrafficListener(int order, TrafficListener listener) { + ((List)trafficListeners[order]).add(listener); + } + public void removeTrafficListener(TrafficListener listener) { + ((List)trafficListeners[0]).remove(listener); + ((List)trafficListeners[1]).remove(listener); + ((List)trafficListeners[2]).remove(listener); + } + + public void addStateChangeListener(StateChangeListener listener) { + stateChangeListeners.add(listener); + } + public void removeStateChangeListener(StateChangeListener listener) { + stateChangeListeners.remove(listener); + } + + public int getPort() { + return port; + } + public String getHost() { + return actual_domain; + } + public String getDomain() { return input_domain; } + + + public boolean sendToClient(HPacket message) { + if (inHandler == null) return false; + new Thread(() -> inHandler.sendToStream(message.toBytes())).start(); + return true; + } + public boolean sendToServer(HPacket message) { + if (outHandler == null) return false; + outHandler.sendToStream(message.toBytes()); + return false; + } + + public void sendToClientAsync(HPacket message) { + new Thread(() -> { + sendToClient(message); + }).start(); + } + public void sendToServerAsync(HPacket message) { + new Thread(() -> { + sendToServer(message); + }).start(); + } + +} diff --git a/src/main/protocol/HMessage.java b/src/main/protocol/HMessage.java new file mode 100644 index 0000000..832619d --- /dev/null +++ b/src/main/protocol/HMessage.java @@ -0,0 +1,41 @@ +package main.protocol; + +public class HMessage { + + public enum Side { + TOSERVER, + TOCLIENT + } + + private HPacket hPacket; + private Side side; + private int index; + + private boolean isBlocked; + + + public HMessage(HPacket packet, Side side, int index) { + this.side = side; + this.hPacket = packet; + this.index = index; + isBlocked = false; + } + + public void setBlocked(boolean block) { + isBlocked = block; + } + public boolean isBlocked() { + return isBlocked; + } + + public HPacket getPacket() { + return hPacket; + } + public Side getDestination() { + return side; + } + + public boolean isCorrupted() { + return hPacket.isCorrupted(); + } +} diff --git a/src/main/protocol/HPacket.java b/src/main/protocol/HPacket.java new file mode 100644 index 0000000..a47dcdd --- /dev/null +++ b/src/main/protocol/HPacket.java @@ -0,0 +1,498 @@ +package main.protocol; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class HPacket { + // te komen: toExpressions (+impl. expressies) + private boolean isEdited = false; + private byte[] packetInBytes; + private int readIndex = 6; + + public HPacket(byte[] packet) { + packetInBytes = packet.clone(); + } + public HPacket(String packet) { + packetInBytes = fromStringToBytes(fromExpressionToString(packet)); + } + public HPacket(int header) { + packetInBytes = new byte[]{0,0,0,4,0,0}; + replaceUShort(4, header); + } + public HPacket(int header, byte[] bytes) { + packetInBytes = new byte[4]; + appendBytes(bytes); + } + + public String toString() { + String teststring = ""; + for (byte x : packetInBytes) { + if ((x < 32 && x >= 0) || x < -96 || x == 93 || x == 91 || x == 125 || x == 123 || x == 127 ) + teststring+="["+((((int)x) + 256 ) % 256)+"]"; + else + try { + teststring+=new String(new byte[]{x}, "ISO-8859-1"); //"ISO-8859-1" + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + return teststring; + } + + + /** + * returns "" if invalid expression, replaces all {} occurences except {l} + * @return + */ + private static String fromExpressionToString(String input) { + try { + int i = 0; + char[] asChararray = input.toCharArray(); + StringBuilder newString = new StringBuilder(); + if (input.startsWith("{l}")) { + newString.append("{l}"); + i = 3; + } + + while (i < input.length()) { + if (asChararray[i] != '{') { + newString.append(asChararray[i]); + i++; + } + else { + i++; + StringBuilder typeBuilder = new StringBuilder(); + while (i < input.length() && asChararray[i] != ':') { + typeBuilder.append(asChararray[i]); + i++; + } + if (i == input.length()) throw new Exception(); + String type = typeBuilder.toString(); + i++; + + StringBuilder inhoudBuilder = new StringBuilder(); + while (i < input.length() && asChararray[i] != '}') { + inhoudBuilder.append(asChararray[i]); + i++; + } + if (i == input.length()) throw new Exception(); + String inhoud = inhoudBuilder.toString(); + i++; + + if (type.equals("u")) { + int probs = Integer.parseInt(inhoud); + if (probs < 0 || probs >= 256 * 256) throw new Exception(); + newString.append("[").append(probs / 256).append("][").append(probs % 256).append("]"); + } + else if (type.equals("i")) { + ByteBuffer b = ByteBuffer.allocate(4).putInt(Integer.parseInt(inhoud)); + newString.append(new HPacket(b.array()).toString()); + } + else if (type.equals("b")) { // could be a byte or a boolean, no one cares + if (inhoud.toLowerCase().equals("true") || inhoud.toLowerCase().equals("false")) { + newString.append(inhoud.toLowerCase().equals("true") ? "[1]" : "[0]"); + } + else { + int probs = Integer.parseInt(inhoud); + if (probs < 0 || probs >= 256) throw new Exception(); + newString.append("[").append(probs).append("]"); + } + } + else if (type.equals("s")) { + int probs = inhoud.length(); + if (probs < 0 || probs >= 256 * 256) throw new Exception(); + newString.append("[").append(probs / 256).append("][").append(probs % 256).append("]"); + + byte[] bts = inhoud.getBytes(StandardCharsets.ISO_8859_1); + for (int j = 0; j < inhoud.length(); j++) { + newString.append("[").append((((int)(bts[j])) + 256) % 256).append("]"); + } + } + else throw new Exception(); + + } + } + return newString.toString(); + } + catch( Exception e) { + return ""; + } + } + private static byte[] fromStringToBytes(String curstring) { + try { + ArrayList bytes = new ArrayList<>(); + int index = 0; + char[] asChararray = curstring.toCharArray(); + byte[] asliteralbytes = curstring.getBytes("ISO-8859-1"); + + boolean startWithLength = false; + if (curstring.startsWith("{l}")) { + startWithLength = true; + index = 3; + } + + while (index < curstring.length()) { + if (asChararray[index] == '[') { + int l = 2; + while (index + l < curstring.length() && asChararray[index + l] != ']') l++; + if (index + l == curstring.length()) throw new Exception(); + + int result = Integer.parseInt(curstring.substring(index + 1, index + l)); + if (result > 255 || result < 0) throw new Exception(); + + byte rl = result > 127 ? (byte)(result - 256) : (byte)result ; + + bytes.add(rl); + index = index + 1 + l; + } + else { + bytes.add(asliteralbytes[index]); + index++; + } + } + + byte[] result; + if (startWithLength) { + result = new byte[bytes.size() + 4]; + + ByteBuffer b = ByteBuffer.allocate(4); + b.putInt(bytes.size()); + for (int i = 0; i < 4; i++) { + result[i] = b.array()[i]; + } + for (int i = 0; i < bytes.size(); i++) { + result[i + 4] = bytes.get(i); + } + } + else { + result = new byte[bytes.size()]; + for (int i = 0; i < bytes.size(); i++) { + result[i] = bytes.get(i); + } + } + + return result; + } + catch (Exception e){} + return new byte[0]; + } + + public byte[] toBytes() { + return packetInBytes; + } + + public int getReadIndex() { + return readIndex; + } + public void setReadIndex(int number) { + readIndex = number; + } + + public boolean isCorrupted() { + + if (packetInBytes.length >= 6) { + if (length() == getBytesLength() - 4) { + return false; + } + } + return true; + } + + public byte readByte() { + readIndex++; + return packetInBytes[readIndex - 1]; + } + public byte readByte(int index) { + return packetInBytes[index]; + } + + public short readShort() { + byte[] btarray = new byte[]{packetInBytes[readIndex], packetInBytes[readIndex + 1]}; + readIndex +=2; + return java.nio.ByteBuffer.wrap(btarray).getShort(); + } + public short readShort(int index) { + byte[] btarray = new byte[]{packetInBytes[index], packetInBytes[index + 1]}; + return java.nio.ByteBuffer.wrap(btarray).getShort(); + } + public int readUshort() { + byte[] btarray = new byte[]{0, 0, packetInBytes[readIndex], packetInBytes[readIndex + 1]}; + readIndex +=2; + return java.nio.ByteBuffer.wrap(btarray).getInt(); + } + public int readUshort(int index) { + byte[] btarray = new byte[]{0, 0, packetInBytes[index], packetInBytes[index + 1]}; + return java.nio.ByteBuffer.wrap(btarray).getInt(); + } + + public int headerId() { + return readUshort(4); + } + + public int readInteger(){ + byte[] btarray = new byte[]{packetInBytes[readIndex], packetInBytes[readIndex + 1], packetInBytes[readIndex + 2], packetInBytes[readIndex + 3]}; + readIndex +=4; + return java.nio.ByteBuffer.wrap(btarray).getInt(); + } + public int readInteger(int index) { + byte[] btarray = new byte[]{packetInBytes[index], packetInBytes[index + 1], packetInBytes[index + 2], packetInBytes[index + 3]}; + return java.nio.ByteBuffer.wrap(btarray).getInt(); + } + + public int length() { + return readInteger(0); + } + public int getBytesLength() { + return packetInBytes.length; + } + + public byte[] readBytes(int length) { + byte[] newbytes = new byte[length]; + for (int i = 0; i < (length); i++) { + newbytes[i] = packetInBytes[i+ readIndex]; + } + readIndex +=length; + return newbytes; + } + public byte[] readBytes(int length, int index) { + byte[] newbytes = new byte[length]; + for (int i = 0; i < (length); i++) { + newbytes[i] = packetInBytes[i+index]; + } + return newbytes; + } + + public long readLong() { + byte[] btarray = readBytes(8); + return java.nio.ByteBuffer.wrap(btarray).getLong(); + } + public long readLong(int index) { + byte[] btarray = readBytes(8, index); + return java.nio.ByteBuffer.wrap(btarray).getLong(); + } + + public String readString() { + int length = readUshort(); + byte[] x = new byte[length]; + for (int i = 0; i < x.length; i++) x[i] = readByte(); + + try { + return new String(x, "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { } + + return null; + } + public String readString(int index) { + int length = readUshort(index); + index+=2; + byte[] x = new byte[length]; + for (int i = 0; i < x.length; i++) { x[i] = readByte(index); index++; } + + try { + return new String(x, "ISO-8859-1"); + } catch (UnsupportedEncodingException e) { } + + return null; + } + + public boolean readBoolean() { + return (readByte() != 0); + } + public boolean readBoolean(int index) { + return (readByte(index) != 0); + } + + + public HPacket replaceBoolean(int index, boolean b) { + isEdited = true; + packetInBytes[index] = b ? (byte)1 : (byte)0; + return this; + } + public HPacket replaceInt(int index, int i) { + isEdited = true; + ByteBuffer b = ByteBuffer.allocate(4).putInt(i); + for (int j = 0; j < 4; j++) { + packetInBytes[index + j] = b.array()[j]; + } + return this; + } + public HPacket replaceByte(int index, byte b) { + isEdited = true; + packetInBytes[index] = b; + return this; + } + public HPacket replaceBytes(int index, byte[] bytes) { + isEdited = true; + int i = 0; + while (index + i < packetInBytes.length && i < bytes.length) { + replaceByte(index + i, bytes[i]); + i++; + } +// +// if (i < bytes.length) { +// appendBytes(Arrays.copyOfRange(bytes, i, bytes.length)); +// fixLength(); +// } + return this; + } + public HPacket replaceUShort(int index, int ushort) { + isEdited = true; + ByteBuffer b = ByteBuffer.allocate(4).putInt(ushort); + packetInBytes[index] = b.array()[2]; + packetInBytes[index + 1] = b.array()[3]; + return this; + } + public HPacket replaceShort(int index, short s) { + isEdited = true; + ByteBuffer b = ByteBuffer.allocate(2).putShort(s); + packetInBytes[index] = b.array()[0]; + packetInBytes[index + 1] = b.array()[1]; + return this; + } + public HPacket replaceString(int index, String s) { + isEdited = true; + byte[] sbytes = s.getBytes(StandardCharsets.ISO_8859_1); + int mover = s.length() - readUshort(index); + + if (mover != 0) { + byte[] newPacket = Arrays.copyOf(packetInBytes, packetInBytes.length + mover); + + if (mover > 0) { + int i = newPacket.length - 1; + while (i > index + mover + 2) { + newPacket[i] = packetInBytes[i - mover]; + i--; + } + } + else { + int i = index + 2 + s.length(); + while (i < newPacket.length) { + newPacket[i] = packetInBytes[i - mover]; + i++; + } + } + + packetInBytes = newPacket; + fixLength(); + } + + replaceUShort(index, s.length()); + for (int i = 0; i < s.length(); i++) { + packetInBytes[index + 2 + i] = sbytes[i]; + } + return this; + } + + //returns if done r not + public boolean replaceFirstString(String oldS, String newS) { + int i = 6; + while (i < packetInBytes.length - 1 - oldS.length()) { + if (readUshort(i) == oldS.length() && readString(i).equals(oldS)) { + replaceString(i, newS); + return true; + } + i++; + } + return false; + } + public HPacket replaceAllString(String oldS, String newS) { + while (replaceFirstString(oldS, newS)) {} + + return this; + } + + + public HPacket appendInt(int i) { + isEdited = true; + packetInBytes = Arrays.copyOf(packetInBytes, packetInBytes.length + 4); + ByteBuffer byteBuffer = ByteBuffer.allocate(4).putInt(i); + for (int j = 0; j < 4; j++) { + packetInBytes[packetInBytes.length - 4 + j] = byteBuffer.array()[j]; + } + fixLength(); + return this; + } + public HPacket appendByte(byte b) { + isEdited = true; + packetInBytes = Arrays.copyOf(packetInBytes, packetInBytes.length + 1); + packetInBytes[packetInBytes.length - 1] = b; + fixLength(); + return this; + } + public HPacket appendBytes(byte[] bytes) { + isEdited = true; + packetInBytes = Arrays.copyOf(packetInBytes, packetInBytes.length + bytes.length); + for (int i = 0; i < bytes.length; i++) { + packetInBytes[packetInBytes.length - bytes.length + i] = bytes[i]; + } + fixLength(); + return this; + } + public HPacket appendBoolean(boolean b) { + isEdited = true; + appendByte((byte)(b ? 1 : 0)); + return this; + } + public HPacket appendUShort(int ushort) { + isEdited = true; + packetInBytes = Arrays.copyOf(packetInBytes, packetInBytes.length + 2); + ByteBuffer byteBuffer = ByteBuffer.allocate(4).putInt(ushort); + for (int j = 2; j < 4; j++) { + packetInBytes[packetInBytes.length - 4 + j] = byteBuffer.array()[j]; + } + fixLength(); + return this; + } + public HPacket appendShort(short s) { + isEdited = true; + packetInBytes = Arrays.copyOf(packetInBytes, packetInBytes.length + 2); + ByteBuffer byteBuffer = ByteBuffer.allocate(2).putShort(s); + for (int j = 0; j < 2; j++) { + packetInBytes[packetInBytes.length - 2 + j] = byteBuffer.array()[j]; + } + fixLength(); + return this; + } + public HPacket appendString(String s) { + isEdited = true; + appendUShort(s.length()); + appendBytes(s.getBytes(StandardCharsets.ISO_8859_1)); + return this; + } + + public HPacket removeFrom(int index) { + return removeRange(index, packetInBytes.length - index); + } + public HPacket removeRange(int index, int length) { + isEdited = true; + for (int i = index; i < packetInBytes.length - length; i++) { + packetInBytes[i] = packetInBytes[i + length]; + } + packetInBytes = Arrays.copyOf(packetInBytes, packetInBytes.length - length); + fixLength(); + return this; + } + + public boolean isReplaced() { + return isEdited; + } + + private void fixLength() { + boolean remember = isEdited; + replaceInt(0, packetInBytes.length - 4); + isEdited = remember; + } + + public static void main(String[] args) { + HPacket packet = new HPacket("[0][0][0]4[15] [0]!PRODUCTION-201802201205-141713395[0][5]FLASH[0][0][0][1][0][0][0][0]"); + packet.replaceFirstString("FLASH", "HTML"); + System.out.println(packet); + + } + +} \ No newline at end of file diff --git a/src/main/protocol/StateChangeListener.java b/src/main/protocol/StateChangeListener.java new file mode 100644 index 0000000..6c2fada --- /dev/null +++ b/src/main/protocol/StateChangeListener.java @@ -0,0 +1,7 @@ +package main.protocol; + +public interface StateChangeListener { + + void stateChanged(HConnection.State oldState, HConnection.State newState); + +} diff --git a/src/main/protocol/TrafficListener.java b/src/main/protocol/TrafficListener.java new file mode 100644 index 0000000..a369119 --- /dev/null +++ b/src/main/protocol/TrafficListener.java @@ -0,0 +1,7 @@ +package main.protocol; + +public interface TrafficListener { + + void onCapture(HMessage message); + +} diff --git a/src/main/protocol/crypto/RC4.java b/src/main/protocol/crypto/RC4.java new file mode 100644 index 0000000..1c67547 --- /dev/null +++ b/src/main/protocol/crypto/RC4.java @@ -0,0 +1,215 @@ +package main.protocol.crypto; +/* + * Copyright (C) 2003 Clarence Ho (clarence@clarenceho.net) + * All rights reserved. + * + * Redistribution and use of this software for non-profit, educational, + * or persoanl purposes, in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * In case of using this software for other purposes not stated above, + * please conact Clarence Ho (clarence@clarenceho.net) for permission. + * + * THIS SOFTWARE IS PROVIDED BY CLARENCE HO "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +import main.protocol.HPacket; + +/** + * This is a simple implementation of the RC4 (tm) encryption algorithm. The + * author implemented this class for some simple applications + * that don't need/want/require the Sun's JCE framework. + *

+ * But if you are looking for encryption algorithms for a + * full-blown application, + * it would be better to stick with Sun's JCE framework. You can find + * a *free* JCE implementation with RC4 (tm) at + * Cryptix (http://www.cryptix.org/). + *

+ * Note that RC4 (tm) is a trademark of RSA Data Security, Inc. + * Also, if you are within USA, you may need to acquire licenses from + * RSA to use RC4. + * Please check your local law. The author is not + * responsible for any illegal use of this code. + *

+ * @author Clarence Ho + */ +public class RC4 { + + private byte state[] = new byte[256]; + private int x; + private int y; + + /** + * Initializes the class with a string key. The length + * of a normal key should be between 1 and 2048 bits. But + * this method doens't check the length at all. + * + * @param key the encryption/decryption key + */ + public RC4(String key) throws NullPointerException { + this(key.getBytes()); + } + + /** + * Initializes the class with a byte array key. The length + * of a normal key should be between 1 and 2048 bits. But + * this method doens't check the length at all. + * + * @param key the encryption/decryption key + */ + public RC4(byte[] key) throws NullPointerException { + + for (int i=0; i < 256; i++) { + state[i] = (byte)i; + } + + x = 0; + y = 0; + + int index1 = 0; + int index2 = 0; + + byte tmp; + + if (key == null || key.length == 0) { + throw new NullPointerException(); + } + + for (int i=0; i < 256; i++) { + + index2 = ((key[index1] & 0xff) + (state[i] & 0xff) + index2) & 0xff; + + tmp = state[i]; + state[i] = state[index2]; + state[index2] = tmp; + + index1 = (index1 + 1) % key.length; + } + + + + } + + /** + * RC4 encryption/decryption. + * + * @param data the data to be encrypted/decrypted + * @return the result of the encryption/decryption + */ + public byte[] rc4(String data) { + + if (data == null) { + return null; + } + + byte[] tmp = data.getBytes(); + + this.rc4(tmp); + + return tmp; + } + + /** + * RC4 encryption/decryption. + * + * @param buf the data to be encrypted/decrypted + * @return the result of the encryption/decryption + */ + public byte[] rc4(byte[] buf) { + + //int lx = this.x; + //int ly = this.y; + + int xorIndex; + byte tmp; + + if (buf == null) { + return null; + } + + byte[] result = new byte[buf.length]; + + for (int i=0; i < buf.length; i++) { + + x = (x + 1) & 0xff; + y = ((state[x] & 0xff) + y) & 0xff; + + tmp = state[x]; + state[x] = state[y]; + state[y] = tmp; + + xorIndex = ((state[x] &0xff) + (state[y] & 0xff)) & 0xff; + result[i] = (byte)(buf[i] ^ state[xorIndex]); + } + + //this.x = lx; + //this.y = ly; + + return result; + } + + + public void printKey() { + System.out.println(new HPacket(state).toString()); + } + + public static void main(String[] args) { + byte[] sharedKey = new byte[27]; + + RC4 p1 = new RC4(sharedKey); + RC4 p2 = new RC4(sharedKey); + + p1.printKey(); + p2.printKey(); + byte[] enc = p1.rc4("hallo".getBytes()); + System.out.println(new String(p2.rc4(enc))); + + p1.printKey(); + p2.printKey(); + + enc = p1.rc4("hallo".getBytes()); + System.out.println(new String(p2.rc4(enc))); + + p1.printKey(); + p2.printKey(); + + enc = p1.rc4("meneeeer dit zijn echt veel meer dan 27 characters dus latne we dit even proberen".getBytes()); + System.out.println(new String(p2.rc4(enc))); + + p1.printKey(); + p2.printKey(); + } + +// public static void main(String[] args) { +// byte[] plainData = "abc123".getBytes(); +// byte[] key = "Key".getBytes(); +// RC4 rc4 = new RC4(key); +// byte[] cipherData = rc4.rc4(plainData); +// System.out.println("加密后: " + new String(cipherData)); +// byte[] _plainData = rc4.rc4(cipherData); +// System.out.println("解密后: " + new String(_plainData)); +// System.out.println(Arrays.equals(plainData, _plainData)); +// } + +} \ No newline at end of file diff --git a/src/main/protocol/memory/FlashClient.java b/src/main/protocol/memory/FlashClient.java new file mode 100644 index 0000000..fb06315 --- /dev/null +++ b/src/main/protocol/memory/FlashClient.java @@ -0,0 +1,410 @@ +package main.protocol.memory; + +import main.irrelevant.Timer; + +import java.io.*; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FlashClient { + + + public class MemorySnippet { + long offset; + byte[] data; + + public MemorySnippet(long offset, byte[] data) { + this.offset = offset; + this.data = data; + } + + public byte[] getData() { + return data; + } + + public long getOffset() { + return offset; + } + } + + private int PID; + private List maps; + + public static FlashClient create() { + File folder = new File("/proc"); + FlashClient client = null; + + do { + File[] fileList = folder.listFiles(); + for (File file : fileList) { + if (file.isDirectory() && MemoryUtils.stringIsNumeric(file.getName())) { + String path = "/proc/" + file.getName() + "/cmdline"; + if (MemoryUtils.fileContainsString(path, "--ppapi-flash-args") || + MemoryUtils.fileContainsString(path, "plugin-container")) { + client = new FlashClient(); + client.PID = Integer.parseInt(file.getName()); + client.maps = new ArrayList<>(); + } + } + } + } while (client == null); + + + System.out.println("* Found flashclient process: " + client.PID); + return client; + } + public void refreshMemoryMaps() { + String filename = "/proc/"+this.PID+"/maps"; + BufferedReader reader; + maps = new ArrayList<>(); + + try { + reader = new BufferedReader(new FileReader(filename)); + String line; + + while ((line = reader.readLine()) != null) { + String[] split = line.split("[- ]"); + if (split[2].startsWith("rw")) { //if (split[2].startsWith("rw")) { + + try { + long start = Long.parseLong(split[0], 16); + long end = Long.parseLong(split[1], 16); + maps.add(new long[]{start, end}); + } + catch (Exception e){ + //this is nothing really + } + + } + } + reader.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + + System.out.println("* Found memory maps (amount: " + maps.size() + ")"); + } + + public List createMemorySnippetList () { + refreshMemoryMaps(); + List result = new ArrayList<>(); + + for (long[] map : maps) { + long begin = map[0]; + long end = map[1]; + + MemorySnippet snippet = new MemorySnippet(begin, new byte[(int)(end - begin)] ); + result.add(snippet); + } + return result; + } + public void fetchMemory(List snippets) { + String memoryPath = "/proc/" + PID + "/mem"; + for (MemorySnippet snippet : snippets) { + long begin = snippet.offset; + try { + RandomAccessFile raf = new RandomAccessFile(memoryPath, "r"); + raf.seek(begin); + raf.read(snippet.getData()); + raf.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * can remove & add & edit maps of memorysnipperlist + */ + public void updateMapLocationsSnippetList(List snippets) { + refreshMemoryMaps(); + + List list2 = new ArrayList<>(); + List not_added = new ArrayList<>(); + + // readd all maps that stayed the same + for (long[] map : maps) { + //if was in snippetlist: + boolean found = false; + for (MemorySnippet snippet : snippets) { + if (snippet.offset == map[0] && snippet.offset + snippet.getData().length == map[1]) { + list2.add(snippet); + snippets.remove(snippet); + found = true; + break; + } + } + if (!found) { + not_added.add(map); + } + + } + + for (int i = 0; i < not_added.size(); i++) { + long[] map = not_added.get(i); + //find potential overlap snippet + MemorySnippet snippet = new MemorySnippet(map[0], new byte[(int)(map[1] - map[0])]); + byte[] data = snippet.getData(); + + for (MemorySnippet potential : snippets) { + //if there is overlap + if ((potential.offset >= map[0] && potential.offset < map[1]) || + (potential.offset + potential.getData().length >= map[0] && potential.offset + potential.getData().length< map[1]) || + (potential.offset < map[0] && potential.offset + potential.getData().length >= map[1]) ) { + + int start = Math.max((int)(potential.offset - map[0]), 0); + int offset2 = -(int)(potential.offset - map[0]); + + for (int j = start; j < Math.min(map[1] - map[0], potential.getData().length - offset2); j++) { + data[j] = potential.getData()[j+offset2]; + } + } + } + list2.add(snippet); + } + + snippets.clear(); + for (MemorySnippet snippet : list2) { + snippets.add(snippet); + } + + } + /** + * creates a new memorysnippet list of data that changed (or not) from the original list with a given left&right buffer + */ + public List differentiate(List original, boolean isChanged, int leftRightbytebuffer) { + //fill a new memorysnippet list with live data + List upToDate = new ArrayList<>(); + for (MemorySnippet memorySnippet : original) { + upToDate.add(new MemorySnippet(memorySnippet.getOffset(), new byte[memorySnippet.getData().length])); + } + fetchMemory(upToDate); + + List result = new ArrayList<>(); + + long totalBytes = 0; + + for (int i = 0; i < original.size(); i++) { + long offset = original.get(i).getOffset(); + byte[] old = original.get(i).getData(); + byte[] curr = upToDate.get(i).getData(); + + if (!isChanged) { + // find all non-changed stuff and put in result + long pre = offset; + for (int j = 0; j < old.length; j++) { + if (old[j] != curr[j]) { + //calculate previous block length + int len = j - ((int)(pre - offset)); + if (len >= leftRightbytebuffer && len > 0) { + result.add(new MemorySnippet(pre, new byte[len])); + totalBytes += len; + } + pre = offset + j + 1; + } + } + int len = old.length - ((int)(pre - offset)); + if (len >= leftRightbytebuffer && len > 0) { + result.add(new MemorySnippet(pre, new byte[len])); + totalBytes += len; + } + } + else { + //find all changed stuff and put result + long pre = offset; + int downCount = -1; //if downCount reaches zero, buffer should be written out + for (int j = 0; j < old.length; j++) { + if (old[j] != curr[j]) { + if (downCount <= 0) { + pre = Math.max(offset, offset + j - leftRightbytebuffer); + } + downCount = leftRightbytebuffer; + } + else { downCount -= 1; } + if (downCount == 0) { + int len = j - ((int)(pre - offset)); + result.add(new MemorySnippet(pre, new byte[len])); + totalBytes += len; + } + } + int len = old.length - ((int)(pre - offset)); + if (downCount > 0 && len >= leftRightbytebuffer) { + result.add(new MemorySnippet(pre, new byte[len])); + totalBytes += len; + } + } + } + fetchMemory(result); + + System.out.println("totalbytes after diff: " + totalBytes); + + return result; + } + + //currently not being used functions: + List searchOffsetForByteArray(byte[] toFind) { + if (toFind.length == 0) return null; + + System.out.println("*** Start searching for: " + new String(toFind)); + String memoryPath = "/proc/" + PID + "/mem"; + List results = new ArrayList<>(); + + Timer t = new Timer(); + t.start(); + + for (long[] map : maps) { + long begin = map[0]; + long end = map[1]; + try { + RandomAccessFile raf = new RandomAccessFile(memoryPath, "r"); + byte[] wholeBuffer = new byte[(int)(end - begin)]; + raf.seek(begin); + raf.read(wholeBuffer); + raf.close(); + + for (int i = 0; i < wholeBuffer.length; i++) { + + int subIndex = 0; + while (subIndex < toFind.length && wholeBuffer[i + subIndex] == toFind[subIndex]) subIndex++; + + if (subIndex == toFind.length) { + long result = (long)i + begin; + results.add(result); + System.out.println("* Found match for " + new String(toFind) + " at address: " + result); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + System.out.println("*** End searching for: " + new String(toFind) + " in " + t.delta() + "ms"); + return results; + } + List searchOffsetForString(String keyString) { + return searchOffsetForByteArray(keyString.getBytes()); + } + + public List findSharedKey (List snippets){ + + byte[] bounds = "09afAF".getBytes(); + + List results = new ArrayList<>(); + + int size = 54; + + for (MemorySnippet snippet : snippets) { + int count = 0; + byte[] data = snippet.getData(); + if (data.length >= size) { + for (int i = 0; i < data.length; i++) { + byte b = data[i]; + if ((b >= bounds[0] && b <= bounds[1]) || + (b >= bounds[2] && b <= bounds[3]) || + (b >= bounds[4] && b <= bounds[5])) { + count++; + } + else { + count = 0; + } + if (count == size && (i + 1 == data.length || !((data[i+1] >= bounds[0] && data[i+1] <= bounds[1]) || + (data[i+1] >= bounds[2] && data[i+1] <= bounds[3]) || + (data[i+1] >= bounds[4] && data[i+1] <= bounds[5]))) ) { + results.add(new String(Arrays.copyOfRange(data, i - size + 1, i + 1)) + " on location: " + (snippet.getOffset() + i)); + } + } + } + + } + return results; + } + + public List findSharedKey2() { + List memorySnippets = new ArrayList<>(); + + int buff = 15000000; + + long verystart = maps.get(0)[0]; + long veryend = maps.get(0)[1]; + + for (int i = 1; i < maps.size(); i++) { + long[] map = maps.get(i); + if (map[1] - veryend <= buff) { + veryend = map[1]; + } + else { + memorySnippets.add(new MemorySnippet(verystart, new byte[(int)(veryend - verystart + buff)])); + verystart = maps.get(i)[0]; + veryend = maps.get(i)[1]; + } + } + memorySnippets.add(new MemorySnippet(verystart, new byte[(int)(veryend - verystart + buff)])); + + fetchMemory(memorySnippets); + return findSharedKey(memorySnippets); + } + + + @SuppressWarnings("Duplicates") + public void pauseProcess() { + String[] args = new String[] {"kill", "-STOP", PID+""}; + Process proc; + try { + proc = new ProcessBuilder(args).start(); + proc.waitFor(); + proc.destroy(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("Duplicates") + public void resumeProcess() { + String[] args = new String[] {"kill", "-CONT", PID+""}; + Process proc; + try { + proc = new ProcessBuilder(args).start(); + proc.waitFor(); + proc.destroy(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) throws InterruptedException { + FlashClient client = FlashClient.create(); + client.refreshMemoryMaps(); + +// List gameHostOccurences = client.searchOffsetForString("game-nl.habbo.com"); +// List rsaOccurences = client.searchOffsetForString("xIBlMDUyODA4YzFhYmVmNjlhMWE2MmMzOTYzOTZiODU5NTVlMmZmNTIy"); +// List occ = client.searchOffsetForString("sirjonasxx"); + +// List l = client.createMemorySnippetList(); +// client.fetchMemory(l); +// Thread.sleep(1000); +// //what didnt change in the last 1000ms? +// List l2 = client.differentiate(l, false, 0); +// Thread.sleep(1000); +// //what changed in the last 1000ms? +// List l3 = client.differentiate(l2, true, 0); +// Thread.sleep(1000); +// //what didnt change in the last 1000ms? +// List l4 = client.differentiate(l3, false, 0); +// Thread.sleep(1000); +// //what changed in the last 1000ms? +// List l5 = client.differentiate(l4, true, 0); + +// client.updateMapLocationsSnippetList(l); + + List res = client.findSharedKey2(); + System.out.println(res); + + System.out.println("test"); + } + +} diff --git a/src/main/protocol/memory/MemoryUtils.java b/src/main/protocol/memory/MemoryUtils.java new file mode 100644 index 0000000..0a5d0a3 --- /dev/null +++ b/src/main/protocol/memory/MemoryUtils.java @@ -0,0 +1,34 @@ +package main.protocol.memory; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +class MemoryUtils { + + static boolean stringIsNumeric(String str) { + for (char c : str.toCharArray()) { + if (c < '0' || c > '9') return false; + } + return true; + } + + static boolean fileContainsString(String path, String contains) { + + try { + List lines = Files.readAllLines(new File(path).toPath()); + for (String line : lines) { + if (line.contains(contains)) return true; + } + } catch (Exception e) { + // process of specified path not running anymore + } + return false; + + } + +} diff --git a/src/main/protocol/memory/Rc4Obtainer.java b/src/main/protocol/memory/Rc4Obtainer.java new file mode 100644 index 0000000..6e734e3 --- /dev/null +++ b/src/main/protocol/memory/Rc4Obtainer.java @@ -0,0 +1,195 @@ +package main.protocol.memory; + +import javafx.application.Platform; +import main.protocol.HPacket; +import main.protocol.packethandler.BufferListener; +import main.protocol.packethandler.IncomingHandler; +import main.protocol.packethandler.OutgoingHandler; +import main.protocol.memory.FlashClient; + +import java.util.List; + +public class Rc4Obtainer { + + public static Rc4Obtainer rc4Obtainer = null; + public static void initialize() { + rc4Obtainer = new Rc4Obtainer(); + rc4Obtainer.client = FlashClient.create(); + } + + FlashClient client = null; + OutgoingHandler outgoingHandler = null; + IncomingHandler incomingHandler = null; + + private Rc4Obtainer() { + + } + + public void setOutgoingHandler(OutgoingHandler handler) { + outgoingHandler = handler; + handler.addBufferListener(() -> { + if (handler.getCurrentIndex() >= 3) hasChangedFromLastCheck = true; + if (!hashappened1 && handler.getCurrentIndex() == 3) { + hashappened1 = true; + onSendFirstEncryptedMessage(); + } + }); + } + + public void setIncomingHandler(IncomingHandler handler) { + incomingHandler = handler; + handler.addBufferListener(() -> { + if (!hashappened2 && handler.getCurrentIndex() == 1) { + hashappened2 = true; + onReceivePubKey(); + } + }); + } + + private List fullmemorybeforekey = null; + private boolean hasChangedFromLastCheck = false; + + private boolean hashappened2 = false; + private void onReceivePubKey() { + incomingHandler.block(); + new Thread(() -> { + + System.out.println("[+] receive pubkey"); + client.pauseProcess(); + + fullmemorybeforekey = client.createMemorySnippetList(); + client.fetchMemory(fullmemorybeforekey); + + System.out.println("[-] receive pubkey"); + + client.resumeProcess(); + incomingHandler.unblock(); + }).start(); + } + + + private boolean hashappened1 = false; + private void onSendFirstEncryptedMessage() { + incomingHandler.block(); + outgoingHandler.block(); + new Thread(() -> { + + System.out.println("[+] send encrypted"); + client.pauseProcess(); + client.updateMapLocationsSnippetList(fullmemorybeforekey); + client.resumeProcess(); + + List diff = searchForPossibleRC4Tables(fullmemorybeforekey); + System.out.println("size: " + getTotalBytesLengthOfDiff(diff)); + for (int i = 0; i < 20; i++) { +// if (i % 2 == 1) { +// incomingHandler.sendToStream(new HPacket(3631).toBytes()); +// } + sleep(200); + boolean rem = hasChangedFromLastCheck; + diff = searchForPossibleRC4Tables(diff); + System.out.println("size: " + getTotalBytesLengthOfDiff(diff) + " and was changed: " + rem); + } + + + System.out.println("[-] send encrypted"); + outgoingHandler.unblock(); + incomingHandler.unblock(); + }).start(); + + + } + + private List searchForPossibleRC4Tables(List snippets) { + List result; + client.pauseProcess(); + if (hasChangedFromLastCheck) { + result = client.differentiate(snippets, true, 255); + hasChangedFromLastCheck = false; + } + else { + result = client.differentiate(snippets, false, 4); + } + client.resumeProcess(); + + return result; + } + + private long getTotalBytesLengthOfDiff(List snippets) { + long tot = 0; + for (FlashClient.MemorySnippet snippet : snippets) { + tot += (snippet.getData().length); + } + return tot; + } + + private void sleep(int ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + + // System.out.println("[+] receive pubkey"); +// incomingHandler.block(); +// client.pauseProcess(); +// fullmemorybeforekey = client.createMemorySnippetList(); +// client.fetchMemory(fullmemorybeforekey); +// System.out.println("[-] receive pubkey"); +// incomingHandler.unblock(); +// client.resumeProcess(); + + // client.pauseProcess(); +// +// client.refreshMemoryMaps(); +// List result = client.findSharedKey2(); +// System.out.println("result size: "+ result); +// +// client.resumeProcess(); + +// System.out.println("[+] send encrypted"); +// client.pauseProcess(); +// client.updateMapLocationsSnippetList(fullmemorybeforekey); +// +// List diff = client.differentiate(fullmemorybeforekey, true, 54); +// +// List results = client.findSharedKey(diff); +// System.out.println("results: " +results.size()); +// for (String s : results) { +// System.out.println(s); +// } +// System.out.println("[-] send encrypted"); +// client.resumeProcess(); + +// payloadBuffer.push(buffer); +// buffer = new byte[]{}; +// tempBlockIncoming = true; +// client = FlashClient.create(); +// client.pauseProcess(); +// fullmemoryb4publickey = client.createMemorySnippetList(); +// client.fetchMemory(fullmemoryb4publickey); +// client.resumeProcess(); + + + +// if (!doneFlash) { +// tempBlockEncrypted = true; +// FlashClient client = IncomingHandler.client; +// List mem = IncomingHandler.fullmemoryb4publickey; +// client.pauseProcess(); +// client.updateMapLocationsSnippetList(mem); +// List diff = client.differentiate(mem, true, 54); +// IncomingHandler.fullmemoryb4publickey = null; +// List results = client.findSharedKey(diff); +// System.out.println("results: " +results.size()); +// for (String s : results) { +// System.out.println(s); +// } +// client.resumeProcess(); +// tempBlockEncrypted = false; +// IncomingHandler.tempBlockIncoming = false; +// doneFlash = true; +// } +} diff --git a/src/main/protocol/packethandler/BufferListener.java b/src/main/protocol/packethandler/BufferListener.java new file mode 100644 index 0000000..1a459fb --- /dev/null +++ b/src/main/protocol/packethandler/BufferListener.java @@ -0,0 +1,8 @@ +package main.protocol.packethandler; + + +public interface BufferListener { + + void act(); + +} diff --git a/src/main/protocol/packethandler/Handler.java b/src/main/protocol/packethandler/Handler.java new file mode 100644 index 0000000..054368e --- /dev/null +++ b/src/main/protocol/packethandler/Handler.java @@ -0,0 +1,100 @@ +package main.protocol.packethandler; + +import main.protocol.HMessage; +import main.protocol.TrafficListener; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public abstract class Handler { + + volatile PayloadBuffer payloadBuffer = new PayloadBuffer(); + volatile OutputStream out; + private volatile Object[] listeners = null; //get notified on packet send + private volatile boolean isTempBlocked = false; + volatile boolean isDataStream = false; + volatile int currentIndex = 0; + + + public Handler(OutputStream outputStream) { + out = outputStream; + } + + public boolean isDataStream() {return isDataStream;} + public void setAsDataStream() { + isDataStream = true; + } + + public void act(byte[] buffer, Object[] listeners) throws IOException { + this.listeners = listeners; + + if (isDataStream) { + payloadBuffer.push(buffer); + notifyBufferListeners(); + + if (!isTempBlocked) { + flush(); + } + else { + if (this instanceof OutgoingHandler) { + System.out.println("blocked outgoing bytes with size: "+ buffer.length); + } + } + } + else { + out.write(buffer); + } + } + + public void block() { + isTempBlocked = true; + } + public void unblock() { + try { + flush(); + } catch (IOException e) { + e.printStackTrace(); + } + isTempBlocked = false; + } + + /** + * LISTENERS CAN EDIT THE MESSAGE BEFORE BEING SENT + * @param message + */ + void notifyListeners(HMessage message) { + for (TrafficListener listener : (List)listeners[0]) { + listener.onCapture(message); + } + for (TrafficListener listener : (List)listeners[1]) { + listener.onCapture(message); + } + for (TrafficListener listener : (List)listeners[2]) { + listener.onCapture(message); + } + } + public abstract void sendToStream(byte[] buffer); + + public abstract void flush() throws IOException; + + + + private List bufferListeners = new ArrayList<>(); + public void addBufferListener(BufferListener listener) { + bufferListeners.add(listener); + } + public void removeBufferListener(BufferListener listener) { + bufferListeners.remove(listener); + } + private void notifyBufferListeners() { + for (int i = bufferListeners.size() - 1; i >= 0; i -= 1) { + bufferListeners.get(i).act(); + } + } + + public int getCurrentIndex() { + return currentIndex; + } +} diff --git a/src/main/protocol/packethandler/IncomingHandler.java b/src/main/protocol/packethandler/IncomingHandler.java new file mode 100644 index 0000000..b830469 --- /dev/null +++ b/src/main/protocol/packethandler/IncomingHandler.java @@ -0,0 +1,46 @@ +package main.protocol.packethandler; + +import main.protocol.HMessage; +import main.protocol.HPacket; +import main.protocol.memory.Rc4Obtainer; + +import java.io.IOException; +import java.io.OutputStream; + +public class IncomingHandler extends Handler { + + public IncomingHandler(OutputStream outputStream) { + super(outputStream); + } + + @Override + public void setAsDataStream() { + super.setAsDataStream(); + Rc4Obtainer.rc4Obtainer.setIncomingHandler(this); + } + + @Override + public void sendToStream(byte[] buffer) { + try { + out.write(buffer); + out.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void flush() throws IOException { + HPacket[] hpackets = payloadBuffer.receive(); + + for (HPacket hpacket : hpackets){ + HMessage hMessage = new HMessage(hpacket, HMessage.Side.TOCLIENT, currentIndex); + notifyListeners(hMessage); + + if (!hMessage.isBlocked()) { + out.write(hMessage.getPacket().toBytes()); + } + currentIndex++; + } + } +} diff --git a/src/main/protocol/packethandler/OutgoingHandler.java b/src/main/protocol/packethandler/OutgoingHandler.java new file mode 100644 index 0000000..ba83af8 --- /dev/null +++ b/src/main/protocol/packethandler/OutgoingHandler.java @@ -0,0 +1,67 @@ +package main.protocol.packethandler; + +import main.protocol.HMessage; +import main.protocol.HPacket; +import main.protocol.memory.Rc4Obtainer; + +import java.io.IOException; +import java.io.OutputStream; + +public class OutgoingHandler extends Handler { + + private final static int encryptOffset = 3; //all packets with index < 3 aren't encrypted + + public OutgoingHandler(OutputStream outputStream) { + super(outputStream); + } + + private void dataStreamCheck(byte[] buffer) { + if (!isDataStream) { + HPacket hpacket = new HPacket(buffer); + isDataStream = (hpacket.getBytesLength() > 6 && hpacket.headerId() == 4000 && hpacket.headerId() == 4000); + if (isDataStream) { + Rc4Obtainer.initialize(); + Rc4Obtainer.rc4Obtainer.setOutgoingHandler(this); + } + } + } + + @Override + public void act(byte[] buffer, Object[] listeners) throws IOException { + dataStreamCheck(buffer); + super.act(buffer, listeners); + } + + @Override + public void sendToStream(byte[] buffer) { + + } + + @Override + public void flush() throws IOException { + + if (currentIndex < encryptOffset) { + HPacket[] hpackets = payloadBuffer.receive(); + for (HPacket hpacket : hpackets){ + HMessage hMessage = new HMessage(hpacket, HMessage.Side.TOSERVER, currentIndex); + notifyListeners(hMessage); + if (!hMessage.isBlocked()) { + out.write(hMessage.getPacket().toBytes()); + } + currentIndex ++; + } + } + + if (currentIndex >= encryptOffset) { + if (payloadBuffer.peak().length > 0) { + HPacket packet = new HPacket(payloadBuffer.forceClear()); + HMessage hMessage = new HMessage(packet, HMessage.Side.TOSERVER, currentIndex); + + notifyListeners(hMessage); + out.write(packet.toBytes()); + currentIndex++; + } + + } + } +} diff --git a/src/main/protocol/packethandler/PayloadBuffer.java b/src/main/protocol/packethandler/PayloadBuffer.java new file mode 100644 index 0000000..7fac4b6 --- /dev/null +++ b/src/main/protocol/packethandler/PayloadBuffer.java @@ -0,0 +1,59 @@ +package main.protocol.packethandler; + +import main.protocol.HPacket; + +import java.util.ArrayList; +import java.util.Arrays; + +public class PayloadBuffer { + + private byte[] buffer = new byte[0]; + + public HPacket[] pushAndReceive(byte[] tcpData){ + push(tcpData); + return receive(); + } + public void push(byte[] tcpData) { + buffer = buffer.length == 0 ? tcpData : combineByteArrays(buffer, tcpData); + } + public HPacket[] receive() { + if (buffer.length < 6) return new HPacket[0]; + + HPacket total = new HPacket(buffer); + if (total.getBytesLength() - 4 == total.length()) { + buffer = new byte[0]; + return new HPacket[]{total}; + } + else if (total.getBytesLength() - 4 > total.length()) { + ArrayList all = new ArrayList<>(); + while (total.getBytesLength() >= 4 && total.getBytesLength() - 4 >= total.length()){ + all.add(new HPacket(Arrays.copyOfRange(buffer, 0, total.length() + 4))); + buffer = Arrays.copyOfRange(buffer, total.length() + 4, buffer.length); + total = new HPacket(buffer); + } + return all.toArray(new HPacket[all.size()]); + } + else { + return new HPacket[0]; + } + } + + + private byte[] combineByteArrays(byte[] arr1, byte[] arr2) { + byte[] combined = new byte[arr1.length + arr2.length]; + System.arraycopy(arr1,0,combined,0 ,arr1.length); + System.arraycopy(arr2,0,combined,arr1.length,arr2.length); + return combined; + } + + + public byte[] peak() { + return buffer; + } + public byte[] forceClear() { + byte[] buff = buffer; + buffer = new byte[0]; + return buff; + } + +} diff --git a/src/main/ui/G-Earth.fxml b/src/main/ui/G-Earth.fxml new file mode 100644 index 0000000..2dea4ee --- /dev/null +++ b/src/main/ui/G-Earth.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/ui/GEarthController.java b/src/main/ui/GEarthController.java new file mode 100644 index 0000000..1c210f8 --- /dev/null +++ b/src/main/ui/GEarthController.java @@ -0,0 +1,87 @@ +package main.ui; + +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Pane; +import javafx.stage.Stage; +import main.protocol.HConnection; +import main.ui.connection.ConnectionForm; +import main.ui.injection.InjectionForm; +import main.ui.logger.LoggerForm; +import main.ui.tools.ToolsForm; + +import java.awt.*; + +public class GEarthController { + + private Stage stage = null; + private volatile HConnection hConnection; + + public ConnectionForm connectionController; + public InjectionForm injectionController; + public LoggerForm loggerController; + public ToolsForm toolsController; + public Pane mover; + + public GEarthController() { + hConnection = new HConnection(); + } + + public void initialize() { + connectionController.setParentController(this); + injectionController.setParentController(this); + loggerController.setParentController(this); + toolsController.setParentController(this); + //custom header bar +// final Point[] startpos = {null}; +// final Double[] xx = {0.0}; +// final Double[] yy = {0.0}; +// final Boolean[] isMoving = {false}; +// +// mover.addEventHandler(MouseEvent.MOUSE_PRESSED, +// event -> { +// startpos[0] = MouseInfo.getPointerInfo().getLocation(); +// xx[0] = stage.getX(); +// yy[0] = stage.getY(); +// isMoving[0] = true; +// }); +// +// mover.addEventHandler(MouseEvent.MOUSE_RELEASED, +// event -> { +// isMoving[0] = false; +// }); +// +// +// mover.setOnMouseDragged(event -> { +// if (isMoving[0]) { +// Point now = MouseInfo.getPointerInfo().getLocation(); +// double diffX = now.getX() - startpos[0].getX(); +// double diffY = now.getY() - startpos[0].getY(); +// stage.setX(xx[0] + diffX); +// stage.setY(yy[0] + diffY); +// } +// }); + } + + public void setStage(Stage stage) { + this.stage = stage; + } + public Stage getStage() { + return stage; + } + + HConnection getHConnection() { + return hConnection; + } + void writeToLog(javafx.scene.paint.Color color, String text) { + loggerController.miniLogText(color, text); + } + + + public void abort() { + hConnection.abort(); + } + +} diff --git a/src/main/ui/SubForm.java b/src/main/ui/SubForm.java new file mode 100644 index 0000000..6c04b1e --- /dev/null +++ b/src/main/ui/SubForm.java @@ -0,0 +1,32 @@ +package main.ui; + + +import main.protocol.HConnection; +import main.protocol.HMessage; +import main.protocol.HPacket; + +import java.awt.*; + +public class SubForm { + + GEarthController parentController; + + public void setParentController(GEarthController controller) { + parentController = controller; + onParentSet(); + } + + + //abstract + protected void onParentSet() { + + } + + protected HConnection getHConnection() { + return parentController.getHConnection(); + } + protected void writeToLog(javafx.scene.paint.Color color, String text) { + parentController.writeToLog(color, text); + } + +} diff --git a/src/main/ui/bootstrap3.css b/src/main/ui/bootstrap3.css new file mode 100644 index 0000000..a79da42 --- /dev/null +++ b/src/main/ui/bootstrap3.css @@ -0,0 +1,893 @@ +.root { + -fx-body-color : #F5F5F5; + -fx-outer-border : #cecece; +} +.button,.menu-button,.toggle-button,.split-menu-button { + -fx-font-size: 14; + -fx-background-radius: 4; + -fx-border-radius: 4; + -fx-pref-height: 30; + -fx-min-width: 30; +} +.button,.menu-button,.split-menu-button,.toggle-button,.number-button { + -fx-background-insets: 0, 0, -1, 0; +} + +.split-menu-button > .label { + -fx-border-radius: 4 0 0 4; + -fx-background-radius: 3 0 0 3; +} + +.split-menu-button > .arrow-button { + -fx-border-radius: 0 4 4 0; + -fx-background-radius: 0 3 3 0; +} + +.lg { + -fx-min-height: 46; + -fx-max-height: 46; + -fx-font-size: 18; +} + +.sm { + -fx-min-height: 30; + -fx-max-height: 30; +} + +.xs { + -fx-min-height: 22; + -fx-max-height: 22; + -fx-font-size: 10; +} + +.primary .arrow, +.success .arrow, +.info .arrow, +.warning .arrow, +.danger .arrow { + -fx-background-color: transparent, white; +} + +.primary > .label, +.success > .label, +.info > .label, +.warning > .label, +.danger > .label { + -fx-text-fill: white; +} + +.action-btn { + -fx-min-width: 80; +} + +/*positions*/ +/*first*/ +.button.first, .menu-button.first, .toggle-button.first, .text-field.first, .text-area.first { + -fx-border-radius: 4 0 0 4; + -fx-background-radius: 4 0 0 4; +} + +.split-menu-button.first > .arrow-button, .split-menu-button.middle > .arrow-button { + -fx-border-radius: 0; + -fx-background-radius: 0; +} + +VBox > .button.first, +VBox > .menu-button.first, +VBox > .toggle-button.first, +VBox > .split-menu-button.first, +VBox > .text-field.first, +VBox > .text-area.first { + -fx-border-radius: 4 4 0 0; + -fx-background-radius: 4 4 0 0; +} + +VBox > .split-menu-button.first > .label { + -fx-border-radius: 4 0 0 0; + -fx-background-radius: 3 0 0 0; +} +VBox > .split-menu-button.first > .arrow-button { + -fx-border-radius: 0 4 0 0; + -fx-background-radius: 0 3 0 0; +} +/*middle*/ +.middle { + -fx-border-radius: 0; + -fx-background-radius: 0; +} + +/*last*/ +.split-menu-button.middle > .label, .split-menu-button.last > .label { + -fx-border-radius: 0; + -fx-background-radius: 0; +} + +.split-menu-button.last { + -fx-border-radius: 0 4 4 0; + -fx-background-radius: 0 4 4 0; +} + +.button.middle, .text-field.middle, .text-area.middle, .split-menu-button.middle, .toggle-button.middle { + -fx-border-radius: 0; + -fx-background-radius: 0; +} + +.button.last, .text-field.last, .text-area.last, .split-menu-button.last, .toggle-button.last, .menu-button.last { + -fx-border-radius: 0 4 4 0; + -fx-background-radius: 0 4 4 0; +} +VBox > .button.last, +VBox > .menu-button.last, +VBox > .toggle-button.last, +VBox > .split-menu-button.last, +VBox > .text-field.last, +VBox > .text-area.last { + -fx-border-radius: 0 0 4 4; + -fx-background-radius: 0 0 4 4; +} + +VBox > .split-menu-button.last > .label { + -fx-border-radius: 0 0 0 4; + -fx-background-radius: 0 0 0 3; +} +VBox > .split-menu-button.last > .arrow-button { + -fx-border-radius: 0 0 4 0; + -fx-background-radius: 0 0 3 0; +} + +/*button styles*/ + +/*default button settings*/ + +/*bgcolor setting*/ +.color-picker,.date-picker > .arrow-button, +.number-button,.left-arrow-button,.right-arrow-button, +.button,.split-menu-button,.toggle-button,.menu-button, +.font-menu-button, .split-menu-button > .label, .split-menu-button > .arrow-button { + -fx-background-color: white; +} + +.color-picker,.date-picker > .arrow-button, +.button,.menu-button,.toggle-button,.number-button,.left-arrow-button,.right-arrow-button, +.font-menu-button, +.split-menu-button > .label,.split-menu-button > .arrow-button { + -fx-border-color: #cccccc; + -fx-text-fill: #333333; +} +/*just for the special split menu button*/ +.split-menu-button > .label { + -fx-border-width: 1 0 1 1; +} +/*for date picker arrow button*/ +.date-picker > .arrow-button { + -fx-border-radius: 0 4 4 0; +} +.combo-box > .arrow-button, .choice-box > .arrow-button { + -fx-border-width: 0; +} +/*hover state*/ +.color-picker:hover, +.date-picker:hover > .arrow-button, +.combo-box:hover,.choice-box:hover, +.number-button:hover,.left-arrow-button:hover,.right-arrow-button:hover, +.button:hover,.menu-button:hover,.toggle-button:hover, +.font-menu-button:hover, +.split-menu-button > .label:hover, .split-menu-button > .arrow-button:hover { + -fx-background-color: #e6e6e6; + -fx-border-color: #acacac; +} +/*pressed selected*/ +.color-picker:pressed,.color-picker:selected, +.number-button:pressed,.number-button:selected, +.date-picker:pressed > .arrow-button, +.combo-box:pressed > .arrow-button,.combo-box:selected > .arrow-button, +.choice-box:pressed > .arrow-button,.choice-box:selected > .arrow-button, +.font-menu-button:pressed,.font-menu-button:selected, +.left-arrow-button:pressed,.left-arrow-button:selected, +.right-arrow-button:pressed,.right-arrow-button:selected, +.button:pressed, .button:selected,.menu-button:pressed,.menu-button:selected +,.toggle-button:pressed,.toggle-button:selected, +.split-menu-button:pressed > .label, .split-menu-button > .arrow-button:pressed { + -fx-background-color: #e6e6e6; + -fx-border-color: #acacac; + -fx-effect: innershadow(gaussian, #adadad, 10, 0, 0, 3); +} + +/*primary*/ + +.button.primary,.split-menu-button.primary,.toggle-button.primary,.menu-button.primary, +.split-menu-button.primary > .label, .split-menu-button.primary > .arrow-button { + -fx-background-color: #337ab7; +} + +.button.primary,.menu-button.primary,.toggle-button.primary, +.split-menu-button.primary > .label,.split-menu-button.primary > .arrow-button { + -fx-border-color: #2e6da4; + -fx-text-fill: white; +} +/*hover state*/ +.button.primary:hover,.menu-button.primary:hover,.toggle-button.primary:hover, +.split-menu-button.primary > .label:hover, .split-menu-button.primary > .arrow-button:hover { + -fx-border-color: #204d74; + -fx-background-color: #286090; +} +/*pressed selected*/ +.button.primary:pressed, .button.primary:selected, +.menu-button.primary:pressed,.menu-button.primary:selected +,.toggle-button.primary:pressed,.toggle-button.primary:selected, +.split-menu-button.primary:pressed > .label, .split-menu-button.primary > .arrow-button:pressed { + -fx-background-color: #286090; + -fx-border-color: #204d74; + -fx-effect: innershadow(gaussian, #204d74, 10, 0, 0, 3); +} + +/*success*/ + +.button.success,.split-menu-button.success,.toggle-button.success,.menu-button.success, +.split-menu-button.success > .label, .split-menu-button.success > .arrow-button { + -fx-background-color: #5cb85c; +} + +.button.success,.menu-button.success,.toggle-button.success, +.split-menu-button.success > .label,.split-menu-button.success > .arrow-button { + -fx-border-color: #4cae4c; + -fx-text-fill: white; +} +/*hover state*/ +.button.success:hover,.menu-button.success:hover,.toggle-button.success:hover, +.split-menu-button.success > .label:hover, .split-menu-button.success > .arrow-button:hover { + -fx-border-color: #398439; + -fx-background-color: #449d44; +} +/*pressed selected*/ +.button.success:pressed, .button.success:selected, +.menu-button.success:pressed,.menu-button.success:selected +,.toggle-button.success:pressed,.toggle-button.success:selected, +.split-menu-button.success:pressed > .label, .split-menu-button.success > .arrow-button:pressed { + -fx-background-color: #449d44; + -fx-border-color: #398439; + -fx-effect: innershadow(gaussian, #398439, 10, 0, 0, 3); +} + +/*info*/ + +.button.info,.split-menu-button.info,.toggle-button.info,.menu-button.info, +.split-menu-button.info > .label, .split-menu-button.info > .arrow-button { + -fx-background-color: #5bc0de; +} + +.button.info,.menu-button.info,.toggle-button.info, +.split-menu-button.info > .label,.split-menu-button.info > .arrow-button { + -fx-border-color: #46b8da; + -fx-text-fill: white; +} +/*hover state*/ +.button.info:hover,.menu-button.info:hover,.toggle-button.info:hover, +.split-menu-button.info > .label:hover, .split-menu-button.info > .arrow-button:hover { + -fx-border-color: #269abc; + -fx-background-color: #31b0d5; +} +/*pressed selected*/ +.button.info:pressed, .button.info:selected, +.menu-button.info:pressed,.menu-button.info:selected +,.toggle-button.info:pressed,.toggle-button.info:selected, +.split-menu-button.info:pressed > .label, .split-menu-button.info > .arrow-button:pressed { + -fx-background-color: #31b0d5; + -fx-border-color: #269abc; + -fx-effect: innershadow(gaussian, #269abc, 10, 0, 0, 3); +} + +/*warning*/ + +.button.warning,.split-menu-button.warning,.toggle-button.warning,.menu-button.warning, +.split-menu-button.warning > .label, .split-menu-button.warning > .arrow-button { + -fx-background-color: #f0ad4e; +} + +.button.warning,.menu-button.warning,.toggle-button.warning, +.split-menu-button.warning > .label,.split-menu-button.warning > .arrow-button { + -fx-border-color: #eea236; + -fx-text-fill: white; +} +/*hover state*/ +.button.warning:hover,.menu-button.warning:hover,.toggle-button.warning:hover, +.split-menu-button.warning > .label:hover, .split-menu-button.warning > .arrow-button:hover { + -fx-border-color: #d58512; + -fx-background-color: #ec971f; +} +/*pressed selected*/ +.button.warning:pressed, .button.warning:selected, +.menu-button.warning:pressed,.menu-button.warning:selected +,.toggle-button.warning:pressed,.toggle-button.warning:selected, +.split-menu-button.warning:pressed > .label, .split-menu-button.warning > .arrow-button:pressed { + -fx-background-color: #ec971f; + -fx-border-color: #d58512; + -fx-effect: innershadow(gaussian, #d58512, 10, 0, 0, 3); +} + +/*danger*/ + +.button.danger,.split-menu-button.danger,.toggle-button.danger,.menu-button.danger, +.split-menu-button.danger > .label, .split-menu-button.danger > .arrow-button { + -fx-background-color: #d9534f; +} + +.button.danger,.menu-button.danger,.toggle-button.danger, +.split-menu-button.danger > .label,.split-menu-button.danger > .arrow-button { + -fx-border-color: #d43f3a; + -fx-text-fill: white; +} +/*hover state*/ +.button.danger:hover,.menu-button.danger:hover,.toggle-button.danger:hover, +.split-menu-button.danger > .label:hover, .split-menu-button.danger > .arrow-button:hover { + -fx-border-color: #ac2925; + -fx-background-color: #c9302c; +} +/*pressed selected*/ +.button.danger:pressed, .button.danger:selected, +.menu-button.danger:pressed,.menu-button.danger:selected +,.toggle-button.danger:pressed,.toggle-button.danger:selected, +.split-menu-button.danger:pressed > .label, .split-menu-button.danger > .arrow-button:pressed { + -fx-border-color: #ac2925; + -fx-background-color: #c9302c; + -fx-effect: innershadow(gaussian, #ac2925, 10, 0, 0, 3); +} + +.menu-item { + -fx-min-width: 200; +} + +.menu-item:focused { + -fx-background-color: #f5f5f5; +} + +.menu-item:focused > * { + -fx-text-fill: #262626; +} +.menu-item:focused .arrow { + -fx-background-color: #333333; +} + +.check-menu-item:checked:hover > .left-container > .check, +.check-menu-item:checked:focused > .left-container > .check, +.radio-menu-item:checked:hover > .left-container > .radio, +.radio-menu-item:checked:focused > .left-container > .radio { + -fx-background-color: #333333; +} + +.context-menu { + -fx-border-radius: 4; + -fx-background-radius: 4; + -fx-border-color: #bebec0; +} + +.context-menu > * { + -fx-padding: 5 0 5 0; +} + +.separator { + -fx-padding: 5 0 5 0; +} + +.text-field { + -fx-pref-height: 30; +} + +.combo-box, .choice-box { + -fx-background-insets: 0; + -fx-border-color: #cecece; + -fx-padding: -1; + -fx-border-width: 1; +} +.combo-box, .choice-box, .color-picker, .date-picker { + -fx-border-radius: 4; + -fx-background-radius: 4; + -fx-pref-height: 30; +} + +.combo-box:editable > .arrow-button { + -fx-background-color: white; +} + +.combo-box:editable > .arrow-button:hover { + -fx-background-color: #e6e6e6; +} + +.combo-box:editable > .arrow-button:pressed { + -fx-effect: innershadow(gaussian, #adadad, 10, 0, 0, 3); +} + +.combo-box > .text-input, .date-picker > .text-input { + -fx-background-radius: 4 0 0 4; + -fx-border-width: 0; +} + +.text-field, .text-area { + -fx-border-color: #cccccc; + -fx-background-color: white; + -fx-border-radius: 4; + -fx-background-radius: 4; + -fx-effect: innershadow(gaussian, transparent, 0, 0, 0, 0); +} + +.text-field:focused, .text-area:focused { + -fx-border-color: #66afe9; + -fx-effect: dropshadow(gaussian, #66afe9, 10, 0, 0, 0); +} + +.text-area .scroll-pane, .text-area .scroll-pane .content { + -fx-background-color: white; + -fx-background-radius: 4; +} + +.tab-pane .tab-header-background { + -fx-background-color: #f4f4f4; +} +.tab-pane.plain .tab-header-background { + -fx-background-color: transparent; +} + +.tab-pane .tab-header-area .tab { + -fx-border-radius: 4 4 0 0; + -fx-background-radius: 5 5 0 0; + -fx-background-color: transparent; + -fx-border-color: transparent; + -fx-padding: 3 10 5 10; + -fx-background-insets: 0; +} + +.tab-pane .tab-header-area .tab .tab-label { + -fx-text-fill: #337ab7; +} + +.tab-pane .tab-header-area .tab:hover { + -fx-background-color: #eeeeee; +} + +.tab-pane .tab-header-area .tab:selected { + -fx-focus-color: transparent; + -fx-border-color: #dddddd #dddddd white #dddddd; + -fx-background-color: white; +} +.tab-pane .tab-header-area .tab:selected .tab-label { + -fx-text-fill: #333333; +} +.tab-pane > .tab-content-area { + -fx-background-color: white; +} + +.tab-pane .tab-header-area .tab .tab-label { + -fx-focus-color: transparent; +} + +.tab-pane:focused > .tab-header-area > .headers-region > .tab:selected .focus-indicator { + -fx-border-color: transparent; +} + +.tab-pane > .tab-header-area > .headers-region > .tab > .tab-container > .tab-close-button { + -fx-background-color: #337ab7; +} +.tab-pane > .tab-header-area > .headers-region > .tab:selected > .tab-container > .tab-close-button { + -fx-background-color: #333333; +} +.tab-pane > .tab-header-area > .headers-region > .tab > .tab-container > .tab-close-button:hover { + -fx-background-color: red; + -fx-cursor: hand; +} + +.scroll-bar { + -fx-background-color: transparent; + -fx-background-radius: 0; + -fx-block-increment: 50; +} + +.corner { + -fx-background-color: transparent; +} + +.scroll-bar .decrement-button, .scroll-bar .decrement-arrow { + visibility: hidden; + -fx-pref-height: 1; + -fx-pref-width: 1; +} + +.scroll-bar .increment-button, .scroll-bar .increment-arrow { + visibility: hidden; + -fx-pref-height: 1; + -fx-pref-width: 1; +} + +.scroll-bar:vertical { + -fx-pref-width: 10; +} + +.scroll-bar:horizontal { + -fx-pref-height: 10; +} + +.scroll-bar:horizontal .track, +.scroll-bar:vertical .track { + -fx-background-color: transparent; + -fx-border-color: transparent; + -fx-background-radius: 5; +} + +.scroll-bar:vertical .track-background, +.scroll-bar:horizontal .track-background { + -fx-background-color: transparent; + -fx-background-insets: 0; + -fx-background-radius: 5; +} + +.scroll-bar:horizontal .thumb { + -fx-background-color: #c9c9c9; + -fx-background-insets: 2 0 2 0; + -fx-background-radius: 5; +} + +.scroll-bar:vertical .thumb { + -fx-background-color: #c9c9c9; + -fx-background-insets: 0 2 0 2; + -fx-background-radius: 5; +} + +.scroll-bar:horizontal .thumb:hover, +.scroll-bar:vertical .thumb:hover { + -fx-background-color: #b5b5b5; +} + +.scroll-bar:horizontal .thumb:pressed, +.scroll-bar:vertical .thumb:pressed { + -fx-background-color: #a0a0a0; +} + +.scroll-bar:vertical .increment-button, .scroll-bar:vertical .decrement-button { + -fx-background-color: transparent; + -fx-background-radius: 5; + -fx-padding: 5; +} + +.scroll-bar:horizontal .increment-button, .scroll-bar:horizontal .decrement-button { + -fx-background-color: transparent; + -fx-background-radius: 5; + -fx-padding: 5; +} + +.scroll-bar:vertical:focused, +.scroll-bar:horizontal:focused { + -fx-background-color: transparent, rgb(96, 96, 96), rgb(96, 96, 96); +} + +.menu-bar { + -fx-background-color: white; +} +.menu-bar > .container > .menu-button { + -fx-background-radius: 0; + -fx-background-insets: 0; + -fx-border-width: 0; + -fx-border-radius: 0; +} +.menu-bar > .container > .menu-button:hover, +.menu-bar > .container > .menu-button:showing { + -fx-background-color: #56c0e0; +} + +.color-palette { + -fx-background-color: white; +} + +.pagination > .pagination-control > .control-box { + -fx-spacing: -1; +} +.pagination > .pagination-control > .control-box > .left-arrow-button { + -fx-border-radius: 3 0 0 3; + -fx-border-insets: 0 0 0 7; + -fx-background-insets: 0 0 0 7, 0 0 0 5, 1 1 1 6, 2 2 2 7; +} +.pagination > .pagination-control > .control-box > .right-arrow-button { + -fx-border-radius: 0 3 3 0; + -fx-border-insets: 0 7 0 0; + -fx-background-insets: 0 7 -1 0, 0 5 0 0, 1 6 1 1, 2 7 2 2; +} +.pagination > .pagination-control > .control-box > .number-button { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.progress-bar > .track { + -fx-pref-height: 10; + -fx-background-radius: 3; + -fx-effect: innershadow(gaussian, #e4e4e4, 4, 0, 0, 1); + -fx-background-color: #f5f5f5; +} + +.progress-bar > .bar { + -fx-background-insets: 0; + -fx-background-color: #337ab7; +} + +.progress-bar.success > .bar { + -fx-background-insets: 0; + -fx-background-color: #5cb85c; +} + +.progress-bar.info > .bar { + -fx-background-insets: 0; + -fx-background-color: #5bc0de; +} + +.progress-bar.warning > .bar { + -fx-background-insets: 0; + -fx-background-color: #f0ad4e +} + +.progress-bar.danger > .bar { + -fx-background-insets: 0; + -fx-background-color: #d9534f; +} + +.tooltip { + -fx-background: white; + -fx-text-fill: #333333; + -fx-background-color: white; + -fx-background-radius: 4px; + -fx-border-radius: 4px; + -fx-border-color: #C0C0C0; + -fx-background-insets: 0; + -fx-padding: 0.667em 0.75em 0.667em 0.75em; /* 10px */ + -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.5), 10, 0.0, 0, 3); + -fx-font-size: 0.85em; +} + +.tooltip.success { + -fx-background: #dff0d8; + -fx-background-color: #dff0d8; + -fx-text-fill: #99bb96; + -fx-border-color: #d6e9c6; +} + +.tooltip.info { + -fx-background: #d8ecf6; + -fx-background-color: #d8ecf6; + -fx-text-fill: #31708f; + -fx-border-color: #bce8f1; +} + +.tooltip.warning { + -fx-background: #fcf8e3; + -fx-background-color: #fcf8e3; + -fx-text-fill: #8a6e3c; + -fx-border-color: #faebcc; +} + +.tooltip.danger { + -fx-background: #f2dede; + -fx-background-color: #f2dede; + -fx-text-fill: #a94442; + -fx-border-color: #ebccd1; +} + +.titled-pane > .title { + -fx-background-color: #f5f5f5; + -fx-border-color: #dddddd; + /*-fx-background-insets: 5, 1, 5;*/ + -fx-background-radius: 3 3 0 0, 2 2 0 0, 1 1 0 0; + -fx-border-radius: 3 3 0 0, 2 2 0 0, 1 1 0 0; + -fx-padding: 11 +} + +.titled-pane > .content { + -fx-background-color: white; + -fx-background-radius: 0 0 4 4; + -fx-border-radius: 0 0 4 4; + -fx-border-color: #dddddd; + /*-fx-padding: -11;*/ +} + +.titled-pane.primary { + -fx-text-fill: white; +} + +.titled-pane.primary > .title { + -fx-background-color: #337ab7; + -fx-border-color: #337ab7; +} + +.titled-pane.primary > .content { + -fx-border-color: #337ab7; +} + +.titled-pane.success .arrow, +.titled-pane.info .arrow, +.titled-pane.warning .arrow, +.titled-pane.danger .arrow { + -fx-background-color: -fx-mark-highlight-color, -fx-mark-color; +} + +.titled-pane.success { + -fx-text-fill: #3c763d; +} + +.titled-pane.success > .title { + -fx-background-color: #dff0d8; + -fx-border-color: #d6e9c6; +} + +.titled-pane.success > .content { + -fx-border-color: #d6e9c6; +} + +.titled-pane.info { + -fx-text-fill: #31708f; +} + +.titled-pane.info > .title { + -fx-background-color: #d9edf7; + -fx-border-color: #bce8f1; +} + +.titled-pane.info > .content { + -fx-border-color: #bce8f1; +} + +.titled-pane.warning { + -fx-text-fill: #8a6d3b; +} + +.titled-pane.warning > .title { + -fx-background-color: #fcf8e3; + -fx-border-color: #faebcc; +} + +.titled-pane.warning > .content { + -fx-border-color: #faebcc; +} + +.titled-pane.danger { + -fx-text-fill: #a94442; +} + +.titled-pane.danger > .title { + -fx-background-color: #f2dede; + -fx-border-color: #eacbd0; +} + +.titled-pane.danger > .content { + -fx-border-color: #eacbd0; +} + +.accordion > .titled-pane > .title, +.accordion > .titled-pane > .content { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.tool-bar:vertical { /* left */ + -fx-border-color: transparent #dddddd transparent transparent; +} + +.tool-bar { /* top */ + -fx-background-color: white; + -fx-border-color: transparent transparent #dddddd transparent; +} + +.tool-bar:vertical { + -fx-background-insets: 0, 0 1 0 0; +} + +/*table view columns*/ +.filler,.column-header,.show-hide-columns-button { + -fx-background-color: #dddddd, white, white; +} +.show-hide-columns-button { + -fx-border-width: 0; + -fx-background-insets: 0 0 1 1; +} +.column-header:hover,.show-hide-columns-button:hover { + -fx-background-color: #dddddd, white, #f8f8f8; +} +.column-header-background > .filler { + -fx-border-color: transparent #dddddd transparent transparent; + -fx-border-insets: 0 1 0 0; +} +.column-drag-header { + -fx-background-color: #2fb254; +} + +/*split pane*/ +.split-pane > .split-pane-divider { + -fx-background-color: white; + -fx-border-color: #eeeeee; + -fx-pref-width: 8; +} +.split-pane:horizontal > .split-pane-divider { + -fx-background-insets: 0, 0 1 0 1; + -fx-border-width: 0 1 0 1; +} +/* vertical the two nodes are placed on top of each other. */ +.split-pane:vertical > .split-pane-divider { + -fx-background-insets: 0, 1 0 1 0; + -fx-border-width: 1 0 1 0; +} +.split-pane > .split-pane-divider:hover { + -fx-background-color: #E0E0E0; +} + +/******************************************************************************* + * * + * CheckBox * + * * + ******************************************************************************/ +.check-box > .box { + -fx-background-radius: 3; + /*-fx-padding: 0.166667em 0.166667em 0.25em 0.25em; !* 2 2 3 3 *!*/ + -fx-padding:0; + -fx-border-color: #56c0e0; + -fx-border-radius: 3; + -fx-background-color: white; +} +.check-box > .box > .mark { + -fx-background-color: null; + -fx-padding: 0.416667em 0.416667em 0.5em 0.5em; /* 5 5 6 6 */ + -fx-shape: "M927.936 272.992l-68.288-68.288c-12.608-12.576-32.96-12.576-45.536 0l-409.44 409.44-194.752-196.16c-12.576-12.576-32.928-12.576-45.536 0l-68.288 68.288c-12.576 12.608-12.576 32.96 0 45.536l285.568 287.488c12.576 12.576 32.96 12.576 45.536 0l500.736-500.768c12.576-12.544 12.576-32.96 0-45.536z"; + -fx-background-insets: -3 -3 1 0; +} +.check-box { + -fx-label-padding: 0.2em 0.0em 0.3em 0.416667em; /* 0 0 0 5 */ + -fx-text-fill: -fx-text-background-color; + -fx-padding: 0 0 2 0; +} +.check-box:indeterminate > .box { + -fx-padding: 0; +} +.check-box:selected > .box > .mark { + -fx-background-color: linear-gradient(to bottom, #56c0e0, #40b8dc); +} + +/******************************************************************************* + * * + * RadioButton * + * * + ******************************************************************************/ + +.radio-button { + -fx-label-padding: 0.0em 0.0em 0.1em 0.416667em; /* 0 0 0 5 */ + -fx-text-fill: -fx-text-background-color; + -fx-padding: 0 0 .5 0; +} +.radio-button > .radio, +.radio-button:focused > .radio { + -fx-border-color: #56c0e0; + -fx-border-radius: 1em; + -fx-background-radius: 1.0em; /* large value to make sure this remains circular */ + -fx-padding: 1 2 3 2; + -fx-background-color: white; +} +.radio-button > .radio > .dot { + -fx-background-color: transparent; + -fx-background-radius: 1.0em; /* large value to make sure this remains circular */ + -fx-padding: 0.333333em; /* 4 -- radius of the inner black dot when selected */ + -fx-background-insets: 3 2 1 2; +} +.radio-button:selected > .radio,.radio-button:hover > .radio { + -fx-fill-color: #56c0e0; +} +.radio-button:pressed > .radio { + -fx-background-color: #50c0e2; +} +.radio-button:selected > .radio > .dot { + -fx-background-color: #56c0e0; +} + +/*common things*/ +.check-box:hover > .box, +.check-box:selected > .box, +.radio-button:hover > .radio, +.radio-button:selected > .radio { + -fx-background-color: linear-gradient(to bottom, white, #efefef); +} + +.check-box:pressed > .box, +.radio-button:pressed > .radio { + -fx-background-color: #50c0e2; +} \ No newline at end of file diff --git a/src/main/ui/connection/Connection.fxml b/src/main/ui/connection/Connection.fxml new file mode 100644 index 0000000..d4590b8 --- /dev/null +++ b/src/main/ui/connection/Connection.fxml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/ui/connection/ConnectionForm.java b/src/main/ui/connection/ConnectionForm.java new file mode 100644 index 0000000..6c331b5 --- /dev/null +++ b/src/main/ui/connection/ConnectionForm.java @@ -0,0 +1,97 @@ +package main.ui.connection; + +import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.event.EventType; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import main.protocol.HConnection; +import main.ui.SubForm; + +import java.io.IOException; + +public class ConnectionForm extends SubForm { + + public ComboBox inpPort; + public ComboBox inpHost; + public Button btnConnect; + public Label lblState; + public TextField outHost; + public TextField outPort; + + private boolean isBusy = false; + + public void initialize() { + inpPort.getEditor().textProperty().addListener(observable -> { + try { + int i = Integer.parseInt(inpPort.getEditor().getText()); + btnConnect.setDisable(i < 0 || i >= 256 * 256); + } + catch (Exception e) { + btnConnect.setDisable(true); + } + }); + + inpPort.getItems().addAll("30000", "38101"); + inpHost.getItems().addAll("game-nl.habbo.com", "game-us.habbo.com"); + + inpPort.getSelectionModel().selectFirst(); + inpHost.getSelectionModel().selectFirst(); + } + + public void onParentSet(){ + getHConnection().addStateChangeListener((oldState, newState) -> Platform.runLater(() -> { + if (newState == HConnection.State.NOT_CONNECTED) { + inpHost.setDisable(false); + inpPort.setDisable(false); + lblState.setText("Not connected"); + btnConnect.setText("Connect"); + outHost.setText(""); + outPort.setText(""); + } + else if (oldState == HConnection.State.NOT_CONNECTED) { + inpHost.setDisable(true); + inpPort.setDisable(true); + btnConnect.setText("Abort"); + } + + if (newState == HConnection.State.CONNECTED) { + lblState.setText("Connected"); + outHost.setText(getHConnection().getHost()); + outPort.setText(getHConnection().getPort()+""); + } + if (newState == HConnection.State.WAITING_FOR_CLIENT) { + lblState.setText("Waiting for connection"); + } + + })); + } + + public void btnConnect_clicked(ActionEvent actionEvent) { + if (!isBusy) { + isBusy = true; + getHConnection().prepare(inpHost.getEditor().getText(), Integer.parseInt(inpPort.getEditor().getText())); + + if (HConnection.DEBUG) System.out.println("connecting"); + + new Thread(() -> { + try { + getHConnection().start(); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + } + else { + getHConnection().abort(); + isBusy = false; + } + } + +} diff --git a/src/main/ui/injection/Injection.fxml b/src/main/ui/injection/Injection.fxml new file mode 100644 index 0000000..fda9bd9 --- /dev/null +++ b/src/main/ui/injection/Injection.fxml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/ui/injection/InjectionForm.java b/src/main/ui/injection/InjectionForm.java new file mode 100644 index 0000000..7975515 --- /dev/null +++ b/src/main/ui/injection/InjectionForm.java @@ -0,0 +1,62 @@ +package main.ui.injection; + +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.scene.control.Button; +import javafx.scene.control.TextArea; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.text.Text; +import main.protocol.HConnection; +import main.protocol.HPacket; +import main.ui.SubForm; + +public class InjectionForm extends SubForm { + public TextArea inputPacket; + public Text lbl_corrruption; + public Text lbl_pcktInfo; + public Button btn_sendToServer; + public Button btn_sendToClient; + + protected void onParentSet() { + getHConnection().addStateChangeListener((oldState, newState) -> Platform.runLater(() -> + updateUI())); + + inputPacket.textProperty().addListener(event -> { Platform.runLater(() -> + updateUI()); + }); + } + + private void updateUI() { + HPacket packet = new HPacket(inputPacket.getText()); + if (!packet.isCorrupted()) { + lbl_corrruption.setText("isCorrupted: False"); + lbl_corrruption.setFill(Paint.valueOf("Green")); + lbl_pcktInfo.setText("header (id:"+packet.headerId()+", length:"+packet.length()+")"); + + btn_sendToClient.setDisable(getHConnection().getState() != HConnection.State.CONNECTED); + btn_sendToServer.setDisable(getHConnection().getState() != HConnection.State.CONNECTED); + } + else { + lbl_corrruption.setText("isCorrupted: True"); + lbl_corrruption.setFill(Paint.valueOf("#ee0404b2")); + lbl_pcktInfo.setText("header (id:NULL, length:"+packet.getBytesLength()+")"); + + btn_sendToClient.setDisable(true); + btn_sendToServer.setDisable(true); + } + + } + + public void sendToServer_clicked(ActionEvent actionEvent) { + HPacket packet = new HPacket(inputPacket.getText()); + getHConnection().sendToServerAsync(packet); + writeToLog(Color.BLUE, "SS -> packet with id: " + packet.headerId()); + } + + public void sendToClient_clicked(ActionEvent actionEvent) { + HPacket packet = new HPacket(inputPacket.getText()); + getHConnection().sendToClientAsync(packet); + writeToLog(Color.RED, "CS -> packet with id: " + packet.headerId()); + } +} diff --git a/src/main/ui/logger/Logger.fxml b/src/main/ui/logger/Logger.fxml new file mode 100644 index 0000000..d96cbcc --- /dev/null +++ b/src/main/ui/logger/Logger.fxml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/ui/logger/LoggerForm.java b/src/main/ui/logger/LoggerForm.java new file mode 100644 index 0000000..abfd249 --- /dev/null +++ b/src/main/ui/logger/LoggerForm.java @@ -0,0 +1,157 @@ +package main.ui.logger; + +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; +import main.protocol.HConnection; +import main.protocol.HMessage; +import main.ui.SubForm; + +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +public class LoggerForm extends SubForm { + + + public TextField txtPacketLimit; + public CheckBox cbx_blockIn; + public CheckBox cbx_blockOut; + public CheckBox cbx_showAdditional; + public CheckBox cbx_splitPackets; + public CheckBox cbx_useLog; + public TextFlow txt_logField; + public Button btnUpdate; + + private int packetLimit = 8000; + + public final static Map colorizePackets; + + static { + //FOR GNOME ONLY, shows up colorized packets + colorizePackets = new HashMap<>(); + colorizePackets.put("BLOCKED", (char)27 + "[35m"); // some kind of grey + colorizePackets.put("INCOMING", (char)27 + "[31m"); // red + colorizePackets.put("OUTGOING", (char)27 + "[34m"); // blue + colorizePackets.put("REPLACED", (char)27 + "[33m"); // yellow + + // others: + colorizePackets.put("INJECTED", ""); + colorizePackets.put("SKIPPED", (char)27 + "[36m"); + colorizePackets.put("DEFAULT", (char)27 + "[0m"); + + if (System.getenv("XDG_CURRENT_DESKTOP") == null || !System.getenv("XDG_CURRENT_DESKTOP").toLowerCase().contains("gnome")) { + for (String key : colorizePackets.keySet()) { + colorizePackets.put(key, ""); + } + } + } + + + public void onParentSet(){ + + getHConnection().addStateChangeListener((oldState, newState) -> Platform.runLater(() -> { + if (newState == HConnection.State.PREPARING) { + miniLogText(Color.ORANGE, "Connecting to "+getHConnection().getDomain() + ":" + getHConnection().getPort()); + } + if (newState == HConnection.State.CONNECTED) { + miniLogText(Color.GREEN, "Connecting to "+getHConnection().getDomain() + ":" + getHConnection().getPort()); + } + if (newState == HConnection.State.NOT_CONNECTED) { + miniLogText(Color.RED, "End of connection"); + } + })); + + getHConnection().addTrafficListener(2, message -> { Platform.runLater(() -> { + if (message.getDestination() == HMessage.Side.TOCLIENT && cbx_blockIn.isSelected() || + message.getDestination() == HMessage.Side.TOSERVER && cbx_blockOut.isSelected()) return; + + String splitter = cbx_splitPackets.isSelected() ? "-----------------------------------\n" : ""; + + String type = message.getDestination() == HMessage.Side.TOCLIENT ?"" + + colorizePackets.get("INCOMING") + "INCOMING" : + colorizePackets.get("OUTGOING") + "OUTGOING"; + + String additionalData = " "; + if (!message.isCorrupted() && cbx_showAdditional.isSelected()) { + additionalData = " (h:"+ message.getPacket().headerId() +", l:"+message.getPacket().length()+") "; + } + + String arrow = "--> "; + + String packet = message.isCorrupted() ? + message.getPacket().getBytesLength() + " (encrypted)": // message.getPacket().toString() : // TEMP CODE TO VIEW ENCRYPTED BODY + message.getPacket().length() < packetLimit ? + message.getPacket().toString() : + colorizePackets.get("SKIPPED") + "= " + (packetLimit) + ")>"; + + + String skipStyle = colorizePackets.get("DEFAULT"); + + String isBlocked = message.isBlocked() ? colorizePackets.get("BLOCKED") + "[BLOCKED] " : ""; + String isEdited = !message.getPacket().isReplaced() || message.isBlocked() ? "" : colorizePackets.get("REPLACED") + "[REPLACED] "; + System.out.println(splitter + isBlocked + isEdited + type + additionalData + arrow + packet + skipStyle); + + }); + }); + } + + public void updatePacketLimit(ActionEvent actionEvent) { + packetLimit = Integer.parseInt(txtPacketLimit.getText()); + } + + @SuppressWarnings("Duplicates") + public void initialize() { + txtPacketLimit.textProperty().addListener(observable -> { + boolean isInt = true; + + try { + Integer.parseInt(txtPacketLimit.getText()); + } catch (NumberFormatException e) { + isInt = false; + } + + btnUpdate.setDisable(!isInt); + }); + + txtPacketLimit.setOnKeyPressed(event -> { + if(event.getCode().equals(KeyCode.ENTER) && !btnUpdate.isDisable()) { + updatePacketLimit(null); + } + }); + } + + public void miniLogText(Color color, String text) { + if (cbx_useLog.isSelected()) { + String color2 = "#" + color.toString().substring(2, 8); + + Calendar rightNow = Calendar.getInstance(); + String hour = addToNumber(""+rightNow.get(Calendar.HOUR_OF_DAY)); + String minutes = addToNumber(""+rightNow.get(Calendar.MINUTE)); + String seconds = addToNumber(""+rightNow.get(Calendar.SECOND)); + String timestamp = "["+hour+":"+minutes+":"+seconds+"] "; + + timestamp = timestamp.replace(" ", "\u00A0"); // disable automatic linebreaks + Text time = new Text(timestamp); + time.setStyle("-fx-opacity: "+0.5+";"); + + text = text.replace(" ", "\u00A0"); + Text otherText = new Text(text + "\n"); + otherText.setStyle("-fx-fill: "+color2+";"); + + txt_logField.getChildren().addAll(time, otherText); + } + } + + private String addToNumber(String text) { + if (text.length() == 1) text = "0" + text; + return text; + } + +} diff --git a/src/main/ui/tools/Tools.fxml b/src/main/ui/tools/Tools.fxml new file mode 100644 index 0000000..362b480 --- /dev/null +++ b/src/main/ui/tools/Tools.fxml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/ui/tools/ToolsForm.java b/src/main/ui/tools/ToolsForm.java new file mode 100644 index 0000000..9242e43 --- /dev/null +++ b/src/main/ui/tools/ToolsForm.java @@ -0,0 +1,117 @@ +package main.ui.tools; + +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.control.Button; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import main.protocol.HPacket; +import main.ui.SubForm; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +public class ToolsForm extends SubForm { + public TextField txt_intDecoded; + public TextField txt_intEncoded; + public TextField txt_ushortDecoded; + public TextField txt_ushortEncoded; + public Button btnEncodeInt; + public Button btnDecodeInt; + public Button btnEncodeUShort; + public Button btnDecodeUshort; + + + public void initialize() { + txt_intDecoded.textProperty().addListener(observable -> { + boolean isInt = true; + + try { + Integer.parseInt(txt_intDecoded.getText()); + } catch (NumberFormatException e) { + isInt = false; + } + + btnEncodeInt.setDisable(!isInt); + }); + + txt_intDecoded.setOnKeyPressed(event -> { + if(event.getCode().equals(KeyCode.ENTER) && !btnEncodeInt.isDisable()) { + btnEncodeInt_clicked(null); + } + }); + + //--------------- + + txt_ushortDecoded.textProperty().addListener(observable -> { + boolean isDouble = true; + + try { + int res = Integer.parseInt(txt_ushortDecoded.getText()); + if (res < 0 || res >= (256*256)) isDouble = false; + } catch (NumberFormatException e) { + isDouble = false; + } + btnEncodeUShort.setDisable(!isDouble); + }); + + txt_ushortDecoded.setOnKeyPressed(event -> { + if(event.getCode().equals(KeyCode.ENTER) && !btnEncodeUShort.isDisable()) { + btnEncodeUShort_clicked(null); + } + }); + + //---------------- + + txt_intEncoded.textProperty().addListener(observable -> { + HPacket packet = new HPacket(txt_intEncoded.getText()); + btnDecodeInt.setDisable(packet.getBytesLength() != 4); + }); + + txt_intEncoded.setOnKeyPressed(event -> { + if(event.getCode().equals(KeyCode.ENTER) && !btnDecodeInt.isDisable()) { + btnDecodeInt_clicked(null); + } + }); + + //---------------- + + txt_ushortEncoded.textProperty().addListener(observable -> { + HPacket packet = new HPacket(txt_ushortEncoded.getText()); + btnDecodeUshort.setDisable(packet.getBytesLength() != 2); + }); + + txt_ushortEncoded.setOnKeyPressed(event -> { + if(event.getCode().equals(KeyCode.ENTER) && !btnDecodeUshort.isDisable()) { + btnDecodeUshort_clicked(null); + } + }); + + } + + public void btnEncodeInt_clicked(ActionEvent actionEvent) { + ByteBuffer b = ByteBuffer.allocate(4); + b.putInt(Integer.parseInt(txt_intDecoded.getText())); + + HPacket packet = new HPacket(b.array()); + txt_intEncoded.setText(packet.toString()); + } + + public void btnDecodeInt_clicked(ActionEvent actionEvent) { + txt_intDecoded.setText(new HPacket(txt_intEncoded.getText()).readInteger(0) + ""); + } + + public void btnEncodeUShort_clicked(ActionEvent actionEvent) { + ByteBuffer b = ByteBuffer.allocate(4); + b.putInt(Integer.parseInt(txt_ushortDecoded.getText())); + + HPacket packet = new HPacket(new byte[]{b.array()[2], b.array()[3]}); + txt_ushortEncoded.setText(packet.toString()); + } + + public void btnDecodeUshort_clicked(ActionEvent actionEvent) { + txt_ushortDecoded.setText(new HPacket(txt_ushortEncoded.getText()).readUshort(0) + ""); + } +}