Добавлен , опубликован
Некоторое время назад задавали вопрос и я рекомендовал использовать функцию StringHash для сокрытия строковых констант(ника или пароля). И я решил проверить возможно ли подобрать строку, хеш которой равен наперед заданному числу.
Думал с чего бы начать и решил неплохо бы узнать алгоритм функции. Нашел я его быстро в Strom.dll по адресу 0x15034874, и перевёл на Python.
def StringHash(s):
    if s == "":
        return 0
    s = [i for i in s.encode("utf-8")]
    for i in range(len(s)):
        if 97 <= s[i] <= 122:
            s[i] -= 32
        elif s[i] == 47:
            s[i] = 92

    l = len(s)
    a = 0
    b = c = 0x9e3779b9
    p = [0, 8, 16, 24, 0, 8, 16, 24, 8, 16, 24]
    r = [-13, 8, -13, -12, 16, -5, -3, 10, -15]

    while len(s) >= 12:
        a += int.from_bytes(s[8:12], "little")
        b += int.from_bytes(s[4:8], "little")
        c += int.from_bytes(s[:4], "little")
        for i in r:
            a, b, c = (c - b - a) ^ (a << i if i > 0 else (a & 0xFFFFFFFF) >> -i), a, b & 0xFFFFFFFF
        s = s[12:]

    d = [c, b, a + l]
    for i in range(len(s)):
        d[i // 4] += s[i] << p[i]
    c, b, a = d
    for i in r:
        a, b, c = (c - b - a) ^ (a << i if i > 0 else (a & 0xFFFFFFFF) >> -i), a, b & 0xFFFFFFFF

    return (lambda e: e - (1 << 32) * (e >> 31))(a & 0xFFFFFFFF)
Как мы видим строка делится на части по 12 байт, а те в свою очередь на 3 части по 4. То есть чтобы восстановить строку нам нужно произвести вычисления в обратном порядке. Значение переменной a у на есть, осталось узнать значения b и c. Для это воспользуемся методом перебора, который я реализовал в функции.
def hash_breaker(h):
    for b in range(100):
        for c in range(100):
            a = h
            for i in [-15, 10, -3, -5, 16, -12, -13, 8, -13]:
                a, b, c = b, c, ((a ^ (b << i if i > 0 else (b & 0xFFFFFFFF) >> -i)) + b + c) & 0xFFFFFFFF

            l = a & 0xFF
            if l != 11:
                continue
            a1 = ((a - l) >> 8 & 0xFFFFFFFF).to_bytes(3, "little")
            b1 = ((b - 0x9e3779b9) & 0xFFFFFFFF).to_bytes(4, "little")
            c1 = ((c - 0x9e3779b9) & 0xFFFFFFFF).to_bytes(4, "little")
            s = c1 + b1 + a1

            ex = False
            for i in s:
                if 97 <= i <= 122 or i == 47 or i == 0:
                    ex = True
                    break
            if ex:
                continue

            return s  
По сути подойдёт даже первая попавшая строка, главное, чтобы не было маленьких английских буквы (символы с кодами от 97 до 122 включительно), слэша(/) и нуля(\0), т.к. все строки null-terminated. В 12 байте хранится длина строки, во избежания лишних проверок я сделал её 11. В итоге эта функции возвращает искомую нами строку.
`
ОЖИДАНИЕ РЕКЛАМЫ...