Solving a Crack Me with Triton and Pin (a.k.a the lazy way)

Some definitions


As stated in Triton's home page:

Triton is a dynamic binary analysis (DBA) framework. It provides internal components like a Dynamic Symbolic Execution (DSE) engine, a Taint Engine, AST representations of the x86 and the x86-64 instructions set semantics, SMT simplification passes, a SMT Solver Interface and, the last but not least, Python bindings. Based on these components, you are able to build program analysis tools, automate reverse engineering and perform software verification.

That might sound gibberish for some of you. So let's cover these definitions first.

AST representation

An AST (Abstract Syntax Tree) is a tree representation of the structure of some code.
Example, the AST representing this instruction:

add eax, ebx  

Could be the following:

As explained here, all of Triton's expressions are on the SSA form.
Meaning that each time a register/flag is modified, a new reference to the new value will be created.

Instruction:  add rax, rdx  
Expression:   ref!41 = (bvadd ((_ extract 63 0) ref!40) ((_ extract 63 0) ref!39))  

In this example from Triton's documentation, ref!41 is the new reference to rax and ref!40 is the reference to the previous one.
This allows to easily build "nested" expressions. (new expressions making references to previously generated ones) and provides the ability to get the expression of a register/flag at each execution point. See here for more details.

AST can be useful in order to manipulate an instructions and also to translate them to another language such as SMTLib language in order to feed an SMT Solver.

SMT Solvers

SMT Solvers such as Z3 are used in many applications such as software verification, constraint solving, security etc...
What one needs to know, in order to understand this post, is that SMT Solvers are supposed to provide a solution verifying a set of constraints.

Let's provide a few constraints for Z3 to solve:

from z3 import *

x = Real('x')  
y = Real('y')  
s = Solver()  
s.add(x + 3 * y == 50, x + y == 20)  
print(s.check())  
print(s.model())  

Which will give the following output:

sat  
[y = 15, x = 5]

In this simple example, 2 constraints are provided to z3:

x + 3 * y == 50  
x + y == 20  

And as you can see Z3 was able to find a solution satisfying these constraints.

Z3 is also able to simplify expressions, which can come in handy in case of obfuscated code.
Here's an example:

from z3 import *

x = Real('x')  
y = Real('y')  
s = Solver()  
print simplify(x + 3 * y - 4 * y - 3 * (y + x / 3))  

Output:

-4*y

Now imagine combining all these concepts in order to analyse and exploit binaries.
This is what Triton is able to do.

The Triton/Pintool (Python) API


Triton provides different DBA mechanisms (through symbolic execution or through tracing win Pin)
In this first post I'll also be covering the Pin tracing feature coupled with a few symbolic execution features.

First things first, let's dissect the instruction count script:

#!/usr/bin/env python2
## -*- coding: utf-8 -*-

from pintool import *  
from triton  import *

count = 0

def mycb(inst):  
    global count
    count += 1

def fini():  
    print count

if __name__ == '__main__':  
    setArchitecture(ARCH.X86_64)
    startAnalysisFromEntry()
    insertCall(mycb, INSERT_POINT.BEFORE)
    insertCall(fini, INSERT_POINT.FINI)
    runProgram()

This tiny script is quite self explanatory.
One can find a full description off all triton/pintool bindings here but here are descriptions of the ones we'll be using:
setArchitecture: Setup an architecture (ARCH.X86_64 or ARCH.X86)

startAnalysisFromEntry: Request Pin to start tracing from the executable's entrypoint.
Internally this is just setting the startAnalysisFromEntry option (there is no c++ function associated with this function)

insertCall: this function provides means to add python callbacks as Pin's routine, instruction and syscall callbacks (among other things).

runProgram: This function is a wrapper to PIN_StartProgram. This function basically execve's to the program we want to analyse. It will never return.

With regards to above mentioned descriptions, one can quickly understand that the mycb callback will be called before each called instruction, and the number of instructions executed will be displayed by the fini callback at the end of the program's execution.

Other important bindings we'll use in this post are:
addCallback: Provides callbacks to Triton whenever it will need a concrete memory, a concrete register or when it will need to perform a symbolic simplification.

