該存儲庫包含一個簡單的虛擬機器的實現,以及將從文件中讀取程序並通過該虛擬機執行的驅動程序。
除虛擬機解釋器外,您還可以找到:
這種特殊的虛擬機有意簡單,但是儘管如此,希望它能以可讀的方式實施。 (這裡的“簡單性”是指我們僅支持少量指令,並且虛擬CPU擁有的寄存器可以存儲字符串和整數,而不是浮點值。)此特定的虛擬機是基於寄存器的,具有十個寄存器,可用於存儲字符串或插圖值。
由於編譯器和分解器是用Perl編寫的,因此不需要特殊處理。
用C編寫的解釋器可以像這樣構建:
$ make
這將生成simple-vm並embedded SRC/的內容。
實施基本的虛擬機,像這樣,是一個非常有理由的問題:
case語句解碼說明。halt或exit指導為止。編寫虛擬機的主要並發症是:
goto repeat ”不是“ 0x10 0x03 0x00 ”。這款特定的虛擬機僅包含一些原語,但確實包括對標籤,循環,有條件跳躍,呼叫和返回的支持。還有一個堆棧,可用於存儲臨時值,並用於call / ret處理。
在此實現中處理標籤可能值得一提,因為許多簡單/演示的虛擬機根本無法處理它們。
為了支持跳轉到不一定定義的標籤,我們的編譯器會保留所有標籤的運行列表(即可能的跳躍目的地),當它遇到跳躍指令或引用標籤的其他內容時,它會輸出一個位置持有人地址,例如::
JUMP 0x0000x10 0x00 0x00因為跳躍指令定義為0x10 。編譯完成後,所有目標都應被發現,並且編譯器可以自由更新生成的字節碼以填充適當的目的地。
注意:在我們的虛擬機中,所有跳躍都是絕對的,因此它們可能看起來像“
JUMP 0x0123”,而不是“JUMP -4”或“JUMP +30”。
注意:同樣的事情適用於處理標籤的其他說明,例如存儲標籤的地址,撥打電話等。
這種虛擬機的設計主要是作為一種學習體驗,但它是基於嵌入的想法而構建的。
標準的simple-vm二進製文件將從文件中讀取opcodes並解釋它們,尺寸小於40k。
由於二進制操作編碼的處理是通過調度桌處理的,因此您可以在系統上將自己的特定於應用程序特定的Opcodes添加到系統中,這將使您可以在願意的情況下執行可以返回應用程序的細微匯總且高效的程序。
有一個示例是在文件src/embedded.c中定義自定義opcode。此示例定義了自定義OPCODE 0xCD ,並執行一個小程序,該程序將OPODE用於演示目的:
$ ./embedded
[stdout] Register R01 => 16962 [Hex:4242]
Custom Handling Here
Our bytecode is 8 bytes long
實施了幾種指令類型:
這些說明非常基本,因為這只是一個玩具,但是添加新的說明並不困難,因此可用的原始圖是相當有用的。
以下是所有說明的示例:
:test
:label
goto 0x44ff # Jump to the given address
goto label # Jump to the given label
jmpnz label # Jump to label if Zero-Flag is not set
jmpz label # Jump to label if Zero-Flag is set
store #1, 33 # store 33 in register 1
store #2, "Steve" # Store the string "Steve" in register 1.
store #1, #3 # register1 is set to the contents of register #3.
exit # Stop processing.
nop # Do nothing
print_int #3 # Print the (integer) contents of register 3
print_str #3 # Print the (string) contents of register 3
system #3 # Call the (string) command stored in register 3
add #1, #2, #3 # Add register 2 + register 3 contents, store in reg 1
sub #1, #2, #3 # sub register 2 + register 3 contents, store in reg 1
mul #1, #2, #3 # multiply register 2 + register 3 contents, store in reg 1
concat #1, #2,#3 # store concatenated strings from reg2 + reg3 in reg1.
dec #2 # Decrement the integer in register 2
inc #2 # Increment the integer in register 2
string2int #3 # Change register 3 to have a string from an int
is_integer #3 # Does the given register have an integer content?
int2string #3 # Change register 3 to have an int from a string
is_string #3 # Does the given register have a string-content?
cmp #3, #4 # Compare contents of reg 3 & 4, set the Z-flag.
cmp #3, 42 # Compare contents of reg 3 with the constant 42. sets z.
cmp #3, "Moi" # Compare contents of reg 3 with the constant string "Moi". sets z.
peek #1, #4 # Load register 1 with the contents of the address in #4.
poke #1, #4 # Set the address stored in register4 with the contents of reg1.
random #2 # Store a random integer in register #2.
push #1 # Store the contents of register #1 in the stack
pop #1 # Load register #1 with the contents of the stack.
call 0xFFEE # Call the given address.
call my_label # Call the defined label
ret # Return from a called-routine.
以下程序將無休止地輸出一個字符串:
:start
store #1, "I like loops"
print_str #1
goto start
該程序首先將字符串“ I like loops ”存儲在寄存器1中,然後在跳回程序開始之前打印該寄存器。
實際將此程序編譯到字節碼運行中:
$ ./compiler ./examples/simple.in
這將產生一個輸出文件,其中包含文件simple.raw中的二進製文件:
$ od -t x1 -An ./examples/simple.raw
01 01 0c 49 20 6c 69 6b 65 20 6c 6f 6f 70 73 03
01 06 00 00
現在,我們可以執行一系列的操作編碼:
./simple-vm ./examples/simple.raw
如果您想調試執行,請運行:
DEBUG=1 ./simple-vm ./examples/simple.raw
在此存儲庫中的examples/子目錄下面還有更多示例。文件示例/quine.in提供了各種功能的一個很好的示例 - 它輸出了自己的操作編碼。
如果您想與AFL進行模糊測試,則應該發現這很簡單:
afl本身。CC=afl-gcc Makefile中的AFL-GCC設置。您可以像這樣編譯我們的每個捆綁樣本:
cd examples/
for i in *.in; do ../compiler $i; done
cd ..
這將匯總examples/*.in examples/*.raw 。將它們放在目錄中,然後啟動您的fuzzer:
mkdir samples/
cp examples/*.raw samples/
現在,您有./samples/僅包含編譯程序。然後,您可以使用這樣的命令行突變/縮小這些示例在Fuzzer的控制下:
export FUZZ=1
afl-fuzz -i ./samples/ -o results/ ./simple-vm @@ 16384
注意:在這裡,我們將每次運行限制為執行不超過16384個說明。這將確保程序中沒有無限循環。
我們將環境變量FUZZ設置為僅包含1以禁用system()函數的使用。這可能會意外刪除您的主機,格式化您的驅動器或向我發送捐款!
可從以下存儲庫中獲得虛擬機編譯器和解釋器的Golang端口:
除此之外,您還可以在我編寫的嵌入式腳本引擎中找到真正的虛擬機器,也可以找到Golang。在這種情況下,腳本語言被解析並轉換為一系列字節碼指令,這些指令由虛擬機執行。與該項目類似,但是字節碼操作更加複雜和高級:
如果您對編譯器和口譯員感興趣,通常也可能會喜歡這些其他項目: