DEV Community

Ayoub Ali
Ayoub Ali

Posted on

RAR Cracker - Breaking Password-Protected RAR Archives

Introduction

The RAR Cracker is a command-line tool written in Go that helps in cracking password-protected RAR archives. It provides two working modes: dictionary mode and brute force mode. The dictionary mode tries to crack the archive by using a dictionary file containing a list of possible passwords. The brute force mode generates all possible combinations of passwords based on user-defined criteria. This article provides an in-depth explanation of the code and its functionality.

Code Explanation

The code is divided into several functions and a main function. Let's dive into each part of the code to understand its purpose and functionality.

  1. crackRar function:

    • This function takes a RAR filename and a password generator channel as input.
    • It attempts to extract the RAR archive by executing the unrar command with a provided password.
    • If the extraction is successful (no error), it prints the extracted file message along with the password and returns.
    • If the password generator channel is exhausted (no more passwords to try), it prints a "Sorry" message indicating that the password couldn't be found.
  2. fromDictionary function:

    • This function takes a dictionary filename as input and returns a password generator channel.
    • It opens the dictionary file and reads each line, sending the passwords through the password generator channel.
    • After reading all lines, it closes the channel and returns.
  3. main function:

    • It serves as the entry point of the program.
    • The program starts by printing a welcome message and prompting the user for a RAR filename.
    • It then validates the existence of the provided filename using the fileExists function.
    • Next, the program asks the user to select a working mode: dictionary or brute force.
    • Depending on the selected mode, it prompts for additional inputs such as the dictionary filename or the criteria for the brute force mode (include letters, symbols, numbers, spaces, minimum and maximum length).
    • Finally, it initiates the cracking process by calling the crackRar function with the provided filename and password generator.
  4. fileExists function:

    • It takes a filename as input and checks if the file exists by using the os.Stat function.
    • It returns a boolean value indicating whether the file exists or not.
  5. brute function:

    • This function takes the starting length, maximum length, and various boolean flags as input.
    • It generates a password generator channel and returns it.
    • The function constructs a character set based on the provided criteria (letters, numbers, symbols, spaces).
    • It calculates the maximum number of combinations based on the length and the base of the character set.
    • It iterates over all possible combinations by generating passwords and sending them through the password generator channel.
    • Once all combinations are generated, it closes the channel and returns.
  6. pow function:

    • This function takes two integers, a and b, and calculates the exponentiation of a^b iteratively.
    • It returns the result of the exponentiation.

Code

package main

import (
    "bufio"
    "fmt"
    "os"
    "os/exec"
    "strings"
)

func crackRar(rarFilename string, pwdGenerator <-chan string) {
    for pwd := range pwdGenerator {
        cmd := exec.Command("unrar", "x", "-p"+pwd, rarFilename)
        err := cmd.Run()
        if err == nil {
            fmt.Println("File extracted")
            fmt.Printf("The password is %s\n", pwd)
            return
        }
    }

    fmt.Println("Sorry, cannot find the password")
}

func fromDictionary(dicName string) <-chan string {
    pwdGenerator := make(chan string)

    go func() {
        file, err := os.Open(dicName)
        if err != nil {
            fmt.Println("Error opening dictionary file:", err)
            close(pwdGenerator)
            return
        }
        defer file.Close()

        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
            pwdGenerator <- scanner.Text()
        }

        close(pwdGenerator)
    }()

    return pwdGenerator
}

func main() {
    fmt.Println("----------------------------------------------------")
    fmt.Println("    RAR Cracker")
    fmt.Println("    by @AYOUB")
    fmt.Println("----------------------------------------------------")

    reader := bufio.NewReader(os.Stdin)

    fmt.Print("Please enter the filename: ")
    filename, _ := reader.ReadString('\n')
    filename = strings.TrimSpace(filename)

    for !fileExists(filename) {
        fmt.Print("No such file, please enter a valid filename: ")
        filename, _ = reader.ReadString('\n')
        filename = strings.TrimSpace(filename)
    }

    var mode string
    for mode != "dictionary" && mode != "brute" {
        fmt.Print("Please select a working mode [dictionary/brute]: ")
        mode, _ = reader.ReadString('\n')
        mode = strings.TrimSpace(mode)
    }

    var pwdGen <-chan string
    if mode == "dictionary" {
        fmt.Print("Please enter the filename of the dictionary: ")
        dicName, _ := reader.ReadString('\n')
        dicName = strings.TrimSpace(dicName)

        for !fileExists(dicName) {
            fmt.Print("No such file, please enter a valid filename: ")
            dicName, _ = reader.ReadString('\n')
            dicName = strings.TrimSpace(dicName)
        }

        pwdGen = fromDictionary(dicName)
    }

    if mode == "brute" {
        var letters, symbols, numbers, spaces bool

        fmt.Print("Include letters? [yes/no] (default yes) ")
        includeLetters, _ := reader.ReadString('\n')
        letters = strings.TrimSpace(includeLetters) != "no"

        fmt.Print("Include symbols? [yes/no] (default yes) ")
        includeSymbols, _ := reader.ReadString('\n')
        symbols = strings.TrimSpace(includeSymbols) != "no"

        fmt.Print("Include numbers? [yes/no] (default yes) ")
        includeNumbers, _ := reader.ReadString('\n')
        numbers = strings.TrimSpace(includeNumbers) != "no"

        fmt.Print("Include spaces? [yes/no] (default no) ")
        includeSpaces, _ := reader.ReadString('\n')
        spaces = strings.TrimSpace(includeSpaces) == "yes"

        fmt.Print("Min length: ")
        var startLength int
        fmt.Scanf("%d\n", &startLength)

        fmt.Print("Max length: ")
        var length int
        fmt.Scanf("%d\n", &length)

        pwdGen = brute(startLength, length, letters, numbers, symbols, spaces)
    }

    fmt.Println("Start cracking")
    fmt.Println("This may take some time, please wait...")
    crackRar(filename, pwdGen)
}

