Imported existing sources
This commit is contained in:
22
DbHashCracker.pro
Normal file
22
DbHashCracker.pro
Normal file
@@ -0,0 +1,22 @@
|
||||
QT += core sql
|
||||
QT -= gui widgets
|
||||
|
||||
DBLIBS +=
|
||||
|
||||
TARGET = hashcracker
|
||||
|
||||
PROJECT_ROOT = ..
|
||||
|
||||
SOURCES += main.cpp \
|
||||
workerthread.cpp
|
||||
|
||||
HEADERS += \
|
||||
workerthread.h
|
||||
|
||||
FORMS +=
|
||||
|
||||
RESOURCES +=
|
||||
|
||||
TRANSLATIONS +=
|
||||
|
||||
include($${PROJECT_ROOT}/app.pri)
|
271
main.cpp
Normal file
271
main.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
#include <QCoreApplication>
|
||||
#include <qlogging.h>
|
||||
#include <QCommandLineParser>
|
||||
#include <QCommandLineOption>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include <QVector>
|
||||
#include <QStringList>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "workerthread.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
qSetMessagePattern(QStringLiteral("%{time HH:mm:ss.zzz} "
|
||||
"["
|
||||
"%{if-debug}D%{endif}"
|
||||
"%{if-info}I%{endif}"
|
||||
"%{if-warning}W%{endif}"
|
||||
"%{if-critical}C%{endif}"
|
||||
"%{if-fatal}F%{endif}"
|
||||
"] "
|
||||
"%{message}"));
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.addOptions({
|
||||
{
|
||||
{ QStringLiteral("f"), QStringLiteral("filename") },
|
||||
QCoreApplication::translate("main", "File containing the wordlist"),
|
||||
QCoreApplication::translate("main", "filename"),
|
||||
QStringLiteral("wordlist.txt")
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("n"), QStringLiteral("table-name") },
|
||||
QCoreApplication::translate("main", "Name of the table to be filled"),
|
||||
QCoreApplication::translate("main", "table"),
|
||||
QStringLiteral("Hashes")
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("d"), QStringLiteral("no-drop-table") },
|
||||
QCoreApplication::translate("main", "Don't drop the (old) hash table at the beginning"),
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("c"), QStringLiteral("no-create-table") },
|
||||
QCoreApplication::translate("main", "Don't create a table at the beginning"),
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("i"), QStringLiteral("no-index") },
|
||||
QCoreApplication::translate("main", "Don't add an index on hash column"),
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("p"), QStringLiteral("no-primary") },
|
||||
QCoreApplication::translate("main", "Don't add an primary key on value column"),
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("t"), QStringLiteral("threads") },
|
||||
QCoreApplication::translate("main", "Thread count"),
|
||||
QCoreApplication::translate("main", "threads"),
|
||||
QString::number(QThread::idealThreadCount())
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("b"), QStringLiteral("buffer-size") },
|
||||
QCoreApplication::translate("main", "Buffer size (per thread)"),
|
||||
QCoreApplication::translate("main", "buffer-size"),
|
||||
QString::number(100)
|
||||
},
|
||||
{
|
||||
{ QStringLiteral("r"), QStringLiteral("no-replace-into") },
|
||||
QCoreApplication::translate("main", "Use INSERT INTO instead of REPLACE INTO")
|
||||
},
|
||||
{
|
||||
QStringLiteral("no-delayed"),
|
||||
QCoreApplication::translate("main", "Dont add DELAYED keyword when inserting")
|
||||
}
|
||||
});
|
||||
|
||||
parser.process(app);
|
||||
bool ok;
|
||||
|
||||
const auto filename = parser.value(QStringLiteral("filename"));
|
||||
const auto tableName = parser.value(QStringLiteral("table-name"));
|
||||
const auto noDropTable = parser.isSet(QStringLiteral("no-drop-table"));
|
||||
const auto noCreateTable = parser.isSet(QStringLiteral("no-create-table"));
|
||||
const auto noIndex = parser.isSet(QStringLiteral("no-index"));
|
||||
const auto noPrimary = parser.isSet(QStringLiteral("no-primary"));
|
||||
|
||||
const auto threads = parser.value(QStringLiteral("threads")).toInt(&ok);
|
||||
if(!ok)
|
||||
{
|
||||
qFatal("could not parse threads");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
const auto bufferSize = parser.value(QStringLiteral("buffer-size")).toInt(&ok);;
|
||||
if(!ok)
|
||||
{
|
||||
qFatal("could not parse bufferSize");
|
||||
return -2;
|
||||
}
|
||||
|
||||
const auto noReplace = parser.isSet(QStringLiteral("no-replace-into"));
|
||||
const auto noDelayed = parser.isSet(QStringLiteral("no-delayed"));
|
||||
|
||||
qDebug() << "filename =" << filename;
|
||||
qDebug() << "tableName =" << tableName;
|
||||
qDebug() << "noDropTable =" << noDropTable;
|
||||
qDebug() << "noCreateTable =" << noCreateTable;
|
||||
qDebug() << "noIndex =" << noIndex;
|
||||
qDebug() << "noPrimary =" << noPrimary;
|
||||
qDebug() << "threads =" << threads;
|
||||
qDebug() << "bufferSize =" << bufferSize;
|
||||
qDebug() << "noReplace =" << noReplace;
|
||||
qDebug() << "noDelayed =" << noDelayed;
|
||||
|
||||
qDebug() << "reading wordlist...";
|
||||
|
||||
QStringList wordlist;
|
||||
|
||||
{
|
||||
QFile file(filename);
|
||||
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
qCritical() << file.errorString();
|
||||
return -1;
|
||||
}
|
||||
|
||||
QTextStream textStream(&file);
|
||||
while(!textStream.atEnd())
|
||||
wordlist.append(textStream.readLine());
|
||||
}
|
||||
|
||||
{
|
||||
QSqlDatabase db = WorkerThread::getDatabase();
|
||||
|
||||
if(!noDropTable)
|
||||
{
|
||||
qDebug() << "dropping old table...";
|
||||
|
||||
auto query = db.exec(QStringLiteral("DROP TABLE IF EXISTS `%0`").arg(tableName));
|
||||
if(query.lastError().isValid())
|
||||
{
|
||||
qCritical() << query.lastError().text();
|
||||
qCritical() << query.lastError().databaseText();
|
||||
qFatal("could not delete old table");
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
if(!noCreateTable)
|
||||
{
|
||||
qDebug() << "creating table...";
|
||||
|
||||
QString indexSql;
|
||||
|
||||
if(noIndex)
|
||||
qWarning() << "will not add an index for hash";
|
||||
else
|
||||
indexSql.append(QStringLiteral(",INDEX(`Hash`)"));
|
||||
|
||||
if(noPrimary)
|
||||
qWarning() << "will not add an primary for value";
|
||||
else
|
||||
indexSql.append(QStringLiteral(",PRIMARY KEY(`Value`)"));
|
||||
|
||||
auto query = db.exec(QStringLiteral("CREATE TABLE `%0` ("
|
||||
" `Hash` CHAR(32) NOT NULL,"
|
||||
" `Value` VARCHAR(64) NOT NULL"
|
||||
" %1"
|
||||
")").arg(tableName).arg(indexSql));
|
||||
if(query.lastError().isValid())
|
||||
{
|
||||
qCritical() << query.lastError().text();
|
||||
qCritical() << query.lastError().databaseText();
|
||||
qFatal("could not create table");
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QStringList> prefixes(threads);
|
||||
|
||||
for(auto iter = wordlist.constBegin(); iter != wordlist.constEnd(); )
|
||||
for(int i = 0; i < threads && iter != wordlist.constEnd(); i++, iter++)
|
||||
prefixes[i].append(*iter);
|
||||
|
||||
const quint64 allcount = quint64(wordlist.count()) * quint64(wordlist.count());
|
||||
|
||||
QVector<WorkerThread*> workers(threads);
|
||||
|
||||
for(int i = 0; i < threads; i++)
|
||||
{
|
||||
workers[i] = new WorkerThread(prefixes.at(i), wordlist, tableName, bufferSize, noReplace, noDelayed);
|
||||
workers.at(i)->start();
|
||||
QObject::connect(workers.at(i), &QThread::finished, [&workers](){
|
||||
qDebug() << "thread finished";
|
||||
|
||||
const auto allFinished = std::all_of(workers.constBegin(), workers.constEnd(), [](const WorkerThread *worker){
|
||||
return worker->isFinished();
|
||||
});
|
||||
|
||||
if(allFinished)
|
||||
{
|
||||
qDeleteAll(workers);
|
||||
workers.clear();
|
||||
qApp->quit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const auto started = QDateTime::currentDateTime();
|
||||
|
||||
QTimer timer;
|
||||
timer.setInterval(1000);
|
||||
QObject::connect(&timer, &QTimer::timeout, [allcount, started](){
|
||||
try {
|
||||
auto formatTime = [](quint64 total){
|
||||
auto seconds = total % 60;
|
||||
total -= seconds;
|
||||
total /= 60;
|
||||
|
||||
auto minutes = total % 60;
|
||||
total -= minutes;
|
||||
total /= 60;
|
||||
|
||||
const auto hours = total;
|
||||
|
||||
return QString("%0:%1:%2")
|
||||
.arg(hours)
|
||||
.arg(minutes, 2, 10, QLatin1Char('0'))
|
||||
.arg(seconds, 2, 10, QLatin1Char('0'));
|
||||
};
|
||||
|
||||
const quint64 sum = WorkerThread::m_counter;
|
||||
const auto speed = WorkerThread::m_speed.fetchAndStoreOrdered(0);
|
||||
|
||||
const auto elapsed = started.secsTo(QDateTime::currentDateTime());
|
||||
const auto avgSpeed = elapsed == 0 ? 0 : sum / elapsed;
|
||||
|
||||
const auto remaining = allcount-sum;
|
||||
|
||||
qInfo().nospace().noquote() << formatTime(elapsed) << " | "
|
||||
<< sum << "/" << allcount
|
||||
<< " (" << QString::number(qreal(sum)/allcount*100., 'f', 2) << "%) | NOW "
|
||||
<< speed << "/sec "
|
||||
<< formatTime(speed == 0 ? 0 : remaining/speed) << " | AVG "
|
||||
<< avgSpeed << "/sec "
|
||||
<< formatTime(avgSpeed == 0 ? 0 : remaining/avgSpeed);
|
||||
} catch (...) {
|
||||
qCritical() << "exception while printing stats";
|
||||
}
|
||||
});
|
||||
timer.start();
|
||||
|
||||
const auto result = app.exec();
|
||||
qDebug() << "finished.";
|
||||
return result;
|
||||
}
|
124
workerthread.cpp
Normal file
124
workerthread.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "workerthread.h"
|
||||
|
||||
#include <QQueue>
|
||||
#include <QPair>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDebug>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QSqlError>
|
||||
|
||||
QAtomicInt WorkerThread::m_dbIdentifier;
|
||||
|
||||
QAtomicInteger<quint64> WorkerThread::m_counter;
|
||||
QAtomicInt WorkerThread::m_speed;
|
||||
|
||||
WorkerThread::WorkerThread(const QStringList &prefixes, const QStringList &suffixes, const QString &tableName,
|
||||
const int bufferSize, const bool noReplace, const bool noDelayed, QObject *parent) :
|
||||
QThread(parent), m_prefixes(prefixes), m_suffixes(suffixes), m_tableName(tableName),
|
||||
m_bufferSize(bufferSize), m_noReplace(noReplace), m_noDelayed(noDelayed)
|
||||
{
|
||||
}
|
||||
|
||||
void WorkerThread::run()
|
||||
{
|
||||
QSqlDatabase db = getDatabase(QStringLiteral("thread%0").arg(m_dbIdentifier++));
|
||||
if(!db.isOpen())
|
||||
return;
|
||||
|
||||
QSqlQuery query = getQuery(db, m_tableName, m_noReplace, m_noDelayed, m_bufferSize);
|
||||
//TODO error handling
|
||||
|
||||
QQueue<QPair<QString, QString> > queue;
|
||||
|
||||
for(const auto &prefix : m_prefixes)
|
||||
for(const auto &suffix : m_suffixes)
|
||||
{
|
||||
const auto word = prefix + suffix;
|
||||
queue.enqueue(qMakePair(QCryptographicHash::hash(word.toUtf8(), QCryptographicHash::Md5).toHex(), word));
|
||||
|
||||
m_counter++;
|
||||
m_speed++;
|
||||
|
||||
if(queue.count() == m_bufferSize)
|
||||
if(!bindAndExecute(query, queue))
|
||||
return;
|
||||
|
||||
if(isInterruptionRequested())
|
||||
return;
|
||||
}
|
||||
|
||||
if(!queue.isEmpty())
|
||||
{
|
||||
query = getQuery(db, m_tableName, m_noReplace, m_noDelayed, queue.count());
|
||||
//TODO error handling
|
||||
|
||||
if(!bindAndExecute(query, queue))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QSqlDatabase WorkerThread::getDatabase(const QString &connectionName)
|
||||
{
|
||||
qDebug() << "connecting to database for" << connectionName;
|
||||
|
||||
QSqlDatabase db(QSqlDatabase::addDatabase(QStringLiteral("QMYSQL"), connectionName));
|
||||
db.setHostName(QProcessEnvironment::systemEnvironment().value(QStringLiteral("MYSQL_HOSTNAME"), QStringLiteral("localhost")));
|
||||
db.setUserName(QProcessEnvironment::systemEnvironment().value(QStringLiteral("MYSQL_USERNAME"), QStringLiteral("hashcracker")));
|
||||
db.setPassword(QProcessEnvironment::systemEnvironment().value(QStringLiteral("MYSQL_PASSWORD"), QStringLiteral("topsecret")));
|
||||
db.setDatabaseName(QProcessEnvironment::systemEnvironment().value(QStringLiteral("MYSQL_DATABASE"), QStringLiteral("hashcracker")));
|
||||
|
||||
if(!db.open())
|
||||
{
|
||||
qCritical() << db.lastError().text();
|
||||
qCritical() << db.lastError().databaseText();
|
||||
qFatal("could not connect to database");
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
QSqlQuery WorkerThread::getQuery(const QSqlDatabase &db, const QString &tableName, const bool noReplace, const bool noDelayed, const int rowCount)
|
||||
{
|
||||
QSqlQuery query(db);
|
||||
|
||||
QString columns;
|
||||
for(int i = 0; i < rowCount; i++)
|
||||
{
|
||||
if(i > 0)
|
||||
columns.append(',');
|
||||
columns.append(QStringLiteral(" (:Hash%0, :Value%0)").arg(i));
|
||||
}
|
||||
|
||||
if(!query.prepare(QStringLiteral("%0%1 INTO `%2` (`Hash`, `Value`) VALUES%3")
|
||||
.arg(noReplace ? QStringLiteral("INSERT") : QStringLiteral("REPLACE"))
|
||||
.arg(noDelayed ? QString() : QStringLiteral(" DELAYED"))
|
||||
.arg(tableName)
|
||||
.arg(columns)))
|
||||
{
|
||||
qCritical() << query.lastError().text();
|
||||
qCritical() << query.lastError().databaseText();
|
||||
qFatal("could not prepare query");
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
bool WorkerThread::bindAndExecute(QSqlQuery &query, QQueue<QPair<QString, QString> > &queue)
|
||||
{
|
||||
for(int i = 0; !queue.isEmpty(); i++)
|
||||
{
|
||||
const auto pair = queue.dequeue();
|
||||
query.bindValue(QStringLiteral(":Hash%0").arg(i), pair.first);
|
||||
query.bindValue(QStringLiteral(":Value%0").arg(i), pair.second);
|
||||
}
|
||||
|
||||
const auto result = query.exec();
|
||||
if(!result)
|
||||
{
|
||||
qCritical() << query.lastError().text();
|
||||
qCritical() << query.lastError().databaseText();
|
||||
qFatal("could not execute query");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
40
workerthread.h
Normal file
40
workerthread.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QThread>
|
||||
#include <QAtomicInteger>
|
||||
#include <QAtomicInt>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
|
||||
template <class T> class QQueue;
|
||||
template <class T1, class T2> struct QPair;
|
||||
|
||||
class WorkerThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
static QAtomicInt m_dbIdentifier;
|
||||
|
||||
public:
|
||||
static QAtomicInteger<quint64> m_counter;
|
||||
static QAtomicInt m_speed;
|
||||
|
||||
explicit WorkerThread(const QStringList &prefixes, const QStringList &suffixes, const QString &tableName,
|
||||
const int bufferSize, const bool noReplace, const bool noDelayed, QObject *parent = Q_NULLPTR);
|
||||
|
||||
void run() Q_DECL_OVERRIDE;
|
||||
|
||||
static QSqlDatabase getDatabase(const QString &connectionName = QLatin1String(QSqlDatabase::defaultConnection));
|
||||
static QSqlQuery getQuery(const QSqlDatabase &db, const QString &tableName, const bool noReplace, const bool noDelayed, const int rowCount);
|
||||
static bool bindAndExecute(QSqlQuery &query, QQueue<QPair<QString, QString> > &queue);
|
||||
|
||||
private:
|
||||
const QStringList m_prefixes;
|
||||
const QStringList m_suffixes;
|
||||
const QString m_tableName;
|
||||
const int m_bufferSize;
|
||||
const bool m_noReplace;
|
||||
const bool m_noDelayed;
|
||||
};
|
Reference in New Issue
Block a user