mirror of
https://github.com/apernet/OpenGFW.git
synced 2024-12-23 01:19:21 +08:00
Merge pull request #106 from apernet/wip-new-trojan
feat: new heuristics for trojan analyzer
This commit is contained in:
commit
f3b72895ad
@ -21,7 +21,7 @@ Telegram グループ: https://t.me/OpGFW
|
|||||||
- フル IP/TCP 再アセンブル、各種プロトコルアナライザー
|
- フル IP/TCP 再アセンブル、各種プロトコルアナライザー
|
||||||
- HTTP、TLS、QUIC、DNS、SSH、SOCKS4/5、WireGuard、その他多数
|
- HTTP、TLS、QUIC、DNS、SSH、SOCKS4/5、WireGuard、その他多数
|
||||||
- Shadowsocks の「完全に暗号化されたトラフィック」の検出など (https://gfw.report/publications/usenixsecurity23/en/)
|
- Shadowsocks の「完全に暗号化されたトラフィック」の検出など (https://gfw.report/publications/usenixsecurity23/en/)
|
||||||
- トロイの木馬キラー (https://github.com/XTLS/Trojan-killer) に基づくトロイの木馬 (プロキシプロトコル) 検出
|
- Trojan プロキシプロトコルの検出
|
||||||
- [WIP] 機械学習に基づくトラフィック分類
|
- [WIP] 機械学習に基づくトラフィック分類
|
||||||
- IPv4 と IPv6 をフルサポート
|
- IPv4 と IPv6 をフルサポート
|
||||||
- フローベースのマルチコア負荷分散
|
- フローベースのマルチコア負荷分散
|
||||||
|
@ -25,7 +25,7 @@ Telegram group: https://t.me/OpGFW
|
|||||||
- HTTP, TLS, QUIC, DNS, SSH, SOCKS4/5, WireGuard, and many more to come
|
- HTTP, TLS, QUIC, DNS, SSH, SOCKS4/5, WireGuard, and many more to come
|
||||||
- "Fully encrypted traffic" detection for Shadowsocks,
|
- "Fully encrypted traffic" detection for Shadowsocks,
|
||||||
etc. (https://gfw.report/publications/usenixsecurity23/en/)
|
etc. (https://gfw.report/publications/usenixsecurity23/en/)
|
||||||
- Trojan (proxy protocol) detection based on Trojan-killer (https://github.com/XTLS/Trojan-killer)
|
- Trojan (proxy protocol) detection
|
||||||
- [WIP] Machine learning based traffic classification
|
- [WIP] Machine learning based traffic classification
|
||||||
- Full IPv4 and IPv6 support
|
- Full IPv4 and IPv6 support
|
||||||
- Flow-based multicore load balancing
|
- Flow-based multicore load balancing
|
||||||
|
@ -21,7 +21,7 @@ Telegram 群组: https://t.me/OpGFW
|
|||||||
- 完整的 IP/TCP 重组,各种协议解析器
|
- 完整的 IP/TCP 重组,各种协议解析器
|
||||||
- HTTP, TLS, QUIC, DNS, SSH, SOCKS4/5, WireGuard, 更多协议正在开发中
|
- HTTP, TLS, QUIC, DNS, SSH, SOCKS4/5, WireGuard, 更多协议正在开发中
|
||||||
- Shadowsocks 等 "全加密流量" 检测 (https://gfw.report/publications/usenixsecurity23/zh/)
|
- Shadowsocks 等 "全加密流量" 检测 (https://gfw.report/publications/usenixsecurity23/zh/)
|
||||||
- 基于 Trojan-killer 的 Trojan 检测 (https://github.com/XTLS/Trojan-killer)
|
- Trojan 协议检测
|
||||||
- [开发中] 基于机器学习的流量分类
|
- [开发中] 基于机器学习的流量分类
|
||||||
- 同等支持 IPv4 和 IPv6
|
- 同等支持 IPv4 和 IPv6
|
||||||
- 基于流的多核负载均衡
|
- 基于流的多核负载均衡
|
||||||
|
@ -9,22 +9,14 @@ import (
|
|||||||
var _ analyzer.TCPAnalyzer = (*TrojanAnalyzer)(nil)
|
var _ analyzer.TCPAnalyzer = (*TrojanAnalyzer)(nil)
|
||||||
|
|
||||||
// CCS stands for "Change Cipher Spec"
|
// CCS stands for "Change Cipher Spec"
|
||||||
var trojanCCS = []byte{20, 3, 3, 0, 1, 1}
|
var ccsPattern = []byte{20, 3, 3, 0, 1, 1}
|
||||||
|
|
||||||
const (
|
// TrojanAnalyzer uses length-based heuristics to detect Trojan traffic based on
|
||||||
trojanUpLB = 650
|
// its "TLS-in-TLS" nature. The heuristics are trained using a decision tree with
|
||||||
trojanUpUB = 1000
|
// about 2000 samples. This is highly experimental and is known to have significant
|
||||||
trojanDownLB1 = 170
|
// false positives (about 8% false positives & 2% false negatives).
|
||||||
trojanDownUB1 = 180
|
// We do NOT recommend directly blocking all positive connections, as this is likely
|
||||||
trojanDownLB2 = 3000
|
// to break many normal TLS connections.
|
||||||
trojanDownUB2 = 7500
|
|
||||||
)
|
|
||||||
|
|
||||||
// TrojanAnalyzer uses a very simple packet length based check to determine
|
|
||||||
// if a TLS connection is actually the Trojan proxy protocol.
|
|
||||||
// The algorithm is from the following project, with small modifications:
|
|
||||||
// https://github.com/XTLS/Trojan-killer
|
|
||||||
// Warning: Experimental only. This method is known to have significant false positives and false negatives.
|
|
||||||
type TrojanAnalyzer struct{}
|
type TrojanAnalyzer struct{}
|
||||||
|
|
||||||
func (a *TrojanAnalyzer) Name() string {
|
func (a *TrojanAnalyzer) Name() string {
|
||||||
@ -32,7 +24,7 @@ func (a *TrojanAnalyzer) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *TrojanAnalyzer) Limit() int {
|
func (a *TrojanAnalyzer) Limit() int {
|
||||||
return 16384
|
return 512000
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *TrojanAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream {
|
func (a *TrojanAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) analyzer.TCPStream {
|
||||||
@ -41,9 +33,11 @@ func (a *TrojanAnalyzer) NewTCP(info analyzer.TCPInfo, logger analyzer.Logger) a
|
|||||||
|
|
||||||
type trojanStream struct {
|
type trojanStream struct {
|
||||||
logger analyzer.Logger
|
logger analyzer.Logger
|
||||||
active bool
|
first bool
|
||||||
upCount int
|
count bool
|
||||||
downCount int
|
rev bool
|
||||||
|
seq [4]int
|
||||||
|
seqIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTrojanStream(logger analyzer.Logger) *trojanStream {
|
func newTrojanStream(logger analyzer.Logger) *trojanStream {
|
||||||
@ -57,33 +51,48 @@ func (s *trojanStream) Feed(rev, start, end bool, skip int, data []byte) (u *ana
|
|||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
if !rev && !s.active && len(data) >= 6 && bytes.Equal(data[:6], trojanCCS) {
|
|
||||||
// Client CCS encountered, start counting
|
if s.first {
|
||||||
s.active = true
|
s.first = false
|
||||||
|
// Stop if it's not a valid TLS connection
|
||||||
|
if !(!rev && len(data) >= 3 && data[0] >= 0x16 && data[0] <= 0x17 &&
|
||||||
|
data[1] == 0x03 && data[2] <= 0x09) {
|
||||||
|
return nil, true
|
||||||
}
|
}
|
||||||
if s.active {
|
}
|
||||||
if rev {
|
|
||||||
// Down direction
|
if !rev && !s.count && len(data) >= 6 && bytes.Equal(data[:6], ccsPattern) {
|
||||||
s.downCount += len(data)
|
// Client Change Cipher Spec encountered, start counting
|
||||||
|
s.count = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.count {
|
||||||
|
if rev == s.rev {
|
||||||
|
// Same direction as last time, just update the number
|
||||||
|
s.seq[s.seqIndex] = len(data)
|
||||||
} else {
|
} else {
|
||||||
// Up direction
|
// Different direction, bump the index
|
||||||
if s.upCount >= trojanUpLB && s.upCount <= trojanUpUB &&
|
s.seqIndex += 1
|
||||||
((s.downCount >= trojanDownLB1 && s.downCount <= trojanDownUB1) ||
|
if s.seqIndex == 4 {
|
||||||
(s.downCount >= trojanDownLB2 && s.downCount <= trojanDownUB2)) {
|
// Time to evaluate
|
||||||
|
yes := s.seq[0] >= 100 &&
|
||||||
|
s.seq[1] >= 88 &&
|
||||||
|
s.seq[2] >= 40 &&
|
||||||
|
s.seq[3] >= 51
|
||||||
return &analyzer.PropUpdate{
|
return &analyzer.PropUpdate{
|
||||||
Type: analyzer.PropUpdateReplace,
|
Type: analyzer.PropUpdateReplace,
|
||||||
M: analyzer.PropMap{
|
M: analyzer.PropMap{
|
||||||
"up": s.upCount,
|
"seq": s.seq,
|
||||||
"down": s.downCount,
|
"yes": yes,
|
||||||
"yes": true,
|
|
||||||
},
|
},
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
s.upCount += len(data)
|
s.seq[s.seqIndex] = len(data)
|
||||||
|
s.rev = rev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Give up when either direction is over the limit
|
|
||||||
return nil, s.upCount > trojanUpUB || s.downCount > trojanDownUB2
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *trojanStream) Close(limited bool) *analyzer.PropUpdate {
|
func (s *trojanStream) Close(limited bool) *analyzer.PropUpdate {
|
||||||
|
@ -251,8 +251,7 @@ Check https://github.com/XTLS/Trojan-killer for more information.
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"trojan": {
|
"trojan": {
|
||||||
"down": 4712,
|
"seq": [170, 282, 167, 470],
|
||||||
"up": 671,
|
|
||||||
"yes": true
|
"yes": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user