Trojan-killer icon indicating copy to clipboard operation
Trojan-killer copied to clipboard

简单的测试结果以及实验建议

Open cty123 opened this issue 1 year ago • 25 comments

感谢大佬分享,我稍微在本地测试了一下。

Xray + Example 内用例测试

通过example里面的config,

Firefox: [email protected]:xxxxx -------> Xray [email protected]:11111 ------> Trojan [email protected]:12345 --------> Trojan [email protected]:22222 ------------> Freedom

准确率非常高。

其他Trojan实现测试

此外我分别用手机上的Sagernet trojan以及trojan-go连接trojan服务器,trojan的服务器是我自己的个人项目,用rust写的。在所有trojan的代理连接中,大概10% - 20%的请求能够被识别出来是trojan proxy,但是波动比较大,有时候好几分钟都不会识别出来一个,这可能跟rustls或者我自己的实现有关。我手动uncomment那行print,以下是一小部分的日志(这边手动把代理服务器写死到127.0.0.1:12346)

127.0.0.1:12346 upCount 334     downCount 4284
127.0.0.1:12346 upCount 671     downCount 4216
is Trojan
127.0.0.1:12346 upCount 1442    downCount 3864
127.0.0.1:12346 upCount 5940    downCount 5343
127.0.0.1:12346 upCount 2939    downCount 387
127.0.0.1:12346 upCount 588     downCount 200
127.0.0.1:12346 upCount 412     downCount 4856
127.0.0.1:12346 upCount 690     downCount 225
127.0.0.1:12346 upCount 349     downCount 4290
127.0.0.1:12346 upCount 349     downCount 4290
127.0.0.1:12346 upCount 349     downCount 4312
127.0.0.1:12346 upCount 349     downCount 4291
127.0.0.1:12346 upCount 318     downCount 5230
127.0.0.1:12346 upCount 318     downCount 5229
127.0.0.1:12346 upCount 415     downCount 5206
127.0.0.1:12346 upCount 415     downCount 5206
127.0.0.1:12346 upCount 1437    downCount 1283
127.0.0.1:12346 upCount 364     downCount 3485
127.0.0.1:12346 upCount 4648    downCount 1283
127.0.0.1:12346 upCount 671     downCount 178
is Trojan
127.0.0.1:12346 upCount 671     downCount 178
is Trojan
127.0.0.1:12346 upCount 2359    downCount 852
127.0.0.1:12346 upCount 988     downCount 883

Rust的实现

使用我自己的trojan在rust上面的实现,

Firefox: [email protected]:xxxxx -------> Trojan [email protected]:8080 ------> Trojan [email protected]:12345 --------> Trojan [email protected]:12346 ------------> Freedom

trojan-killer无法检测出任何连接,[重要!]但是这不代表Rust的TLS就一定安全,很有可能只是目前没有合适的upCount和downCount[重要!]。从流量分析中可以看出rust的TLS实现跟其他版本的TLS实现比较不一样

正常网页浏览误报

此外,我也测试了一下直接用浏览器http代理,打开各种网页,文档,youtube等等,确实如readme所说,没有一个请求被误报。

建议

  1. 虽然正常的网页浏览不会被误报,但是因为trojan的检测是通过上行以及下行流量的大小来判断的,可能会有其他的误报情况。我能想到的一个用例是restful服务的请求,很多简单的crud请求所产生的数据流量可能会导致误报。我觉得可以在实验里面加一组go实现的一个简单的restful微服务来作为对照组。

  2. 测试一下grpc以及quic是否能通过类似的方法检测

cty123 avatar May 14 '23 11:05 cty123

首先,感谢你的测试

此外,我也测试了一下直接用浏览器http代理,打开各种网页,文档,youtube等等,确实如readme所说,没有一个请求被误报。

其实还是有的,我和 @yuhan6665 都看到过,但出现概率非常非常之低,可以忽略不计的程度

我又看了下源代码,感觉这个检测方法总体来说是可行的,只是upCount和downCount根据不同的实现可能要选用不同的值。

我们这边的测试,包括群里很多人的测试结果都是直接刷屏(识别率目测至少有 80%)

我看了你贴上来的数据,发现一个问题,就是很多 upCount 看起来并不包含现代浏览器的 TLS Client Hello

现代浏览器的 TLS Client Hello 基本上就是 517 字节,所以这方面你可以排查下,毕竟是检测 TLS in TLS

有尝试将电脑端浏览器的 HTTP 代理设为 Trojan 吗?

RPRX avatar May 14 '23 12:05 RPRX

