The idea is to make a function that maps hash values back to password-like strings. With that function chains like this are built:
I=initial password guess
P=new password-like string
V=hash value
H()=hash function
U()=unhash function (converts V to a new P)
I -> V0=H(I0) -> P0=U(V0) -> V1=H(P0) -> P1=U(V1) -> ... -> Vfinal
Make many chains with different initial guesses, and store the first and last values [I, Vfinal] in a database. When you find a password hash, you repeat the H(U(H(U(...)))) process until you it matches one of your saved Vfinal values. This tells us the hash value path from the password hash we're trying to break and hash from the known initial guess merged, just like in this card trick.Re-creating the chain from the chain of hashes from our known initial value, we discover a string that might not be the actual password, but is a string that hashes to the same value. With modern cryptographic hashes, collisions are unlikely, but rainbow tables were invented when passwords used very poor hashes.