From 386da45ce2a8ee61323c297a88bf81324278c2d8 Mon Sep 17 00:00:00 2001
From: Syping <schiedelrafael@keppe.org>
Date: Sat, 24 Oct 2020 23:51:02 +0200
Subject: [PATCH] initial commit

---
 CMakeLists.txt      |  38 ++++++
 src/brutethread.cpp |  39 ++++++
 src/brutethread.h   |  49 ++++++++
 src/checksum.cpp    |  40 +++++++
 src/checksum.h      |  34 ++++++
 src/checksum_qt.cpp |  37 ++++++
 src/checksum_qt.h   |  39 ++++++
 src/main.cpp        | 118 ++++++++++++++++++
 src/mainthread.cpp  | 285 ++++++++++++++++++++++++++++++++++++++++++++
 src/mainthread.h    |  62 ++++++++++
 10 files changed, 741 insertions(+)
 create mode 100644 CMakeLists.txt
 create mode 100644 src/brutethread.cpp
 create mode 100644 src/brutethread.h
 create mode 100644 src/checksum.cpp
 create mode 100644 src/checksum.h
 create mode 100644 src/checksum_qt.cpp
 create mode 100644 src/checksum_qt.h
 create mode 100644 src/main.cpp
 create mode 100644 src/mainthread.cpp
 create mode 100644 src/mainthread.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..08ce949
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,38 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(checkbrute LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt5 COMPONENTS Core REQUIRED)
+
+set(CHECKBRUTE_SOURCES
+    src/main.cpp
+    src/mainthread.cpp
+    src/brutethread.cpp
+    src/checksum.cpp
+    src/checksum_qt.cpp
+)
+
+set(CHECKBRUTE_HEADERS
+    src/mainthread.h
+    src/brutethread.h
+    src/checksum.h
+    src/checksum_qt.h
+)
+
+add_executable(checkbrute
+    ${CHECKBRUTE_HEADERS}
+    ${CHECKBRUTE_SOURCES}
+)
+
+target_link_libraries(checkbrute PRIVATE Qt5::Core)
+
+install(TARGETS checkbrute DESTINATION bin)
diff --git a/src/brutethread.cpp b/src/brutethread.cpp
new file mode 100644
index 0000000..56b5fb0
--- /dev/null
+++ b/src/brutethread.cpp
@@ -0,0 +1,39 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+#include <QTextStream>
+#include "brutethread.h"
+
+brutethread::brutethread(const QByteArray &fileContent, quint64 length, QVector<checksum*> checksum_vector, QVector<QByteArray> checksums) : checksum_vector(checksum_vector), checksums(checksums), fileContent(fileContent), length(length) {}
+
+void brutethread::run()
+{
+    quint64 size = fileContent.size();
+    for (quint64 seek = 0; seek + length < size; seek++) {
+        for (checksum *generator : checksum_vector) {
+            const QByteArray content = fileContent.mid(seek, length);
+            const QByteArray generatedHash = generator->generateChecksum(content).toHex();
+            for (const QByteArray &hash : checksums) {
+                if (generatedHash.left(hash.length()) == hash) {
+                    QTextStream(stdout) << "MATCH: " << generator->formatName() << " Checksum " << hash << " Start " << seek << " End " << seek+length << Qt::endl;
+                    emit matched();
+                }
+            }
+        }
+    }
+}
diff --git a/src/brutethread.h b/src/brutethread.h
new file mode 100644
index 0000000..e6d3574
--- /dev/null
+++ b/src/brutethread.h
@@ -0,0 +1,49 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+#ifndef BRUTETHREAD_H
+#define BRUTETHREAD_H
+
+#include <QThread>
+#include <QObject>
+#include "checksum.h"
+
+struct brutelength
+{
+    quint64 min;
+    quint64 max;
+};
+
+class brutethread : public QThread
+{
+    Q_OBJECT
+public:
+    explicit brutethread(const QByteArray &fileContent, quint64 length, QVector<checksum*> checksum_vector, QVector<QByteArray> checksums);
+    void run();
+
+private:
+    QVector<checksum*> checksum_vector;
+    QVector<QByteArray> checksums;
+    QByteArray fileContent;
+    quint64 length;
+
+signals:
+    void matched();
+};
+
+#endif // BRUTETHREAD_H
diff --git a/src/checksum.cpp b/src/checksum.cpp
new file mode 100644
index 0000000..a4787cc
--- /dev/null
+++ b/src/checksum.cpp
@@ -0,0 +1,40 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+#include <QTextStream>
+#include "checksum.h"
+
+QByteArray checksum::generateChecksum(const QByteArray &data)
+{
+    Q_UNUSED(data)
+    QTextStream(stderr) << QString("WARNING: %1::generateChecksum is not implemented!").arg(metaObject()->className()) << Qt::endl;
+    return QByteArray();
+}
+
+const QString checksum::formatName()
+{
+    QTextStream(stderr) << QString("WARNING: %1::formatName is not implemented!").arg(metaObject()->className()) << Qt::endl;
+    return QString();
+}
+
+int checksum::checksumSize()
+{
+    QTextStream(stderr) << QString("WARNING: %1::checksumSize is not implemented!").arg(metaObject()->className()) << Qt::endl;
+    return 0;
+}
+
diff --git a/src/checksum.h b/src/checksum.h
new file mode 100644
index 0000000..038e3b9
--- /dev/null
+++ b/src/checksum.h
@@ -0,0 +1,34 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+#ifndef CHECKSUM_H
+#define CHECKSUM_H
+
+#include <QByteArray>
+#include <QObject>
+
+class checksum : public QObject
+{
+    Q_OBJECT
+public:
+    virtual QByteArray generateChecksum(const QByteArray &data);
+    virtual const QString formatName();
+    virtual int checksumSize();
+};
+
+#endif // CHECKSUM_H
diff --git a/src/checksum_qt.cpp b/src/checksum_qt.cpp
new file mode 100644
index 0000000..7c69284
--- /dev/null
+++ b/src/checksum_qt.cpp
@@ -0,0 +1,37 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+#include <QCryptographicHash>
+#include "checksum_qt.h"
+
+checksum_qt::checksum_qt(QCryptographicHash::Algorithm algorithm, const QString &name) : algorithm(algorithm), name(name) {}
+
+QByteArray checksum_qt::generateChecksum(const QByteArray &data)
+{
+    return QCryptographicHash::hash(data, algorithm);
+}
+
+const QString checksum_qt::formatName()
+{
+    return name;
+}
+
+int checksum_qt::checksumSize()
+{
+    return QCryptographicHash::hashLength(algorithm);
+}
diff --git a/src/checksum_qt.h b/src/checksum_qt.h
new file mode 100644
index 0000000..95a58f0
--- /dev/null
+++ b/src/checksum_qt.h
@@ -0,0 +1,39 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+#ifndef CHECKSUM_QT_H
+#define CHECKSUM_QT_H
+
+#include <QCryptographicHash>
+#include "checksum.h"
+
+class checksum_qt : public checksum
+{
+    Q_OBJECT
+public:
+    explicit checksum_qt(QCryptographicHash::Algorithm algorithm, const QString &name);
+    QByteArray generateChecksum(const QByteArray &data);
+    const QString formatName();
+    int checksumSize();
+
+private:
+    QCryptographicHash::Algorithm algorithm;
+    QString name;
+};
+
+#endif // CHECKSUM_MD5_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..c633619
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,118 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+// Qt includes
+#include <QCommandLineOption>
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <QTextStream>
+#include <QFileInfo>
+#include <QThread>
+
+// checkbrute includes
+#include "mainthread.h"
+#include "checksum_qt.h"
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication a(argc, argv);
+    a.setApplicationName("checkbrute");
+    a.setApplicationVersion("0.1");
+
+    QCommandLineParser commandLineParser;
+    commandLineParser.addHelpOption();
+    commandLineParser.addVersionOption();
+    commandLineParser.addPositionalArgument("source", "Source file to bruteforce.");
+    commandLineParser.addPositionalArgument("checksums", "File containing checksums.");
+    QCommandLineOption algorithmsOption = QCommandLineOption("algorithms", "Algorithms to use for bruteforcing.", "algorithms");
+    QCommandLineOption startOption = QCommandLineOption("start", "Byte to start bruteforcing from.", "start");
+    QCommandLineOption stopOption = QCommandLineOption("stop", "Byte when to stop bruteforcing.", "stop");
+    QCommandLineOption lengthsOption = QCommandLineOption("lengths", "Lengths to use for bruteforcing.", "lengths");
+    QCommandLineOption threadsOption = QCommandLineOption("threads", "Threads to use for bruteforcing.", "threads");
+    QCommandLineOption strictOption = QCommandLineOption(QStringList() << "s" << "strict", "Match checksums strict.");
+    commandLineParser.addOptions(QList<QCommandLineOption>() << algorithmsOption << startOption << stopOption << lengthsOption << threadsOption << strictOption);
+    commandLineParser.process(a);
+
+    QTextStream(stderr) << "INFO: Launch " << a.applicationName() << " " << a.applicationVersion() << "..." << Qt::endl;
+
+    const QStringList args = commandLineParser.positionalArguments();
+    if (args.count() != 2) {
+        QTextStream(stderr) << "ERROR: Missing required arguments!" << Qt::endl;
+        return 1;
+    }
+    const QString bruteforceFile = args.at(0);
+    const QString checksumsFile = args.at(1);
+    const bool strictHash = commandLineParser.isSet(strictOption);
+
+    if (strictHash) {
+        QTextStream(stderr) << "INFO: Strict matching enabled" << Qt::endl;
+    }
+    else {
+        QTextStream(stderr) << "INFO: Strict matching disabled" << Qt::endl;
+    }
+
+    if (!QFileInfo(bruteforceFile).isFile()) {
+        QTextStream(stderr) << "ERROR: Source file " << bruteforceFile << " doesn't exists!" << Qt::endl;
+        return 1;
+    }
+
+    if (!QFileInfo(checksumsFile).isFile()) {
+        QTextStream(stderr) << "ERROR: Checksums file " << checksumsFile << " doesn't exists!" << Qt::endl;
+        return 1;
+    }
+
+    const QString start = commandLineParser.value(startOption);
+    const QString stop = commandLineParser.value(stopOption);
+    const QString algorithms = commandLineParser.value(algorithmsOption);
+    const QString lengths = commandLineParser.value(lengthsOption);
+    const QString threads = commandLineParser.value(threadsOption);
+
+    std::map<std::string,checksum*> checksum_map;
+    checksum_map["MD4"] = new checksum_qt(QCryptographicHash::Md4, "MD4");
+    checksum_map["MD5"] = new checksum_qt(QCryptographicHash::Md5, "MD5");
+    checksum_map["SHA1"] = new checksum_qt(QCryptographicHash::Sha1, "SHA1");
+    checksum_map["SHA2-256"] = new checksum_qt(QCryptographicHash::Sha256, "SHA2-256");
+    checksum_map["SHA2-512"] = new checksum_qt(QCryptographicHash::Sha512, "SHA2-512");
+    checksum_map["SHA3-256"] = new checksum_qt(QCryptographicHash::Sha3_256, "SHA3-256");
+    checksum_map["SHA3-512"] = new checksum_qt(QCryptographicHash::Sha3_512, "SHA3-512");
+#if QT_VERSION >= 0x050902
+    checksum_map["Keccak-256"] = new checksum_qt(QCryptographicHash::Keccak_256, "Keccak-256");
+    checksum_map["Keccak-512"] = new checksum_qt(QCryptographicHash::Keccak_512, "Keccak-512");
+#endif
+
+    // 224 and 384 bit hashes are partial
+    if (strictHash) {
+        checksum_map["SHA2-224"] = new checksum_qt(QCryptographicHash::Sha224, "SHA2-224");
+        checksum_map["SHA2-384"] = new checksum_qt(QCryptographicHash::Sha384, "SHA2-384");
+        checksum_map["SHA3-224"] = new checksum_qt(QCryptographicHash::Sha3_224, "SHA3-224");
+        checksum_map["SHA3-384"] = new checksum_qt(QCryptographicHash::Sha3_384, "SHA3-384");
+#if QT_VERSION >= 0x050902
+        checksum_map["Keccak-224"] = new checksum_qt(QCryptographicHash::Keccak_224, "Keccak-224");
+        checksum_map["Keccak-384"] = new checksum_qt(QCryptographicHash::Keccak_384, "Keccak-384");
+#endif
+    }
+
+    mainthread instance(bruteforceFile, checksumsFile, algorithms, start, stop, lengths, threads, strictHash, checksum_map);
+    QObject::connect(&instance, &mainthread::finished, &a, &QCoreApplication::quit);
+    if (!instance.isInitialised())
+        return 1;
+
+    instance.run();
+
+    return a.exec();
+}
diff --git a/src/mainthread.cpp b/src/mainthread.cpp
new file mode 100644
index 0000000..eb51326
--- /dev/null
+++ b/src/mainthread.cpp
@@ -0,0 +1,285 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+// Qt includes
+#include <QCoreApplication>
+#include <QTextStream>
+#include <QFile>
+
+// checkbrute includes
+#include "mainthread.h"
+
+mainthread::mainthread(const QString &bruteforceFile, const QString &checksumsFile, const QString &algorithms, const QString &start, const QString &stop, const QString &lenghts_str, const QString &threads_str, bool strictHash, const std::map<std::string,checksum*> &checksum_map) : checksum_map(checksum_map), strictHash(strictHash)
+{
+    if (!algorithms.isEmpty()) {
+        for (const QString &algorithm : algorithms.split(',')) {
+            std::map<std::string,checksum*>::const_iterator it;
+            it = checksum_map.find(algorithm.toUpper().toStdString());
+            if (it != checksum_map.end()) {
+                QTextStream(stderr) << "INFO: Using " << QString::fromStdString(it->first) << " algorithm" << Qt::endl;
+                checksum_vector << it->second;
+            }
+        }
+    }
+    else {
+        std::map<std::string,checksum*>::const_iterator it;
+        for (it = checksum_map.begin(); it != checksum_map.end(); it++) {
+            QTextStream(stderr) << "INFO: Using " << QString::fromStdString(it->first) << " algorithm" << Qt::endl;
+            checksum_vector << it->second;
+        }
+    }
+    if (checksum_vector.isEmpty()) {
+        QTextStream(stderr) << "ERROR: No algorithms are used!" << Qt::endl;
+        initialised = false;
+        return;
+    }
+
+    QFile bruteforceFileHandler(bruteforceFile);
+    if (bruteforceFileHandler.open(QIODevice::ReadOnly)) {
+        QTextStream(stderr) << "INFO: Reading source file " << bruteforceFile << "..." << Qt::endl;
+        fileContent = bruteforceFileHandler.readAll();
+        bruteforceFileHandler.close();
+        QTextStream(stderr) << "INFO: Source file have " << fileContent.size() << " bytes" << Qt::endl;
+    }
+    else {
+        QTextStream(stderr) << "ERROR: Source file " << bruteforceFile << " can't be read!" << Qt::endl;
+        initialised = false;
+        return;
+    }
+
+    QFile checksumsFileHandler(checksumsFile);
+    if (checksumsFileHandler.open(QIODevice::ReadOnly)) {
+        QTextStream(stderr) << "INFO: Reading checksums file " << checksumsFile << "..." << Qt::endl;
+        QByteArray checksumFileContent = checksumsFileHandler.readAll();
+        checksumsFileHandler.close();
+        for (const QByteArray &data : checksumFileContent.split('\n')) {
+            const QByteArray tdata = data.trimmed();
+            if (!tdata.isEmpty()) {
+                QByteArray binary = QByteArray::fromHex(tdata);
+                if (!binary.isEmpty()) {
+                    QTextStream(stderr) << "INFO: Found valid checksum " << tdata.toLower() << Qt::endl;
+                    checksums << tdata.toLower();
+                }
+                else {
+                    QTextStream(stderr) << "INFO: Found invalid data " << tdata << Qt::endl;
+                }
+            }
+        }
+    }
+    else {
+        QTextStream(stderr) << "ERROR: Checksums file " << bruteforceFile << " can't be read!" << Qt::endl;
+        initialised = false;
+        return;
+    }
+    if (checksums.isEmpty()) {
+        QTextStream(stderr) << "ERROR: No checksums are used!" << Qt::endl;
+        initialised = false;
+        return;
+    }
+
+    if (!start.isEmpty()) {
+        bool startOk;
+        begin = start.toULongLong(&startOk);
+        if (!startOk) {
+            QTextStream(stderr) << "ERROR: Start value is not valid!" << Qt::endl;
+            initialised = false;
+            return;
+        }
+    }
+    else {
+        begin = 0;
+    }
+
+    if (!stop.isEmpty()) {
+        bool stopOk;
+        end = stop.toULongLong(&stopOk);
+        if (!stopOk) {
+            QTextStream(stderr) << "ERROR: Stop value is not valid!" << Qt::endl;
+            initialised = false;
+            return;
+        }
+    }
+    else {
+        end = (quint64)fileContent.size();
+    }
+
+    if (begin >= end) {
+        QTextStream(stderr) << "ERROR: Stop value must be placed after Start value!" << Qt::endl;
+        initialised = false;
+        return;
+    }
+
+    // Clear unneeded bytes
+    if (begin != 0 && end != (quint64)fileContent.size()) {
+        fileContent = fileContent.mid(begin, end - begin);
+        fileContent.squeeze();
+    }
+
+    if (!lenghts_str.isEmpty()) {
+        for (const QString &length_str : lenghts_str.split(',')) {
+            if (length_str.contains('-')) {
+                const QStringList length_list = length_str.split('-');
+                if (length_list.count() == 2) {
+                    bool lengthOk;
+                    quint64 min = length_list.at(0).toULongLong(&lengthOk);
+                    if (!lengthOk || min < 1 || min > (quint64)fileContent.size()) {
+                        QTextStream(stderr) << "ERROR: Found invalid length \"" << min << "\"!" << Qt::endl;
+                        initialised = false;
+                        return;
+                    }
+                    quint64 max = length_list.at(1).toULongLong(&lengthOk);
+                    if (!lengthOk || max > (quint64)fileContent.size() || max < 1) {
+                        QTextStream(stderr) << "ERROR: Found invalid length \"" << max << "\"!" << Qt::endl;
+                        initialised = false;
+                        return;
+                    }
+                    brutelength length;
+                    length.min = min;
+                    length.max = max;
+                    lengths << length;
+                }
+                else {
+                    QTextStream(stderr) << "ERROR: Found invalid length \"" << length_str << "\"!" << Qt::endl;
+                    initialised = false;
+                    return;
+                }
+            }
+            else {
+                bool lengthOk;
+                quint64 val = length_str.toULongLong(&lengthOk);
+                if (lengthOk && val >= 1) {
+                    brutelength length;
+                    length.min = val;
+                    length.max = val;
+                    lengths << length;
+                }
+                else {
+                    QTextStream(stderr) << "ERROR: Found invalid length \"" << length_str << "\"!" << Qt::endl;
+                    initialised = false;
+                    return;
+                }
+            }
+        }
+    }
+    else {
+        brutelength length;
+        length.min = 1;
+        length.max = (quint64)fileContent.size();
+        lengths << length;
+    }
+    if (lengths.isEmpty()) {
+        QTextStream(stderr) << "ERROR: No lengths are used!" << Qt::endl;
+        initialised = false;
+        return;
+    }
+
+    if (!threads_str.isEmpty()) {
+        bool threadsOk;
+        quint16 threadCount = threads_str.toUShort(&threadsOk);
+        if (!threadsOk || threadCount > 255) {
+            QTextStream(stderr) << "ERROR: Threads value is not valid!" << Qt::endl;
+            initialised = false;
+            return;
+        }
+        mthreads = (quint8)threadCount;
+    }
+    else {
+        int threadCount = QThread::idealThreadCount();
+        if (threadCount < 255) {
+            mthreads = (quint8)threadCount;
+        }
+        else {
+            mthreads = 255;
+        }
+    }
+
+    found = 0;
+    threads = 0;
+    initialised = true;
+    doneThreads = false;
+}
+
+void mainthread::threadFinished()
+{
+    threads--;
+    eventLoop.quit();
+    sender()->deleteLater();
+    if (doneThreads && threads == 0) {
+        QCoreApplication::processEvents(QEventLoop::AllEvents);
+        QTextStream(stderr) << "INFO: Bruteforce finished, found " << found << " matches!" << Qt::endl;
+        emit finished();
+    }
+}
+
+bool mainthread::isInitialised()
+{
+    return initialised;
+}
+
+void mainthread::matched()
+{
+    found++;
+}
+
+void mainthread::run()
+{
+    QTextStream(stderr) << "INFO: Set max threads to " << mthreads << Qt::endl;
+    QTextStream(stderr) << "INFO: Start bruteforcing..." << Qt::endl;
+    QVector<brutelength>::const_iterator it;
+    const int lengthsCount = lengths.count();
+    for (it = lengths.constBegin(); it != lengths.constEnd(); it++) {
+        brutelength length = *it;
+        quint64 min = length.min;
+        quint64 max = length.max;
+        if (min == max) {
+            if (threads >= mthreads)
+                eventLoop.exec();
+            QTextStream(stderr) << "[" << std::distance(lengths.constBegin(), it) + 1 << "/" << lengthsCount << "] [" << min << "/" << max << "] Bruteforcing...\r";
+            brutethread *thread = new brutethread(fileContent, min, checksum_vector, checksums);
+            QObject::connect(thread, &QThread::finished, this, &mainthread::threadFinished);
+            QObject::connect(thread, &brutethread::matched, this, &mainthread::matched);
+            thread->start(QThread::LowPriority);
+            threads++;
+        }
+        else if (min < max) {
+            for (quint64 clength = min; clength <= max; clength++) {
+                if (threads >= mthreads)
+                    eventLoop.exec();
+                QTextStream(stderr) << "[" << std::distance(lengths.constBegin(), it) + 1 << "/" << lengthsCount << "] [" << clength << "/" << max << "] Bruteforcing...\r";
+                brutethread *thread = new brutethread(fileContent, clength, checksum_vector, checksums);
+                QObject::connect(thread, &QThread::finished, this, &mainthread::threadFinished);
+                QObject::connect(thread, &brutethread::matched, this, &mainthread::matched);
+                thread->start(QThread::LowPriority);
+                threads++;
+            }
+        }
+        else {
+            for (quint64 clength = min; clength >= max; clength--) {
+                if (threads >= mthreads)
+                    eventLoop.exec();
+                QTextStream(stderr) << "[" << std::distance(lengths.constBegin(), it) + 1 << "/" << lengthsCount << "] [" << clength << "/" << max << "] Bruteforcing...\r";
+                brutethread *thread = new brutethread(fileContent, clength, checksum_vector, checksums);
+                QObject::connect(thread, &QThread::finished, this, &mainthread::threadFinished);
+                QObject::connect(thread, &brutethread::matched, this, &mainthread::matched);
+                thread->start(QThread::LowPriority);
+                threads++;
+            }
+        }
+    }
+    doneThreads = true;
+}
diff --git a/src/mainthread.h b/src/mainthread.h
new file mode 100644
index 0000000..927717c
--- /dev/null
+++ b/src/mainthread.h
@@ -0,0 +1,62 @@
+/*****************************************************************************
+* checkbrute Checksum Bruteforcing Tool
+* Copyright (C) 2020 Syping
+*
+* Redistribution and use in source and binary forms, with or without modification,
+* are permitted provided that the following conditions are met:
+*
+* 1. Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* 2. Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+*
+* This software is provided as-is, no warranties are given to you, we are not
+* responsible for anything with use of the software, you are self responsible.
+*****************************************************************************/
+
+#ifndef MAINTHREAD_H
+#define MAINTHREAD_H
+
+// Qt includes
+#include <QEventLoop>
+#include <QObject>
+
+// checkbrute includes
+#include "brutethread.h"
+#include "checksum.h"
+
+class mainthread : public QObject
+{
+    Q_OBJECT
+public:
+    explicit mainthread(const QString &bruteforceFile, const QString &checksumsFile, const QString &algorithms, const QString &start, const QString &stop, const QString &lenghts, const QString &threads, bool strictHash, const std::map<std::string,checksum*> &checksum_map);
+    bool isInitialised();
+    void run();
+
+public slots:
+    void threadFinished();
+    void matched();
+
+private:
+    std::map<std::string,checksum*> checksum_map;
+    QVector<checksum*> checksum_vector;
+    QVector<QByteArray> checksums;
+    QVector<brutelength> lengths;
+    QByteArray fileContent;
+    QEventLoop eventLoop;
+    bool doneThreads;
+    bool initialised;
+    bool strictHash;
+    quint8 mthreads;
+    quint8 threads;
+    quint64 found;
+    quint64 begin;
+    quint64 end;
+
+signals:
+    void finished();
+};
+
+#endif // MAINTHREAD_H