smbd
要 trace 一個程式,首先要了解這個程式到底是做什麼的,其功能為何。smbd 就如前面說的,提供 file access 的功能。samba 的兩個主要 daemon 是:
- nmbd 提供 name service,
- smbd file access、print ...... 等等的 service。
工具
分析一個程式,可以使用最簡單的 joe、jed、vi 等 editor 來蠻幹。雖然這些都是 powerful 的工具,但卻無法直接提供有用的資訊。我們必需分心去組合這些工具所提供的服務,才能分析得到有用資訊。
以往我也是熱忠於使用簡單工具的人,相信這些工具的功能強大,能做到任何我想做事,事實上也是如此。然而,隨了工作越來越多,我發覺時間越來越不夠用時,我發覺好的工具能讓我在相同的一天 24 小時裡,有更多的產能。我不再需要花時間去進行一再重複的動作,只為了得到一些資訊。電腦的偉大之處,不就是幫我們進行日常一再重複的工作嗎?身為 hacker 和工程師,為何讚嘆徒手完成繁雜而重複工作的行為呢?外界認為為,軟體工程師是自動化的推手,但我認為是自動化層度最低的行業。是到了改善的時侯了。
分析軟體時,有幾個工具可用,如: ctags、cflow、doxygen 等。傳統上,我們使用 ctags,為 source code 產生 index。配合 tags 的使用,editor 可以幫我們快速找到 function 和 variable 的定義,而穿梭在程式碼之間。而 cflow 可以幫我們找分析出程式的流程,讓我們了解 function 的 static 行為、相依性和呼叫過程。配合 cflow2vcg,能以 tree 呈現程式流程。cflow 配合 cflow2vcg,能讓我們一目了然,快速的了解程式的架構。然而,巨細彌遺的內容,往往使我們失去了焦點。我建議配合 cflow2vcg 產生的圖,trace 程式碼,並刪減 cflow 產生的旁支末節,以得到一張大綱,作為以後進一步了解細節的 guide line。
doxygen 其實是一個產生文件的工具,幫助軟體工程師產生程式碼的文件。然而,doxygen 會為 source code 產生 HTML 檔,為每一個 function 和 variable 產生 hyperlink,並列出 refer 或被 refer 的位置。透過 doxygen 產生的 hyperlink,我可以使用 mouse,直覺的穿梭在程式碼之間。雖然我還是比較喜歡使用 keyboard,但是卻不是那麼直覺。分析程式碼本來就是一種腦力密集的工作,何以我還要分析去使用不夠直覺的工具?因而,使用 doxygen 產生的文件,似乎能更快完成工作。然而 doxygen 也非完美,無法自動記錄 trace 的過程。
進行分式
samba 已經有 Doxyfile,可直接拿來產生文件。透過 hyperlink,得以穿梭在 source code 之間,颯沓如流星 。
主要架構
smbd 的進入點是在 source/smbd/server.c 裡的 main(),其主要流程如下:
main() {smbd/server.c} open_sockets_smbd() or open_sockets_inetd() smbd_set_server_fd() loop accept new connection fork() child? smbd_set_server_fd() return smbd_process() {smbd/process.c} loop setup_select_timeout() blocking_locks_timeout() {smbd/blocking.c} set_change_notify_timeout() {smbd/notify.c} !timeout_processing()? /* process 一直沒有收到新的 conneciton */ return; /* idle for a while */ run_events() {lib/events.c} receive_message_or_smb() /* 根據 timeout 時間,select socket */ message_dispatch() {lib/messages.c} ref var dispatch_fns process_smb() construct_reply() msg_type != 0? yes reply_special() {smbd/reply.c} /* 處理和 session 有關 */ no construct_reply_common() switch_message() /* 依 smb_com 執行對應的 smb_messages */ send_smb() {lib/util_sock.c}
main 的前半段是在進行 initialize,直到 open_sockets_smbd()。open_sockets_smbd() 接受 connection request,並 fork process。由於 smbd 有不同的執行方式,可當 stand-alone daemon,也可透過 inetd 執行,open_sockets_smbd() 依據情況,採取不同的處理方式。例如:透過 inetd 執行的情況,就直接使用 inetd pass 過來的 socket,而非 accept 新的 socket。
smbd_process() 主要的工作,就是接收來自 socket 的 SMB message,依據分類,呼叫對應的 function 進行 service。smbd_process() 的主要動作為:
- 呼叫 receive_message_or_smb() 以接收來自網路的 SMB message
- 執行 process_smb() 以處理 SMB message 所撘載的 request
static const struct smb_message_struct { 00582 const char *name; 00583 int (*fn)(connection_struct *conn, char *, char *, int, int); 00584 int flags; 00585 } smb_messages[256] = { 00586 00587 /* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, 00588 /* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, 00589 /* 0x02 */ { "SMBopen",reply_open,AS_USER }, 00590 /* 0x03 */ { "SMBcreate",reply_mknew,AS_USER}, 00591 /* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC },
結論
分析 source code,首要就是了解主要的架構。製作出如上面列出的主要流程後,再依據分析的目的,參考主要流程,進行更深入的了解,以便在骨架上填上血和肉。透過這樣的方式,能分析的更快、更好。
若是一開始就栽入 source code 的細節裡,往往是陷入見樹不見林的窘境。因此,首要務是掌握大綱、抑制對細節的渴望,才能快速、正確的了解系統的精神。