如何将z3.String
转换为ASCII值序列?
例如,下面是一些我认为可以检查字符串中所有字符的ASCII值是否总计为100的代码:
import z3
def add_ascii_values(password):
return sum(ord(character) for character in password)
password = z3.String("password")
solver = z3.Solver()
ascii_sum = add_ascii_values(password)
solver.add(ascii_sum == 100)
print(solver.check())
print(solver.model())
不幸的是,我收到此错误:
TypeError: ord() expected string of length 1, but SeqRef found
很明显ord
与z3.String
不兼容。 Z3中有什么功能吗?
答案 0 :(得分:1)
您正在混合Python字符串和Z3字符串;不幸的是,两者是完全不同的类型。
在Z3py中,String
只是8位值的序列。 Z3所能做的实际上非常有限。例如,您不能像在add_ascii_values
函数中那样遍历字符。请参见本页以了解允许的功能:https://rise4fun.com/z3/tutorialcontent/sequences(此页面以SMTLib的术语列出;但是等效的功能可从z3py界面获得。)
使用Z3序列和字符串时,需要记住一些重要的限制/事项:
您必须非常明确地说明长度;特别是,您不能sum
遍历任意符号长度的字符串。您可以执行一些操作而无需显式指定长度,但这是有限的。 (例如正则表达式匹配,子字符串提取等)
您不能从字符串中提取字符。我认为这是一个疏忽,但SMTLib暂时无法这样做。相反,您会得到一个长度为1的列表。这在编程中引起很多麻烦,但是有解决方法。见下文。
每当您遍历字符串/序列时,都必须上升到固定范围。有多种编程方法,因此您可以将“ N为止的所有字符串都覆盖为一个常数” N,但是它们确实很毛。
牢记所有这些,我将像下面这样对您的示例进行编码;将password
的长度限制为10个字符:
from z3 import *
s = Solver()
# Work around the fact that z3 has no way of giving us an element at an index. Sigh.
ordHelperCounter = 0
def OrdAt(inp, i):
global ordHelperCounter
v = BitVec("OrdAtHelper_%d_%d" % (i, ordHelperCounter), 8)
ordHelperCounter += 1
s.add(Unit(v) == SubString(inp, i, 1))
return v
# Your original function, but note the addition of len parameter and use of Sum
def add_ascii_values(password, len):
return Sum([OrdAt(password, i) for i in range(len)])
# We'll have to force a constant length
length = 10
password = String("password")
s.add(Length(password) == 10)
ascii_sum = add_ascii_values(password, length)
s.add(ascii_sum == 100)
# Also require characters to be printable so we can view them:
for i in range(length):
v = OrdAt(password, i)
s.add(v >= 0x20)
s.add(v <= 0x7E)
print(s.check())
print(s.model()[password])
OrdAt
函数可解决无法提取字符的问题。还要注意我们如何使用Sum
而不是sum
,以及所有“循环”如何具有固定的迭代计数。为了方便起见,我还添加了一些约束以使所有ascii代码都可打印。
运行此命令,您将得到:
sat
":X|@`y}@@@"
让我们检查一下它的确不错:
>>> len(":X|@`y}@@@")
10
>>> sum(ord(character) for character in ":X|@`y}@@@")
868
因此,我们确实得到了长度为10的字符串;但是ord的总和为什么不等于100?现在,您必须记住序列是由8位值组成的,因此该算术以256为模进行。因此,总和实际上是:
>>> sum(ord(character) for character in ":X|@`y}@@@") % 256
100
为避免溢出,可以使用更大的位向量,或者更简单地使用Z3的无界整数类型Int
。为此,只需将BV2Int
更改为:即可使用add_ascii_values
函数:
def add_ascii_values(password, len):
return Sum([BV2Int(OrdAt(password, i)) for i in range(len)])
现在我们将得到:
unsat
那是因为我们每个字符的值都至少为0x20
,而我们需要10个字符;所以没有办法使它们的总和等于100。而z3正是在告诉我们。如果将总和目标提高到更合理的水平,您将开始获得适当的价值。
使用z3py进行编程与使用Python进行常规编程不同,并且z3 String
对象与Python本身完全不同。请注意,SMTLib人员甚至尚未对序列/字符串逻辑进行标准化,因此情况可能会发生变化。 (特别是,我希望它们会添加用于在索引处提取元素的功能!)。
说了这么多,遍历https://rise4fun.com/z3/tutorialcontent/sequences是熟悉它们的好开始,并可以随时提出进一步的问题。