现代浏览器的 TLS Client Hello 基本上就是 517 字节

是的,我刚测了下,在firefox这里Client Hello刚好是517字节。不过不同的TLS实现会导致不少偏差,比如说Rust的rustls使用TLS1.3的情况下,ClientHello的大小会在648 - 654之间浮动

有尝试将电脑端浏览器的 HTTP 代理设为 Trojan 吗?

我感觉我测试环境的设置跟你测试的环境比较不一样,在pc端我是通过这种方法来测试的,

Firefox: [email protected]:xxxxx -------> Trojan [email protected]:8080 ------> Trojan [email protected]:12345 --------> Trojan [email protected]:12346 ------------> Freedom

跑下来什么都识别不了,我还在debug, 也不确定是不是我设置有问题,但是网络是通的,可以正常浏览网页。

============================================== Edit: 我把你Http响应的那段代码去掉了,trojan killer直接接收tcp请求然后转发到127.0.0.1:12346

Edit2: trojan client 和trojan server之间使用的是Rustls实现的TLS1.3

Edit3: 通过wireshark抓包,我发现firefox确实有把大小为517的包发给trojan client,但是trojan client没有发送过大小为517的包到trojan killer

Firefox到trojan client

Frame 163: 583 bytes on wire (4664 bits), 583 bytes captured (4664 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 37442, Dst Port: 8080, Seq: 41, Ack: 13, Len: 517

16 03 01...

看来是client hello了没错

cty123 avatar May 14 '23 13:05 cty123

比较规范的做法就是 padding 至 512(517)字节,rustls 应该也是 512(517)吧

我觉得你应该先试试 example 中的 trojan,有兴趣再弄其它的,~~不要本末倒置~~

RPRX avatar May 14 '23 13:05 RPRX

我试试看,另外通过wireshark抓包,rustls的clienthello是333

Frame 28: 399 bytes on wire (3192 bits), 399 bytes captured (3192 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 38468, Dst Port: 12346, Seq: 1, Ack: 1, Len: 333
    Source Port: 38468
    Destination Port: 12346
    [Stream index: 7]
    [Conversation completeness: Incomplete, DATA (15)]
    [TCP Segment Len: 333]
    Sequence Number: 1    (relative sequence number)
    Sequence Number (raw): 458511839
    [Next Sequence Number: 334    (relative sequence number)]
    Acknowledgment Number: 1    (relative ack number)
    Acknowledgment number (raw): 1960627581
    1000 .... = Header Length: 32 bytes (8)
    Flags: 0x018 (PSH, ACK)
    Window: 512
    [Calculated window size: 65536]
    [Window size scaling factor: 128]
    Checksum: 0xff75 [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps
    [Timestamps]
    [SEQ/ACK analysis]
    TCP payload (333 bytes)
Transport Layer Security

Frame 28: 399 bytes on wire (3192 bits), 399 bytes captured (3192 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 38468, Dst Port: 12346, Seq: 1, Ack: 1, Len: 333
Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 328
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 324
            Version: TLS 1.2 (0x0303)
            Random: 81e875007c354b95b72a67924c0d19799d914e232e83a5bbde2ff1ac03baafd7
            Session ID Length: 32
            Session ID: db341b7a9c886acfcc74327421bc2bcd0be2593605653c17329aee1c55737e3d
            Cipher Suites Length: 20
            Cipher Suites (10 suites)
            Compression Methods Length: 1
            Compression Methods (1 method)
            Extensions Length: 231
            Extension: supported_versions (len=5)
            Extension: ec_point_formats (len=2)
            Extension: supported_groups (len=8)
            Extension: signature_algorithms (len=20)
            Extension: extended_master_secret (len=0)
            Extension: status_request (len=5)
            Extension: server_name (len=16)
            Extension: signed_certificate_timestamp (len=0)
            Extension: key_share (len=38)
            Extension: psk_key_exchange_modes (len=2)
            Extension: pre_shared_key (len=91)
            [JA3 Fullstring: 771,4866-4865-4867-49196-49195-52393-49200-49199-52392-255,43-11-10-13-23-5-0-18-51-45-41,29-23-24,0]
            [JA3: 983a9cd8000c1d87618a64a6f5af2673]

Rustls这种实现可能解释了为什么前段时间没有被大规模封禁

cty123 avatar May 14 '23 13:05 cty123

其实关系不大,因为这里的 TLS in TLS 检测暂不关心外层 TLS 指纹,而内层 TLS 基本上就是浏览器的 HTTPS

前段时间没有出现特别的反馈说 rust 实现的 trojan 更抗封

RPRX avatar May 14 '23 14:05 RPRX

刚测了下,example没有任何问题,非常准。我在想rust的不准是否因为外层使用tls1.3的缘故,我看xray好像是1.2,1.3都支持的

cty123 avatar May 14 '23 14:05 cty123

我能看到的问题就是,你最初展示出来的数据中,upCount 大多非浏览器的 TLS Client Hello(现在你知道它固定为 517 了)

外层使用 TLSv1.3 是没问题的,~~反而我没测过外层 TLSv1.2~~,总之这个。。。你自己研究吧

(可能你需要在 issue 开头处添加说明,以避免 “10%-20%” 这个数据误导他人)

RPRX avatar May 14 '23 14:05 RPRX

不过你这个方法的可行性我觉得没什么问题,主要看误封的比例有多少,我感觉很多restful api可能会恰好符合这个特征,有空可以搭个微服务测一下

cty123 avatar May 14 '23 14:05 cty123

就,目前来说,我比较关心你在开头写的 “10%-20%”,我觉得你应当在开头补充一些说明,避免产生误导。

日常使用 trojan 浏览网页,就是我们的测试所针对的场景。(不过,大多数 APP 的 TLS 应该也是 517)

RPRX avatar May 14 '23 14:05 RPRX

就,目前来说,我比较关心你在开头写的 “10%-20%”,我觉得你应当在开头补充一些说明,避免产生误导。

我已经改了,但是我觉得这其实跟trojan关系不大,trojan + grpc 和 trojan + quic还没有证明能够识别。不知道为什么这个项目叫trojan-killer.

cty123 avatar May 14 '23 14:05 cty123

你确定 trojan+grpc 和 trojan+quic 是原版 trojan-gfw 有的东西吗?~~尤其是 trojan+quic 简直离谱。~~

RPRX avatar May 14 '23 14:05 RPRX

你确定 trojan+grpc 和 trojan+quic 是原版 trojan-gfw 有的东西吗?

我指的是trojan协议本身 https://trojan-gfw.github.io/trojan/protocol.html. Trojan + grpc 感觉比较异端一点,Trojan + quic我觉得没什么问题,自用了很久了。trojan协议本身只说了外层应该用TLS加密,没有限定TLS版本或者是传输层。等你把REALITY做出来,我能给你搞个Trojan + REALITY

cty123 avatar May 14 '23 14:05 cty123

还有一件事是,我可以给你说,trojan-killer 理论上对你用 rust 实现的 trojan 是完全有效的,因为 trojan 协议就是这么设计的,你用不同语言实现只是改了外层 TLS 指纹,基本上不会影响内层 TLS in TLS 的流量特征。你对着它没测出什么,大概率是你哪里没设置对,毕竟我也不知道你把代码改成啥样了,你需要再研究研究。

RPRX avatar May 14 '23 14:05 RPRX

你用不同语言实现只是改了外层 TLS 指纹,基本上不会影响内层 TLS in TLS 的流量特征

这点我完全同意,我在overview里面也说了,

很有可能只是目前没有合适的upCount和downCount

cty123 avatar May 14 '23 14:05 cty123

很有可能只是目前没有合适的upCount和downCount

不是,这个推测是错的,它们是通用的,是你哪里没设置对,~~是不是需要我写一下 TLS in TLS 检测的原理~~。

RPRX avatar May 14 '23 14:05 RPRX

你确定 trojan+grpc 和 trojan+quic 是原版 trojan-gfw 有的东西吗?

我指的是trojan协议本身 https://trojan-gfw.github.io/trojan/protocol.html. Trojan + grpc 感觉比较异端一点,Trojan + quic我觉得没什么问题,自用了很久了。trojan协议本身只说了外层应该用TLS加密,没有限定TLS版本或者是传输层。等你把REALITY做出来,我能给你搞个Trojan + REALITY

我们一般认为,trojan 协议为 trojan-gfw 实现的、trojan-gfw 客户端支持的、流行的那个 trojan 协议。

此外 REALITY 出来几个月了,并且 Xray 一直都支持 Trojan + REALITY 这种配法。。。

RPRX avatar May 14 '23 14:05 RPRX

不是,这个推测是错的,它们是通用的,是你哪里没设置对

有这个可能,我还在debug中

是不是需要我写一下 TLS in TLS 检测的原理

简单点来说你这个原理就是通过加密过后的tls 握手包的大小来判断的吧,主流浏览器的握手包都是固定的,当然欢迎你发文解释原理

cty123 avatar May 14 '23 15:05 cty123

但是我觉得这其实跟trojan关系不大

我大概猜出来了,你是觉得这个主要是和内外两层 TLS 有关,而和 trojan 协议本身关系不大。然而事实不是这样,这个就是 trojan 协议设计上的问题(若加一些 padding 就能规避这种简单的检测),并且其实它与外层 TLS 实现关系不大,所以它叫 trojan-killer。

RPRX avatar May 14 '23 15:05 RPRX

我大概猜出来了,你是觉得这个主要是和内外两层 TLS 有关,而和 trojan 协议本身关系不大

是的,我的想法是跟Trojan本身的关系不大,而是tls in tls暴露了内部的特征

并且其实它与外层 TLS 关系不大

这个问题主要是因为内层的TLS对吧?

cty123 avatar May 14 '23 15:05 cty123

我大概猜出来了,你是觉得这个主要是和内外两层 TLS 有关,而和 trojan 协议本身关系不大

是的,我的想法是跟Trojan本身的关系不大,而是tls in tls暴露了内部的特征

首先是客观存在 TLS in TLS 问题,其次是 trojan 没有处理该问题,才会导致这么容易就能检测。

并且其实它与外层 TLS 关系不大

这个问题主要是因为内层的TLS对吧?

我那句话的意思是,这里主要检测的就是内层 TLS,与你外层 TLS 用什么实现关系不大。

RPRX avatar May 14 '23 15:05 RPRX

首先是客观存在 TLS in TLS 问题,其次是 trojan 没有处理该问题,才会导致这么容易就能检测。

你这么说我也同意

cty123 avatar May 14 '23 15:05 cty123

嗯,这个仓库就是直观地揭露出确实存在的 TLS in TLS 问题

以及让大家看到要检测未处理该问题的 Trojan 协议有多么的容易和精准

RPRX avatar May 14 '23 15:05 RPRX

通过用wireshark比较分析流量,我发现firefox的ClientHello是这么一个逻辑,如果ClientHello的大小小于517, 那么会被加上padding使得大小刚好为517。但是在部分情况下,ClientHello会带有一个叫做

Extension: pre_shared_key (len=272)

的extension, 由于这个extension非常的长,往往会使得ClientHello的大小变成650左右,这个时候firefox就不会再去加padding, 而且这个clientHello的具体大小会受到sni长度的影响。

经过我的测试,如果Firefox发的clientHello请求的大小刚好为517, 那么CCS后面的数据包的大小会刚好落在 650 -750 字节这个区间内,这个时候你的if statement就会起效判断为trojan. 如果FF发的ClientHello请求的大小为650左右,CCS 后面的数据往往会超过800字节,导致判断失效。这是我说成功率只有10%-20%的原因。

Frame 370: 725 bytes on wire (5800 bits), 725 bytes captured (5800 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 55436, Dst Port: 11111, Seq: 236, Ack: 40, Len: 659
    Source Port: 55436
    Destination Port: 11111
    [Stream index: 10]
    [Conversation completeness: Incomplete, DATA (15)]
    [TCP Segment Len: 659]
    Sequence Number: 236    (relative sequence number)
    Sequence Number (raw): 1254675842
    [Next Sequence Number: 895    (relative sequence number)]
    Acknowledgment Number: 40    (relative ack number)
    Acknowledgment number (raw): 3762826686
    1000 .... = Header Length: 32 bytes (8)
    Flags: 0x018 (PSH, ACK)
    Window: 512
    [Calculated window size: 65536]
    [Window size scaling factor: 128]
    Checksum: 0x00bc [unverified]
    [Checksum Status: Unverified]
    Urgent Pointer: 0
    Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps
        TCP Option - No-Operation (NOP)
        TCP Option - No-Operation (NOP)
        TCP Option - Timestamps
    [Timestamps]
    [SEQ/ACK analysis]
    TCP payload (659 bytes)
Hypertext Transfer Protocol
Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 654
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 650
            Version: TLS 1.2 (0x0303)
            Random: 67a386ba35e4e7927f0cc8924987a4b66cff9d6b8662e5e20204f34b0aabd311
            Session ID Length: 32
            Session ID: c164eaa4d9bda436b5c1891b64eb8f45b1b04db83ea08567696d60abb5603344
            Cipher Suites Length: 34
            Cipher Suites (17 suites)
            Compression Methods Length: 1
            Compression Methods (1 method)
            Extensions Length: 543
            Extension: server_name (len=35)
                Type: server_name (0)
                Length: 35
                Server Name Indication extension
                    Server Name list length: 33
                    Server Name Type: host_name (0)
                    Server Name length: 30
                    Server Name: incoming.telemetry.mozilla.org
            Extension: extended_master_secret (len=0)
                Type: extended_master_secret (23)
                Length: 0
            Extension: renegotiation_info (len=1)
                Type: renegotiation_info (65281)
                Length: 1
                Renegotiation Info extension
            Extension: supported_groups (len=14)
                Type: supported_groups (10)
                Length: 14
                Supported Groups List Length: 12
                Supported Groups (6 groups)
            Extension: ec_point_formats (len=2)
                Type: ec_point_formats (11)
                Length: 2
                EC point formats Length: 1
                Elliptic curves point formats (1)
            Extension: application_layer_protocol_negotiation (len=14)
                Type: application_layer_protocol_negotiation (16)
                Length: 14
                ALPN Extension Length: 12
                ALPN Protocol
            Extension: status_request (len=5)
                Type: status_request (5)
                Length: 5
                Certificate Status Type: OCSP (1)
                Responder ID list Length: 0
                Request Extensions Length: 0
            Extension: delegated_credentials (len=8)
                Type: delegated_credentials (34)
                Length: 8
                Signature Hash Algorithms Length: 6
                Signature Hash Algorithms (3 algorithms)
            Extension: key_share (len=107)
                Type: key_share (51)
                Length: 107
                Key Share extension
            Extension: supported_versions (len=5)
                Type: supported_versions (43)
                Length: 5
                Supported Versions length: 4
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)
            Extension: signature_algorithms (len=20)
                Type: signature_algorithms (13)
                Length: 20
                Signature Hash Algorithms Length: 18
                Signature Hash Algorithms (9 algorithms)
            Extension: psk_key_exchange_modes (len=2)
                Type: psk_key_exchange_modes (45)
                Length: 2
                PSK Key Exchange Modes Length: 1
                PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
            Extension: record_size_limit (len=2)
                Type: record_size_limit (28)
                Length: 2
                Record Size Limit: 16385
            Extension: pre_shared_key (len=272)
                Type: pre_shared_key (41)
                Length: 272
                Pre-Shared Key extension
                    Identities Length: 235
                    PSK Identity (length: 229)
                    PSK Binders length: 33
                    PSK Binders

cty123 avatar May 14 '23 18:05 cty123

这种情况确实存在,~~下个版本安排~~,但是我看你贴上来的这些 upCount,看起来并不含这种情况,此外你可以用 Chrome 试试

RPRX avatar May 15 '23 00:05 RPRX

实际测试,准确度很高,正常的HTTPS流量的存在误报情况,但是非常少。实装的话,配合一定时间的持续检测,精确封锁应该是问题不大

juilletVent avatar May 15 '23 05:05 juilletVent

嗯,我现在只找到了一个问题。FF下upCount我建议再抓取一段800 - 850字节,用来适配附带pre_shared_key的情况。另外pre_shared_key出现的时候,downCount往往都在230多一点,还没查清楚具体为什么。工作忙,周末有空再接着测。

cty123 avatar May 15 '23 21:05 cty123

我觉得你可以先把你的 rust 实现测不出来的问题解决,这个应该比较简单,根据你的描述,可能你并没有把流量发给 trojan-killer

RPRX avatar May 16 '23 00:05 RPRX

Any update?

RPRX avatar Sep 03 '23 14:09 RPRX

Any update?

有的,一直拖着没发出来(懒)。根本原因是我在trojan的header这里没做缓存,导致整个trojan握手的header分了好几次发送,所以tls-in-tls的client hello总是在 第一次ssc之后才发出去,而不是之前,所以总是会检测失败。

cty123 avatar Sep 03 '23 15:09 cty123

导致整个trojan握手的header分了好几次发送

你的意思是 header 的每个字段分别发出去吗,这样的话每次都会增加十几字节,~~而且由于特征过于明显很少有人这么实现~~

如果你有时间的话,可否测试一下 Trojan-killer 对 Xray-core 以外其它代理软件的适用性:

  • clash(它把 header 和 body 分开发,会增加十几字节,~~当然对它来说直接检测客户端第二个 AEAD 包长度更精准~~)
  • v2fly(应该没有悬念,下面几个也是)
  • clash.meta
  • sing-box
  • trojan-go
  • trojan-gfw

RPRX avatar Sep 04 '23 05:09 RPRX