Try   HackMD

隔離執行環境的建構與應用

副標題: 加密貨幣領域的系統軟體
資料整理: jserv

為什麼需要隔離執行環境?

依據 Intel Trusted eXecution Technology 投影片,Trusted Execution Environment (TEE) 的應用情境有:

  • 隔離惡意程式對關鍵系統的衝擊,像是線上支付、非對稱加密系統的私鑰保存;
  • 避免系統在 Firmware Over-the-air 升級之際,不慎無法復原或者想要退回到指定的系統版本,這時就需要 TEE 的介入;
  • 提升企業內部資訊系統的可靠性;

由於資訊安全已是現代資訊系統不可或缺的一環,所以許多硬體都提供了 TPM (Trusted Platform Module) 作為 TEE 的基礎建設,可以把 TPM 想為電腦的指紋一樣,可作為電腦身分的唯一辨識,非一般的電腦晶片組,利用 TPM 晶片加上 BIOS、作業系統,即可創造出唯一僅能被自己電腦所用的加密金鑰,於是透過 TPM 加密過後,即使 BIOS 被洗掉,硬碟裡的東西也受到保護。

Linaro 也致力於以 ARM 處理器架構為基礎的 TEE,參見 A Gentle Introduction to Trusted Execution and OP-TEE

Moxie 處理器架構與 TEE

Gcc Toulibre 投影片 提到 Moxie 處理器架構提出全新的指令集架構 (ISA),並讓 GNU Toolchain 在極少的修改即可支援 Moxie 架構:

  • gcc/config/moxie/ 目錄下的 md (machine description) 檔案約有 560 行
  • gcc/config/moxie/ 目錄則少於 2000 行

任職於 Red Hat 的 Anthony Green 是 Moxie 架構從 VHDL 到 GNU Toolchain 的維護人,他有一篇極好的文章 How To Retarget the GNU Toolchain in 21 Patches 探討 GNU Toolchain 如何修改以加入新架構 (如 Moxie) 的支援,一路從 binutils, gcc, glibc, qemu 到 libffi

看似陽春的處理器架構到底有什麼搞頭?這又與 TEE 有何關聯?

法國的加密貨幣硬體錢包公司 Ledger 發布的第一款產品是 Ledger Nano,提供袖珍型的硬體錢包,允許使用者將加密貨幣「私鑰的儲存」以及「 交易的產生和簽名」都在這款硬體中進行,排除了外界的攻擊和資訊洩漏風險。Ledger 公司推出 BOLOS (Blockchain Open Ledger Operating System) 作為區塊鏈處理的關鍵作業系統,裡頭就用到 Moxie 處理器架構。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

簡單來說,Ledger 公司透過特製的 Moxie 虛擬機器,作為 TEE 的基礎,在上圖右方可見到智慧合約執行在 Moxie 虛擬機器,並且透過 BOLOS API,TEE 以外的通用執行環境 (如 Linux, Android, MS-Windows 等等) 得以知曉交易的狀況並作出變更的請求。

這間加密貨幣硬體錢包製造商 Ledger,在 2017 年宣布和 Intel 合作,Ledger 將其錢包產品的 BOLOS 作業系統與 Intel 的 Software Guard Extensions (SGX,軟體防護擴展指令) 技術作整合,為加密貨幣持有者提供一個更安全的儲存方式。Intel SGX 最早在 2013 年推出,透過特殊指令和軟體可將應用程式和數據放入一個「圍圈」(enclave) 中。這種類似沙盒的可信執行環境為其中的關鍵程式碼和數據提供了保護,防止惡意軟體的攻擊,因此保障了使用者資料的機密性和完整性。Ledger 的錢包產品導入這項技術之後, 私鑰的儲存以及交易的產生和簽名都將在 SGX 的安全空間之內,而非個別應用程式。

關於 BOLOS 開放的 API 文件可見 BOLOS TEE,目前已有許多加密貨幣的橋接程式碼,維護在 LedgerHQ 的 GitHub 頁面

Ledger BOLOS Enclave 的程式碼來說,跟 Moxie 相關的目錄有:

這樣的手法不罕見,事實上 Ledger 公司建構 BOLOS 的基礎工作借鏡於另一間區塊鏈新創公司 Bloq。探討 Bloq 公司的技術時,我們不能不提該公司執行長 Jeff Garzik,他是 Bitcoin core 之前相當活躍的開發者。在 2013 年 5 月,Jeff Garzik 因為活躍於 Bitcoin core 開發,而被比特幣交易公司 BitPay 延攬,隨後在 2015 年 10 月,他和加密貨幣領域的投資人 Matt Roszak 共同創辦 Bloq 公司,促進區塊鏈應用普及。

Bloq 公司推對開放原始碼實驗室,並用 "Bloq Labs" 命名,類似 20 世紀的 Bells Labs 之於促進電信業發展,該實驗室廣泛與 Drivechains, Qtum, Veriblock, Bitcoinj 和 Android Bitcoin Wallet 等計畫/公司合作,透過聯盟的力量,讓研究聯盟機構的大型企業之間搭起互利共榮的橋樑。

Bloq 公司同樣以 Moxie 處理器架構為虛擬機器的基礎,開發 ora 作為 Biitcoin blockchain 之上智慧合約的驗證引擎,可對照看該公司的投影片 Bloq: Bringing Enterprise to Blockchain

moxiebox 執行環境

moxiebox 是台灣成功大學延伸 Jeff Garzik 的 ora 開發成果的初步嘗試,希望藉由教案,引導學生理解虛擬機器的設計、探索 GNU Toolchain 內部,以及擴充 TEE 並思考相關應用。

moxiebox 的設想中,藉由客製化的 Moxie 模擬器和虛擬機器,打造出隔離執行 (sandbox) 的可靠運作環境,具體來說,提供以下能力:

  1. 載入數位簽章過的 Moxie 執行檔與相關資料;
  2. 在高度客製的環境中執行程式,為確保執行的正確性,一律單工且沒有外部 I/O 存取
  3. 允許外界執行環境 (如通用的 Linux 作業系統) 取得上述執行的結果

採用 Moxie 架構一來確保極小的處理器和 ISA 實作,二來又保證 GNU Toolchain 的支援,我們可從兩個面向去思考這樣的執行環境:

  • 從 Moxie 程式的觀點:這是個單執行緒的執行環境,作業系統核心和應用程式包裝在受限的環境中;
  • 從隔離執行的觀點來看,應用程式執行在非特權模式,並且只能存取給定的記憶體範圍,moxiebox 提供記憶體映射 (也就是 mmap 系統呼叫),但附帶非常多的限制;

詳細資訊請見文件 sandbox execution environment

透過 moxiebox 開發隔離執行的程式

儘管 moxie 處理器架構由 GNU Toolchain 所支持,但從原始程式碼編譯到整個得以運作,不僅耗時又有太多細節要處理,這裡我們採用修改過的 crosstool-NG 作為打造支援 moxie 處理器架構的 GNU Toolchain 產生器。

首先,參照 crosstool-NG 的 Setting up host OS 以得知必要的預備項目,以 Ubuntu Linux 為例,需要安裝以下套件:

$ sudo apt install gcc gperf bison flex \
                   texinfo help2man make \
                   libncurses5-dev python-dev \
                   bzip2 xz-utils gawk \
                   autotools-dev automake autoconf

然後取得我們維護的 crosstool-NG 並安裝: (注意不要用 root 權限執行下述命令)

$ git clone https://github.com/jserv/crosstool-ng
$ cd crosstool-ng
$ ./bootstrap
$ ./configure --prefix=$HOME/.local
$ make
$ make install

這裡我們將 $HOME/.local 作為 crosstool-ng 的安裝目錄,這樣可避免不同版本之間的衝突,記得要更新 $PATH 環境變數:

$ export PATH=$HOME/.local/bin:$PATH

這也可更新到 ~/.bashrc 的最後一行,避免日後還要額外設定。

一旦 crosstool-ng 安裝到指定目錄後,我們可透過 ct-ng 來處理 GNU Toolchain 建構的組態和編譯細節,不過要特別注意,在 crosstool-ng 的設計考量中,使用者需要建立新的目錄,以便針對不同的組態去準備各種 GNU Toolchain 的輸出。

接下來我們示範如何編譯支援 Moxie 處理器架構的 GNU Toolchain。當然要先建立新的目錄:

$ mkdir -p ~/build-toolchain
$ cd ~/build-toolchain

重頭戲就是使用 ct-ng,我們用預設的 moxiebox 組態即可:

$ ct-ng moxie-none-moxiebox

參考的輸出訊息為:

#
# configuration written to .config
#

當然還可以用 $ ct-ng menuconfig 去細緻調整各種組態,不過我們先忽略。下一步終於可從無到有編譯 GNU Toolchain,請確保網路暢通並有超過 2 GB 的磁碟空間:

$ ct-ng build

由於 crosstool-NG 是從網路下載各種 GNU Toolchain 編譯所需要的原始程式碼套件,所以前半段可能會因為網路連線速度不佳或者遠端伺服器暫停運作,而頻頻失敗,屆時會需要分析 ct-ng build 所產生的 build.log

大約需要等待 15 分鐘 (或更長,取決於你的網路速度、電腦運算能力,還有磁碟速度),最後 crosstool-ng 會在 $HOME/x-tools/moxie-none-moxiebox 產生對應的 GNU Toolchain 執行檔。

有了 GNU Toolchain,我們終於可準備使用 moxiebox:

$ git clone https://github.com/sysprog21/moxiebox
$ cd moxiebox

記得要將 crosstool-ng 產生的 GNU Toolchain 執行檔加入 $PATH 環境變數,可透過以下命令:

$ source envsetup

由於 moxiebox 用到 ELF 的處理,需要安裝對應的函式庫:

$ sudo apt install libelf-dev 

編譯和驗證 moxiebox,如下方命令:

$ make
$ make check

參考的輸出訊息如下:

