Вы объединяете строки 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;но как получается, что орды не составляют до 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 будет хорошим началом для ознакомления с ними и не стесняйтесь задавать дополнительные вопросы.