ShadowsocksR Protocol Plugin Documentation

ShadowsocksR Protocol Plugin Documentation


Overview

Used to conveniently generate various protocol interfaces. It works by wrapping the original protocol with an encoding and decoding layer, which not only allows it to masquerade as other protocol traffic, but also enables the original protocol to be converted into other protocols for compatibility or improvement (though the interface functionality is not yet fully implemented and is still being tested and refined). The server and client must be configured with the same protocol plugin. Plugins are divided into two categories: obfuscation plugins and protocol definition plugins.

Introduction to Existing Plugins

1. Obfuscation Plugins

This type of plugin is used to define the encrypted communication protocol, and is usually used for protocol obfuscation. Some plugins are compatible with the original protocol.

plain: indicates no obfuscation; data packets are sent directly using the encrypted output of the protocol

http_simple: This is not implemented fully according to the HTTP/1.1 standard. It only adds a GET request header and a simple response, after which the original protocol stream continues. After using this obfuscation, results observed in some regions suggest that it appears to have fooled QoS. For this kind of obfuscation, its purpose is not to reduce signatures; on the contrary, it provides a strong signature in an attempt to deceive the GFW’s protocol detection. Note that once its usage becomes widespread, its obvious signature may lead to blocking. This plugin can be compatible with the original protocol (the server must be configured as http_simple_compatible), and its latency is almost no different from the original protocol (and may even be faster in regions where QoS exists). Other than the header packet, there are no redundant packets. The client supports custom parameters, where the parameter is the host in the HTTP request. For example, setting it to cloudfront.com disguises it as a cloud server request. Multiple hosts can be separated by commas, such as a.com,b.net,c.org, in which case one will be used at random. Note that incorrect settings for this parameter may cause the connection to be terminated or even the IP to be blocked. If you are not sure how to set it, leave it blank. The server also supports custom parameters, whose meaning is a comma-separated list of parameters that clients are allowed to fill in.
Advanced settings for this plugin (supported by the C# version, Python version, and ssr-libev version): this plugin allows you to customize an almost complete HTTP
header, except that the GET and host in the first two lines cannot be modified; you can customize the content starting from the third line. Example:
baidu.com#User-Agent: abcnAccept: text/htmlnConnection:
keep-alive

This is the content to be filled into the obfuscation parameter. The part before the # is the host mentioned above, and the part after it is the custom header. All line breaks are represented by n (when written in the configuration file, you may also use n directly without having to write it as n; line break characters will also be converted). If you need to use a standalone character, you can write it as . There is no need to write n at the very end, as the program will automatically append two consecutive line breaks.

http_post: Mostly the same as http_simple, except that it uses the POST method to send data, which conforms to HTTP standards and is more deceptive, but the behavior of using only POST requests can easily be detected as anomalous through statistical analysis. This plugin is compatible with http_simple, and can also be compatible with the original protocol (the server must be configured as http_post_compatible). For parameter settings and related details, refer to http_simple. Be especially careful that if you use a custom HTTP
header, you must provide the boundary.

random_head (not recommended): Before communication begins, it sends an almost random data packet (currently the last 4 bytes are CRC32, which can become a fingerprint; an improved version will come later), after which the original protocol stream follows. The goal is to make the first packet contain no valid information at all, so statistical learning mechanisms can go to hell. This plugin is compatible with the original protocol (the server must be configured as random_head_compatible), but compared with the original protocol it adds one extra handshake, so connection time will be somewhat longer. Other than the handshake process, there are no redundant data packets, and custom parameters are not supported.

tls1.2_ticket_auth (strongly recommended): Simulates a TLS 1.2 handshake connection when the client has a session
ticket. It is currently a complete simulation implementation and, according to packet-capture software tests, perfectly disguises itself as TLS 1.2. Because there is a ticket, it does not send certificates or go through other complex steps, so firewalls cannot make judgments based on certificates. It also comes with some anti-replay capability and packet-length obfuscation. If a replay attack occurs, it can be found in the server log by searching with grep "replay attack"; you can use this plugin to detect whether the network in your region is interfering with TLS. Firewalls are fairly powerless against TLS, so its anti-blocking ability should be stronger than that of other plugins, though it may also encounter quite a bit of interference. However, the protocol itself can detect any interference, and when interference is encountered it disconnects immediately, avoiding long waits and letting the client or browser reconnect on its own. This plugin is compatible with the original protocol (the server must be configured as tls1.2_ticket_auth_compatible), but compared with the original protocol it adds one extra handshake, so connection time will be somewhat longer. When automatic reconnection is enabled in the C# client, it performs better than other plugins. The client supports custom parameters; the parameter is SNI, meaning the field used to send the host name. This function is very similar to TOR’s meek plugin. For example, setting it to cloudfront.net disguises it as a cloud server request. You can separate multiple hosts with commas, such as a.com,b.net,c.org, in which case one will be used at random. Note that incorrect settings for this parameter may cause the connection to be disconnected or even the IP to be blocked. If you are not sure how to set it, leave it blank. It is recommended to set the custom parameter to cloudflare.com or cloudfront.net. The server does not currently support custom parameters.

2. Protocol Definition Plugins

This type of plugin is used to define the protocol before encryption, usually for length obfuscation and to enhance security and stealth. Some plugins are compatible with the original protocol.

origin: Indicates use of the original SS protocol. This configuration provides the fastest speed and highest efficiency, and is suitable for environments with few restrictions or loose censorship. Otherwise, it is not recommended.

verify_deflate (not recommended): Applies deflate compression to every packet. The data format is: packet length (2 bytes) | compressed data stream | Adler-32 of the original data stream. This format omits the two-byte header 0x78, 0x9C. In addition, data that has already been compressed or encrypted is difficult to compress further (and may increase by 1 to 20 bytes), while unencrypted HTML text can achieve good compression results. Because compression and decompression are relatively CPU-intensive, this obfuscation plugin is not recommended for simultaneous use by many users. This plugin is not compatible with the original protocol; do not add the _compatible suffix under any circumstances.

verify_sha1 (the original OTA protocol, now deprecated): Performs SHA-1 verification on every packet. For a detailed protocol description, see One Time
Auth
. The handshake packet adds 10 bytes, and all other data packets add 12 bytes. This plugin is compatible with the original protocol (the server must be configured as verify_sha1_compatible).

auth_sha1 (deprecated): Performs SHA-1 verification on the first packet, and also sends a random client ID (4 bytes), connection ID (4 bytes), and Unix timestamp (4 bytes) generated by the client. Subsequent communication uses Adler-32 as the checksum. This plugin provides authentication resistant to ordinary replay attacks. By default, the same port supports at most 64 clients simultaneously; this value can be modified to limit the number of clients. With this plugin, the UTC time difference between the server and client must not exceed 1 hour; usually it is enough for the client to synchronize its local time and set the correct time zone. This plugin has the same handshake latency as the original protocol and is compatible with the original protocol (the server must be configured as auth_sha1_compatible). It supports custom server-side parameters; the parameter is a decimal integer indicating the maximum number of simultaneous clients.

auth_sha1_v2 (deprecated): Similar to auth_sha1, it removes time validation to avoid connection failures on some devices caused by time issues, expands the client ID to 8 bytes, and uses larger-length obfuscation. It is compatible with the original protocol (requires configuring auth_sha1_v2_compatible on the server side), supports server-side custom parameters, and the parameter is a decimal integer indicating the maximum number of simultaneous client uses.

auth_sha1_v4 (not recommended): Like auth_sha1, it performs SHA-1 verification on the first packet, and also sends a random client ID (4 bytes), connection ID (4 bytes), and Unix timestamp (4 bytes) generated by the client. Subsequent communication uses Adler-32 as the checksum, with packet length verified separately to resist packet capture replay detection. It uses larger-length obfuscation. The UTC time difference between the server and client using this plugin must not exceed 24 hours, meaning only the year and date need to be correct. It is compatible with the original protocol (requires configuring auth_sha1_v4_compatible on the server side), supports server-side custom parameters, and the parameter is a decimal integer indicating the maximum number of simultaneous client uses.

auth_aes128_md5 or auth_aes128_sha1 (both recommended): The authentication part of the first packet uses Encrypt-then-MAC mode to provide real immunity against CCA attacks on authentication packets, preventing various probing and replay attacks. This protocol also supports multi-user single-port mode; for specific setup methods, see breakwa11’s blog. The UTC time difference between the server and client using this plugin must not exceed 24 hours, meaning only the year and date need to be correct. There is also basic verification for the UDP portion. This plugin is not compatible with the original protocol, supports server-side custom parameters, and the parameter is a decimal integer indicating the maximum number of simultaneous client uses.

auth_chain_a (strongly recommended): The authentication part of the first packet uses Encrypt-then-MAC mode to provide real immunity against CCA attacks on authentication packets, preventing various probing and replay attacks. The data stream includes RC4 encryption, and this protocol also supports multi-user single-port mode. Different users cannot decrypt each other’s data, and the encryption key is different each time. For specific setup methods, see breakwa11’s blog. The UTC time difference between the server and client using this plugin must not exceed 24 hours, meaning only the year and date need to be correct. The UDP portion also includes encryption and length obfuscation. When using this plugin, it is recommended to use none for encryption. This plugin is not compatible with the original protocol, supports server-side custom parameters, and the parameter is a decimal integer indicating the maximum number of simultaneous client uses. The minimum value can be set directly to 1. This plugin can respond in real time to the actual number of clients (your client must have at least one connection that remains open to ensure it occupies one client slot; otherwise, if set to 1, other clients will definitely be unable to connect once one of them connects).

The auth_chain_a plugin is recommended. Among the above plugins, it has relatively strong obfuscation capabilities and the highest resistance to detection, making it difficult to identify and block even when used by multiple people. Also, if you want to publish a public proxy, all of the above auth plugins can strictly limit the number of client uses (note that if auth_sha1_v4_compatible is used, users will not be subject to the limit as long as they use the original protocol), and the auth_chain_a protocol provides the most precise limitation.

Obfuscation Features

name RTT encode speed bandwidth anti replay attack cheat QoS anti analysis
plain 0 100% 100% No 0 /
http_simple 0 20%/100% 20%/100% No 90 70
http_post 0 20%/100% 20%/100% No 100 70
random_head (X) 1 100% 85%/100% No 0 10
tls1.2_ticket_auth 1 98% 75%/ 95% Yes 100 90

Notes:

  • 20%/100% means the first packet is 20%, while the rest run at 100% speed (or bandwidth). For other obfuscation methods with
    RTT
    greater than 0, the first value indicates the estimated average effective bandwidth utilization when browsing ordinary web pages, and the latter indicates the value after excluding the handshake response, which applies when downloading large files.
  • RTT
    indicates whether this obfuscation introduces additional latency. 1 RTT means the time required for one round trip of communication data.
  • Obfuscation with
    RTT
    not equal to 0 and without anti replay attack
    capability carries a risk of active probing regardless of the protocol, meaning random_head is not recommended.
    If RTT is 0, then as long as the protocol is not
    origin, there is no risk of active probing. Of course, since the original protocol itself also carries a risk of active probing, there is no need to worry too much for now as no active probing behavior has currently been observed.
  • cheat QoS indicates the ability to deceive router QoS.
    100 means it can deceive perfectly, 0 means it has no effect at all, and around 50 means stricter routers can detect it.
  • anti analysis indicates resistance to protocol analysis. For plain,
    it depends on the protocol; the scores for the others are based on feedback from users online. A value of 100 indicates perfect disguise.

Protocol Features

Assume method = “aes-256-cfb”
All of the following protocols are anti-CPA

Name RTT Encoding speed Bandwidth CCA resistance Replay attack resistance MITM detection resistance Packet length analysis resistance
origin 0 100% 99% No No No 0
verify_deflate 0 30% 97%~110% No No No 6
verify_sha1 (X) 0 85% 98%/99% No No No 0
auth_sha1 (X) 0 95% 97% No Yes No 4
auth_sha1_v2 (X) 0 94% 80%/97% No Yes No 10
auth_sha1_v4 0 90% 85%/98% No Yes No 10
auth_aes128_md5 0 80% 70%/98% Yes Yes Yes 10
auth_aes128_sha1 0 70% 70%/98% Yes Yes Yes 10
auth_chain_a 0 70% 75%/98% Yes Yes Yes 15

Notes:

  • The above are average test results for browsing ordinary web pages (not downloading or watching videos). Different websites may produce different deviations.
  • encode
    speed is only intended as a reference for relative speed; code execution speed varies across different environments.
  • The 110% upper limit for verify_deflate bandwidth (effective bandwidth) is მხოლოდ an estimate. If the data is compressed or encrypted, the compression effect will be poor.
  • verify_sha1 bandwidth means an average effective bandwidth of 98% for uploads and 99% for downloads.
  • auth_aes128_md5 bandwidth is lower when browsing ordinary web pages (to provide stronger length obfuscation, while the size of each individual packet is kept within
    TCP MSS
    , so the actual impact on speed is very small). However, when watching videos or downloading, its effective data ratio is higher than auth_sha1 and can reach 97%, so there is no need to worry about download speed. auth_chain_a is similar to auth_aes128_md5.
  • If other obfuscation plugins are also used at the same time, the bandwidth value will be reduced. The exact impact depends on both the obfuscation plugin used and the web pages being browsed.
  • For the packet length analysis resistance column, the full score is 100, where 0 means completely ineffective and below 5 means only a slight effect. For the specific analysis method, please refer to papers by Principal Fang and others.
  • For the packet timing analysis resistance column, Principal Fang’s paper indicates that although it can be utilized, it is difficult to exploit in practice (that is, they have not yet made it practically usable), so nothing is currently being done about this.

Obfuscation and Protocol Configuration Recommendations

  • Recommended protocol: auth_chain_a is the best choice. In this case, it is recommended not to use encryption (set it to none), and the obfuscation can be chosen freely.
  • Encryption selection: if the protocol is auth_chain_a, then use none for encryption (but this does not mean the password can be left blank or mismatched on both sides). If the protocol is not auth_aes128_md5 or auth_aes128_sha1, then rc4 encryption cannot be used (rc4-md5 can be used). In this case, you can choose among rc4-md5, salsa20, and chacha20-ietf for encryption (rc4-md5 can be replaced with the aes series, and salsa20 can be replaced with chacha20 or bf-cfb). If using SSR, you can also specifically choose rc4-md5-6.
  • Recommended obfuscation: if QoS is obvious in your area, it is recommended to choose between http_simple and tls1.2_ticket_auth; the specific choice can be determined through your own testing. If choosing obfuscation actually makes it slower, then please choose plain. If you do not care about QoS but are concerned about whether your personal vps can remain usable for a long time, then choose plain or tls1.2_ticket_auth for obfuscation, and choose auth_chain_a, auth_aes128_md5, or auth_aes128_sha1 for the protocol.
  • If you use it for gaming, or in situations where connection latency matters, it is recommended not to use tls1.2_ticket_auth obfuscation; use another obfuscation method or plain instead.
  • On the server side, http_simple and http_post are mutually compatible, and there is no difference in how they are used.
  • If you are at a company, school, or in certain environments and find that the original SS protocol is unavailable, it is recommended that you enable http_simple, http_post, or tls1.2_ticket_auth obfuscation, and use port 80 or 443 accordingly. This usually solves the problem. It can also help bypass network restrictions in your environment (such as blocking access to cloud storage or prohibiting uploads, etc.)
  • If you use tls1.2_ticket_auth obfuscation or do not enable obfuscation, then it is best not to use origin or verify_sha1 for the protocol
  • If you use a two-layer proxy, generally you only need to consider using obfuscation or a strengthened protocol on the segment that crosses the firewall, unless it is for anonymity
  • If you find that your proxy suddenly stops working, but changing to another port makes it work again, or it works again after waiting 15 minutes to half an hour, please contact me in this case

Configuration Method

Server-side configuration: use the latest SSR manyuser branch
There is a field named protocol in user-config.json or config.json, and its possible values are currently:
origin
verify_deflate (not recommended)
verify_sha1 (obsolete)
verify_sha1_compatible (obsolete)
auth_sha1 (obsolete)
auth_sha1_compatible (obsolete)
auth_sha1_v2 (obsolete)
auth_sha1_v2_compatible (obsolete)
auth_sha1_v4 (not recommended)
auth_sha1_v4_compatible (not recommended)
auth_aes128_md5
auth_aes128_sha1
auth_chain_a

There is a field named obfs in user-config.json or config.json, and its possible values are currently:
plain
http_simple
http_simple_compatible
http_post
http_post_compatible
random_head (obsolete)
random_head_compatible (obsolete)
tls1.2_ticket_auth
tls1.2_ticket_auth_compatible

By default:
"protocol":"auth_aes128_md5",
"obfs":"tls1.2_ticket_auth_compatible",
Correspondingly,
the default protocol plugin parameter is "protocol_param":""
the default obfuscation plugin parameter is "obfs_param":""
For protocol, the server and client must match exactly
When the server is configured as xxabc_compatible (that is, with compatible as the suffix), it means the server supports using the original client, or an SSR client configured with the plugin xxabc or plain.

Client configuration: using this SSR version, find the corresponding node in Edit Server Configuration, then select the plugin you need from the lists for the protocol and obfs options, and fill in the corresponding parameters.

Compatibility

At present, the ssr-libev client, ssr-python, and ssr-csharp support all protocols and obfuscation methods that are not marked as obsolete or not recommended.

Implementation Interface

The following uses C# as an example for explanation

interface IObfs

Member functions

InitData()
Parameters: none
Returns: a variable of a custom type, usually used to store global information for this interface; it should not return null; in C, return void*
Description: called before creating the first instance; it will not be called repeatedly for the same server configuration; on the server side it is called when establishing the listener, and on the client side it is called on the first connection.

SetServerInfo(ServerInfo serverInfo)
Parameter: the ServerInfo structure, containing the following member variables:

  • host:
    string type, the server IP; the client needs to resolve the domain name to an IP; if there is a front proxy, the domain name used in the configuration can also be used directly; the server needs to obtain the listening IP
  • port: integer type, the server listening port
  • param: user-set parameter, string type
  • data:
    the result returned by InitData, of type object (in C, use void*)
  • iv:
    the IV array used by the client or server during encryption (in C, an additional field needs to be added to record its length; the same applies below)
  • recv_iv: the IV array received by the client or server
  • key:
    the key used for encryption (not the original key, but an array of the specified length generated via BytesToKey)
  • tcp_mss: integer type, TCP packet size, set to 1460
  • overhead: integer type, protocol header size, must be set by the caller

Return: none
Description: Called when the instance is constructed (when each connection is established). Before calling, iv and key must already be initialized; after data is received, recv_iv is initialized first and then the plugin is called.

int GetOverhead() Parameters: none
Return: the size of the additional header added by this plugin during communication

Dispose()
Parameters: none
Return: none
Description: Called when the instance is destructed (when each connection is closed)

byte[] ClientPreEncrypt(byte[] plaindata, int datalength,
out int outlength)

Parameters: the byte array to be processed and its length
Return: the processed byte array and its length
Description: This interface is called before encryption when the client sends data to the server

byte[] ClientEncode(byte[] encryptdata, int datalength,
out int outlength)

Parameters: the byte array to be encoded and its length
Return: the encoded byte array and its length
Description: This interface is called after encryption when the client sends data to the server

byte[] ClientDecode(byte[] encryptdata, int datalength,
out int outlength, out bool needsendback)

Parameters: the byte array to be decoded and its length
Return: the decoded byte array and its length, and whether the needsendback flag indicates that data should be sent back to the server immediately. If needsendback is true, ClientEncode will be called immediately with a byte array of length 0 as the argument
Description: This interface is called before decryption when the client receives data from the server

byte[] ClientPostDecrypt(byte[] plaindata, int
datalength, out int outlength)

Parameters: the byte array to be processed and its length
Return: the processed byte array and its length
Description: This interface is called after decryption when the client receives data from the server

byte[] ServerPreEncrypt(byte[] plaindata, int datalength,
out int outlength)

Parameters: the byte array to be processed and its length
Return: the processed byte array and its length
Description: This interface is called before encryption when the server sends data to the client

byte[] ServerEncode(byte[] encryptdata, int datalength,
out int outlength)

Parameters: the byte array to be encoded and its length
Return: the encoded byte array and its length
Description: This interface is called after encryption when the server sends data to the client

byte[] ServerDecode(byte[] encryptdata, int datalength,
out int outlength, out bool needdecrypt, out bool
needsendback)

Parameters: the byte array to be decoded and its length
Returns: the decoded byte array and its length, plus the needdecrypt flag indicating whether the data needs to be decrypted (generally this should be true), and the needsendback flag indicating whether data should be sent back to the client immediately. If needsendback is true, ServerEncode will be called immediately and its return value will be sent. The parameter passed in this call is a byte array of length 0
Description: this interface is called when the server receives client data before decryption

byte[] ServerPostDecrypt(byte[] plaindata, int
datalength, out int outlength)

Parameters: the byte array to be processed and its length
Returns: the processed byte array and its length
Description: this interface is called when the server receives client data after decryption

byte[] ClientUdpPreEncrypt(byte[] plaindata, int
datalength, out int outlength)

Parameters: the byte array to be processed and its length
Returns: the processed byte array and its length
Description: this interface is called when the client sends UDP data to the server before encryption

byte[] ClientUdpPostDecrypt(byte[] plaindata, int
datalength, out int outlength)

Parameters: the byte array to be processed and its length
Returns: the processed byte array and its length
Description: this interface is called when the client receives UDP data from the server after decryption

byte[] ServerUdpPreEncrypt(byte[] plaindata, int
datalength, out int outlength)

Parameters: the byte array to be processed and its length
Returns: the processed byte array and its length
Description: this interface is called when the server sends UDP data to the client before encryption

byte[] ServerUdpPostDecrypt(byte[] plaindata, int
datalength, out int outlength)

Parameters: the byte array to be processed and its length
Returns: the processed byte array and its length
Description: this interface is called when the server receives client UDP data after decryption

Plugin Development

There are two types of plugins: protocol plugins and obfuscation plugins

Among them, the InitData, SetServerInfo,
and Dispose interfaces must be implemented; the other interfaces are communication interfaces

To write a protocol plugin, you need to override ClientPreEncrypt,
ClientPostDecrypt, ServerPreEncrypt,
and ServerPostDecrypt. For all others, return them as-is. needdecrypt must be true, and needsendback must be false.

To write an obfuscation plugin, you need to override ClientEncode, ClientDecode,
ServerEncode, and ServerDecode. For all others, return them as-is.

If the part you write contains only the client-side portion, then you only need to implement the two interfaces prefixed with Client; the same applies to the server side.

Currently, the applications that support this plugin interface are ShadowsocksR C# and ShadowsocksR Python

Leave a Comment

Your email address will not be published. Required fields are marked *

中文 EN
🚀

RedGate VPN

免费节点太挤太慢?
升级高速稳定专线

立即体验 →

告别卡顿

RedGate VPN
全球高速节点

免费下载 →
Scroll to Top