Brute force passwords in C (CS50 exercise) This script attempts to crack passwords by going through all possible 'words', hashing them, and comparing the hash to the input.

It seems to work, but I don't know if I have written 'good' C. I'm looking for advice on what areas would be most important/beneficial to focus on (e.g. efficiency, design, readability, something else?) in the next thing I write and how to improve within those. I also don't feel that comfortable with low level concepts like memory and pointers and I'm not sure if I'm using them right.

I'm not really sure what I want though so please give me whatever advice you think I need.

#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;cs50.h&gt;
#include &lt;crypt.h&gt;

char ALPHABET[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int alphabetSize = 52;

int max_length = 4;

void check(char *guess, char *salt, char *hash)
{
    string hashedGuess = crypt(guess, salt);
    if (!strcmp(hash, hashedGuess))
    {
        printf("%s\n", guess);
        free(guess);
        exit(0);
    }
}

// recursively fill the buffer and check it each time
void brute_force(char *buf, int index, int length, string salt, string hash)
{
    for (int i = 0; i &lt; alphabetSize; i++)
    {
        sprintf(buf + index, "%c", ALPHABET[i]);

        if (index &lt; length)
        {
            brute_force(buf, index + 1, length, salt, hash);
        }
        else
        {
            check(buf, salt, hash);
        }
    }
}

char *crack(string hash, string salt)
{
    // create a buffer big enough to hold the longest possible password
    char *buf = (char *) malloc(max_length + 1);

    // increment guess length starting from 1
    for (int length = 0; length &lt; max_length; length++)
    {
        brute_force(buf, 0, length, salt, hash);
    }
    return "";
}

bool valid_args(int argc, string argv[])
{
    if (argc == 2)
    {
        return true;
    }
    return false;
}

int main(int argc, string argv[])
{
    if (valid_args(argc, argv))
    {
        string hash = argv[1];

        char salt[3];
        strncpy(salt, hash, 2); // salt is first 2 characters of hash

        string password = crack(hash, salt);
    }
    else
    {
        printf("Invalid arguments\n");
        return 1;
    }
    return 0;
}
valid_args is a well-known anti-idiom:

    if (condition) {
        return true;
    }
    return false;


is a long way to say

    return condition;

crack always return an empty string "". It is as good as returning nothing.
check terminates the program on success. This seriously limits its utility: the business logic doesn't belong to such depth of the call tree. Consider returning a boolean instead.
I do not endorse recursion when an iterative approach suffices. Try to express a power set generation iteratively (hint: think of base-52 numbers).
From what we can see, it is impossible to tell whether crypt returns a static buffer, or allocates a new one for each call. Consider documenting that.
I happen to know that &lt;cs50.h&gt; defines string as an alias to char *. I don't think it is a good idea to begin with; in any case you should be consistent: brute_force passes salt and hash as string, while check receives them as char *.