- EDXis then added to itself, twice. This is equivalent of edx = edx × 4 ,
which means that so far you’ve essentially calculated edx = edx ×4,092. - Finally, EDXis shifted back right by 10 bits, which is the equivalent of
dividing by 1,024. The final formula is edx = edx × 4092 ÷ 1024.
You might be wondering why Cryptex didn’t just use the MULinstruction to
multiply EDXby 4,092 and then apply the DIVinstruction to divide the result
by 1,024. The answer is that such code would run far more slowly than the one
we’ve just analyzed. MUL and DIV are both relatively slow instructions,
whereas ADD, SUB, and the shifting instructions are much faster. It is important
to note that this sequence reveals an interesting fact about Cryptex: It was most
likely compiled using some kind of an optimize-for-fast-code switch, rather
than with an optimize-for-small-code switch. That’s because using the direct
arithmetic instructions for division and multiplication would have produced
smaller, yet slower, code. The compiler was clearly aiming at the generation of
high-performance code, even at the cost of a somewhat larger executable.
The result of this little arithmetic sequence goes right into the printfcall
that prints the current file entry. This is quite illuminating because it tells you
exactly what Cryptex was trying to calculate in the preceding arithmetic
sequence: the file size. In fact, it is quite obvious that because the file size is
printed in kilobytes, the final division by 1,024 simply converts the file size
from bytes to kilobytes. The question now is, what was that original number
and why was Cryptex multiplying it by 4,092? Well, it would seem that the file
size is maintained by using some kind of fixed block size, which is probably
somehow related to the cluster you saw earlier while decrypting the buffer.
The problem is that the cluster you were dealing with earlier was 4,104 bytes
long, and here you’re dealing with clusters that are only 4,092 bytes long. The
difference is not clear at this point.
The original number of clusters that you multiplied was taken from offset +8
in the current file entry structure, so you know that offset +8 contains the file size
in clusters. This raises the question of where does Cryptex store the actual file
size? It would not be possible to accurately recover encrypted files without cre-
ating them with the exact size they had originally. Therefore Cryptex must also
maintain the accurate file size somewhere in the archive file.
Other than the file size, the printfcall also takes the file name, which is
easily obtained by taking the address of offset +14 from ESI. Keep in mind
that ESIwas incremented by 8 earlier, so this is actually offset +1C from the
original data structure, which matches what you saw in our data dump, where
the string started at offset +1C.
After printing the file name and size, the program loops back to print the
next entry. To reach the next item, Cryptex increments ESIby 0x98bytes (152
in decimal), which is clearly the length of each entry. This indicates that there
is a fixed number of characters reserved for each file name. Since you know
Deciphering File Formats 225