终有一天,我会抛弃你们

想了很多,最後的出來的結論!

我在品蔥,留下了很多翻墻的技術文章。我和其他人不同,他們只是教大家怎麽搭建VPS翻墻。
而我,是從源碼級別,去解釋翻墻的原理。
在品蔥,我寫了包括但不限於Lantern(lampshade),V2ray(socks5 over WSS),BoringSSL(TLS),迷霧通(cshirt2),Tor(Meek-Lite),火狐的DoH+ESNI。。。

進來的人,你們將看到的是我,封筆之作《手把手教你寫VPN》

1.迷霧通代碼講解系列,將不再更新。。。
**2.這篇文章,將凝聚我一生所有翻墻技術的精華**
3.這篇文章,**將以Wireguard代碼為講解依據**
4.這篇文章以後,我不會再發任何和翻墻技術有關的文章
5.以後在品蔥,我就老老實實發音與了。。。
39
分享 2020-04-28

19 个评论

支持干货!品葱牛啤!人才济济!
霏艺Faye 图书管理员
作爲小白,我希望自己可以成爲翻墻界的黃埔軍校
培養大量翻墻技術領域的精英,為推翻共產黨的暴政儘一份自己的努力!

可惜,我的智商限制,真的沒有那個本事。。。

以後,你們要學會自己照顧自己。。。翻墻的事情,網上資料有很多!
師傅領進門,修行在個人!
我的文章,只是帶你們進入這個世界。
以後,你們能做到什麽樣子,全看你們自己。


我原本的意圖,只是教更多人寫翻墻軟件!
只是帶了這麽久,現在只能讓你們自己去摸索了~
支持。

留下這樣寶貴的文本。給以後同墻鬥爭的後輩們作為珍貴的學習資料。 真的非常有價值。感謝你。
作爲小白,我希望自己可以成爲翻墻界的。培養大量翻墻技術領域的精英,為推翻共產黨的暴政儘一份自己的努力...


请问大佬,如何评价禁闻网的翻墙器 https://git.io/jww

我用了他们今年新开发的软件,手机和电脑就跟没墙一样舒畅。而且完全免费。这种好日子能过多久?
注意一下,品葱不支持Markdown.
要不找站长说说?Markdown比BBCode好用多了。
请问大佬,如何评价禁闻网的翻墙器 https://git.io/jww我用了他们今年新开发的软件,手...
那个其实就是ssr线路,他们有几个很高速的节点,挺好的,不过上品葱还是加一个tor吧
没事 累了就歇歇 别给自己太大压力
文科大佬教理科生学代码.(迫真)
授人以鱼不如授人以渔,lz善莫大焉
大佬为什么要抛弃葱民?您是哪去了
回墙国了,少发言为妙,等过几个月回去了再说。
您的工作非常有用,感谢分享。
说回做讲解,累了就休息,在这么险恶的世道上,对自己好一点
感謝樓主為自由的付出與奉獻👍注意別累壞自己了,身體是革命的本錢
为啥不发技术文章了?
要是不想在这里发,可以自建博客,咱们会过去支持滴。
作爲小白,我希望自己可以成爲翻墻界的。培養大量翻墻技術領域的精英,為推翻共產黨的暴政儘一份自己的努力...

同意你所说。
像编程随想这样的大佬,都没有开发翻墙工具,而是通过写博客,帮助更多人觉醒。

再次给大伙儿提个醒:
还是那句话:在掌握扎实的计算机网络和密码学知识之前,【不要】尝试自己开发翻墙工具!
肉身在墙内的人,还要做好如同编程随想的匿名措施,避免被跨省。
霏艺Faye 图书管理员
0x0 故事背景

在Linux的世界裏,萬物皆文件

擧個例子,《你的名字》電影,你們看過麽?
三葉打開自己的作業本,看到了瀧留下來的文字。同樣的瀧也可以看到三葉給自己的留言。這個過程,在操作系統裏,叫做通信。

我們沒法和人直接通信的時候,就會用類似留言的方法,達到通信的效果。
在操作系統裏,文件【File】作爲留言的媒介。所以,把一切當作文件吧~



0x1 概要設計

VPN 可以分爲兩個部分來實現,設備 通信
第一個部分,我們需要模擬一張網卡【設備,亦或者我說的文件
第二個部分,我們需要讓虛擬網卡,可以發送網絡數據。【通信,和真實網卡通信】

開始抽象了對不對?繼續剛才的例子吧~
假設三葉的作業本有個名字,叫虛擬網卡,三葉是真實網卡,瀧是火狐瀏覽器。
瀧在三葉的作業本上寫了字,三葉在第二天就可以看到了~

翻譯成VPN:
火狐瀏覽器【立花瀧】,告訴虛擬網卡【在作業本留言】,我要去訪問Google。
真實網卡【宮水三葉】,通過閲讀虛擬網卡上的留言,就去訪問Google了~


大概僞代碼長這樣

int main()
{
// 創建一張虛擬網卡
虛擬網卡 device = new 虛擬網卡();

while(true) {
// 從虛擬網卡讀取數據
Buffer buffer = new Buffer()
device.read(buffer);

// 告訴真實網卡
network.send(buffer);

// 從真實網卡讀取數據
network.recv(buffer);

// 寫到虛擬網卡
device.write(buffer);
}

return 0;
}
霏艺Faye 图书管理员
0x2 詳細設計

2.1 我們實現第一部分,新建一個虛擬網卡
這裏有兩種手段,第一種,我們稱爲内核態,第二種,我們稱爲用戶態;
希望通過内核態實現的人,去讀https://github.com/torvalds/linux/tree/master/drivers/net/wireguard
文件是device.c
我比較懶,沒那麽多時間教大家内核態開發。。。

我們直接從簡單的入手吧~
我們用用戶態來實現,很簡單,就一句話~
int fd = open("/dev/net/tun", O_RDWR);

搞定,我們得到了一個虛擬網卡~
是不是很容易? 經過我實際測試,個別操作系統的路徑不一定是
/dev/net/tun,可能是/dev/tun,所以,大家根據自己實際的操作系統,去寫這個代碼!
另外指定下
ifr.ifr_flags = (IFF_TUN | IFF_NO_PI | IFF_MULTI_QUEUE)

重點説下IFF_TUN,這個表示虛擬網卡工作在IP層,如果用來TAP,表示工作在數據鏈路層
IP層的單位是包【packet】,數據鏈路層的單位是幀【frame】

如何對這個虛擬網卡讀寫數據呢?
寫數據
write(fd, buffer, buflen));

讀數據
read(fd, buffer, &buflen);

2.2 我們實現第二部分,網絡通信
這個就是很容易了,比較隨意
最簡單,就是把讀到的buffer,直接tcp發出去,再用tcp收buffer
然後,把buffer,寫回虛擬網卡

當然,也可以用v2ray封裝一下,通過v2ray的 payload來發送,
也可以用shadowsocks發送,也可以用https。。。。

今天我們重點講wireguard,所以底層協議就用wireguard了~

wireguard,基於noise 協議框架,所以我們建一個noise的class
霏艺Faye 图书管理员
參考資料
https://noiseprotocol.org
https://www.wireguard.com/protocol/

我們繼續詳細設計的網絡通信部分

首先,根據文檔,我們需要實現握手協議
handshake

所以,我們的noise這個class需要有一個handshake方法

因爲wireguard,是peer to peer方式,沒有服務器和客戶端的概念。

這裏有一個發起方,和回應方
First Message: Initiator to Responder
第一條消息,由發起方,發送 handshake initiator消息給對端【這裏應該是你發給VPN產商的地址】
Second Message: Responder to Initiator
第二條消息,由回應方,返回handshake response消息給我們【VPN產商返回給我們】

這裏,就完成了握手~
後面的都是數據交換包【通過ChaCha20-Poly1305加密】
Subsequent Messages: Exchange of Data Packets


先説第一個包怎麽寫吧~
msg = handshake_initiation {
    u32 message_type // 寫死 1 不可能其他值
    u32 sender_index // 一個計數器,每次握手包,自己+1
    u8 unencrypted_ephemeral[32]  //我們的公鑰。明文,否則對端沒有辦法知道我們的公鑰
    u8 encrypted_static[AEAD_LEN(32)] // 加密的内容,後面講
    u8 encrypted_timestamp[AEAD_LEN(12)] //加密的時間戳
    u8 mac1[16]  // 算一次摘要
    u8 mac2[16]  // 再算一次摘要
}


msg.encrypted_static = AEAD(key, 0, initiator.static_public, initiator.hash)

AEAD 表示chacha20-poly1305加密算法,我們不需要自己實現,直接NSS【火狐自帶的TLS實現】就好了
key從哪裏來呢?回歸我前面說的HKDF算法~
initiator.chaining_key = HASH(CONSTRUCTION) 
// CONSTRUCTION = “Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s” 字符串什麽意思,你得去讀下Noise協議框架的入門知識了,表示這個協議内容是怎麽定義的。
// HASH 表示 Blake2s 算法
// 寫死[96, 226, 109, 174, 243, 39, 239, 192, 46, 195, 53, 226, 160, 37, 210, 208, 22, 235, 66, 6, 248, 114, 119, 245, 45, 56, 209, 152, 139, 120, 205, 54]; 對CONSTRUCTION做blake2s算hash是結果一樣的,沒必要每次算
initiator.hash = HASH(HASH(initiator.chaining_key || IDENTIFIER) || responder.static_public)
// IDENTIFIER = "WireGuard v1 zx2c4 Jason@zx2c4.com"
// HASH(initiator.chaining_key || IDENTIFIER) 寫死  [34, 17, 179, 97, 8, 26, 197, 102, 105, 18, 67, 219, 69, 138, 213, 50, 45, 156, 108, 102, 34, 147, 232, 183, 14, 225, 156, 101, 186, 7, 158, 243];
和服務器的公鑰或運算后,再算一次blake2s

initiator.hash = HASH(initiator.hash || msg.unencrypted_ephemeral)
把剛才的hash和自己的公鑰,算一次或,然後再算一次hash
temp = HMAC(initiator.chaining_key, msg.unencrypted_ephemeral)
initiator.chaining_key = HMAC(temp, 0x1)
// 得到HKDF的PRF

temp = HMAC(initiator.chaining_key, DH(initiator.ephemeral_private, responder.static_public))
initiator.chaining_key = HMAC(temp, 0x1)
key = HMAC(temp, initiator.chaining_key || 0x2)
// HKDF 的extend 得到 最終的密鑰

HMAC 表示Blake2s【和sha256一樣算摘要的】,NSS裏也有了~

寫的真複雜啊。。。

initiator.hash = HASH(initiator.hash || msg.encrypted_static)


然後繼續算hash 。。。


繼續剛才的過程,計算一次新的key,用新的key,加密時間戳。。。
再根據協議,算一次mac【blake2b摘要】
第二次mac,是和cookie有關,先不講。。。

。。。。


response的協議,怎麽拼,也一樣




我就是對報文怎麽拼,給你講一遍。。。
不可能所有協議都講


要學會舉一反三!!!
自己根據https://www.wireguard.com/protocol/

把所有協議自己實現掉。。。
當然可以和我一樣,用現成的v2ray或者shadowsocks

這樣就不用自己寫協議層了。。。
-----------------------------------
我更多是告訴大家,這裏需要實現網絡通信協議
具體什麽協議,你們自己看著辦,不被GFW識別就行了

我建議別用裸的Wireguard,流量特徵太明顯了
如果我是GFW,我會把所有UDP報文長度是92的包,無條件扔掉~

然後,我也大概告訴大家,自己寫協議,需要怎麽寫代碼
大概流程給了一個模板
霏艺Faye 图书管理员
説了這麽多

我覺得正常的人,應該能發現,VPN和v2ray這樣的代理,在網絡通信上,是一模一樣的。
從通信角度來説,沒有誰比誰安全的説法。

VPN和代理,最大的區別,只是在客戶端上模擬了一張虛擬網卡,把虛擬網卡的流量封裝以後,通過真實網卡,轉發給了VPN服務器,然後再把收到的報文,寫回到虛擬網卡

僅此一個過程罷了!!!

而模擬一個虛擬網卡,無非就是

open("/dev/net/tun")一句話的事情

我希望,大家不要再去糾結VPN和v2ray誰安全,誰好用,這樣毫無價值的討論了!!!

我覺得,任何一個開發V2ray的人,都有足夠的能力寫個VPN

這就不是一個很難的事情!

不管是寫代理,還是寫VPN,我覺得難度係數是一樣的。。。
別天天看不起這個看不起那個!

當然了,我這裏省略了很多東西

比如流控,ratelimiter,你們自己實現下。。。
再比如,多綫程去讀寫虛擬網卡,提升性能[Tor作者,寫了一個libevent,特別好用!]


。。。我本想寫更詳細一些。。。

奈何寫這麽點東西,都要花了我3個小時。。。

你們知道的,我比較懶。。。所以,你們懂得~
後續,我也不會更新了


我寫代碼【我不會編程,裝作會的樣子】,都是先寫一個框架【骨頭】。
再慢慢 寫細節【肌肉,器官】
最後寫個GUI【皮膚】

關於語言,不建議Python,的確對虛擬網卡操作,Python性能不行【GIL鎖】
也不建議Golang【太費内存】

我建議,裸金屬語言【C,C++】

框架,大概先是實現創建一個虛擬網卡,然後實現讀取虛擬網卡的數據
最後寫個網絡通信層,把數據封裝【加密】,以後發到網絡對端
接收對端發來的應答,拆開【解密】得到數據,寫回虛擬網卡


這個過程,可以使用libevent來做多綫程【epoll方式】
我也想展開説下多路復用這個過程。。。但是寫起來,好多好多啊。。。


我經常給人解釋select poll epoll的工作原理
當年看Linux内核源碼的時候,專門看了select内核裏的實現
但是,總是發現,入門人員,無法理解監聽,同步,異步,這些概念。。。

-----------------
以下是我的個人信息:
知乎上【墻内那個】,你們如果關心過KVM(Kernel-based Virtual Machine)領域的回答。
那個經常回答特別權威,在知乎上,也算小有名氣的那位,是我同學。。。

他,其實也算知乎上的網紅了吧。。。而且專門回答KVM方面問題爲主。。。
非常專業,我的編程知識,是他一把手帶出來的!!!
我的能力,如果你們覺得有5分,那麽他就是100分。。。

實力差距很明顯,他對Linux内核源碼的研究,在國際上都算權威了~
國際上很多開源項目,他都有參與。而且精通多門編程語言,是一個天才

我因爲很多原因,大學沒畢業
故,我經常説的一句話,學歷不重要
關於爲什麽沒畢業,廣播站裏的故事,基本差不多吧。。。所以特別有共鳴,寫在廣播站。

所以,我還是很羡慕大家能去讀大學,能畢業的。
我在2049説過,很羡慕大家,就是這個意思。我説廣播站的故事對我很重要,也是這個意思。

所以,我累了,以後的我,只想安安靜靜發歌就好。
現在的我有幾千葱,足夠我發幾百首!
每天3首,也足夠我發1年了吧。。。

要发言请先登录注册

要发言请先登录注册