0

I am doing a task in class about a password guesser. I stumbled into a lot of problems trying to solve this task, my first approach was to use for loops (code below), but I realized that the amount of 'for loops' is equal to the length of the string.

a_z = 'abcdefghijklmnopqrstuvwxyz'
pasw = 'dog'
tests = 0
guess = ''
azlen = len(a_z)

for i in range(azlen):
    for j in range(azlen):
        for k in range(azlen):
            guess = a_z[i] + a_z[j] + a_z[k]
            tests += 1
            if guess == pasw:
                print('Got "{}" after {} tests'.format(guess, str(tests)))
                break

input()

The program above is very concrete. It only works if there are exactly 3 characters entered. I read that you could use a package called intertools, however, I really want to find another way of doing this. I thought about using recursion but don't even know where to start.

Arount
  • 9,853
  • 1
  • 30
  • 43
Crude
  • 13
  • 1
  • 1
  • 7
  • Why use indices at all? You can use things like `for c in a_z:` directly rather than the cumbersome two step process `for i in len(a_z):` followed by `a_z[i]` in the body of the loop. For your more general question, recursion is natural, but you could also use simple iteration. If you have a list of all passwords of a given length, can you modify it to get all passwords of that length + 1? This will run into memory problems sooner or later, but your code isn't practical to begin with. – John Coleman May 07 '18 at 14:00
  • Thanks for the advice, really appreciate it, @John Coleman. Yeah, I am still new to python. I know about the memory problems. So do you know of a way to implement recursion? – Crude May 07 '18 at 14:09
  • For recursion: write a function something like `passwords(n)` which gives a list or preferably a generator of all passwords of length `n`. What is the basis case? In the non-basis case, how would you use `passwords(n-1)` to generate the passwords of length `n`? – John Coleman May 07 '18 at 14:12
  • A radically different but surprisingly easy approach: just count from `0` to `26^N-1` in base 26 -- using `a,b,...,z` as the "digits". You learned to count in elementary school -- just extend that knowledge to a different digit set. – John Coleman May 07 '18 at 14:18
  • Okay, I kind of understand. I am struggling with the generation part. I was thinking about using an array `guess = ['a', 'a', 'a']` with the amount of elements equal to the length of the input. Then I thought to change `guess[-1]` to the next letter thus cycling through aab, aac, etc. but to change `guess[-2]` and then go back to `guess[-1]` to go from aaz to aba is where I get confused. – Crude May 07 '18 at 14:20
  • One way: in a nested loop, loop through the output of `passwords(n-1)` and loop through the individual characters in `a_z`, concatenating them and either appending them to a big return list or (better) yielding them one by one (if you have seen `yield` in your studies). Ditch your reliance on string/list indices, they aren't needed for this sort of thing. – John Coleman May 07 '18 at 14:31
  • Okay, thank you for your help. I will look more into it. – Crude May 07 '18 at 14:35
  • Hi, @JohnColeman. Your idea worked perfectly when I wrote it in Pascal. When I wrote it in python, however, it didn't work properly but thanks anyway! – Crude May 09 '18 at 16:37

4 Answers4

2
import string
import itertools

for possible_password in itertools.permutations(string.ascii_letters, 3): 
    print(possible_password)
Sam
  • 228
  • 1
  • 9
  • Thanks for the help man. But is there any other way, like not using intertools or something? – Crude May 07 '18 at 13:58
  • 1
    Why don't you want to use `itertools`? It's built-in – Arount May 07 '18 at 13:59
  • I know that its much easier and faster, our teacher asked us to come up with an alternative way of doing it and this is the closest I got so far. – Crude May 07 '18 at 14:05
  • You can take a look directly at the python documentation for [itertools.permutations](https://docs.python.org/3/library/itertools.html#itertools.permutations). It contains an example of a function which produces similar results. I wouldn't use it in any homework without citing it though. – Sam May 07 '18 at 14:42
  • 1
    Yea, I hope your teacher tell you it's only for learning purpose, because first rule of the developer is: Use libraries, especially built-in ones – Arount May 07 '18 at 14:44
  • Yeah haha, It was only to make us think out of the box. I will use libraries in the future though. – Crude May 09 '18 at 16:35
  • 1
    `itertools.permutations` doesn't generate combinations where one letter occurs multiple times (not unusual for passwords). Use `itertools.product(ascii_letters, repeat=3)` instead. – Devon Oct 01 '22 at 13:48
0

If you don't want to use itertools you can certainly do this with recursion, which will work with passwords of any (reasonable) length—it's not wired to three characters. Basically, each recursive call will attempt to append a new character from your alphabet to your running value of guess. The base case is when the guess attains the same length as value you're seeking, in which case you check for a match. If a match is found, return an indication that you have succeeded (I used return True) so you can short circuit any further searching. Otherwise, return a failure indication (return False). The use of a global counter makes it a bit uglier, but produces the same results you reported.

ALPHABET = 'abcdefghijklmnopqrstuvwxyz'

def brute_force_guesser(passwd, guess = ''):
    global _bfg_counter
    if len(guess) == 0:
        _bfg_counter = 0
    if len(guess) == len(passwd):
        _bfg_counter += 1
        if guess == passwd:
            print('Got "{}" after {} tests'.format(guess, str(_bfg_counter)))
            return True
        return False
    else:
        for c in ALPHABET:
            if brute_force_guesser(passwd, guess + c):
                return True
        return False

brute_force_guesser('dog')    # => Got "dog" after 2399 tests
brute_force_guesser('doggy')  # => Got "doggy" after 1621229 tests

One way to avoid the global counter is by using multiple return values:

ALPHABET = 'abcdefghijklmnopqrstuvwxyz'

def brute_force_guesser(target, guess = '', counter = 0):
    if len(guess) == len(target):
        counter += 1
        if guess == target:
            print('Got "{}" after {} tests'.format(guess, str(counter)))
            return True, counter
        return False, counter
    else:
        for c in ALPHABET:
            target_found, counter = brute_force_guesser(target, guess + c, counter)
            if target_found:
                return True, counter
        return False, counter

brute_force_guesser('dog')    # => Got "dog" after 2399 tests
brute_force_guesser('doggy')  # => Got "doggy" after 1621229 tests
pjs
  • 18,696
  • 4
  • 27
  • 56
  • That Is a really good way of thinking about it @pjs. I apologize for the late reply, however, I did find a solution but there is a simple error that I can not find. – Crude May 09 '18 at 16:32
0

Here is my full answer, sorry if it's not neat, I'm still new to coding in general. The credit goes to @JohnColeman for the great idea of using bases.

import math
global guess

pasw = str(input('Input password: '))
chars = 'abcdefghijklmnopqrstuvwxyz' #only limeted myself to lowercase for simplllicity.
base = len(chars)+1

def cracker(pasw):
    guess = ''
    tests = 1
    c = 0
    m = 0

    while True:
        y = tests
        while True:
            c = y % base
            m = math.floor((y - c) / base)
            y = m
            guess = chars[(c - 1)] + guess
            print(guess)
            if m == 0:
                break

        if guess == pasw:
            print('Got "{}" after {} tests'.format(guess, str(tests)))
            break
        else:
            tests += 1
            guess = ''


cracker(pasw)
input()
Crude
  • 13
  • 1
  • 1
  • 7
0
import itertools
import string

def guess_password(real):
    chars = string.ascii_lowercase + string.digits
    attempts = 0
    for password_length in range(1, 20):
        for guess in itertools.product(chars, repeat=password_length):
            attempts += 1
            guess = ''.join(guess)
            if guess == real:
                return 'the password is {}, found in {} guesses.'.format(guess, attempts)
            print(guess, attempts)

print(guess_password('abc'))
Doj
  • 1,244
  • 6
  • 13
  • 19
  • 2
    Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Doj Jan 01 '21 at 20:54