You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I know this may not be within the scope of your answer, but I still hope you can help me out.
I want to use Java to construct a Trojan request, and it does follow the data in the document, but it never works. This is my code.
I'm sorry, I don't know where the problem is.
package org.example;
import org.example.entity.TrojanWrapperRequest;
import org.example.util.Sha224Util;
import javax.net.ssl.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @author mohuangNPC
* @version 1.0
* @date 2024/11/4 9:19
*/
public class TrojanMainTest {
private static final int LISTEN_PORT = 7890;
private static final String TROJAN_HOST = "xxx.xxx.xxx";
private static final int TROJAN_PORT = 443;
private static final String password = "password1";
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(LISTEN_PORT)) {
System.out.println("Trojan proxy server listening on port " + LISTEN_PORT);
while (true) {
Socket clientSocket = serverSocket.accept();
new Thread(new ClientHandler(clientSocket)).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* The specific thread that accepts the request
* +-----------------------+---------+----------------+---------+----------+
* | hex(SHA224(password)) | CRLF | Trojan Request | CRLF | Payload |
* +-----------------------+---------+----------------+---------+----------+
* | 56 | X'0D0A' | Variable | X'0D0A' | Variable |
* +-----------------------+---------+----------------+---------+----------+
*
* where Trojan Request is a SOCKS5-like request:
*
* +-----+------+----------+----------+
* | CMD | ATYP | DST.ADDR | DST.PORT |
* +-----+------+----------+----------+
* | 1 | 1 | Variable | 2 |
* +-----+------+----------+----------+
*/
static class ClientHandler implements Runnable {
private final Socket clientSocket;
public ClientHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
long threadId = Thread.currentThread().getId();
String requestLine = "";
try (InputStream clientInput = clientSocket.getInputStream();
OutputStream clientOutput = clientSocket.getOutputStream()) {
TrojanWrapperRequest trojanWrapperRequest = new TrojanWrapperRequest();
TrojanWrapperRequest.TrojanRequest trojanRequest = new TrojanWrapperRequest.TrojanRequest();
// Set password and Request
{
trojanRequest.setCmd(0X01);
String dstAddr = "";
int dstPort = 443;
int atyp = 1;
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int byteRead;
while ((byteRead = clientInput.read()) != -1) {
if (byteRead == '\n') {
break;
}
baos.write(byteRead);
}
requestLine = baos.toString(StandardCharsets.UTF_8.name());
System.err.println(threadId+"----Received line: " + requestLine);
if (requestLine != null) {
String[] parts = requestLine.split(" ");
if (parts.length > 1) {
String url = parts[1];
String host = extractHostDomain(url);
int port = extractPort(url);
System.err.println(threadId+"----Host: " + host);
dstAddr = host;
System.err.println(threadId+"----Port: " + port);
dstPort = port;
System.err.println(threadId+"----Is IP: " + isValidIPAddress(host));
if (isValidIPAddress(host)) {
atyp = 1;
} else {
atyp = 3;
}
}
}
}
trojanRequest.setAtyp(atyp);
trojanRequest.setDstPort(dstPort);
trojanRequest.setDstAddr(dstAddr);
}
trojanWrapperRequest.setPassword(Sha224Util.encryptThisString(password));
trojanWrapperRequest.setTrojanRequest(trojanRequest);
// Do not verify certificates
{
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory factory = sc.getSocketFactory();
try (SSLSocket trojanSocket = (SSLSocket) factory.createSocket(TROJAN_HOST, TROJAN_PORT);
InputStream trojanInput = trojanSocket.getInputStream();
OutputStream trojanOutput = trojanSocket.getOutputStream()) {
// begin handshake
trojanSocket.startHandshake();
System.err.println(threadId+"----trojan connect success");
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Assemble the data according to the Trojan protocol
{
out.write(trojanWrapperRequest.getPassword().getBytes(StandardCharsets.UTF_8));
out.write(0X0D);
out.write(0X0A);
out.write(trojanRequest.getCmd());
out.write(trojanRequest.getAtyp());
encodeAddress(trojanRequest.getAtyp(), out, trojanRequest.getDstAddr());
out.write((trojanRequest.getDstPort() >> 8) & 0xFF);
out.write(trojanRequest.getDstPort() & 0xFF);
out.write(0X0D);
out.write(0X0A);
// The domain name port has been read once when obtaining it, so it needs to be added here
out.write(requestLine.getBytes(StandardCharsets.UTF_8));
}
byte[] byteArray = out.toByteArray();
trojanOutput.write(byteArray);
Thread thread = new Thread(() -> {
clientToProxy(threadId, clientInput, trojanOutput);
});
thread.start();
clientToProxy(threadId,clientInput, trojanOutput);
proxyToClient(threadId,trojanInput, clientOutput);
// Waiting for the client to proxy forwarding to complete
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* Data from the client to the server
* @param threadId
* @param input
* @param output
*/
private static void clientToProxy(long threadId,InputStream input, OutputStream output) {
byte[] buffer = new byte[4096];
int bytesRead;
try {
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Response from the proxy server
* @param threadId
* @param input
* @param output
*/
private static void proxyToClient(long threadId,InputStream input, OutputStream output) {
byte[] buffer = new byte[4096];
int bytesRead;
try {
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static String extractHost(String url) {
if (url.startsWith("http://")) {
url = url.substring(7);
} else if (url.startsWith("https://")) {
url = url.substring(8);
}
String s = url.split("/")[0];
return url.split("/")[0];
}
/**
* Get the domain name or IP
* @param url
* @return
*/
private static String extractHostDomain(String url) {
if (url.startsWith("http://")) {
url = url.substring(7);
} else if (url.startsWith("https://")) {
url = url.substring(8);
}
String s = url.split("/")[0];
return s.split(":")[0];
}
/**
* Get Port
* @param url
* @return
*/
private static int extractPort(String url) {
int defaultPort = url.startsWith("https://") ? 443 : 80;
String host = extractHost(url);
if (host.contains(":")) {
String[] parts = host.split(":");
return Integer.parseInt(parts[1]);
}
return defaultPort;
}
/**
* Determine whether it is an IP or a domain name
* @param ip
* @return
*/
private static boolean isValidIPAddress(String ip) {
String ipPattern =
"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
return ip.matches(ipPattern);
}
/**
* Processing IP or domain name
* @param addressType
* @param out
* @param dstAddr
* @throws IOException
*/
private static void encodeAddress(int addressType, ByteArrayOutputStream out, String dstAddr) throws IOException {
if (addressType == 1) {
String[] split = dstAddr.split("\\.");
for (String item : split) {
int b = Integer.parseInt(item);
out.write(b);
}
} else if (addressType == 3) {
out.write(dstAddr.length());
out.write(dstAddr.getBytes(StandardCharsets.UTF_8));
} else {
throw new RuntimeException("error address");
}
}
}
The text was updated successfully, but these errors were encountered:
Check SHA-224 Hash Implementation to ensure that your Sha224Util.encryptThisString(password) function is generating the SHA-224 hash as expected, Trojan expects the hex-encoded version of the SHA-224 hash, so make sure the result is correct.
Trojan protocol expects specific line breaks (CRLF, 0x0D0A), so check that the formatting exactly matches the protocol, like the following if i am not mistaking (but dont take my word for it):
After the password hash, you should have a CRLF (0x0D, 0x0A).
At the end of the Trojan request section, you should also include a CRLF.
When encoding destination addresses, for domain names (ATYP 0x03), make sure you correctly write the length byte, and the UTF-8 address after. And ye, make sure the encoding is right.
Socks5-like request structure - make sure the trojan request follows the exact byte order
CMD (0x01) – usually represents CONNECT.
ATYP (0x01 for IPv4 or 0x03 for domain names).
DST.ADDR – encoded according to ATYP.
DST.PORT – encoded in big-endian (network byte order).
(found this on the internet, maybe it helps)
As i understand you are spawning a thread to forward data from clientInput to trojanOutput, and also handling data from trojanInput to clientOutput in proxyToClient(). just make sure these threads dont interfere with each other, maybe by calling flush() after writing data to ensure it's sent immediately
Also check that the SSLContext is correctly initialized and that trojanSocket.startHandshake() successfully establishes an ssl connection
Also, the simplest, check network configs and firewall rules.
I know this may not be within the scope of your answer, but I still hope you can help me out.
I want to use Java to construct a Trojan request, and it does follow the data in the document, but it never works. This is my code.
I'm sorry, I don't know where the problem is.
The text was updated successfully, but these errors were encountered: