發表文章

目前顯示的是有「Programming」標籤的文章

再論 Zoom 的安全性:AES_128_ECB

圖片
Zoom 的風暴越演越大,在教育局一聲令下全體禁用之後,我們看到葉丙成老師出來發話了。細節要看他的 Facebook,但是因為我擔心之後會被蓋掉,所以這邊先放上新聞的連結。 教部停用Zoom 葉丙成:老師們的努力全白費 如我所料,另外一位自認是資安專家的李老師出來講話了。 很明顯是在對罵 ...  以我個人來說,這百分百是政治議題。要資安?那請問政府能不能看你的信件,我不是說中國或是美國,我指的是中華民國或是台灣,看你要怎樣稱呼。 離題了,我們回到 Zoom 這邊來。我這邊只針對爆出來的問題做分析。 昨天又看到一則新聞,關於 Zoom 使用 AES_128_ECB 的方式來進行加密,內容如下: Move Fast and Roll Your Own Crypto: A Quick Look at the Confidentiality of Zoom Meetings 說真的,這篇寫的內容非常好,有憑有據,比之前只會隨便抓兩句話就上來吹一整篇的人好多了。真要說缺點的話是沒有註明軟體的版本,畢竟 Zoom 都一直有在更新。在通篇文章裡面,扣掉資料傳中國以外(請注意,我知道美國和歐洲應該是有法律規定用戶資料必須要留在國內,所以我之前的文章討論的純粹是技術,畢竟大家拿出來講的是資安而比而不談政治),最大的問題只有一個,Zoom 是使用 AES_128_ECB 的方式進行加密,而且金鑰是來自中國的。按照文章來說,架構如下: 關於金鑰來自中國這件事情我在技術上沒有意見(政治上另外一回事),因為這個架構就是很單純,在資安上是「公正第三方」(Trusted Third Party),簡單來說是你不能去質疑它的安全性,正如你在使用 Google 服務的時候你也不會去質疑 Google 的安全性一樣。問題出在 AES_128_ECB。這沒啥好說的,這是一個問題。128 的意思代表金鑰長度 128 bits,以目前來說還算夠用,根據 wikipedia 的資料 Since 2015, NIST guidance says that "the use of keys that provide less than 112 bits of security strength for key agre...

GCC Built-in Functions

最近上課的時候鬧了一個大笑話(而且那門課還是組合語言不是程式設計...)。我寫了下面這隻程式: int main() { printf( "Hello\n" ); return 0; } 很明顯,這程式沒有 include 任何 header file,理論上應該是要包含 stdio.h。在編譯的過程中,compiler 吐出了下面的 警告信息 ( 不是錯誤信息唷 ): test.c: In function ‘main’: test.c:3:5: warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]      printf( "Hello\n" );      ^ test.c:3:5: warning: incompatible implicit declaration of built-in function ‘printf’ test.c:3:5: note: include ‘<stdio.h>’ or provide a declaration of ‘printf’ 但是程式會動,還是可以印出 Hello 的字樣。為什麽?我這時候信誓旦旦的跟大家說,因為 printf 在 libc.so 裡面有,因此就算編譯的時候找不到,gcc 在連結 libc.so 的時候還是會看的到 printf ,所以這時候還是可以執行的。為了證明這件事情,我用 nm 去看一下編出來的 test.o 0000000000000000 T main                  U puts 等一下, where is my printf ?? ...在學生面前要保持鎮定,大概 printf 在系統裡被改成 puts ... 然後再做實驗給同學看,這次換成利用 libm.so 的 pow 函式。 int main() { printf( "2^3 = %f\n", pow( 2.0,3.0 ) ); return 0; } 然後說,這...

利用 printf 來把值寫入某個變數

在 C 語言中,透過 printf 來印東西一點都不稀奇。但你知道 printf 也可以拿來寫值嗎?? 看看下面的範例,看看會印出來什麼東西。 #include "stdio.h" int main() { int val; printf("bala %n bala\n", &val); printf("val = %d\n", val); return 0; } 印出來的結果如下: blah  blah val = 5 為什麼呢?如果我們去查 manual 的話,可以看到下面的定義: n The number of characters written so far is stored into the integer indicated by the int * (or variant) pointer argument. No argument is converted. 也就是使用 %n 的時候,他會把 到目前為止已經印出多少個字元的數目給寫入一個 int 的變數 。常見的做法在於對齊,如下面的程式 int n; printf("%s: %nFoo\n", "hello", &n); printf("%*sBar\n", n, " "); 但只有這樣嗎?其實,%n 也可以拿去寫值到程式內特定的記憶體位置喔,只要你好好的處理 pointer 的部分的話。所以 printf 其實比想像的還要危險呢。

使用 Serial Port 來除錯的時候請小心

一般來說,程式設計師在除錯的時候常常會用的一種方式就是 print 大法,在程式適當的地方插入一些顯示資訊的指令,提供開發者更多資料來除錯。而在使用這個方法來除錯的時候,有很高的機會是利用 serial port 來顯示信息,特別是在嵌入式系統的開發上。然而當透過 serial port 來印信息的時候有著潛在的風險,因為在工作上遇到了,所以就特別紀錄在下面。首先先來看看下面這一段程式。 1 2 3 4 5 6 7 #!/usr/bin/python import sys import time for item in range( 0, 10000, 1 ): print str( item ) + " ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" 這段程式相當的簡單,就只是單純的把一個字串印個 10000 次罷了。然後我們分別透過 SSH 以及 Serial port 連到測試設備上執行這個程式,並利用 time 這個指令來量測時間。結果記錄在下面(省略印字串的輸出)。 SSH: root@OpenWrt:~# time ./test_print_01.py real    0m 0.68s user    0m 0.18s sys     0m 0.08s Serial Port (115200): root@OpenWrt:~# time ./test_print_01.py real    1m 8.18s user    0m 0.17s sys     0m 0.07s 可以發現時間差距非常的多,理由很簡單,因為 serial port 的輸出速度只有 115200 bps (本篇的實驗設備),所以慢也很正常的。問題是,如果今天要除錯的程式是一隻 time critical 的程式,那這就會遇到很大的麻煩。先把程式列在下面: test_print.py 1 2 ...

隨筆:技術的演進

在我還在念碩士班的時候,那時候一個碩士學姐講了一個她在工作上有人來面試的經驗。 學姊:你好,歡迎來應徵軟體開發工程師吧。你看看這些題目 ... 應徵者:這些我不會ㄟ ... 學姊:(太難了嗎 ... ) 沒關係,能寫多少就先寫,有問題也可以討論看看 應徵者:但是我看不懂 學姊:你知道我們要找的是 JAVA 的開發工程師吧 應徵者:我對寫 JAVA script 很有經驗 學姊:... 在當下,實驗室的人都笑了。連 JAVA 和 JAVA script 都分不清的人也敢過來面試,JAVA script 不過就是一堆美化網頁用的小技巧罷了... 這麼多年過去了,看看 HTML5、node JS ... 我不禁為當年的無知吸了一口氣。

libpcap 使用範例

最近因為工作上的需求,所以透過 libpcap 寫了一個小程式。程式紀錄在下面。應該不難懂,所以就不多做說明。程式主要參考 sniffex ,不過主要針對  DHCP 的封包進行攔截以及顯示。 #include #include #include #include #include #include #include #include #define SIZE_ETHERNET (14) #define SIZE_UDP (8) #define ETHER_ADDR_LEN (6) const char *DHCP_MSG_TYPE_STR[9] = { "None", "DHCP Discover", "DHCP Offer", "DHCP Request", "DHCP Decline", "DHCP ACK", "DHCP NACK", "DHCP Release", "DHCP Inform" }; struct sniff_ethernet { uint8_t ether_dhost[ETHER_ADDR_LEN]; /* destination host address */ uint8_t ether_shost[ETHER_ADDR_LEN]; /* source host address */ uint16_t ether_type; /* IP? ARP? RARP? etc */ }; struct sniff_ip { uint8_t ip_vhl; /* version << 4 | header length >> 2 */ uint8_t ip_tos; /* type of service */ uint16_t ip_len; /...

Write FD set 的用途

一段時間,我其實都不知道 select 和 epoll 這一類 function 裏面的 writefds 到底有什麼用。read fd set很容易理解,那就是有東西要讀,或者白話一點,收到封包了。那 write fd set 到底要幹嘛呢?直到今天遇到了一個情境我才使用了它 ... 在寫 TCP socket 程式的時候,會使用到 connect 這個函式。一般來說, connect 這個函式是 blocking 的,也就是會一直到事情做完才會返回。但如果將 socket 設定成 non-blocking 模式呢?這時候 connect 會立刻返回並將 error no 設定為 EINPROGRESS 。下面是"男人"的解釋: EINPROGRESS               The  socket  is  nonblocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by  selecting  the  socket  for  writing.   After select(2) indicates writability , use getsockopt(2) to read  the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully  ( SO_ERROR is zero ) or unsuccessfully ( SO_ERROR is one of the usual  error codes listed here, explaining the reason for the failure ). 好了,這時候我們可以看到 select 可以用來判斷 connect 是否...

如何透過 Python 來呼叫 ioctl

首先,先來抄一段 wikipedia 的定義: In computing, ioctl (an abbreviation of input/output control ) is a system call for device-specific input/output operations and other operations which cannot be expressed by regular system calls. It takes a parameter specifying a request code ; the effect of a call depends completely on the request code. Request codes are often device-specific. 簡單來說,ioctl 是針對 device 去做輸入輸出(白話一點,get/set)操作的一種方式,在 Linux 底下,所有的 kernel module 都可以被視做是一個 device,所以把 ioctl 當作一種 UK 的溝通機制也未嘗不可。不過如果要傳大量資料的話還是採用其他的方式比較妥當,像是 netlink 。在溝通的時候,User Space 和 Kernel Space 必須先定義好雙方溝通的格式。最常見的範例基本上就是 ifconfig 這隻程式了,在這裡也不去看它,反正是一支很簡單的 C 程式。 本篇要紀錄的是,在 Python 裡要怎麼呼叫 ioctl。範例很簡單,,就是實作一支類似 ifconfig 的程式(不過只有 get,懶得寫 set)。直接放在下面: #!/usr/bin/python import socket import fcntl import sys import struct import array def get_if_list (): try : fd = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) # #define SIOCGIFCONF 0x8912 /* get iface list ...

當 send 被 Block 的時候

今天在工作上遇到一個問題。有兩個 process ( process A 和 process B ) 透過 unix domain socket 彼此在傳遞資料,結果發現 process A 在送資料到 process B  的時候,程式居然卡在 send 這支系統函式。send 是會卡住的函式嗎? 答案是: 會 !來看看「男人」怎麼說: ssize_t send(int sockfd, const void *buf, size_t len, int flags ); When the message does not fit into the send  buffer  of  the  socket,  send()  normally blocks , unless the socket has been placed in nonblocking I/O mode .  In nonblocking mode it would fail with the error EAGAIN or EWOULDBLOCK in this case.   The  select(2)  call may be used to determine when it is possible to send more data. 意思是當呼叫 send 的時候,send 會先將資料放到 kernel 的 buffer,如果 buffer 已經放不進去了,而且 send 又處於 BLOCK 的模式時,這時候 send 就會卡住。而為什麼 buffer 會滿呢?因為 process B 處理封包的時間過久,進而造成 kernel buffer 被 process A 源源不絕的封包給塞滿了,然後 process A 的 send 就被 block 了。解決這個問題的方法可以將 socket 設成 non-blocking mode ,或是在傳送封包的時候把 flag 設成 MSG_DONTWAIT 。而這也就是為什麼 epoll, select 這類的函式需要去偵測 fd 是否已經是可寫狀態的原因。不過更根本要處理的問題是,為什麼 process...

為什麼 printf 印不出東西來?

一個簡簡單單的問題,隨手做個紀錄。下面這個小程式毫無意義可言,簡單來說就是印出 Hello World 以後進入無窮迴圈。 #include #include int main() { printf( "Hello World!!" ); while(1) { } return 0; } 問題來了,這個 printf 並沒有印出來 Hello World。可是把程式改成下面這個樣子就沒問題了。 #include #include int main() { printf( "Hello World!!\n" ); while(1) { } return 0; } 這是什麼原因呢?因為 printf 其實只是把東西放到 buffer 當中而不會立刻顯示。buffer 有三種模式,分別是 unbuffered , block buffered , line buffered 。 而預設的 stdout 是 line buffered ,所以只有當遇到 '\n' 才會清空 buffer 把內容印出來,而 stderr 預設就是 unbuffered 。要確保內容一定印的出來的方法,可以使用 fflush( stdout ),或是乾脆直接輸入下面的函式: setbuf( stdout, NULL );

當 console 壞掉的時候 ...

這是今天在工作上看到的一個小技巧,所以把它記錄下來。在程式開發的過程中很容易會使用 print 的方式來追蹤並解決問題,以 C 來說最常見的就是 printf,如果要吐到 console 上的話就開啟 /dev/console 並使用 fprintf 。 今天聽到的問題是:「版子的 console 壞掉了怎麼辦?」 最不花腦筋的答案:「換張版子吧~」 廢話,如果是這樣也不用紀錄了。我今天看到的作法紀錄在下面: $ killall klogd $ mv /dev/console /dev/console_bak $ ln -sf /dev/pts/0 /dev/console 這作法真是太 cool 了,連程式都不用改唷,當然前題是你可以透過 terminal 連進去就是了。第1行的目的是少印一些資訊。 那 /dev/pts/0 是什麼東西呢?pts 是 pseudo terminal slave 的縮寫。細節可以 google 得到,這邊只講最簡單的,當有 terminal 連到 linux 主機時,主機會分派一個 pts 給他。舉例來說,我在 linux 啟動兩個 gnome-terminal,再透過 who 這個指令,可以看到下面的訊息: $ who neokent  tty7         2016-03-03 15:05 (:0) neokent  pts/2        2016-03-03 15:06 (:0) neokent  pts/5        2016-03-03 17:08 (:0) 其中 tty7 是我的桌面,pts/2 和 pts/5 是我的兩個 terminal。這時候如果在 pts/2 的 terminal 上執行 echo Hello > /dev/pts/5 可以在 pts/5 上看到 Hello 唷。

TCP 和 UDP 共用同一個 Port?

這幾天在做網路實驗,利用 iperf3 來觀測流量的時候,發現一個神奇的地方,那就是 iperf3 在設定 server 端的時候,不用指定是 tcp 還是 udp,這跟 iperf 第2版以前是不一樣的。 iperf2: SYNOPSIS       iperf -s [ options ]       iperf -u -s [ options ] GENERAL OPTIONS       -u, --udp            use UDP rather than TCP iperf3: SYNOPSIS        iperf3 -s [ options ]        iperf3 -c server [ options ] CLIENT SPECIFIC OPTIONS       -u, --udp            use UDP rather than TCP 這代表 TCP 和 UDP 的 Socket 是可以同時使用同一個 Port 的。其實我一直以為是不行的(這就叫作孤陋寡聞 ...)。為了實驗起見,我自己寫了一個小小的 Echo Server,在指定的 Port 上同時監聽 TCP 和 UDP,來看看是否能夠運作(Client的程式就不附在上面了)。   #include <stdio.h> // for printf() and fprintf() #include <sys/socket.h> // for socket(), bind(), and connect() #include <arp...