getPathConstraints: This function holds some of the magic that will make the CrackMe's resolution the easiest possible.
It provides a list of all path constraints that allowed the execution to reach the current execution point. Meaning the equations/symbolic expressions of each control flow instructions (je, jne, ja etc..) preceding the current execution point.

convertMemoryToSymbolicVariable: This function converts a memory address to a symbolic variable (the same kind of variables used with Z3) these variables will be the ones Z3 will provide models/possible solutions.

takeSnapshot/restoreSnapshot: these function allow us to take a whole machine context (cpu and memory) and restore it. This can help to simulate the restart of the execution (remember runProgram never returns).

The binary


The binary we are going to analyse is a crackme I wrote based on a challenge I've payed with recently.

With radare2 or IDA we can quickly see the mess before success (095C):

       <0x4005b6>                              
if false -> f t <- if true  
     .------' '--.                           
     |           |                           
     |           |                           
[_06ea_]      [_0703_]                       
                   f t                       
           .-------' '-.                     
           |           |                     
           |           |                     
      [_070c_]      [_0716_]                 
                         f t                 
                 .-------' '-.               
                 |           |               
                 |           |               
            [_071f_]      [_0729_]           
                               f t           
                       .-------' '-.         
                       |           |         
                       |           |         
                  [_073c_]      [_0746_]     
                                     f t     
                                 .---' |     
                                 |     |     
                                 |     |     
                            [_0755_]   |     
                             v         |     
                             '----.    '.    
                                  |     |    
                                  |     |    
                                 [_0759_]    
                                      f t    
                                 .----'.'    
                                 |     |     
                                 |     |     
                            [_0774_]   |     
                             v         |     
                             '----.    '.    
                                  |     |    
                                  |     |    
                                 [_0778_]    
                                      f t    
                                 .----'.'    
                                 |     |     
                                 |     |     
                            [_0793_]   |     
                             v         |     
                             '----.    '.    
                                  |     |    
                                  |     |    
                                 [_0797_]    
                                      f t    
                                 .----'.'    
                                 |     |     
                                 |     |     
                            [_07af_]   |     
                             v         |     
                             '----.    '.    
                                  |     |    
                                  |     |    
                                 [_07b3_]    
                                      f t    
                                 .----'.'    
                                 |     |     
                                 |     |     
                            [_07d3_]   |     
                             v         |     
                             '----.    '.    
                                  |     |    
                                  |     |    
                                 [_07d7_]    
                                      f t    
                                 .----'.'    
                                 |     |     
                                 |     |     
                            [_07f7_]   |     
                             v         |     
                             '----.    '.    
                                  |     |    
                                  |     |    
                                 [_07fb_]    
                                      f t    
                                 .----'.'        
                                 |     |         
                                 |     |         
                            [_081b_]   |         
                             v         |         
                             '----.    '.        
                                  |     |        
                                  |     |        
                                 [_081f_]        
                                      f t        
                                 .----'.'        
                                 |     |         
                                 |     |         
                            [_0853_]   |         
                             v         |         
                             '----.    '.        
                                  |     |        
                                  |     |        
                                 [_0857_]        
                                      f t        
                                 .----'.'        
                                 |     |         
                                 |     |         
                            [_0878_]   |         
                             v         |         
                             '----.    '.        
                                  |     |        
                                  |     |        
                                 [_087c_]        
                                      f t        
                                 .----'.'        
                                 |     |         
                                 |     |         
                            [_08b9_]   |         
                             v         |         
                             '----.    '.        
                                  |     |        
                                  |     |        
                                 [_08bd_]        
                                      f t        
                                 .----'.'        
                                 |     |         
                                 |     |         
                            [_08fa_]   |         
                             v         |         
                             '--.     .'         
                                |     |          
                                |     |          
                               [_08fe_]          
                                    f t          
                              .-----' '.         
                              |        |         
                              |        |         
                         [_0920_]      |         
                            t f        |         
                     .------' '------. |         
                     |               | |         
                     |               | |         
                  [_095c_]      [_0952_]         

A quick look at the binary shows us that each of the checks are only depending on argv[1]:
variables used for the checks are located in rbp-0x12, rbp-0x11, rbp-0x10, rbp-0xf, rbp-0xe, rbp-0xd
and each of these variables are only depending on argv[1]. Example with this snippet:

          |     |                                 
    =------------------------------------=      
    |  0x400759                          |      
    | movzx eax, byte [rbp - local_11h]  | << depends only on argv[1]  
    | xor al, byte [rbp - local_eh]      | << depends only on argv[1]  
    | movzx eax, al                      |      
    | add eax, 0x11                      |      
    | sub eax, dword [rbp - local_8h]    | << constant value 2  
    | movzx edx, byte [rbp - local_11h]  | << depends only on argv[1]  
    | add edx, 0xa                       |      
    | cmp eax, edx                       |      
    | je 0x400778 ;[h]                   |      
    =------------------------------------=      
                  f t                                 
          .-------' '-------------------.             
          |                             |             
          |                             |             
  =-------------------------------=     |             
  |  0x400774                     |     |             
  | add dword [rbp - local_4h], 1 |     |             
  =-------------------------------=     |             
      v                                 |             
      '-------.     .-------------------'             
              |     |                                 
              |     |                                 
   =------------------------------------=      
   |  0x400778                          |      
   | movzx eax, byte [rbp - local_10h]  | << depends only on argv[1]  
   | xor al, byte [rbp - local_fh]      | << depends only on argv[1]  
   | movzx edx, al                      |      
   | movzx eax, byte [rbp - local_10h]  | << depends only on argv[1]  
   | add edx, eax                       |      
   | movzx eax, byte [rbp - local_fh]   | << depends only on argv[1]  
   | add eax, 2                         |      
   | cmp edx, eax                       |      
   | je 0x400797 ;[i]                   |      
   =------------------------------------=      
                  f t                                 
          .-------' '-------------------.             
          |                             |             
          |                             |             
  =-------------------------------=     |             
  |  0x400793                     |     |             
  | add dword [rbp - local_4h], 1 |     |             
  =-------------------------------=     |             

