ที่เก็บนี้มีการใช้งานสำหรับเครื่องเสมือนจริง อย่างง่าย พร้อมกับไดรเวอร์ซึ่งจะอ่านโปรแกรมจากไฟล์และดำเนินการผ่านเครื่องเสมือนนั้น
นอกเหนือจากล่ามเครื่องเสมือนจริงที่คุณจะพบ:
เครื่องเสมือนนี้โดยเฉพาะนั้นง่าย แต่ก็หวังว่าจะมีการใช้งานในแบบที่อ่านได้ ("ความเรียบง่าย" ที่นี่หมายความว่าเรารองรับคำแนะนำเพียงเล็กน้อยเท่านั้นและการลงทะเบียนซีพียูเสมือนจริงสามารถเก็บสตริงและจำนวนเต็มได้ แต่ไม่ใช่ค่าจุดลอยตัว) เครื่องเสมือนนี้โดยเฉพาะนั้นมีการลงทะเบียน
เพราะคอมไพเลอร์และ decompiler เขียนด้วย 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 ไบนารีได้รับการจัดการผ่านโต๊ะจัดส่งจึงเป็นไปได้เล็กน้อยสำหรับคุณที่จะเพิ่ม opcodes เฉพาะแอปพลิเคชันของคุณเองในระบบซึ่งจะช่วยให้คุณสามารถเรียกใช้โปรแกรมเล็ก ๆ และมีประสิทธิภาพซึ่งสามารถโทรกลับเข้าไปในแอปพลิเคชันของคุณเมื่อพวกเขาต้องการ
มีตัวอย่างของการกำหนด opcode ที่กำหนดเองในไฟล์ src/embedded.c ตัวอย่างนี้กำหนด opcode ที่กำหนดเอง 0xCD และดำเนินการโปรแกรมขนาดเล็กซึ่งใช้ opcode นั้นเพื่อวัตถุประสงค์ในการสาธิต:
$ ./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 จากนั้นพิมพ์ลงทะเบียนก่อนที่จะกระโดดกลับไปที่จุดเริ่มต้นของโปรแกรม
เพื่อรวบรวมโปรแกรมนี้ลงใน Bytecode Run:
$ ./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
ตอนนี้เราสามารถดำเนินการชุด opcodes:
./simple-vm ./examples/simple.raw
หากคุณต้องการแก้ไขข้อบกพร่องการดำเนินการให้เรียกใช้:
DEBUG=1 ./simple-vm ./examples/simple.raw
มีตัวอย่างเพิ่มเติมที่เก็บไว้ใต้ examples/ ไดเรกทอรีย่อยในที่เก็บนี้ ตัวอย่างไฟล์/quine.in เป็นตัวอย่างที่ดีของคุณสมบัติต่าง ๆ - มันส่งออก opcodes ของตัวเอง
หากคุณต้องการที่จะทดสอบการทดสอบด้วย AFL คุณควรพบว่าค่อนข้างตรงไปตรงมา:
afl เองตามคำแนะนำCC=afl-gcc ใน Makefileคุณสามารถรวบรวมตัวอย่างที่รวมของเราแต่ละรายการได้เช่น:
cd examples/
for i in *.in; do ../compiler $i; done
cd ..
สิ่งนี้จะรวบรวม examples/*.in examples/*.raw วางสิ่งเหล่านั้นไว้ในไดเรกทอรีและเริ่มฟัซเซอร์ของคุณ:
mkdir samples/
cp examples/*.raw samples/
ตอนนี้คุณมี ./samples/ มีโปรแกรมที่รวบรวมเท่านั้น จากนั้นคุณสามารถกลายพันธุ์/permute ตัวอย่างเหล่านั้นภายใต้การควบคุมของฟัซเซอร์ด้วยบรรทัดคำสั่งเช่นนี้:
export FUZZ=1
afl-fuzz -i ./samples/ -o results/ ./simple-vm @@ 16384
หมายเหตุ : ที่นี่เราแต่ละคนเรียกใช้เพื่อดำเนินการไม่เกิน 16384 คำแนะนำ สิ่งนี้จะช่วยให้มั่นใจได้ว่าโปรแกรมไม่มีลูปที่ไม่มีที่สิ้นสุด
เราตั้งค่าตัวแปรสิ่งแวดล้อม FUZZ ให้มี 1 เพียงเพื่อปิดการใช้งานฟังก์ชั่น system() ซึ่งอาจลบไดเรกทอรีที่บ้านของคุณจัดรูปแบบไดรฟ์ของคุณหรือส่งเงินบริจาคให้ฉัน!
พอร์ต Golang ของคอมไพน์เสมือนเครื่องคอมไพน์และล่ามมีให้บริการจากที่เก็บต่อไปนี้:
นอกจากนั้นคุณยังสามารถค้นหาเครื่องเสมือน จริงที่แท้จริง ภายในเอ็นจิ้นสคริปต์แบบฝังที่ฉันเขียนเช่นเดียวกับ Golang ในกรณีนั้นภาษาสคริปต์จะถูกแยกวิเคราะห์และแปลงเป็นชุดคำสั่ง bytecode ซึ่งดำเนินการโดยเครื่องเสมือน คล้ายกับโครงการนี้ แต่การดำเนินการ bytecode มีความซับซ้อนและระดับสูงมากขึ้น:
หากคุณสนใจคอมไพเลอร์และล่ามโดยทั่วไปคุณอาจสนุกกับโครงการอื่น ๆ เหล่านี้ด้วย: