mirror of
https://github.com/feather-wallet/feather.git
synced 2024-11-16 17:27:38 +00:00
Merge pull request 'CLI: add wallet bruteforce' (#338) from tobtoht/feather:bruteforce into master
Reviewed-on: https://git.featherwallet.org/feather/feather/pulls/338
This commit is contained in:
commit
0a0c610dc6
7 changed files with 271 additions and 15 deletions
85
src/cli.cpp
85
src/cli.cpp
|
@ -5,8 +5,10 @@
|
|||
|
||||
// libwalletqt
|
||||
#include "libwalletqt/TransactionHistory.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
#include "model/AddressBookModel.h"
|
||||
#include "model/TransactionHistoryModel.h"
|
||||
#include "utils/brute.h"
|
||||
|
||||
CLI::CLI(AppContext *ctx, QObject *parent) :
|
||||
QObject(parent),
|
||||
|
@ -17,23 +19,86 @@ CLI::CLI(AppContext *ctx, QObject *parent) :
|
|||
}
|
||||
|
||||
void CLI::run() {
|
||||
if(mode == CLIMode::CLIModeExportContacts ||
|
||||
mode == CLIMode::CLIModeExportTxHistory) {
|
||||
if(!ctx->cmdargs->isSet("wallet-file")) return this->finishedError("--wallet-file argument missing");
|
||||
if(!ctx->cmdargs->isSet("password")) return this->finishedError("--password argument missing");
|
||||
if (mode == CLIMode::ExportContacts || mode == CLIMode::ExportTxHistory)
|
||||
{
|
||||
if(!ctx->cmdargs->isSet("wallet-file"))
|
||||
return this->finishedError("--wallet-file argument missing");
|
||||
if(!ctx->cmdargs->isSet("password"))
|
||||
return this->finishedError("--password argument missing");
|
||||
ctx->onOpenWallet(ctx->cmdargs->value("wallet-file"), ctx->cmdargs->value("password"));
|
||||
}
|
||||
else if (mode == CLIMode::BruteforcePassword)
|
||||
{
|
||||
QString keys_file = ctx->cmdargs->value("bruteforce-password");
|
||||
if (!keys_file.endsWith(".keys")) {
|
||||
return this->finishedError("Wallet file does not end with .keys");
|
||||
}
|
||||
|
||||
QStringList words;
|
||||
if (ctx->cmdargs->isSet("bruteforce-dict")) {
|
||||
QString data = Utils::barrayToString(Utils::fileOpen(ctx->cmdargs->value("bruteforce-dict")));
|
||||
words = data.split("\n");
|
||||
}
|
||||
|
||||
if (!ctx->cmdargs->isSet("bruteforce-chars")) {
|
||||
return this->finishedError("--bruteforce-chars argument missing");
|
||||
}
|
||||
QString chars = ctx->cmdargs->value("bruteforce-chars");
|
||||
|
||||
brute b(chars.toStdString());
|
||||
if (words.isEmpty()) {
|
||||
qDebug() << "No dictionairy specified, bruteforcing all chars";
|
||||
while (true) {
|
||||
QString pass = QString::fromStdString(b.next());
|
||||
if (ctx->walletManager->verifyWalletPassword(keys_file, pass, false)) {
|
||||
this->finished(QString("Found password: %1").arg(pass));
|
||||
break;
|
||||
}
|
||||
qDebug() << pass;
|
||||
}
|
||||
}
|
||||
else {
|
||||
bruteword bb(chars.toStdString());
|
||||
bool foundPass = false;
|
||||
for (auto word: words) {
|
||||
if (word.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
bb.setWord(word.toStdString());
|
||||
|
||||
while (true) {
|
||||
QString pass = QString::fromStdString(bb.next());
|
||||
if (pass == "") {
|
||||
break;
|
||||
}
|
||||
if (ctx->walletManager->verifyWalletPassword(keys_file, pass, false)) {
|
||||
this->finished(QString("Found password: %1").arg(pass));
|
||||
foundPass = true;
|
||||
break;
|
||||
}
|
||||
qDebug() << pass;
|
||||
}
|
||||
if (foundPass) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundPass) {
|
||||
this->finished("Search space exhausted");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CLI::onWalletOpened() {
|
||||
if(mode == CLIMode::CLIModeExportContacts){
|
||||
if(mode == CLIMode::ExportContacts){
|
||||
auto *model = ctx->currentWallet->addressBookModel();
|
||||
auto fn = ctx->cmdargs->value("export-contacts");
|
||||
if(model->writeCSV(fn))
|
||||
this->finished(QString("Address book exported to %1").arg(fn));
|
||||
else
|
||||
this->finishedError("Address book export failure");
|
||||
} else if(mode == CLIModeExportTxHistory) {
|
||||
} else if(mode == ExportTxHistory) {
|
||||
ctx->currentWallet->history()->refresh(ctx->currentWallet->currentSubaddressAccount());
|
||||
auto *model = ctx->currentWallet->history();
|
||||
auto fn = ctx->cmdargs->value("export-txhistory");
|
||||
|
@ -45,14 +110,14 @@ void CLI::onWalletOpened() {
|
|||
}
|
||||
|
||||
void CLI::onWalletOpenedError(const QString &err) {
|
||||
if(mode == CLIMode::CLIModeExportContacts ||
|
||||
mode == CLIMode::CLIModeExportTxHistory)
|
||||
if(mode == CLIMode::ExportContacts ||
|
||||
mode == CLIMode::ExportTxHistory)
|
||||
return this->finishedError(err);
|
||||
}
|
||||
|
||||
void CLI::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
||||
if(mode == CLIMode::CLIModeExportContacts ||
|
||||
mode == CLIMode::CLIModeExportTxHistory)
|
||||
if(mode == CLIMode::ExportContacts ||
|
||||
mode == CLIMode::ExportTxHistory)
|
||||
return this->finishedError("invalid password");
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
#include "appcontext.h"
|
||||
|
||||
enum CLIMode {
|
||||
CLIModeExportContacts,
|
||||
CLIModeExportTxHistory
|
||||
ExportContacts,
|
||||
ExportTxHistory,
|
||||
BruteforcePassword
|
||||
};
|
||||
|
||||
class CLI : public QObject
|
||||
|
|
|
@ -225,6 +225,11 @@ bool WalletManager::walletExists(const QString &path) const
|
|||
return m_pimpl->walletExists(path.toStdString());
|
||||
}
|
||||
|
||||
bool WalletManager::verifyWalletPassword(const QString &keys_file_name, const QString &password, bool no_spend_key, uint64_t kdf_rounds) const
|
||||
{
|
||||
return m_pimpl->verifyWalletPassword(keys_file_name.toStdString(), password.toStdString(), no_spend_key, kdf_rounds);
|
||||
}
|
||||
|
||||
QStringList WalletManager::findWallets(const QString &path)
|
||||
{
|
||||
std::vector<std::string> found_wallets = m_pimpl->findWallets(path.toStdString());
|
||||
|
|
|
@ -105,6 +105,9 @@ public:
|
|||
//! checks is given filename is a wallet;
|
||||
Q_INVOKABLE bool walletExists(const QString &path) const;
|
||||
|
||||
//! verify wallet password
|
||||
Q_INVOKABLE bool verifyWalletPassword(const QString &keys_file_name, const QString &password, bool no_spend_key, uint64_t kdf_rounds = 1) const;
|
||||
|
||||
//! returns list with wallet's filenames, if found by given path
|
||||
Q_INVOKABLE QStringList findWallets(const QString &path);
|
||||
|
||||
|
|
19
src/main.cpp
19
src/main.cpp
|
@ -78,6 +78,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|||
QCommandLineOption exportTxHistoryOption(QStringList() << "export-txhistory", "Output wallet transaction history as CSV to specified path.", "file");
|
||||
parser.addOption(exportTxHistoryOption);
|
||||
|
||||
QCommandLineOption bruteforcePasswordOption(QStringList() << "bruteforce-password", "Bruteforce wallet password", "file");
|
||||
parser.addOption(bruteforcePasswordOption);
|
||||
|
||||
QCommandLineOption bruteforceCharsOption(QStringList() << "bruteforce-chars", "Chars used to bruteforce password", "string");
|
||||
parser.addOption(bruteforceCharsOption);
|
||||
|
||||
QCommandLineOption bruteforceDictionairy(QStringList() << "bruteforce-dict", "Bruteforce dictionairy", "file");
|
||||
parser.addOption(bruteforceDictionairy);
|
||||
|
||||
auto parsed = parser.parse(argv_);
|
||||
if(!parsed) {
|
||||
qCritical() << parser.errorText();
|
||||
|
@ -92,7 +101,8 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|||
bool quiet = parser.isSet(quietModeOption);
|
||||
bool exportContacts = parser.isSet(exportContactsOption);
|
||||
bool exportTxHistory = parser.isSet(exportTxHistoryOption);
|
||||
bool cliMode = exportContacts || exportTxHistory;
|
||||
bool bruteforcePassword = parser.isSet(bruteforcePasswordOption);
|
||||
bool cliMode = exportContacts || exportTxHistory || bruteforcePassword;
|
||||
|
||||
if(cliMode) {
|
||||
QCoreApplication cli_app(argc, argv);
|
||||
|
@ -110,12 +120,15 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|||
if(exportContacts) {
|
||||
if(!quiet)
|
||||
qInfo() << "CLI mode: Address book export";
|
||||
cli->mode = CLIMode::CLIModeExportContacts;
|
||||
cli->mode = CLIMode::ExportContacts;
|
||||
QTimer::singleShot(0, cli, &CLI::run);
|
||||
} else if(exportTxHistory) {
|
||||
if(!quiet)
|
||||
qInfo() << "CLI mode: Transaction history export";
|
||||
cli->mode = CLIMode::CLIModeExportTxHistory;
|
||||
cli->mode = CLIMode::ExportTxHistory;
|
||||
QTimer::singleShot(0, cli, &CLI::run);
|
||||
} else if(bruteforcePassword) {
|
||||
cli->mode = CLIMode::BruteforcePassword;
|
||||
QTimer::singleShot(0, cli, &CLI::run);
|
||||
}
|
||||
|
||||
|
|
122
src/utils/brute.cpp
Normal file
122
src/utils/brute.cpp
Normal file
|
@ -0,0 +1,122 @@
|
|||
#include "brute.h"
|
||||
|
||||
brute::brute(const std::string &chars) {
|
||||
this->chars = chars;
|
||||
this->len = chars.length();
|
||||
}
|
||||
|
||||
std::string brute::next() {
|
||||
std::string out;
|
||||
int c = count;
|
||||
int i;
|
||||
while (c >= 0){
|
||||
i = (c % len);
|
||||
out = chars[i] + out;
|
||||
c = (c / len) - 1;
|
||||
}
|
||||
count += 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
bruteword::bruteword(const std::string &chars) {
|
||||
this->m_chars = chars;
|
||||
this->m_currentStrategy = strategy::ReplaceSingle;
|
||||
}
|
||||
|
||||
void bruteword::setWord(const std::string &word) {
|
||||
this->m_currentStrategy = strategy::ReplaceSingle;
|
||||
m_word = word;
|
||||
resetIndex();
|
||||
}
|
||||
|
||||
void bruteword::nextStrategy() {
|
||||
m_currentStrategy = static_cast<strategy>(static_cast<int>(m_currentStrategy) + 1);
|
||||
resetIndex();
|
||||
}
|
||||
|
||||
void bruteword::resetIndex() {
|
||||
iword = 0;
|
||||
ichar = 0;
|
||||
}
|
||||
|
||||
std::string bruteword::next() {
|
||||
std::string out = m_word;
|
||||
|
||||
switch(m_currentStrategy) {
|
||||
case strategy::ReplaceSingle: {
|
||||
if (iword >= m_word.length()) {
|
||||
this->nextStrategy();
|
||||
return next();
|
||||
}
|
||||
out[iword] = m_chars[ichar];
|
||||
ichar++;
|
||||
if (ichar == m_chars.length()){
|
||||
ichar = 0;
|
||||
iword++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
case strategy::AppendSingle: {
|
||||
if (ichar >= m_chars.length()) {
|
||||
this->nextStrategy();
|
||||
return next();
|
||||
}
|
||||
out += m_chars[ichar];
|
||||
ichar++;
|
||||
return out;
|
||||
}
|
||||
|
||||
case strategy::PrependSingle: {
|
||||
if (ichar >= m_chars.length()) {
|
||||
this->nextStrategy();
|
||||
return next();
|
||||
}
|
||||
out = m_chars[ichar] + out;
|
||||
ichar++;
|
||||
return out;
|
||||
}
|
||||
|
||||
case strategy::SwitchLetters: {
|
||||
if (iword+1 >= out.length()) {
|
||||
this->nextStrategy();
|
||||
return next();
|
||||
}
|
||||
char l = out[iword];
|
||||
out[iword] = out[iword+1];
|
||||
out[iword+1] = l;
|
||||
iword++;
|
||||
return out;
|
||||
}
|
||||
|
||||
case strategy::DeleteSingle: {
|
||||
if (iword >= out.length()) {
|
||||
this->nextStrategy();
|
||||
return next();
|
||||
}
|
||||
out = out.substr(0, iword) + out.substr(iword+1);
|
||||
iword++;
|
||||
return out;
|
||||
}
|
||||
|
||||
case strategy::AddSingle: {
|
||||
if (iword+1 >= out.length()) {
|
||||
this->nextStrategy();
|
||||
return next();
|
||||
}
|
||||
out = out.substr(0, iword+1) + m_chars[ichar] + out.substr(iword+1);
|
||||
ichar++;
|
||||
if (ichar == m_chars.length()) {
|
||||
ichar = 0;
|
||||
iword++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
case strategy::END: {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
47
src/utils/brute.h
Normal file
47
src/utils/brute.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
#ifndef FEATHER_BRUTE_H
|
||||
#define FEATHER_BRUTE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class brute {
|
||||
public:
|
||||
explicit brute(const std::string &chars);
|
||||
|
||||
std::string next();
|
||||
|
||||
std::string chars;
|
||||
std::string current;
|
||||
int len;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
class bruteword {
|
||||
public:
|
||||
enum strategy {
|
||||
ReplaceSingle = 0,
|
||||
AppendSingle,
|
||||
PrependSingle,
|
||||
SwitchLetters,
|
||||
DeleteSingle,
|
||||
AddSingle,
|
||||
END
|
||||
};
|
||||
|
||||
explicit bruteword(const std::string &chars);
|
||||
void nextStrategy();
|
||||
void setWord(const std::string &word);
|
||||
|
||||
std::string next();
|
||||
|
||||
private:
|
||||
void resetIndex();
|
||||
|
||||
std::string m_chars;
|
||||
std::string m_word;
|
||||
strategy m_currentStrategy;
|
||||
|
||||
int iword = 0;
|
||||
int ichar = 0;
|
||||
};
|
||||
|
||||
#endif //FEATHER_BRUTE_H
|
Loading…
Reference in a new issue