One can also see that the binary expects an 11 bytes password as only argv[1] offsets from 0 to 0xa are used. (see main's first basic block)

We can also spot two kinds of checks: the ones leading to the end of the program's execution (call to exit) and those which might lead to the incrementation of a variable:

                     | cmp eax, 0xa1                      |        
                     | je 0x400746 ;[f]                   |        
                      =------------------------------------=        
                                          f t                                   
                 .------------------------' '--.                                
                 |                             |                                
                 |                             |                                
=-------------------------=     =-----------------------------------=  
|  0x40073c               |     |  0x400746                         |  
| mov edi, 1              |     | movzx eax, byte [rbp - local_12h] |  
| call sym.imp.exit ;[c]  |     | xor al, byte [rbp - local_dh]     |  
=-------------------------=     | movzx eax, al                     |  
                                | cmp eax, dword [rbp - local_8h]   |  
                                | je 0x400759 ;[g]                  |  
                                =-----------------------------------=  
                                      f t                            
                        .-------------' '-------------.              
                        |                             |              
                        |                             |              
                =-------------------------------=     |              
                |  0x400755                     |     |              
                | add dword [rbp - local_4h], 1 |     |              
                =-------------------------------=     |              
                    v                                 |              
                    '-------.     .-------------------'              
                            |     |                                  

We'll definitely want to avoid prematurely stopping the program so the first kind of checks should not lead us to the exit calls.

Regarding the other checks (leading to the variable incrementation) one can see cases where je is used and cases where jne is used. But how to know which path to take ? the true or the false ?

                |     |                            
            =-----------------------------------=  
            |  0x4007b3                         |  
            | mov rax, qword [rbp - local_30h]  |  
            | add rax, 8                        |  
            | mov rax, qword [rax]              |  
            | add rax, 4                        |  
            | movzx eax, byte [rax]             |  
            | movsx edx, al                     |  
            | movzx eax, byte [rbp - local_dh]  |  
            | xor eax, edx                      |  
            | cmp eax, 0x2f                     |  
      --->> | je 0x4007d7 ;[k]                  |  
            =-----------------------------------=  
                    f t                            
           .--------' '------------------.         
           |                             |         
           |                             |         
   =-------------------------------=     |         
   |  0x4007d3                     |     |         
   | add dword [rbp - local_4h], 1 |     |         
   =-------------------------------=     |         
       v                                 |         
       '--------.     .------------------'         
                |     |                            
                |     |                            
            =-----------------------------------=  
            |  0x4007d7                         |  
            | movzx edx, byte [rbp - local_11h] |  
            | movzx eax, byte [rbp - local_fh]  |  
            | sub edx, eax                      |  
            | movzx eax, byte [rbp - local_dh]  |  
            | sub edx, eax                      |  
            | mov eax, edx                      |  
            | mov edx, eax                      |  
            | shr edx, 0x1f                     |  
            | add eax, edx                      |  
            | sar eax, 1                        |  
            | cmp eax, dword [rbp - local_4h]   |  
      --->> | jne 0x4007fb ;[l]                 |  
            =-----------------------------------=  
                    f t                            
           .--------' '------------------.         
           |                             |         

The answer is simple: each check is a value comparisaon ("equality check"), meaning that one value would lead to a path, and the rest would lead to another path.
As the binary is verifying a password we can safely assume that the correct paths are the ones which need a single solution in order to be taken. Meaning that je should jump a jne shouldn't.

This means that all the following basic blocks need to be avoided:
0x40070c, 0x40071f, 0x40073c, 0x400755, 0x400774, 0x400793, 0x4007af, 0x4007d3, 0x40081b, 0x400853, 0x400878, 0x4008b9, 0x400952

And all the these other blocks need to be taken:
0x4007f7, 0x4008fa

These are all the information we need in order to solve the CrackMe. Yes, that's all we need. There is no need to understand all the obscure calculations done in each block.

Time for some magic !


Here is the script I wrote in order to solve this challenge:

#!/usr/bin/env python2
## -*- coding: utf-8 -*-

from triton  import *  
from ast     import *  
from pintool import *

snapshot_done = False  
argv1 = 0  
symVarConstraints = []  
PASSWORD_SIZE = 11  
def superAnd(constraints):  
    pathConstraints_and = ast.equal(bvtrue(), bvtrue())
    for i in range(len(constraints)):
        pathConstraints_and = ast.land(pathConstraints_and, constraints[i])
    return (pathConstraints_and)

def model2string(model):  
    s = str()
    for i in range(PASSWORD_SIZE):
        try :s += chr(model[i].getValue())
        except: pass
    return s

def inject(address, data):  
    for index, char in enumerate(data):
        setCurrentMemoryValue(address + index, ord(char))

def static_vars(**kwargs):  
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate


ENTRY = 0x4005b6  
# basic blocks to avoid (reflecting password errors)
avoid = [ 0x40070c, 0x40071f, 0x40073c, 0x400755, 0x400774, 0x400793, 0x4007af, 0x4007d3, 0x40081b, 0x400853, 0x400878, 0x4008b9,  0x400952]  
# basic blocks to take (reflecting password match)
take = [0x4007f7, 0x4008fa]

@static_vars(last_injected = "", numMandatoryPaths = 0)
def before_symproc(instruction):  
    pathConstraints = []
    ins_addr = instruction.getAddress()

    if ins_addr in take:
        before_symproc.numMandatoryPaths = before_symproc.numMandatoryPaths + 1

    if (ins_addr in avoid) or\
            ((ins_addr == 0x4007fb) and (before_symproc.numMandatoryPaths != 1)) or\
            ((ins_addr == 0x4008fe) and (before_symproc.numMandatoryPaths !=2) ):
        before_symproc.numMandatoryPaths = 0
        print "[+] Wrong password"
        pco = getPathConstraints()
        for pc in pco:
            if pc.isMultipleBranches():
                for branch in pc.getBranchConstraints():
                    # filter out branch constraints which lead to addresses we want
                    # to avoid
                    if branch['dstAddr'] in avoid:
                        pathConstraints.append(lnot(branch['constraint']))
                    # force taking mandatory branches
                    if branch['dstAddr'] in take:
                        pathConstraints.append((branch['constraint']))

        # Get a model that will not go through previous bad password basic
        # blocks and verifiying that all inputs are printable (see symVarConstraints
        # creation)
        full_constraint = superAnd(symVarConstraints + pathConstraints)
        model = getModel(ast.assert_(full_constraint))
        string = model2string(model)
        before_symproc.last_injected = string
        print "[+] Possible solution : \"%s\" (%s)"\
            % (string, string.encode('hex'))
        string += "\x00"
        print "[+] Injecting it, and restoring snapshot"
        inject(argv1, string)
        clearPathConstraints()
        restoreSnapshot()
    if ins_addr == 0x40095c:
        print "[+] Good password: ", before_symproc.last_injected
        disableSnapshot()
        clearPathConstraints()
        setCurrentRegisterValue(REG.RIP, 0x40096b)


def before(inst):  
    global snapshot_done
    global argv1
    ins_addr = inst.getAddress()

    if inst.getAddress() == ENTRY:
        if not snapshot_done:
            # On 64 bits archs rdi id arc and rsi is argv
            rsi = getCurrentRegisterValue(REG.RSI)
            argv1 = getCurrentMemoryValue(rsi + 8, CPUSIZE.REG)

            offset = 0
            # The binary is expecting a filname size of 11 bytes as the checks are
            # done on arv[1][0] to argv[1][10]
            # So we need to symbolize 9 bytes starting from &agrv[0][0]
            # Then we need to symbolize these 9 bytes
            while (offset < PASSWORD_SIZE):
                setCurrentMemoryValue(argv1 + offset, ord("_"))
                # symbolize current input (argv[1][offset])
                symvar = convertMemoryToSymbolicVariable(MemoryAccess(argv1 + offset, CPUSIZE.BYTE))
                # Inputs must be printable, so we add that constraint to each one
                # of the inputs:
                symVarConstraints.append(ast.bvuge(variable(symvar), bv(0x20,  8)))
                symVarConstraints.append(ast.bvule(variable(symvar), bv(0x7E, 8)))
                # Go get next input
                offset += 1
            # End it with a null char for strlen to work properly
            setCurrentMemoryValue(argv1 + offset, ord('\0'))
            print "[+] Symbolized %d bytes of memory at 0x%x" % (offset, argv1)

            print "[+] Taking snapshot"
            takeSnapshot()
            snapshot_done = True

def constantFolding(node):  
    if node.isSymbolized():
        return node
    return ast.bv(node.evaluate(), node.getBitvectorSize())

if __name__ == '__main__':  
    # Define the architecture
    setArchitecture(ARCH.X86_64)

    enableSymbolicOptimization(OPTIMIZATION.ALIGNED_MEMORY, True)
    enableSymbolicOptimization(OPTIMIZATION.ONLY_ON_SYMBOLIZED, True)
    # Start the symbolic analysis from the 'main' function
    startAnalysisFromAddress(ENTRY)

    # Add callbacks
    addCallback(constantFolding, CALLBACK.SYMBOLIC_SIMPLIFICATION)
    insertCall(before, INSERT_POINT.BEFORE)
    insertCall(before_symproc,   INSERT_POINT.BEFORE_SYMPROC)

    # Run the instrumentation - Never returns
    runProgram()

Basically this script checks if we are executing wrong basic blocks, requests for a model in order to avoid these basic blocks, patches argv[1] and restarts the program until it finds the correct password validating all path constraints.
Let's focus on the two most important callbacks:

before:

This function is only doing something at the entrypoint, it is symbolizing 11 bytes from argv[1]:

while (offset < PASSWORD_SIZE):  
    setCurrentMemoryValue(argv1 + offset, ord("_"))
    # symbolize current input (argv[1][offset])
    symvar = convertMemoryToSymbolicVariable(MemoryAccess(argv1 + offset, CPUSIZE.BYTE))
    # Inputs must be printable, so we add that constraint to each one
    # of the inputs:
    symVarConstraints.append(ast.bvuge(variable(symvar), bv(0x20,  8)))
    symVarConstraints.append(ast.bvule(variable(symvar), bv(0x7E, 8)))
    # Go get next input
    offset += 1

It is also building a list (symVarConstraints) which will hold all symbolic variables constraints.
In our case, the constraints are simple: argv[1] must only contain printable characters (from 0x20 to 0x7E)
This is done with ast.bvuge(variable(symvar), bv(0x20, 8)) and ast.bvule(variable(symvar), bv(0x7E, 8))

ast.bvuge being an unsigned comparison (Greater or Equal) of two bit vectors and ast.bvule being an unsigned comparison (Less than or Equal) of two bit vectors

so these two expressions may be translated to

  • symvar need to be greater or equal to 0x20.
  • symvar need to be less or equal to 0x7E.

before_symproc:

This function is the one doing the magic:

first it is checking whether the program is executing the avoid addresses or if it missed the take addresses:

    if ins_addr in take:
        before_symproc.numMandatoryPaths = before_symproc.numMandatoryPaths + 1

    if (ins_addr in avoid) or\
            ((ins_addr == 0x4007fb) and (before_symproc.numMandatoryPaths != 1)) or\
            ((ins_addr == 0x4008fe) and (before_symproc.numMandatoryPaths !=2) ):

Then, in the case we actually are executing an address we want to avoid, or in the case we missed an address we actually wanted to take, it is building a pathConstraints list where all branches leading to avoid addresses have there path constraint negated (with ast.lnot). And all branches leading to take addresses are stored as is.

        before_symproc.numMandatoryPaths = 0
        print "[+] Wrong password"
        pco = getPathConstraints()
        for pc in pco:
            if pc.isMultipleBranches():
                for branch in pc.getBranchConstraints():
                    # filter out branch constraints which lead to addresses we want
                    # to avoid
                    if branch['dstAddr'] in avoid:
                        pathConstraints.append(lnot(branch['constraint']))
                    # force taking mandatory branches
                    if branch['dstAddr'] in take:
                        pathConstraints.append((branch['constraint']))

Finally, still in the wrong cases, the script builds a full constraint (full_constraint) and-ing (with superAnd) the symvar constraints (printable) and all the previous path constraints.
this full_constraints is then provided to z3 through Triton's getModel API in order to get a possible solution. Then, we just replace argv[1] with the new model, we clear all path constraints and restore the snapshot (restart the program):

        # Get a model that will not go through previous bad password basic
        # blocks and verifiying that all inputs are printable (see symVarConstraints
        # creation)
        full_constraint = superAnd(symVarConstraints + pathConstraints)
        model = getModel(ast.assert_(full_constraint))
        string = model2string(model)
        before_symproc.last_injected = string
        print "[+] Possible solution : \"%s\" (%s)"\
            % (string, string.encode('hex'))
        string += "\x00"
        print "[+] Injecting it, and restoring snapshot"
        inject(argv1, string)
        clearPathConstraints()
        restoreSnapshot()

Here is what we get:

$ triton solve.py ./CrackMe _
[+] Symbolized 11 bytes of memory at 0x7ffe0a329f8b           
[+] Taking snapshot                                           
[+] Wrong password                                            
[+] Possible solution : "@@@ @@@@@ @" (4040402040404040402040)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "B*@!o@@A H;" (422a40216f40404120483b)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "@@'@_H.@r\O" (404027405f482e40725c4f)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "`@@`?1P@p`3" (604040603f315040706033)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "@B!Ag7!9|;:" (40422141673721397c3b3a)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "rxCRB0|/aq/" (7278435242307c2f61712f)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "afNQq$<Dpa"" (61664e5171243c44706122)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "5F C:$BBh; " (354620433a244242683b20)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "On)C"XdBygB" (4f6e294322586442796742)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "\@"[(0|V|5U" (5c40225b28307c567c3555)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "CD"t0 Rod=$" (434422743020526f643d24)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "@r!t0NRock!" (40722174304e526f636b21)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "Tr't0NRoik5" (54722774304e526f696b35)
[+] Injecting it, and restoring snapshot                      
[+] Wrong password                                            
[+] Possible solution : "Tr!t0NRock5" (54722174304e526f636b35)
[+] Injecting it, and restoring snapshot                      
[+] Good password:  Tr!t0NRock5                               

Magic isn't it ?