2017年2月

SS通讯步骤

目前在写一个Socks5转SS的工具(某些奇怪需求需要), 在互联网上翻了几个网站并没有详细说明SS通讯步骤. 所以在这记录下.

密码初始化

本文章aes-256-cfb加密来说明, 当使用AES时需要提供KeyIV(初始化向量). 其中IV类似于给hash加盐, 不过这个盐是基于上一个数据块加密后的出来的盐. 那为啥还需要提供IV呢?因为第一次用的时候没上一块数据只能自己指定一个.

SS生成KeyIV是基于用户指定的密码来生成的, 以下为TypeScript基于密码生成KeyIV代码:

function generateKeyIVByPassword(passwordString: string, keyLength: number, ivLength: number): any {
    var password = new Buffer(passwordString, "binary");
    var hashBuffers: Array<Buffer> = [];
    for (var dataCount = 0, loopCount = 0; dataCount < keyLength + ivLength; loopCount++) {
        var data: any = password;
        if (loopCount > 0) {
            data = Buffer.concat([hashBuffers[loopCount - 1], password]);
        }
        var md5 = crypto.createHash("md5");
        var md5Buffer = md5.update(data).digest();
        hashBuffers.push(md5Buffer);
        dataCount += md5Buffer.length;
    }
    var hashBuffer: Buffer = Buffer.concat(hashBuffers);
    return {
        key: hashBuffer.slice(0, keyLength),
        iv: hashBuffer.slice(keyLength, keyLength + ivLength)
    };
}

从上述代码中可以得知SS作者先将用户指定的密码迭代MD5多次; 其结束条件是迭代多次的MD5结果长度相加, 如果长度大于加密算法所需Key长度 + IV长度则退出循环. 最后将MD5 Hash结果截断来获得KeyIV.

通讯过程(TCP过程)

当客户端连接至服务端后会直接发送 客户端IV + 已被加密的Socks5头 + TCP数据.

IV交换

Client连接后, Client发送的第一个数据前N个字节(取决于加密算法)是AESIV用于来解密来自Client的数据.
然后Server发送Client的第一个数据也是类似. 其结构图如下:
ShadowsocksIVED.png

注意!SS中这样交换IV是不安全做法.

通讯

SS省略了Socks5协商过程直接进入通讯过程. Socks5通讯需要客户端提供 目标IP或域名 + 端口. 当客户端提供完成之后服务端会连接至目标IP并返回是否连接成功; 如果连接成功就能够直接通讯了.

格式如下:
027071B4-415D-4ACE-BF39-EFABB2F7B64A.png

上述说过SS省略了协商过程, 所以这里Socks5请求也是缩水的. 其把前3个字节砍掉了, 也就是说Client只会发来ATYP, DST ADDR, DST PROT. 当连接至DST ADDRDST ADDR发送的数据加密发送给Client, Client发送的数据解密发送给DST ADDR. 这样SS就完成了通讯.

参考资料