func fileExists(filename string) bool {
    _, err := os.Stat(filename)
    return err == nil
}

func brute(startLength, length int, letters, numbers, symbols, spaces bool) <-chan string {
    pwdGenerator := make(chan string)

    go func() {
        charset := ""
        if letters {
            charset += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
        }
        if numbers {
            charset += "0123456789"
        }
        if symbols {
            charset += "!@#$%^&*()-_=+[]{}|;:',.<>/?`~"
        }
        if spaces {
            charset += " "
        }

        base := len(charset)
        maxCombinations := pow(base, length)

        for i := 0; i < maxCombinations; i++ {
            password := ""
            n := i
            for j := 0; j < length; j++ {
                password = string(charset[n%base]) + password
                n /= base
            }
            pwdGenerator <- password

            if len(password) < length {
                break
            }
        }

        close(pwdGenerator)
    }()

    return pwdGenerator
}

func pow(a, b int) int {
    result := 1
    for i := 0; i < b; i++ {
        result *= a
    }
    return result
}
Enter fullscreen mode Exit fullscreen mode

This Code has several limitations:

  1. Limited Password Cracking Techniques: The code only provides two password cracking techniques: dictionary mode and brute force mode. It does not include more advanced techniques such as hash cracking algorithms (e.g., MD5, SHA1) or rainbow table lookups. These techniques are often more efficient and effective in cracking passwords.

  2. Dependency on External Tool: The code relies on the unrar command-line tool to extract RAR archives. This dependency assumes that the tool is installed and accessible on the system where the code is executed. If the unrar tool is not available, the code will not function correctly.

  3. No Parallelization: The code performs the password cracking process sequentially, trying one password at a time. This approach can be slow, especially for longer password lengths or large dictionaries. Introducing parallelization techniques (e.g., using Goroutines in Go) could significantly improve the speed of the cracking process.

  4. Limited Error Handling: The code lacks comprehensive error handling. For example, if an error occurs during the extraction process or while reading the dictionary file, the code simply prints an error message and continues execution. It would be more robust to handle such errors gracefully, providing better feedback to the user and handling potential issues with file I/O or command execution.

  5. Lack of User Feedback during Brute Force Mode: In the brute force mode, where passwords are generated programmatically, there is no feedback to the user regarding the progress or estimated time remaining. Adding some form of progress indication or status updates would enhance the user experience and give a better sense of the cracking process.

  6. Limited Password Complexity Configuration: While the code allows users to specify the inclusion of letters, numbers, symbols, and spaces, it does not provide more fine-grained control over password complexity. For instance, it does not allow users to define specific character sets or enforce password policies (e.g., minimum requirements for uppercase letters, lowercase letters, special characters).

  7. No Support for Modern RAR Encryption: The code assumes that the RAR archives are encrypted using traditional password-based encryption. It does not support cracking RAR archives that use more advanced encryption methods, such as strong symmetric encryption or public-key cryptography.

  8. Security and Legality Concerns: It's important to note that using this code to crack passwords without proper authorization is illegal and unethical. It's crucial to respect privacy and adhere to applicable laws and regulations. This code should be used for educational purposes or with the appropriate legal authorization.

These limitations highlight areas where the code could be enhanced to improve functionality, performance, and security.

Conclusion:

The provided code demonstrates a basic RAR cracker implemented in Go. It offers two modes: dictionary and brute force. The dictionary mode uses a file containing a list of potential passwords, while the brute force mode generates all possible combinations based on user-defined criteria. The code showcases the use of channels for password generation, command execution using os/exec, and file operations with the os package. By understanding this code, you can gain insights into how password cracking mechanisms work and how to implement them in Go.

GitHub

Top comments (0)