...
moxiebox/tests/sha256.c:10: (find_data:0x141c) 4 executions
moxiebox/tests/sha256.c:23: (hash_data:0x1436) 1 executions
	[ sha256 ]

make[1]: Leaving directory 'moxiebox/tests'

moxiebox 最重要的執行檔為 src/sandbox,用法如下:

src/sandbox \
      -e Moxie執行檔的名稱 \
      -d 輸入資料 \
      -o 保存Moxie執行檔的輸出

也可追加 -p gmon.out 參數,輸出支援 GNU gprof 的 profiling data,可比照 tests/run-sha256.sh,觀察 moxie-none-moxiebox-gprof 的輸出。

gprof 的手法為 instrumentation,原理可參見 Understanding Performance Collection Methods

至於 GDB 也可支援,參閱 遠端除錯 先熟悉 GDB 命令和相關操作。目前 moxiebox 已實作基本的 GDB stub,允許遠端執行的 Moxie 程式碼和本地的 gdb 之間進行溝通。


上圖左邊是moxie-none-moxiebox-gdb,開發者可在互動操作環境中,追蹤程式的執行。右邊則是 moxiebox 創造出來的 Moxie 虛擬機器和其上的程式,而剛提到的 GDB stub 就是負責讓雙方得以溝通的小程式,詳情參閱原始程式碼 src/sandbox.cc 裡頭 "gdb" 字樣對應的程式碼。

先在 Ubuntu Linux 開啟 2 個虛擬終端機 (記得要切換到 moxiebox 所在目錄並 $ source envsetu,兩個都是),為了描述的便利,姑且稱為「終端機 A」和「終端機 B」。在終端機 A 執行下述命令:

$ src/sandbox -e tests/rtlib -g 9999
ep 00001000
ro 00000f8c-00001540 elf0
rw 00001640-00001aa8 elf1
rw 00002aa8-00012aa8 stack
ro 00013aa8-00013b48 mapdesc

這時候 sandbox 就會等待遠端 GDB 的連線。

我們可切換到終端機 B,隨後執行以下命令:

$ moxie-none-moxiebox-gdb -q tests/rtlib

預期的輸出訊息為:

Reading symbols from tests/rtlib...done.
(gdb)

其中 (gdb) 就是命令提示訊息,可在後方輸入 GDB 命令,參照朝陽科技大學洪朝貴教授整理的 除錯程式: gdb 以獲得初步認知。

以下是操作示範,混雜了輸入和 GDB 輸出,只要是 (gdb) 開頭都表示使用者的命令:

(gdb) target remote :9999
Remote debugging using :9999
0x00001000 in __start ()
(gdb) b main
Breakpoint 1 at 0x13da: file rtlib.c, line 73.
(gdb) c
Continuing.

Breakpoint 1, main (argc=0, argv=0x2) at rtlib.c:73
73	{
(gdb) x/4i $pc
=> 0x13da <main>:	push	$sp, $r6
   0x13dc <main+2>:	push	$sp, $r7
   0x13de <main+4>:	dec	$sp, 0x38
   0x13e0 <main+6>:	ldi.l	$r2, 0x11
(gdb) step
main (argc=0, argv=0x2) at rtlib.c:74
74	    test_string_func();

上述命令涵蓋: (對應到個別命令)

  1. 透過 GDB stub,連線到 port 9999 (同一台 TCP/IP 主機)
  2. 設定中斷點在函示 main
  3. 命令 ccontinue 的縮寫,顧名思義,讓原本 sandbox 上 Moxie 程式得以繼續執行
  4. 命令 step 讓 GDB 得以逐行 (以 C 語言原始程式碼的行號為主) 執行

我們可透過 GDB 掌握執行時期的資訊,像是:

(gdb) info registers 
$fp            0x12a9c	0x12a9c
$sp            0x12a5c	0x12a5c
$r0            0x0	0
$r1            0x2	2
$r2            0x0	0
$r3            0x0	0
$r4            0x8	8
$r5            0x0	0
$r6            0x0	0
$r7            0x0	0
$r8            0x0	0
$r9            0x0	0
$r10           0x0	0
$r11           0x0	0
$r12           0x12a78	76408
$r13           0x0	0
$pc            0x13e0	0x13e0 <main+6>
$cc            0x0	0

以及函式呼叫的狀況 (參照 你所不知道的C語言:函式呼叫篇 以得知背景知識):

(gdb) info frame 
Stack level 0, frame at 0x12a5c:
 $pc = 0x13e0 in main (rtlib.c:74); saved $pc = 0x110140
 source language c.
 Arglist at 0x12a9c, args: argc=0, argv=0x2
 Locals at 0x12a9c, Previous frame's sp is 0x12a5c
 Saved registers:
  $fp at 0x12a9c, $r6 at 0x12a98, $r7 at 0x12a94, $pc at 0x12aa0

在 crosstool-ng 預設組態中,GDB 沒有開啟 Python 整合支援,需要透過 ct-ng menuconfig 手動開啟該選項並重新 ct-ng build 已得到支援 Python 的 moxie-none-moxiebox-gdb