调用修改输入的函数时如何正确使用 z3

How to properly use z3 when calling a function that modifies the input

我正在学习如何使用 z3(我想在 CTF 挑战赛中使用它)并且已经开始同意我妻子最喜欢的说法 "I need help" :)我在这里发帖,希望有人可以查看我的嵌入式示例问题(CTF 二进制文件中的实际代码太大,太复杂,无法包含在这里)并帮助指导我获得上述帮助。

我想要完成的是:

现在我已经编写并重写了下面的代码,直到我脸色发青,头发都拔光了,我还没有弄清楚我做错了什么。作为健全性检查,我添加了代码,当在没有 z3 的情况下使用时,它会生成我正在寻找的 64 位密钥(它没有弄清楚列表值之一是什么,但向我显示了我希望 z3 弄清楚的最终答案)。

最后,我知道下面的代码在 z3 性质上相当简单(而且代码只是为了帮助支持我的问题而设计的,因此没有编码为 release/production 标准)并且我可能是缺少基本概念(在我对这个 SMT 事物非常陌生之后)所以不要害怕证实我妻子的理论:我很愚蠢 ;) 因为这不是第一次如此可能的琐碎事情让我发疯几天(我相信这不会是最后一次)。

非常感谢任何能在这件事上把我的头从沙子里拉出来并告诉我我的方法错误的人。

顺便说一句:sample_func 代码是来自 CTF 二进制

的实际汇编语言代码的 python 表示
from z3 import *

def sample_func(pDataBuf_1, pDataBuf_2):

    tVar1 = pDataBuf_1[0]
    tVar2 = pDataBuf_1[1]

    xVar1 = pDataBuf_1[0]
    xVar2 = pDataBuf_1[1]


    tVar3 =(  (tVar1 * 0x1000 | tVar2 >> 0x14) + xVar1 ) & 0xFFFFFFFF
    tVar4 =(  ((tVar3 ^ xVar2) & xVar2 ^ tVar3) + xVar2 + -0x3e423112 + tVar2 ) & 0xFFFFFFFF

    pDataBuf_1[0] = tVar4
    pDataBuf_1[1] = tVar3

    #
    # I am adding the return value as a value for z3 to check against.  What I 
    # really want is to have z3 just operate on the bitvector values.  In the real
    # binary these bitvector values will be used to form two 64 bit keys.
    #
    return (tVar3 << 32) | tVar4

use_z3 = False

non_z3_pbuf1 = [0x67452301, 0xefcdab89] 

p1 = z3.BitVecVal(0x00000000, 32) # Z3 needs to figure out that p1 == 0x67452301
p2 = z3.BitVecVal(0xefcdab89, 32)

tlist1 = [p1, p2]

non_z3_pbuf2 = [0x79707062, 0x6d326e34]

pb1 = z3.BitVecVal(0x79707062, 32)
pb2 = z3.BitVecVal(0x6d326e34, 32)

tlist2 = [pb1, pb2]

if use_z3 == True:
    s = Solver()

    while True:
        s.add( sample_func(tlist1, tlist2) == 0xb97541fda15711fd)    
        print(s)
        if s.check() == sat:
            break;
else:
    #
    # Using non_z3_pbuf1 and non_z3_pbuf2 will generate the following 
    # results:
    #
    #    0xb97541fda15711fd
    #    0xa15711fd
    #    0x6d326e34
    #
    x = sample_func(non_z3_pbuf1, non_z3_pbuf2)
    print(hex(x))
    print(hex(non_z3_pbuf1[0]))
    print(hex(non_z3_pbuf2[1]))

最好提出具体问题,而不是笼统的建议。但看起来你走在正确的道路上。特别是,您应该使用 print s.sexpr(),其中 s 是求解器实例,以查看 z3 试图求解的内容。很难从你的问题中解读出一个确切的目标,但这里有一个简单的重写:

from z3 import *

def sample_func(pDataBuf_1, pDataBuf_2):

    tVar1 = pDataBuf_1[0]
    tVar2 = pDataBuf_1[1]

    xVar1 = pDataBuf_1[0]
    xVar2 = pDataBuf_1[1]


    tVar3 =(  (tVar1 * 0x1000 | tVar2 >> 0x14) + xVar1 ) & 0xFFFFFFFF
    tVar4 =(  ((tVar3 ^ xVar2) & xVar2 ^ tVar3) + xVar2 + -0x3e423112 + tVar2 ) & 0xFFFFFFFF

    pDataBuf_1[0] = tVar4
    pDataBuf_1[1] = tVar3

    return (tVar3 << 32) | tVar4

p1 = BitVec('p1', 32)
p2 = BitVec('p2', 32)

tlist1 = [p1, p2]

pb1 = BitVec('pb1', 32)
pb2 = BitVec('pb2', 32)

tlist2 = [pb1, pb2]

s = Solver()
s.add( sample_func(tlist1, tlist2) == 0xb97541fda15711fd)
print s.sexpr()

res = s.check()

if res == sat:
   print s.model()
else:
   print "Didn't get sat"

当我 运行 这个时,我得到:

(declare-fun p2 () (_ BitVec 32))
(declare-fun p1 () (_ BitVec 32))
(assert (let ((a!1 (bvand (bvadd (bvor (bvmul p1 #x00001000) (bvashr p2 #x00000014)) p1)
                  #xffffffff)))
(let ((a!2 (bvadd (bvxor (bvand (bvxor a!1 p2) p2) a!1) p2 #xc1bdceee p2)))
  (= (bvor (bvshl a!1 #x00000020) (bvand a!2 #xffffffff)) #xa15711fd))))

[p1 = 2944401152, p2 = 269336837]

现在,您应该研究打印内容的第一部分。那就是你 "constructed" 给 z3 的程序。它采用类似 lisp 的表示法,但应该易于阅读。需要注意的一件事是变量 pb1pb2 从未出现过!那是因为你没有在 sample_func 中使用它们。那是你想要的吗?下一步是研究打印的表达式并确保它确实是您想要计算的。你可以从那里拿走它。

最后,确保找到的模型 z3(即本例中的 [p1 = 2944401152, p2 = 269336837])确实是您构建的程序的正确答案。如果您真的是认真的,那将清楚地表明您所写的内容! (答案无疑是正确的;但可能不是您想要的。)

一般来说,在使用 z3py 编程时,您应该考虑 "functions" 而不是 "overwriting" 变量。但最好在取得进展时继续这条道路和具体问题。祝你好运!