1
votes

Le moyen le plus rapide d'indexer un gros fichier de hachage trié

Je suis en train de créer un index basé sur un fichier pour le fichier texte trié des mots de passe haveibeenpwned et je me suis demandé ce qui était le moyen le plus rapide de le faire?

J'ai pensé qu'un bon moyen de créer un index rapidement grepable serait de diviser le fichier trié en 256 fichiers nommés avec les deux premiers chiffres hexadécimaux (c'est-à-dire FF.txt, FE.txt, etc.). J'ai trouvé que ripgrep rg était environ 5 fois plus rapide que grep sur mon ordinateur. J'ai donc essayé quelque chose comme ceci:

for i in {255..0} 
do
    start=$(date +%s)
    hex="$(printf  '%02x' $i  | tr [:lower:] [:upper:])"
    rg "^$hex" pwned-passwords-ntlm-ordered-by-hash-v4.txt > ntlm/$hex-ntlm.txt
    echo 0x$hex completed in $(($(date +%s) - $start)) seconds
done

C'est la solution la plus rapide que j'ai pu trouver. ripgrep est capable de créer chaque fichier en 25 secondes. Je regarde donc environ 100 minutes pour créer cet index. Lorsque je divise le travail en deux et que je les exécute en parallèle, chaque paire de fichiers est créée en 80 secondes. Il semble donc préférable de laisser ripgrep opérer sa magie et travailler en série.

Évidemment, je n'indexerai pas cette liste trop souvent, mais c'est juste amusant d'y penser. Avez-vous des idées sur un moyen plus rapide (en plus d'utiliser une base de données) pour indexer ce fichier?


9 commentaires

Si vous voulez de la vitesse, n'utilisez pas de commandes externes ou de fourches. Pas de $ (...) , pas de tubes, pas de tr , pas de rg .


Merci @ kamil-cuk pour l'aide au formatage!


Par exemple: printf -v hex '% 02X' "$ i" est un moyen beaucoup plus rapide de faire hex = "$ (printf '% 02x' $ i | tr [: lower: ] [: supérieur:]) ".


Et printf -v start '% (% s) T' -1 est une alternative beaucoup plus rapide à start = $ (date +% s)


Bien sûr, vous ne souhaitez pas non plus utiliser date +% s plus tard dans votre écho .


... après tous ces changements, votre plus gros coût sera d'exécuter toutes les copies de rg . Ne sachant pas ce que c'est, il est difficile de dire comment l'optimiser. En général, si vous pouvez exécuter un outil une seule fois au lieu d'une seule entrée par boucle, vous devez toujours le faire (un seul appel traitant toutes vos données, vs un seul appel par donnée) .


Le fichier a 11 Go, donc le coût de date +% s est négligeable. L'opération rg "^ $ hex" est la seule qui prend beaucoup de temps. rg fonctionne de la même manière que grep et selon leur readme github s'exécute plus rapidement que grep .


Ahh. Dans ce cas, je commencerais par optimiser le fichier - pré-triez-le, puis vous pouvez utiliser des outils grep basés sur seek () qui font une bissection pour s'exécuter en temps logarithmique plutôt qu'en O ( n). Ou, s'il est déjà trié, arrêtez d'utiliser des outils qui essaient de travailler avec des entrées non triées et passez à des outils optimisés pour les entrées triées uniquement.


@pixelrebel En gros, vous voulez faire une recherche binaire sur un fichier trié. Google court m'a donné sgrep et ce fil de discussion unix.stackexchange . Il n'y a aucun besoin de "hacher" le fichier - il est déjà trié, le diviser en parties plus petites n'est pas vraiment la manière optimale à ce sujet.


3 Réponses :


1
votes

ripgrep , comme tout autre outil capable de travailler avec des fichiers d'entrée non triés, n'est pas le bon outil pour ce travail. Lorsque vous essayez de grep des entrées triées, vous voulez quelque chose qui puisse couper en deux votre fichier d'entrée pour trouver une position en temps logarithmique. Pour des entrées suffisamment importantes, même une implémentation lente O (log n) sera plus rapide qu'une implémentation O (n) hautement optimisée.

