Python for Reverse Engineering

Nick Craig-Wood nick at craig-wood.com
Thu Nov 11 07:31:16 EST 2004


James S <james-newsgroups at echoblast.com> wrote:
>  I got a few questions about the strings, so I figured I would share the 
>  answers.
> 
>  You wrote the algorithm that generated these strings?
> 
>  Yes
> 
>  How are the strings verified and proven to be legitimate?
> 
>  There is a checksum encoded in the string that provides a basic check. 
>  There is a unquice number encoded in each string that represents each 
>  possible key.
> 
>  There are 28629150 valid keys in the space of 28629150^5

ie about 24 bits of serial number in about 123 bits of key.  If done
properly 123 bits of keys will ensure it isn't broken.

>  Does the same algorithm that created them verify them?
> 
>  The same string is feed back through the algo in reverse. Providing the 
>  origional information that generated it.

Here is my solution to the problem ;-) If your solution is anything
like this then it will never be broken!  However if you keep the keys
to the algorithm in code you give to the user then it can be reverse
engineered of course.

$ python serial_key.py 314159265 141421356               

Original serial 314159265
User Key is  CRYX7T2FedtoJteoeeJx4MKiNvAgYSmKOB
Decoded serial number is 314159265
This is a valid key True

Original serial 141421356
User Key is  CL33jINQiZo8xMqI0gnh3CCtutc1cXdfxH
Decoded serial number is 141421356
This is a valid key True


"""
Make a user key using md5

The secret keys must be kept secret, apart from that no harm in
publishing the algorithm

The user key is serial + md5(serial)

The serial numbers are fixed length strings and are encoded
unencrypted (but obfuscated) into the user key.

This is encoded into base 62 using str_to_long from ASCII to long then
long_to_str to get the long into base 62

This method is crypto secure IMHO (perhaps you'd better use SHA256 if
you are feeling really paranoid though!)

"""

import md5

SERIAL_LEN = 9
ASCII   = "".join([chr(i) for i in range(256)])
KEYBASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
KEY1    = "secret key"
KEY2    = "another secret key"

def str_to_long(s, chars):
    "Return a long representation of a string"
    base = len(chars)
    n = 0L
    for c in s:
        n *= base
        n += chars.index(c)
    return n

def long_to_str(n, chars):
    "Return a string representation of a long"
    base = len(chars)
    s = []
    while n:
        n, c = divmod(n, base)
        s.append(chars[c])
    s.reverse()
    return "".join(s)

def make_digest(serial):
    "Makes a digest from a serial number and secret keys"
    return md5.md5(KEY1 + serial + KEY2).digest()

def encode_key(serial, digest):
    "Encode the serial + digest in human readable form - returns a string"
    assert len(serial) == SERIAL_LEN
    return long_to_str(str_to_long(serial + digest, ASCII), KEYBASE)

def decode_key(key):
    "Returns a serial and digest decoded from a key"
    decoded = long_to_str(str_to_long(key, KEYBASE), ASCII)
    return decoded[:SERIAL_LEN], decoded[SERIAL_LEN:]

def check_digest(serial, digest):
    "Returns bool whether serial matches digest"
    return make_digest(serial) == digest

if __name__ == "__main__":
    import sys
    if len(sys.argv) <= 1:
        print "Put some serials on the command line"
    for serial in sys.argv[1:]:
        serial = serial.ljust(SERIAL_LEN)[:SERIAL_LEN]
        print
        print "Original serial", serial
        digest = make_digest(serial)
        user_key = encode_key(serial, digest)
        print "User Key is ", user_key

        # Now decode user_key to show how its done
        decoded_serial, decoded_digest = decode_key(user_key)
        print "Decoded serial number is", decoded_serial
        print "This is a valid key", check_digest(decoded_serial, decoded_digest)



-- 
Nick Craig-Wood <nick at craig-wood.com> -- http://www.craig-wood.com/nick



More information about the Python-list mailing list