在这节课中,我们讨论了定义程序语义的一种特定风格:操作语义,即程序在某些虚拟机上执行。我们还讨论了符号执行,它类似于操作语义,只是程序是在一个符号虚拟机上执行的,所有的程序路径都是以系统的方式探索的。最后,我们讨论了concolic的执行,即程序的具体执行和符号执行。
在这个任务中,我们将构建几个执行器:具体执行器、符号执行器和concolic执行器。具体来说,这个作业分为四个部分,每个部分都包含一些教程和问题。第一部分是关于具体执行的,您将根据我们在类中讨论的大步骤操作语义实现一个执行器;第二部分是符号执行,第三部分是concolic执行,它将符号执行和具体执行相结合;最后,也就是最后一部分,我们将通过将这种技术应用于一个很容易理解的安全问题:SQL注入,来获得一些符号执行应用程序的经验。有些问题被标记为练习,你应该解决。还有一些问题被标记为挑战,它们是可选的。首先下载这个代码模板。
Part A: Concrete execution(具体执行)
具体执行的思想很简单:我们构建一个虚拟机来执行程序。VM的执行规则遵循操作语义规则(大步骤或小步骤,取决于您选择的样式)。在很多情况下,这样的执行引擎通常被称为解释器,因为我们直接解释代码,而不是将它们编译为本地代码。
在本部分中,我们将为MiniPy语言构建一个具体的执行器(解释器)。
MiniPy的抽象语法:
其中非终结符F表示函数,(x1, ..., xn)表示函数的参数,S表示函数中的语句,E表示函数的返回表达式;非终结符S代表一个语句;非终结符E代表一个表达式;非终结符B代表一个二进制操作符。
您将构建的具体执行程序(解释器)将直接执行MiniPy的源代码。为此,您将首先构建数据结构来表示MiniPy的语法,这种数据结构称为抽象语法树(AST),正如我们在前面的作业中多次看到的,针对各种语言。
练习1:阅读mini_py中的代码。它包含我们为您定义的AST数据结构。为了保持简单(但不是更简单),我们修改了MiniPy语法,最大的变化是删除了函数调用语句f(E1,…,En)。你还会发现函数类中的魔法方法__str__()没有实现。你在这个练习中的任务就是完成它。完成代码之后,不要忘记运行测试用例以获得所需的代码打印结果。
# This bunch of code declare the syntax for the language MiniPy:
'''
B ::= + | - | * | / | == | != | > | < | >= | <=
E ::= n | x | E B E
S ::= pass
| x = E
| seq(S, S)
| f(E1, ..., En)
| if(E, S, S)
| while(E, S)
F ::= f((x1, ..., xn), S, E)
'''
##################################
# bops
class Bop(Enum):
ADD = "+"
MIN = "-"
MUL = "*"
DIV = "/"
EQ = "=="
NE = "!="
GT = ">"
GE = ">="
LT = "<"
LE = "<="
##########################################
# expressions
class Expr:
pass
class ExprNum(Expr):
def __init__(self, n: int):
self.num = n
def __str__(self):
return f"{self.num}"
class ExprVar(Expr):
def __init__(self, var: str):
self.var = var
def __str__(self):
return f"{self.var}"
class ExprBop(Expr):
#表达式函数、比如 a + b、a - b这种式子
def __init__(self, left: Expr, right: Expr, bop: Bop):
self.left = left
self.right = right
self.bop = bop
def __str__(self):
if isinstance(self.left, ExprBop):
left_str = f"({self.left})"
else:
left_str = f"{self.left}"
if isinstance(self.right, ExprBop):
right_str = f"({self.right})"
else:
right_str = f"{self.right}"
return f"{left_str} {self.bop.value} {right_str}"# 左 OP 右边
###############################################
# statement
class Stmt:
def __init__(self):
self.level = 0
def __repr__(self):
return str(self)
class StmtAssign(Stmt):
# 语句函数 比如 x = ?
def __init__(self, var: str, expr: Expr):
super().__init__()
self.var = var
self.expr = expr
def __str__(self):
indent_space = self.level * "\t"
return f"{indent_space}{self.var} = {self.expr}\n" # a = 式子
class StmtIf(Stmt):
def __init__(self, expr: Expr, then_stmts: List[Stmt], else_stmts: List[Stmt]):
super().__init__()
self.expr = expr
self.then_stmts = then_stmts
self.else_stmts = else_stmts
def __str__(self):
indent_space = self.level * "\t"
for stm in self.then_stmts:
stm.level = self.level + 1
for stm in self.else_stmts:
stm.level = self.level + 1
then_stmts_str = "".join([str(stmt) for stmt in self.then_stmts])
else_stmts_str = "".join([str(stmt) for stmt in self.else_stmts])
then_str = (f"{indent_space}if {self.expr} :\n"
f"{then_stmts_str}")
if self.else_stmts:
return (f"{then_str}"
f"{indent_space}else:\n"
f"{else_stmts_str}")
else:
return then_str
#while语句主要是两个部分,条件、函数体,其中函数体包含多个语句,所以传入的是Stmt类型的语句数组List[Stmt],条件一般是不等式,所以需要ExprBop函数来构建
class StmtWhile(Stmt):
def __init__(self, expr: Expr, stmts: List[Stmt]):
super().__init__()
self.expr = expr
self.stmts = stmts
def __str__(self):
indent_space = self.level * "\t"
for stmt in self.stmts:
stmt.level = self.level + 1
#函数体:把数组里面的stmt数组一条条输出
stmts_str = "".join([str(stmt) for stmt in self.stmts])
return (f"{indent_space}while {self.expr}:\n"
f"{stmts_str}")
# 返回的结果是 while(条件表达式):换行 函数体
###############################################
# function
class Function:
def __init__(self, name: str, args: List[str], stmts: List[Stmt], ret: Expr):
self.name = name
self.args = args
self.stmts = stmts
self.ret = ret
def __str__(self):
arg_str = ",".join(self.args)
for stmt in self.stmts:
stmt.level += 1
stmts_str = "".join([str(stmt) for stmt in self.stmts])
# exercise 1: Finish the magic methods __str__() method to get the
# desired code-printing result:
#
# Your code here:
return ("def " + self.name + "(" + arg_str + ")\n") + (stmts_str) + ("\treturn ") + str(self.ret)
###############################################
# test
#StmtAssign('s',ExprNum(0)),表达的是 s = 0
# StmtAssign('i', ExprNum(0)), 表达的是 i = 0
# StmtWhile 表达while语句、需要传入条件,当然是传入表达式,就需要ExprBop函数,
#表达while i <= n -3
#ExprBop(左边,右边,操作符)
#ExprBop(i, n-3, <=)
#n-3又需要ExprBop函数来表示,所以是 ExprBop(n,3, -)
#对应好相应的类型,i要写成ExprVar('i'),n写成ExprVar('n'),3写成ExprNum(3),-写成Bop.MIN,<=写成Bop.LE
#所以这个while表达式就是 StmtWhile(ExprBop(ExprVar('i'), ExprBop(ExprVar('n'), ExprNum(3), Bop.MIN), Bop.LE)
#同理IF表达式也是这样分析
test_stmt = [StmtAssign('s', ExprNum(0)),
StmtAssign('i', ExprNum(0)),
StmtWhile(ExprBop(ExprVar('i'), ExprBop(ExprVar('n'), ExprNum(3), Bop.MIN), Bop.LE),
[StmtAssign('s', ExprBop(ExprVar('s'), ExprVar('i'), Bop.ADD)),
StmtAssign('i', ExprBop(ExprVar('i'), ExprNum(1), Bop.ADD)),
StmtIf(ExprBop(ExprVar('s'), ExprVar('i'), Bop.GT),
[StmtAssign("b", ExprBop(ExprVar('s'), ExprNum(1), Bop.MIN))],
[])
]),
StmtIf(ExprBop(ExprVar('s'), ExprVar('i'), Bop.GT),
[StmtAssign("s", ExprBop(ExprVar('i'), ExprNum(1), Bop.MIN))],
[StmtAssign("s", ExprBop(ExprVar('i'), ExprNum(1), Bop.ADD))])
]
test_func = Function(name='printer_test', args=['n'], stmts=test_stmt, ret=ExprVar('s'))
if __name__ == '__main__':
# Your code should print:
#
# def printer_test(n):
# s = 0
# i = 0
# while i <= (n - 3):
# s = s + i
# i = i + 1
# if s > i:
# b = s - 1
# if s > i:
# s = i - 1
# else:
# s = i + 1
# return s
#
print(test_func)
输出结果是:
为了构建具体的执行器,我们首先需要定义内存模型,为此,我们将使用Python的数据类模型。我们在具体的.py文件中定义了一个Python数据类内存,它包含了具体内存的定义。内存操作定义为:
练习2:阅读concrete.py文件中的代码,在继续之前确保您理解了具体的模型数据结构。
#中科大软院-华保健老师的形式化课程笔记、欢迎私信交流~