pts-line-bisect en est un outil, mais bien sûr, vous êtes également invité à écrire le vôtre. Vous devrez l'écrire dans une langue avec un accès complet au syscall seek () , qui n'est pas exposé dans bash.


1 commentaires

pts_lbsearch est exactement ce que je recherche pour résoudre le problème plus large. Aucun index n'est nécessaire du tout! pts_lbsearch -p hashes.sorted $ myhash trouve le résultat (et plus important encore, le résultat négatif) instantanément! Cette réponse m'a envoyé dans la recherche binaire lapin-trou pour en savoir plus sur son fonctionnement sur cette application. Je me souviens avoir appris la recherche binaire à l'université, mais je ne l'ai pratiquement jamais appliquée à un fichier texte trié. Incroyable. Merci!



0
votes

Vous lisez le fichier 256 fois, en effectuant une analyse complète du fichier à chaque fois. Envisagez une approche qui lit le fichier une fois, en écrivant chaque ligne dans un descripteur de fichier ouvert. Je pense que python serait un choix facile d'implémentation (si c'est votre truc). Vous pouvez optimiser en gardant le fichier ouvert jusqu'à ce que vous frappiez un nouveau code hexadécimal au début de la ligne. Si vous voulez être encore plus intelligent, il n'est pas nécessaire de parcourir le fichier trié ligne par ligne. Sur la base de l'indice de Charles Duffy, vous pouvez créer une heuristique pour échantillonner le fichier (en utilisant seek () ) pour obtenir la valeur hexadécimale suivante. Une fois que le programme a trouvé le décalage d'octet de la valeur hexadécimale suivante, le bloc d'octets peut être écrit dans le nouveau fichier. Cependant, comme il est étiqueté 'bash', gardons la solution définie dans ce domaine:

while 
  read line 
do
  hex=${line:0:2}
  echo $line >> ntlm/$hex-ntlm.txt
done < pwned-passwords-ntlm-ordered-by-hash-v4.txt


2 commentaires

Merci d'avoir répondu. C'est l'état d'esprit dans lequel je devais entrer pour résoudre ce problème. Malheureusement, la solution ci-dessus est en fait plus lente que le ripgrep de force brute car elle est monothread. Cependant, il s'avère qu'une recherche binaire est l'application parfaite pour ce problème, alors j'ai donné le chèque à M. Duffy. Merci encore pour la leçon!


C'est tout à fait correct - le point de vue important était la recherche binaire. De plus, effectuer l'opération en parallèle est essentiel car des E / S de fichier sont impliquées.



0
votes

J'ai écrit un script Python3 qui résout les recherches rapides de recherche binaire dans le fichier de hachage sans avoir à créer un index. Il ne répond pas directement à votre question (indexation) mais résout probablement le problème sous-jacent que vous vouliez résoudre avec un index - rechercher rapidement des hachages individuels. Ce script vérifie des centaines de mots de passe en quelques secondes.

import argparse
import hashlib

parser = argparse.ArgumentParser(description='Searches passwords in https://haveibeenpwned.com/Passwords database.')
parser.add_argument('passwords', metavar='TEST', type=str, help='text file with passwords to test, one per line, utf-8')
parser.add_argument('database', metavar='DATABASE', type=str, help='the downloaded text file with sha-1:count')
args = parser.parse_args()

def search(f: object, pattern: str) -> str:

    def search(left, right: int) -> str:
        if left >= right:
            return None

        middle = (left + right) // 2
        if middle == 0:
            f.seek(0, 0)
            test = f.readline()
        else:
            f.seek(middle - 1, 0)
            _ = f.readline()
            test = f.readline()

        if test.upper().startswith(pattern):
            return test
        elif left == middle:
             return None
        elif pattern < test:
            return search(left, middle)
        else:
            return search(middle, right)

    f.seek(0, 2)
    return search(0, f.tell())

fsource = open(args.passwords)
fdatabase = open(args.database)
source_lines = fsource.readlines()
for l in source_lines:
    line = l.strip()
    hash_object = hashlib.sha1(line.encode("utf-8"))
    pattern = hash_object.hexdigest().upper()
    print("%s:%s" % (line, str(search(fdatabase, pattern)).strip()))
fsource.close()
fdatabase.close()


0 commentaires