Skip to content

Commit

Permalink
Release 1.0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
zhkl0228 committed Aug 26, 2024
1 parent 4c0ab26 commit 6cdcd23
Show file tree
Hide file tree
Showing 16 changed files with 154 additions and 34 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ impersonate browsers' TLS/JA3 and HTTP/2 fingerprints. If you are blocked by som
website for no obvious reason, you can give `impersonator` a try.

## Features
- Supports JA3/TLS fingerprints impersonation.
- Supports TLS/JA3/JA4 fingerprints impersonation.
- Supports HTTP/2 fingerprints impersonation.

## Usage
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.zhkl0228</groupId>
<artifactId>impersonator</artifactId>
<version>1.0.3</version>
<version>1.0.4</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
Expand All @@ -16,7 +16,7 @@
<kotlin.version>1.9.10</kotlin.version>
</properties>
<name>impersonator</name>
<description>Spoof TLS/JA3 and HTTP/2 fingerprints in Java</description>
<description>Spoof TLS/JA3/JA4 and HTTP/2 fingerprints in Java</description>
<url>https://github.com/zhkl0228/impersonator</url>
<licenses>
<license>
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/com/github/zhkl0228/impersonator/Android.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.Vector;

/**
* Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36
* v127.0.6533.120
*/
class Android extends ImpersonatorFactory {
Expand Down Expand Up @@ -48,8 +47,7 @@ protected void onInterceptRequest(Request.Builder builder) {
}

@Override
public void onSendClientHelloMessage(Map<Integer, byte[]> clientExtensions) throws IOException {
super.onSendClientHelloMessage(clientExtensions);
protected void onSendClientHelloMessageInternal(Map<Integer, byte[]> clientExtensions) throws IOException {
clientExtensions.put(ExtensionType.signed_certificate_timestamp, TlsUtils.EMPTY_BYTES);
clientExtensions.put(ExtensionType.session_ticket, TlsUtils.EMPTY_BYTES);
randomSupportedVersionsExtension(clientExtensions, ProtocolVersion.TLSv13, ProtocolVersion.TLSv12);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.zhkl0228.impersonator;

import java.io.IOException;
import java.util.Map;

public interface ExtensionListener {

void onClientExtensionsBuilt(Map<Integer, byte[]> clientExtensions) throws IOException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface ImpersonatorApi {

OkHttpClient newHttpClient(KeyManager[] km, TrustManager[] tm);

void setExtensionListener(ExtensionListener extensionListener);

}
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ static void randomExtension(Map<Integer, byte[]> clientExtensions, String order,
}
}

private static void sortExtensions(Map<Integer,byte[]> clientExtensions, Map<Integer,byte[]> copy, String order) {
public static void sortExtensions(Map<Integer,byte[]> clientExtensions, Map<Integer,byte[]> copy, String order) {
String[] tokens = order.split("-");
for(String token : tokens) {
int type = Integer.parseInt(token);
Expand All @@ -194,11 +194,24 @@ public void onEstablishSession(Map<Integer, byte[]> clientExtensions) throws IOE
}

@Override
public void onSendClientHelloMessage(Map<Integer, byte[]> clientExtensions) throws IOException {
public final void onSendClientHelloMessage(Map<Integer, byte[]> clientExtensions) throws IOException {
clientExtensions.remove(ExtensionType.status_request_v2);
clientExtensions.remove(ExtensionType.encrypt_then_mac);
onSendClientHelloMessageInternal(clientExtensions);
if (extensionListener != null) {
extensionListener.onClientExtensionsBuilt(clientExtensions);
}
}

private ExtensionListener extensionListener;

@Override
public void setExtensionListener(ExtensionListener extensionListener) {
this.extensionListener = extensionListener;
}

protected abstract void onSendClientHelloMessageInternal(Map<Integer, byte[]> clientExtensions) throws IOException;

private final int[] cipherSuites;
private final String userAgent;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.util.Vector;

/**
* Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
* v127.0.6533.120
*/
class MacChrome127 extends ImpersonatorFactory {
Expand Down Expand Up @@ -70,8 +69,7 @@ static void addApplicationSettingsExtension(Map<Integer, byte[]> clientExtension
}

@Override
public void onSendClientHelloMessage(Map<Integer, byte[]> clientExtensions) throws IOException {
super.onSendClientHelloMessage(clientExtensions);
protected void onSendClientHelloMessageInternal(Map<Integer, byte[]> clientExtensions) throws IOException {
clientExtensions.put(ExtensionType.signed_certificate_timestamp, TlsUtils.EMPTY_BYTES);
clientExtensions.put(ExtensionType.session_ticket, TlsUtils.EMPTY_BYTES);
randomSupportedVersionsExtension(clientExtensions, ProtocolVersion.TLSv13, ProtocolVersion.TLSv12);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.Vector;

/**
* Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:129.0) Gecko/20100101 Firefox/129.0
* v129.0.2
*/
class MacFirefox129 extends ImpersonatorFactory {
Expand Down Expand Up @@ -55,8 +54,7 @@ protected void onHttp2ConnectionInit(Http2Connection http2Connection) {
}

@Override
public void onSendClientHelloMessage(Map<Integer, byte[]> clientExtensions) throws IOException {
super.onSendClientHelloMessage(clientExtensions);
protected void onSendClientHelloMessageInternal(Map<Integer, byte[]> clientExtensions) throws IOException {
clientExtensions.put(ExtensionType.session_ticket, TlsUtils.EMPTY_BYTES);
addSignatureAlgorithmsExtension(clientExtensions, SignatureAndHashAlgorithm.create(SignatureScheme.ecdsa_secp256r1_sha256),
SignatureAndHashAlgorithm.create(SignatureScheme.ecdsa_secp384r1_sha384),
Expand Down
22 changes: 6 additions & 16 deletions src/main/java/com/github/zhkl0228/impersonator/MacSafari17.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.Vector;

/**
* Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15
* v17.5 (18618.2.12.111.5, 18618)
*/
class MacSafari17 extends ImpersonatorFactory {
Expand Down Expand Up @@ -47,19 +46,11 @@ private MacSafari17(Type type, String userAgent) {

@Override
protected void onInterceptRequest(Request.Builder builder) {
if (type == Type.MacSafari) {
builder.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
builder.header("Accept-Language", "zh-CN,zh-Hans;q=0.9");
builder.header("Sec-Fetch-Dest", "document");
builder.header("Sec-Fetch-Mode", "navigate");
builder.header("Sec-Fetch-Site", "none");
} else if (type == Type.iOS) {
builder.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
builder.header("Accept-Language", "zh-CN,zh-Hans;q=0.9");
builder.header("Sec-Fetch-Dest", "document");
builder.header("Sec-Fetch-Mode", "navigate");
builder.header("Sec-Fetch-Site", "none");
}
builder.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
builder.header("Accept-Language", "zh-CN,zh-Hans;q=0.9");
builder.header("Sec-Fetch-Dest", "document");
builder.header("Sec-Fetch-Mode", "navigate");
builder.header("Sec-Fetch-Site", "none");
}

@Override
Expand All @@ -86,8 +77,7 @@ protected void onHttp2ConnectionInit(Http2Connection http2Connection) {
}

@Override
public void onSendClientHelloMessage(Map<Integer, byte[]> clientExtensions) throws IOException {
super.onSendClientHelloMessage(clientExtensions);
protected void onSendClientHelloMessageInternal(Map<Integer, byte[]> clientExtensions) throws IOException {
clientExtensions.put(ExtensionType.signed_certificate_timestamp, TlsUtils.EMPTY_BYTES);
addSignatureAlgorithmsExtension(clientExtensions, SignatureAndHashAlgorithm.create(SignatureScheme.ecdsa_secp256r1_sha256),
SignatureAndHashAlgorithm.rsa_pss_rsae_sha256,
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/bouncycastle/tls/OfferedPsks.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public OfferedPsks(Vector identities)
this(identities, null, -1);
}

private OfferedPsks(Vector identities, Vector binders, int bindersSize)
public OfferedPsks(Vector identities, Vector binders, int bindersSize)
{
if (null == identities || identities.isEmpty())
{
Expand Down
30 changes: 29 additions & 1 deletion src/test/java/com/github/zhkl0228/impersonator/AndroidTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package com.github.zhkl0228.impersonator;

import org.bouncycastle.tls.OfferedPsks;
import org.bouncycastle.tls.PskIdentity;
import org.bouncycastle.tls.TlsExtensionsUtils;

import java.util.Vector;

public class AndroidTest extends SSLProviderTest {

public void testBrowserLeaks() throws Exception {
Expand All @@ -20,8 +26,30 @@ public void testScrapFlyHttp2() throws Exception {
"Accept-Encoding,Accept-Language,Sec-Fetch-Dest,Sec-Fetch-Mode,Sec-Fetch-Site,User-Agent");
}

private ExtensionListener extensionListener;

public void testBrowserScan() throws Exception {
try {
extensionListener = clientExtensions -> {
Vector<PskIdentity> identities = new Vector<>();
identities.add(new PskIdentity(new byte[113], 1));
Vector<byte[]> binders = new Vector<>();
binders.add(new byte[33]);
TlsExtensionsUtils.addPreSharedKeyClientHello(clientExtensions, new OfferedPsks(identities, binders, 1));
};
doTestBrowserScan("t13d1517h2_8daaf6152771_00af0ad20359",
"fc74547072f11256f8c59f904a3b8ba2", "GREASE-772-771|2-1.1|1027-2052-1025-1283-2053-1281-2054-1537|1|2|GREASE-29-23-24|GREASE-4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53|0-10-11-13-16-17513-18-23-27-35-41-43-45-5-51-65037-65281-GREASE-GREASE");
} finally {
extensionListener = null;
}
}

@Override
protected ImpersonatorApi createImpersonatorApi() {
return ImpersonatorFactory.android();
ImpersonatorApi api = ImpersonatorFactory.android();
if (extensionListener != null) {
api.setExtensionListener(extensionListener);
}
return api;
}
}
5 changes: 5 additions & 0 deletions src/test/java/com/github/zhkl0228/impersonator/IOSTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public void testScrapFlyHttp2() throws Exception {
"Accept,Accept-Encoding,Accept-Language,Sec-Fetch-Dest,Sec-Fetch-Mode,Sec-Fetch-Site,User-Agent");
}

public void testBrowserScan() throws Exception {
doTestBrowserScan("t13d2014h2_a09f3c656075_87f85be62a52",
"e1730e79f9b04a90f132376a68c013ad", "GREASE-772-771-770-769|2-1.1|1027-2052-1025-1283-515-2053-2053-1281-2054-1537-513|1|1|GREASE-29-23-24-25|GREASE-4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10|0-10-11-13-16-18-21-23-27-43-45-5-51-65281-GREASE-GREASE");
}

@Override
protected ImpersonatorApi createImpersonatorApi() {
return ImpersonatorFactory.ios();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package com.github.zhkl0228.impersonator;

import org.bouncycastle.tls.ExtensionType;
import org.bouncycastle.tls.OfferedPsks;
import org.bouncycastle.tls.PskIdentity;
import org.bouncycastle.tls.TlsExtensionsUtils;

import java.util.Vector;

public class MacChromeTest extends SSLProviderTest {

public void testBrowserLeaks() throws Exception {
Expand All @@ -18,8 +25,30 @@ public void testScrapFlyHttp2() throws Exception {
"Accept,Accept-Encoding,Accept-Language,Cache-Control,Cookie,Sec-Ch-Ua,Sec-Ch-Ua-Mobile,Sec-Ch-Ua-Platform,Sec-Fetch-Dest,Sec-Fetch-Mode,Sec-Fetch-Site,Sec-Fetch-User,Upgrade-Insecure-Requests,User-Agent");
}

private ExtensionListener extensionListener;

public void testBrowserScan() throws Exception {
try {
extensionListener = clientExtensions -> {
Vector<PskIdentity> identities = new Vector<>();
identities.add(new PskIdentity(new byte[113], 1));
Vector<byte[]> binders = new Vector<>();
binders.add(new byte[33]);
TlsExtensionsUtils.addPreSharedKeyClientHello(clientExtensions, new OfferedPsks(identities, binders, 1));
};
doTestBrowserScan("t13d1517h2_8daaf6152771_00af0ad20359",
"a8aeb7ff0eab0699cbd1968f7930bd86", "GREASE-772-771|2-1.1|1027-2052-1025-1283-2053-1281-2054-1537|1|2|GREASE-25497-29-23-24|GREASE-4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53|0-10-11-13-16-17513-18-23-27-35-41-43-45-5-51-65037-65281-GREASE-GREASE");
} finally {
extensionListener = null;
}
}

@Override
protected ImpersonatorApi createImpersonatorApi() {
return ImpersonatorFactory.macChrome();
ImpersonatorApi api = ImpersonatorFactory.macChrome();
if (extensionListener != null) {
api.setExtensionListener(extensionListener);
}
return api;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package com.github.zhkl0228.impersonator;

import org.bouncycastle.tls.ExtensionType;
import org.bouncycastle.tls.OfferedPsks;
import org.bouncycastle.tls.PskIdentity;
import org.bouncycastle.tls.TlsExtensionsUtils;

import java.util.Vector;

public class MacFirefoxTest extends SSLProviderTest {

public void testBrowserLeaks() throws Exception {
Expand All @@ -20,8 +27,31 @@ public void testScrapFlyHttp2() throws Exception {
"Accept,Accept-Encoding,Accept-Language,Sec-Fetch-Dest,Sec-Fetch-Mode,Sec-Fetch-Site,Sec-Fetch-User,Upgrade-Insecure-Requests,User-Agent");
}

private ExtensionListener extensionListener;

public void testBrowserScan() throws Exception {
try {
extensionListener = clientExtensions -> {
Vector<PskIdentity> identities = new Vector<>();
identities.add(new PskIdentity(new byte[113], 1));
Vector<byte[]> binders = new Vector<>();
binders.add(new byte[33]);
TlsExtensionsUtils.addPreSharedKeyClientHello(clientExtensions, new OfferedPsks(identities, binders, 1));
clientExtensions.remove(ExtensionType.session_ticket);
};
doTestBrowserScan("t13d1715h2_5b57614c22b0_b783cf897974",
"ff1204efe089d7f732723ac52a644713", "772-771|2-1.1|1027-1283-1539-2052-2053-2054-1025-1281-1537-515-513|1||29-23-24-25-256-257|4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53|0-10-11-13-16-23-28-34-41-43-45-5-51-65037-65281");
} finally {
extensionListener = null;
}
}

@Override
protected ImpersonatorApi createImpersonatorApi() {
return ImpersonatorFactory.macFirefox();
ImpersonatorApi api = ImpersonatorFactory.macFirefox();
if (extensionListener != null) {
api.setExtensionListener(extensionListener);
}
return api;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public void testScrapFlyHttp2() throws Exception {
"Accept,Accept-Encoding,Accept-Language,Sec-Fetch-Dest,Sec-Fetch-Mode,Sec-Fetch-Site,User-Agent");
}

public void testBrowserScan() throws Exception {
doTestBrowserScan("t13d2014h2_a09f3c656075_87f85be62a52",
"e1730e79f9b04a90f132376a68c013ad", "GREASE-772-771-770-769|2-1.1|1027-2052-1025-1283-515-2053-2053-1281-2054-1537-513|1|1|GREASE-29-23-24-25|GREASE-4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10|0-10-11-13-16-18-21-23-27-43-45-5-51-65281-GREASE-GREASE");
}

@Override
protected ImpersonatorApi createImpersonatorApi() {
return ImpersonatorFactory.macSafari();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,18 @@ protected final void doTestScrapFlyHttp2(String http2_digest, String http2_finge
headers_fp_digest, obj.getString("headers_fp_digest"));
}
}

protected final void doTestBrowserScan(String ja4, String fp_hash, String fp) throws Exception {
JSONObject obj = doTestURL("https://tls.browserscan.net/api/tls");
JSONObject tls = obj.getJSONObject("tls");
assertNotNull(tls);
if(fp_hash != null) {
assertEquals(String.format("\n%s\n%s", fp, tls.getString("fp")),
fp_hash, tls.getString("fp_hash"));
}
if (ja4 != null) {
assertEquals(String.format("\n%s\n%s", ja4, tls.getString("ja4")),
ja4, tls.getString("ja4"));
}
}
}

0 comments on commit 6cdcd23

Please sign in to comment.