如何使用z3解决这个问题?

时间:2017-10-24 08:06:06

标签: python z3 z3py

我刚刚开始使用 z3 编写脚本,仅用于CTF挑战。

for ( i = 0; i <= 7; ++i )
{
   s2[i] += s2[i] % 10;
   if ( s2[i] > 123 )
     s2[i] = -100 - s2[i];
}
strcmp("H0Tf00D:<", s2)

这是非常简单的逻辑,甚至可以手动完成。 但是当我正在学习 z3 时,我想是否可以使用z3来完成这项工作。

如果条件使用z3 ,我已经将我的家庭作业做了一些,并且我发现的内容并不多。

这些是我研究过的一些事情:

  1. Z3 Prover Github
  2. rise4fun - guide
  3. z3-how-to-encode-if-the-else-in-z3-python
  4. z3py-tutorial - ericpony Github
  5. P.S。我不想要解决方案,只想知道是否可以使用z3来完成,如果是,那么请指出正确的方向。

    UPDATE1

    我已经取得了很大的进步(虽然没什么):

    from z3 import *
    
    s1 = Int('s[1]')
    s2 = Int('s[2]')
    s3 = Int('s[3]')
    s4 = Int('s[4]')
    s5 = Int('s[5]')
    s6 = Int('s[6]')
    s7 = Int('s[7]')
    
    s = Solver()
    
    a = "H0Tf00D:<"
    

1 个答案:

答案 0 :(得分:1)

鉴于已经过了一段时间,我认为发布(一种可能的)解决方案是合适的,以防人们在将来发现这种情况:

from z3 import *

def get_decoded(target):
    """
    Helper function to "decode" a target string using Z3
    """

    #
    # We create a Z3 array for the contents of the string (this saves
    # needing have multiple copies of the "s[i]" variables.
    #
    # However, this could be less efficient, but this isn't a concern for this
    # puzzle
    #
    string = Array("string", BitVecSort(32), BitVecSort(32))

    #
    # We save a copy of the string as the "initial" string, as we need to
    # interrogate the string _before_ the updates are made
    #    
    initial_string = string

    #
    # Create a Z3 solver
    #
    s = Solver()

    #
    # We now iterate over the length of the "target" string, and construct the
    # encoding as per the CTF instance
    #
    for idx in range(len(target)):
        #
        # Extract the single character at "idx" from the target string
        #
        single_c = target[idx]

        #
        # Find its ASCII value
        #
        ord_val = ord(single_c)

        #
        # Generate the corresponding Z3 constant
        #
        ord_const = BitVecVal(ord_val, 32)

        #
        # Generate the cell position as a Z3 constant
        #
        cell_pos = BitVecVal(idx, 32)

        #
        # Calculate the non-conditional part of the update
        #
        add_rem = string[cell_pos] + SRem(string[cell_pos], 10)

        #
        # Calculate the conditional part of the update using a Z3 If
        #
        to_store = If(add_rem > 123, -100 - add_rem, add_rem)

        #
        # Update the string with our calculated value
        #
        string = Store(string, idx, to_store)

        #
        # Assert that our calculated position is equal to the input value
        #
        s.add(string[cell_pos] == BitVecVal(ord_val, 32))

    #
    # Check the SMT instance and obtain the model
    #
    assert s.check() == sat
    model = s.model()

    #
    # We now interrogate the model to find out what the "original" string was
    #
    output = []

    #
    # Output string is the same length as the input string
    #
    for idx in range(len(target)):
        #
        # As before, calculate the cell position
        #
        cell_pos = BitVecVal(idx, 32)

        #
        # Extract the value for the index in the string
        #
        model_val = model.eval(initial_string[cell_pos]).as_signed_long()

        #
        # Get the ASCII value (we've stored the ASCII integer value, not the
        # char!)
        #
        model_char = chr(model_val)

        #
        # Append it to our output string
        #
        output.append(model_char)

    #
    # Return the joined string
    #
    return "".join(output)


def get_encoded(value):
    """
    Helper function to "encode" a string using Python
    """

    output = []

    #
    # Iterate over the input string
    #
    for idx in range(len(value)):
        #
        # Value at position (as ASCII int)
        #
        ord_val = ord(value[idx])

        #
        # Value we're going to store
        #  
        to_store = ord_val + ord_val % 10

        #
        # Conditional check
        #
        if to_store > 123:

            #
            # As per the CTF
            #
            to_store = -100 - to_store

        #
        # Add it to the output string
        #
        output.append(chr(to_store))

    #
    # Return it
    #
    return "".join(output)


if __name__ == "__main__":
    """
    Entry point
    """

    #
    # String we're aiming for
    #
    target = "H0Tf00D:<"
    print "Target:                   \"{:s}\"".format(target)

    #
    # Calculate the "reverse" using Z3
    #
    decoded = get_decoded(target)
    print "Decoded (via z3):         \"{:s}\"".format(decoded)

    #
    # We now re-encode
    #
    encoded = get_encoded(decoded)
    print "Encoded (via Python):     \"{:s}\"".format(encoded)

    #
    # Both strings should be equal
    #
    assert encoded == target

# EOF