From a3e6aa787b6c3b019bf492961246b8fa2e1bb1da Mon Sep 17 00:00:00 2001 From: wh201906 Date: Tue, 11 Aug 2020 11:30:33 +0800 Subject: [PATCH 01/20] Check Access Bits when writing to selected blocks --- README.md | 2 +- module/mifare.cpp | 55 +++++++++++++++++++++++++++++++++++++++-------- module/mifare.h | 1 + 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 228b2de..0b104e3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ A GUI for [Proxmark3](https://github.com/Proxmark/proxmark3) client ## Preview ![preview](README/img/preview.png) -more previews [here](README/doc/previews.md) +[more previews](README/doc/previews.md) *** diff --git a/module/mifare.cpp b/module/mifare.cpp index 5c0f63e..a82816b 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -273,7 +273,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe { QString data; QString result; - bool isKeyBlock = (blockId < 128 && ((blockId + 1) % 4 == 0)) || ((blockId + 1) % 16 == 0); + bool isTrailerBlock = (blockId < 128 && ((blockId + 1) % 4 == 0)) || ((blockId + 1) % 16 == 0); if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) { @@ -298,7 +298,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe data.remove(" "); // when the target block is a key block and the given key type is KeyA, try to check whether the KeyB is valid(by Access Bits) // if the given key type is KeyB, it will never get the KeyA from the key block - if(isKeyBlock && keyType == KEY_A) // in this case, the Access Bits is always accessible + if(isTrailerBlock && keyType == KEY_A) // in this case, the Access Bits is always accessible { data.replace(0, 12, key); QList ACBits = data_getACBits(data.mid(12, 8)); @@ -307,7 +307,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe data.replace(20, 12, "????????????"); } } - else if(isKeyBlock && keyType == KEY_B) + else if(isTrailerBlock && keyType == KEY_B) { data.replace(20, 12, key);; data.replace(0, 12, "????????????"); // fill the keyA part with ? @@ -536,8 +536,8 @@ bool Mifare::_writeblk(int blockId, KeyType keyType, const QString& key, const Q { QString result; QString input = data.toUpper(); - input.remove(" "); + input.remove(" "); if(data_isDataValid(input) != DATA_NOSPACE) return false; @@ -601,6 +601,8 @@ void Mifare::writeSelected(TargetType targetType) { QList failedBlocks; QList selectedBlocks; + bool yes2All = false, no2All = false; + for(int i = 0; i < cardType.block_size; i++) { if(ui->MF_dataWidget->item(i, 1)->checkState() == Qt::Checked) @@ -609,6 +611,29 @@ void Mifare::writeSelected(TargetType targetType) for(int item : selectedBlocks) { bool result = false; + bool isTrailerBlock = (item < 128 && ((item + 1) % 4 == 0)) || ((item + 1) % 16 == 0); + + if(isTrailerBlock && !data_isACBitsValid(dataList->at(item).mid(12, 8))) // trailer block is invalid + { + if(!yes2All && !no2All) + { + QMessageBox::StandardButton choice = QMessageBox::information(parent, tr("Info"), + tr("The Access Bits is invalid!\nIt could make the whole sector blocked irreversibly!\nContinue to write?"), + QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll); + if(choice == QMessageBox::No) + continue; + else if(choice == QMessageBox::YesToAll) + yes2All = true; + else if(QMessageBox::NoToAll) + { + no2All = true; + continue; + } + } + else if(no2All) + continue; + } + if(targetType == TARGET_MIFARE) { result = _writeblk(item, KEY_A, keyAList->at(data_b2s(item)), dataList->at(item), TARGET_MIFARE); @@ -1192,24 +1217,36 @@ int Mifare::data_b2s(int block) return -1; } -QList Mifare::data_getACBits(const QString& text) //return empty QList if the text is invalid +bool Mifare::data_isACBitsValid(const QString& text, QList* returnHalfBytes) { QString input = text; - QList result; input.remove(" "); if(input.length() < 6) { - return result; + return false; } input = input.left(6); quint32 val = input.toUInt(nullptr, 16); - quint8 halfBytes[6]; + QList halfBytes; for(int i = 0; i < 6; i++) { - halfBytes[i] = (val >> ((5 - i) * 4)) & 0xf; + halfBytes.append((val >> ((5 - i) * 4)) & 0xf); } qDebug() << val; if((~halfBytes[0] & 0xf) == halfBytes[5] && (~halfBytes[1] & 0xf) == halfBytes[2] && (~halfBytes[3] & 0xf) == halfBytes[4]) + { + if(returnHalfBytes != nullptr) + *returnHalfBytes = halfBytes; + return true; + } + else + return false; +} + +QList Mifare::data_getACBits(const QString& text) //return empty QList if the text is invalid +{ + QList halfBytes, result; + if(data_isACBitsValid(text, &halfBytes)) { for(int i = 0; i < 4; i++) { diff --git a/module/mifare.h b/module/mifare.h index 7865755..b328314 100644 --- a/module/mifare.h +++ b/module/mifare.h @@ -108,6 +108,7 @@ public: static QList data_getACBits(const QString &text); static int data_b2s(int block); + static bool data_isACBitsValid(const QString &text, QList *returnHalfBytes = nullptr); public slots: signals: From a7985c5c895b69aeb926c3991507d0498de60164 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Tue, 11 Aug 2020 11:51:27 +0800 Subject: [PATCH 02/20] Stop the running command after disconnected --- common/util.cpp | 10 +++++++++- common/util.h | 2 ++ ui/mainwindow.cpp | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/common/util.cpp b/common/util.cpp index 3d73cc3..707dfe7 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -23,11 +23,14 @@ void Util::processOutput(QString output) void Util::execCMD(QString cmd) { qDebug() << cmd; - emit write(cmd + "\r\n"); + if(isRunning) + emit write(cmd + "\r\n"); } QString Util::execCMDWithOutput(QString cmd, unsigned long waitTime) { + if(!isRunning) + return ""; QTime currTime = QTime::currentTime(); QTime targetTime = QTime::currentTime().addMSecs(waitTime); isRequiringOutput = true; @@ -61,3 +64,8 @@ void Util::setClientType(Util::ClientType clientType) { this->clientType = clientType; } + +void Util::setRunningState(bool st) +{ + this->isRunning = st; +} diff --git a/common/util.h b/common/util.h index adc9291..b2ca006 100644 --- a/common/util.h +++ b/common/util.h @@ -31,9 +31,11 @@ public: public slots: void processOutput(QString output); void setClientType(Util::ClientType clientType); + void setRunningState(bool st); private: bool isRequiringOutput; + bool isRunning; QString* requiredOutput; QTime timeStamp; ClientType clientType; diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 2b30c0f..8fbbd8a 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -875,6 +875,7 @@ void MainWindow::signalInit() connect(this, &MainWindow::connectPM3, pm3, &PM3Process::connectPM3); connect(pm3, &PM3Process::PM3StatedChanged, this, &MainWindow::onPM3StateChanged); + connect(pm3, &PM3Process::PM3StatedChanged, util, &Util::setRunningState); connect(this, &MainWindow::killPM3, pm3, &PM3Process::kill); connect(util, &Util::write, pm3, &PM3Process::write); From 862f0775f8e9418b5ec3757b921b6020594c1b73 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Wed, 12 Aug 2020 12:30:15 +0800 Subject: [PATCH 03/20] Shorten the waitTime for execCMDWithOutput() --- common/util.cpp | 22 +++++++++++++++++++--- common/util.h | 24 +++++++++++++++++++++++- module/mifare.cpp | 7 ++++--- ui/mainwindow.cpp | 15 ++++++++++++--- ui/mainwindow.h | 3 +++ 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/common/util.cpp b/common/util.cpp index 707dfe7..bb4f3ab 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -27,22 +27,38 @@ void Util::execCMD(QString cmd) emit write(cmd + "\r\n"); } -QString Util::execCMDWithOutput(QString cmd, unsigned long waitTime) +QString Util::execCMDWithOutput(QString cmd, ReturnTrigger trigger) { + bool isResultFound = false; + QRegularExpression re; + re.setPatternOptions(QRegularExpression::DotMatchesEverythingOption); + if(!isRunning) return ""; QTime currTime = QTime::currentTime(); - QTime targetTime = QTime::currentTime().addMSecs(waitTime); + QTime targetTime = QTime::currentTime().addMSecs(trigger.waitTime); isRequiringOutput = true; requiredOutput->clear(); execCMD(cmd); while(QTime::currentTime() < targetTime) { QApplication::processEvents(); + for(QString otpt : trigger.expectedOutputs) + { + re.setPattern(otpt); + isResultFound = re.match(*requiredOutput).hasMatch(); + if(requiredOutput->contains(otpt)) + break; + } + if(isResultFound) + { + delay(200); + break; + } if(timeStamp > currTime) { currTime = timeStamp; - targetTime = timeStamp.addMSecs(waitTime); + targetTime = timeStamp.addMSecs(trigger.waitTime); } } isRequiringOutput = false; diff --git a/common/util.h b/common/util.h index b2ca006..bbf935a 100644 --- a/common/util.h +++ b/common/util.h @@ -9,6 +9,7 @@ #include #include #include +#include class Util : public QObject { @@ -20,12 +21,33 @@ public: CLIENTTYPE_ICEMAN, }; + struct ReturnTrigger + { + unsigned long waitTime; + QStringList expectedOutputs; + ReturnTrigger(unsigned long time) + { + waitTime = time; + expectedOutputs = QStringList(); + } + ReturnTrigger(QStringList outputs) + { + waitTime = 10000; + expectedOutputs = outputs; + } + ReturnTrigger(unsigned long time, QStringList outputs) + { + waitTime = time; + expectedOutputs = outputs; + } + }; + Q_ENUM(Util::ClientType) explicit Util(QObject *parent = nullptr); void execCMD(QString cmd); - QString execCMDWithOutput(QString cmd, unsigned long waitTime = 2000); + QString execCMDWithOutput(QString cmd, ReturnTrigger trigger = 10000); void delay(unsigned int msec); ClientType getClientType(); public slots: diff --git a/module/mifare.cpp b/module/mifare.cpp index a82816b..92d377e 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -119,7 +119,7 @@ void Mifare::chk() "hf mf chk *" + QString::number(cardType.type) + " ?", - 1000 + cardType.type * 1000); + Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|----------------\\|"})); qDebug() << result; int offset = 0; @@ -183,7 +183,8 @@ void Mifare::nested() result = util->execCMDWithOutput( "hf mf nested " + QString::number(cardType.type) - + " *", 10000); + + " *", + Util::ReturnTrigger(10000, {"Can't found", "\\|000\\|"})); } else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) { @@ -624,7 +625,7 @@ void Mifare::writeSelected(TargetType targetType) continue; else if(choice == QMessageBox::YesToAll) yes2All = true; - else if(QMessageBox::NoToAll) + else if(choice == QMessageBox::NoToAll) { no2All = true; continue; diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 8fbbd8a..aac66f5 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -125,6 +125,10 @@ void MainWindow::refreshCMD(const QString& cmd) ui->Raw_CMDHistoryWidget->addItem(cmd); } +void MainWindow::on_stopButton_clicked() +{ + +} // ********************************************************* // ******************** raw command ******************** @@ -798,12 +802,15 @@ void MainWindow::uiInit() connectStatusBar = new QLabel(this); programStatusBar = new QLabel(this); PM3VersionBar = new QLabel(this); + stopButton = new QPushButton(this); setStatusBar(connectStatusBar, tr("Not Connected")); setStatusBar(programStatusBar, tr("Idle")); setStatusBar(PM3VersionBar, ""); + stopButton->setText(tr("Stop")); ui->statusbar->addPermanentWidget(PM3VersionBar, 1); ui->statusbar->addPermanentWidget(connectStatusBar, 1); ui->statusbar->addPermanentWidget(programStatusBar, 1); + ui->statusbar->addPermanentWidget(stopButton); ui->MF_dataWidget->setColumnCount(3); ui->MF_dataWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Sec"))); @@ -812,7 +819,7 @@ void MainWindow::uiInit() ui->MF_dataWidget->verticalHeader()->setVisible(false); ui->MF_dataWidget->setColumnWidth(0, 55); ui->MF_dataWidget->setColumnWidth(1, 55); - ui->MF_dataWidget->setColumnWidth(2, 430); + ui->MF_dataWidget->setColumnWidth(2, 450); ui->MF_keyWidget->setColumnCount(3); ui->MF_keyWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Sec"))); @@ -820,8 +827,8 @@ void MainWindow::uiInit() ui->MF_keyWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("KeyB"))); ui->MF_keyWidget->verticalHeader()->setVisible(false); ui->MF_keyWidget->setColumnWidth(0, 35); - ui->MF_keyWidget->setColumnWidth(1, 115); - ui->MF_keyWidget->setColumnWidth(2, 115); + ui->MF_keyWidget->setColumnWidth(1, 125); + ui->MF_keyWidget->setColumnWidth(2, 125); MF_widgetReset(); typeBtnGroup = new QButtonGroup(this); @@ -887,6 +894,8 @@ void MainWindow::signalInit() connect(ui->MF_UIDGroupBox, &QGroupBox::clicked, this, &MainWindow::on_GroupBox_clicked); connect(ui->MF_simGroupBox, &QGroupBox::clicked, this, &MainWindow::on_GroupBox_clicked); connect(ui->MF_sniffGroupBox, &QGroupBox::clicked, this, &MainWindow::on_GroupBox_clicked); + + connect(stopButton, &QPushButton::clicked, this, &MainWindow::on_stopButton_clicked); } void MainWindow::setStatusBar(QLabel * target, const QString & text) diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 873f6d4..55c8c6f 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "common/pm3process.h" #include "module/mifare.h" @@ -148,12 +149,14 @@ private slots: void on_MF_selectTrailerBox_stateChanged(int arg1); + void on_stopButton_clicked(); private: Ui::MainWindow* ui; QButtonGroup* typeBtnGroup; QLabel* connectStatusBar; QLabel* programStatusBar; QLabel* PM3VersionBar; + QPushButton* stopButton; QAction* myInfo; QAction* checkUpdate; QSettings* settings; From f2d00ee0888c9c8e9cee3aabd049e5f92c14a103 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Thu, 13 Aug 2020 09:30:47 +0800 Subject: [PATCH 04/20] Replace QString with const QString& --- common/pm3process.cpp | 3 +-- common/pm3process.h | 8 ++++---- common/util.cpp | 6 +++--- common/util.h | 12 ++++++------ module/mifare.cpp | 16 ++++++++-------- module/mifare.h | 10 +++++----- ui/mainwindow.cpp | 8 ++++---- ui/mainwindow.h | 16 ++++++++-------- ui/mf_attack_hardnesteddialog.h | 5 +++-- ui/mf_sim_simdialog.h | 2 +- ui/mf_trailerdecoderdialog.h | 4 ++-- ui/mf_uid_parameterdialog.h | 5 +++-- 12 files changed, 48 insertions(+), 47 deletions(-) diff --git a/common/pm3process.cpp b/common/pm3process.cpp index 08aef5a..8181353 100644 --- a/common/pm3process.cpp +++ b/common/pm3process.cpp @@ -14,7 +14,7 @@ PM3Process::PM3Process(QThread* thread, QObject* parent): QProcess(parent) connect(this, &PM3Process::readyRead, this, &PM3Process::onReadyRead); } -void PM3Process::connectPM3(const QString path, const QString port) +void PM3Process::connectPM3(const QString& path, const QString& port) { QString result; Util::ClientType clientType = Util::CLIENTTYPE_OFFICIAL; @@ -93,7 +93,6 @@ void PM3Process::testThread() qDebug() << "PM3:" << QThread::currentThread(); } - qint64 PM3Process::write(QString data) { return QProcess::write(data.toLatin1()); diff --git a/common/pm3process.h b/common/pm3process.h index 093ebfd..749c501 100644 --- a/common/pm3process.h +++ b/common/pm3process.h @@ -21,8 +21,8 @@ public: void testThread(); public slots: - void connectPM3(const QString path, const QString port); - void setSerialListener(const QString &name, bool state); + void connectPM3(const QString& path, const QString& port); + void setSerialListener(const QString& name, bool state); qint64 write(QString data); private slots: void onTimeout(); @@ -34,8 +34,8 @@ private: QTimer* serialListener; QSerialPortInfo* portInfo; signals: - void PM3StatedChanged(bool st, QString info = ""); - void newOutput(QString output); + void PM3StatedChanged(bool st, const QString& info = ""); + void newOutput(const QString& output); void changeClientType(Util::ClientType); }; diff --git a/common/util.cpp b/common/util.cpp index bb4f3ab..1da8256 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -9,7 +9,7 @@ Util::Util(QObject *parent) : QObject(parent) qRegisterMetaType("Util::ClientType"); } -void Util::processOutput(QString output) +void Util::processOutput(const QString& output) { // qDebug() << "Util::processOutput:" << output; if(isRequiringOutput) @@ -20,14 +20,14 @@ void Util::processOutput(QString output) emit refreshOutput(output); } -void Util::execCMD(QString cmd) +void Util::execCMD(const QString& cmd) { qDebug() << cmd; if(isRunning) emit write(cmd + "\r\n"); } -QString Util::execCMDWithOutput(QString cmd, ReturnTrigger trigger) +QString Util::execCMDWithOutput(const QString& cmd, ReturnTrigger trigger) { bool isResultFound = false; QRegularExpression re; diff --git a/common/util.h b/common/util.h index bbf935a..04a4ec0 100644 --- a/common/util.h +++ b/common/util.h @@ -30,12 +30,12 @@ public: waitTime = time; expectedOutputs = QStringList(); } - ReturnTrigger(QStringList outputs) + ReturnTrigger(const QStringList& outputs) { waitTime = 10000; expectedOutputs = outputs; } - ReturnTrigger(unsigned long time, QStringList outputs) + ReturnTrigger(unsigned long time, const QStringList& outputs) { waitTime = time; expectedOutputs = outputs; @@ -46,12 +46,12 @@ public: explicit Util(QObject *parent = nullptr); - void execCMD(QString cmd); - QString execCMDWithOutput(QString cmd, ReturnTrigger trigger = 10000); + void execCMD(const QString& cmd); + QString execCMDWithOutput(const QString& cmd, ReturnTrigger trigger = 10000); void delay(unsigned int msec); ClientType getClientType(); public slots: - void processOutput(QString output); + void processOutput(const QString& output); void setClientType(Util::ClientType clientType); void setRunningState(bool st); @@ -63,7 +63,7 @@ private: ClientType clientType; signals: void refreshOutput(const QString& output); - void write(QString data); + void write(QString data); // connected to PM3Process::write(QString data); }; #endif // UTIL_H diff --git a/module/mifare.cpp b/module/mifare.cpp index 92d377e..b07593e 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -872,7 +872,7 @@ void Mifare::data_clearKey(bool clearAll) } } -bool Mifare::data_isKeyValid(const QString &key) +bool Mifare::data_isKeyValid(const QString& key) { if(key.length() != 12) return false; @@ -938,7 +938,7 @@ void Mifare::setCardType(int type) } } -bool Mifare::data_loadDataFile(const QString &filename) +bool Mifare::data_loadDataFile(const QString& filename) { QFile file(filename, this); if(file.open(QIODevice::ReadOnly)) @@ -985,7 +985,7 @@ bool Mifare::data_loadDataFile(const QString &filename) } } -bool Mifare::data_loadKeyFile(const QString &filename) +bool Mifare::data_loadKeyFile(const QString& filename) { QFile file(filename, this); if(file.open(QIODevice::ReadOnly)) @@ -1022,7 +1022,7 @@ bool Mifare::data_loadKeyFile(const QString &filename) } } -QString Mifare::bin2text(const QByteArray &buff, int i, int length) +QString Mifare::bin2text(const QByteArray& buff, int i, int length) { QString ret = ""; char LByte, RByte; @@ -1040,7 +1040,7 @@ QString Mifare::bin2text(const QByteArray &buff, int i, int length) return ret; } -bool Mifare::data_saveDataFile(const QString &filename, bool isBin) +bool Mifare::data_saveDataFile(const QString& filename, bool isBin) { QFile file(filename, this); if(file.open(QIODevice::WriteOnly)) @@ -1084,7 +1084,7 @@ bool Mifare::data_saveDataFile(const QString &filename, bool isBin) } } -bool Mifare::data_saveKeyFile(const QString &filename, bool isBin) +bool Mifare::data_saveKeyFile(const QString& filename, bool isBin) { QFile file(filename, this); if(file.open(QIODevice::WriteOnly)) @@ -1179,12 +1179,12 @@ void Mifare::data_data2Key() } } -void Mifare::data_setData(int block, const QString &data) +void Mifare::data_setData(int block, const QString& data) { dataList->replace(block, data); } -void Mifare::data_setKey(int sector, KeyType keyType, const QString &key) +void Mifare::data_setKey(int sector, KeyType keyType, const QString& key) { if(keyType == KEY_A) keyAList->replace(sector, key); diff --git a/module/mifare.h b/module/mifare.h index b328314..dfde298 100644 --- a/module/mifare.h +++ b/module/mifare.h @@ -106,9 +106,9 @@ public: void saveSniff(const QString& file); void data_fillKeys(); - static QList data_getACBits(const QString &text); + static QList data_getACBits(const QString& text); static int data_b2s(int block); - static bool data_isACBitsValid(const QString &text, QList *returnHalfBytes = nullptr); + static bool data_isACBitsValid(const QString& text, QList *returnHalfBytes = nullptr); public slots: signals: @@ -125,9 +125,9 @@ private: QRegularExpression* keyPattern; QString bin2text(const QByteArray& buff, int start, int length); - QString _readblk(int blockId, KeyType keyType, const QString &key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); - QStringList _readsec(int sectorId, KeyType keyType, const QString &key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); - bool _writeblk(int blockId, KeyType keyType, const QString &key, const QString &data, TargetType targetType = TARGET_MIFARE, int waitTime = 300); + QString _readblk(int blockId, KeyType keyType, const QString& key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); + QStringList _readsec(int sectorId, KeyType keyType, const QString& key, TargetType targetType = TARGET_MIFARE, int waitTime = 300); + bool _writeblk(int blockId, KeyType keyType, const QString& key, const QString& data, TargetType targetType = TARGET_MIFARE, int waitTime = 300); }; #endif // MIFARE_H diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index aac66f5..421a019 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -86,7 +86,7 @@ void MainWindow::on_PM3_connectButton_clicked() } } -void MainWindow::onPM3StateChanged(bool st, QString info) +void MainWindow::onPM3StateChanged(bool st, const QString& info) { pm3state = st; setState(st); @@ -898,7 +898,7 @@ void MainWindow::signalInit() connect(stopButton, &QPushButton::clicked, this, &MainWindow::on_stopButton_clicked); } -void MainWindow::setStatusBar(QLabel * target, const QString & text) +void MainWindow::setStatusBar(QLabel * target, const QString& text) { if(target == PM3VersionBar) target->setText(tr("HW Version:") + text); @@ -908,7 +908,7 @@ void MainWindow::setStatusBar(QLabel * target, const QString & text) target->setText(tr("State:") + text); } -void MainWindow::setTableItem(QTableWidget * widget, int row, int column, const QString & text) +void MainWindow::setTableItem(QTableWidget * widget, int row, int column, const QString& text) { if(widget->item(row, column) == nullptr) widget->setItem(row, column, new QTableWidgetItem()); @@ -993,7 +993,7 @@ void MainWindow::on_GroupBox_clicked(bool checked) settings->endGroup(); } -void MainWindow::saveClientPath(const QString & path) +void MainWindow::saveClientPath(const QString& path) { settings->beginGroup("Client_Path"); settings->setValue("path", path); diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 55c8c6f..2c7fcd5 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -44,10 +44,10 @@ public: void initUI(); bool eventFilter(QObject *watched, QEvent *event); public slots: - void refreshOutput(const QString &output); - void refreshCMD(const QString &cmd); - void setStatusBar(QLabel* target, const QString & text); - void onPM3StateChanged(bool st, QString info); + void refreshOutput(const QString& output); + void refreshCMD(const QString& cmd); + void setStatusBar(QLabel* target, const QString& text); + void onPM3StateChanged(bool st, const QString& info); void MF_onTypeChanged(int id, bool st); private slots: @@ -175,12 +175,12 @@ private: void signalInit(); void MF_widgetReset(); - void setTableItem(QTableWidget *widget, int row, int column, const QString &text); + void setTableItem(QTableWidget *widget, int row, int column, const QString& text); void setState(bool st); - void saveClientPath(const QString &path); + void saveClientPath(const QString& path); signals: - void connectPM3(const QString path, const QString port); + void connectPM3(const QString& path, const QString& port); void killPM3(); - void setSerialListener(const QString &name, bool state); + void setSerialListener(const QString& name, bool state); }; #endif // MAINWINDOW_H diff --git a/ui/mf_attack_hardnesteddialog.h b/ui/mf_attack_hardnesteddialog.h index 0f13bf7..d120b2a 100644 --- a/ui/mf_attack_hardnesteddialog.h +++ b/ui/mf_attack_hardnesteddialog.h @@ -3,7 +3,8 @@ #include -namespace Ui { +namespace Ui +{ class MF_Attack_hardnestedDialog; } @@ -19,7 +20,7 @@ public: private: Ui::MF_Attack_hardnestedDialog *ui; signals: - void sendCMD(QString cmd); + void sendCMD(const QString& cmd); private slots: void on_buttonBox_accepted(); }; diff --git a/ui/mf_sim_simdialog.h b/ui/mf_sim_simdialog.h index 7fa53a1..49c9ba2 100644 --- a/ui/mf_sim_simdialog.h +++ b/ui/mf_sim_simdialog.h @@ -26,7 +26,7 @@ private: Ui::MF_Sim_simDialog *ui; int cardType; signals: - void sendCMD(QString cmd); + void sendCMD(const QString& cmd); private slots: void on_buttonBox_accepted(); }; diff --git a/ui/mf_trailerdecoderdialog.h b/ui/mf_trailerdecoderdialog.h index 93a6abd..e863f1c 100644 --- a/ui/mf_trailerdecoderdialog.h +++ b/ui/mf_trailerdecoderdialog.h @@ -23,11 +23,11 @@ public: private slots: - void on_accessBitsEdit_textChanged(const QString &arg1); + void on_accessBitsEdit_textChanged(const QString& arg1); void on_blockSizeChanged(int id, bool st); - void on_boxChanged(const QString &arg1); + void on_boxChanged(const QString& arg1); private: Ui::MF_trailerDecoderDialog *ui; QRegularExpressionValidator* validator; diff --git a/ui/mf_uid_parameterdialog.h b/ui/mf_uid_parameterdialog.h index ec580cb..85e9fc0 100644 --- a/ui/mf_uid_parameterdialog.h +++ b/ui/mf_uid_parameterdialog.h @@ -3,7 +3,8 @@ #include -namespace Ui { +namespace Ui +{ class MF_UID_parameterDialog; } @@ -18,7 +19,7 @@ public: private: Ui::MF_UID_parameterDialog *ui; signals: - void sendCMD(QString cmd); + void sendCMD(const QString& cmd); private slots: void on_buttonBox_accepted(); }; From 2f38d3c8c54e8275f8f5d4bd23c33a50c0e8b537 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Thu, 13 Aug 2020 16:39:04 +0800 Subject: [PATCH 05/20] Support choose history command by Key_Up and Key_Down --- Proxmark3GUI.pro | 2 ++ common/myeventfilter.cpp | 13 +++++++++++++ common/myeventfilter.h | 22 ++++++++++++++++++++++ ui/mainwindow.cpp | 38 ++++++++++++++++++++++++++++++++++++++ ui/mainwindow.h | 8 ++++++++ ui/mainwindow.ui | 5 ++++- 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 common/myeventfilter.cpp create mode 100644 common/myeventfilter.h diff --git a/Proxmark3GUI.pro b/Proxmark3GUI.pro index 85feb73..08abf01 100644 --- a/Proxmark3GUI.pro +++ b/Proxmark3GUI.pro @@ -16,6 +16,7 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + common/myeventfilter.cpp \ main.cpp \ common/pm3process.cpp \ common/util.cpp \ @@ -27,6 +28,7 @@ SOURCES += \ ui/mf_attack_hardnesteddialog.cpp \ HEADERS += \ + common/myeventfilter.h \ common/pm3process.h \ common/util.h \ module/mifare.h \ diff --git a/common/myeventfilter.cpp b/common/myeventfilter.cpp new file mode 100644 index 0000000..b717ca3 --- /dev/null +++ b/common/myeventfilter.cpp @@ -0,0 +1,13 @@ +#include "myeventfilter.h" + +MyEventFilter::MyEventFilter(QEvent::Type filter) +{ + targetEventType = filter; +} + +bool MyEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if(event->type() == targetEventType) + emit eventHappened(obj, *event); + return QObject::eventFilter(obj, event); +} diff --git a/common/myeventfilter.h b/common/myeventfilter.h new file mode 100644 index 0000000..3ab624a --- /dev/null +++ b/common/myeventfilter.h @@ -0,0 +1,22 @@ +#ifndef MYEVENTFILTER_H +#define MYEVENTFILTER_H + +#include +#include + +class MyEventFilter : public QObject +{ + Q_OBJECT + +public: + explicit MyEventFilter(QEvent::Type filter); +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +signals: + void eventHappened(QObject* obj_addr, QEvent& event); +private: + QEvent::Type targetEventType; +}; + +#endif // MYEVENTFILTER_H diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 421a019..f3dbaac 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -29,6 +29,7 @@ MainWindow::MainWindow(QWidget *parent): util = new Util(this); mifare = new Mifare(ui, util, this); + keyEventFilter = new MyEventFilter(QEvent::KeyRelease); } MainWindow::~MainWindow() @@ -177,6 +178,36 @@ void MainWindow::sendMSG() // send command when pressing Enter on_Raw_sendCMDButton_clicked(); } +void MainWindow::on_Raw_CMDEdit_keyPressed(QObject* obj_addr, QEvent& event) +{ + if(obj_addr == ui->Raw_CMDEdit && event.type() == QEvent::KeyRelease) + { + QKeyEvent& keyEvent = static_cast(event); + if(keyEvent.key() == Qt::Key_Up) + { + if(stashedIndex > 0) + stashedIndex--; + else if(stashedIndex == -1) + stashedIndex = ui->Raw_CMDHistoryWidget->count() - 1; + } + else if(keyEvent.key() == Qt::Key_Down) + { + if(stashedIndex < ui->Raw_CMDHistoryWidget->count() - 1 && stashedIndex != -1) + stashedIndex++; + else if(stashedIndex == ui->Raw_CMDHistoryWidget->count() - 1) + stashedIndex = -1; + } + if(keyEvent.key() == Qt::Key_Up || keyEvent.key() == Qt::Key_Down) + { + ui->Raw_CMDEdit->blockSignals(true); + if(stashedIndex == -1) + ui->Raw_CMDEdit->setText(stashedCMDEditText); + else + ui->Raw_CMDEdit->setText(ui->Raw_CMDHistoryWidget->item(stashedIndex)->text()); + ui->Raw_CMDEdit->blockSignals(false); + } + } +} // ***************************************************** // ******************** mifare ******************** @@ -798,6 +829,8 @@ void MainWindow::MF_widgetReset() void MainWindow::uiInit() { connect(ui->Raw_CMDEdit, &QLineEdit::editingFinished, this, &MainWindow::sendMSG); + ui->Raw_CMDEdit->installEventFilter(keyEventFilter); + connect(keyEventFilter, &MyEventFilter::eventHappened, this, &MainWindow::on_Raw_CMDEdit_keyPressed); connectStatusBar = new QLabel(this); programStatusBar = new QLabel(this); @@ -1000,3 +1033,8 @@ void MainWindow::saveClientPath(const QString& path) settings->endGroup(); } // *********************************************** + +void MainWindow::on_Raw_CMDEdit_textChanged(const QString &arg1) +{ + stashedCMDEditText = arg1; +} diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 2c7fcd5..91daafa 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -21,6 +21,7 @@ #include #include +#include "common/myeventfilter.h" #include "common/pm3process.h" #include "module/mifare.h" #include "common/util.h" @@ -49,6 +50,7 @@ public slots: void setStatusBar(QLabel* target, const QString& text); void onPM3StateChanged(bool st, const QString& info); void MF_onTypeChanged(int id, bool st); + void on_Raw_CMDEdit_keyPressed(QObject *obj_addr, QEvent &event); private slots: void on_PM3_connectButton_clicked(); @@ -150,6 +152,8 @@ private slots: void on_MF_selectTrailerBox_stateChanged(int arg1); void on_stopButton_clicked(); + void on_Raw_CMDEdit_textChanged(const QString &arg1); + private: Ui::MainWindow* ui; QButtonGroup* typeBtnGroup; @@ -160,6 +164,10 @@ private: QAction* myInfo; QAction* checkUpdate; QSettings* settings; + MyEventFilter* keyEventFilter; + + QString stashedCMDEditText; + int stashedIndex = -1; void uiInit(); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index edd93f4..3279bd5 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -120,7 +120,7 @@ - 0 + 1 @@ -1198,6 +1198,9 @@ 0 + + Qt::NoFocus + Qt::ScrollBarAlwaysOn From 73533608e6470b498f55fc4394e8d15dbc25ab9c Mon Sep 17 00:00:00 2001 From: wh201906 Date: Fri, 14 Aug 2020 00:46:07 +0800 Subject: [PATCH 06/20] Add some useless UI --- ui/mainwindow.cpp | 19 +++-- ui/mainwindow.ui | 213 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+), 7 deletions(-) diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index f3dbaac..6ad89ba 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -119,13 +119,6 @@ void MainWindow::refreshOutput(const QString& output) ui->Raw_outputEdit->moveCursor(QTextCursor::End); } -void MainWindow::refreshCMD(const QString& cmd) -{ - ui->Raw_CMDEdit->setText(cmd); - if(cmd != "" && (ui->Raw_CMDHistoryWidget->count() == 0 || ui->Raw_CMDHistoryWidget->item(ui->Raw_CMDHistoryWidget->count() - 1)->text() != cmd)) - ui->Raw_CMDHistoryWidget->addItem(cmd); -} - void MainWindow::on_stopButton_clicked() { @@ -178,6 +171,18 @@ void MainWindow::sendMSG() // send command when pressing Enter on_Raw_sendCMDButton_clicked(); } + +void MainWindow::refreshCMD(const QString& cmd) +{ + ui->Raw_CMDEdit->blockSignals(true); + ui->Raw_CMDEdit->setText(cmd); + if(cmd != "" && (ui->Raw_CMDHistoryWidget->count() == 0 || ui->Raw_CMDHistoryWidget->item(ui->Raw_CMDHistoryWidget->count() - 1)->text() != cmd)) + ui->Raw_CMDHistoryWidget->addItem(cmd); + stashedCMDEditText = cmd; + stashedIndex = -1; + ui->Raw_CMDEdit->blockSignals(false); +} + void MainWindow::on_Raw_CMDEdit_keyPressed(QObject* obj_addr, QEvent& event) { if(obj_addr == ui->Raw_CMDEdit && event.type() == QEvent::KeyRelease) diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 3279bd5..5207ec6 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -1168,6 +1168,219 @@ + + + LF/Data + + + + + 30 + 30 + 121 + 211 + + + + LF Config + + + + 2 + + + 2 + + + 5 + + + 2 + + + 2 + + + + + Frequency + + + + 5 + + + 2 + + + 5 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + 125k + + + + + + + + 0 + 0 + + + + 134k + + + + + + + + + + + + BitRate: + + + + + + + Decimation: + + + + + + + + + + Averaging: + + + + + + + + + + + + + + Threshold: + + + + + + + + + + Skips: + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + Get + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + Set + + + + + + + + + + + T55xx + + + + + 20 + 10 + 256 + 192 + + + + RawCommand From ce262db52c300ce65efb970a93e38e3f6b496852 Mon Sep 17 00:00:00 2001 From: Ruling Date: Tue, 27 Oct 2020 11:31:33 +0800 Subject: [PATCH 07/20] Update zh_CN.ts --- lang/zh_CN.ts | 84 +++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index 316cf77..79786fd 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -125,7 +125,7 @@ UID: - + 卡号: @@ -164,7 +164,7 @@ Trailer Data: (like "FF0780" or "FF 07 80") - 输入Access Bits + 输入控制位数据 (形如“FF0780”或“FF 07 80”) @@ -195,22 +195,22 @@ Data Block Permission: - 数据Block访问权限: + 块数据访问权限: Block0 - + 块0 Block1 - + 块1 Block2 - + 块2 @@ -242,17 +242,17 @@ KeyA - + A密钥 Access Bits - Access Bits + 控制位 KeyB - + B密钥 @@ -321,7 +321,7 @@ It could make the whole sector blocked irreversibly! Card Type - 卡类型 + 卡片类型 @@ -368,12 +368,12 @@ It could make the whole sector blocked irreversibly! Data - + 数据 Key - + 密钥 @@ -383,7 +383,7 @@ It could make the whole sector blocked irreversibly! Card Info - 读卡片信息 + 读卡信息 @@ -408,17 +408,17 @@ It could make the whole sector blocked irreversibly! Block: - + 块: Key: - + 密钥: Key Type: - Key类型: + 密钥类型: @@ -433,7 +433,7 @@ It could make the whole sector blocked irreversibly! Data: - + 数据: @@ -443,12 +443,12 @@ It could make the whole sector blocked irreversibly! Dump - Dump命令 + 导出Dump Restore - Restore命令 + 初始化卡 @@ -474,7 +474,7 @@ It could make the whole sector blocked irreversibly! Wipe - 擦除 + 清除 @@ -611,7 +611,7 @@ It could make the whole sector blocked irreversibly! Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) - 二进制Data文件(*.bin *.dump);;文本Data文件(*.txt *.eml);;所有文件(*.*) + 二进制数据文件(*.bin *.dump);;文本数据文件(*.txt *.eml);;所有文件(*.*) @@ -638,43 +638,43 @@ It could make the whole sector blocked irreversibly! Plz select the font of data widget and key widget - 请选择Data窗口和Key窗口的字体 + 请选择数据窗口和密钥窗口的字体 Data must consists of 32 Hex symbols(Whitespace is allowed) - Data必须由32个十六进制字符组成(中间可含有空格) + 数据必须由32个十六进制字符组成(中间可含有空格) Key must consists of 12 Hex symbols(Whitespace is allowed) - Key必须由12个十六进制字符组成(中间可含有空格) + 密钥必须由12个十六进制字符组成(中间可含有空格) Plz select the data file: - 请选择data文件: + 请选择数据文件: Plz select the key file: - 请选择key文件: + 请选择密钥文件: Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) - 二进制Key文件(*.bin *.dump)二进制Data文件(*.bin *.dump);;所有文件(*.*) + 二进制密钥文件(*.bin *.dump)二进制密钥文件(*.bin *.dump);所有文件(*.*) Plz select the location to save data file: - 请选择文件保存的位置: + 请选择数据文件保存的位置: Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) - 二进制Data文件(*.bin *.dump);;文本Data文件(*.txt *.eml) + 二进制数据文件(*.bin *.dump);文本数据文件(*.txt *.eml) @@ -686,27 +686,27 @@ It could make the whole sector blocked irreversibly! Plz select the location to save key file: - 请选择key文件保存的位置: + 请选择密钥文件保存的位置: Binary Key Files(*.bin *.dump) - 二进制Key文件(*.bin *.dump) + 二进制密码文件(*.bin *.dump) Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. - 普通Mifare卡的Block0无法写入,UID也不能更改 + 普通Mifare卡的块0无法写入,卡号也不能更改 Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. - UID卡(在国外叫Chinese Magic Card)的Block0可写,UID可变 + UID卡(在国外叫中国魔术卡)的块0可写,卡号可变 There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. - 国外把UID卡分为Chinese Magic Card Gen1和Gen2 + 国外把UID卡分为中国魔术卡 Gen1和Gen2 @@ -741,7 +741,7 @@ It could make the whole sector blocked irreversibly! the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. - 可通过普通的写块命令来写Block0,可重复擦写 + 可通过普通的写块命令来写块0,可重复擦写 @@ -756,7 +756,7 @@ It could make the whole sector blocked irreversibly! you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). - Block0只能写入一次 + 块0只能写入一次 @@ -771,7 +771,7 @@ It could make the whole sector blocked irreversibly! It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). - 锁卡前和普通UID/CUID卡一样可以反复读写Block0,用特殊命令锁卡后就和FUID卡一样了 + 锁卡前和普通UID/CUID卡一样可以反复读写块0,用特殊命令锁卡后就和FUID卡一样了 @@ -808,22 +808,22 @@ It could make the whole sector blocked irreversibly! Sec - + 扇区 Blk - + KeyA - + A密钥 KeyB - + B密钥 @@ -843,7 +843,7 @@ It could make the whole sector blocked irreversibly! Running - 运行中 + 正在运行 From 986e9aacfe792d090bac32630104e319c935e470 Mon Sep 17 00:00:00 2001 From: Ruling Date: Fri, 30 Oct 2020 12:58:11 +0800 Subject: [PATCH 08/20] Correct some Chinese Translation Correct some Chinese Translation --- lang/zh_CN.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index 79786fd..dd84ab2 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -448,7 +448,7 @@ It could make the whole sector blocked irreversibly! Restore - 初始化卡 + 一键写卡 From 0c4220c9a5dbcf2f59d681c9768a94fa465c1626 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Sat, 31 Oct 2020 18:19:06 +0800 Subject: [PATCH 09/20] Change some translation --- lang/zh_CN.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index dd84ab2..0990cac 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -195,7 +195,7 @@ Data Block Permission: - 块数据访问权限: + 数据块访问权限: @@ -242,7 +242,7 @@ KeyA - A密钥 + 密钥A @@ -252,7 +252,7 @@ KeyB - B密钥 + 密钥B @@ -443,12 +443,12 @@ It could make the whole sector blocked irreversibly! Dump - 导出Dump + Dump命令 Restore - 一键写卡 + Restore命令 @@ -474,7 +474,7 @@ It could make the whole sector blocked irreversibly! Wipe - 清除 + 擦除 @@ -701,12 +701,12 @@ It could make the whole sector blocked irreversibly! Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. - UID卡(在国外叫中国魔术卡)的块0可写,卡号可变 + UID卡(在国外叫Chinese Magic Card)的块0可写,卡号可变。 There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. - 国外把UID卡分为中国魔术卡 Gen1和Gen2 + 国外把UID卡分为Chinese Magic Card Gen1和Gen2 @@ -818,12 +818,12 @@ It could make the whole sector blocked irreversibly! KeyA - A密钥 + 密钥A KeyB - B密钥 + 密钥B From 3c3944d150d86b8517795657313044069afcf78f Mon Sep 17 00:00:00 2001 From: wh201906 Date: Sun, 1 Nov 2020 22:43:46 +0800 Subject: [PATCH 10/20] Slight change --- ui/mainwindow.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 5207ec6..2e7cc0b 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -1175,8 +1175,8 @@ - 30 - 30 + 10 + 10 121 211 From 6d3dd5056c05689f1c8e8fc3edaee391ecffd748 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Wed, 4 Nov 2020 15:09:28 +0800 Subject: [PATCH 11/20] Replace textChanged() with valueChanged(). This fixes #9 --- ui/mf_trailerdecoderdialog.cpp | 10 +++++----- ui/mf_trailerdecoderdialog.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/mf_trailerdecoderdialog.cpp b/ui/mf_trailerdecoderdialog.cpp index 3b09a16..fca353d 100644 --- a/ui/mf_trailerdecoderdialog.cpp +++ b/ui/mf_trailerdecoderdialog.cpp @@ -15,10 +15,10 @@ MF_trailerDecoderDialog::MF_trailerDecoderDialog(QWidget *parent) : sizeGroup->addButton(ui->size4Button, 4); sizeGroup->addButton(ui->size16Button, 16); connect(sizeGroup, QOverload::of(&QButtonGroup::buttonToggled), this, &MF_trailerDecoderDialog::on_blockSizeChanged); - connect(ui->C0Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); - connect(ui->C1Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); - connect(ui->C2Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); - connect(ui->C3Box, &QSpinBox::textChanged, this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C0Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C1Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C2Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); + connect(ui->C3Box, QOverload::of(&QSpinBox::valueChanged), this, &MF_trailerDecoderDialog::on_boxChanged); ui->dataBlockWidget->setRowCount(3); ui->dataBlockWidget->setColumnCount(4); @@ -123,7 +123,7 @@ void MF_trailerDecoderDialog::setTableItem(QTableWidget* widget, int row, int co widget->item(row, column)->setText(text); } -void MF_trailerDecoderDialog::on_boxChanged(const QString &arg1) +void MF_trailerDecoderDialog::on_boxChanged(int arg1) { quint8 ACBits[4]; ACBits[0] = ui->C0Box->value(); diff --git a/ui/mf_trailerdecoderdialog.h b/ui/mf_trailerdecoderdialog.h index 93a6abd..d0040e0 100644 --- a/ui/mf_trailerdecoderdialog.h +++ b/ui/mf_trailerdecoderdialog.h @@ -27,7 +27,7 @@ private slots: void on_blockSizeChanged(int id, bool st); - void on_boxChanged(const QString &arg1); + void on_boxChanged(int arg1); private: Ui::MF_trailerDecoderDialog *ui; QRegularExpressionValidator* validator; From a56a503b23fb4f7fac8d022d242d91098fd12372 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Fri, 12 Feb 2021 23:45:25 +0800 Subject: [PATCH 12/20] Some slight changes Add HighDPI support(not tested) Support Mifare darkside attack Hide unfinished function tabs --- main.cpp | 1 + module/mifare.cpp | 15 +++++++++++++++ module/mifare.h | 1 + testlog.md | 25 +++++++++++++++++++++++++ ui/mainwindow.cpp | 20 ++++++++++++++++++-- ui/mainwindow.h | 3 +++ ui/mainwindow.ui | 18 +++++++++++++++++- 7 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 testlog.md diff --git a/main.cpp b/main.cpp index 999eb0b..94d2ec7 100644 --- a/main.cpp +++ b/main.cpp @@ -8,6 +8,7 @@ int main(int argc, char *argv[]) { + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication a(argc, argv); MainWindow w; QSettings* settings = new QSettings("GUIsettings.ini", QSettings::IniFormat); diff --git a/module/mifare.cpp b/module/mifare.cpp index b07593e..4ac028b 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -252,6 +252,21 @@ void Mifare::hardnested() ui->funcTab->setCurrentIndex(1); } +void Mifare::darkside() +{ + if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + util->execCMD("hf mf mifare"); + ui->funcTab->setCurrentIndex(1); + } + else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + { + util->execCMD("hf mf darkside"); + ui->funcTab->setCurrentIndex(1); + } + +} + void Mifare::sniff() { util->execCMD("hf mf sniff"); diff --git a/module/mifare.h b/module/mifare.h index dfde298..b825d56 100644 --- a/module/mifare.h +++ b/module/mifare.h @@ -66,6 +66,7 @@ public: QString info(bool isRequiringOutput = false); void chk(); void nested(); + void darkside(); void hardnested(); void sniff(); void snoop(); diff --git a/testlog.md b/testlog.md new file mode 100644 index 0000000..1e11118 --- /dev/null +++ b/testlog.md @@ -0,0 +1,25 @@ +# Some Test Log + +*** + +# Version:0.1.3 +## Mifare +### Card Info ++ Official, Mifare card: Passed ++ Iceman, Mifare card: Passed ++ Official, no card: Passed ++ Iceman, no card: Passed + +### Check default Password ++ Official, all FFFFFFFFFFFF: Passed ++ Iceman, all FFFFFFFFFFFF: Passed ++ Official, no card: Passed ++ Iceman, no card: Stuck to searching card, then failed to communicate with PM3 ++ Official, all unknown: Passed ++ Iceman, all unknown: Passed ++ Official, partially unknown: Passed ++ Iceman, partially unknown: Passed + +### Darkside Attack ++ Official: Passed ++ Iceman: Passed \ No newline at end of file diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 6ad89ba..ed0f0ca 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -7,6 +7,7 @@ MainWindow::MainWindow(QWidget *parent): { ui->setupUi(this); myInfo = new QAction("wh201906", this); + currVersion = new QAction("Ver: " + QApplication::applicationVersion().section('.', 0, -2), this); // ignore the 4th version number checkUpdate = new QAction(tr("Check Update"), this); connect(myInfo, &QAction::triggered, [ = ]() { @@ -17,6 +18,7 @@ MainWindow::MainWindow(QWidget *parent): QDesktopServices::openUrl(QUrl("https://github.com/wh201906/Proxmark3GUI/releases")); }); this->addAction(myInfo); + this->addAction(currVersion); this->addAction(checkUpdate); settings = new QSettings("GUIsettings.ini", QSettings::IniFormat); @@ -30,6 +32,11 @@ MainWindow::MainWindow(QWidget *parent): mifare = new Mifare(ui, util, this); keyEventFilter = new MyEventFilter(QEvent::KeyRelease); + + + // hide unused tabs + ui->funcTab->removeTab(1); + ui->funcTab->removeTab(1); } MainWindow::~MainWindow() @@ -127,6 +134,11 @@ void MainWindow::on_stopButton_clicked() // ******************** raw command ******************** +void MainWindow::on_Raw_CMDEdit_textChanged(const QString &arg1) +{ + stashedCMDEditText = arg1; +} + void MainWindow::on_Raw_sendCMDButton_clicked() { util->execCMD(ui->Raw_CMDEdit->text()); @@ -1039,7 +1051,11 @@ void MainWindow::saveClientPath(const QString& path) } // *********************************************** -void MainWindow::on_Raw_CMDEdit_textChanged(const QString &arg1) + + +void MainWindow::on_MF_Attack_darksideButton_clicked() { - stashedCMDEditText = arg1; + setState(false); + mifare->darkside(); + setState(true); } diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 91daafa..948e991 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -154,6 +154,8 @@ private slots: void on_stopButton_clicked(); void on_Raw_CMDEdit_textChanged(const QString &arg1); + void on_MF_Attack_darksideButton_clicked(); + private: Ui::MainWindow* ui; QButtonGroup* typeBtnGroup; @@ -162,6 +164,7 @@ private: QLabel* PM3VersionBar; QPushButton* stopButton; QAction* myInfo; + QAction* currVersion; QAction* checkUpdate; QSettings* settings; MyEventFilter* keyEventFilter; diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 2e7cc0b..6801816 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -120,7 +120,7 @@ - 1 + 0 @@ -568,6 +568,19 @@ + + + + + 40 + 0 + + + + Darkside + + + @@ -1169,6 +1182,9 @@ + + true + LF/Data From 5330ed8d1402064d455bb7cbeae5cbad506c5b50 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Sat, 13 Feb 2021 23:58:11 +0800 Subject: [PATCH 13/20] Some changes Refactor execCMDWithOutput Add UI for advanced settings Add stop button(reconnect) Update translations --- common/util.cpp | 16 +- common/util.h | 1 + lang/en_US.ts | 454 +++++++++++++++++++++++++++++-------------- lang/languages.ini | 0 lang/zh_CN.ts | 465 +++++++++++++++++++++++++++++++-------------- module/mifare.cpp | 49 ++--- ui/mainwindow.cpp | 94 ++++++++- ui/mainwindow.h | 16 +- ui/mainwindow.ui | 266 +++++++++++++++++++++++++- 9 files changed, 1044 insertions(+), 317 deletions(-) create mode 100644 lang/languages.ini diff --git a/common/util.cpp b/common/util.cpp index 1da8256..e9bf045 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -22,13 +22,16 @@ void Util::processOutput(const QString& output) void Util::execCMD(const QString& cmd) { - qDebug() << cmd; + qDebug() << "executing: " << cmd; if(isRunning) emit write(cmd + "\r\n"); } QString Util::execCMDWithOutput(const QString& cmd, ReturnTrigger trigger) { + // if the trigger is empty, this function will wait trigger.waitTime then return all outputs during the wait time. + // otherwise, this function will return empty string if no trigger is detected, or return outputs if any trigger is detected. + // the waitTime will be refreshed if the client have new outputs bool isResultFound = false; QRegularExpression re; re.setPatternOptions(QRegularExpression::DotMatchesEverythingOption); @@ -42,27 +45,32 @@ QString Util::execCMDWithOutput(const QString& cmd, ReturnTrigger trigger) execCMD(cmd); while(QTime::currentTime() < targetTime) { + if(!isRunning) + break; QApplication::processEvents(); for(QString otpt : trigger.expectedOutputs) { re.setPattern(otpt); isResultFound = re.match(*requiredOutput).hasMatch(); - if(requiredOutput->contains(otpt)) + if(isResultFound) + { + qDebug() << "output Matched: " << *requiredOutput; break; + } } if(isResultFound) { delay(200); break; } - if(timeStamp > currTime) + if(timeStamp > currTime) //has new output { currTime = timeStamp; targetTime = timeStamp.addMSecs(trigger.waitTime); } } isRequiringOutput = false; - return *requiredOutput; + return (isResultFound || trigger.expectedOutputs.isEmpty() ? *requiredOutput : ""); } void Util::delay(unsigned int msec) diff --git a/common/util.h b/common/util.h index 04a4ec0..05d0467 100644 --- a/common/util.h +++ b/common/util.h @@ -50,6 +50,7 @@ public: QString execCMDWithOutput(const QString& cmd, ReturnTrigger trigger = 10000); void delay(unsigned int msec); ClientType getClientType(); + static const int rawTabIndex = 1; public slots: void processOutput(const QString& output); void setClientType(Util::ClientType clientType); diff --git a/lang/en_US.ts b/lang/en_US.ts index b881d0e..5a58b0b 100644 --- a/lang/en_US.ts +++ b/lang/en_US.ts @@ -310,534 +310,681 @@ It could make the whole sector blocked irreversibly! - + Select Trailer - + Card Type - + MINI 320 - + 1K 1024 - + 2K 2048 - + 4K 4096 - + File - - + + Load - - + + + Save - - + + Data - + + Key - + Attack - + Card Info - + Check Default - + Nested - + Hardnested - + + Darkside + + + + Read/Write - + Block: - + Key: - + Key Type: - + Snoop - + List Data - + Data: - + Normal(Require Password) - + Dump - + Restore - + Chinese Magic Card(Without Password) - + Lock UFUID Card - - + + About UID Card - + Set Parameter - + Wipe - - + + Simulate - - + + + Clear - + Select All - + KeyBlocks->Key - + KeyBlocks<-Key - + Fill Keys - + Trailer Decoder - + Set Fonts - - + + Read One - - + + Write One - - - + + + Read Selected - - - + + + Write Selected - - + + Sniff - + + LF/Data + + + + + LF Config + + + + + Frequency + + + + + 125k + + + + + 134k + + + + + BitRate: + + + + + Decimation: + + + + + Averaging: + + + + + Threshold: + + + + + Skips: + + + + + Get + + + + + Set + + + + + T55xx + + + + RawCommand - - + + History: - + ClearHistory - + Send - + ClearOutput - - - - - - - - - - - + + Settings + + + + + Client + + + + + Preload environment variables + + + + + Value + + + + + Add + + + + + Delete + + + + + Note: +If the variable name already exists, this app will add the new value to the head of the existing one, so these new values have higher priority when calling Proxmark3 client. +The environment variables added here won't affect other apps. + + + + + Start arguments + + + + + <port> -f + + + + + Note: +-f is necessary because the GUI need to handle the output in time +In some cases the arguments should be set to "-p /dev/<port> -f" +or "-p <port> -f" + + + + + Keep buttons enabled even the client is running or disconnected + + + + + GUI + + + + + Language + + + + + + + + + + + + + + Info - + Plz choose a port first - + Connected - - - + + Not Connected - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) - - - + + + Failed to open - + Continue? - + Check Update - + Some of the data and key will be cleared. - + Plz select the font of data widget and key widget - + Data must consists of 32 Hex symbols(Whitespace is allowed) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) - + Plz select the data file: - + Plz select the key file: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) - + Plz select the location to save data file: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) - - - + + + Failed to save to - + Plz select the location to save key file: - + Binary Key Files(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. - + There are some types of Chinese Magic Card Gen2. - + CUID Card: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) - + FUID Card: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) - + UFUID Card: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). - + Plz select the trace file: - + Trace Files(*.trc);;All Files(*.*) - + Plz select the location to save trace file: - + Trace Files(*.trc) - - + + Idle - - + + Stop + + + + + Sec - + Blk - + KeyA - + KeyB - + HW Version: - + PM3: - + State: - + Running @@ -845,31 +992,56 @@ It could make the whole sector blocked irreversibly! Mifare - + Success! - - - - + + + + + + + Info - + Plz provide at least one known key - - + + Failed! - + + The Access Bits is invalid! +It could make the whole sector blocked irreversibly! +Continue to write? + + + + + Successful! + + + + + Failed to write to these blocks: + + + + + Select them? + + + + Failed to read card. diff --git a/lang/languages.ini b/lang/languages.ini new file mode 100644 index 0000000..e69de29 diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index 0990cac..ac86f92 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -314,534 +314,690 @@ It could make the whole sector blocked irreversibly! Mifare(IC)卡 - + Select Trailer 选中密码块 - + Card Type 卡片类型 - + MINI 320 - + 1K 1024 - + 2K 2048 - + 4K 4096 - + File 文件 - - + + Load 加载 - - + + + Save 保存 - - + + Data 数据 - + + Key 密钥 - + Attack 破解 - + Card Info 读卡信息 - + Check Default 验证默认密码 - + Nested Nested攻击 - + Hardnested Hardested攻击 - + + Darkside + Darkside攻击 + + + Read/Write 读/写 - + Block: 块: - + Key: 密钥: - + Key Type: 密钥类型: - + Snoop 嗅探(Snoop) - + List Data 列出嗅探数据 - + Data: 数据: - + Normal(Require Password) 普通卡(需要密码) - + Dump Dump命令 - + Restore Restore命令 - + Chinese Magic Card(Without Password) UID卡(不需要密码) - + Lock UFUID Card 锁定UFUID卡 - - + + About UID Card 关于UID卡 - + Set Parameter 设置卡参数 - + Wipe 擦除 - - + + Simulate 模拟 - - + + + Clear 清空 - + Select All 全选 - + KeyBlocks->Key 密码区->密码 - + KeyBlocks<-Key 密码区<-密码 - + Fill Keys 填充密码 - + Trailer Decoder Trailer解码 - + Set Fonts 设置字体 - - + + Read One 读取单个区 - - + + Write One 写入单个区 - - - + + + Read Selected 读取选中块 - - - + + + Write Selected 写入选中块 - - + + Sniff 嗅探 - + + LF/Data + + + + + LF Config + + + + + Frequency + + + + + 125k + + + + + 134k + + + + + BitRate: + + + + + Decimation: + + + + + Averaging: + + + + + Threshold: + + + + + Skips: + + + + + Get + + + + + Set + + + + + T55xx + + + + RawCommand 原始命令 - - + + History: 命令历史: - + ClearHistory 清空历史 - + Send 发送 - + ClearOutput 清空输出 - - - - - - - - - - - + + Settings + 设置 + + + + Client + 客户端 + + + + Preload environment variables + 预加载环境变量 + + + + Value + + + + + Add + 添加 + + + + Delete + 删除 + + + + Note: +If the variable name already exists, this app will add the new value to the head of the existing one, so these new values have higher priority when calling Proxmark3 client. +The environment variables added here won't affect other apps. + 注意: +如果变量名已经存在,则会将新变量值添加至现有变量值之前,启动客户端时会优先使用此处添加的变量值。 +此处添加的环境变量不会影响其它程序。 + + + + Start arguments + 启动参数 + + + + <port> -f + + + + + Note: +-f is necessary because the GUI need to handle the output in time +In some cases the arguments should be set to "-p /dev/<port> -f" +or "-p <port> -f" + 注意: +-f选项用于使客户端实时返回命令回显,必须添加 +部分情况下启动参数需设置为"-p /dev/<port> -f" +或"-p <port> -f" + + + Note: -f is necessary because the GUI need to handle the output in time + 注意:-f选项用于使客户端实时返回命令回显,必须添加 + + + + Keep buttons enabled even the client is running or disconnected + 保持所有按钮可点击,即使未连接客户端或有任务正在运行 + + + + GUI + 图形化界面 + + + + Language + 语言 + + + + + + + + + + + + + Info 信息 - + Plz choose a port first 请先选择端口 - + Connected 已连接 - - - + + Not Connected 未连接 - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) 二进制数据文件(*.bin *.dump);;文本数据文件(*.txt *.eml);;所有文件(*.*) - - - + + + Failed to open 无法打开 - + Continue? 确定? - + Check Update 检查更新 - + Some of the data and key will be cleared. 部分数据和密码将被清除 - + Plz select the font of data widget and key widget 请选择数据窗口和密钥窗口的字体 - + Data must consists of 32 Hex symbols(Whitespace is allowed) 数据必须由32个十六进制字符组成(中间可含有空格) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) 密钥必须由12个十六进制字符组成(中间可含有空格) - + Plz select the data file: 请选择数据文件: - + Plz select the key file: 请选择密钥文件: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) 二进制密钥文件(*.bin *.dump)二进制密钥文件(*.bin *.dump);所有文件(*.*) - + Plz select the location to save data file: 请选择数据文件保存的位置: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) 二进制数据文件(*.bin *.dump);文本数据文件(*.txt *.eml) - - - + + + Failed to save to 无法保存至 - + Plz select the location to save key file: 请选择密钥文件保存的位置: - + Binary Key Files(*.bin *.dump) 二进制密码文件(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. 普通Mifare卡的块0无法写入,卡号也不能更改 - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. UID卡(在国外叫Chinese Magic Card)的块0可写,卡号可变。 - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. 国外把UID卡分为Chinese Magic Card Gen1和Gen2 - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. 指通常所说的UID卡,可以通过后门指令直接读写块而无需密码,在PM3和此GUI中有特殊命令处理这类卡片 - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. 这个叫法在国内比较罕见,在国外指CUID/FUID/UFUID这类对后门指令不响应的卡(防火墙卡) - + There are some types of Chinese Magic Card Gen2. 以下是Gen2卡的详细介绍 - + CUID Card: CUID卡: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. 可通过普通的写块命令来写块0,可重复擦写 - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) (hf mf wrbl 0 A FFFFFFFFFFFF <待写入数据>) - + FUID Card: FUID卡: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). 块0只能写入一次 - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) (更高级的穿防火墙卡,可以过一些能识别出CUID卡的读卡器) - + UFUID Card: UFUID卡: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). 锁卡前和普通UID/CUID卡一样可以反复读写块0,用特殊命令锁卡后就和FUID卡一样了 - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). 所有UID卡都似乎更容易被Nested攻击破解 - + Plz select the trace file: 请选择trace文件: - + Trace Files(*.trc);;All Files(*.*) Trace文件(*.trc);;所有文件(*.*) - + Plz select the location to save trace file: 请选择trace文件保存的位置: - + Trace Files(*.trc) Trace文件(*.trc) - - + + Idle 空闲 - - + + Stop + 停止 + + + + Sec 扇区 - + Blk - + KeyA 密钥A - + KeyB 密钥B - + HW Version: 固件版本: - + PM3: 连接状态: - + State: 运行状态: - + Running 正在运行 @@ -849,31 +1005,58 @@ It could make the whole sector blocked irreversibly! Mifare - + Success! 成功! - - - - + + + + + + + Info 信息 - + Plz provide at least one known key 请至少提供一个已知密码 - - + + Failed! 失败! - + + The Access Bits is invalid! +It could make the whole sector blocked irreversibly! +Continue to write? + 控制位无效! +使用该控制位可能导致目标扇区损坏且无法恢复! +确定要写入吗? + + + + Successful! + 成功! + + + + Failed to write to these blocks: + 写入以下块失败: + + + + Select them? + 选中这些块? + + + Failed to read card. 读卡失败。 diff --git a/module/mifare.cpp b/module/mifare.cpp index 4ac028b..bc6b10e 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -106,7 +106,7 @@ QString Mifare::info(bool isRequiringOutput) else { util->execCMD("hf 14a info"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); return ""; } } @@ -115,17 +115,16 @@ QString Mifare::info(bool isRequiringOutput) void Mifare::chk() { QRegularExpressionMatch reMatch; - QString result = util->execCMDWithOutput( - "hf mf chk *" - + QString::number(cardType.type) - + " ?", - Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|----------------\\|"})); - qDebug() << result; - + QString result; int offset = 0; QString data; if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) { + result = util->execCMDWithOutput( + "hf mf chk *" + + QString::number(cardType.type) + + " ?", + Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|----------------\\|"})); for(int i = 0; i < cardType.sector_size; i++) { reMatch = keyPattern->match(result, offset); @@ -148,6 +147,11 @@ void Mifare::chk() } else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) { + result = util->execCMDWithOutput( + "hf mf chk *" + + QString::number(cardType.type) + + " ?", + Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|---\\|----------------\\|"})); for(int i = 0; i < cardType.sector_size; i++) { reMatch = keyPattern_res->match(result, offset); @@ -213,7 +217,8 @@ void Mifare::nested() result = util->execCMDWithOutput( "hf mf nested " + QString::number(cardType.type) - + knownKeyInfo, 10000); + + knownKeyInfo, + Util::ReturnTrigger(10000, {"key is wrong", "\\|000\\|"})); } else { @@ -249,7 +254,7 @@ void Mifare::hardnested() MF_Attack_hardnestedDialog dialog(cardType.block_size); connect(&dialog, &MF_Attack_hardnestedDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::darkside() @@ -257,12 +262,12 @@ void Mifare::darkside() if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) { util->execCMD("hf mf mifare"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) { util->execCMD("hf mf darkside"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } } @@ -270,19 +275,19 @@ void Mifare::darkside() void Mifare::sniff() { util->execCMD("hf mf sniff"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::snoop() { util->execCMD("hf 14a snoop"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::list() { util->execCMD("hf list mf"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, TargetType targetType, int waitTime) @@ -709,13 +714,13 @@ void Mifare::writeSelected(TargetType targetType) void Mifare::dump() { util->execCMD("hf mf dump"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::restore() { util->execCMD("hf mf restore"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::wipeC() @@ -724,7 +729,7 @@ void Mifare::wipeC() "hf mf cwipe " + QString::number(cardType.type) + " f"); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::setParameterC() @@ -741,7 +746,7 @@ void Mifare::setParameterC() MF_UID_parameterDialog dialog(lis[0].toUpper(), lis[1].toUpper(), lis[2].mid(0, 2).toUpper()); connect(&dialog, &MF_UID_parameterDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } } @@ -765,19 +770,19 @@ void Mifare::simulate() MF_Sim_simDialog dialog(cardType.type); connect(&dialog, &MF_Sim_simDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::loadSniff(const QString& file) { util->execCMD("hf list mf -l " + file); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::saveSniff(const QString& file) { util->execCMD("hf list mf -s " + file); - ui->funcTab->setCurrentIndex(1); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::data_syncWithDataWidget(bool syncAll, int block) diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index ed0f0ca..c4d0f7e 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -92,6 +92,9 @@ void MainWindow::on_PM3_connectButton_clicked() saveClientPath(ui->PM3_pathEdit->text()); emit connectPM3(ui->PM3_pathEdit->text(), port); } + QProcess proc; + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + //env.insert(); } void MainWindow::onPM3StateChanged(bool st, const QString& info) @@ -112,11 +115,8 @@ void MainWindow::onPM3StateChanged(bool st, const QString& info) void MainWindow::on_PM3_disconnectButton_clicked() { - pm3state = false; - setState(false); emit killPM3(); emit setSerialListener("", false); - setStatusBar(connectStatusBar, tr("Not Connected")); } void MainWindow::refreshOutput(const QString& output) @@ -128,7 +128,19 @@ void MainWindow::refreshOutput(const QString& output) void MainWindow::on_stopButton_clicked() { - + if(!pm3state) + on_PM3_disconnectButton_clicked(); + else + { + on_PM3_disconnectButton_clicked(); + for(int i = 0; i < 10; i++) + { + util->delay(200); + if(!pm3state) + break; + } + on_PM3_connectButton_clicked(); + } } // ********************************************************* @@ -866,7 +878,6 @@ void MainWindow::uiInit() ui->MF_dataWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Sec"))); ui->MF_dataWidget->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("Blk"))); ui->MF_dataWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("Data"))); - ui->MF_dataWidget->verticalHeader()->setVisible(false); ui->MF_dataWidget->setColumnWidth(0, 55); ui->MF_dataWidget->setColumnWidth(1, 55); ui->MF_dataWidget->setColumnWidth(2, 450); @@ -875,7 +886,6 @@ void MainWindow::uiInit() ui->MF_keyWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Sec"))); ui->MF_keyWidget->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("KeyA"))); ui->MF_keyWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("KeyB"))); - ui->MF_keyWidget->verticalHeader()->setVisible(false); ui->MF_keyWidget->setColumnWidth(0, 35); ui->MF_keyWidget->setColumnWidth(1, 125); ui->MF_keyWidget->setColumnWidth(2, 125); @@ -917,11 +927,21 @@ void MainWindow::uiInit() ui->PM3_pathEdit->setText(settings->value("path", "proxmark3").toString()); settings->endGroup(); + settings->beginGroup("Client_Args"); + ui->Set_Client_startArgsEdit->setText(settings->value("args", " -f").toString()); + settings->endGroup(); + + settings->beginGroup("Client_forceButtonsEnabled"); + ui->Set_Client_forceEnabledBox->setChecked(settings->value("state", false).toBool()); + settings->endGroup(); + ui->MF_RW_keyTypeBox->addItem("A", Mifare::KEY_A); ui->MF_RW_keyTypeBox->addItem("B", Mifare::KEY_B); on_Raw_CMDHistoryBox_stateChanged(Qt::Unchecked); on_PM3_refreshPortButton_clicked(); + + loadClientPreloadEnv(); } void MainWindow::signalInit() @@ -1059,3 +1079,65 @@ void MainWindow::on_MF_Attack_darksideButton_clicked() mifare->darkside(); setState(true); } + +void MainWindow::on_Set_Client_envDeleteButton_clicked() +{ + ui->Set_Client_envTable->removeRow(ui->Set_Client_envTable->currentRow()); +} + +void MainWindow::on_Set_Client_envAddButton_clicked() +{ + ui->Set_Client_envTable->insertRow(ui->Set_Client_envTable->rowCount()); +} + +void MainWindow::on_Set_Client_envClearButton_clicked() +{ + ui->Set_Client_envTable->clearContents(); + ui->Set_Client_envTable->setRowCount(0); +} + +void MainWindow::on_Set_Client_envSaveButton_clicked() +{ + settings->beginGroup("Client_Env"); + for(int i = 0; i < ui->Set_Client_envTable->rowCount(); i++) + { + QTableWidgetItem* key = ui->Set_Client_envTable->item(i, 0); + QTableWidgetItem* val = ui->Set_Client_envTable->item(i, 1); + if(key == nullptr || val == nullptr || key->text().isEmpty() || val->text().isEmpty()) + continue; + settings->setValue(key->text(), val->text()); + qDebug() << "Env saved: " << i << key->text() << val->text(); + } + settings->endGroup(); +} + +void MainWindow::loadClientPreloadEnv() +{ + ui->Set_Client_envTable->clearContents(); + settings->beginGroup("Client_Env"); + QStringList keyList = settings->allKeys(); + ui->Set_Client_envTable->setRowCount(keyList.size()); + for(int i = 0; i < keyList.size(); i++) + { + ui->Set_Client_envTable->setItem(i, 0, new QTableWidgetItem(keyList[i])); + ui->Set_Client_envTable->setItem(i, 1, new QTableWidgetItem(settings->value(keyList[i]).toString())); + } + settings->endGroup(); +} + + +void MainWindow::on_Set_Client_startArgsEdit_editingFinished() +{ + settings->beginGroup("Client_Args"); + settings->setValue("args", ui->Set_Client_startArgsEdit->text()); + settings->endGroup(); +} + +void MainWindow::on_Set_Client_forceEnabledBox_stateChanged(int arg1) +{ + settings->beginGroup("Client_forceButtonsEnabled"); + settings->setValue("state", arg1 == Qt::Checked); + settings->endGroup(); +} + + diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 948e991..e0f9e9d 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "common/myeventfilter.h" #include "common/pm3process.h" @@ -90,7 +91,6 @@ private slots: void on_MF_RW_writeSelectedButton_clicked(); - void on_MF_RW_dumpButton_clicked(); void on_MF_RW_restoreButton_clicked(); @@ -156,6 +156,20 @@ private slots: void on_MF_Attack_darksideButton_clicked(); + void on_Set_Client_envDeleteButton_clicked(); + + void on_Set_Client_envAddButton_clicked(); + + void on_Set_Client_envSaveButton_clicked(); + + void loadClientPreloadEnv(); + + void on_Set_Client_startArgsEdit_editingFinished(); + + void on_Set_Client_forceEnabledBox_stateChanged(int arg1); + + void on_Set_Client_envClearButton_clicked(); + private: Ui::MainWindow* ui; QButtonGroup* typeBtnGroup; diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 6801816..813c8c3 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -6,7 +6,7 @@ 0 0 - 970 + 1029 770 @@ -120,7 +120,7 @@ - 0 + 4 @@ -170,6 +170,12 @@ QAbstractItemView::SingleSelection + + true + + + false + 20 @@ -329,6 +335,9 @@ QAbstractItemView::SingleSelection + + false + 20 @@ -1515,6 +1524,259 @@ + + + Settings + + + + + + + + Client + + + + + + Preload environment variables + + + + + + + + + QAbstractItemView::SelectRows + + + true + + + false + + + + Key + + + + + Value + + + + + + + + + + Add + + + + + + + Delete + + + + + + + Clear + + + + + + + Save + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Note: +If the variable name already exists, this app will add the new value to the head of the existing one, so these new values have higher priority when calling Proxmark3 client. +The environment variables added here won't affect other apps. + + + true + + + + + + + Qt::Horizontal + + + + + + + Start arguments + + + + + + + <port> -f + + + + + + + Note: +-f is necessary because the GUI need to handle the output in time +In some cases the arguments should be set to "-p /dev/<port> -f" +or "-p <port> -f" + + + true + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Keep buttons enabled even the client is running or disconnected + + + true + + + + + + + + + GUI + + + + + + Language + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + From 83445c7eef017d6b12a808f81dbbecbf116834c7 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Sun, 14 Feb 2021 22:47:56 +0800 Subject: [PATCH 14/20] Add new features Support change language in Settings Support customized start arguments Strech data section in MF_dataWidget and key section in MF_keyWidget --- common/pm3process.cpp | 4 +- common/pm3process.h | 2 +- common/util.cpp | 24 ++++++ common/util.h | 4 + lang/en_US.ts | 162 ++++++++++++++++++++++------------------ lang/languages.ini | 3 + lang/zh_CN.ts | 170 +++++++++++++++++++++++------------------- main.cpp | 49 ++++++------ ui/mainwindow.cpp | 51 +++++++++---- ui/mainwindow.h | 8 +- ui/mainwindow.ui | 31 +++++--- 11 files changed, 303 insertions(+), 205 deletions(-) diff --git a/common/pm3process.cpp b/common/pm3process.cpp index 8181353..2313ddf 100644 --- a/common/pm3process.cpp +++ b/common/pm3process.cpp @@ -14,14 +14,14 @@ PM3Process::PM3Process(QThread* thread, QObject* parent): QProcess(parent) connect(this, &PM3Process::readyRead, this, &PM3Process::onReadyRead); } -void PM3Process::connectPM3(const QString& path, const QString& port) +void PM3Process::connectPM3(const QString& path, const QString& port, const QStringList args) { QString result; Util::ClientType clientType = Util::CLIENTTYPE_OFFICIAL; setRequiringOutput(true); // using "-f" option to make the client output flushed after every print. - start(path, QStringList() << port << "-f", QProcess::Unbuffered | QProcess::ReadWrite); + start(path, args, QProcess::Unbuffered | QProcess::ReadWrite); if(waitForStarted(10000)) { waitForReadyRead(1000); diff --git a/common/pm3process.h b/common/pm3process.h index 749c501..d6c55a0 100644 --- a/common/pm3process.h +++ b/common/pm3process.h @@ -21,7 +21,7 @@ public: void testThread(); public slots: - void connectPM3(const QString& path, const QString& port); + void connectPM3(const QString& path, const QString& port, const QStringList args); void setSerialListener(const QString& name, bool state); qint64 write(QString data); private slots: diff --git a/common/util.cpp b/common/util.cpp index e9bf045..3443441 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -93,3 +93,27 @@ void Util::setRunningState(bool st) { this->isRunning = st; } + +bool Util::chooseLanguage(QSettings* guiSettings, QMainWindow* window) +{ + // make sure the GUISettings is not in any group + QSettings* langSettings = new QSettings("lang/languages.ini", QSettings::IniFormat); + QMap langMap; + langSettings->setIniCodec("UTF-8"); + langSettings->beginGroup("Languages"); + QStringList langList = langSettings->allKeys(); + for(int i = 0; i < langList.size(); i++) + langMap.insert(langSettings->value(langList[i]).toString(), langList[i]); + langSettings->endGroup(); + delete langSettings; + bool isOk = false; + QString selectedText = QInputDialog::getItem(window, "", "Choose a language:", langMap.keys(), 0, false, &isOk); + if(isOk) + { + guiSettings->beginGroup("lang"); + guiSettings->setValue("language", langMap[selectedText]); + guiSettings->endGroup(); + guiSettings->sync(); + } + return isOk; +} diff --git a/common/util.h b/common/util.h index 05d0467..3327330 100644 --- a/common/util.h +++ b/common/util.h @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include class Util : public QObject { @@ -51,6 +54,7 @@ public: void delay(unsigned int msec); ClientType getClientType(); static const int rawTabIndex = 1; + static bool chooseLanguage(QSettings *guiSettings, QMainWindow *window); public slots: void processOutput(const QString& output); void setClientType(Util::ClientType clientType); diff --git a/lang/en_US.ts b/lang/en_US.ts index 5a58b0b..2d3f93a 100644 --- a/lang/en_US.ts +++ b/lang/en_US.ts @@ -363,13 +363,12 @@ It could make the whole sector blocked irreversibly! - + Data - Key @@ -465,7 +464,7 @@ It could make the whole sector blocked irreversibly! - + About UID Card @@ -626,7 +625,7 @@ It could make the whole sector blocked irreversibly! - + History: @@ -660,6 +659,11 @@ It could make the whole sector blocked irreversibly! Preload environment variables + + + Variable + + Value @@ -711,55 +715,65 @@ or "-p <port> -f" - - Language + + Language: + + + + + Choose Language + + + + + (Restart this app to use new language) - - - - - - - - - - - + + + + + + + + + + + Info - + Plz choose a port first - + Connected - - + + Not Connected - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) - - - + + + Failed to open - + Continue? @@ -769,222 +783,222 @@ or "-p <port> -f" - + Some of the data and key will be cleared. - + Plz select the font of data widget and key widget - + Data must consists of 32 Hex symbols(Whitespace is allowed) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) - + Plz select the data file: - + Plz select the key file: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) - + Plz select the location to save data file: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) - - - + + + Failed to save to - + Plz select the location to save key file: - + Binary Key Files(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. - + There are some types of Chinese Magic Card Gen2. - + CUID Card: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) - + FUID Card: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) - + UFUID Card: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). - + Plz select the trace file: - + Trace Files(*.trc);;All Files(*.*) - + Plz select the location to save trace file: - + Trace Files(*.trc) - - + + Idle - + Stop - - + + Sec - + Blk - + KeyA - + KeyB - + HW Version: - + PM3: - + State: - + Running diff --git a/lang/languages.ini b/lang/languages.ini index e69de29..439ccd6 100644 --- a/lang/languages.ini +++ b/lang/languages.ini @@ -0,0 +1,3 @@ +[Language] +English=en_US +简体中文=zh_CN \ No newline at end of file diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index ac86f92..a2abf8a 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -367,13 +367,12 @@ It could make the whole sector blocked irreversibly! - + Data 数据 - Key 密钥 @@ -469,7 +468,7 @@ It could make the whole sector blocked irreversibly! - + About UID Card 关于UID卡 @@ -630,7 +629,7 @@ It could make the whole sector blocked irreversibly! - + History: 命令历史: @@ -664,6 +663,11 @@ It could make the whole sector blocked irreversibly! Preload environment variables 预加载环境变量 + + + Variable + 变量 + Value @@ -709,6 +713,21 @@ or "-p <port> -f" 部分情况下启动参数需设置为"-p /dev/<port> -f" 或"-p <port> -f" + + + Language: + 语言: + + + + Choose Language + 选择语言 + + + + (Restart this app to use new language) + (重启以使用新语言) + Note: -f is necessary because the GUI need to handle the output in time 注意:-f选项用于使客户端实时返回命令回显,必须添加 @@ -724,55 +743,54 @@ or "-p <port> -f" 图形化界面 - Language - 语言 - - - - - - - - - - - - - + 语言 + + + + + + + + + + + + + Info 信息 - + Plz choose a port first 请先选择端口 - + Connected 已连接 - - + + Not Connected 未连接 - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) 二进制数据文件(*.bin *.dump);;文本数据文件(*.txt *.eml);;所有文件(*.*) - - - + + + Failed to open 无法打开 - + Continue? 确定? @@ -782,222 +800,222 @@ or "-p <port> -f" 检查更新 - + Some of the data and key will be cleared. 部分数据和密码将被清除 - + Plz select the font of data widget and key widget 请选择数据窗口和密钥窗口的字体 - + Data must consists of 32 Hex symbols(Whitespace is allowed) 数据必须由32个十六进制字符组成(中间可含有空格) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) 密钥必须由12个十六进制字符组成(中间可含有空格) - + Plz select the data file: 请选择数据文件: - + Plz select the key file: 请选择密钥文件: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) 二进制密钥文件(*.bin *.dump)二进制密钥文件(*.bin *.dump);所有文件(*.*) - + Plz select the location to save data file: 请选择数据文件保存的位置: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) 二进制数据文件(*.bin *.dump);文本数据文件(*.txt *.eml) - - - + + + Failed to save to 无法保存至 - + Plz select the location to save key file: 请选择密钥文件保存的位置: - + Binary Key Files(*.bin *.dump) 二进制密码文件(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. 普通Mifare卡的块0无法写入,卡号也不能更改 - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. UID卡(在国外叫Chinese Magic Card)的块0可写,卡号可变。 - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. 国外把UID卡分为Chinese Magic Card Gen1和Gen2 - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. 指通常所说的UID卡,可以通过后门指令直接读写块而无需密码,在PM3和此GUI中有特殊命令处理这类卡片 - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. 这个叫法在国内比较罕见,在国外指CUID/FUID/UFUID这类对后门指令不响应的卡(防火墙卡) - + There are some types of Chinese Magic Card Gen2. 以下是Gen2卡的详细介绍 - + CUID Card: CUID卡: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. 可通过普通的写块命令来写块0,可重复擦写 - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) (hf mf wrbl 0 A FFFFFFFFFFFF <待写入数据>) - + FUID Card: FUID卡: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). 块0只能写入一次 - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) (更高级的穿防火墙卡,可以过一些能识别出CUID卡的读卡器) - + UFUID Card: UFUID卡: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). 锁卡前和普通UID/CUID卡一样可以反复读写块0,用特殊命令锁卡后就和FUID卡一样了 - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). 所有UID卡都似乎更容易被Nested攻击破解 - + Plz select the trace file: 请选择trace文件: - + Trace Files(*.trc);;All Files(*.*) Trace文件(*.trc);;所有文件(*.*) - + Plz select the location to save trace file: 请选择trace文件保存的位置: - + Trace Files(*.trc) Trace文件(*.trc) - - + + Idle 空闲 - + Stop 停止 - - + + Sec 扇区 - + Blk - + KeyA 密钥A - + KeyB 密钥B - + HW Version: 固件版本: - + PM3: 连接状态: - + State: 运行状态: - + Running 正在运行 diff --git a/main.cpp b/main.cpp index 94d2ec7..4f87d81 100644 --- a/main.cpp +++ b/main.cpp @@ -4,57 +4,50 @@ #include #include #include -#include +#include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QApplication a(argc, argv); MainWindow w; + QSettings* settings = new QSettings("GUIsettings.ini", QSettings::IniFormat); + settings->setIniCodec("UTF-8"); settings->beginGroup("lang"); - QVariant lang = settings->value("language", "null"); - if(lang == "null") + QString currLang = settings->value("language", "").toString(); + settings->endGroup(); + if(currLang == "") { -#ifdef Q_OS_WIN - lang = "lang/en_US.qm"; -#else - lang = "lang/en_US.ts"; -#endif - QStringList langList; - langList.append("English"); - langList.append("简体中文"); - QString seletedText = QInputDialog::getItem(&w, "", "Choose a language:", langList, 0, false); - if(seletedText == "English") + if(Util::chooseLanguage(settings, &w)) { -#ifdef Q_OS_WIN - lang = "lang/en_US.qm"; -#else - lang = "lang/en_US.ts"; -#endif + settings->beginGroup("lang"); + currLang = settings->value("language", "").toString(); + settings->endGroup(); } - else if(seletedText == "简体中文") - { + else + currLang = "en_US"; + } + currLang = "lang/" + currLang; #ifdef Q_OS_WIN - lang = "lang/zh_CN.qm"; + currLang += ".qm"; #else - lang = "lang/zh_CN.ts"; + currLang += ".ts";; #endif - } - } QTranslator* translator = new QTranslator(&w); - if(translator->load(lang.toString())) + if(translator->load(currLang)) { a.installTranslator(translator); - settings->setValue("language", lang); } else { - QMessageBox::information(&w, "Error", "Can't load " + lang.toString() + " as translation file."); + QMessageBox::information(&w, "Error", "Can't load " + currLang + " as translation file."); } - settings->endGroup(); delete settings; w.initUI(); w.show(); return a.exec(); } + + diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index c4d0f7e..8b50189 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -22,6 +22,7 @@ MainWindow::MainWindow(QWidget *parent): this->addAction(checkUpdate); settings = new QSettings("GUIsettings.ini", QSettings::IniFormat); + settings->setIniCodec("UTF-8"); pm3Thread = new QThread(this); pm3 = new PM3Process(pm3Thread); @@ -32,7 +33,7 @@ MainWindow::MainWindow(QWidget *parent): mifare = new Mifare(ui, util, this); keyEventFilter = new MyEventFilter(QEvent::KeyRelease); - + resizeEventFilter = new MyEventFilter(QEvent::Resize); // hide unused tabs ui->funcTab->removeTab(1); @@ -66,7 +67,7 @@ void MainWindow::on_PM3_refreshPortButton_clicked() QStringList serialList; foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { - qDebug() << info.isBusy() << info.isNull() << info.portName(); + qDebug() << info.isBusy() << info.isNull() << info.portName() << info.description(); serial.setPort(info); if(serial.open(QIODevice::ReadWrite)) @@ -89,8 +90,9 @@ void MainWindow::on_PM3_connectButton_clicked() QMessageBox::information(NULL, tr("Info"), tr("Plz choose a port first"), QMessageBox::Ok); else { + QStringList args = ui->Set_Client_startArgsEdit->text().replace("", port).split(' '); saveClientPath(ui->PM3_pathEdit->text()); - emit connectPM3(ui->PM3_pathEdit->text(), port); + emit connectPM3(ui->PM3_pathEdit->text(), port, args); } QProcess proc; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); @@ -240,6 +242,20 @@ void MainWindow::on_Raw_CMDEdit_keyPressed(QObject* obj_addr, QEvent& event) // ***************************************************** // ******************** mifare ******************** +void MainWindow::on_MF_keyWidget_resized(QObject* obj_addr, QEvent& event) +{ + if(obj_addr == ui->MF_keyWidget && event.type() == QEvent::Resize) + { + QTableWidget* widget = (QTableWidget*)obj_addr; + int keyItemWidth = widget->width(); + keyItemWidth -= widget->verticalScrollBar()->width(); + keyItemWidth -= 2 * widget->frameWidth(); + keyItemWidth -= widget->horizontalHeader()->sectionSize(0); + widget->horizontalHeader()->resizeSection(1, keyItemWidth / 2); + widget->horizontalHeader()->resizeSection(2, keyItemWidth / 2); + } +} + void MainWindow::MF_onTypeChanged(int id, bool st) { typeBtnGroup->blockSignals(true); @@ -860,6 +876,8 @@ void MainWindow::uiInit() connect(ui->Raw_CMDEdit, &QLineEdit::editingFinished, this, &MainWindow::sendMSG); ui->Raw_CMDEdit->installEventFilter(keyEventFilter); connect(keyEventFilter, &MyEventFilter::eventHappened, this, &MainWindow::on_Raw_CMDEdit_keyPressed); + ui->MF_keyWidget->installEventFilter(resizeEventFilter); + connect(resizeEventFilter, &MyEventFilter::eventHappened, this, &MainWindow::on_MF_keyWidget_resized); connectStatusBar = new QLabel(this); programStatusBar = new QLabel(this); @@ -904,7 +922,7 @@ void MainWindow::uiInit() settings->beginGroup("UI_grpbox_preference"); QStringList boxNames = settings->allKeys(); - QGroupBox* boxptr; + QGroupBox * boxptr; foreach(QString name, boxNames) { boxptr = this->findChild(name); @@ -932,7 +950,8 @@ void MainWindow::uiInit() settings->endGroup(); settings->beginGroup("Client_forceButtonsEnabled"); - ui->Set_Client_forceEnabledBox->setChecked(settings->value("state", false).toBool()); + keepButtonsEnabled = settings->value("state", false).toBool(); + ui->Set_Client_forceEnabledBox->setChecked(keepButtonsEnabled); settings->endGroup(); ui->MF_RW_keyTypeBox->addItem("A", Mifare::KEY_A); @@ -1036,13 +1055,13 @@ void MainWindow::setState(bool st) { setStatusBar(programStatusBar, tr("Idle")); } - ui->MF_attackGroupBox->setEnabled(st); - ui->MF_normalGroupBox->setEnabled(st); - ui->MF_UIDGroupBox->setEnabled(st); - ui->MF_simGroupBox->setEnabled(st); - ui->MF_sniffGroupBox->setEnabled(st); - ui->Raw_CMDEdit->setEnabled(st); - ui->Raw_sendCMDButton->setEnabled(st); + ui->MF_attackGroupBox->setEnabled(st || keepButtonsEnabled); + ui->MF_normalGroupBox->setEnabled(st || keepButtonsEnabled); + ui->MF_UIDGroupBox->setEnabled(st || keepButtonsEnabled); + ui->MF_simGroupBox->setEnabled(st || keepButtonsEnabled); + ui->MF_sniffGroupBox->setEnabled(st || keepButtonsEnabled); + ui->Raw_CMDEdit->setEnabled(st || keepButtonsEnabled); + ui->Raw_sendCMDButton->setEnabled(st || keepButtonsEnabled); } void MainWindow::on_GroupBox_clicked(bool checked) @@ -1136,8 +1155,14 @@ void MainWindow::on_Set_Client_startArgsEdit_editingFinished() void MainWindow::on_Set_Client_forceEnabledBox_stateChanged(int arg1) { settings->beginGroup("Client_forceButtonsEnabled"); - settings->setValue("state", arg1 == Qt::Checked); + keepButtonsEnabled = (arg1 == Qt::Checked); + settings->setValue("state", keepButtonsEnabled); settings->endGroup(); } + +void MainWindow::on_Set_GUI_setLanguageButton_clicked() +{ + Util::chooseLanguage(settings, this); +} diff --git a/ui/mainwindow.h b/ui/mainwindow.h index e0f9e9d..ebd4791 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "common/myeventfilter.h" #include "common/pm3process.h" @@ -52,6 +53,7 @@ public slots: void onPM3StateChanged(bool st, const QString& info); void MF_onTypeChanged(int id, bool st); void on_Raw_CMDEdit_keyPressed(QObject *obj_addr, QEvent &event); + void on_MF_keyWidget_resized(QObject *obj_addr, QEvent &event); private slots: void on_PM3_connectButton_clicked(); @@ -170,6 +172,8 @@ private slots: void on_Set_Client_envClearButton_clicked(); + void on_Set_GUI_setLanguageButton_clicked(); + private: Ui::MainWindow* ui; QButtonGroup* typeBtnGroup; @@ -182,6 +186,7 @@ private: QAction* checkUpdate; QSettings* settings; MyEventFilter* keyEventFilter; + MyEventFilter* resizeEventFilter; QString stashedCMDEditText; int stashedIndex = -1; @@ -190,6 +195,7 @@ private: PM3Process* pm3; bool pm3state; + bool keepButtonsEnabled; QThread* pm3Thread; Mifare* mifare; @@ -204,7 +210,7 @@ private: void setState(bool st); void saveClientPath(const QString& path); signals: - void connectPM3(const QString& path, const QString& port); + void connectPM3(const QString& path, const QString& port, const QStringList args); void killPM3(); void setSerialListener(const QString& name, bool state); }; diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 813c8c3..642ae2d 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -120,7 +120,7 @@ - 4 + 0 @@ -1559,7 +1559,7 @@ - Key + Variable @@ -1714,17 +1714,28 @@ or "-p <port> -f" GUI - - - - Language - - - - + + + Language: + + + + + + + Choose Language + + + + + + + (Restart this app to use new language) + + From 6e5f654cc578d745adb737c81eaeaa619ea65534 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 15 Feb 2021 18:38:34 +0800 Subject: [PATCH 15/20] Support running a external script before client start, to configure the environment variables Optimize stop(reconnect) logic Search valid ports automatically Update translations --- common/pm3process.cpp | 25 ++- common/pm3process.h | 7 + lang/en_US.ts | 371 +++++++++++++++++++--------------------- lang/languages.ini | 6 +- lang/zh_CN.ts | 386 ++++++++++++++++++++---------------------- ui/mainwindow.cpp | 177 +++++++++---------- ui/mainwindow.h | 30 ++-- ui/mainwindow.ui | 200 +++++++++------------- 8 files changed, 567 insertions(+), 635 deletions(-) diff --git a/common/pm3process.cpp b/common/pm3process.cpp index 2313ddf..269a4f0 100644 --- a/common/pm3process.cpp +++ b/common/pm3process.cpp @@ -17,14 +17,19 @@ PM3Process::PM3Process(QThread* thread, QObject* parent): QProcess(parent) void PM3Process::connectPM3(const QString& path, const QString& port, const QStringList args) { QString result; - Util::ClientType clientType = Util::CLIENTTYPE_OFFICIAL; + Util::ClientType clientType; setRequiringOutput(true); + // stash for reconnect + currPath = path; + currPort = port; + currArgs = args; + // using "-f" option to make the client output flushed after every print. start(path, args, QProcess::Unbuffered | QProcess::ReadWrite); if(waitForStarted(10000)) { - waitForReadyRead(1000); + waitForReadyRead(10000); setRequiringOutput(false); result = *requiredOutput; if(result.indexOf("[=]") != -1) @@ -36,6 +41,10 @@ void PM3Process::connectPM3(const QString& path, const QString& port, const QStr result = *requiredOutput; setRequiringOutput(false); } + else + { + clientType = Util::CLIENTTYPE_OFFICIAL; + } if(result.indexOf("os: ") != -1) // make sure the PM3 is connected { emit changeClientType(clientType); @@ -50,6 +59,11 @@ void PM3Process::connectPM3(const QString& path, const QString& port, const QStr } } +void PM3Process::reconnectPM3() +{ + connectPM3(currPath, currPort, currArgs); +} + void PM3Process::setRequiringOutput(bool st) { isRequiringOutput = st; @@ -110,3 +124,10 @@ void PM3Process::onReadyRead() } } + +void PM3Process::setProcEnv(const QStringList* env) +{ +// qDebug() << "passed Env List" << *env; + this->setEnvironment(*env); +// qDebug() << "final Env List" << processEnvironment().toStringList(); +} diff --git a/common/pm3process.h b/common/pm3process.h index d6c55a0..b2a6631 100644 --- a/common/pm3process.h +++ b/common/pm3process.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "util.h" @@ -24,6 +25,8 @@ public slots: void connectPM3(const QString& path, const QString& port, const QStringList args); void setSerialListener(const QString& name, bool state); qint64 write(QString data); + void reconnectPM3(); + void setProcEnv(const QStringList* env); private slots: void onTimeout(); void onReadyRead(); @@ -33,6 +36,10 @@ private: void setRequiringOutput(bool st);// It only works in this class now QTimer* serialListener; QSerialPortInfo* portInfo; + QString currPath; + QString currPort; + QStringList currArgs; + signals: void PM3StatedChanged(bool st, const QString& info = ""); void newOutput(const QString& output); diff --git a/lang/en_US.ts b/lang/en_US.ts index 2d3f93a..a9aff47 100644 --- a/lang/en_US.ts +++ b/lang/en_US.ts @@ -285,419 +285,402 @@ It could make the whole sector blocked irreversibly! - - Path: - - - - - Refresh - - - - + Connect - + Disconnect - + Mifare - + Select Trailer - + Card Type - + MINI 320 - + 1K 1024 - + 2K 2048 - + 4K 4096 - + File - - + + Load - - - + + Save - - + + Data - + Key - + Attack - + Card Info - + Check Default - + Nested - + Hardnested - + Darkside - + Read/Write - + Block: - + Key: - + Key Type: - + Snoop - + List Data - + Data: - + Normal(Require Password) - + Dump - + Restore - + Chinese Magic Card(Without Password) - + Lock UFUID Card - - + + About UID Card - + Set Parameter - + Wipe - - + + Simulate - - - + + Clear - + + Client Path: + + + + + Port: + + + + + Refresh Ports + + + + Select All - + KeyBlocks->Key - + KeyBlocks<-Key - + Fill Keys - + Trailer Decoder - + Set Fonts - - + + Read One - - + + Write One - - - + + + Read Selected - - - + + + Write Selected - - + + Sniff - + LF/Data - + LF Config - + Frequency - + 125k - + 134k - + BitRate: - + Decimation: - + Averaging: - + Threshold: - + Skips: - + Get - + Set - + T55xx - + RawCommand - - + + History: - + ClearHistory - + Send - + ClearOutput - + Settings - + Client - - Preload environment variables - - - - - Variable + + Preload script path: - Value - - - - - Add - - - - - Delete - - - - Note: -If the variable name already exists, this app will add the new value to the head of the existing one, so these new values have higher priority when calling Proxmark3 client. -The environment variables added here won't affect other apps. +If the client requires some enviroment variables, you can make a script file(*.bat on Windows or *.sh on Linux) to configure them, +then put the path of the script there - + Start arguments - + <port> -f - + Note: -f is necessary because the GUI need to handle the output in time In some cases the arguments should be set to "-p /dev/<port> -f" @@ -705,75 +688,75 @@ or "-p <port> -f" - + Keep buttons enabled even the client is running or disconnected - + GUI - + Language: - + Choose Language - + (Restart this app to use new language) - - - - - - - - - - - + + + + + + + + + + + Info - + Plz choose a port first - + Connected - - + + Not Connected - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) - - - + + + Failed to open - + Continue? @@ -783,222 +766,222 @@ or "-p <port> -f" - + Some of the data and key will be cleared. - + Plz select the font of data widget and key widget - + Data must consists of 32 Hex symbols(Whitespace is allowed) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) - + Plz select the data file: - + Plz select the key file: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) - + Plz select the location to save data file: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) - - - + + + Failed to save to - + Plz select the location to save key file: - + Binary Key Files(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. - + There are some types of Chinese Magic Card Gen2. - + CUID Card: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) - + FUID Card: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) - + UFUID Card: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). - + Plz select the trace file: - + Trace Files(*.trc);;All Files(*.*) - + Plz select the location to save trace file: - + Trace Files(*.trc) - - + + Idle - + Stop - - + + Sec - + Blk - + KeyA - + KeyB - + HW Version: - + PM3: - + State: - + Running diff --git a/lang/languages.ini b/lang/languages.ini index 439ccd6..d9a11e4 100644 --- a/lang/languages.ini +++ b/lang/languages.ini @@ -1,3 +1,3 @@ -[Language] -English=en_US -简体中文=zh_CN \ No newline at end of file +[Languages] +en_US=English +zh_CN=简体中文 \ No newline at end of file diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index a2abf8a..957f603 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -289,421 +289,403 @@ It could make the whole sector blocked irreversibly! - - Path: - 路径: - - - - Refresh - 刷新端口 - - - + Connect 连接 - + Disconnect 断开 - + Mifare Mifare(IC)卡 - + Select Trailer 选中密码块 - + Card Type 卡片类型 - + MINI 320 - + 1K 1024 - + 2K 2048 - + 4K 4096 - + File 文件 - - + + Load 加载 - - - + + Save 保存 - - + + Data 数据 - + Key 密钥 - + Attack 破解 - + Card Info 读卡信息 - + Check Default 验证默认密码 - + Nested Nested攻击 - + Hardnested Hardested攻击 - + Darkside Darkside攻击 - + Read/Write 读/写 - + Block: 块: - + Key: 密钥: - + Key Type: 密钥类型: - + Snoop 嗅探(Snoop) - + List Data 列出嗅探数据 - + Data: 数据: - + Normal(Require Password) 普通卡(需要密码) - + Dump Dump命令 - + Restore Restore命令 - + Chinese Magic Card(Without Password) UID卡(不需要密码) - + Lock UFUID Card 锁定UFUID卡 - - + + About UID Card 关于UID卡 - + Set Parameter 设置卡参数 - + Wipe 擦除 - - + + Simulate 模拟 - - - + + Clear 清空 - + + Client Path: + 客户端路径: + + + + Port: + 端口: + + + + Refresh Ports + 刷新端口 + + + Select All 全选 - + KeyBlocks->Key 密码区->密码 - + KeyBlocks<-Key 密码区<-密码 - + Fill Keys 填充密码 - + Trailer Decoder Trailer解码 - + Set Fonts 设置字体 - - + + Read One 读取单个区 - - + + Write One 写入单个区 - - - + + + Read Selected 读取选中块 - - - + + + Write Selected 写入选中块 - - + + Sniff 嗅探 - + LF/Data - + LF Config - + Frequency - + 125k - + 134k - + BitRate: - + Decimation: - + Averaging: - + Threshold: - + Skips: - + Get - + Set - + T55xx - + RawCommand 原始命令 - - + + History: 命令历史: - + ClearHistory 清空历史 - + Send 发送 - + ClearOutput 清空输出 - + Settings 设置 - + Client 客户端 - - Preload environment variables - 预加载环境变量 - - - - Variable - 变量 + + Preload script path: + 预加载脚本路径: - Value - - - - - Add - 添加 - - - - Delete - 删除 - - - Note: -If the variable name already exists, this app will add the new value to the head of the existing one, so these new values have higher priority when calling Proxmark3 client. -The environment variables added here won't affect other apps. +If the client requires some enviroment variables, you can make a script file(*.bat on Windows or *.sh on Linux) to configure them, +then put the path of the script there 注意: -如果变量名已经存在,则会将新变量值添加至现有变量值之前,启动客户端时会优先使用此处添加的变量值。 -此处添加的环境变量不会影响其它程序。 +如果客户端需要配置环境变量才能正常运行,可以将配置环境变量所需的脚本文件(Windows系统内为*.bat,linux系统内为*.sh)路径填入此处 - + Start arguments 启动参数 - + <port> -f - + Note: -f is necessary because the GUI need to handle the output in time In some cases the arguments should be set to "-p /dev/<port> -f" @@ -714,83 +696,75 @@ or "-p <port> -f" 或"-p <port> -f" - + Language: 语言: - + Choose Language 选择语言 - + (Restart this app to use new language) - (重启以使用新语言) - - - Note: -f is necessary because the GUI need to handle the output in time - 注意:-f选项用于使客户端实时返回命令回显,必须添加 + (重启此程序以使用新语言) - + Keep buttons enabled even the client is running or disconnected 保持所有按钮可点击,即使未连接客户端或有任务正在运行 - + GUI 图形化界面 - Language - 语言 - - - - - - - - - - - - - + + + + + + + + + + + Info 信息 - + Plz choose a port first 请先选择端口 - + Connected 已连接 - - + + Not Connected 未连接 - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml);;All Files(*.*) 二进制数据文件(*.bin *.dump);;文本数据文件(*.txt *.eml);;所有文件(*.*) - - - + + + Failed to open 无法打开 - + Continue? 确定? @@ -800,222 +774,222 @@ or "-p <port> -f" 检查更新 - + Some of the data and key will be cleared. 部分数据和密码将被清除 - + Plz select the font of data widget and key widget 请选择数据窗口和密钥窗口的字体 - + Data must consists of 32 Hex symbols(Whitespace is allowed) 数据必须由32个十六进制字符组成(中间可含有空格) - - + + Key must consists of 12 Hex symbols(Whitespace is allowed) 密钥必须由12个十六进制字符组成(中间可含有空格) - + Plz select the data file: 请选择数据文件: - + Plz select the key file: 请选择密钥文件: - + Binary Key Files(*.bin *.dump);;Binary Data Files(*.bin *.dump);;All Files(*.*) 二进制密钥文件(*.bin *.dump)二进制密钥文件(*.bin *.dump);所有文件(*.*) - + Plz select the location to save data file: 请选择数据文件保存的位置: - + Binary Data Files(*.bin *.dump);;Text Data Files(*.txt *.eml) 二进制数据文件(*.bin *.dump);文本数据文件(*.txt *.eml) - - - + + + Failed to save to 无法保存至 - + Plz select the location to save key file: 请选择密钥文件保存的位置: - + Binary Key Files(*.bin *.dump) 二进制密码文件(*.bin *.dump) - + Normally, the Block 0 of a typical Mifare card, which contains the UID, is locked during the manufacture. Users cannot write anything to Block 0 or set a new UID to a normal Mifare card. 普通Mifare卡的块0无法写入,卡号也不能更改 - + Chinese Magic Cards(aka UID Cards) are some special cards whose Block 0 are writeable. And you can change UID by writing to it. UID卡(在国外叫Chinese Magic Card)的块0可写,卡号可变。 - + There are two versions of Chinese Magic Cards, the Gen1 and the Gen2. 国外把UID卡分为Chinese Magic Card Gen1和Gen2 - + Gen1: - + also called UID card in China. It responses to some backdoor commands so you can access any blocks without password. The Proxmark3 has a bunch of related commands(csetblk, cgetblk, ...) to deal with this type of card, and my GUI also support these commands. 指通常所说的UID卡,可以通过后门指令直接读写块而无需密码,在PM3和此GUI中有特殊命令处理这类卡片 - + Gen2: - + doesn't response to the backdoor commands, which means that a reader cannot detect whether it is a Chinese Magic Card or not by sending backdoor commands. 这个叫法在国内比较罕见,在国外指CUID/FUID/UFUID这类对后门指令不响应的卡(防火墙卡) - + There are some types of Chinese Magic Card Gen2. 以下是Gen2卡的详细介绍 - + CUID Card: CUID卡: - + the Block 0 is writeable, you can write to this block repeatedly by normal wrbl command. 可通过普通的写块命令来写块0,可重复擦写 - + (hf mf wrbl 0 A FFFFFFFFFFFF <the data you want to write>) (hf mf wrbl 0 A FFFFFFFFFFFF <待写入数据>) - + FUID Card: FUID卡: - + you can only write to Block 0 once. After that, it seems like a typical Mifare card(Block 0 cannot be written to). 块0只能写入一次 - + (some readers might try changing the Block 0, which could detect the CUID Card. In that case, you should use FUID card.) (更高级的穿防火墙卡,可以过一些能识别出CUID卡的读卡器) - + UFUID Card: UFUID卡: - + It behaves like a CUID card(or UID card? I'm not sure) before you send some special command to lock it. Once it is locked, you cannot change its Block 0(just like a typical Mifare card). 锁卡前和普通UID/CUID卡一样可以反复读写块0,用特殊命令锁卡后就和FUID卡一样了 - + Seemingly, these Chinese Magic Cards are more easily to be compromised by Nested Attack(it takes little time to get an unknown key). 所有UID卡都似乎更容易被Nested攻击破解 - + Plz select the trace file: 请选择trace文件: - + Trace Files(*.trc);;All Files(*.*) Trace文件(*.trc);;所有文件(*.*) - + Plz select the location to save trace file: 请选择trace文件保存的位置: - + Trace Files(*.trc) Trace文件(*.trc) - - + + Idle 空闲 - + Stop 停止 - - + + Sec 扇区 - + Blk - + KeyA 密钥A - + KeyB 密钥B - + HW Version: 固件版本: - + PM3: 连接状态: - + State: 运行状态: - + Running 正在运行 diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 8b50189..297520e 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -38,6 +38,12 @@ MainWindow::MainWindow(QWidget *parent): // hide unused tabs ui->funcTab->removeTab(1); ui->funcTab->removeTab(1); + + portSearchTimer = new QTimer(this); + portSearchTimer->setInterval(2000); + connect(portSearchTimer, &QTimer::timeout, this, &MainWindow::on_portSearchTimer_timeout); + portSearchTimer->start(); + } MainWindow::~MainWindow() @@ -60,31 +66,27 @@ void MainWindow::initUI() // will be called by main.app // ******************** basic functions ******************** -void MainWindow::on_PM3_refreshPortButton_clicked() +void MainWindow::on_portSearchTimer_timeout() { - ui->PM3_portBox->clear(); - QSerialPort serial; - QStringList serialList; + QStringList newPortList; foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { - qDebug() << info.isBusy() << info.isNull() << info.portName() << info.description(); - serial.setPort(info); - - if(serial.open(QIODevice::ReadWrite)) - { - serialList << info.portName(); - serial.close(); - } +// qDebug() << info.isBusy() << info.isNull() << info.portName() << info.description(); + if(!info.isNull()) + newPortList << info.portName(); } - foreach(QString port, serialList) + if(newPortList != portList) // update PM3_portBox when available ports changed { - ui->PM3_portBox->addItem(port); + portList = newPortList; + ui->PM3_portBox->clear(); + ui->PM3_portBox->addItems(portList); } } void MainWindow::on_PM3_connectButton_clicked() { qDebug() << "Main:" << QThread::currentThread(); + QString port = ui->PM3_portBox->currentText(); if(port == "") QMessageBox::information(NULL, tr("Info"), tr("Plz choose a port first"), QMessageBox::Ok); @@ -92,11 +94,27 @@ void MainWindow::on_PM3_connectButton_clicked() { QStringList args = ui->Set_Client_startArgsEdit->text().replace("", port).split(' '); saveClientPath(ui->PM3_pathEdit->text()); + QProcess envSetProcess; + QFileInfo envScriptPath(ui->Set_Client_envScriptEdit->text()); + if(envScriptPath.exists()) + { + qDebug() << envScriptPath.absoluteFilePath(); +#ifdef Q_OS_WIN + // cmd /c "">>nul && set + envSetProcess.start("cmd /c \"" + envScriptPath.absoluteFilePath() + "\">>nul && set"); +#else + // sh -c '. "">>/dev/null && env' + envSetProcess.start("sh -c \' . \"" + envScriptPath.absoluteFilePath() + "\">>/dev/null && env"); +#endif + envSetProcess.waitForReadyRead(10000); + clientEnv = QString(envSetProcess.readAll()).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); +// qDebug() << "Get Env List" << clientEnv; + } + else + clientEnv.clear(); + emit setProcEnv(&clientEnv); emit connectPM3(ui->PM3_pathEdit->text(), port, args); } - QProcess proc; - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - //env.insert(); } void MainWindow::onPM3StateChanged(bool st, const QString& info) @@ -105,11 +123,13 @@ void MainWindow::onPM3StateChanged(bool st, const QString& info) setState(st); if(st == true) { + portSearchTimer->stop(); setStatusBar(PM3VersionBar, info); setStatusBar(connectStatusBar, tr("Connected")); } else { + portSearchTimer->start(); setStatusBar(PM3VersionBar, ""); setStatusBar(connectStatusBar, tr("Not Connected")); } @@ -141,7 +161,7 @@ void MainWindow::on_stopButton_clicked() if(!pm3state) break; } - on_PM3_connectButton_clicked(); + emit reconnectPM3(); } } // ********************************************************* @@ -256,14 +276,14 @@ void MainWindow::on_MF_keyWidget_resized(QObject* obj_addr, QEvent& event) } } -void MainWindow::MF_onTypeChanged(int id, bool st) +void MainWindow::MF_onMFCardTypeChanged(int id, bool st) { - typeBtnGroup->blockSignals(true); - qDebug() << id << typeBtnGroup->checkedId(); + MFCardTypeBtnGroup->blockSignals(true); + qDebug() << id << MFCardTypeBtnGroup->checkedId(); if(!st) { int result; - if(id > typeBtnGroup->checkedId()) // id is specified in uiInit() with a proper order, so I can compare the size by id. + if(id > MFCardTypeBtnGroup->checkedId()) // id is specified in uiInit() with a proper order, so I can compare the size by id. { result = QMessageBox::question(this, tr("Info"), tr("Some of the data and key will be cleared.") + "\n" + tr("Continue?"), QMessageBox::Yes | QMessageBox::No); } @@ -274,7 +294,7 @@ void MainWindow::MF_onTypeChanged(int id, bool st) if(result == QMessageBox::Yes) { qDebug() << "Yes"; - mifare->setCardType(typeBtnGroup->checkedId()); + mifare->setCardType(MFCardTypeBtnGroup->checkedId()); MF_widgetReset(); mifare->data_syncWithDataWidget(); mifare->data_syncWithKeyWidget(); @@ -282,10 +302,10 @@ void MainWindow::MF_onTypeChanged(int id, bool st) else { qDebug() << "No"; - typeBtnGroup->button(id)->setChecked(true); + MFCardTypeBtnGroup->button(id)->setChecked(true); } } - typeBtnGroup->blockSignals(false); + MFCardTypeBtnGroup->blockSignals(false); } void MainWindow::on_MF_selectAllBox_stateChanged(int arg1) @@ -898,23 +918,20 @@ void MainWindow::uiInit() ui->MF_dataWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("Data"))); ui->MF_dataWidget->setColumnWidth(0, 55); ui->MF_dataWidget->setColumnWidth(1, 55); - ui->MF_dataWidget->setColumnWidth(2, 450); ui->MF_keyWidget->setColumnCount(3); ui->MF_keyWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(tr("Sec"))); ui->MF_keyWidget->setHorizontalHeaderItem(1, new QTableWidgetItem(tr("KeyA"))); ui->MF_keyWidget->setHorizontalHeaderItem(2, new QTableWidgetItem(tr("KeyB"))); - ui->MF_keyWidget->setColumnWidth(0, 35); - ui->MF_keyWidget->setColumnWidth(1, 125); - ui->MF_keyWidget->setColumnWidth(2, 125); + ui->MF_keyWidget->setColumnWidth(0, 45); MF_widgetReset(); - typeBtnGroup = new QButtonGroup(this); - typeBtnGroup->addButton(ui->MF_Type_miniButton, 0); - typeBtnGroup->addButton(ui->MF_Type_1kButton, 1); - typeBtnGroup->addButton(ui->MF_Type_2kButton, 2); - typeBtnGroup->addButton(ui->MF_Type_4kButton, 4); - connect(typeBtnGroup, QOverload::of(&QButtonGroup::buttonToggled), this, &MainWindow::MF_onTypeChanged); + MFCardTypeBtnGroup = new QButtonGroup(this); + MFCardTypeBtnGroup->addButton(ui->MF_Type_miniButton, 0); + MFCardTypeBtnGroup->addButton(ui->MF_Type_1kButton, 1); + MFCardTypeBtnGroup->addButton(ui->MF_Type_2kButton, 2); + MFCardTypeBtnGroup->addButton(ui->MF_Type_4kButton, 4); + connect(MFCardTypeBtnGroup, QOverload::of(&QButtonGroup::buttonToggled), this, &MainWindow::MF_onMFCardTypeChanged); ui->MF_keyWidget->installEventFilter(this); ui->MF_dataWidget->installEventFilter(this); @@ -954,13 +971,14 @@ void MainWindow::uiInit() ui->Set_Client_forceEnabledBox->setChecked(keepButtonsEnabled); settings->endGroup(); + settings->beginGroup("Client_Env"); + ui->Set_Client_envScriptEdit->setText(settings->value("scriptPath").toString()); + settings->endGroup(); + ui->MF_RW_keyTypeBox->addItem("A", Mifare::KEY_A); ui->MF_RW_keyTypeBox->addItem("B", Mifare::KEY_B); on_Raw_CMDHistoryBox_stateChanged(Qt::Unchecked); - on_PM3_refreshPortButton_clicked(); - - loadClientPreloadEnv(); } void MainWindow::signalInit() @@ -970,9 +988,11 @@ void MainWindow::signalInit() connect(util, &Util::refreshOutput, this, &MainWindow::refreshOutput); connect(this, &MainWindow::connectPM3, pm3, &PM3Process::connectPM3); + connect(this, &MainWindow::reconnectPM3, pm3, &PM3Process::reconnectPM3); connect(pm3, &PM3Process::PM3StatedChanged, this, &MainWindow::onPM3StateChanged); connect(pm3, &PM3Process::PM3StatedChanged, util, &Util::setRunningState); connect(this, &MainWindow::killPM3, pm3, &PM3Process::kill); + connect(this, &MainWindow::setProcEnv, pm3, &PM3Process::setProcEnv); connect(util, &Util::write, pm3, &PM3Process::write); @@ -1055,13 +1075,18 @@ void MainWindow::setState(bool st) { setStatusBar(programStatusBar, tr("Idle")); } - ui->MF_attackGroupBox->setEnabled(st || keepButtonsEnabled); - ui->MF_normalGroupBox->setEnabled(st || keepButtonsEnabled); - ui->MF_UIDGroupBox->setEnabled(st || keepButtonsEnabled); - ui->MF_simGroupBox->setEnabled(st || keepButtonsEnabled); - ui->MF_sniffGroupBox->setEnabled(st || keepButtonsEnabled); - ui->Raw_CMDEdit->setEnabled(st || keepButtonsEnabled); - ui->Raw_sendCMDButton->setEnabled(st || keepButtonsEnabled); + setButtonsEnabled(st || keepButtonsEnabled); +} + +void MainWindow::setButtonsEnabled(bool st) +{ + ui->MF_attackGroupBox->setEnabled(st); + ui->MF_normalGroupBox->setEnabled(st); + ui->MF_UIDGroupBox->setEnabled(st); + ui->MF_simGroupBox->setEnabled(st); + ui->MF_sniffGroupBox->setEnabled(st); + ui->Raw_CMDEdit->setEnabled(st); + ui->Raw_sendCMDButton->setEnabled(st); } void MainWindow::on_GroupBox_clicked(bool checked) @@ -1099,52 +1124,6 @@ void MainWindow::on_MF_Attack_darksideButton_clicked() setState(true); } -void MainWindow::on_Set_Client_envDeleteButton_clicked() -{ - ui->Set_Client_envTable->removeRow(ui->Set_Client_envTable->currentRow()); -} - -void MainWindow::on_Set_Client_envAddButton_clicked() -{ - ui->Set_Client_envTable->insertRow(ui->Set_Client_envTable->rowCount()); -} - -void MainWindow::on_Set_Client_envClearButton_clicked() -{ - ui->Set_Client_envTable->clearContents(); - ui->Set_Client_envTable->setRowCount(0); -} - -void MainWindow::on_Set_Client_envSaveButton_clicked() -{ - settings->beginGroup("Client_Env"); - for(int i = 0; i < ui->Set_Client_envTable->rowCount(); i++) - { - QTableWidgetItem* key = ui->Set_Client_envTable->item(i, 0); - QTableWidgetItem* val = ui->Set_Client_envTable->item(i, 1); - if(key == nullptr || val == nullptr || key->text().isEmpty() || val->text().isEmpty()) - continue; - settings->setValue(key->text(), val->text()); - qDebug() << "Env saved: " << i << key->text() << val->text(); - } - settings->endGroup(); -} - -void MainWindow::loadClientPreloadEnv() -{ - ui->Set_Client_envTable->clearContents(); - settings->beginGroup("Client_Env"); - QStringList keyList = settings->allKeys(); - ui->Set_Client_envTable->setRowCount(keyList.size()); - for(int i = 0; i < keyList.size(); i++) - { - ui->Set_Client_envTable->setItem(i, 0, new QTableWidgetItem(keyList[i])); - ui->Set_Client_envTable->setItem(i, 1, new QTableWidgetItem(settings->value(keyList[i]).toString())); - } - settings->endGroup(); -} - - void MainWindow::on_Set_Client_startArgsEdit_editingFinished() { settings->beginGroup("Client_Args"); @@ -1158,11 +1137,23 @@ void MainWindow::on_Set_Client_forceEnabledBox_stateChanged(int arg1) keepButtonsEnabled = (arg1 == Qt::Checked); settings->setValue("state", keepButtonsEnabled); settings->endGroup(); + if(keepButtonsEnabled) + setButtonsEnabled(true); } - - void MainWindow::on_Set_GUI_setLanguageButton_clicked() { Util::chooseLanguage(settings, this); } + +void MainWindow::on_PM3_refreshPortButton_clicked() +{ + on_portSearchTimer_timeout(); +} + +void MainWindow::on_Set_Client_envScriptEdit_editingFinished() +{ + settings->beginGroup("Client_Env"); + settings->setValue("scriptPath", ui->Set_Client_envScriptEdit->text()); + settings->endGroup(); +} diff --git a/ui/mainwindow.h b/ui/mainwindow.h index ebd4791..862adf2 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "common/myeventfilter.h" #include "common/pm3process.h" @@ -51,7 +52,7 @@ public slots: void refreshCMD(const QString& cmd); void setStatusBar(QLabel* target, const QString& text); void onPM3StateChanged(bool st, const QString& info); - void MF_onTypeChanged(int id, bool st); + void MF_onMFCardTypeChanged(int id, bool st); void on_Raw_CMDEdit_keyPressed(QObject *obj_addr, QEvent &event); void on_MF_keyWidget_resized(QObject *obj_addr, QEvent &event); private slots: @@ -65,7 +66,8 @@ private slots: void on_Raw_clearOutputButton_clicked(); void sendMSG(); - void on_PM3_refreshPortButton_clicked(); + + void on_portSearchTimer_timeout(); void on_Raw_CMDHistoryBox_stateChanged(int arg1); @@ -154,29 +156,26 @@ private slots: void on_MF_selectTrailerBox_stateChanged(int arg1); void on_stopButton_clicked(); + void on_Raw_CMDEdit_textChanged(const QString &arg1); void on_MF_Attack_darksideButton_clicked(); - void on_Set_Client_envDeleteButton_clicked(); - - void on_Set_Client_envAddButton_clicked(); - - void on_Set_Client_envSaveButton_clicked(); - - void loadClientPreloadEnv(); - void on_Set_Client_startArgsEdit_editingFinished(); void on_Set_Client_forceEnabledBox_stateChanged(int arg1); - void on_Set_Client_envClearButton_clicked(); - void on_Set_GUI_setLanguageButton_clicked(); + void setButtonsEnabled(bool st); + + void on_PM3_refreshPortButton_clicked(); + + void on_Set_Client_envScriptEdit_editingFinished(); + private: Ui::MainWindow* ui; - QButtonGroup* typeBtnGroup; + QButtonGroup* MFCardTypeBtnGroup; QLabel* connectStatusBar; QLabel* programStatusBar; QLabel* PM3VersionBar; @@ -197,6 +196,9 @@ private: bool pm3state; bool keepButtonsEnabled; QThread* pm3Thread; + QTimer* portSearchTimer; + QStringList portList; + QStringList clientEnv; Mifare* mifare; Util* util; @@ -211,7 +213,9 @@ private: void saveClientPath(const QString& path); signals: void connectPM3(const QString& path, const QString& port, const QStringList args); + void reconnectPM3(); void killPM3(); void setSerialListener(const QString& name, bool state); + void setProcEnv(const QStringList *env); }; #endif // MAINWINDOW_H diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 642ae2d..d1fa6d4 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -6,14 +6,14 @@ 0 0 - 1029 - 770 + 1050 + 700 - 970 - 770 + 800 + 600 @@ -53,13 +53,20 @@ - Path: + Client Path: + + + + Port: + + + @@ -68,6 +75,9 @@ 0 + + true + @@ -79,7 +89,7 @@ - Refresh + Refresh Ports @@ -1528,7 +1538,7 @@ Settings - + @@ -1540,88 +1550,23 @@ - Preload environment variables + Preload script path: - - - - - QAbstractItemView::SelectRows - - - true - - - false - - - - Variable - - - - - Value - - - - - - - - - - Add - - - - - - - Delete - - - - - - - Clear - - - - - - - Save - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - + + + + + Note: -If the variable name already exists, this app will add the new value to the head of the existing one, so these new values have higher priority when calling Proxmark3 client. -The environment variables added here won't affect other apps. +If the client requires some enviroment variables, you can make a script file(*.bat on Windows or *.sh on Linux) to configure them, +then put the path of the script there true @@ -1709,55 +1654,62 @@ or "-p <port> -f" - - - GUI - - - - - - - - Language: - - - - - - - Choose Language - - - - - - - (Restart this app to use new language) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - + + + Qt::Horizontal + + + + + GUI + + + + + + + + Language: + + + + + + + Choose Language + + + + + + + (Restart this app to use new language) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + From 6baa09221e83072a5d1715ae6d037729ba9df53d Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 15 Feb 2021 23:00:02 +0800 Subject: [PATCH 16/20] Support chk(), nested() in latest Iceman repo --- common/pm3process.cpp | 7 +++++-- module/mifare.cpp | 36 ++++++++++++++++++++---------------- module/mifare.h | 1 + testlog.md | 25 ------------------------- 4 files changed, 26 insertions(+), 43 deletions(-) delete mode 100644 testlog.md diff --git a/common/pm3process.cpp b/common/pm3process.cpp index 269a4f0..a80397c 100644 --- a/common/pm3process.cpp +++ b/common/pm3process.cpp @@ -37,8 +37,11 @@ void PM3Process::connectPM3(const QString& path, const QString& port, const QStr clientType = Util::CLIENTTYPE_ICEMAN; setRequiringOutput(true); write("hw version\r\n"); - waitForReadyRead(1000); - result = *requiredOutput; + for(int i = 0; i < 10; i++) + { + waitForReadyRead(200); + result += *requiredOutput; + } setRequiringOutput(false); } else diff --git a/module/mifare.cpp b/module/mifare.cpp index bc6b10e..3acfb0f 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -6,7 +6,8 @@ const Mifare::CardType Mifare::card_mini = 5, 20, {4, 4, 4, 4, 4}, - {0, 4, 8, 12, 16} + {0, 4, 8, 12, 16}, + "mini" }; const Mifare::CardType Mifare::card_1k = { @@ -14,7 +15,8 @@ const Mifare::CardType Mifare::card_1k = 16, 64, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60} + {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60}, + "1k" }; const Mifare::CardType Mifare::card_2k = { @@ -22,7 +24,8 @@ const Mifare::CardType Mifare::card_2k = 32, 128, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, - {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124} + {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124}, + "2k" }; const Mifare::CardType Mifare::card_4k = { @@ -30,7 +33,8 @@ const Mifare::CardType Mifare::card_4k = 40, 256, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 16, 16, 16, 16, 16, 16, 16, 16}, - {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 144, 160, 176, 192, 208, 224, 240} + {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 144, 160, 176, 192, 208, 224, 240}, + "4k" }; const Mifare::AccessType Mifare::dataCondition[8][4] = @@ -79,8 +83,8 @@ Mifare::Mifare(Ui::MainWindow *ui, Util *addr, QWidget *parent): QObject(parent) data_clearKey(); // fill with blank QString data_clearData(); // fill with blank QString dataPattern = new QRegularExpression("([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"); - keyPattern_res = new QRegularExpression("\\|\\d{3}\\|.+?\\|.+?\\|.+?\\|.+?\\|"); - keyPattern = new QRegularExpression("\\|\\d{3}\\|.+?\\|.+?\\|"); + keyPattern_res = new QRegularExpression("\\|\\s+\\d{3}\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|"); + keyPattern = new QRegularExpression("\\|\\s+\\d{3}\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|"); } QString Mifare::info(bool isRequiringOutput) @@ -148,10 +152,10 @@ void Mifare::chk() else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) { result = util->execCMDWithOutput( - "hf mf chk *" - + QString::number(cardType.type) - + " ?", - Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|---\\|----------------\\|"})); + "hf mf chk --" + + cardType.typeText, + Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", keyPattern_res->pattern()})); + qDebug() << "mf_chk_iceman_result" << result; for(int i = 0; i < cardType.sector_size; i++) { reMatch = keyPattern_res->match(result, offset); @@ -188,7 +192,7 @@ void Mifare::nested() "hf mf nested " + QString::number(cardType.type) + " *", - Util::ReturnTrigger(10000, {"Can't found", "\\|000\\|"})); + Util::ReturnTrigger(15000, {"Can't found", "\\|000\\|"})); } else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) { @@ -197,7 +201,7 @@ void Mifare::nested() { if(data_isKeyValid(keyAList->at(i))) { - knownKeyInfo = " " + QString::number(i * 4) + " A " + keyAList->at(i); + knownKeyInfo = " --blk " + QString::number(i * 4) + " -a -k " + keyAList->at(i); break; } } @@ -207,7 +211,7 @@ void Mifare::nested() { if(data_isKeyValid(keyBList->at(i))) { - knownKeyInfo = " " + QString::number(i * 4) + " B " + keyBList->at(i); + knownKeyInfo = " --blk " + QString::number(i * 4) + " -b -k " + keyBList->at(i); break; } } @@ -215,10 +219,10 @@ void Mifare::nested() if(knownKeyInfo != "") { result = util->execCMDWithOutput( - "hf mf nested " - + QString::number(cardType.type) + "hf mf nested --" + + cardType.typeText + knownKeyInfo, - Util::ReturnTrigger(10000, {"key is wrong", "\\|000\\|"})); + Util::ReturnTrigger(15000, {"Can't authenticate", keyPattern_res->pattern()})); } else { diff --git a/module/mifare.h b/module/mifare.h index b825d56..869d992 100644 --- a/module/mifare.h +++ b/module/mifare.h @@ -37,6 +37,7 @@ public: quint16 block_size; quint8 blk[40]; quint8 blks[40]; + QString typeText; }; enum AccessType diff --git a/testlog.md b/testlog.md deleted file mode 100644 index 1e11118..0000000 --- a/testlog.md +++ /dev/null @@ -1,25 +0,0 @@ -# Some Test Log - -*** - -# Version:0.1.3 -## Mifare -### Card Info -+ Official, Mifare card: Passed -+ Iceman, Mifare card: Passed -+ Official, no card: Passed -+ Iceman, no card: Passed - -### Check default Password -+ Official, all FFFFFFFFFFFF: Passed -+ Iceman, all FFFFFFFFFFFF: Passed -+ Official, no card: Passed -+ Iceman, no card: Stuck to searching card, then failed to communicate with PM3 -+ Official, all unknown: Passed -+ Iceman, all unknown: Passed -+ Official, partially unknown: Passed -+ Iceman, partially unknown: Passed - -### Darkside Attack -+ Official: Passed -+ Iceman: Passed \ No newline at end of file From 24a6e1869bd4eb6e37922e3b30473f881e56682e Mon Sep 17 00:00:00 2001 From: wh201906 Date: Mon, 15 Feb 2021 23:46:05 +0800 Subject: [PATCH 17/20] Support hardnested(), darkside(), readblk(), readsec() in latest Iceman repo --- common/util.cpp | 8 ++++--- common/util.h | 6 +++--- lang/en_US.ts | 30 +++++++++++++------------- lang/zh_CN.ts | 34 ++++++++++++++--------------- module/mifare.cpp | 36 +++++++++++++++++++++++-------- ui/mf_attack_hardnesteddialog.cpp | 32 ++++++++++++++++++--------- ui/mf_attack_hardnesteddialog.h | 1 + 7 files changed, 90 insertions(+), 57 deletions(-) diff --git a/common/util.cpp b/common/util.cpp index 3443441..c620b97 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -1,11 +1,12 @@ #include "util.h" +Util::ClientType Util::clientType = CLIENTTYPE_OFFICIAL; + Util::Util(QObject *parent) : QObject(parent) { isRequiringOutput = false; requiredOutput = new QString(); timeStamp = QTime::currentTime(); - this->clientType = CLIENTTYPE_OFFICIAL; qRegisterMetaType("Util::ClientType"); } @@ -79,14 +80,15 @@ void Util::delay(unsigned int msec) while(QTime::currentTime() < timer) QApplication::processEvents(QEventLoop::AllEvents, 100); } + Util::ClientType Util::getClientType() { - return this->clientType; + return Util::clientType; } void Util::setClientType(Util::ClientType clientType) { - this->clientType = clientType; + Util::clientType = clientType; } void Util::setRunningState(bool st) diff --git a/common/util.h b/common/util.h index 3327330..fed751e 100644 --- a/common/util.h +++ b/common/util.h @@ -52,12 +52,12 @@ public: void execCMD(const QString& cmd); QString execCMDWithOutput(const QString& cmd, ReturnTrigger trigger = 10000); void delay(unsigned int msec); - ClientType getClientType(); + static ClientType getClientType(); static const int rawTabIndex = 1; static bool chooseLanguage(QSettings *guiSettings, QMainWindow *window); public slots: void processOutput(const QString& output); - void setClientType(Util::ClientType clientType); + static void setClientType(Util::ClientType clientType); void setRunningState(bool st); private: @@ -65,7 +65,7 @@ private: bool isRunning; QString* requiredOutput; QTime timeStamp; - ClientType clientType; + static ClientType clientType; signals: void refreshOutput(const QString& output); void write(QString data); // connected to PM3Process::write(QString data); diff --git a/lang/en_US.ts b/lang/en_US.ts index a9aff47..0df4a6c 100644 --- a/lang/en_US.ts +++ b/lang/en_US.ts @@ -989,56 +989,56 @@ or "-p <port> -f" Mifare - + Success! - - + - - - - + + + + + Info - + Plz provide at least one known key - - + + Failed! - + The Access Bits is invalid! It could make the whole sector blocked irreversibly! Continue to write? - + Successful! - + Failed to write to these blocks: - + Select them? - + Failed to read card. diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index 957f603..3ff7b4f 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -532,13 +532,13 @@ It could make the whole sector blocked irreversibly! Read One - 读取单个区 + 读取单个块 Write One - 写入单个区 + 写入单个块 @@ -997,34 +997,34 @@ or "-p <port> -f" Mifare - + Success! 成功! - - + - - - - + + + + + Info 信息 - + Plz provide at least one known key 请至少提供一个已知密码 - - + + Failed! 失败! - + The Access Bits is invalid! It could make the whole sector blocked irreversibly! Continue to write? @@ -1033,22 +1033,22 @@ Continue to write? 确定要写入吗? - + Successful! 成功! - + Failed to write to these blocks: 写入以下块失败: - + Select them? 选中这些块? - + Failed to read card. 读卡失败。 diff --git a/module/mifare.cpp b/module/mifare.cpp index 3acfb0f..95eab92 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -298,6 +298,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe { QString data; QString result; + QRegularExpressionMatch currMatch; bool isTrailerBlock = (blockId < 128 && ((blockId + 1) % 4 == 0)) || ((blockId + 1) % 16 == 0); if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) @@ -317,9 +318,10 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe + " " + key, waitTime); - if(result.indexOf("isOk:01") != -1) + currMatch = dataPattern->match(result); + if(currMatch.hasMatch()) { - data = dataPattern->match(result).captured().toUpper(); + data = currMatch.captured().toUpper(); data.remove(" "); // when the target block is a key block and the given key type is KeyA, try to check whether the KeyB is valid(by Access Bits) // if the given key type is KeyB, it will never get the KeyA from the key block @@ -347,15 +349,19 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe "hf mf cgetblk " + QString::number(blockId), waitTime); - if(result.indexOf("Chinese magic") != -1) + currMatch = dataPattern->match(result); + if(currMatch.hasMatch()) { - data = dataPattern->match(result).captured().toUpper(); + data = currMatch.captured().toUpper(); data.remove(" "); } else data = ""; } - else if(targetType == TARGET_EMULATOR) + } + if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + if(targetType == TARGET_EMULATOR) { result = util->execCMDWithOutput( "hf mf eget " @@ -365,6 +371,18 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe data.remove(" "); } } + else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + { + if(targetType == TARGET_EMULATOR) + { + result = util->execCMDWithOutput( + "hf mf egetblk " + + QString::number(blockId), + 150); + data = dataPattern->match(result).captured().toUpper(); + data.remove(" "); + } + } return data; } @@ -373,7 +391,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, QStringList data; QString result, tmp; QRegularExpressionMatch reMatch; - int offset = -1; + int offset = -1; // for targetType == TARGET_EMULATOR for(int i = 0; i < cardType.blk[sectorId]; i++) { @@ -397,7 +415,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, + " " + key, waitTime); - offset = result.indexOf("isOk:01"); + offset = result.indexOf("isOk:01"); // find successful flag } else if(targetType == TARGET_UID) { @@ -405,7 +423,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, "hf mf cgetsc " + QString::number(sectorId), waitTime); - offset = result.indexOf("Chinese magic"); + offset = result.indexOf("error") == -1 ? 0 : -1; // find failed flag } if(offset != -1) { @@ -424,7 +442,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, } // if failed, try to read them seperately. // (when one of the block cannot be read, the rdsc will return nothing, so you need to read the rest of blocks manually) - else if(targetType != TARGET_UID) // if the targetType is Chinese Magic Card, then the result implies the backdoor command is invalid. + else if(targetType == TARGET_UID || targetType == TARGET_EMULATOR) // if the targetType is Chinese Magic Card, then the result implies the backdoor command is invalid. { for(int i = 0; i < cardType.blk[sectorId]; i++) data[i] = _readblk(cardType.blks[sectorId] + i, keyType, key, targetType, waitTime); diff --git a/ui/mf_attack_hardnesteddialog.cpp b/ui/mf_attack_hardnesteddialog.cpp index 6896764..79b01c6 100644 --- a/ui/mf_attack_hardnesteddialog.cpp +++ b/ui/mf_attack_hardnesteddialog.cpp @@ -21,14 +21,26 @@ MF_Attack_hardnestedDialog::~MF_Attack_hardnestedDialog() void MF_Attack_hardnestedDialog::on_buttonBox_accepted() { - emit sendCMD("hf mf hardnested " - + ui->knownKeySectorBox->currentText() - + " " - + ui->knownKeyTypeBox->currentText() - + " " - + ui->knownKeyBox->text() - + " " - + ui->targetKeySectorBox->currentText() - + " " - + ui->targetKeyTypeBox->currentText()); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + emit sendCMD("hf mf hardnested " + + ui->knownKeySectorBox->currentText() + + " " + + ui->knownKeyTypeBox->currentText() + + " " + + ui->knownKeyBox->text() + + " " + + ui->targetKeySectorBox->currentText() + + " " + + ui->targetKeyTypeBox->currentText()); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) // same format in v4.9237 + emit sendCMD("hf mf hardnested " + + ui->knownKeySectorBox->currentText() + + " " + + ui->knownKeyTypeBox->currentText() + + " " + + ui->knownKeyBox->text() + + " " + + ui->targetKeySectorBox->currentText() + + " " + + ui->targetKeyTypeBox->currentText()); } diff --git a/ui/mf_attack_hardnesteddialog.h b/ui/mf_attack_hardnesteddialog.h index d120b2a..785cb75 100644 --- a/ui/mf_attack_hardnesteddialog.h +++ b/ui/mf_attack_hardnesteddialog.h @@ -2,6 +2,7 @@ #define MF_ATTACK_HARDNESTEDDIALOG_H #include +#include "common/util.h" namespace Ui { From 63f3424871ca43b1a41d49ce138aabbc7cd9d2e4 Mon Sep 17 00:00:00 2001 From: wh201906 Date: Tue, 16 Feb 2021 01:17:50 +0800 Subject: [PATCH 18/20] Support wipe(), lockUFUID(), setUIDParameter(), in latest Iceman repo Fix a error in keyPattern Test all R/W functions in Iceman client and Official client --- common/util.cpp | 1 + module/mifare.cpp | 76 ++++++++++++++++++++++------------- ui/mf_uid_parameterdialog.cpp | 20 ++++++--- ui/mf_uid_parameterdialog.h | 1 + 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/common/util.cpp b/common/util.cpp index c620b97..9e360b4 100644 --- a/common/util.cpp +++ b/common/util.cpp @@ -49,6 +49,7 @@ QString Util::execCMDWithOutput(const QString& cmd, ReturnTrigger trigger) if(!isRunning) break; QApplication::processEvents(); +// qDebug() << "currOutput:" << *requiredOutput; for(QString otpt : trigger.expectedOutputs) { re.setPattern(otpt); diff --git a/module/mifare.cpp b/module/mifare.cpp index 95eab92..5cb6f38 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -83,8 +83,8 @@ Mifare::Mifare(Ui::MainWindow *ui, Util *addr, QWidget *parent): QObject(parent) data_clearKey(); // fill with blank QString data_clearData(); // fill with blank QString dataPattern = new QRegularExpression("([0-9a-fA-F]{2} ){15}[0-9a-fA-F]{2}"); - keyPattern_res = new QRegularExpression("\\|\\s+\\d{3}\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|"); - keyPattern = new QRegularExpression("\\|\\s+\\d{3}\\s+\\|\\s+.+?\\s+\\|\\s+.+?\\s+\\|"); + keyPattern_res = new QRegularExpression("\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|"); + keyPattern = new QRegularExpression("\\|\\s*\\d{3}\\s*\\|\\s*.+?\\s*\\|\\s*.+?\\s*\\|"); } QString Mifare::info(bool isRequiringOutput) @@ -94,15 +94,13 @@ QString Mifare::info(bool isRequiringOutput) if(isRequiringOutput) { QString result = util->execCMDWithOutput("hf 14a info", 500); - result.replace("UID :", "|||"); - result.replace("ATQA :", "|||"); - result.replace("SAK :", "|||"); - result.replace("TYPE :", "|||"); - QStringList lis = result.split("|||"); - if(lis.length() > 4) + int begin, end; + begin = result.indexOf("UID"); + if(begin != -1) { - qDebug() << lis[1] + lis[2] + lis[3]; - return lis[1] + lis[2] + lis[3]; + end = result.indexOf("SAK", begin); + end = result.indexOf("\n", end); + return result.mid(begin, end - begin + 1); } else return ""; @@ -128,7 +126,8 @@ void Mifare::chk() "hf mf chk *" + QString::number(cardType.type) + " ?", - Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", "\\|---\\|----------------\\|----------------\\|"})); + Util::ReturnTrigger(1000 + cardType.sector_size * 200, {"No valid", keyPattern->pattern()})); + qDebug() << result; for(int i = 0; i < cardType.sector_size; i++) { reMatch = keyPattern->match(result, offset); @@ -610,7 +609,7 @@ bool Mifare::_writeblk(int blockId, KeyType keyType, const QString& key, const Q + " " + input, waitTime); - return (result.indexOf("Chinese magic") != -1); + return (result.indexOf("error") == -1); // failed flag } else if(targetType == TARGET_EMULATOR) { @@ -747,10 +746,17 @@ void Mifare::restore() void Mifare::wipeC() { - util->execCMD( - "hf mf cwipe " - + QString::number(cardType.type) - + " f"); + if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + util->execCMD( + "hf mf cwipe " + + QString::number(cardType.type) + + " f"); + } + else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + { + util->execCMD("hf mf cwipe"); + } ui->funcTab->setCurrentIndex(Util::rawTabIndex); } @@ -761,11 +767,15 @@ void Mifare::setParameterC() QMessageBox::information(parent, tr("Info"), tr("Failed to read card.")); else { - QStringList lis = result.split("\r\n"); - lis[0].replace(" ", ""); - lis[1].replace(" ", ""); - lis[2].replace(" ", ""); - MF_UID_parameterDialog dialog(lis[0].toUpper(), lis[1].toUpper(), lis[2].mid(0, 2).toUpper()); + result.replace("\r\n", ""); + result.replace(QRegularExpression("\\[.\\]"), ""); + result.replace("UID", ""); + result.replace("ATQA", ""); + result.replace("SAK", ""); + result.replace(" ", ""); + QStringList lis = result.split(':'); + qDebug() << lis; + MF_UID_parameterDialog dialog(lis[1].toUpper(), lis[2].toUpper(), lis[3].toUpper()); connect(&dialog, &MF_UID_parameterDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) ui->funcTab->setCurrentIndex(Util::rawTabIndex); @@ -774,12 +784,24 @@ void Mifare::setParameterC() void Mifare::lockC() { - util->execCMD("hf 14a raw -pa -b7 40"); - util->execCMD("hf 14a raw -pa 43"); - util->execCMD("hf 14a raw -pa E0 00 39 F7"); - util->execCMD("hf 14a raw -pa E1 00 E1 EE"); - util->execCMD("hf 14a raw -pa 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47"); - util->execCMD("hf 14a raw 52"); + if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + util->execCMD("hf 14a raw -pa -b7 40"); + util->execCMD("hf 14a raw -pa 43"); + util->execCMD("hf 14a raw -pa E0 00 39 F7"); + util->execCMD("hf 14a raw -pa E1 00 E1 EE"); + util->execCMD("hf 14a raw -pa 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47"); + util->execCMD("hf 14a raw -a 52"); + } + else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + { + util->execCMD("hf 14a raw -ak -b 7 40"); + util->execCMD("hf 14a raw -ak 43"); + util->execCMD("hf 14a raw -ak E0 00 39 F7"); + util->execCMD("hf 14a raw -ak E1 00 E1 EE"); + util->execCMD("hf 14a raw -ak 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47"); + util->execCMD("hf 14a raw -a 52"); + } } void Mifare::wipeE() diff --git a/ui/mf_uid_parameterdialog.cpp b/ui/mf_uid_parameterdialog.cpp index e30705d..030d3b1 100644 --- a/ui/mf_uid_parameterdialog.cpp +++ b/ui/mf_uid_parameterdialog.cpp @@ -18,10 +18,18 @@ MF_UID_parameterDialog::~MF_UID_parameterDialog() void MF_UID_parameterDialog::on_buttonBox_accepted() { - emit sendCMD("hf mf csetuid " - + ui->UIDLineEdit->text() - + " " - + ui->ATQALineEdit->text() - + " " - + ui->SAKLineEdit->text()); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + emit sendCMD("hf mf csetuid " + + ui->UIDLineEdit->text() + + " " + + ui->ATQALineEdit->text() + + " " + + ui->SAKLineEdit->text()); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) // same format in v4.9237 + emit sendCMD("hf mf csetuid " + + ui->UIDLineEdit->text() + + " " + + ui->ATQALineEdit->text() + + " " + + ui->SAKLineEdit->text()); } diff --git a/ui/mf_uid_parameterdialog.h b/ui/mf_uid_parameterdialog.h index 85e9fc0..8812168 100644 --- a/ui/mf_uid_parameterdialog.h +++ b/ui/mf_uid_parameterdialog.h @@ -2,6 +2,7 @@ #define MF_UID_PARAMETERDIALOG_H #include +#include "common/util.h" namespace Ui { From 4f10e3d75cb0e4dbce9139e401173e287780321d Mon Sep 17 00:00:00 2001 From: wh201906 Date: Tue, 16 Feb 2021 13:52:04 +0800 Subject: [PATCH 19/20] All functions are compatible to Iceman/RRG fork Use different foreground color for Trailer block and Block 0 --- .gitignore | 1 - lang/en_US.qm | Bin 0 -> 23 bytes lang/en_US.ts | 128 ++++--- lang/zh_CN.qm | Bin 0 -> 16922 bytes lang/zh_CN.ts | 130 ++++--- module/mifare.cpp | 85 +++-- module/mifare.h | 2 +- ui/mainwindow.cpp | 7 +- ui/mainwindow.h | 2 +- ui/mainwindow.ui | 4 +- ui/mf_sim_simdialog.cpp | 55 ++- ui/mf_sim_simdialog.h | 4 +- ui/mf_sim_simdialog.ui | 781 +++++++++++++++++++++++++++------------- 13 files changed, 805 insertions(+), 394 deletions(-) create mode 100644 lang/en_US.qm create mode 100644 lang/zh_CN.qm diff --git a/.gitignore b/.gitignore index 1358d83..fb50266 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ *.so.* *_pch.h.cpp *_resource.rc -*.qm .#* *.*# core diff --git a/lang/en_US.qm b/lang/en_US.qm new file mode 100644 index 0000000000000000000000000000000000000000..9dad8dffceb9623e88f8b96d9cd0caf25574c6fa GIT binary patch literal 23 fcmcE7ks@*G{hX<16=n7(EZlpygMop8iIEWihQJ9+ literal 0 HcmV?d00001 diff --git a/lang/en_US.ts b/lang/en_US.ts index 0df4a6c..f73b0da 100644 --- a/lang/en_US.ts +++ b/lang/en_US.ts @@ -45,72 +45,102 @@ - + u - + UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used - + + --atqa + + + + + Provide explicit ATQA (2 bytes) + + + + + --sak + + + + n - + Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite - + i - + Interactive, means that console will not be returned until simulation finishes or is aborted - + x - + Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) - + e - - set keys found from 'reader attack' to emulator memory (implies x and i) + + set keys found from 'reader attack' to emulator memory (implies x(--crack) and i) - + + -v + + + + + verbose output + + + + f - + + Provide explicit SAK (1 byte) + + + + get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) - + r - + Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works @@ -352,7 +382,7 @@ It could make the whole sector blocked irreversibly! - + Data @@ -411,11 +441,6 @@ It could make the whole sector blocked irreversibly! Key Type: - - - Snoop - - List Data @@ -556,6 +581,11 @@ It could make the whole sector blocked irreversibly! Sniff + + + Sniff(14a) + + LF/Data @@ -739,7 +769,7 @@ or "-p <port> -f" - + Not Connected @@ -934,54 +964,54 @@ or "-p <port> -f" - - + + Idle - + Stop - - + + Sec - + Blk - + KeyA - + KeyB - + HW Version: - + PM3: - + State: - + Running @@ -989,56 +1019,56 @@ or "-p <port> -f" Mifare - + Success! - - - + + - - - + + + + Info - + Plz provide at least one known key - - + + Failed! - + The Access Bits is invalid! It could make the whole sector blocked irreversibly! Continue to write? - + Successful! - + Failed to write to these blocks: - + Select them? - + Failed to read card. diff --git a/lang/zh_CN.qm b/lang/zh_CN.qm new file mode 100644 index 0000000000000000000000000000000000000000..3edc62af218d3d62201f97afbe22450d6656d6fb GIT binary patch literal 16922 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 E|D47u?*IS* literal 0 HcmV?d00001 diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index 3ff7b4f..3efde9e 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -45,72 +45,102 @@ 模拟 - + u - + UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used 4或7字节的UID,如果不指定,则使用模拟器内存中的4字节UID - + + --atqa + + + + + Provide explicit ATQA (2 bytes) + 指定ATQA(2个字节) + + + + --sak + + + + + Provide explicit SAK (1 byte) + 指定SAK(1个字节) + + + n - + Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite 在读卡器读取<n>个块后自动退出模拟,n为0或不指定时永远不退出 - + i - + Interactive, means that console will not be returned until simulation finishes or is aborted 交互模式,勾选后PM3客户端将在模拟完成或者模拟中断后才可继续使用 - + x - + Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) 破解,对读卡器进行攻击,通过nr/ar攻击来钓出密码(无卡嗅探) - + e - - set keys found from 'reader attack' to emulator memory (implies x and i) - 在获得密码后自动将密码写入模拟器内存(自动勾选x和i) + + set keys found from 'reader attack' to emulator memory (implies x(--crack) and i) + 在获得密码后自动将密码写入模拟器内存(自动勾选x(--crack)和i) - + + -v + + + + + verbose output + 更多输出内容 + + + f - + get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) 从<filename.txt>当中获取用于破解读卡器的UID(批量模拟)(自动勾选x和i) - + r - + Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works 生成随机nonce而不是顺序的nonce,这种情况下PM3将不对读卡器进行标准攻击,只进行moebius攻击 @@ -356,7 +386,7 @@ It could make the whole sector blocked irreversibly! - + Data 数据 @@ -415,11 +445,6 @@ It could make the whole sector blocked irreversibly! Key Type: 密钥类型: - - - Snoop - 嗅探(Snoop) - List Data @@ -560,6 +585,11 @@ It could make the whole sector blocked irreversibly! Sniff 嗅探 + + + Sniff(14a) + 嗅探(14a) + LF/Data @@ -747,7 +777,7 @@ or "-p <port> -f" - + Not Connected 未连接 @@ -942,54 +972,54 @@ or "-p <port> -f" Trace文件(*.trc) - - + + Idle 空闲 - + Stop 停止 - - + + Sec 扇区 - + Blk - + KeyA 密钥A - + KeyB 密钥B - + HW Version: 固件版本: - + PM3: 连接状态: - + State: 运行状态: - + Running 正在运行 @@ -997,34 +1027,34 @@ or "-p <port> -f" Mifare - + Success! 成功! - - - + + - - - + + + + Info 信息 - + Plz provide at least one known key 请至少提供一个已知密码 - - + + Failed! 失败! - + The Access Bits is invalid! It could make the whole sector blocked irreversibly! Continue to write? @@ -1033,22 +1063,22 @@ Continue to write? 确定要写入吗? - + Successful! 成功! - + Failed to write to these blocks: 写入以下块失败: - + Select them? 选中这些块? - + Failed to read card. 读卡失败。 diff --git a/module/mifare.cpp b/module/mifare.cpp index 5cb6f38..064a9ae 100644 --- a/module/mifare.cpp +++ b/module/mifare.cpp @@ -89,7 +89,7 @@ Mifare::Mifare(Ui::MainWindow *ui, Util *addr, QWidget *parent): QObject(parent) QString Mifare::info(bool isRequiringOutput) { - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { if(isRequiringOutput) { @@ -120,7 +120,7 @@ void Mifare::chk() QString result; int offset = 0; QString data; - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) { result = util->execCMDWithOutput( "hf mf chk *" @@ -148,7 +148,7 @@ void Mifare::chk() } } } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { result = util->execCMDWithOutput( "hf mf chk --" @@ -185,7 +185,7 @@ void Mifare::nested() QString result; int offset = 0; QString data; - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) { result = util->execCMDWithOutput( "hf mf nested " @@ -193,7 +193,7 @@ void Mifare::nested() + " *", Util::ReturnTrigger(15000, {"Can't found", "\\|000\\|"})); } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { QString knownKeyInfo = ""; for(int i = 0; i < cardType.sector_size; i++) @@ -262,34 +262,41 @@ void Mifare::hardnested() void Mifare::darkside() { - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) - { + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) util->execCMD("hf mf mifare"); - ui->funcTab->setCurrentIndex(Util::rawTabIndex); - } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) - { + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) util->execCMD("hf mf darkside"); - ui->funcTab->setCurrentIndex(Util::rawTabIndex); - } + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::sniff() { - util->execCMD("hf mf sniff"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf mf sniff"); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf sniff"); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } -void Mifare::snoop() +void Mifare::sniff14a() { - util->execCMD("hf 14a snoop"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf 14a snoop"); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf 14a sniff"); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::list() { - util->execCMD("hf list mf"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf list mf"); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("trace list -t mf"); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } @@ -300,7 +307,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe QRegularExpressionMatch currMatch; bool isTrailerBlock = (blockId < 128 && ((blockId + 1) % 4 == 0)) || ((blockId + 1) % 16 == 0); - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { if(targetType == TARGET_MIFARE) { @@ -358,7 +365,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe data = ""; } } - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) { if(targetType == TARGET_EMULATOR) { @@ -370,7 +377,7 @@ QString Mifare::_readblk(int blockId, KeyType keyType, const QString& key, Targe data.remove(" "); } } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { if(targetType == TARGET_EMULATOR) { @@ -397,7 +404,7 @@ QStringList Mifare::_readsec(int sectorId, KeyType keyType, const QString& key, data.append(""); } - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { // try to read all blocks together if(targetType == TARGET_MIFARE) @@ -583,7 +590,7 @@ bool Mifare::_writeblk(int blockId, KeyType keyType, const QString& key, const Q if(data_isDataValid(input) != DATA_NOSPACE) return false; - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL || util->getClientType() == Util::CLIENTTYPE_ICEMAN) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { if(targetType == TARGET_MIFARE) { @@ -734,26 +741,28 @@ void Mifare::writeSelected(TargetType targetType) void Mifare::dump() { - util->execCMD("hf mf dump"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf mf dump"); ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::restore() { - util->execCMD("hf mf restore"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf mf restore"); ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::wipeC() { - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) { util->execCMD( "hf mf cwipe " + QString::number(cardType.type) + " f"); } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { util->execCMD("hf mf cwipe"); } @@ -784,7 +793,7 @@ void Mifare::setParameterC() void Mifare::lockC() { - if(util->getClientType() == Util::CLIENTTYPE_OFFICIAL) + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) { util->execCMD("hf 14a raw -pa -b7 40"); util->execCMD("hf 14a raw -pa 43"); @@ -793,7 +802,7 @@ void Mifare::lockC() util->execCMD("hf 14a raw -pa 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 18 47"); util->execCMD("hf 14a raw -a 52"); } - else if(util->getClientType() == Util::CLIENTTYPE_ICEMAN) + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) { util->execCMD("hf 14a raw -ak -b 7 40"); util->execCMD("hf 14a raw -ak 43"); @@ -806,12 +815,13 @@ void Mifare::lockC() void Mifare::wipeE() { - util->execCMD("hf mf eclr"); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL || Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("hf mf eclr"); } void Mifare::simulate() { - MF_Sim_simDialog dialog(cardType.type); + MF_Sim_simDialog dialog(cardType.type, cardType.typeText); connect(&dialog, &MF_Sim_simDialog::sendCMD, util, &Util::execCMD); if(dialog.exec() == QDialog::Accepted) ui->funcTab->setCurrentIndex(Util::rawTabIndex); @@ -819,13 +829,24 @@ void Mifare::simulate() void Mifare::loadSniff(const QString& file) { - util->execCMD("hf list mf -l " + file); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf list mf -l " + file); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + if(util->execCMDWithOutput("trace load -f " + file, Util::ReturnTrigger({"loaded"})) != "") + util->execCMD("trace list -t mf"); + } + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } void Mifare::saveSniff(const QString& file) { - util->execCMD("hf list mf -s " + file); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + util->execCMD("hf list mf -s " + file); + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + util->execCMD("trace save -f " + file); + ui->funcTab->setCurrentIndex(Util::rawTabIndex); } diff --git a/module/mifare.h b/module/mifare.h index 869d992..b50ff33 100644 --- a/module/mifare.h +++ b/module/mifare.h @@ -70,7 +70,7 @@ public: void darkside(); void hardnested(); void sniff(); - void snoop(); + void sniff14a(); void list(); void readOne(TargetType targetType = TARGET_MIFARE); void readSelected(TargetType targetType = TARGET_MIFARE); diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 297520e..673acdc 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -836,10 +836,10 @@ void MainWindow::on_MF_Sniff_sniffButton_clicked() setState(true); } -void MainWindow::on_MF_Sniff_snoopButton_clicked() +void MainWindow::on_MF_14aSniff_snoopButton_clicked() { setState(false); - mifare->snoop(); + mifare->sniff14a(); setState(true); } @@ -852,6 +852,7 @@ void MainWindow::MF_widgetReset() { int secs = mifare->cardType.sector_size; int blks = mifare->cardType.block_size; + QBrush trailerItemForeColor = QBrush(QColor(0, 160, 255)); ui->MF_RW_blockBox->clear(); ui->MF_keyWidget->setRowCount(secs); ui->MF_dataWidget->setRowCount(blks); @@ -876,8 +877,10 @@ void MainWindow::MF_widgetReset() setTableItem(ui->MF_keyWidget, i, 1, ""); setTableItem(ui->MF_keyWidget, i, 2, ""); setTableItem(ui->MF_dataWidget, mifare->cardType.blks[i], 0, QString::number(i)); + ui->MF_dataWidget->item(mifare->cardType.blks[i] + mifare->cardType.blk[i] - 1, 2)->setForeground(trailerItemForeColor); ui->MF_dataWidget->item(mifare->cardType.blks[i], 0)->setCheckState(Qt::Checked); } + ui->MF_dataWidget->item(0, 2)->setForeground(QBrush(QColor(255, 160, 0))); ui->MF_selectAllBox->setCheckState(Qt::Checked); ui->MF_selectTrailerBox->setCheckState(Qt::Checked); diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 862adf2..b2747c9 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -149,7 +149,7 @@ private slots: void on_MF_fillKeysButton_clicked(); - void on_MF_Sniff_snoopButton_clicked(); + void on_MF_14aSniff_snoopButton_clicked(); void on_MF_trailerDecoderButton_clicked(); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index d1fa6d4..5143dba 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -1129,7 +1129,7 @@ - + 40 @@ -1137,7 +1137,7 @@ - Snoop + Sniff(14a) diff --git a/ui/mf_sim_simdialog.cpp b/ui/mf_sim_simdialog.cpp index c0ba180..2db9c19 100644 --- a/ui/mf_sim_simdialog.cpp +++ b/ui/mf_sim_simdialog.cpp @@ -1,12 +1,34 @@ #include "mf_sim_simdialog.h" #include "ui_mf_sim_simdialog.h" -MF_Sim_simDialog::MF_Sim_simDialog(int cardType, QWidget *parent) : +MF_Sim_simDialog::MF_Sim_simDialog(int cardType, QString cardTypeText, QWidget *parent) : QDialog(parent), ui(new Ui::MF_Sim_simDialog) { ui->setupUi(this); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + ui->atqaGroupBox->setVisible(false); + ui->atqaLine->setVisible(false); + ui->sakGroupBox->setVisible(false); + ui->sakLine->setVisible(false); + ui->vGroupBox->setVisible(false); + ui->vLine->setVisible(false); + } + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + ui->fGroupBox->setVisible(false); + ui->fLine->setVisible(false); + ui->rGroupBox->setVisible(false); + ui->rLine->setVisible(false); + ui->uBox->setText("-u"); + ui->nBox->setText("-n"); + ui->iBox->setText("-i"); + ui->xBox->setText("--crack"); + ui->eBox->setText("-e"); + } this->cardType = cardType; + this->cardTypeText = cardTypeText; } MF_Sim_simDialog::~MF_Sim_simDialog() @@ -55,12 +77,27 @@ void MF_Sim_simDialog::on_fBox_clicked(bool checked) void MF_Sim_simDialog::on_buttonBox_accepted() { QString paras; - paras += (ui->uBox->isChecked() ? "u " + ui->uEdit->text() + " " : ""); - paras += (ui->nBox->isChecked() ? "n " + ui->nEdit->text() + " " : ""); - paras += (ui->iBox->isChecked() ? "i " : ""); - paras += (ui->xBox->isChecked() ? "x " : ""); - paras += (ui->eBox->isChecked() ? "e " : ""); - paras += (ui->fBox->isChecked() ? "f " + ui->fEdit->text() + " " : ""); - paras += (ui->rBox->isChecked() ? "r " : ""); - emit sendCMD(QString("hf mf sim ") + "*" + QString::number(cardType) + " " + paras.trimmed()); + if(Util::getClientType() == Util::CLIENTTYPE_OFFICIAL) + { + paras += (ui->uBox->isChecked() ? "u " + ui->uEdit->text() + " " : ""); + paras += (ui->nBox->isChecked() ? "n " + ui->nEdit->text() + " " : ""); + paras += (ui->iBox->isChecked() ? "i " : ""); + paras += (ui->xBox->isChecked() ? "x " : ""); + paras += (ui->eBox->isChecked() ? "e " : ""); + paras += (ui->fBox->isChecked() ? "f " + ui->fEdit->text() + " " : ""); + paras += (ui->rBox->isChecked() ? "r " : ""); + emit sendCMD(QString("hf mf sim ") + "*" + QString::number(cardType) + " " + paras.trimmed()); + } + else if(Util::getClientType() == Util::CLIENTTYPE_ICEMAN) + { + paras += (ui->uBox->isChecked() ? "-u " + ui->uEdit->text() + " " : ""); + paras += (ui->atqaBox->isChecked() ? "--atqa " + ui->atqaEdit->text() + " " : ""); + paras += (ui->sakBox->isChecked() ? "--sak " + ui->sakEdit->text() + " " : ""); + paras += (ui->nBox->isChecked() ? "-n " + ui->nEdit->text() + " " : ""); + paras += (ui->iBox->isChecked() ? "-i " : ""); + paras += (ui->xBox->isChecked() ? "--crack " : ""); + paras += (ui->eBox->isChecked() ? "-e " : ""); + paras += (ui->vBox->isChecked() ? "-v " : ""); + emit sendCMD(QString("hf mf sim --") + cardTypeText + " " + paras.trimmed()); + } } diff --git a/ui/mf_sim_simdialog.h b/ui/mf_sim_simdialog.h index 49c9ba2..34a455b 100644 --- a/ui/mf_sim_simdialog.h +++ b/ui/mf_sim_simdialog.h @@ -3,6 +3,7 @@ #include #include +#include "common/util.h" namespace Ui { @@ -14,7 +15,7 @@ class MF_Sim_simDialog : public QDialog Q_OBJECT public: - explicit MF_Sim_simDialog(int cardType, QWidget *parent = nullptr); + explicit MF_Sim_simDialog(int cardType, QString cardTypeText, QWidget *parent = nullptr); ~MF_Sim_simDialog(); private slots: @@ -25,6 +26,7 @@ private slots: private: Ui::MF_Sim_simDialog *ui; int cardType; + QString cardTypeText; signals: void sendCMD(const QString& cmd); private slots: diff --git a/ui/mf_sim_simdialog.ui b/ui/mf_sim_simdialog.ui index 1a9c15a..2fcbf31 100644 --- a/ui/mf_sim_simdialog.ui +++ b/ui/mf_sim_simdialog.ui @@ -7,309 +7,598 @@ 0 0 461 - 456 + 524 Simulate - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - u - - - - - - - - 0 - 0 - - - - - 100 - 16777215 - - - - - - - - - 0 - 0 - - - - UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + u + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + UID 4 or 7 bytes. If not specified, the UID 4B from emulator memory will be used + + + true + + + + + + + + + + Qt::Horizontal + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + --atqa + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + Provide explicit ATQA (2 bytes) + + + true + + + + + + + + + + Qt::Horizontal + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + --sak + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + Provide explicit SAK (1 byte) + + + true + + + + + - + Qt::Horizontal - - - - - n - - - - - - - - 0 - 0 - - - - - 100 - 16777215 - - - - - - - - - 0 - 0 - - - - Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + n + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + Automatically exit simulation after <numreads> blocks have been read by reader. 0 = infinite + + + true + + + + + - + Qt::Horizontal - - - - - i - - - - - - - - 0 - 0 - - - - Interactive, means that console will not be returned until simulation finishes or is aborted - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + i + + + + + + + + 0 + 0 + + + + Interactive, means that console will not be returned until simulation finishes or is aborted + + + true + + + + + - + Qt::Horizontal - - - - - x - - - - - - - - 0 - 0 - - - - Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + x + + + + + + + + 0 + 0 + + + + Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s) + + + true + + + + + - + Qt::Horizontal - - - - - e - - - - - - - - 0 - 0 - - - - set keys found from 'reader attack' to emulator memory (implies x and i) - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + e + + + + + + + + 0 + 0 + + + + set keys found from 'reader attack' to emulator memory (implies x(--crack) and i) + + + true + + + + + - + Qt::Horizontal - - - - - f - - - - - - - - 0 - 0 - - - - - 100 - 16777215 - - - - - - - - - 0 - 0 - - - - get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + f + + + + + + + + 0 + 0 + + + + + 100 + 16777215 + + + + + + + + + 0 + 0 + + + + get UIDs to use for 'reader attack' from file 'f <filename.txt>' (implies x and i) + + + true + + + + + - + Qt::Horizontal - - - - - r - - - - - - - - 0 - 0 - - - - Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works - - - true - - - - + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + r + + + + + + + + 0 + 0 + + + + Generate random nonces instead of sequential nonces. Standard reader attack won't work with this option, only moebius attack works + + + true + + + + + + + + + + Qt::Horizontal + + + + + + + + 5 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + -v + + + + + + + + 0 + 0 + + + + verbose output + + + true + + + + + - + Qt::Horizontal From b8c4e8e339d193bb6f0396bcbf12153bf5c1546b Mon Sep 17 00:00:00 2001 From: wh201906 Date: Tue, 16 Feb 2021 14:25:20 +0800 Subject: [PATCH 20/20] V0.1.4 --- Proxmark3GUI.pro | 2 +- README.md | 37 ++++++++++++++++++++++++++------ README/doc/README_zh_CN.md | 38 +++++++++++++++++++++++++++------ README/doc/supported_Iceman.md | 9 -------- README/img/preview.png | Bin 49099 -> 43967 bytes README/img/preview_zh_CN.png | Bin 53642 -> 49836 bytes 6 files changed, 63 insertions(+), 23 deletions(-) delete mode 100644 README/doc/supported_Iceman.md diff --git a/Proxmark3GUI.pro b/Proxmark3GUI.pro index 08abf01..31f5966 100644 --- a/Proxmark3GUI.pro +++ b/Proxmark3GUI.pro @@ -54,7 +54,7 @@ qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target -VERSION = 0.1.3 +VERSION = 0.1.4 QMAKE_TARGET_PRODUCT = "Proxmark3GUI" QMAKE_TARGET_DESCRIPTION = "Proxmark3GUI" QMAKE_TARGET_COMPANY = "wh201906" diff --git a/README.md b/README.md index 0b104e3..07e6c72 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Proxmark3GUI -A GUI for [Proxmark3](https://github.com/Proxmark/proxmark3) client +![downloads](https://img.shields.io/github/downloads/wh201906/Proxmark3GUI/total) + +A cross-platform GUI for [Proxmark3](https://github.com/Proxmark/proxmark3) client [中文](README/doc/README_zh_CN.md) @@ -32,25 +34,46 @@ A GUI for [Proxmark3](https://github.com/Proxmark/proxmark3) client ## About Iceman fork/repo The [Iceman fork/repo](https://github.com/RfidResearchGroup/proxmark3) has more powerful functions like offline sniff. These guys even developed a new hardware called Proxmark3 RDV4 with smart card support. But the official repo and the Iceman repo is not fully compatible. -This GUI was designed for only official repo at first, but I'm trying to make it compatible with Iceman repo. - -Supported functions when using Iceman client: - -[supported functions](README/doc/supported_Iceman.md) +This GUI is compatible with Iceman/RRG repo(tested on v4.9237) *** ## About Compiled Windows clients A cool guy [Gator96100](https://github.com/Gator96100) creates [ProxSpace](https://github.com/Gator96100/ProxSpace) and makes it possible to compile both the firmware and client on Windows. -Also, he makes the [pre-compiled Windows client](http://www.proxmark.org/forum/viewtopic.php?id=3975) so you can download it and run your PM3 client on Windows instantly. +Also, he makes the [pre-compiled Windows client](https://www.proxmarkbuilds.org/) so you can download it and run your PM3 client on Windows instantly. I included his compiled client in my releases so you can use the GUI on the fly, and you can also use the GUI with your prefered client. Great thanks to him. *** +## Build on Linux + + cd ~ + git clone https://github.com/wh201906/Proxmark3GUI.git + cd Proxmark3GUI + mkdir build + cd build + qmake ../ + make + make clean + cp -r ../lang ./ + ./Proxmark3GUI + +*** + ## Update Log: +### V0.1.4 ++ Optimize performance ++ Optimize UI ++ Search available ports automatically ++ Add High-DPI support ++ Support configuring environment variables by script +(Useful when the client requires specific environment variables) ++ All functions are compatible with Iceman/RRG repo(tested on v4.9237) ++ Fix some bugs + ### V0.1.3 + Fix Trailer Decoder + Add feedback when writing selected blocks diff --git a/README/doc/README_zh_CN.md b/README/doc/README_zh_CN.md index 4f04611..7d4605d 100644 --- a/README/doc/README_zh_CN.md +++ b/README/doc/README_zh_CN.md @@ -1,5 +1,7 @@ # Proxmark3GUI -一个自制的[Proxmark3](https://github.com/Proxmark/proxmark3) GUI +![downloads](https://img.shields.io/github/downloads/wh201906/Proxmark3GUI/total) + +一个自制的[Proxmark3](https://github.com/Proxmark/proxmark3) GUI,可在Windows/Linux系统下运行 [English](../../README.md) @@ -30,23 +32,47 @@ *** ## 关于冰人版 - 这个GUI一开始是针对官方版本做的,现在正在尽力让它兼容冰人版的功能 - (没钱买RDV4也没钱买两台PM3,测一次冰人就要烧一次固件 qwq) + [冰人版](https://github.com/RfidResearchGroup/proxmark3)(Iceman/RRG)的客户端和固件更新更为激进,相比官方版具有更多的功能 + 此GUI所有功能均兼容冰人版(在v4.9237上测试通过) - [已支持功能](../doc/supported_Iceman.md) *** ## 关于预编译Windows客户端 -一个国外大佬 [Gator96100](https://github.com/Gator96100) 做了个 [ProxSpace](https://github.com/Gator96100/ProxSpace) 以便在Windows平台上编译PM3固件和客户端,他还把自己编译好的客户端放到了[论坛](http://www.proxmark.org/forum/viewtopic.php?id=3975)里面 -文件都是放到Google Drive上面的,国内网络无法访问,所以我在release版本里面放了个带预编译客户端版本的GUI。这个GUI也可以搭配你自己的客户端使用 +一位国外大佬 [Gator96100](https://github.com/Gator96100) 做了个 [ProxSpace](https://github.com/Gator96100/ProxSpace) 以便在Windows平台上编译PM3固件和客户端,他还把自己编译好的客户端放到了[网站](https://www.proxmarkbuilds.org/)上 +release页面中有含客户端的GUI。这个GUI也可以搭配你自己的客户端使用 (本来打算在CSDN下载里面放几个最新版客户端的,结果不能把下载币改为0) 感谢大佬 *** +## 在Linux系统下编译 + + cd ~ + git clone https://github.com/wh201906/Proxmark3GUI.git + cd Proxmark3GUI + mkdir build + cd build + qmake ../ + make + make clean + cp -r ../lang ./ + ./Proxmark3GUI + +*** + ## 更新日志: +### V0.1.4 ++ 优化性能 ++ 优化用户界面 ++ 自动搜索可用端口 ++ 支持高分屏 ++ 可通过外部脚本配置环境变量 +(在客户端需要配置环境变量时很有用) ++ 全功能兼容冰人版(在v4.9237上测试通过) ++ 修复部分bug + ### V0.1.3 + 修复访问控制位解码器 + 写多个块时显示写入结果 diff --git a/README/doc/supported_Iceman.md b/README/doc/supported_Iceman.md deleted file mode 100644 index 70dbef0..0000000 --- a/README/doc/supported_Iceman.md +++ /dev/null @@ -1,9 +0,0 @@ -## About Iceman fork/repo - -The [Iceman fork/repo](https://github.com/RfidResearchGroup/proxmark3) has more powerful functions like offline sniff. These guys even developed a new hardware called Proxmark3 RDV4 with smart card support. But the official repo and the Iceman repo is not fully compatible. This GUI was designed for only official repo at first, but I'm trying to make it compatible with Iceman repo. -Supported functions when using Iceman client: -+ Command Line -+ Mifare Card info -+ Mifare Check default keys -+ Mifare Nested Attack -+ Mifare Read/Write \ No newline at end of file diff --git a/README/img/preview.png b/README/img/preview.png index 0c26557aa39667abdab95a25f4a080ed44abe1b1..ad618bed32b898d3037c6f26e4504b50fabbb8a7 100644 GIT binary patch literal 43967 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 z z6o-rmCAwg)i-pev9nwP^m!4SSrQ{P79Kg@v2lnugvfXKJ6}Yc{OjT5+*|Uc03iL|! zZcubukU9&QOhu;v&4}C`7v%S8jqi~!MfNrdIQKGNYm>qq&@>z6@Be`22sB1^PAc?o zmrBLocW-Ren|n21h!t_3SD7v{7Eags7R2ke&K8M%(s$>_OCOFhX{e7{*1vcPrM&3-(aM(R9E4zh9fi z?fdz~E-+v@AJ4EkRBpC~@!>i|F3?0*vp4Cv9gOjl*iKen4qFaYSvld&m%8$}qeo&V zWAB0c)MZAsvN4xz{98uc+kObG$7bcY*pl6d^2IB-oM^jPIJ&B%o248r|n$<*A zVJxroBI`3bL}G1dudpl7YVd8H<83l5xhdz%;8ZwWhH@TNG{l$=+{Y`Od>@6c%eUN8 z?)$s;MnCY{hQamSW)2Q<#mr-M5c|Rg57lX}W>aXM3z{<;`qFG6 zoMNp1*%z{14|xFW7%`ikdz*%IS3@}_nG>fr-}(jyo2^AJgLeOcr>o_YJ>{!c0{ep9 z=M6C(yYn4do<>4X5-2J;+G8&1OFWNg^DWNn5{lgYI2Ucu^hE3dTAPJfxwmb+SGqtlmWCE{yC8*Wu;7tFnLA$!5_wZjIt0$vHmnXRzUKG7OEP@4!3FSmap;9yUVfV`+GUu|i>y1H$Y@ zm-?ZjE8J#K&HbMl>&YA6X>__#@wV^_x<>_4+x<2>k@(LiJd$?XYH)%W(6;*m-yjI@ z!lSuytaXGR4!K}98ciCsBbaC37;CUPuu#}ym_Pv$8|8CydsPK4d41mP(WhsfIc`&H zusD?~w@TgLjcMm1Q%BGHypQK-9WP%K=2~UeZ7O_aIHQ8lK7!qjHZtr;L~>30x!HL)o+G%^+MBARp$mj9a-6zvO zf?*kbcEHBm+MRd8eqop&`t{QRu|K9e_O?5;Yp6LS>W%btm^;E+tP+^UwW8F{OurVM zzHA-aQOK!N_3Yz%>-sg|x1`Y+%s3$9kpkaOD8fGDGCVhQPwcczKGPDI4?_w?p7BS; z(<5>O*~;b%i#1!Wp|`7kZ#DcFt$#?>$eAzb=8~QF@y|TR@2{WUyoUIvw`YwR%L_~R z9i}72|1e-Ww_nDAs^ynO^>RDuMOlW9bBI+7RjCeKpm3yG^bLtUp_YH2D4FP;JT{dS z6le(CMS?OwWpKM2_m1Y)K|*fkRr_~0li$!s@T!!n*q6p6UY0@$2T4@vcd9P(*#DrK z0)UkZ@k2=dI~!}eJ^nW!O+(UH6MFdp&{WH^41|7v0F*R)MtCS0YhN*U#yDA4zB1B>Iw*+!6fO;)wV80h|TdftDK_AyNgz zljFL3{Di=rBm-+}WqP5flJtb`1ggZthbO%EU9K+bB!4FTt^45WyTb?FK9huDTVfbm zcB@3#od#baI0@uBI3y#CJfx;dECw7Pv4^QFS85vxBLdhtII>VEqjmg14LpK@#`F#t zAfgixd;8kFzK__bO3hQhm#MVYdf%qOjEYy~=vcn-XHGYXpQn0d(`Ecz6DTocpMo;R zo9tH^!~FH)1lIH@rNDQ*)kZx`j00O_?cw?;x;UgtZnV4{Ju;CFvQcnKU7>k9ej671 zqR$74-W1iM!qn0nok}LraBAUf1%*sN}M$=+XjkS2U63kw519>U&H(E$XdKFmLhjYeK` zm4+!*i6=nr2IZs}d_(VDlnV9MOPhCt7}!p@EqtP1HWKDL9mqtoM~O%d33G8e}qT zK)OAWG{=8UJSD9|z1PM!ErBX8e`>z>>_@2F=$$k;e@TS%)RuTuHO0U}Yh+88*eG3uZQ z6C1-iRXDq%l9@_R0raH(ce(YH=7#9Mv50%F&cB7JK54lUF=9m^q4HMcU zY;isK1wImzPKlf2Bct{;RfE{)WDKeRfirh@)+DB{X^pz7Y1KzN#54R;SK%O4!;L5S z?hG)@Y>+z*E~Uj5r<$$xaDpjBX0=fag7k^yqP9K9nr))_HuJ^W2RK^`sH4UKB;r)L zpsu4`*0giUnnAXEMP6G^a|_TG6ue_Q7Qy5TG}wYWEsJ;0GYZn9X|OY3tcug4X!NU8 zvmwH~7CiXd)6nv&WFuO*L)i3gNh1#bl2%}1g$~a5cEC2pd?LucG}U4+dGEYz_i>dG zl2CDrJFuldu(MPvh>Rm-yJsZ!oT2zN^h3Rhf>XBRqg0m$zb5Q0m<@VT(h7_^IBC^J zS!Y!IY#V{|F`)$@4BDd(FT~i(#A`Lc8}~R zKFe`G>}=T>a$7SRI;(t{L+n;R%5Y{eLnlh{vW0He4YP`O4o;d^(oTeaV12{inF#0e zT!^snMS2(rh@$5MKSyD=;@aqe5(ihrnd4HO#4}?#ULo><@+*R|F(C*{{&;$ds8~ji zOd{kzQhZO8qe(e>11yZw)-apFX{tk@0W-uulPEki zs!lpJ9cSH+gxka7!E_{?n7uQdB4gk=6{(?Jq#}pJuv=BVjqCu{ zMW7_Q2iqB#M$DxT%`aaK&hanbHUjw?(1wxDO;0SrF@d7bA%N3?+m zG6&LAzu;P0?vNOQ{w|!DD#~LCnT=V7yQSM#OiuVxIt_B6vCkkeDq7;MRn3zMr{&Qb zKzyDbYh2VAE2=JR-K%8fNtlgs@q{zqy|$Nr?gzZ*%k?t_HERlkqw`wkVy#L z&p|jXfkd4=P?7z^)Vo8jQd5L6KS;sz49~tEK!RSQ_2o;V0a>Y5i8}`+>0dA)LRTok zB-|^5TsLUGrtsyHcy)b}JtL>yUE1MsAc;yk*hn7t{RbYVD)e`y;!lL^41=X9-}y=) ziw8$$qO=d?DScCc*h=~X#L!8C@oT&$ng9w=->EI1R;@(jKk!^4^Gp)oT@H3Sd;$2NJc*kkMrIO^!*rsfUWWivM>uNv z0X7f=^BmaND2<7Z?v#no)2&PguJcKL6YgIRF{%>4TLyDp&R2Y+-wNM0G6SJ>!b8t{ zNuLGtK_aO+i_;r)I{5f$5Tiui7VINcH#570HsK?g>j-hUeF#K%N#v92%$T^iUYT<*2&4J*56xZF8(t|Q-ayo;LOsnB zI0NS>ZN0{u+V|kVIO-c17nfL_Bb_95C*=8U7UB;&+9>jW8CiJ45D1=fIaG{eoK*z< z4Wfe0>Aq|jrr&H`up30BTo|N+sT;kcSR1$|cZ~3`WQK{3!4?Hx@H}cge%yrMEDb~nwXByEK?rhw1Y;qvG{$X3sgWf4hAxn8T4isqsu2{ z5bRkVwxQyJAdf7ViyA1raTm-Oe0&nYVShaFsgzD1Aa@crq7Yaj6c(8!qUWKK$O1?Kg`B1y&35&;%{1c^lR=UC?~jRk{S!4 z?K#2(t&sbJ_ud%@B0+=%qw*XH!l=CRMbeDUq0neg})hGMWJcqCXyq??9jY^-t z8)|)i4`3um$N6rJu1$VHmGxK|57I|Lh1CrPM&ikM**x1sN6Z}Y*LV=MXA9%Mr1>o= z$$U|gP}cDvOngJyYG#CyL7Xq;aimij>l}sXjjt&&xW-Z6V-yj)QiG;}ZyQ&MS*uQ9 z=?&2VTyMuPZzl0$sM!FaOh`HZ(H zi{c?S)Kz&z;=_cu0`!vKOHXh3yG>P76gI)ATs3E|f+=~0(;YIij!dIf_d3`TIays9 zTr@k>YxC8>mEmx%f{t6O4h7!o$+T;vbA&CSXlw(EiruU$tUag!wBs_o78v62O<9Fv zYv5WpW;Tc>^mg|`O>O&J?10p@o_w|PRU6-)$l!PmHynV5ln^tKbJH}o>&s~wO5;c6 zX|QXzIBVn#=E{Qdz=z^WJGVns!29KNaLJ&rHozmQ%pl#Wq0*=V)Z&mG9b14Ob+DiW zpTxx`q;b@?EwVP=$;vQT311$#M*Qg<&N>g6B)FGc}=U)K}(TLt{p6C|o9z_ZU6yFhof6=JXnQWyPVpzH^eW_q=m zIA4Vmys`P7YV3=$`nZ<#Wk5rdd(I`R=*mmP1)u5r9Debh_mNXq2w00yDyUliV4d?{ z;(BHV7}7!mAdvK-S*ewt@N9SMrJ zxbd3KGzXdYKELol+svC1Ub(-0ORk4s{V{n1^ZY*qp`WVu^PsO754sr^S{x>Q*MEy36n4o zn?h@N@;f^hAx~&95V!vBoi$YV+I4Mny~a5xpTLE{~Sbymnt}Lj$M;8 ztGtt*&6{$E_F=(uD9&Xr0}zh<4PSdQlPxdCk?*BM=pNR-dx2qX7!gROIpIOpu1sYNHoY=u zv~ru4c=|~(f&2k^|xI@@vjI;vSf6n2}1gl_P9+)GV2&SCEY=n)gY~W!nxu@R=@_GU<6~Dmp?Mql* zjv}4O6<_7XR|p%xg%ZCT1U5Z&IF8#4ZhMKN`flp(hKDtIyp8LiVEcAS528uKiFbck zAhCndj%FY!K%pZm$~S7UmVeHnd&RFE(r@Q~3KpOU7oBFZ1{11wcnYGjxA|8scVqZm zH3xSDKqEYZ>v<*wJBLj_`nO1DWYXdHj;v0Q^QzMBaN?N_o`RmaD-Q6q@q#T-@+Dr+ zPQ?{A(!z_gh+lo9k*})zJS3fo2GkpJTD!$70mbNWc^zFPtyxi%GaA;Qqd-A#_1fH} zIebVVvg1j-ves@V4xwdzhQb~h`SzV3EO&;jReVO9)Y*#D2rLK0#mnb(X|YMci=0Eo;tejW&%!eHni`S9mOx_>0Ajz z8N;raA^3==(+Q0hh*x_UWyG5`H!wz)>iHm2=65ya>tkZQ=;0Cz%%|svT&|66jqiCN z$&xt4SKbECijzQTT;MdIyXgU#Bjj?_rvMnf5z`;Esz?@ai(1tT8a>yf% z8hmy$LInHh3C0FveZsHRse9!l*5TzAEu#~d68kd1pOsVoo~|3}9Nf$=JaNFbJu=+p zcXxz|uY{~V35Ku8l->p2Dj5`?NvHDXKl|oPdGkzNa(sk8CZNC6a_&!(`P`+|HfXot zm}K8cOTs?k#S|)XUXiT#$WL5rj4(AD_QYL)-X{tO2ChuTQ?B1Ha}o%r2lg>#Jn$l7 zw3w~MF{Iou8|f&7oFus+cc_ApNVSM{q=u7Iysdz%YbO!5E_?t=cK{g_Hd)q$MeGS!RTdh zBng6#{Z{!Z>Kl*lU{vn@A0McLfXrym^4!7q*xfrO876%QhbxI_;v0yk$K`YkoZQ;T zXA;+{CcBR|;&aL%_iGF{3s%Vr7TG`HP)asn&bE|Z$p8`Tmx86DOcaJU%*YPy-xPRY zASJ(mK)N3c35KN;;=E)T+l=eKi-pBL2YRIl!E{{Jhf+PDz%Sti_?}8O8g-pnDu&O# zyA(j&TwnV1Oj@o+zYM%wFUyZ`>*2{{bXgAF2)k1UNs_RIdJk-qMZg#ZJZ}yttezA| z_p{D1N$7C)-6qEAI|aZ%bN@o={FM=qt0hVT3Jt)Na~I-H5gv4mVv6=+IcTiIDu4pd z$Y8p0M?}&1W>hi<5JQ2I_UjYyQQgs>yucXN%nhG#!xpx}QJyf=*Pg{f1I;KOHm68; z_@e|#V6;TI(P#7z9&#oG9EFOxQ4erdA%`Q^GSs| zGomSD9M#|=+4skJOD>V7BzM4Cs(S*}MXETiSp^d=7J7B^$_dbwAqc7bZ2fd3a3N$( z?_Ec_(zr4wU8P5Qd+m0~OkKg2)rQG}s52Y6)9{AFO0+nb$64YQFu~$6!P4s|zQ(i; zL>G>~mE5SA%7So0SwYtzJ6q7Q@RE%sv3xCuw3;oeIESpB?T}M^J$q~pI=OXbG93(# zhiZGK9dKO zE}9fBc=fNbPOe*JBSTE+YMw-;Sa`f1vm>%?`{?RG!tFY2*-&E1>eR=$QL~YrW<$JW zKA=US1FMfZetIqm!fT(w`FiUV&1ktIG=+$6bd4=q99yguTfeEEy%#Rc8y6C*|k zok%0@Hwa-BW6eP~M7^G|EZxHjXWo%?Rt~XjDb5U74%q`01JE@{s`o|C<{%Y8@|a0w z_9b>dO$O2?e}&CulzmTkLsY~Eh$+&QmL>{232>&gz($7g!nYe61(8wt;5FI(nNyG%y5rV1$SR7|ZRP)o|DIB)uLPZ5MBoBNa3>`OD*$?1CC|7r&A3tM-4l5UQsd(IsNw|^ zBxhFd*}V558zR50zVW{h4WCKrvV7yoNW4nH5|@kzn5*UnIfortu)fU4qXNCe^-9p)A-+r~LF~#E1YDaT$w!`g}fRMC?SuZ-{PnLjDDfv>^AC2Lx(wsg`&aJl^N}B*96z|g zKUH`C6_fY(ADl&?_IrPG3%*iQLS#ZoFbg1|gyr}AWjW3NNU(?!-~Xz@{TcBq_RiP; z()xmZx3B9gSaoB*D?L%&pPIV$n_|ZMP5Hp+Wc3~W73UN}+TtZ9TY6;TN(pr}kzKIM@)E65k=g@}3lmHQEVecsQavO^SL<~VR?llI2++T#z+w-Su95^6a)TSWP zcP3U1AGDQTX+SmK1E03U3Vdh-{VOVN5(Etd5d%&^~qI+UcdvX%2ymE%GT z)*`IITS;$a?bD$Xnf3LuB~?{9dVBbRvhKe#N; zwFZdrbJqkKBQx?V@viPOkKgj^KBXcf3Ah0fk))U!xHk2IP9rIVwC)!zW%lG%xed28 z-MLG-Oa?zc%{P_K47X5KD<}r!mRGQGkjPR=otmZiY}tNrmH=hPd6?G7F;N~hyK|>L z)qr}S{LW>TT?6jGAU4VqVTldLN%VXBlG(vC=^|L*Brq5lpm(0emybu$1o9=e;kN1J zTjCb!mlbP7EWfXe0DROLSYFHeSdvHE=eoB;d**Fxl>q@xyfu_-N8WZPJ)3%aTT7QS zklajlFupCy8jq~;ZBFbRnrOfQ>6kERGBa1TIw06KVB{#Zx+d~V zMaSK*{{c2SWeMO5{-zY19IAVf{F40%5NW(zX88$QP*(o9{-I!i|AFJT(DU~-@DI3* z2j1=#Q9{dghag&U_sH>c`&hNr$TJTa=D8KEwu4sK_~>vU0}uYfh$!%6tK03XXzN;3 zft<2DcWKC(g@tAPB&mVByV&f-7MCV6L?z)RFWF>X(4zDYZ9NhVy1!#cyAfAz;CPMRZ&QVg+@%g5-%7yT@TJ(bkWAbv@#wdJ&5D z(W@f!D>C5*H`S$T5Pm2M3gvE(GiBp_ww|r_H}4DsOa!PYpTxjdd_P#OOTo$)&SqZn zrerptxjyKaq zu_)Y>rQNXOLXCt5$!Z4Q=UK=}MEkvq3UNL`t>sjygO--im@2&PU45aGZs=;O;}^Re zMsLcawe1j~QTBY~(fmmK)6z%A(g=f_1sq;0`kpy7;0LpGiJv}F3J`tEF8~Tov~3Pb z2p0t9_Mg+TfC3+w*z3JRSCCbe+ZCo)%DFqAP?+5ws>7DwxHAlQLtky9Hv#B-==YVY zN{vyZ4|GR8OuIuHDimWx#KA?$D!wpPWG(V()@fMwRdxy}onk!%t7+h7SCq!19+KIv zM(|g+7nVL>FmQ@nN;3czXZVYut{2<?=hMnilUd+F+buLl{r5LVKTsuu zTa8%m$xrHFO#;f-3c1%=SM5SpCdDlp=dp?Gyjf@a|G zwy{BEU>BnB@lYiBXJ6{eBDv_$)P>aK9&UgMG5pb;&T9%^NyF~+zedI9drWf%uWJe< zBMeWvXeM0sjPF0d)w)xl=v#eZs{GVFvQ42UjKQHn7RIAoM(=8*a)MJ)lVSXxLuu?i zur`~T&`6EZ!cYU=cB7T8hGbxpb+>)q3r^3ykK7D#BiDo+^<4edm00p1Kv9=5(jyp3 zaKLcYc?dU)-pxUoW|~VIvPukv zieZNCdyjkxJ6Ar|Dkybz=lA3QbeMzP>yv4-NasaTZY7dTo}aP*?;9HEh+qn($VeXhD3@9v=petu`$H@BX?Eg=RO&B{y~Hi}KlfPt2{L8>vUBct-+x~`7>nQlA&f=n zVk9C{BqXyh57QArT=0sie+V`I;UfJNJ2+MSeF6{!l6r&rUTz;%);SlA0ANM@-l-pH z9QcRsRBTY<&QM~X49UtTk0Vp8)`|Gu4DqzBlQZ#)ZYFa!35{ulsSN~3=1zkwb%?|U z>qUq>7`Om>J`NAy@pzy{|KBpZ!*sVjE*QPB1=M3>$kRGj*X%}n3G z);8Lm{c91nbg)_zYU2tguib##z}l^bYN{8ZOnxs?(Tgs2pIavLP4L@tacGK(=spFp zLXJ&c=QfoIhxZFpZJRfs)3hZ;MJeLAF+t4Qo&3a+Go_U2ocW*7rn%5?s3uCsPsjjD zE|zz3K#Q z?+7Dz`e3sDGO2mBx7r3HPSxw=DJm+yn=u6X*{`^H>Wc=qn- z%DJK081)$o6HKN_({^cwy?9ptn8TRMZr(gi{IS}(x1ti?5NUfjGyYrJe#zAtv?b^`QOSh96;<8vw-v6KXE=!mmi zyxL}#U?t5(8~1FNx%-S7Q4?7g`j+Q$*E+P<3(gi-yL^d16fE|3cgT)z3U|n;YuE#6 z_!XUv84wW|(!IYHWIFoe0Vt zV@!EA@Nu?q^ZNR7P^8S3^bD3)t#0i@HYP^ds=|@KW#=Ju){CLEB>hY-mr8LmeO0+O zC11a)w2j-MS;xx4bQ=x7-UtN0-~b7!w)j}oWnJtoY@=@CQ8Y~s!>$g^ZOc;bK!Qu( zRW3Se;dK)psKiBC?9^B5%M~lh4v1s6hpPSko!EuP1?EH@Pu}xBwvqn5!E+;F_ULjr zY}oee(^>M+5S&8du0~&qiIK5#HZlvoT#Y%=ajU{{j#i*XniZF&^Gi!aF1Zgx19KvU{2(n2M~(28 z<_Be8CnZA#vo?2=iw|JcPYlr^?)&w9s{{>2j#sc|@pFqoD?+TL8I!kaZ!YQjCyr|O zOI{cIzyK38=T5}6wHxXek8dqb98}Fw$+oXqEr#vLWb}$S+(n4AXZr79C|yY87#A&t zlBi@C-nKWi^F#Szoj2DysIuzX8#_tAd2c; zQNlVijfPrRjkDqjljPv~$`L)jIMjvc8E0|s;7Gdwo7F{)odgN3IKwt}@~E|=;cfSi zQI||vN!bKf&N7a8{7AG{{uRdC!f@&V%hB@PEKEr3n@RjNjB}Cpn^DU7oakjj~Dm6!!Yw`_2V^_=|*EN{RM$y zOO^lA)|o~%b*6FLwj$#wLa~aFVxTIx6%a)%TUb9=`5-!Jbu_dM@8|K;~EzKVAL z(DZQ7sk1^SuY3q`l<@M-Tc#UJHB<(Jvh3w=H*_{cWgy&-nHSGVVXKSUB%!*?^_evF z1H1Bp5PmeQyj~#QyuWpTLl($GJk{(+i}Jg-x;u8p|GLAo^3G8It9_*>ZJqn*Xw$d@ zUDv$Wh5&2c^rLb#Cc-nqKu-RS0vxtyBM3%De%t0HgppjYr0z=9k9~YmWD2}IFtcY% z2SWfsnd*feJ*(YKJ8Q0buqC&JB@<+peP2Ku)j6TC$n8_OBwHxt$7|TYuK~-c(4d>_ z12;K{-%R`(nG=>1quq|dpG#<;K{uU6Nk3j^fWG|7i1;CrdIXz?O@S*mOW!LjQhb7( zrf}KB<{gdA%0C!B#Z5M30s^=pTnyzH+F08`&IW{m;#d4Y;Y()qc%hK??Fhbe%=Hql! z7QX;};lABzYpAyV86l)P-21b0`qL6&pWI1rBdoGf%tbwrZMLINr4onqrHzW>sYuK) zj{D;;qZ*~yj`#Gt{m<6C;i{HembiVhF(!S~JJhwwbew&C9tqiY2y(Cx^`xcVNmU8} zUxC3p2qu`1R=9J;(6u_dNLa?weS8HD_Pbz!lVs6#O=W}BOmO@ey3w;35t)>{{HV}; zSkLBE@`;xXaCcYx*-b909rxw7{*`a~{&{*fGy9*oOI```sun?Nk zH5qS(iKrQ77?mlTIA1L`S2%&SzlG4c@?Pp-DK@iT@FKt{=+U+febeyX+S-1S8!d$ceA1zH z9nT5kO64aKpg}ZI*t^Hn*0tL?u1vpI&>wRxt_N!y&YyWei9QKvXTg_uU49KL*-P?c zj4|sdw_#fJNaz)y^1SV(I$)}M&0(0a+mRNYc3V0(O_WD;hO$m3IHZA~l~GhwWB8t` zDnrbjsLq>C1M`EbCZ5zfrbiffV6A?R_H`2RevS~BX;s>E z3vj9AK`)Dke=Z$$bNu&y-oAJ1!}J2LzV-6H-i7FdWck`p)O>{1?bN980!CZi#+ayY zUS6S~JZl7}Cu9HWi;yO%b+<)|UL%+ZYIm6+>? zHPD48e()zSiPQ~AQkJW7r5YMgff9|mP`Iouo<8WVis2Tg{D>M$hn;`%Q z$Mz?Q`lahDCP*}b<2(#=n%k`igAM(NglxYO){@X&Upi#kJL%laqaN%$cC(bEc-|UI zbM)`MvlY0Zx=oa(I!)~vpHF>Cjm`WXZiVS*A2yesU^Nw|@#XRV`yu&|LC%^^OPgsM zBq5NZ(s!U8z=)MRN8Osq%FHU;M=GN(U;HxJldpl*;QfDtvS&ef=Zf!DB5cuNSua-W zhaOU*@v18%i_+_$x|?>+s6W)eV8(Ur&k}=A%iH0jG)q6?y?RyeglJcQ&wNUN@_MJ{ zz2$ior1}q(Ut@wz?rxGynifXHPzo8H+wM5f#$HLRM=D4LzUqsIaH)-%s!NtGb%~m? zSTi>C^^s(!hI7vXJ~r`EBgD~$X1(EPm_sd&&+x5?#d}5I8Oo`2R%oZ3k9#o|_S6mL z|EQVN0oEx?V_~)e;|nob9%W?jrkYG|4*xb_~ z(MM~8vlqeb&<30o&v1#Ubm#Lc=`-fyqN}0^0->t_h}apv8JzrAOo6Mz%bp}382Hj( zi%EnD$#R!v^d(n$-3I8xb`?Mfb>Y28$Hmc)%s-k9_1%uHC#AeF_C3a$2)+_{H(7C=EA zXHuj?=1y2@8nEa=e@r7PnA-F>2VP_nn zG-3$8fv))6o(}fwdvObr9$=Vsil=s^JWMO^^&P&owV2cxnLgOY9N{=R#oZ7XpC?CZ z-CkMyDWvb@9mfI1R+Y~!SC zLk)%1)}xrRV_hiCQ1BhC&PZHrnXEqZAb)-T^;(9iX00!piejJeAJdp3aqHKPjJt<$ z68axeTfZc1&hrW1?z7aRdqu^!2K~$zCSBl7#{8D}a7D{$e%FD|6WNwE|4%dm0hAz6 z8nY%)izKyBf6^;_ZiF+41>xF{D|;^DsqGb(xTS0g=S!167~h7_D{L$;6gAi1hBNMQx{euNxgoT2{vZEPagx9fED|HE~k|q z0>83yzU7{Q4a)eEkGX%280d)LOul21O+I43n8B~DI>59*NtKnuf z3<1gt$zz*3jylCp_QIq={n4YzlF9Xa8dl*5-=|S9uwQ#T3 z{;<_Ix`Q&<+CHQjuOV?^aVNRD_*FM#B;vgKb0N+{{Vt}tnsFMRa`;e$N_H4~AGfl#cPjFuVQaOAJ8x3%}wys93Ei#tCw zDT4+;88-{*M;4biCL%=(Ac7J*B*@dlr^xI4tAOtRy6$0M379X5vP6%7(Sk6|a$zk< d8G!n2;H!sr96m^a(Y{>6CyLb>MNOpBG7g^4>>?hBf@ z5Ta0N8lqAvDk@Sbk|L5KDkAbe+APlZ|NZ@c?|IL8Ifnxe=UG0_XSwg|zOL)x-US=; ztv~PoSwcc$EBO2wI|+%69l%@Rryqe|@V*u~z{iGAJM&W#m0brWfQ|3{PFkOokf=`C zyzKP@uq}D@JS0>?Vq5d-dqW$%1STOtyaYaT(&4)2lql|zqQB=r)XH})Pj_uhx?Ub! zTN77utj4wQn5?_Xo!vM$S9P_rj$^0wg6tHUVP$4}6)2Te>h^z_-pNG04Q$BXrxxk+ zxCfn0K65Pl$6PDL=SN5t$4dA4LNhvbTgHC5t4ek7fi;KsPwN(L@Zf7*`Q`NvmTe0U zW;)Ztdub;3Y3HX;Gosq4V&SOmB8BlrBuFEk;Z#eT_U{FnE+O$gKUGUSozQ!FZR>=b z(CBehw`gtS)u}gIR=?cXB&oCZgPbDJF~IBkBdv|ApC3>Dxr2Ex#lmD>nD^LBXih9n z{4Dy=AD_BBZIBST>vrRT)m^nrfL+cib(o3iDj}cVM1DC$bVc_GgbB;jTri$KktSYz z%Se|uT8;qDkr%#s^%#ka?>9zap`j@rlxF%R@GKh&`WfP`NODaA zUuFz1hUWDvHQ%$zFcTOg*pBd|S8PLgJ?f=#j`*Rty2%v>rxi-*?0`UQMVKp`K0}yh z6KvvVK1Lde7&Clsfno<87E0e=C+77aulp)>Fb>AZuS7tm=fuBPh+4#lK)LbF9-AU$ z`7}6k6wP>?%;4+}Gnq$sFCBU;BmS~EBx8&I%1G+$Fi|X(Jx&b%cum`cos*KBwpCQa$@8Hf4O&xbbH&`;Y@hi0S;J0BCEDVN2a|1Z=S4zdzMl)dYVA^?gRf+-0WT>V}Bw@h%j=bW~h)`|;ZoU&5AoLMEZoz(;PKZf-xL!3~{eGBh~4OY&Y>^=h4&l8fNNH=2myxR2a z_DQ!akh6#5^CiwPv6;Awrru$ui9x51HQO%Ru_J*E;rMSn$8Fix%Hi*yM9=;75rS!>yYYcJtj<6DpD-6~)xP~4R zF6GAfQTCb5Yh<$L!}*F6O*5-w`y?Ir9!d2w0@mg^m5VkFRRUKkL^2eiIuz+c5eYWu zYPccZRkCwSQUm7+AFqg6=yX^dtDih89S;p2(8MlvGE&i+u}$v%&c8>l&{&0h1H$P+ zvqcTs5-Z}AkXiGIvAmJy+)R9)I>-=VYR5c6N_kUr={}=*aVHBx>x?ps6%8Z!9jVt0 z!N`OQFpf%&beL#b8s@3OFwj)#*=&**>&oSk8dujw#@=FS-Z7)yZG^2u1e2KEAxgj! z#AP2QT8iSF4ZMOQz*IDkKAz0))-<3!nD;T1`w}X*WWg<;6-*lk zi3mAgF8U*h91pNbYM8JT7B6;VsF#;Lv>3EbxPWnoF1kXlvR^tEuU_`jxtA0*JVjSp zUBjzVX<{f{e1X7n(|GqeFE3q8Hc)#P8Bd2Ih#}LOk?nZL1}v<6SYRujvt_D+ol9&G zg3j48QR7Txd{PBD9p!SNj409b#**=CCLHufD3T6>Mg&WWf20XdvLU~%4rIp8XVS6B z$NV2!WH?R>(=`a@eJiaCq(P`SUhr6a1ronVT-qSuB3F>cQ8Q-gW(zEW4S_R6a3oA* z6TyUW4`>fKgE(2q|$7#A~MM9V!viA+7|Q5O8hDVj7X*)s4ksB zVq`QK8>JD0_cJEqR#*9@<9>#BiK_X2;z@%!CGs6`mSe60RW9aJD2E|Ri9YnSe}P!FR8}<^1{JhB&v9rT#1Bi2%VyKdECuL-dc*gteQ;woW{RyKS6ePhhE`%>Yne<-^?b(aNK1S@=(U>mC0^aU zwQu!PV*4)NwNK;!X@`_SZ_U`sI4-S?e{gNj>$6Pk%Rdw2_Wvz&wmCE^35ye!io@lB zgYSPGHCq{N;C56kw-<-W|2PxJSZHdf>Mu5Z8{IkE#9%`se`agSu;Is_@ry?gCVG-Q zTT!p=#3A6=Eu$iCLV}jp$~4!r_w>6_ojgMe>kMgWNYx>8bMs6|dA8;QzClhczl~zC zcB?y1{MpFA)4NWlR{s?}y1{-W+J(tMv@H4tu<`1!%* zTuiR-rXieahF12O$bnl6AM(u`;0>8AJz>qtK~V@7P`yl~6Qb>TznO%@yQ2La@Wz=| zf4`SZ-+{I#iTfGzVW)aBe6BRnJk%WfD|(Fc$j6e0-gzdRtxWQ7i@qmim0|t7e5Fi7 z-(OA{C#RPhwpC#~wAsq6CD#`*FTMUUPyYc?+Z-c2br!tYl5R(n+1 zt4vBm!XBNi*++QE_OZ+>$MqlU=0z5JZ^w`B#&*cf^M`!&vBPo^ZTkoz_UFFaDse)u z39&p(5#_{wmdbmt*}8RZ*akIed>cGh$1#Mkrfm{>22A zt?J)X-6nX+EUGk#laRO|H*Vn=c=`EcW~CaKhi8a_1*E)XuwYEZjFzff9`9JxT52>3 zTe4*WQopY^- zp)P;M_f23hslK$O|H%;eMJY@8pejC%2F?ZlylKx;MOLM(_b*Z76g9UgKdU%~agn+k zD(X84i5?aGQq=bb{wNrxVzDpH;`>sWFSQm5X1 z#@f7^9OOG-P^s2EPVT^ZsMJ;v!o4S3kg9}q$ul;=#GL;F7)1zD(@tw8>J*RFf(D#I4lP z%keY0gV@9fvuZT%$#aCT{RaxrYM1BlT~ucmeBSm%Ysfi6<540asEq@e#}5@*R(-yCa}!4j3P!|!i!E$ zsyZjcS>c4vlGa2kt8U{7nBf_;PS4g~b!F{+;aGqDGUiWcv;3oMThgFPS^dMCk~O&K zDg6B@Pp=5}Vk<5Dl&Ja?2~lONinAM?nkiz4$}<`wAcJ$(IM9eyvqmI2R^&S{{!j)M zML*_NLa1Ua^lOF3<#wuv3h6-dzf^lF*#SGuF-V}t>jd6vC!bMjPqjc=@4BS2u?ULR zp~8)t=Z2WmP`AObSf6?5+i6XiNZORBL(Z?=ktlN34!0tDEuCgl4C1og5{9h;8ODR7 zfkl;Wa;{ELoS;Hbo?%qP&=SpuP0c)edB-hbM_rreqPuXUCWZYvWN9LA#a~dGw{*nB zVN06J{+dTOItORJleqC>fhZQut_H&BFKdBt3{Q-?xcyKHC3gHgfh#09Z|5)d7Mm(1 zB~|o4%Fq8*Fv8_mfo}8DAFjo>g)uUGYlLOQo{m9kJH*X%H7Nhm*P;Bab}2aYXMp#< zf?EA{;@T;SmiJZTX5(K_uVuFZp%p^WvOT*N8m&~*UL5prdXpGdLHzV*&GV?)il`*B zdd?fVivL47^mjmgjfI)TE?!vZMDkb^w(06ouPQ7=kRxeYnrdoap3$zx##()g!Uw$~ zN^k9J{v^LvE;($FthuS$(R@+3g}og(>cpOsTbb!=fmq_BcKt1!Q~3lT{)+qH_0@Aq zw4xu~qA4~iPlPA-lo)X$@I7yP+ZJO-4dT6vSM%Eqh{O3e6$lS`ys<;uUi)G-t7DKe zc95~!`*UAD{7T=ndXAbzzl=>V1eos}ep9(&GSqj!Z`SJXH%Ky;Zw@q=3_TIF;(g{{ zfNW;ctbawUWyQmZr=)y&2QV=K;a{8r>?>CW!#i{x{1BIPM&$e!CsUGe$%EEm);O;v zIuR1vQw{pnA^0UT!UI42Q`%~~UR4}st}a-<5;5X=;AX149%GMJ+HYBI6^US@(>>bS z-sHHRYIYHIwS_kzj_tP~N`&$I*VKSD?05gOa*pqSb5X{vF!xyAS{gs^d>&{jCZL^8 zyyFp^Vh^tJ7@x;aEfHo$eRo}k+U2AjPEjbe&R)&65;vfSBx~Y)`d8TV*L5@ zrjXGj-(7B?*LtF(+KwWEkp{kUa;l(D&cF)8&X&gmlbNuzdaPE!m6WEhxcMeO`Tm=J za;C9|H*ek?{hR&)d3myc$KvmpwflSIj0Ll9%-B0?aN-8|*Z;&9D6jewzi;59<`75L zYI2|NH^}j-gA_fXqbUMHB~TUK_}}n|_fss4`1P^+>S$`2m@X2+>50dl1xa3da;j}5 z0}I=pqvg!M#S>5xJPD#9qQ`be%e3^Z5*JLSqu8sNSQ9A7mE}bPZQu;cGOHlVI!4@t z2jh977H2Pk>Cv#gCoscUnX~xjPxPR5@P7kFYsg89udlBk2K4vawRLCTsYq7rl(_Ny zv6k(d6<5~6L2GP%6eE={{Adx~T9Wa>JY2fGG$$P4O*A6{CBVE%WNQ?tI1QhY=rIM%7Hy z4@I}YNKU~%>IH$m_sMv^dBTbK6})(ppk<_H_LhcfUyvXzPgFj;BAwt8KMLifs2xch zWI4l%N1pV(Zs?f6oO)b5rvLk`C}{BX-QH{pDewRr$&5Zd_BGgY`^Ljfz?ZC6B~$iP2fy|Q^SbV{#sT5>ufoZ_$*sJm9<}4PnLkKHPxNB~vy(l{ri@hwVs;om zcEcxjsUIhutf^5MHWoRzrlO_s3ybMNsvydb?jsr7mo5E%q~5iO7#GkT{U0CRn3|UJ zU|aMb_=iSCsMQkxg_#LQ#7VfUcARpJAN$9}pXbp7!)47-w0cb#%;SS}{RGGvik)F*>&E&=I?f zJsWO7j%^NV%~@F?B8B45iA)2Om@L9NlG(G-2&?jICRmu5p76XafFayICSr&JFk;g5 zVC-nB*e*VwHHjg7UQXZJg-TgTB4d{6z&#vJYf%>bF_&Z%FIM(iqiRGFhZ5tPznJjTISEsFY`^sjZ zSLrkgD6+~Zk`Wu{wo)1`H%Mq0#>s*Nyc)AhN+SulP@n7}%;qBK@rIhuaGoD`lwRJI zh{qyCu~2;~y>FW8v!y^0h+-v)B-B+8_?%BvWleK|7&$Q;YF#QH{4Uwf4}0NVahl9jEVv223Jbu{#3tYGGw#6mdCYxkp~qKg;W)GP^hfv(0^> z?;C^$;*D6JlpE3V775vA!SdV-&Y3=^_4|X{ZN{lSK8waad#w(7^_jKOd(iK4eJuz3 zLY1y0`s;D$8vW~gXbVS5{5H5*gXhaoMp6l*&qP{r3z8yKx_f>nBCZ2(kw<}9drNpu!e^l(6ux-4qa-=4hT8IMF4pWsQLnHzu!9-10hbbw{WdSd`gpsOAbVRygR!jN^9xQB5f_3~?^YU~xN{$rYtt+wnI>D7RTkv>| z;MXbQfjTGma<~_X%w-?J`-RD!8AiDbeJP^VB{Ouh7hy;J5;=f??S5U`8;R2WZHc6U)q0S+SCLuu`MPV0Z$0v{44UQj>*;E zB&Zig+%OCM1y1F;5teQ$(Pw^m5=6T^UR)BT>=d2*ON&L7ly^?`Za>!mVT1b+6eQzZ zu1?Z=)_plQP@VrEw2g(Vc5n3`_WFbAUiqxgyCdYRMoiJiuz8=(qudE(J?!aOat3Qe zq&4wlaz3mj1WS9x-cDsrS)P_nO8nH37#Uw;k23Nvx)$Y&vyptD?iK&_VB_XmijQ$NM#x6fcQwh zniHmn0XgJ<$K0#!^K;$k`2ecyoa}G^+)NiNS;@;u(kK7Jqs`J9@6uGA!~D;OVB)T+ z_zN4O`vx-I(w$SC-h65;dcuh{qLcUPn4Rs^slL0%j_Ptx)Tzk40Dc#NSXm55xTt1l z;Omcq6>&q|KU6DR=0gamoT794R3Yg{-E8`py!7yko;TI{pI*MaN6+l0m3iFw3yn@z z$hocu;RY5BSI zN=3^`XF)YKTAf9G^?J)LHjI6iR%)OWxw2;;@ckU(qu!XHr!P3($1U+}r|rr_SZbnT zkFlkk0x&wMe3py?NYxM9$*m&AVkirW^*cW!%Qe!+!>jbMR}Jqaq$>X8_p|ZdjjyzR zw#=Y%-c8oS($4J1Nm&$zJNG_&|Har|kEmK6)A_oxVfRzaU;SvalYEitvQ$QA-bV7& zCS9-C0eQ*bUU%Mwf}|85GmsGjx0tPY=zdC_X7MMjdhGkGoZs-2dD(Z-m95^@K-iMi=GpfeOC`A+N=u?5@ZbQr zAG63Eiz3qkrrue6@v&Gc^Q5TXz4+OiZ`?NQnno8iYWCkvsw37bfRdXwpg47J-_Dw4 zo7`vazmy%#%%Y6I)g6yf98H1@2x zE^VRAQu`=A@b6|jCKuwIUDERcEJRnGgK;W1yU0NfcC`jy$r{hS_l-xrqPrO)nksu; zNSIYbUFApLs<9BF4p#wZz567Q4op-gD(}XZi7#&UJgA3hs$99SLh!7Ps}3(eW8mLf zG1e3OSH`G>X4R@+FqsC?r@K;hlMEbaPixhp>1eF*)Ivq$oI_2k^MQgRu!-NL#twY zXbdX^&0UC6S9V%xze*>aKvt>j3)Q$;DIX_X3GeoCS(^oq)~I>&6ghtQ0+(Tnh$U{1 zds!-MKt)d;p-Zcj$CpFkDv9qRYQkOL+jPC=w4&tXp2aLoPY^1PYG&_R7YR5`=eS{m z=!hB(j-qy7v!n?Jg*n;M62gyD?v|*Y-@j2~RHAyR9$Jy%awdM_(G5n^!a4&Z10Bw zw>m zDOnFk%znI{8kN%FGKss)m!ZsiY~W$8MOB63!z!u2RJ+e7v5YjYP2%Z$zZ{)%*&=be zq_je?Mvv)imp6TK`}Pkez3kg5fuv0gY-Qq^z0%U*&_;mqqC0B;fcG9M3m4B}d zH&Bn|p{Q$`KoRr^oU|r@T18*AAhOMYWG^Y-;BV(!C#Z~XnT*WfMXYA%8x!Vl z4!mA)BR|`=x_M!%Vuw@+rcSZW)%psMlwp6&_c!pK;|tMJCU17xtNd8q&2BmPSMI*) zG7)}xhicQdr)keS$EIqE*tE#fbs6`Omh{KhH#cD#8`ZFIMxT=ZypK9yX2htwa^Q`Q|B04FG;o;jtHh$e-J0F_PR`mjx{Z#?QF@YLYGLJwP4^?Ik85u1n+*f!AVoxp7Y z5wu0Y>8EO66J*SYk&)5M*OxFf8t+#c^YNbC@%sbZ5dXAC(d*XP5} z;#VDCMGm7{6S!a7Cgp_yrM|u!&<;V$5;{q@4F71Ly3#|?yI|grV7M^X$g_GEABgI* zddDMu!mEz(VI6cQyI)qv2*^(-E9_fBcCdF>1G?mvUE@DFSGrFqwjMp{bT32+;o}xy ziM*l*d2Hgp3Q#=Lhc5r?l80~0LcsGwHR)37U~&EeaEH0NJui)G-2E`;Gkz_(<)YN8 z>|bexF-|DuX)ejA;xT2Ogziv=y@y8Wk5*Oyuv_i8yL(QlUEWam84!9;EHSLltQ$Sx zW)Qi$&g1Q~!+*Hjtn_AA%}-^hU?Z3>2uE|xJO14oi5uWibz0M~!3&=z{@@-caSNQJ z4_8KGZJ{oMSL)fMSX#}V)!SaX;O!j&_1-uZ`d7+Ol{bi5|4E(3Od|wcw~UaVEa+bs zhP)UE>o-$Z4bFX#3@z`uaDLlTurs{gddSP+6etU1OW<8xcOwffz~VuD z#SSKONMGI0%#d?#*jIrRV&(nk6mUJ9a!)((jk~=Vzc4jQnknvYPTcO}<#lp9Kl;ix(8WEOTq^N+Gu9X$kLRbS-EgFJ3BiyC4Oo& zEr@7^KUSyiuj#mH2xD}IhV*YsA072JOjv1@n_Q*ZXSRgyOFbRXQ*6CKr7c}O(0?HGbtHQM0x@7S1@DQ&oYd( z@1}YFm+*1pjQ^ljmuWx)DlXlKS(@s*Kiv<>ej*_mxB z5~TF+>^a`gJuM$RNtnb%a#7Rl=*iOJuslXKoIoQ)^REaq{ZRfyUKc2nFRabtG@Qpj zo;s30uHm+;kzQemN7cFpqJ5ikry4oaYHxe*om9Jgdo65a@m&B;_sf^xKHAg;l?}&d zMqC{7wF)X)nYX)K^;i_Q3XXmYv~1Ju`_MRa%x-YzEVD6KE-><_^Z3ua9ldGHbNPp) zHQXO{c3K-oPUpz!DA@#)roB9zsvzR~p%^6t{WY{)vGRjKkS@&>73oedcf#g3ho?znC+U^%MT#e7q#1|aC=b0J^ggribTj>I}8%et(1 zn=dE&;#8im4(?jWUTw#B79V^EAE&{HCNYMOxlvappA_3<`MaYal{%{@bvRYA%R5z9 zI!&$LDdNojnADgTajS@M?i(VE^#O{k3vBAGn1RhVFm9Z6(fQxa-)1_YeCf zbv?Vr-$r`gFO|CaZOBgD)x%d0^ZFjE?0m>^7`k%{*SLi392>M!gskGFYIWHj7xT?b zZS6bO83B_C3#d3_!0BimM*PnsLdvX zTmuA#8^=QbCmutIQ#g6a&8bam=on`)BsNx!MakBqwUvEo=|8E2 zwpSFbD#AjU9H5Q-vK8fp+FGl=0hZaT)6qCbzQ|19`1SKfKK-=wss#aw3EkLjmu)+7VU}#NXbbOl-lCFHDQu_N`71)h1x^6!w#zZtGHw zETDDw{HAwSe;j?2hY18}4|0&YY*Gn%GeRZH0_hC6Lw#(`^HIIZvhoCUYK;JVnGV@kyTSE!T>cuidZ4qntRgn3==prm@$2@G0=9fL|l{P#qC$GYMpZu@l*n zufBhzwf(gX(!jugIgAU9wrT_jZy!KJapzo>xM_$}!h=8KqdS+<$%>B~tFL9u4L|ng zIwl3%Wy7-kz(G$8Aqj*-m>LDWP|3&y>+w{VVlevzZxoxL18#J#f{<{w9vYo&&!FH> zA;(e-cuFh@tqrxA5?0?yhJGJjUe~~MReIN|MZ$=zx|Z*p=Q*Ed%MV&fC?|izTS{w~Y4(LPG6qHRN4HNe7sv-Q9;czs)pG?; zzeT=B+O1cV?p>Z0?4el_5;tL{-NQ|EzEFLvl?uSf*kambEMs9mUBHd;Jsur@$~DA* z)EMa$+#Vhps6Ar?J*Kb1-sCh}*&w4piInS-YxxB$U43k_rQ}=%4u|_4WK|qemwA5> zTdMOHmDL4%tYv;-)emajZ9Ek;VA8?6kH?1PN9-C-107QMmKI=Js>e0MD~E$C zrrOJ*Nl4K-H43MF969lOrA9eS*m~!H{=5y;v$_$D7#Qn)@w{qzJU4Gka?=Pa{jd5v zSI#|0I!m^~QsvBOgBZth*fV5cvooOKvR9E+W2Zwe!8&*5Z&3aX)=4AP0@TC(53`0M zH*C2Sd{RRtA=(p@b{toyn#gyomRE9`H%PtJZ!cFIL&Z+Rnt-YMfcDPN%!>yY6E(Dp zB`1bw>2QES*P-@r5^UNa@rxNSAg#cFY)U&woNZUltt^2-n!f+d$M3zWi^MDh$ufdJ zC0vWZr^J(-C`0*a1&v&EH!du7U zoehU?{^L^nn3=d;zWvRcKjbF)Z_Mk*ZgSoFPg6t0D{;8%GtcFALEPPY<7OT{d>97B zy(IrAaUv7IvsgyF+rrlI)xy$eNVBIB5QqmYsjurR9(vXL49+kY7hSsY%&HQXOaQgp z{r~6S{i@?4@sG#YjS?Mlt0J^kYAi8qCqVhulma%0g{l`2dhOTF7$-mo|7c%MwZR|z zsnfRPfP}BLmvcs(c)!eKQ8IkrDIm9$+}X5U?%nJxW|$D?mcB?M`8fSS3&w)z1uuEh zS$SZJio}z%rErod&2Ie)sp~{d2T5z6&R41io2}#B=B6UfH48Zn~>0v_?+iKfH7aUQlN{3p}A7+$Br zkaLx1kQuK-hZ8vxrBbuvd3rDz)~OW7QS=J7jJ%{aO~)4t8}577S32P#=@BOW>Lot1 z{j)f-FC`I8tx)k3p2N)lmuhyl&UrR0LU=Z}DnXDA$XMH)Ss~3PRWkmpg#ttBMC_^2 zA4|CpYsgcxJM7z>4dw(H6MJ6=xuFb){938LlW@>mJ{q5Ke>_?5rtPDB1b=M`B5bOB^51$xi;{Kr6Q_QQNh9%UTBo} zR|vi=t4@1$hnhd6n$5SZhkbo_OWn{^L-$-V)iCZoFS>htGHg1{P4B>dmQ)!$eW>FF z+}-g>QRnxUeD@%=%7-3PgGiGw?=8`vk7Qp?4}$8a-Z|+FcT--0e}~3D&pOB>LERudLJaBN;)y<1I8Oav(EQvx zo2*TSRenmS@kw~#^K8e2finh_Mk`mrpcM#%s7R>W+CWFlnMK>KE0ci>(M&&;z%WNi z7`p)sr;5~Wb9%l#WGs)^E07i?r+GjeHK7xkRSoWTTQmLz!}m*tYa}q!oQ>!QgOD|M zJQw=>PxFUHpIjh*F0UG35@7vk?x+mh)UWmJT#7Yq&`Nfid~aKU*yh@Lz<%4bIZy@Z z2u3C=AcMr?HnZZ9gtL%|-RQ8Twv{{YaX;X4mP-ww9GbRmFsI!s&e13(V|^i{(6{?| zz*sma9n>XPPfo4~aV{Z{(cTGNoKvH1rC~s4)&v}YlOo>sYl_`49#L%Dd3D-&X90hH zVzZ@NXM_oaHUIC*iUU+uDy`~M>+`0P&;($k~=l@ru%_sf9BaP;Vj;Ok2h zfRj1MQi7(;-x7LAQ&fgnU*qpT`^NYk*+koz-z9C}rD3^F)1eM1fWT|r<3%|q z>1Am^pa))7WWP*Vo$Og%EX81N>U zA2CF-=0G2Ge%JB!Z14~p3kmkWgTrrqW-bLAkt6j>0m$DT(@=Y22A1U9%WL+anRZ$K`nks1RSrS4si6#^B@Jv=$yRO(fVI|pMnls8iVymrmL~nD;y#;q=|TX%cdH z;5^}vaFeO_<>1ML!!D0eK0}^;`96?(^lWfHqUe5P)XCYv&d2s@(I%`A|WYO z826>wn(l0}Fir3IzvPZJRRH7yqP=eI@~DRe50Y$Xl`81HZhmrwA8aCm^>$zUCzS>C z0SuZtG)BpmRS%7ykxr=2Rm>gH@K=Zu@*@0u_W3pxsb1b+#IsfnT1lMfZNLf!k-4JF z-~GjKs3o*6{0>kq`9M_&tYSn>UFt1d;==L5A}Y;%VGyh^w2-MN-#L=_E7IJ$7{m!a zkk;wMeSwuzIYV6@7UxK5Y-m0kXKp5#uCpueEG zfc5Y|c|y?odi!j}t_#i%so&dfcg%L}dcS83;=26?Ww_3BhGR%0udG5mOGQZF;r z2Zw^8K=bdqs4PVh`+^ANi`<5q^##_OJIVlr^nI4xgM14FQt7R7p-^a^M;$h5iuLzJR1&Dy1fcy0KWD> zi_uxNO#p=(aO@QL`BAJCdEnE)tsYUMS9?$Ez6cNdX=G;CxMddg>Q1)TL4eJ8b~ys} zcChOCJbEo0<3g7-W9UQGon{gmkviiwZfM(wOq*HO>?x~xgICKi0ETBDaAb%0drJQ{ z&uin!D0Dja=N3dj^$oTaR%6vm4WlO`d1oG`-X=D_4?)>Sz|Uf*;L6}=kF4N%Prv}M zo)_{$_=syZ8hSct{fwR_Z*8EM9C+8jdpmh|9lvq5LK18#$GJv;bpz)3;u&jbHTm@L zwIae1Y$8V^scf_h4>yhLxt|$-9>|5kcB3)#sjGBdkvQ|yu=CXgc)LxUqYXSGllY{d zxXJnH=pHSzdQqL?KAUdlczK+2Kn%ed?MMqpb&f#Y9_O}tURj>v`dUuQDZBZvDxZ@a zJp^Yw?wNB`m4@m%1}a)+el`NbyL-Uqa~>50ojOf(p`|(UXVXydJ1f$q|H`IEf4_kF zf=o`$zLhmb^&JOsW0#5wpIkPv!T_|Cvfd_Cs|gUpiZ6BT@eU_nqmWC-D@j*Oz`Hh) zGm?6us-Ih!E?}Uy^syRM`l-U80X~#(>E6dJoIL$4%wL-rC z$SBq4_8LPDlv0sla7e~aaRK*)Rkpcq$2*AsPOwhk(yWJWyz zP)-*;9wm`kuGo3e5sU0><4(~+T`8cO8&2w}sxG17Yq9{faj;FTLD z1_!@Hj%L@X!bQ_E3(N4Es##o4NZgDGyfHT&5 zW~!wswv9H*IR?eS2V_}&lHmlSX%;7*aUIV~qj_`ym>^Erb$830ug~p2Q`c ztqV1|(WJ;0!&e-=3d*<(svu-hon}-Y>3&`%VkzPcx3IHuHS07=#Es6U|ZHI4+N4&b%!iLS1v z@p;7Jf{y7Ein(es084g#b-pGC%sLl zUn1^F=DT{Hq@^r)3zsfngOuNy}(ng-4WtcbYZZGN}qpJN9`YMIJB=~78UGoIR)mc z6LmbVu}Jt_1)@+H1&{5mzq`W~TVE^qV)PiT6kv+zLgm0gKQtFkv2gW~3bldNydL~R zUfaYkuNS=R&Sz}?`tkKfh^HrV2n%N;beEFq6fph4mTr+i(KPq6G&gEhBaQZ4^CyjL zJpC^&JJ}`VTwmPbJVagQ{+MFk`(Q5E+wW}+LqB@laZNFGclM_2Cq^35L>na`hzr95 zJ4*ZcFUm)rAomp0O&^V1fUx1~(M3mr!)$Fx+rzZp$6GV82t`Lfz>HL3GuuR{`K;*u zfgy^+;q23WYYqV>$E97ohb2KfyG4nA$Y8Pp(Yg@cvmzZ$nRHH+KbfB$K+34Z@o%E8 zahmQ-RlEdyjm4vIO?6L1%D(XqyBZbz$h~x{Y2&ua=I@BxQwxGg@pE4;LLnOU>p6854u8@qc@z1jC)v)V>1@QH{V8$v#@!bU8L8 zIEl%5iQnDLI_(i>gEV5TL}B>Pr+TwzbM_G5iHy1NquF`CRI{({AaI)jN2B=#tZTVj zCGZgY7KEA#zVHC!VkE9M$M0NMF#VTu02L)3$>%k``G$&Q`2d@Zc(a;@fx>*-UmDeI znmtB8^*Q34j{GWgL8|x<0A#GdI?9)t;r2+e?g^+EGD-+6yA=ipfvI)}g5SkzbxE+{ z0KHs4*&S9>4A}VDLnu(sd+fG64dJjJ1TEj*TBOHRyyxKxK4$Uj5}whtbx^U7Ot-2_ zK;_rJ)jBplI(8h)qb_yYSaDc)Ks8HcqU~D^>+L7!oj%WJ&j}_X0rwZ}lt_GVSMgnL z^B8caFGH9XD%wtx7>g&HERku*Ve5x`L8=DK2As3m1VEm2jYUPh8K`a8PLIrUKK>}2 zA^j(1`=^q3bo`#mN>WeoFK4at8!ZNWLr~dGjoREO+C{NWc9axORJVr)zFZjBB~~i~ z9I7LH7QUgPbj$!SX(@syCUg;8F6}vmwDf!0h4Se;?|9af)3p$WNR*ASho<#x{=iE| zICO^AXMawx)7VJ@(|uWV}GI9~>vi@`1G;*{yKaJlB4K9<0(DNOL|v7?pl+K*(|;)*w)6Wg|>&% zMGdIS>gfyledv@-ps7L!nq1xqvp%l5liZYtABuS|Qr0q)%}uW@O*msXH1GX*bW-&4 zk(N0ob^LWIGYa=4mbMhEeKpvApG+M2rm*T?tEua_73zH6lsg!U>%H=Hbb%+e_e-pRWX`2kL6q z#ZA{$uPz85t$46g=vCa9!j?*6FBiXJMG2X}ym!my&kY~XWQg7`g-)Ig+N>_>Z2#TB z*u2Xe>nUa}>^~wr!O#b{1gj)P#(*x*%W#*!ug`;4!$~$m(*ig-teK#ceT()f_`w^g z@msWgRxIHkDW`rj4`sDsQ`d{bS%2d+rges+w2ns5z}(O%CptLu$aZRETV^~IN5}Cw zUFZO#m>l6g#>)yXe3C_m)HLDPL;cjMre3gVh2Vk*BR*i);bKs(U+Uetup^SU$1P)* z##J6Q`c@GijA5GtUoSxZ8WV3kG-z)SARDoJIqs)_iMW|1 ztAC1lNV_LxB2okwkDrhK!fkZko>smNidlgOe?2aDfv5tFPx8yCDH$_Zg<)kUm12+1 z)RcN*19OHId($(r1QDzrtg&|cOF4;42i91qkPD>zi7lxY%DOb+ReSi#d9ztQ zMO8-4R&L=Dh7wk)YmcZUft5K0-E%{d&rCe8NuALd>D3$MPzFU7hd<4w2~GWgXJ#`y z(OmD=A-)yS#BBch+YWgNhu7;R=47x*v++-9#}R6+=uHfr4nS=#_-|^nk99`po<4{z zl2@_GbLsZAZ{qgfn)98fyUtXq)yPRWNd5D_0RJ+y_+`jWi)M~)=;95vN3m0+DA&H3 zWA?w*>Bu;M@@g%qUbDm-PU&<2f5Wn7Bq3SVo&G-|%Uf_2u|N23ZE84!LCxkMEA@^} zW5TEO8hQ3`*HEc-UOJbq+UVkoQww2LJGGO01te0YlecYp0NU3iJod@8U`;?`zNh{6 zuL`O>MCdcfhi8-OGS3ZqKJU5?SV_n~_kMGdR707K1m5ilhf09U`{DX}yZbv#Aa)25 zC{rE)u0cI$XgnB7w~%`OsGxIL2fB$pJ=_9N?|OfAIfC&K724uDPU-hdPz-eRWq6%uE8=0nU@+w$H$WS$h7z%dY9!2foBafAgSk z#T(-4vufw352$#jZD6&TcX}fpVh4x7Iz6K>OT>ll>+zz%_^^uH?2SGh5?Y?Cg0h4p zZ^slYDm2u-D;HGU)-tJ&Ox-&L@!t39OU^KYUQk^JsMv3m?u{iUuhB>`nv>g>OmhG9 zl-uc~UtQ>jncfS}s@b2?Wgc*xSCk$vrVm0Cj3cd`*g(?tFKh*e5NZ*+Cw||@r^J6!rhkuj2PDUg-JVNS za@;+kKd>=;CTfQsS_53`z=iPerWR-QrbjIG$AVYvH12a#Ka^ zk)KzuCtgeyv7$9e@EvYj;PkSY2HnFfgh(^qp2N}qDL`WY$cwQ46^a}BYR#PU{;qBt zD66Ttjwt@K3~GU;vc%JYpV?6(Ng`gppb zkqbfoa^Xanj55bX!xz$jTd!&Sr*RsTk1Zb7!BUP_drcaHybiy6(ne!3>S47inE{Zo zmeBkel_OH+pVZ({Iy6esXmS}z%Xs5kb=_o|*^}GP+l@Mf5KAQ{6G^ddA-|kI21X&V zm)@s6)O>E~I~uPG=+5LoGm-leH#Udcr*i80Gc8NI7Xyzk`}gZu`xCs2EN>dtb=8)p zIs{#VT8j5b!3x<%a>o;$g-bj8NYn{I1_4$YkRd$8mptd1GN1izXz%USv7SSjD~8R> zjeuFZIn{nfP3O}fKUi-2$YrV!$kbcoa%+e*u-BC%$eqGs<%{%D)F>S6HpaRR0gkX= zTG%l1z;TAw5HK`vDahNMJgu_wmRp(f?X#NcjT;};YAu#NBm{g5Bmoi?m}-i#i2*H? z)6bqgs}>vj^eK8un9T>w7zCsiYvvd55EANN0dqDdrXA~Y+JyJRJ;vH^f$y(vABal3uFl3m@$0uU#rbI;`gsGf6@1S z!YRfl%W#sD7V*zt(2hw>*c0_TSMM>SvkXbRbVAda7scOnPdmb{-(tvp^&rE_B5>MB z1?bs`b9654FAEedhrLblu$1J~^q}z6nRc3B-j-W+LK>Q|^^5zZz^ug)%vu0X+~YcR zl%Zw`ybm-hdZc+Vd^29LI+n>Xhyng1z1;11RiT=>eZ%kQGyBmuOj301oA7tJvF*RH zn()@jf@$NUBy4lf?)yS?mU^yy{I99)Z&+-QwvTzyd zQkPu2Xj${4RTpsCOKoog{PV5 za%3Bn`Pq#mD_&~ux_Sreg6p_u z>6rRnLp_!5pmCT_KUSHdy+Z6Rn3N*~ly&COG;46z*Y5a7cuS^E`jutvQzs48`d7mX zzS%j;;GxdG3mhG$D0L6UV=_LtYEtEp+Z1$M1Ec-bm%RU{44%5xX|1BMM||*K*Q^Iz zvr!8%9XKE447S?VvNAPi6J~GvU>nNb2PF<`jo*iSUs(JmU$}@ER!}{A^Mh+2jO`)$ zs>emx*Lp&u2>z+dVBd2!XI}ujk4a;&b+B_?#k#dw5?7zCZ|)fIxz*Sr)aL2=4k7=3 z-l2dJq$uxybmPc2(8hO4(977-SP|2X7LRErsAQOFWx{}pcFM21&u&xwJmxY&=(Cs! zU#jLS&wT-ZpNk*0ztIAMv%QG{!gl-Zwa|{M93p{YUE7B}n_yNvq^@X%kwPQP=B&;3JHsc=X}|B( zQ!QFq#wVD^I!gKCwP4M^c55|X>wozte}V>{|E!Ag;`*-PK`%iyY!BdFKZ<9zfO~Dc2Vs^!qbJf^fXHH*kWD-f}kD63qN7fVG%! zeoyMkk7)k)$$3h42=ys{`g6IVduoTEj}IUNIFjwy?G*x;JWaQ^)wc*YQhK}(+)Q}l zZ$(NPaF5m?vsFItzQ5V=X68G5_VlUaKGz(vNTNDSdgr^slUD67+`3!wI{+c)3*Xye z-FrJ2MlS(o&U(ZsEzVzm9Qdqk+IzgO^z4)a*~(|&=4B*AxIC`|G5V)$mE%2${R4|?NBdT#&Qv}_+n(|@^34tg=; zsZq9U=XyUbz^gh-WP;!c7DyfE$R1? zJ8_9J*d^WZ!q-XAV-;93?9E8=;HI;2pMw^@I_v&?9F>#uqYoAJH} zxB3q#E8fpqOWMERiD|G3$(FkFYN9m-Yc(8hy^ij(zeXwb$nljG1ISxDwK0DwG>>~) zVg%a1S{H!~@)TjWaqJsj%q%JEDBHjmowzX^>jpoiB&;i0*fmKt7<@p3XXS8|_8R7h z>{f~Q`n~S7$~+C7Yv|Z%n&9ei%VDi6)*z9sp)1Vvu>rp3xy{ucKLZ*PfF(P~mS=^;aPNE-uU8JDx?x_QQIFhmh8|!T|?3JTQe?$9ajQ~F8G^@ zlzDHDZ(VAyhXJ>s3U(aRKR@!shJ=Zd?jKaxKD?G8ZIyu_k*-^MR@bM?T%9iJ$ZsCtAxtt98aG4Fo--%uD{ivF~Z zHeqg^s({BWw}$h~km8WgfDCsMZkRmL2PM~;l9%%hJEa3UO~|wQYx-5z>;0os@)RF& zFa>XGS*B>TR+o8rugrl>mVd<+Y_fs7pa7M@<5}Z3ITz*>XS)$BLA^EXN8lXhr?D(3I=HEboT8e9P=syBM0JhZ@lmZngUqC#q-abf31jlXdo#j{3<<9;W>2N zazXKDR+dz*tt(&rTVD1d7*CZezs8Wo>(ujOde>Omr7($dV=ES`-IU{=&PLwj{+1yl z!R@%kQXaWa6&a7*S_XMm22862dy5>Q2}RR(x7M)93##LVOU0tj$t3Y+*%QmhVk#|Y zIowZ3=NqLi@?z@ppwH`5|+~7dao6e0)Dp$@0YW>VTvucXZYK!A}K<^iO zwV|IRAdKE8O3#}FnAlLRCX`6MjKgEbdq%r?9F+%ZM|nyTMkm`>k1v>8Ls&tHT|b|;@@9=i&L+V@JyWuT;^ zM#MRosN2(V&me2`_r}mTI5#Zg+lNolGUqK`;gel+N!L^J{CO*Blz67+Wnx*ILg;N~z-$v+ zJm6vqV05GTZ~|k}x_!eJ8kHrm;Klq=H7w`e?R>&E@3V_Fqt5V=3G1O49l3N-iU>}$ zHY5BsjnLQuxi{sT3KuOSh!6S>LpGGBWZKUmdKcBnQ=D%G5$6`aYWFU;-K0xp6bD>y8p5ppMdZc(^c$4W1#_i^YbSB&&88`b+C1OlG-;WB> z!^%198*t#wSGgQ~Dl|QhUP_+^q%mqa|O}lmmLc1W6C*;K# zNvjqmekI1!4(3yf5*kDP!MhPsTn^bRwexe!hp_=^trm|<@Btrfu3o|Ty`+)3z!YEo z=9J%044P9k5|A42JKs@_H_eVoT!4w=4wyKX`7r`ff92UJ*QZ_Jt@Bl)B`NRb{wh%x z^;1pyIv^A%(di_tbcppQFm%xL$}E;gb7#>z?#=9AxSjHLyvTqD^RQ-srQUElpv6yX zA@id}{=v;L`D2}=s!jF0?ZT>3xn>o!+xWn1u+j2=2zT0U+Iz%G!2g} z9xxr_NADuuHx84HWvS3Y(P=l;?w~sso$5U{C?G^t2#yfmPfCZ${I?t`3r^q4`J2ap3_4rgS~(ntT%D4 zj#k11&J(*Aq_E1JwCYBZG%b@kcy%Ex6AnARitpfhQ{LZJT~2C=CiFPJ5z-Ut9YUy#B%!SoZAE@^?x36n@O#`kIhc7>&f7DNK zG{4+mWhadt3Gz~D3koK==}HHr$O*#seOwNqgR>pU57wC8{bllbCE+aordjFrf|f9{ z1~Tv@iBcVKWOLE{;0Nol$4jR+;Icylhr8ZTotMj&h}3Kdv1&9-solPHVD=gZe!cvR zutwT11U1t4%WU?N6R!*UUv(eVI3Z$d8ySGkU$$kv}(#!yC;glW*ID z@iCw%NZJrG1`=6#TdJUP_t>_Tdfx+khcMc6pN9g~5Q6G2D@z;bkFQ_O?~9t)4}=+9{rnIBEKZB_~ZL(Dk868>i_j{IY#XjGq$P0D3t z9%E9#-HZWBjTIGv<{e_$?H_?cf<&5|Bk7XK)$KEs1gWASM>0A^rZ@)(E7p8k=sles zt*EM?(CKmLL{4-3yCs3)0Sj9Ae4Sq+E(K__LQaHiFwXfdo5Fkk{jK}k*)f?DvP}@0 zc7jG-lHz5j4;Dxf*8+OGE1WiK|4y&A)!#iHuhii(;Yi0g_x#J(v=Vk~(pxKK-nMIW zR||^{FIw0qrK2?G45uVS?yKZDKftYu@bagyPMBT+s3Wh7ZP?W*B5swZWU6p~lC+6C z;VT`@JskeXN!aW!tV!*oOqA!>w?qy$+iR6|J&tXBO22RLBjb`385pB^;>D(})>8A+ zxS!F}EzWZTecCDJJAO=g7Lq3}MNabs`^AuHgB;r-pU8QmvF(GL)@k=rUJU_9<+>nQ zRmGn6w!`MpJMN$QX#a~oBM(OHP3-6vS(>G?X3)Q}nLqpB{T;MW$unbKYrlrJi@7?~ zC$H@{^`T_(m783ij$~U|rfe&Ky%2uGnJOSJ-~Au55gEboC~n90!r)|!UYr}R2IJpHy+R>X7gw@^D; zBe}s^zyDqEVUvL9F&{I^Wjf(#E2gnA9Q!?uvY%0gcs`c^DR(VHu!bU?^J`-JU~*Bj!-KOX<_fX(~@7z@*Wx>nTt+dMD3X z0Woy=iD&&C`%&d*k}$Z*GW7Uuvl%5BdIMzQCT^PyZrg(KbEtKEe49uoU|<@zEJPeo za3RA`H5UgJ-}Y8%nORcXlvLR{{A;mdVyeAy%b$w}6jf&A#iRj(L-$D&bi)&yCsYYZ zJKf{e_)F8z99^*5Jb9EHi;YY7W1fF+v9%a~8R;oL-UG%kaW{a34zq=xZ`za*p*nV6 z)Dkf?oZ=buer)vGxVd$^YKtNwU=g3FGp{{Ae_{OsIL$AXuO@lwGtCOaYdR*bCOmK~ zKZ}J9oevx)d!JZ~DN09N5!Ub}m}PlR@Rig;kUfZdy<$)}c`P~O`XF2Qyf#0{+anOU zq-zt!HU>TpkYl586Ng9zq4`4MSeEp24#!M(zq|c?>OxdX@S=@ffG0;j3RXQ7_h71N zN*}wwfB>n?XiKKGDqvSf|e{)UATs!i-{hE#GVlj6YLS7>bU#@;PM4m}5~|e=@E# zZ@Vy29_~J5>+5UkMnDy5y{BEdLAMMjUEJ9&6ujbPIv810{ON?<~_~5$8Rndjtt%i zlB^3lv3%F?KgrmvE%diMC>^}oGnP9@SG-}ZbZpj2evdh3i=(OBm?w84O9`;@v(D9kx zVT8_F86(aV;A4!K9rwqc#bYI~kSEfX=4_-nMdQn2W)?J!7=0p*yl^B7DwjN!+%9#V zKHt!O#nt*x@;CS8&uq<4=n^YXFupu=5Y_!J;e9l6BF?ms8=b zAKb^Q~N^eUS3*R%_$4lpreBw!X z#1}_n-j|62q^vD736hCU$fj7waYZ)`-Qf=UnZ8#wt+7FJ`3c#(qeOkso_N<%*Xs`r z%%6%%?Y6AT`^#WAj>7aSr?|szStRwj;$Gj9h0`VxyVr*pFFzPNI^r?gyq4J4KB+x# zG@)#Lblb$>*^Tqz?qK`E6uZ~(;^S9Da4@;tZhX6z%$4_nJMmu=L`EZ`mJdV?xvS9u zWA@ja2mOXb*|&YfwxNSrDOxxXAC72iNR6kdRBRhK0Fb{Ri~YRRFB+l{sJn3K9y<4~ z`oJz9YE1Evix2+bV8+0=9(E6v5N~Op^wsg#FT(0Y>v)kJF{>cSXMDKH0JTH3->a}+lJAGpuqzfnbiv%z zO_~F^rR~BE48(bkw@*k|0VD~|qGxBcZly&W%+D|#O|8=YF~|*WRKUe>az56;aw!;Z zC~|JQallB?G0W+@v`$x14>7?x{v_u^9<*;Cb_BB-)HJ=z+yTptc@S_9AK$Fzo{Gbm+OreA zImCbNY>vp}%Njmj@p!d!B3SP5wh;NfqVBOZbskhXQdOf*7E%ARwzf&%tgXGcCANc9 zFQ2{5aAvz~Pa(nr`GquoZ`fx1fX93#<=Ur#07xPUmE4u$U4ScAf+W(J-E>VPAh7d; zd<0~~Xj))Rc>6DHt$)hv(7;AN;HIxw#@y6SDSSnm*WzTGz2~L&)(#JQXPyNlAs9b_ zx_#>ZCv7+q$o2%_n+ElCk%tkgAZu1_5jbV-j;aEZ6rkwf9l}{Ebikb1 zizc6Si3n9u-n%(vGzLJsOlO*K&x+63Pki9_`-SyVHF(`A$byb*D}_Ul>B2ewwrh~| z?g_8mr9cFT_j(NCw13Vp5*Z8u%6mBlr~IoUCk~Qtb4@_IJc#>_+7dG5|Ji#n2`*vCbgMc4vp$}662tV%hOk^vt!RBw3U%(Rvy78Qw3}X zd2@kG&7VSu|C5Ghru=svZBX3Cdk+sS??a?gzqYVXRJQ)_{N1*nqXDKIP{%X3m?@^V zkucazov#d>s6G21gqf-~IHEZ*qGfwDR z`$b^y&sDrfO(5?2?&r0WBX6WH(iJ1r$CIO}=B9(rd^wNQs_>fhQ3?=Aql-n*I4MVe zlMI#Z{87L2q`S1x>i0K*cTy8{&=A@)*hI@&V^!)@*%6Ih?0vJ9<#_)N{ilT~ql6~WJF`YI6;}Alzq@!! z>I|s*l>~m`BOLurJYBXE_%hshnoe|kqD&wM-u81_JBiCEa*$Z$C@qVb{pC#Hql%$` zigAT;?mQ@94`YByoXU8b!4=+hjWItUScIWiyf&-j|3s)0RGK$ZsFko?zZUa;>MHi~ zSkgp8S4m@G#sk(jQ;M{U9ZTye^EL=c$ZAjoV(p-JZ{Qq149S;{w(pqMm!<3c24wMP zulLscl=A=55ncwoNJ?Fqtw0A~%fZL>)*(xr#gJ~y#b`A`5lc%=Zx<_(2X_edhd^26 zN@?a!J?ms+pb7vmoiAkqp~$k7aaepsr|<`&w@X#cU2nYCc$ojSacP^#WZheZh7u$P zf%;;lhae!(gy*8knZC9y9=3t@l08pQgf~$+qX};)Eu3s z2HscGfoQvj*G`5Ecdxh@r=bGPpHe}saB=8-A>r1b;g!T>Ig`iPpD48vNxIizmqw~5 zK?hhR|K=6+szvrOpr*ltIp||^1SaltD4lr!vzH*e>xA9=!nc%>eLrbtdT%lQue`ry zfpQ(5;?Eg;g4aeZT$`?u*Lc(O6sDNL=Qs!Db9|B)D{z*5rlG9I@?Ibj3yajs&^0lJ zy|u%ZS>D5=hP4-H;u#`w%?-z7t%~zD2K^UiY*k7Bx9)rGX^|wl9Hxmt$9f9Cu}%W{ zHtlME*N%HSs$Jm2u~KRu0bW(f11Vptc;cAZuBY2xKA;xvA24(L_tFSY|HI(r;rUJP zF#?zW(t!Q@SS=fWtnO4n$46d^`Rww3SH}!*`ewyie9B*?q~GHs+u3b5Bdefc2Ur?Xun|c%bzHZ}6 zSwn*rS!mM_L2Zx#p31ZRKHC~;jTf$#b_p)(OwsZFbwkg4$FVG)ya1qLZ&vHqlaxYJ zv3FUzYa`{DvWpC9ZL%^?!J6X09997pyE>s!IzH0x6yr-!=rKv&op$1cOq7P=kQ0Po zu&%i!f#+rB%<3|3_3I5Kx!tWvJ8oyvRJeA6!}qn!Zs9YT%!a0FzwEpn4!|jHF-yf@ zrZy_6!2{WxsrC6AAX2;dlI`1tk9wA_2dLRPjFM&FugN6qS;P2>cD6+k*4ubyENbm} z$It!1yR;||VG`4JzB~54H+^_}vR_5p7@%Ssw-MUEeEn1{EW|3Kh-Jf0w-PO+G6brwomA>%+{U`=0Ew&qQ`xxzm*q18k~jc5)T z`KXOh+v{-;j|~HbMRmHuqN&`_KV1AhLZ^(MmT070@w3A@_$oOrg&l9QbZc!1>>FAv zrOz>dn7TwmN?li#2a*o$;5AcOT%IQsySOEDe>?q4_Fa`|>27mUC^>GzNJ78?mDY*Je*L)F;yvL^IM8U4ZH; zUDSwtOgo*g{XVPaVh}u-7e5jGXwD z;YB!YwSlJRL7@vlbQz|ln9-4sbx@mc4gx!FB#c(IbLK+Q2B%&FP(Nki4zJ*}e}R|i zAW!9@Ywz;W>E*1c_(5e#Z2rpcCFv7MH(ugf<|n@>MW|Th?|dOi`)m%HEauv~pbmYb zU1PrQJID9WKMOAOry}c8Z=&!l_Z>6SeMswFCF_xGkPo9Oi#mnN-b6n`Mu=zL-_+|! z$N$!FUj1(k=joyUZ4Kw!Lar_@Ur`n>Js22fPt)w%4ADA~8VNfXE^};`Mvq-hV!5HY9w_-SjQ)~{7%Fzc+d>@4Ft%0%cd8fs126Sh^T$nD^;7I%hhd-2(KM5 zKv&fD6um}97yZKD-qG`v+lr23*_2NIxeD6NaG&{@+56S)ngDx#xQg(`LvTPr-SPOq zb0f#EF+spGEKd5g@udY2#n@+=cXRWLb0bbBTcYz6>j`RkUp_W2Ix04*HqnO{Jt2VSeD>^gM<}MvZVKi5o zXE!0*YN4c&w1%$ey`xo|T=>8mD9^!d%zy#GGwB!w^8U-))ZFNr$eYSt0Oh?K&-gAE z*y3E@YAWyCI0)z&@5($If*hP$-KB1qM%Yzh ziC-zlVObZT3`Y733zzrzp9FTT@xEG>idB7sZ>)8%xnWL!`KA5?QZIikjgg%p6RntV zG-6niK|Ap&FXj-p^7GMAyQ;vIO3@iZe#y>nCl)oD8iq$g4Z_w$`&M2QS1P=u$J+q1E9cYv!me-8ygW3gF=T^kijHftK7ImqAJ3__PycQ`ZBqPE-ckX4wuVrrM7RBkeU#&dto}Fc z1B}fH@(kxoI@(280>Krs%411PQGlM_2gA&k>RlbXBt69v8J1)HSU2RH{kdipcv6i) zAX{kTIi}kjtby7hU*wNv_8FX&hVurXZcYQE0!?rkQ_jBg>q<%JWY>%b0%Wq0je4B=Y+Rd%no{3KHTD&_Jw zKd7y#-oJpw9l8^?INqRn;-n`4pf0$&5clUfjJL@iGVwX^2l zao-aN*&o9IFKl?gsPJs3>RKm|goV>QkVb}2Y`k41!q=U~;T*=JPOBNKEg4_+;t0}b zStxrL(jI~Ua~BYFf|u$~B+#tPV=}188PR|e*@_6nBU*X`+Iqd#^R0~5yDitcXQQJ6 zE(O0@&9+KTy29|sali))mFftg64vp-!gUm-x-(!^r*%2WnD=YNZ}dg2-N;GLpMFhD zYUf&jdUGLFyGB2YcJixIHB8OTCkGDZ&^|f6LR;2{8xxT1fmBsDAPtJoOgdKCO)Z_h zjD2mThsMMTmaQCKXkgLvQpCuXozSMB{@`{9Rj^1MemudK^}!|wsqC%VYS%?*EDh{^ zmXhHv+ACgyacGu-cRC&IF~B9bAMNoCkLJT5k;qxZ#sZ;7ZQ^=u#s(G0de`<(R!)(! zT6G)_r3Te=O&=V$66?%4H!nzinz6)01Sw)7+zX*#)s=jiEe9UxE(>P*)Efb;&6__PEDVta;wDY z))ueH1r5v1>AQ!y751UaYikFoq-%(JK?!tRx4Z!*1F=JDYTlyTH;r-ZeA5mQnBs#x zsI6jJ-fXqt@;!6L*NWc8^T!mJVwZJsGmH@`?Np;y?^OxP?J}8BEIo>l0X= zsNp%XFr|oT2Z+=nBmxX?Ap5fyr<*#0?7JmkgQxIFNSkTQrEDu;l1`TN@8e>NL9AykjbY=ehPCU=ikDjB^nKYI+RIePk%j}3 z04vCe0)FBu?Q_V{Ft|2TJoXh>rbO>ImK*byy=oIQ^PE$7{VhXz6kA zTMFWd-K>ml>5+*y6nKBrL92C^pwS=X zASgQtXaIUxSZXbMK>Ny!4ncf0Ukdzq?vzOL$iw^?qDbb(tmV2E6+|gR^Dj9~1%CPK z0=c2*4aj!Oz)1TX-F=%HIlXj{Diu721;OX@^v3%1394ikST6-l*P=;S0T1r()#Ud5 zXE4W)4DKWs-qqXturSKhA~+Pnm05(=Nb{N;28iO`F?n`>a*G+kc%($?S@ISCb4GO< znY)uWOie1MKa}q+QUmvEzT07voN%#-tC>kO#B{gmZeLQOQGY30iI@;-0z0dCA`4s- z0u@FO1a8W+QR|;n_om-4s^WkX^OnyM8^|`g{q{9@$cD^-B8XX2l6g%%Vyo}PKET)d z;ZX49lk6nPz7id+)<-Ia4zS{<8_s6=Vr8DfMrJH|sl8V+~wsL0*9Abr)0 z|K+@ut|DsQmblWSgDhNY{#Nv`bkc0f-9Bl!B2d`?7C3NiE&G;4@B#m{7$a4k=w@BZ zfKCYZwTcB(DP5*RP~gH6c+_>$jIXS4O5V`8s}go}xNrR> z)UMUu6&BTTmBoJa#CmqpnyEO&vNPFIKN642z;&G_l(3Y!P458 z`qw#$12gu0t59T3*bTmVxf6h%aZcTUatHfN<;2U+%3-&_uSy^IEF`g8ZNAqlRN+Q` z`?)>+qhVA|A^6@hm;7g@Cr){<6;^k?Gv_XIBafD?#D7%$R-}atW>$xu^?KB_(s~3r zz?Eos1Z{hWbLyTN??*TG_7Iez=Fs#Lc+e9w>{>Xr0Qk%gPrhAPWowJHX}-1wT;S)B z84>{$GG|6pxKGaOkS{4gh=F2wQXR^J@J9IhInjmHs*aurGkeeGN7*bl6Jy6x4;dcq zZ_g&Mni1x(M8%A_zi5$ruS5IC=X)&^ftldfm-1l$t;12*uFkWtja}v;;)I~c8<_5T zN%{u->~Q}$C4kMsf3l*T^0f3K z1eaLdwPC-ad~ovK5o{#?dxiC%gF27!4DJDaUt(GF#A| zedogN|K4Srh&fEtK9H$M&}UR6$}V^amZ0PkLKJ?9rM;Gc&-q1SQ0@9MsT!`uTPHiu z3{SN1(@1H{CX>k@{6RsX>nmW|?l+OBDbP0A=l$O0(4kKF+xjpese$S+Gs_vBD@-~B znx#F7Zp{8XDizLg#$T&abL8R$)_8{C<<5+R@pS)ULlfRM&9AKK{H zt%Z|C7UO9CU~KbpijhqNEVb~A|Yi9a^t_;tKU^`OLfmxgrtodMWPg-L5o zP}5B9w;~oZqs0tKQIk7nBgwse)h};+9XB)atz>Ekv+$_71eXef$##9t>L(Fx2X`_E z>|41tuCy4TIpmqI0vS%Kd!;kHiuLnvM6_jF!QN8H2lc{KPF%)lVJgMl2kLxvoRFzd zyJ|;qQgehPyW@Ko+4ALrYDe6K9Sq{zO{2az$hnazgv>lo$ep3z=It#S$NnZ2JzPC12#Gl=ghgvNtC)mlLt5M30%7)p!rrDmOUJTJzN45u05TNhK=nh99IMR~&ElnF zk|bfN9NRa#@CK4bbxzEYfGW71jG`@HSC=xw{Fx%;BuL{76J#T-mRWsn z7}$lum8H2d^kE;ptcjf2m^J4p$~+*2?CF*ReFr4rpiq}F=lJ63@^H=m<(8Y4zALU0 z(EUB=UA{*JHY8yr6dAQWn$G@r9P|>9LgAcVIc&S*mtqh zfb3XBdA&5IG1F}{;30LKh_LHs3M}5^xUw!^$L5GmsthXaNyI}ub6zjC?w5;fYjEG- zSSSw|ohl#13ZqJ&7gnRFOo2_JKBT2{Hmw~^Ri(s(wBYC1LCkj#RSIle16S9T!(P4% zg3f1I9yr^on85Bh&$pgHHZV{M)0vcZ13^*Zdc7qt1JO30`FC(VnsYg%vR}qIEc;@k z?U#M6kGy>H){)d4x6=RH%UJ_S%1z_fOBMpY3ysPcZ?E1w!F{eih#+oz&g#xR@y#EQ zLFDE7256q+2rSVtzH&&r3MtyrdZh&;+~ho>Dr~pj&)K&hxfX__UU!)O4P+AIE>q$R z4ClN(#P3|Y-L8Q0)azHkn5Z_SM_MlrewOw&ub;zrBASuz2MDW6>q02WGe>&)y%odO zdPU`c@YuqfQJwWUQmXo$pzO(mf16J==ZaQqOpx1i3n!ke*RET8bOKcrnB1VyB!PSXuFpmlDtcJ#rC+ zqm4@us}5P@_Pc2CKDf3!I93WtFZjQFi-y0oLj_3{zO~z=$Cz9kmG!S#>s=3LuSB;k$e4dcI_!&IY0z%&rDk>; z)O(lOH?0tp+eah!9pIdLV+BaQD9MaMKk%u|CfS+s_+VF5wOeuHgYDXszrT z0Hqf^2~BWR#z)*4x$+v(-?d;wINYg2S+KVhIaJv=626*c3AS=g3_4;~(|>|Z|ES~2 zIl?{$qu)|vv%7^GY4%!4N7_x2jJ6*4KdV(>f}_jL067Gwzh)v^TSxO%%jNk8hXT$o zwuRC*^F`1iyPwEuWt=s+5+KHK{Q|km;pD0o>C0(k^ve$?Cx6L@8BEDpi>#-I81wpq5J%M z@=T*xkby-I)s7}{?A5{!^hfd(X;0oKp0-+83WY2&QkyrHPN2osI>hFA+VU;q^W5|4 zG{zUzSCe)QWF~{oKKQfO`p;XW0D0$$rTet*s9!D`_?g{(j8-4?quGdhEqBJ)5lVky z;1ga5$m;VsV&z3keg}J_T%U`$uHiZs0#R(Eu@&aHgN@dKiDrkJ70hk|{Ma_P#t2m_ zW3rr0qJpU`hSKV2lAw{a<3+$MA0yC6^4s(Ju9sI_vUu5n_oY1n!n0YSMglygS8ig^1|5YI$r+-V5h+#ka&8Ck(`bp+#5bH zD0f_Du+Z@Uvyy%F>$s!A9gr68?#kndlC`}4*GZE08(ciz7_TdWBI_q1uH4?&%H$Q zFJ7?IY34e?>2r(17PC;L!H)@33BFuZ;4WzRzA)U{+7h%JTqn+>4eJtzDvqM@PqD^-Ph;`%` zW?txdz^=QJhVE+XucYnI?r6*#Gqv16rO1(JC77cX7J=Mve`8Gs?UINaE&EWo z&kPg9EEM%QHIz9e=xQ#?+)b)}>TOM@;O}DT%qeVAhwN|dw=^fr>2Ud{9hOq{_SFvT zgf9_OXBOTXFttIn$yVdeeOR^@1UF`t+H)DREXYDz8mIGOk&gS1IrWK1%@538Xl&An zHBzno93v;1A2r=Lf?~T|_)xi{@+HJPtNV~(eh|5mQ^x09SItowh~3SmQpjT~TEAHQ z*Y0EDFe{Er_0mTI4jf7qb4w{)Hk}DCoL~rpsDAc02!GXkf!i1H`6PazD{4)9X!-J! zL9ru<;uNfMhM2HFJ0q^WGlUkJgbJP@cHkp^LB6!%V5FtiCIR#D`a91Qq;vKb!9&KH zP10oo&t%_n&&knV7QvZPzDAl{=Q48JFd|o@?6@9*^@ukQl1i%xB6~ENSTYrL7W%o} zLZFyaPFVW(%ta)5G{Vv)KbV!e*0;YF2A@heYG&DOIJ#hY=7?&g@^$Pn??6s_(BR-i zi!2^k8N;%sM1ENbxEZJx;fbbTo_*lBB!E+x2tGkD&PHTu{KdEhAXER(}u z;sEA!yr7l3`z+txV+Tpdfz~8is!OItI$S-tLpxhfYR# z^1zJgUH65as3c@{r!JY5mgU;^d4&>9o=Ubz(hm@$zFu~X)@8syEdhNN-c~mi+T#rB zcCH!^0{gZ{ zBk;m+e=Ds2(v4T?%R&R-6wmr#A-8F81{n*yz9D7KMK3}%X!uY zZgdCnC*2_DIVP!y;FH`0_7^vJ7=PXh*85t<(d*Z*F(JvWza>=lQ+DPfXs60~&Vvg*8uiwDjMJ7C+>0jY$e`TW(z`jDIO# zIPD=!1PO702{9vH5l~bC2Ozo%3XyDH`V>hguL7y__cdpPD^f82(_F^DRBwiMz3v}l zxthdD)0{6b?X4GWit<*bJ$$mM!L217(1vN~{jR8)*Ib7f1cq4jlwYp!X|;i2gk1+H zqy>JVO=a)b^Y~2>G$fgFCpv0onmA!@F-=^m*yVrSi?{QSv)V}VT76~YXc}rzF{!B8 z`L?(zMcgNNA$KL*wWyMZ2)iyzKj`i9iyB|vMC_dR1>P*D52uGVMVN}&bFr4Re?k|s zEKc*}`MyC)$1bn;@o}k&mFW)Fm9?86itR6UItO}=^{95ab4@2RG^AV!JM0RXD9abj z`dvG@p7=M54_{H@2F~W4eSf?o@12C#%c8o+yUTUt+%EGR>WsAcbf;m;3^hL;#6J*3 zF@c)8^R00S@1wbJJq?B-1Kk|Ql{$M zTat6Q^V{C;HYJ#TAacMybkN;5M@kg+_NA|pcGa3f=XAnKwoTJYf|7_^e6F1o+F?<6 z>doPNc*wb7Ns`QUId4VqKJirCSLI6tmCK|x8WFPPDpFc)0E&9f467mZvK68 z?W+~EuJtsa?B^`CX78YFMmt`r8{{g(FK`?}jR;SBl@gmpEr;F?Gb1ClYfmSfDsK-< z31aUf8PPVQ^%O~e^S(9Nl`?NGe+~(f$aT4CuZ#1O9;xi!&7&|~91d-j@UL$o&jbyF zC$T~cif_;bJl<7Cbyka@A`Hkb>_|X!JAfnaq8*vVn&Xh^fjynan4ZESqm5LB)1zfz zl1yy{g3rvyYA(9xnBFlByQy9wGV6AT6%Y1}3ES={b5xCc&cApNuI$Nnx&7Yl^#S7n zq!+nsS!ZeOx+ifxS2a0va>1n;?AsIAWZzrCc?LBRvK+5}3ptREiZ)igUfegV-ol2R zdpy={0ve>$o@jPRuhd2NXx$>Y&a9T3IVGJE;4HY+7fxVG^9xf=2T=ApYwcj0yG)ijI2LyrRg*m>e(AcJ)+$Q!Ev;53 z*UGtx&9YY$>39FSIALGsRoP&jWb-B#oXz6Tw)+s zjh|t+r*Dz&sP~1M_(e*9*wU5onI{^5&{J93%^=Q~1{W`&r_2obCl_uc7)NbYC@;VU zS#fi+yzKEaYrVR!j`YpM9)Fc(%o{mK0V9QHe>|gC5AWVId23ZxFDvES;*J@0VB(!= z!7E7xXg;d7l=^c*d+ru~^crYq6&feX(4NAimw*Q7XD=25(CY`LL=9fgZDpjn4BeI| zeP`R~&A!{-5t9inmZ$SW(Q#=Bu3(1+f3Z?vBW=F*$p~h}?A(nT^*tcTWrX#jJ#`-^ z+Yy4hRU{M43dA?d7w+-uy>~iC(p6*9X>KTQ&S{HMqW7tDQQ%hzG0DJGTtM9^m#o8* z0unl1aPRswQ;EPx8OUzCzCzd{G2Me!EL|VHt|zb2RqO6Qzs8F}6(Aq((P@rjJ4`IGER<~pn4C-5iD`j^Wxj zR!jx3msSZekXKY})lR}OTA7lt36;~GkvsKwl{A-@`?2UHq36UJOZbm5oQIBR?7!Nh z9u?v%AA2?R^L8Vr_do*~fM<=of+(c$z*iD}Bd@|J38&jOl8!V{X{Lx<(Sn0>{?pD7nY2VybjlsIdTBE zM*k>^l0JB+oHJZv^r2)%TMEkDLUv&_+1D6j>94RKNw5#@DK>1VzMJE!m-A#|Cqt7B z5!kCNA_*d@)L2!)jMWi-{}}sjcx#uu5dfS?&hTEL*8qIcSO1^ht}~#iY+J`+#xjbG zq97pPMFpgz^guFW9h53fdJ_;SA`n6d5EKO!Bx0l+z(IPGmQVvq6B39JBtQs=G$BBw z1(HzS0d(fpci*pfevojov(Mh=?6TJPeOXIiX6{5!(89jBN!4kXvOXiol%(Z@x4cCk z0|)?!I51d)2l|QQ3K`#sfTo@P9gH5hXwOmoV!)IHB>f;jdwBRKiqP~RkTNUkPN?h0 z9BOk;Hk3D*=*`>=04rw&8%>m1Qt-s=$2v?`#F0g+-Q~tPe1BR65O5|HfPpAyoHz~X zlFyY=L_?1SbnBHK07;*uyzvtWed4~F)cJ;ZbWz=OZq$JG(k~=Ub^R4PNx(HVWHD9E zz<8Whsm*W_pu%EF)o<9nQFWayUf1tNxw7;vfPK!mI6KdCk6v1dB#D3xP81zJwcXyc zfJl$h-xvWNdIKOyuYASrb?|Z?!InwvDD27>o*xnnqn8%UYm8c}=hbQCE{~^(FNrJ% z2`?|*dj`}sXJFT4P<+@0kO~6mx`RS|pP;^8{cXX#P>N6h5T-7oJ!H^5MJ~=IMV8L6#z74$0n`x zjSJtj$r)_5pvn`4`J(r4ZK1&Nr}^3=h8*#mMUo*!d&t_st40C>0!v24RC|=If=y+5 z06BZqto(OUHPIiumX4@CB25p+&bwC)B`#}}(tNjf#ThDGiU*4^{63ed+KG(m${a$+DBERc!;ee}#N~gRD zDb92IVo%PNJg_`XAXr!EFL<;Fz2a=2jegg3*cG$dB3X}M?D#a%*KHhQV)2mej~kBx z{6o?16j=c3%jf6=4_la}0ifFS?XBe3L$}d&U#nPkZsOE%H!(=CG{CC|H!7w}sUlQ@;ShtgXu$ng2((<(~&e z^paa9yH@7y+Qt#iTPyHu?`ukIh9+U6?D<(P*$U)7A z@FU2p=u1Taq$?U-M z24$U=oF1Fmh^n#%T%dZE2HyT|WSj&;b}onq2!D%IFm3lG=z*c^PK&EUz7H3V0~-6_ z$y+Wr@3fgqH5nOQ!hV3RPZ<*Z$U4;AeBoVJzu31si-4~7J{~$y^PRfN8+@O4hiVo~ z0jf0oQhOtQ(Iv-z9!q{vI}Xf5gd+W(qNKF! zt_K1+nT`bS1O00DbVG?PPr~PWl(1J6TSJI;{&N2>V{$yP> zn#uWe10L>J#~TAvuXKk%Wg9mNdPtI_ZLg>7)0Z#O)4#Tbc1116i!(fxlo^Gjyi}#K zde&$SM5CAPaN|z6Go9CafJ|=xCcy_L;ivy)3aS-qakcomWuf($63XY&jj>xCy7CUyu`yjNt1+^K!VEzPaW zBTTo1G`v5TmT^4-aqL0e@R17o!koHV8DG`;ahC~eJf-sZrG-MwA0$q7^UWhwhWX@_ z^}bYdzy`{7DXFnk&4oF%io$1fMVfud-RxF7)TN>e(L*FD(nOS1gra8>pKx#qvY-2O z`UbSwh|yJGQWoQ8+pCq-6XcZEn!bBIwY7)$V;g@qN#k;=GEv-$4?#7YQSOMzukIt8 zjU>7&f)_?=!1T(~B8k_{G}x{que;H(`+1IOV#gT=6Agml+53T5WpOW74`XV_Az7*G zNk;V}@kx$&?Q1!N;=u!vEmbb==a-#{v)gcl$Z3AE&ew+%C+}3D^IGrWEM|f#0R5j! zCJuJXE@V85B_%g&NQPYcm1Rp+qgn6+MgEP3dbnBNhmA;u4 z5)Yqz{sjH?(A4jb=585RbNLq57Ng-hVl?nMlJCsi%E?*!9I0S-zE2F3z(3* zxGICyf45SP8GxL|hj=19ks-`Q=;XV!cqJW~xEM)dmtD$Q07BaXAvPC@xms^MhiV;G zTdp&bFGc+G2& z>w~b)tu=Ucwj3ZxZZ&4%hDxe8gL zq0ux&dpXgNx%&MX=r2~xN#UEhfVPthOD@U|F^Q%iSe<}(NCyVTRh^wRS>h@bJlnrnbywR0s5(A{@D4Gn zLQeK!k4);OvDZ(lc7y{z7>V@eT9V;<A!(&g&e_dsA4yy%o{d z3K?5C6L*EvU%0<&)|v64{-L;1%?|~eH3_3X=MmO^m{PQu+w565f~xrnE@5VMQ_QJL zT!txay~7KDskq~Jn2Lt;cq&z;>g#&oAlFoDZM=Ow%}R{ldXm6Ep}M(L84`HCV6B|X zG)q}y4U-Qi1cX#+8+)xt#Yv60N#W`g@gbG+pB9OaWhq6ZRTn9niDHutUzF`_wzd!V zb+II3l6nnacU9gZcf9y<7I~d%7gOIUNb60oo~&H zv26Er>`X*3Bc_(&-Ag9Rz!f$CTW@U3-h+M&*(CDA0h1S`L?~;vl?5GhZrECck#7BU zAka^{Nm~X8=%SLtwk2}H$q}`*KPtNot&<)T?19#1xW=PQZw>(`@AfRuy|3|pj|cwF zxw6*^Qn?xS`!#{@r5i4411}EoI`9V43(xHeC5{1D3vm4MHf+cK+57!U{u^C@*P#^8 z*?RfvqBD7=UW<3DeSo#>vUYU`AQx5s)}H&jc3h4_2X=})a@cM?=+_^4wtQrOPx^kL z*mdwuUr717s7TGNh5?6z@l;sSnf|S}e|7TzUg=ir1>RFAChUVCyWO(~!xDPnDUJW! zr9W?8=pGV!vb3W7^dx$G(mn98hS7=Q`migS!&c85Qx=@>Jn>10PZbF`W?(<^gHDa+ zi_Ahllk>YsFRs$-VjD9rEGmXw;y;kcFM1PvQps=N+$mt5lLYWbX5=;1L<4ssXlZri`q(PAspatcRhz&mhZhrq9Ul6d zah@sn5)Kj+SP`WnbRqR;m4}s^we;~ooTyQ-$k1ezs!+k??n7BTC6H$oQ4*mw>je12 zno@7Wh#G|g+MF#*;b+B+S_oVXex(z@XN0#sJF3hT_5#X;!AE4Ea%REv`((7J&a6c6Too6+DD z|7h#iA)n``&dH6=+prYjV;V?hDM$9NF?uO;e9x!>0T*|>y-l}K40v+fLc-k&rRRo# zOCz(&Y?2PWLC8?O>p`UAR)1=n@ek6!03 z)6zGhdbP3Hf$bC01|kMdGM^u4v`lr%t2+)yXFCnZz8>o7YRATpz39;6h=TXhVx4CC zEo!S}NJw6!?QZY^Rn?QUGa@C4D*xZ zQHH7T>t=5BjauTetI=_tzIqi0RK4+K4AVN#n1_rFHwfOHVhW=_7cc;s5oJpQz|YF8 z!V8zBu223k8Qz4;@pEoO*{w z&=s{o+Xq%|MYk8}C7#h)E(w&9D|qwrU0Shb>Z)HCu`@PB7FwSh>AuxzL&|5L4sST< zD-8M*$R+t0;VktKV|zh~nz&AC;Q=vR6b#-f5x%Y$E^LC%sJwR89i>+*CBgJc_7~1- zW6DBB=VD@}KxuKbr#P?FP~=M)lOg0I_2iWl-`lMDzzZ|jlgMbviQjzmh%U=2VB5t3 zyxg8ENM9uTm3Gx`^iP3O6Zu|(q3URHCK1ltZ+YBK>inM5qd(s|%5wn&@jDBrJhN^! znHUY!8brntgC-sPJ(tYRz^l#*K`*CdYhALq*{C2+{n)oL_KvTSZ74A)t%H+Ht&4K1 zVX@7R8X|2mft;Ti6F#;I@VU=qUv~H?rWE@EY-vB&+qH5ISKNM={HldL(@G?m&u(ipN;reTAR3p8J_|7#855& z;lXmfa}8jfU+%*F^&_igVnbcP^>$*`I!&}wOf#Bu-XHD?uuewU93EMFLN1b3DL9Ve zwm&#pwK&*QI>fowjVZczCkZXQlEd$4CzbD8YtefSqsZkEf_mJ({ELU{vrMU}-uV}7 z4ML-Z&BeWDG5Q*|nr3R(Tcuz0r+Kc5_FMkQNGNsw>=)h^OmMkLu_H4*wL>Cce$q87 zybFYue6Si?V%83^Hf!sOglpc7)G$LO@6#Akq0A_QP}u{>E4Sy zIDUBRN|vJI^mX5+{?EbB^3$r+a`fpP&AS|2*EhIK*3sM0)7Jg&DtrsNdxW$^$!gkK zr;W0ox~AHT2@Pi!8MdnFg{yMzrY^)0F1)f3gr%%Va0hPbAFDNc5&q2GqXN1w>p~&i zlQkZ4=(5kDlTyj8`Yxkhu`BY6>3+%9@z~h~&5kZ~RGdd}O19wWJ($H?zVRvEcdB6V zzVTOtT1;U7<2S{OqljYyl*p)H%>@Hi^T~r57=Kz|>Zp)QciWsH-w4-Vc!kqw9K4BP z$Hzkj9`67W9^J7MPp?<1dkb{LUe7chjZ`N}c$&`h=DSVzV%6=pej{wKIoc*{i_%rS1(!8r$CK@ zfG_2{l!*}@viwY~M6o}D>fj|}0US=Ap>jWBjE^xmFIi>nPlTFkSGrY8W|A;Vy@BEe=FAq0 zLYP+4I{$g^konp;;!42$19pXCsqY>i~wjSl5$5`1ON$9T5c+Jj$ z_Oull&TX&7!WTLM%1MEo10K`<=^il`Z}0v+isQ_bF=Nqci*UpR@AeO-1G6^ZSQ)(6 zfJ#{>>a~P%ao9t_@}rk(CSCDA!W58@K-VCHE>-0*IqDj13AgHPpuwy zY?1rmj>V)(p}bs`JIbyyZ*ooEd(R9vhxDd>*urpKA3%9&D5i6RHdSrByjVOiNSX3` z*`{pcsKrv`C4naf4BI%2aq1D2K_r+9luxwS=wb9A#d%!iK%x&UP0fe=DS@*i;T-ralmyWjtV--P6Fm^m|Z&ig#?^Sr}z;~V+{yF_+@ zKp=suS1#THfqoSMfqog@!3(scj?4D~Kel<@($@tQcORYsKKyF0W2gfHl}7E}c(5J# zywm-PnHLDOr)hKB*6Q}r1_b)7bM>Om?Z=jLY{BTm_@F^nN~N`Qq}cN^8NHjuyWWD% z!W`qd&zw$~)nVIi78*Q3Zk#@;n|~`$zdxw`xNN^b)&0?Oo2zjzq&|42pQ)7odPxWt zcN|K^O6wi?QvG7L-Wdo5xBbj1IY$G*^k<|nn`h6Z?%k}24z$HqX&@1bY>SP_xeKIR zdo=LfApT8_?!zu+s;moFTP#W=6Bj}PIwE(=)H#B^xb*s0P zWgE`oz#gTI7Ndn~y@-)_t)CAZII!NwUGL{k`ZGUpaBgrCdkl;8q9Pj}MT1Z@BdWI% z8tlEBOHf_jx3v#DgBk56H!d6OiC}PPsO5WT?idZ>2@Zfa)`$lhrYJZw=iR&$;FJ4d zXBm~q+Dz`G6V>xbva%G909yeZuTS(NuCFJMW=)r;qfODT^r2I)CrTT*8g=t{{7>0R>;UvzA zWziBiFIh3%Wecj4YQwM~nKR~AgFr0pW=CJ-Jy0gBZ5bNIfDaYbiu)$ON0nVK;8Ii- z)@Yyhlf>oFnV2!+OrM*#6S8afc-H-eUi<}yD5_(%DGo#R=Z?q0q&7#dO$w(r{*wO6 zCVG@h{}i>tw=eLm1ngGMjW8a8w>2e;^;E1XcaYtPYM3ZSuo9`+3q)`31kR6?%4I^) zmU6q9V0t~7OA0K0u~6O5UGzaMe-QUw;I?ZxcHif$C6JcLxD)TO&^2q+nC0k!dF>`6 zj-lU-eUb$f;qlR#yudYWh~Jt~2al)PtmWc1P(dLAYHdehrAF$J`R-|>wPf~7&WbX4 zV;5?XjTm+1G^>*EoD~YSB{i@pwc&fv%nxM4x)BM=dgC@#`l4Z743&)A#XOH#5Vjn< zyN+lS?_3BP9<4-r5Z>B-4nLH3aHAh_-eu7zq|ljT%H^z=!(<48Ffl#HD2^fOD^$1L zR8Ywc0e?{Y3SM&-!I~d2y^>Mp&mDpKsbiKS-XKjjuN$Z&6+GB8el+CtVTT@%P`Qyc zIV!eRu9k#Z3ug&2WEl3lL&8^halziZhrmlbC~hgs34H$H2`Foq-kN1u%qqp# zcz^G{hZ zVPIdfA0f4NfmMK@ayq0rok7q8>;5R-Ltm1SuzK}CRwC`*ex$c(?nKKvWdusWOxzF% z5<4o*h#E((H$fHMq3jO1L`tT-2$^NQ?Iz-6J50=gKrwac-=+K_xMJ;HR3 z9G-4;t*)1Zn7}rL97nR@euLuAdW{_CwsZT0H*Zvr7K-}d1rIvUJxzzscd%?R2pMg~ zD{{a!XjbPioSqK9@)qH5(b7>LZ&*vGw^)0TKBcLWb^ShshTOszjfXsO%iC6C7J*&HZZXCa{Dmsx9!>;uf@ z-q5Vz@W!J}R!>G*w(IPW;BZT$@I&4o)2BFdI4+Uhp1R?9MDE;5flw*(w~DUsLcbbu z22n&Q7nWO~G|s?pjcUr9O0b#_VxN#E;1S`?(nK~mW&L_YB=h7ZT7j&zm^hoo)FETk zkKpj_Qw__>U=IXEe@{)esknz5d?JI@F1{vCiXyLCvsZN9G@44ugL@}74a z!Ef}-L>@3_})#PRX#46>i@+Z*C9#O}qD z_68leIFXvMk;6)3(4&vDjULXiu0=c!vj}|UC0u;5VR$PLygO9N2UfW-Xnelss5LKl zObqFl3!hx5H9;`usi|zERw9_43h!ovUvtRBWWNnTDw16(?hanhM0$W%n!w`?3oHy0 z+_R`j_M*HOp!X%ttM1`uT;vc#c4Iu8Kvol}b-l^HA@=e>fD%XhsCIM(J!7`F;~QEu zllv{&O))A5o!3h7^Xy+*zQ_FDgfkrsu*y30+q7GRMj)+M6|KxFPk3Kj-@zoo0Ot5(Oa*IxUgS7uOMf%vP3uZ@LLU5QWx)0f&`I$TM z>Uv?<=81rAFAa;IKIGtzSjrvJ+z=OR5Wd;`S=Tp;6+&~5(D=y(I?=S?tw{xXa#5it z8km$@e+rU1=zj(a~6x7obkSm$u9Q=#&^nB9Sb$1h;z1m#K>fyf^xT zW32x-$M^>#vRELbyw(q8_Xln+vCQ>m=tgs3vBmW6*OIGf1T7}DbLzL{Dpb8pDj|w( z`kJkpGRTR6CR?SY^LlzPtH)&4i0S#Bl8TDh z*6#!&@ofD~J8p9+x5cD_y9P7Z8n3bRf1l!25r=B_Dq*=s>C%LL*K&4j(J-~@-o1Nm zQy(7S$=(nbZS3AuXV8s%O|V4 zY+0XisjWd%(!+cZ5}3s+G9Ee~k+BjDTr!7-RAK4d8HLKm7umb7#-P1N1YJ(4x@BtJ z(|6;%a0RuRah3MF;Lj7JVJ?H>kO0Vw;e6zsUdk^Z?fm>8{d9JHhWyUe4-UweaC5W! zgRuQ)_@;`X7_EIT+6}DGe37*z~7hk+~IHz-hohNjw5Q7WlaQD4_3{T z_8e$5Qfk`?ar^2sBG}N=t{y9)ixpm?#QHdzJ3u}lH_Fm&(Klq}{rk=yqd)BnOSGtU z;rJ>CJt%VGDv`RYN|1M^>A!%i_GPY+oVhr`nD0=bJJaTl%QA+75F6aGQQzArA-6?-7t$ryhBO5JRjDSiwoo%Jy(HP-_SanOjJ>QjK5i87W$ zQ|-pmVTpFk85QbOo-sP-+#*0t6IRGdYaZapOqZZWg;fzkyc1Ow!!%?JT1V9^PvNGd zP**;TUwxm$R4uxs>i$+3&D`&AUC2bs%2-zXT+T~l z*j_Y<>5*;A4oq~#BPTXijA0f7MuOY-nn&d{htlT#Z++cP3arrMldRw^&3OL)|$%Z!KK&Ao)}6uD3}=aIAgImhqj${_)@ zN=Y}vs8U<@Zzm|dRlzy4lMeAIJ@#yq-U z;2d>(&FguQOL4vwjuGTCHlj+p>h&24A1H0luzepqQC$3v80B4sie&`5G2D?~VNJE9 zRMfP-v|QEuj_d40tW)3slQ83kNoKS={Vt7Mf<1`pKkk2)K)bZ$NKoxbQ=J!?YbW5A zhsxm3mhi$aq~GIxm=j7Qq`{A>!te|gLvu0V=RIBNBKB;KzpbW7heLhl0OX5XM9~S` zfS0SLyD*%ak;~|;9(t~8K|J!S1cIICvkfGFhLF0unUGI)%CbZe+V-eir^N8DPF7x- zzAO_L&q%a8+p2#;N>*e=fN==NDp(hYNcmyDc=SpsBa zawrc6y{vc0LbfUr^J?rFTA-%wYNq zukL_UIf!)>8{jiN5SD}9ShtuRah1!TLm`Z{BT>Kd}MBw)k97N9quG zK%YKlkyRd%OJAt5o*wr#7$y{#5-$p{gM;hfa#Pdpo7eb+Ze0tOfJHB(Pk zErU8Q_Jz)gBFIB*yQ!%#Oi_8od~bx#t_zN=V_(3`gg0eADQyG^U$6bw5?<;S)ORT#oVZ$84O`8!|LM4Rw1O zC(dYB>*!cpu~Xk?7d9s>x>51YY?v?RIIGYhwd&HSww{or9mFUuc&9A|PamxCxDs&L zv9J1}@OH7dftioPk9!?&nO31@@M54RWMd>>E^5WnO4Fz*Ne(l z@I^EaNdD{qxlif+IvZWad0?i6?iwkaeN|Yd>iNO7&qm9)oPsoP{BWHu+B}H9Z3a)A zX?U9_u0(DL10!_?tntc%-ihoq)`_gsNS6#Afl{H8R4wXrwTikdM|85Ot;eXG_@;(3 z9d9iDxmT(Q%@jPI<9@OIlVTjp{HS^TauxxyoLKRn|ngGhS; zZ~}-{re+R+>d>w!U_bKt0IEYfr_f;y-yc6jvLwOGtEhh=v$tCSfj}FC&tV?UVV3sI zV>U^|PZBbJLe>BDUEj>e&ENcsPKZeI2xw&fzpxcMGV%to+T%oOlaknZ_oJy>X;hl% zQ?MH#D!hF862{+GQd!yjQwd4)I_Tgab;#k9^Mmg@fU$ql0Jui>+C{y0hZ7-0If#qu zCU>}fibp_^E8i9t78Y2Hj2!!txZ+MQ^ZAp<0eK#*1D?`FPt`F?lB zuvY`A@y6y?9$mOo8A#e#Kjj*N!wGJxP9W{UdprVa#%Zc!@VaNQZ$z0_Nu7i$N8{3! z+ZBhAIpO@_l%p*VpD#=xCP%fAV_2GcB$MZtOU{Sh#|s>}Ekn*Kb6LJ>QCaV+ z*5$IfpC_{KnD$Dym0#&RMX#y0s+<-@nFO40#Y$r$Pm5@LG;c7>N>=&ub+aporL5NT z8%rcOCyd5c0ub)v>7uLVsYQ6BK6wF-Kq=IQ;4$tu3+|#F$QpCtD*N+n>AkJ;3$Np<5M^4=p8M%$>Yclrx9Dn1u?sBGb^pYjJ9u;p^L?Agu)Hga`IFKuXZURPoAVr#Z8Y`H6h8yL>f#Xc zR`eklxTucc!3iKXeAqR?TyiX(FKL{bVtQ;IUi)4Fz|HI+y!-M4b+xf9?3KXSLlm$OGwk)j6iTGUMD0*wy1{)iHJj7>pG!n~^?|##h1?8vO`?YnfAv&>Oe%+7VfGlp%wQ0E zhoP(GESw+wdHI}3?wGY z<(sbq(aoYCwt1MX+x0uWV0}4$Iimn9)Q+?G1!#UJT*Uf=S@*aEW~7IW3;om`D{l5m zR!s;A{7Zd2X$z%K{gHyC-ThbVKWLKw&KKg~K0ihUi>3f=7Bu}rux`p*D+1jM=9Mpa zh<_amP5!9SBVQn#58h}3dm{r#6xyKk`w15wkk#E8I%~2k5!T_LweiJ-UrFiXOSbiT z_;MINpyB$D;l`gs2R(WI@P8yg5?VbaB_%zA0I9EEt`Y}j9ik^SUf2#o2QSkfUmilt zEmMEcsX5ODl~(*i#A~huEoBo=Zv&xE`i_W91=``Br-Ra1cTG)~FL*y+i7>%6acb00 zb)wI(kk2q=r8LOndW!0EC;c%VflG3Br;T)<9k#mj3&{I<5^7^P^-HT}>dw=zeQQ#Qy>i&lD>R=iXZfO;i$r354;LzvG1}n79ZLJKeOwY zEz?1eWu6@xUA6&JZgI|d@2*7juslG^`Z%Z6jRP-OQ@fMbC%gf+DF{MiefjYN z1GEdgqLa-U$xXHvdOCvOQOYUeaS_dKazmrb3`zd%Q1Gd;l2F%a57 z{Y6#Nv(>VorVQiD-DLQM=eQp*5?O-eS}VBW0;rpkCZdHIbr!PK_T94f3zhr*n8fne z>EU)2lXT0WX{7ycD#VVVF-7H1N_kHD1r2KH>5=iJd{;U=W5(U0lKDWXN!Sgq&@Y+7 zIv}sEpVwzt`1(%UtVm07U3`?85cjC%bl=oaXe0j0q_ z;j4N#{SQe=PAz?-L9+2@NH<8-LFLXI12X{?%dYQELXrz$Vw9L@q>@dZ!{-&~Lg&yJ zYQV=vxmJkmYvhYadhjEM*1yxX&)NTwrq!J139*YN-`$X}2;QJ&>xGB$sjVB24_U-} z?EqDn6k>!8sHhH zW(;Pj4dOfg>~U*g44gSi?LVQotlf6?!V3?fellV%$SKX#&xQeS5*(As##V2;t z&7Iv&#J5VCe)mdg*W!DT~zDvK3>@YyZZ72{QF>s4Hrm5cJreA@65 zMAFL}t%r!CEK<8|gH~^755;Os1=^RN-|*U++Ag5gtnlJFJVR$qvhkk(v-qp(r-v^M zYrOc$f4kymiMC?Z(8MZ=ZD~H822wlmhnoi z8i@+^cu}86-f23}zyR8c(d*Kc-=u`3O=DFB${->YF2h=eLJIvsAghKUuV781(}rV7 z+d31Hlat@@oS8(Og|8)88|nO5xxo?j9LLgtX%&)gFv926`m#y;eu{dL`Ug2!ioG7ZG)c7QWo} zvOotazu!QxSK-d*iuNkAuM%Lx88}EDvvT-1HSxjah}2Y1YP5n12=qa6$|8VzfOFw| zyz5L&?X`Ex>GQj@_m{HFr|vi~t&Qv$XBtyQ&(Ga}zL-egv+C22mx#TaHA|6MN5bw7 z@LEpxDyj6O$wk_ZNiFOC$P8E6DfV5f-3ZnPdM86`zlc7Ht zBk2EPu<21P$m@5tR0Hf_1OO%P+{g6pdtJo*Owoi6)BF|x%Zcj(xPXCyL7bjx=&(6#n8N6coKhGy9cf+p_ z6@PP)6M`63A)V0zKJ~2|DtNl(0b3s?a_A#VaQbO6-LZa^mtgpQHTICHFbbA}^)?2O zPOg6Ct-9~ryV}P~4^wVw`_h)AU0rIY{Oiv;BQK!*vnthV`UtCI2}uDZe$sV5%{4|a zv6WZ_sVwUm71yGv+_GuKV0xVzL9(iWuY4UEH+5&}Zux7}NOFIij~zR)S@pxJ#=9;A zbB&{5dy8Qt=0mropy*fEmYQHGZ39e;p(^;9#7f&edpu*>UwzIkKF`o@*x(tW&;s3qPr7$X_l37TNmO}pHOhvdaS`R z&!^vW1h`b6tAx*3;7UhV;B6z&GI2NP;eA)*dnVB%*gPM0W4i^6##z4|-(tmMce8c9 z0##To;E3FraD&hgo_0N;BeLKnK?7%t&h8E7t=S7TGxLbpBj(8%32;jhDIXgEKOT@l zYR<2&&W!d^XDV&|VD!k1SGjlWTnT%$qS<~IDDHbQbfj!`$0epl3YQD_*ok%B98i1_ zVq~Z4jvW%fz1KK)(up8i-d^lo(i&ZXc$6*2tRMo`igdI9&n#xI@E_Rt--rAb)}Dz)Kx$0@xOl#}D)qRw+k+N}!N*YDtQw%qM%N#uL=cM?L;S~pnv8%Q73t~iL= zfH_W)mVlqTbkG)b%9i`>j%m6xVO%Z-*)dS$L)Y`|Gj68tW?sLcHM?J1>O9p3$ex|m{9UZ>G@?4 z>r~ZSvm#5%B>PZ$(FR zO*(F;BV@|Ojf5K)qNB`A_i}4?(lj@29=VrLhM3Ja(26_v?Kz5Eiu0Etw@IY>w|t8W zQ?fBZQFbD(aHI1D5Vke4=apF>&VQjW!7T=1Bk8 z6!9v|y*p+Vsfwi*OC=@Q!w9Rr3mu%sa+t36wfD!_eDhaxFHoH0gR;yy9e45N{H~K^ zW%!-K?m2q2XsidgA+Z9{k|%A^?fXMduzs2QVnfJjNZ`{2#iKWWtk#xs%S17ZGw=d|nqIdz>swsK{Qj(l19wJ* z&)tDCJVvmWeERABd7D9f^Gf8;Kfelzf+y$Fat}p*MnCeGEBHF!QPq^v1efLB?h>7N zf9wQI+!Sx3sFlv@%P!N;Y=hO-i*3wh3pHy7z~Ps|K;8O=jYcO3NLM#@ks+e~KW z@p_?LpC=0I%0gT@N|_)uNWlop^@r8sP1N4akDa+S`*tCy_UtrN`&65v>sy`Gx>tBC)q_{9}V)^nli1pmmvHkdEW`AK|oHg!RUO^6n z*2a?LelQfm6>iJPlLn@I^E2F2W#e-8n<3clT8g3Zgk3&YzUCbFt>k#XOg32D+?~;J=CeYg`k}UUyQFo?cbC2A)ai4l~t^Ltw(q);H zeD>~-zcQT)4jU+>ZtQ##JAX|o=J-k80Q5mCLvTO z+VX1yGN;0|JO*wS5maD3$?O7a?l!9t{Uwu**pA{7TJQY_m_&l$WM&Ns5X6_ ztPgru$(|~sH>lMWVsV$rk1t6B8%*4Ana&BWaF)xt#Ve3{_MIKCpS=z3H>6`Xd$t_HvssRO=#e&y0^ z8C6%J`^%T3QGHq)={}5L25~mT->5jXyoVcI>q}pdh%3f=|0O!)&=f^(G>kQDnVAwxk>RVE2>vve z;l;@C(j&YV4D6W4q%=UhpeNK`iYkx5d`r{OUf;TrSICm^iw@Tr#;by$*BS|6vinF4^gI5`smjgfahiWn^LJ=hL3y=FEB;g5%#ZQqlzcI z%BPxz0Zp2hS~HarC_A@lQ7tsEsFZpZX@MHVB9LWeFr`2IrH6WBy69!QZ{P%u3yr#2 zAW3*Y*QLX^D=QpVnNEv_((eNXpqBm2#tJwhZNzHo)yy-IdQT$;4j8G=bubXWEE zT3nve!-jrEpZo?+vBStUa~dAlS)U4!xi%6N>uB_T*W-Fk$XS!%h7p`5gV@%H@m;g& z{PgWf%F*;I-~ync?$l$jruR;CWa z96CiX^Ajw#?6xbemSK+Fy7spLI@A}Y4hgRBB^&nu#!uOV?ozSS9UuneJP8i}g%-Di z`6!hC?8lqDhs^C++a)3DKBKpNAAmi(lZp4dux8!631$yBcUT#L*7)u~j9T@?vq()%Z_t89KsUrvYi1pqZYTvz^N-TuKeydNr8PB& z1f>4h^$$UXHjNN|ki6c86nIufB-=d*^(k-KVQ}bMC!lp z2USc?42H&B5%`LF$)3IV;><+3tF4xB4%2o5ol}kIMBe!Irrn0Z{zd-)^u3&4g6j1! z5^qE{oFby2$qsLl>6z>|>tSJI)WVZVnp)4qA%1;p>s_OL3B>P`oy$?GN6dWu-1x!A zE@T5MHKX|8&rMdz-qBI&!j~1Oo?L9UeNZ2#?mAmRrSoYOodvS`dcfw(vDAfHkFjuz z`}aGon~rdL(5z1ZH>OI5Jn6G{{948Q#!DM7Rp)noi4wW-0RyDmY3!BtpykOf4O^wn z0Qr6%Q>i1&YF!bS+Kprc_TpL0sZ^XvevCfvK8Si%RTVLqhYxUk{IpgTa1PJj=!E3| z5gJ>6dN6;@sN+(5-DzTsiE}_0Q+jT%R3Xl`2D$Ww+bb=~w-L{RPloY*0&2)K{n^D{ zX%FI$_>vB2@c-`83S`$S^vMU&z@NQ4$bB$yDgDY^?r9T5M%k>9*4ZY72pXd@Jj+4Cu$62OXY8M;*AksboVKUk&7HtXp5#ycCw3 zl=9#R)FH;edl1o|T3pa29?5s}$W^9O1!o082V1oDf_wCLYzEgficRzbTia|dn;5WA zuWBSCsXD>wYUq{@;6bV`r7MlfNqf{r(y-Yyye+{ADBjI{koMrIY*LwUU#9D3S|7hV z_jTT|mornAgE~tqMtRei(tdVyV!@79JSbQ;I;MlVH-M+Xk>CA~+ z*r*I2c7wtQ%cQA?6{|2WrC2QMx~!2fbCg%4#dsn!Or4@n@<8=s(yk6#db!fu_Q|Pv zYKvoj3{)-e-bmA739gp`!T%-t;U8Pi!5P#Q?xpo}`5i|?rZM4W##LU2U*L5HvVCUj zFmTL`S56B-{}1UfyRrY+`pGLY#!j_MM|G$E)10_g5wqmq6j( zGO3B0at&(_wN%}KAUN*z0Lttz^=FX$pbMl0xZeVS%<*+(236txu|q!or_?h(OuAXz zg}J#5(c{ZUQY!1Kk|6b_{vi_Cu~&5<-}dYM+_tCV-7v_z%7dK571J#8aklVG>lH-H zLf<`Gc4DFQdz^p+(MY(otOJnJ;XU5T>yF1%a8Q*49Vh8gl7kBdr5Lw&L-*RNP~+?? zK$10X?`ZBsC>Cl-48aDTt|+4 zJ6~AKXDVl}XiAKatjhh{;KD9n#avpK`x^Cz(|MAmlx}ozu)1Ttl$|$ul_>Z*_4VEh|SBJ;sYU(CE8Ssa}#3>GzF}U==TF4Fg%jcAuN3{69BgH667k&&*;I=rk|J zF^|r4DJL8?d5+HzU^iUbjQG3wU;oE(cB&7`uO4mcMyr2-I)|&93Hd(mB_$hfyrFYi z*W-=Q98(9Azd@63W_*17TfOVkEgA<}Mv<*i7FC*B5Q+GyT!M>;8E{r0!1ok7&g_34 zoR2wv(_9NN?Cx9IE+iqjk~P%$2M`Eq2>eGhCZgf2pvwFQ@hmr&--sCIlIANobQe7A z)8_2@d*JUV7L&rLHEQd1BX|bXd*s*5ul2?y1uA~;NqY&DpTc5kJ%lM{B+gO8f`o3fT$jtoC|Na^Fx zI>UE<|Lp{tFb@exl0BeD0LxxG=J91c7U22ysJJ}7%p(}m*+tX4^%w;B-=s{u8?VUl zs|cNp`N#EBFB?Vx2mk^$2-L~{mlD5c_sq@n4{x<^9&Fzrk$8Qpuonc-p65em;91~t zhVajMyJ3?`_x)T)XV|0D!nD9Qn=@^H0uW1@ZEwLSG`vU0xIh@WW26c8{&~{2R;;ng^KAA9J)7i0Y$VRbdL$pP(=%Z{+f*21Cbgcu|F3>=e}8U zLf6=k_l1{o2(2jnLUyZjjnUMbi=t+*1Q~ns2x~y`VSw&BMRri1H5`Mld|R3RR;teO zn{v2Wk<;sGI$@#UjzBpP;S`d^R&RtuUs#b#$SrX{7^i`7o%4&O+vc=MLo&Q@nyQ|s zYkk|^1tW?||Kz4CoA?+P7soL|Y+@bk(e~CO?$XrFa1_Pk@NIpnTz16hg5UZbn665Y z+Dj-$b1DO2gQgN^Rg)}O&oxpUoK#s0zEgVyGlCeHV=OrXeCf=aVWHzLoe~5=yWBXP zj*`UgphfJ^gft<%es$a@xhsA<=sE~U(!Iii!}kvAge%&2R})-oWpGzfR8U4wH|1d3L^WF2~aO5pIWw zM@c%otf2Z61>HrOLFko9khXwta%~ds#ctY0sC}6z8)c_DW45uK`Rh}+FGh+hV&27! zG%sm|!J}k7ZmgH6*~V3|dk3>M_tIvb*Fr0~%-;iec9@N{o7xkOhe*ukc1*+T9SWQ%&%F3r|iC@J*iUoo@Gi7IY zwTr_~Q-%W8GTQngzFs`5d2@Q(TQ;ATE4%vBEguRXMzLVG1Z=ZWl*+ykvRg9u{{-Y z{cPm%s+4alv8z0we8&k>N#B7ebXeGSl>4l1ih_lCX^`*@f#L6AnG|(c@b$l}dcEU(ba^ChnL&OP-e3e0CpMokPI za>Tvyl60$-ETD8O?d21-TN<3s+!UZB%!Frc+&+hWp|=o5p0Fk^@2yQYs_#X)n8Y|E zY>Q?Y*&iuE*Wb7QX*(KZ@(6gIXecZD%7sO4-b z>R?P0BFw4aW|iSwA^07sqOh9b7wElfIw{$nRD;D*$Jvwhc&>Ecg!AMJ!Y=qTEBS-b zq;#?f{}s={WH|SQmwgm@{oP!c3H8cd{74t)%KXmS#ZEI+APuhgac|jXPJx@;BWG7X zQjWVV`VAznx{9@X4pVthem8b``1IYQfr-s20*8<{9gXOn7 zw7l&8+?uB8<^mJ=z3(dP);GFVPN&VEw(wuPrPMeb z-kXFf$oYxasSpp*qT1m#LE!N??^V+6Khj>sVDxKZKR4^s?2;EXOC!)@y1tt0w0*rl z{Eb#uG(wW$InekmVyzATS!JD^(*|}luGBoWfq5qu;Ls%l)tX8n)oQ~8(RJR#^n!aZ zt58fu*I;RB1R8xu_=A;OM*%-*TA)GT(n<2jNTO8ae1{z?rKzOmHGdT^Kb(6?%W1fr`>SxtEd z4R44#E}lT;7$tz_{W>+jaW6&ixdrjdAl82LAkRHJ8$?Db-Bze(igW1CHVO`6{RFhG z8fR;U@d~CKHa8l=YtfKC-peQILBlq&qO?JUJc@|lZ=dqLAO`?i`q3jTeGee1EWaTa z3Z31#p8TVJL4@a%!=}$tg-^23e9H}+*>qZ(xY>ytPJ&n`*DN=~m3GCwRixoEw$s2~ zl)v9VW1Y9tZ^w!>cfQuVP?u6z>?MhcXn5^GA)cX!HWY6IgUlk-3QWW4R z5a`*+CTNs6>arskbd>a&N_Ca{M@{eC6p+)>YP^4@bAKYaa}`k81gc22#JG*rz0ZxD zY%65A$C^EPQz`1an4kt9MU}b_-iBFf)*hWgIv)_qJpd{~}Us;J;mv zjG{lW5-1_9a{{W>wlGy2^$Mt3HB-QfOZ^zT1p;|TSz;2`W`AvIGL`H(_0!{UMEGRT zw42)fhzXmIqW5G;@QN7At+#@Pb7FV|%Kg(nOuW&$d~VwoBT($=aShDa_J;&WU1>VH zr7nWb{*^;?bFcKC#>kNdQme=RbyV0v8`h%y74Mx}qGckWHHLC|V}rxb@bKNi8vQN_ z6n>%gkz_-DrAQjze&8xZ1@r#9bLVqFk9B?UpmDc8Y5j(!m56MT$Rk{@vGxC1wfn^H zV2eRUGC1GlSG2PK!kM21ho7qhvMK6dsWtUS^Gn#Z=M%~(5~XIuvU+?|K9exqGd*TX z9QGZFpI4r`8#M=>#{NyL_yt{T$ZoKacD4T4rRnDg&HRh^;tB|i7Zut{Hi7V7dp%<$ zG1s^GwFUS4(A+v>rD*razpG1XdyKC9U9tvl&>BPV_gWY| z^1sH?{~G_Npe4OmA?+y7ZiP?s-A6amrSJx?KEgc@`zRykF>wNg>fk%7PE{#Wfq3Rd zjnCZuyJZ51$P$E~I`6Du;9f}a>CoE{lsIbUbBoW1Tl)Kp<`}0gP+r?F+!Q;!6a4Az zUzF7Yydt~Ww4d^yp0?5}f+n0_&B7L6pwg5$kzW6$Y<>9$592|`oMClz^~hs_s5^#u z`l0H|r5RiLjWYI$L^}-fcW$tEf~%&ft>9lIRT+Mz*pd)|N&$tH`RXER=3JJTrMI7b zOnQW;t#69vl&oB;HyhdWccm!o`LbIl-45c`JugsO0=qM)1KMw48mH!+tk&=E4Kp;o zGXRN_S_Mo35x`3g*pzH5g_r-qOf&4K0a;yrA+{gjenKMSZi$1Y+*Nb8p_-A7%SUio zf77SJEPm>;Yiv{$d?mAfP9os64dnCpiqnQ6;;6=WJWQ0)ryTnAl*~S77{WX@r!k6< zHsKP@w$EOPVu*1~4$O+Ks@yG8DPGw{e)|t5+A;6>62L^m3=A#~M_>lWgam}XA{OJA zBV7QmB(3fP|9*ljc6cKcHj;CqfJ%LpQl4t}AAV1aH5V;s!lq!ZvyFb)Vc2II+Q~|P zHcs$28T27o0nbSt+vFLB34779qAk5un=@|sY2^9w<)D95(uytXXSbxZN#fV9x3m&m zfy%yACt(Co9;nUwTfw>>8wDgce5?!UqKgNmq$j*Z9xiWq-bpYzHgTy#VeFe6yIx50 z#(1ggYShgCk3c+mNZ_33-n>o04SmfupWb5bHf(am47~46V3B@KDeV&MSj;0ZDDCjp ze+=;|o|{yQMLPP2xamcn9h)-HxQn=%a_6oD&^BxbnB!wVpBj#P<*9eXBF6z@NLo`& z1A)5oHX_4g7OLf{3Tud2U!ou2e)FmxRf)+=swK@_@^0LK|0Xq zm$A_Tq3PeZz)2P7;INY`aGFQp4)31)_|%g(WPc(J<%olJ{Xqw9!qS#j3A@}>SL5Cx zbM`DikcDY0j*G*do3)ky!}b1jcIh=UZY*rosf8phvOz@B$Yh+l9iTp`lT-PEIQyv4ZG7w&h$ z7Ne5rtA+EG6PCWwjIxmX4|o2}Mb-JMH29s9{P3r9eR=r)Dm)9ot$$0&aKKK#ya&)% z4k<1ol~w9iN99)6E;j8LkKvb=mS!&$C;eI6M+~#?Mo+{rYVSrG%xh2ptJPO>LAB_A z;fF)42LD@m%hP@Ne+lZe{vC(z8E5h<*wjm*fI`t)VH7DZePJZY(}bBnvby^Q+IRMI zUZKp-T07SZSlBbW?^}2s)yja z1(I`Nl22)2kK4b)IWOKYI)i0hLdKZ<&5c~#y6R+TZZ) zzdaqouTO+vyCF-M(Al6^?}vNj(6#05wBbHIzQsSPvx>Rd>H0XG0S0ObDA0VdsAI?eXsBD{{8O9 z0h7V^j5Jxgzh~KyTRavP|p(#}?;m;j^z+?r*fHmz(*SS}2_YmR`2lu0VU4UQc31+Q4W- z55J`;r8ItwP!gV8DKR=(w0e3SHbis{%Z}fAFA|^T5jB9~HgvMiY3;gnh)+*bB zj|Ba_AAFp{)o}fkOPyz0!>q8=2EtffaqzYQHFWsP1BKF8pLVN%{)#0u>gXAJH0?@x z85zn?#Le=E?7gHL)n5-os2aGPfyO4du!2BVP%+j=wQ`X9^Nf_@9X_r=5=4sjd%wA%1CX2`hE=5KUcP(S{>>LX~StR6q7SCSQBi86lQA{O@2*gHW(dCY?L zj6+EJ^d09>rmibQyJusGnK#{+XgUytU(m^5>^AyMhg19TyQra4l%-C$d>eS$Wu8)G z<^9z>5$ySss5>sSjCch`wxU>^a>9Eld$~n^N~&*>&}_Hs*|G&bPsR2gmj#sES+_MI zpk9Rw$BASqiFMRF@Au#IwzyG`q`X9&qC~-JZM-Sime8}nC6W2?JL|m<>EdMCqrt0k~&24x7`u6PG zE&=E888ivv)JvDI6(>Im*Vi9lNR-X|E4lMR|H$R|eNO}=n9v{3sr46R-y;2LAK>A) zN|!hr)BnG+I*E@A{*SB<7VU((d3G|CS1Eitldz^J-&B(E@iq2!Gxch^-T%}8tj;kd z8tY;WrlOAXWsD3eTB@|F$I1g&RZ3P=-mr?9XQ&mB5+~+mDGmnTJFSRpr!u@%OOM#3 z1#WC{bX>!6e{`Y)S>-0eS3Yc1wrFYSZ}#UBCM@XSf1`g`4CXtALLiI5{L3zlG;nFw zn<;iN+E7rI@fa%6hNlDBpU}BTYlYuigF~Z6T$J&Rw4JqgQ8)Iy{_AEpQvo(jQt)~+ zTG><%cFVR}{?iKhgo>40r0GTF(!V}aKhGIcj`~AqIyW~=QTIq-GCo*a(2lJ_0;&pb z2E;$Jzt9V4yIWdJ!VoN8_qQHbTwNMrfF{%*X!QK`TN7Mp`a&CuNoYVyBHORc*!A}r zi&USTbK1IJXL#`#_pZj~nzhO8jDqVo#TxZkKqB#>!e<_wr=Y6JSWuHyLtBbK3fXU+Uwf{aPCi>rroJ69v^fa^ee7f z9iOUabhjwU)a$)g>k?hWuM4D8*Zee{06lX&*W^F`c;@3_0546>%20 z)VfMJ|L^j#B|MMQ?+SuPL3`-ljae*&=bq<3^kZLB{W*h0oe#LF1Ozl`v$1uh{3f$S zFa)^WsYn1CD9S3HwC&W|1?=laB-pv_*#t?m^VO1*Dk=9@Pu-JstUWC#!7H=!80BXs z5_bZ^m@u(o$EbAzS$4u=g&WxYMA%LWvk0Z6R?$5Uw~}nHc1Q|bYLK!&d0i#atI{A!X0!0wZ=5@)x+r+Gn_jUe+r9Ruj7J=_ zM9W)WW7}lwfHC_`ZikR(`SuvkwR)S*si)Wz5&KI-thF+2&_w5|cl!7&Ya6^hJ6n=S z9(|(jh^gzHvf=#C-q}m5XBOBAB}* zlV;|~+l>{7ktu(+=(?ZR+Avo6Zp)>i23CG*k**u30-$*KYdGrHGQDa-;3x2;N0?!&q)v2ySWC;9MNP zWqlYOYG)+m?5WbstLZ6tW6ff9wR^w-sv?fOisVeRcZkiD@sG!?`^rM$F+Ye9MW3pfX|mlJx+v{^G#`kBrat?kZXp>b9%r9DcC1=BWN;Q36w()^yc^ zFzrXWX-5P$9qYU4%O+JY$Ib`;9ov_|j+9)tr)wf2bVAbG~8sr-&_ov)AV1%ZKO|D0(`Q~i*JQmE2V{)DM;d1RB` zO6>0tpGNH@Zd!7D=^xTWR)-(8#G6_%x}Xvk`b%*by}&KwGroCo!6*FM9kM;OE}!5b zb+rY1#YTUkm%VS7z2N_;hqF~n-14$@aqzWU?FmWAvBGP-ehz~S*1C3k8Z0w$AS&&n z9dR^6Qiay2KUT;laN{o3dytfV^24|80^~Z+@7>P1r3}Gtv_#;+7KwO`Xt|TT-~3 zIMy?;kakmaD|~*U8x7-Q3Q3=tkbrKqU7Gd~68+a=J&-K@19G&&&&0{OjvY;*0ewI@ zGuT1o_VfykO51w@@ulvOk4u{hhAyY@I-`F^XZwT@c2&QvsrXo!V_i2n>AGogJuI4!H>R2m&!GM(as%qBZr^zZ<7`rC7oyh$vq~4D2Erx zZAS#N`*Xi-eyF`A^!guc_e|sJAZaJ787Kz_mA|tmo>3kJ6(+Xt^XD+pF#C|C#%%Ri z%m_F>W}XJyy%vGV9KPbZwTZgppIIb@zLBUO2ro3 zc3Gp~)HXqfua;{szFS-LtQB(WMLAd*C2Y2*{)B)g<89H*3mnD1PzBs^vLWyoU zrRf^)QPF!(_yHegSjD~R{n)MyW$3mbSpXmkI%>~4W6 zyQ?p`diG3LB2(`0TaUx)!G4OE)a)a@n0_Z%-T#Z# zM4joj1Obb10_dxl!e+Bj{n_LLoO4XqG2PVr-~1&njxINDV zrvas+L5L65$>1e=P_a9&(K==SKzL_yyn5aD?dMgL*sY zl+UGa_=p|XaiQ4iqJ8w%e!NPe`%g;=!T!*U7O2>DH&zZMN=9cTWjO^Ovg1HQWqx3C zb;m0zLPv|sYaicHxU(Q2H~W_SQ$lbv(%F7c>9yCnxbiQU(J-Cgj|$yg6h}tu@9g!2 z;=WdLYmPS|1>f>U*TlC;Bl{X14Zk}{w9op~D3ts3(Rxb{i^?gR-j*Fqv2DIm7ZLsApFqi1>};}2D~c-9R8BFts#?Y=5CkZT zS*lk^)sJlb*n?gOybHZ#u^$WJR5e_w6` zFTawT7rgHLvZzQ|;oderi378ZJhCMvELUefY&!T~5V~vEL(7kl*_9;|JQ%CpP3#se z0m9`uYy7F}-cE6R1ZE>mi}5??rB3RNqeX7GzrV;wJb-fiQNu*Simk@uW#4~Y>H$rZ zKK4MN*y~)yknU&q+blV9G(J&8{@$JcO4+o$lAG~5jgtOg0PmE-(b3}8j#qmC)<#9r z8e^~j3foNYJJ zcp=SP{1{)8zQv3<8h~>?w8EiiNc^^yE%%wN)Tj9cc%kk_uv8pJB9b+MglTBG<*6c@ z)3BD-k;!l4IxgS%#`@Du?krwt?fZu&KX3u(f~6ZVy6)9OZ>eY-AcjKH%B%@YDYYY) z^q25|?o_^?lrpHj&ObeRFK&aN1lm<>b91v#zV!+S{9z+!{JNuX&krjvw^OIV>gJ`w z$zDURh?^E&@#pi*jr$n`B!l=j=An>Lvp0gXbuPlK-~GnY&67Pk1|EK9STu-D;5H`m`nGus1{Ew1l!ur}dz+5{+NWi0q6ba3r85Q6LK?L~=KT7gn)XU&50o$OG z>|wKdC!JS%%supn-s@D`$y59TSyT6b(Gz~mvq{-KT$pM4@Xp8M-Yvx5&e4iJ_W`6I zu5p~^~_;^sb&Kn5kni8e6hIs2ZUT7KVq zy8H-A$H)|E_nx^P9*LWT^2a9%oqs|e>MsIDDKjA_Z&-PZ@ZG*EDb%tPdHLA>?q zV+BsTmljWpT&;?4zUpR$;Bm-atDwYrQ<}|dHM^Frs(5-|M6%`Ye-#|pc@5gI_?_Ug9Q^o4ItWm%I z@?|hdh7-S(irqiW*nzIj%&-QLUkqZlJkEW}6g&nejX1@}jQv0FXHDari)UeS_r`mL}Rj0wDRJkM+KdK4;pK7k;ttHCJfEcb}?mTb&P^uR3`jZ~`uhOoQYv&k0Ss zrgpQ4kB}%N=kogf`}c~nQy?jlb!o#<6s9R)nxPk{-5Ddx`Y*$BVr1j<|>UMZ>N(!w`)KmMi2 z0p^y?%p#}%G3{T-#>JmxBa&X`r+{9$;QGFNh|+KAH(@GEfgelLMf|}1o(pn)ueDDH zVtYKOcV(?xzTi-;Bio(UPFYQNdLC@QU^)cF!ANd}u1Y{lnOZabz_+9T$S03k2uZwr z2kaGgFS@Z~4{d4~)t22Saf~wAO*6xQJiqcMJxJ)763H}VIOUfn;qAeEVdK|pJV0^7wx0$wtx0DFO<8Uy+g4T;kW4b@6)U5 z4O#j4YAeTUx!AY&&SyYv_g9odmOk#2^xvRWgk5l1&~n!Q5a>LJbKN7-wW)k^!RPQD zbIehvkw}$HF1FK;m{RTrz-$8lA{O`<=Q($Gi38(mx--5UN6Y=HqVoCW)P=RYK0UA9 zern41KMBotZuR9%m{j^Y0HWtUEVii-2QKXPyTZBqf}?(bkJp!JGqPWG-4L64^c_lf zALc{SBkcGVXS?s-Wc6u;Amvi-b>@SX*OHetYRxvcp zzF2zOU;XrBHD@L=1eS|1+Dc}~TL!HcFd~j?)to0y9rQnP4gWPgl@af~&&sB!&3H59ee>z9%6r}w?V}NrOKdMJd@t?+2D6K+-!v>*hidsX z6O|(mUmt&@&Ko|Z5^h*_0OIHFy(RYsaZ#!3)HNjnYIwN*O7UIiO%oHQ*0*o$;GKK7 z6tXxo3RIDI&nEp+Ek(Tc0QObE4;i~hJGm%FIjSW$z`Jtdf%SN+lFzAPaCYHgmK17NYaVb5?_^^N=6Wjst5L0l4GOF6d!ds#bB|q zt=x~(K&tMz6DLdhB0O?us{ok&6Zvwq)Snqfdyl7sXFs@9?ioj5J&chTe$gWKE;?%n z*Oiu*`nc=?GLzjbfASpI>;$+Kw@S<5c(p%oI0@7y`hDzG&cUa840nPU%E#ZmYB;bg zu~aR#7aTbYGgCIP-8+-@;~?Je1F&p1sAK8{9ax3n53>OiCJ>_YaRb?ABic!wu#Jng zH6%eH3BAAC*q-zf>OTZopM{pKWm*k4o{s(Nb7Yp&TVZaK=wB|uzqbpf&uzoai#f}_ z(8|S(AoSwVU&;`*Qf`@m>Ptyt-RoAPie0j%1AQY#%HuttZTT79Fx&Y4>uW+UVU;CJ zlqq5Sa1Z+`Wje2^K}b$eO^#w4`NA{o)LBHP ze9!%9r&@0@a-2L?KW?;XO)-0P zC37|z#I$ac1sP)F+j4Vr!Kfyla+cj84|R0~1~RwdJ7pXOQ2i@yShz@LcH`m$rNhu6 zm1xFUHn?FoH8YX-aW6GB5>L%)ArPU}&c1+Y&w0cx)64VrC+!+Au4&DbBIrx47TPva zkg;`)`F<$FGbto{X5*oTSih_Fp^*2pn{9VL^%9A?ovswt|60$?i>H*-kxT)#PEtqh zz5?rt=vM9z8;72Ao>HQGIN$<&F@dWpkbM>8)eY{i_urm#JF zv=2N*E_R5^B-DiM#UVt3*1RRGHBBDnCE1Pms>sIH>zWKMvx5Z3i(*8CfZsX2PHA-# zO#Bk(L~?`4MBx^;Uj0^-m-0Mm{;I_FaW=MndSqVzT#5M&ci+f7p>8YAzAMG{LNi_q zsIU{0C+F(?5P!Tllj0G+fsOs=oyJH63)*D+DD_L1%esKs{>K?4U}i^>s;~;qV|9h* zH=)&c>YaXIKi;bI(l?W)X$1M(~8o0G8try!QtJ z9!?ysU}lbwH2{BFN8lr0zWVC7xtSx1{fDW`Lt8&z?b*8EgqSKGd*#}I@2=KsN615y z?*EAD>6Zb1D0VEW5SPlB>Tq;MDbmo&y^rF{HB^6 zJNGaU+zl42P848)32!l9dRwr;lMo$eBHSl z*RF}7guacK8V+T~DoRC{mOUZ^!jGC)ct1!>)VIsGbl-wNhM6U{UA0m!`ah*^MHWskm z^o)WwFtH}=bkaGllzz)|ir;&rqNB(q9P>vWw&ql#;03l;uY?+Flzmlc&X;6jxGw-%nErr zo9Eshxr1EsB$DW}5>v9iU{DQw#&^bhFs6F$&?3oimBh%ZOY=V#g3%6?kHGk5Us9Ot zpHc76pld(Cm=WvnTc0Pxa2)mZqOMx)(2A3_dH!wE_T!7Dx9fGUbN1P=Z=Py zaqDq)i#hgRco|CIsi<1T+i((i2)jX+?E~^8AQ^_E+COVtTbjY zJ;uf>OJ&}1`8DBS0ybON#E^t6n-A{q43Z5=_IT1?FI#*hKQh8R)8{yBcYvfb`mz6^ zaV!H23b7ce8AD$ETIpP)$G-#5_(IZCA0%D(P7L=Hb4$<{gO*k%=y%VZG3vJ~Yado_ zj>u}^dP6{-#b z6(5hc;msj_fHo1M7LsG&RvynM6#40-a0${@tIN@y^Sv}Pb6^Y%H_Q)Ljyt1v@5X8Q@n<`uMxYl@3Oml{^Wr#E@$xZ{0{zQ|_mgxB5zk?E(-bX+WRRrB5teC?b>q zs|;#+bzFs!!Eh8N+)#L!H8C`UsTJ&qmc1pU4iiXwT6|tX#Uq1#(8mks>n2J=C;^f{Sz0BDhPBVfz`{`ZP?(oRnNXVE_XaZfoyYB*7#z`U0VJ5eYFH6_%NVmlZ>MP^5jYH(L(4Y#ka`HMskWgD|^VZqnNhF9VoI5j&CG2eH% zIq9N8K2*Y;+AQ z`-2y@I}tKr@n_txcSWSAh|0UQ2r9MHkNEgiAch^VqNqb2gzJPoO}uG(v3@)Pq^r54 zeJ>{}pTCZmTY={sS1}nRy)U9#8?Gv%j>X+*)mW{p*O;p^HND(B-)GymSP)=t*;!&O zALYfsEH_a!ZU|VJStw;8dVQm?Pr{3k4(eYKU9xn~*y#S~Uayc|ST{ENxXR}Kbou_Y z$yt1wBNbDi)mPTa^H&av@sMQY#agrPXmJ(;${edf#K$8F(Uva8!+2;v?jsaGj7utv2wy)_Fz` z$XryUFp1_0hb|^^GSUcwl#xYrvr7V0v%mw~_skrWx8Zami#e zC}Chu+3+a+!c%XacC`&v(AYhYvyPTcwQ=Ds3kI3cWRU`3U^4GPNaNkv+JM10~W zXGNKU5Q+w|;Y=QvQ6Gk`PFL@$Qpt6+DY2t#IbiFJx~foEuTvMU3X|Gp1&CD^aGT9l zM>LNQ7D3gCk12B_5_TIDSC%dk~KkIO-sf3ylj(Khg&0Qjy4Bk zuOLvtJEIcb`^six7v~kH364{>vJ%+K831Nk=-$jWUwlOF<>E+FOHT5N0WDvG-~n-f zeC$~L_I@Eu4}bR(n?e3p#+5TaUzbzQ^*O}s_>wx*xlLNrh}d9q3HvdIso}n3;jEA| zEfr}82@CTz=l#zP=Cc5|>V5fYtK-8~ab`z{$K%YV;$BN4!?wF2;BU|0`_;B|IRF({For0)DC$>A^6m}k;Z_MK1~>oFfITGlGls%B$6!F0Ih z#ql?4h62Y**Q1vn#x1GV>PS_Mh6*c;N!nDt$cT(kW9LR*?Yy#t>svqGdN}}I2rz># ze+fXp$;mbsu^GK>KQ5560)?$VawM)-)nE9=+1q1JKs*A0691~LxrRrvSASpX)*W>m zBCJQLR!JyrrSz8q3i94oL37Gq4O$|eb$NMt<;C``SbWXuJY%kRpTlG_ikZ(xr2k-U+(fi;DSEj{BIuv@$hk_T`1 zo2vgSqlB|oMQ73*1SWlB}H7G);~tF4QQ8U+`xg_rVtA7bF* zH29;%rTwo?lAi;^{~pfsGZW>+sru+Qx@gAO>7Wo|KT5-CA$Qgt#KI@TPnM5e!V^OmA+HVWbM^*4i zzBxOZp4+5nYC(&P<45~S)cAqvWWEJ%^qWU<{87&;jf+CvaKZiCZ$S*>NNR%(aK3uq zrz=E;UE0Omp=4PT>$TTJ4f3Yag3RU@t|L4$x}yNHW1Djdp5mk>Uy$NGFzdZflVx);~eV0H1D zhY(C|<#YFiv)jFLm&78Etau&TnHJo04PZO}_#5_YW*y3Fy7rXV3f|a9f6!Po3M+Vg zw645+O8=;7*K8P38_Pj#&?s@eS$lShA}@N$2w6eyO6T&kctpo-uGu>gG6 zJ0f3Tw0QwB?W!JwH-;Rr|ES^{ zx%--$F0CENT=#W+95Ob4ev@v79M+~dt9*>kix}oHF)q{7X)Jb(b}6`=_5>C-D7wF= zM;dE0CD(LnaJNZlaB>ltomul_{Xrsr@=?+lz1aMaeyj6%uC7O{Makh|m6*cMi}vLB zl#_v`+gkd&u|CfA zPX;cn#DXB`RQ!;*>qMuAW&=4j%&|h0;VanRph=8M%`rggI{Rj4x~ND+9XBei>>xT@ zKEbqNI{9(OEIEqPrWgewO?nKoBXdv1rtXU#@YG~3&#;4X*6!3p3DV^Pj|am|#2D!Y z;@5Ghk^GkVmsE6dbhx!_YC7FbrMbw_WH}W3RJQJ7B4kF<@ym<+I1x^0L0THo7aN+4 zACSyJ5W-`p8yGUU^fZ^0HgciZ15WK%Yqaf#GNb^JvbilU2;v`2%flYrP9Hv-AfAK!zTThd2 zS`MG;#dbH`DejL2UEa&d?nVqys{q7Wb&b>77(U>x%T7Hu&wv=aOw`D)FZ5#|rkJ&) zC&~WjlH2*AcHi(~^&;^$_46|=kwy|&wsJqe_57NgJQ8gu_yUqOvj$#--o07C;YCc` zjAy?o5N2+dgZ}=2l?f*A_YV~k>z-fm*_&WyBv{71*AMGsBH#u-d{QsK6S%elQzW>Z z^j?|>Al*jMuPT>84A(bd+oWqB{$;l9`fj#rY@4#Hqjql3}BYmtjW^(4r>;g4JR7lRZTX03)jvqd|sM3D`&1r z{LPd}8RZZRV5B{g<&_o(0dVa{1elTyTZ~E7Q7cQ!CcAHdV2b9E0rQrc7EmV4FA;x) zc;=euZadM?g8bY5CdQILu96H%XhUJ9-tF@b-@s&kneRwdKCzwoUpBT||JY%<6__Zl z-0^7T5VuM3WBg48#SCjhM4UZB^gSl0g)CeaTI7yiktd+u73hBfddU@ohew;JCP$C0 zP)uS9BrvOyUU4o~*4U1gJ*? z56$dth~9+`g%{LeSQ<%h$-XYx6Elv89Y_r%xgDgtn9&9zBJWwLEMNS@^X>NFo?7l9vCNW=s35t~{;l}2zDwkX-8Q=0#k8bz;j$6nL?A7D#>kgj3_}Sq-R@Je>jLS zx)?XFWWRbLZoO|JK&L7%A6h~LUlhm# zHI;xvxEee7N*at$fYG6CH!&KH3NMhHR}`WIk6X9Q{M8Tl6WDHPCgEpc6|Bp^=!Rf) zzSxNk)S>z+bkjBN>z8v0y!S(^~&vSlk*D? zKoF0qgDN@`@R+J*4xEPaSd3BbL7Se<7$&GcXTB#US7Y)DrbV*DBe>tS>`ciZGB%B7 zJtU_ICm`r6zG#9?ouoZ3xWn$+)gqoeoj&=vbcB;Hn!MYDq=m*2_L19cX*B7Aam$`` zydEAlX>^p_4uz@uGRKMpPk(a#nT+|f)42s2Iu0)Qt-y`|l*ln=i4rNnK!G@y3J{a2 z$T2)NmagFT<-7M@^+98n}47+9$ zvEXJae|Y)iMTHMZ{#=)+H?pbVQ(7+-N{kMGh++RN`{%OQ8+oI0c7Wq>uP5Z(6UXV?n>z@j_xVfDQ~T!^3v? zKo&Hff(`m z6fT!}cacI<*{d^+cB69fydSrK9b8u?m?o}Q#NkFgiu1L~$ugZcC)Ld3Hmh~RYIeGZ$5*OB-D zU-KuvXREO-+@^-*rmL02mSw{`aNmaV+6;fWx1(}#togyJ5r@yV{naB3G5<^J0SZ{( zf80+6_rGH1f|h|e{s!Om`_qY^-ugGb-IDgcuIXt-s4p^$q-L4bsnH1>4hV*0}v@{r~EWR&AGMylAM{DXG<)z2uiN{W7ff{dS48d+dZ)#SP(}Ig3lS>5!dVWF~m&E zFHM$_1>V2L#XOs*)6toOCb+Vv!wWCW%e!FA!?|HesxF%Bq{-t|9E*lmY-w(Nrg{;M zF=f_~uo>|cJM|ItwkD}BF(-}1fCiC=3(QsSDy~h9v zgFFgJDsiv|%%`ZCtY|H~23K8JpdmgeRp5q`8s6AHWRUqdS7+hrv>c%I5y2e+&HT_w zm*O_ehe|y>ZCfL?%naXt2AijO%@}Q`-XYWbx@FQ>-Eh#dJ}7^Lb^Y<}OZb>BABJq` znlYx&O168@>5?T%v16q1N|dBJ^b9a0t29r0CzQQSr&HC(81gI9unaPH;GVvljf-X0 z-w`D z^-7PKx(dTl9)W}W=xugoM(K!Z(;`W3^xcQUH$m+p+X*Ws(ZVqA!Eqlo?y4QN%Qnj z!op%*aarb7fJdm4lA=Zo)L8HA*IHGM@!>|lmYNe>nsiq1Bcw74w~df?MUl7k1?5*J zdOf*hWq)n>F_Yu4Q69cCQqNuX`t^&3AnOW`W?^}_c-Z?Ypnt%T*ph4j&T>(ilP%&| z|Jrs^E9wrg=y>J|l6M|cnSLB4WhI&3p|tqO?H($$gRS~ud2+_~mlSf>m#^Pf^L4-wD1XwA*L$3YWk{DsWD!>=9#z%7 zUO7GEonRk3mBY?0mPI$}E@T}`qg_8`QLRt;(cgz(f=E&uS?wC5QIo|Bjv{7i%`1#B z_E!kb-G=r;oPr>~67=7Z7fDzAqx#3Y+|fikv2^j)kf>u;Yn548shiNXM!g7*3ZuUN`yY?%WIr3qW_6BlwVr}v!-<|JXP}5$3Z5q z!#WbM2Vw{2>FIMgd>fG+q;RsGH}uq|a&A5-axBM4fwipw+W^Dfly9UxAiDPzjt(-Cz%5DnsnXVtp^uYKLB^lj zJ;Ofv0F@d1NK9Eshn*+u5wFT1?C{j+N-lbZWXC`-gPSN zNkSRUK~ZK~lB0(}LK#|oy~cWyUU;9Q>a)}q<+!m{P|o}4X8K%OKY$dc!5>x2onL&t z-_Md}kQh7N=D+#xy!RhfEf~Xeug@%P?KV?~$8!qA)qEf=*sVn!>-RHlchfJG9CM_%@*vKMGyZE||2e7wG@_eNUv+T*6Qtzcx-Mb_Il5c-aUeEam)$Hn}( zuklEi5y&-!+@ag0CU)5dc7)V5rRS#P>WbG~BCsXttaskMz{VWxA_27-kr!=R)DWl< z5p&a?kssuES^q%4(`X@KQ&d6}11x=Y%**Y~pS^J=RU0r=Wc6io<-lCOZJ$idlLX#p ziq?KxDwGrStvz;Pd7J8*(wyVK(!Mxy>N$Rlfe$eEo*W8wn>Y$Amjx9J&D8vV9?!{& z!}4u!)W8`xmM|AAv*f1txLs&GS?CsX!{~AZJV%nFeCcEfHDVv^VW8VMW{HqKyDbYJ z&0DI`3BQGRBvsT6o4za~IF7ta?=z?`(7IUpn11|xnMQiyr_Q-Z#u0U>y`yMy!qR#0 zHdJdnuOWitBxeK?Y5CjOkmndffz9?(s zy-Ia6MNSxX)k_u;4p2Q5U{{wsirUCB5hddok3yeq%6ML(qE!0!4q z!iX!XUL)1UO1XrK+57bqr5sBMmkz{F?LiNv?li) zUnNisNQ2K;iln1ipTec`%@0AtKi(>*JsA?{Mw_IJWarQeE@K9RuC84jWH4}K+_E5gb9V|fr2ir9%xy@*(p%9LwZ($f z?izR0Qr(dt4xt-19S8NBz-g=NPkt4HXgj1*%2xm-OQTD`D+0lr(*qFne0@qULYEPD z2#SZ|=#C>4y!}XQ&qg$6qPmpcD`kdID6NzP62LA;h!ZN+>80$^Teq#@evV4Ws|W=6 zqSGlSKcw?s_Iq_-4Xi+IyD=)G>$pd;t1HJ8MKGj83mOyS3Dd!kR~MqieT_XlU!u|& zN3LMBC^^o>tWV=Pmh8R%plLlMLuFVdPil8Pu>cpABRQi{(t4g#@Cwfmw)ZUu2lyaW zg)RnL(KY&y6d5J(B6`k9osTI+i&tew%F1XjYOuzlADfr(UqZs3Fg zB+x72>13geSTLD5# ziHgdPt?wq$@U`9_KXoYAeLXDQwvRJT$!%%&a3dQ#u!OML_Lt102z%E^9h@y{DYuqP z^Wh8WR~}z*NI=G|+}%Vk5Wy-51|&>Wy2K4$Zsr28f%v5Yi8OA#PT%cZr=F9_tiG&D zWBr`Ly_Y715S||~FTI&&VAZ%?h$&nLW5wLlFp~edrLyVej+~xzl6hd=FBR4Vp@Qi8 zJR@*d9|xv+fx|p~q28=a-#-mUlWWqW5DdE((!sEJu{uoWk#Q=9x7}u)HOSO;l^_O5 z!`b8Ai~EA&I+HQ_IZA@g70g5327Ak@6z)B1&LipBz{2_4H3vMou*Hh(FJ@YTUHfVG!Sh)=YpH6_ZjK7Pkw)>jYyl@Qm4A~ z`HqUo9^-bx))H-o41q4IEZ2>4fzIZM*zbd20tM?AhDEUe$oHK#Eb%I#hHxqg`Jn zfMe4^Nq=_qeRh-R^-`?g@&BpqJHwh-*LFperW6IGHybEbL^_0y^rljjB1MAq7KlJV zP!N$Ky$GR~MH7(T1f&KCOc7fR?q5-|RQX)E<#w89=2`#-@ZlaQ6cOh4y|zYTy= zvEPAYmW+D;U!PvMYsB+Un30LLwxG3L=llBerx;+9Q}~gkjP}lXzo%G|GfC-T01C-d z@DIbn=1Wqs{Vf zpDsIna!k?k(reA5Bsb}hVzT|$VOzcl5QbM;T83!F|KvnOCZCK`olq`T}Jj;Xy763qkg==g@ zS`X~zD&fRp-<8bJL)+Y7ZmWQ%g5kPBQ8?Ug9s+Fulsr9tNI(3!g?t|O@va#Fay|sw zHixf%Ru>2Wkb~n&qp*+jyko^4E|NJAm}6g`#+bQX$PNV6(1X_>o^3O4z*M2(RJV&3 ziJsEH>-4_>LRwS@-+j^5mY+tBGWoPchw1?ac&wq3g?!oKu@_p$%e?n+9M&@;n*729 z5MVxba**9l3;UIQtCQV^t{P{VFNGcPlmZ|wT3xmNB)-J447ao?PdJ8@H*5q*?2utX zPlO`e9^`Y0f-}kp>HEK1Dd08hNx!mMy_E!91agd> za{OI#Y^R8QTLN8wN)5bbLDnP^H?=%U)Dg|D9m zdSWZqGu0}du0;UQlhyh);f=jqroa5pASW+OKJ*6`Ks@c~Pr|HQ&hF0piw};%z*Z+! znESV1AbFhxp5#S(jm<1o7kHpOp`iCgk;Jz@0TO)3|26bNJq{P2W2o7uf z&;2>aRPf;&h5)I+V)d@9?)q`X$OLQ~ah55n8NKd{0B8 z&aw5K6Hb+x590j-(lVhDp%Wr7J&5`KmIx$GtnX}tKqe+jh#p290%9=sy&b(^U;K2n zr_aJXv5?U3GMsbcvK(i$aVYS7%2jKWnev!*O+O$5G-ZlD!>LTxqjXSfE=8P1 znV8*q+*h=nis2;<(m{S`3jh=sO;B@A|5Deo2^h|hOPL(iL=)qT7ziH}TZ0wVgL3?O-DtSd z{6Tq*JiD?5wswoMk{BY_T3E^TDsAndYI|O5#0!8d_1@WK6|#2txwr+UP8+XoQWH~Q zIy>NP0!}@`0DyO$>TwY4MyWkSP8Bg>`>Q@oz7R-=(6?0-cH7TnPzO?b}UHyzH zYTFQ!{&wI7PW^0l?1$=+i-sH$I>@w*AaXhU0SS}iPw^Ix0iJXFXP&?D2X)TIYRskr z4mH8lC0O*LmDh{(*QY$El7JYmwY?X-*D%9$;V7@}#-;f3MaL)Wr;vo9pZt#F5=WSE zYxSt}Ai?#nSee{#&Goj`F_V&{txg+;8tJdf!73}?Xs!_cXl{6eO3b`6Tg84>w9a@iek|x#;Rw(9kSL>XLtOq8-MW{#Ql4PQ5?8wJLNAJ1+H7}Lxwy`?BokwUID!0AUc?az1#p@O0I%i9f=+ErK>pBU4j{&q zaWnindo0z^>0C)>!3&BL6H-Ju;DcLq6&EpE*@kDbE3w=Jj-7nP-WO2Nj7|giGC`rY zbDRHmzQVjlLJc%wN&DUCH{~=4=cLo_U!~#&T$ZgJiE{nni{2k*I0DS~;yahqWJCA^ zPJmi^f+Bl>bxn1JQs%1F4P*eRMk5%2h&(Gsj6GT@+?B|KY-hITBl+9{Qu6iLRx|TXYaJdGR zM?kzCtT#CLeWw42n2mZ)`bRmTU&oqYFw)@1M&e|JR$~z@HB8;WA;T4anEC3iimg;n z)#^Cam-WInN4%Pjx34AGjocR+4Y~EGP`7uAPLsY~XGRQw%xdF%>W^f?2wvc(>f%nP z$!qnIquWmNH8W_ANyhqs8zsHX$&cze`S&zyfLhNl^cS=?n<%-M!~O4}wo^DR%*S}` zJ5_p#sjq#jx5DsR0q_E)KaJ{RxsGX0%QZ90F2T-;06Pr#pa$f5@!eo^`69`v8rnA# zpMB1D*&5p+*rMj;n>tOay$-)M#%zs`LZWpI1aH1xsTZ}#OL@Nb30LlAUoXh#W(Lo5 zU`_@6U9U^q^Qyv}%KEyd%0!(=;xJXyf(>KOl| z=v;^Y`I1Z!fOF#Kd}>#Qp8PUa0M+KT`7f*cmliqozr0glZK4HC5>6^i*|;3o^Fvm= zxgO8`k5>J&LjeJ|kL!T11HI20a6O6y%z~A)tgyn?>B6>S57?|`_o{1l^S#n*HvHCY zPa8GKYkKEQY=bU=+F;?CQ$5V!RW zO2M*c0;hJ6Gr=Ann@61YR^1CeJPmFiM+nJoLA-spI>f9?=bDdp1)iwXax_IE4$&LK zG_)qSssP;VUhFz7?HH!|WF3^3i)E7w+5Uv>=pWlcqSvOSkL~bz{Er)tj^|6Iv2~wd zgjh0@9RH;zZu~XEUKI5lo>y<3cDhB{ZLkUNy$wclVZMh2B=$erk;ZzF(>9M&mz1`L z$PI0lMy=P+%)AD|KW_*eh}8JC$r47NRN&G-?)~wR;%pF4u-yz1N^}6FCOU*2S8q6A zk7He4)IA~N>B-za-xSt*NY2xH{KigItpNiW$Qr6~8D!Qr83SF#b3P_SH4XNEcO|#m zH>Tml)+n}SHs*tgzKuAQg0w|c>K0P^Hgz#L4)SmY>$^IY-nMJg)stFDgUv?bk^P(} z2dxJ!fHiV2hdp?az~?4gckJ-c%i`N~NYdx(TsZktNa{AIW&S*Vz<=#1reVJvS?4_i z>g?Fl24Mjyv!*sby`)x1HWcf=(d42dVc}=r)%1PCJya>wKwx$Oc06xx!FzRY_rp{R z{O;{Zx1BYtE2l561h5T@yzA$AauGx z4sVX18{T7gKe57JA85J8hg14qPT28&w^-AP`l* z9IA7+od>b6-6uU`IN9P&hErXe&V?ot-rBp2A-i=(Gg$*ay9Z-@^AS|ocuXh(9^olU z&Yn=*WpI!+EZ{UT(5kKUQ^n^-whKBjeSdkQX)?=p;nr?HwrOq5ZU2LJU4F%d@|kqq z;^vX2X!N-k$w(j&xDu9AQWf3=LL!3#_*;H1gJ2F&`ie)kDEx72!o66A&fb|_mGr@I5Jpr7$Vg~j9e-q zd47M@{c-Qzq>7f9vFqC{7h5D3wQaslh9prrY0A8q_R&>`o-sTbzjwwvg+w zB}$RHCoFfkAxtx-FuFrb{QN*pPX%%9$&kFy$v&M+?*_G8I{W;Rs_9;h3EKQu7-gzb z;uTurl(sb}J9l;mw=vIIhiJmXjZUni8~3oDoRi}FdU>%}NnByg92fCuf}n~usenzT zp42t$=fb@lCMEG>meo6GMSl-V(H7j+$@h(emSb7`y%Pk~8$k6AyB-HXm6{j)Hpy6a zgt%E0#W9($LLKYw>DJXOaL&hwZ&yy5SYmHPd-+tF$NN^AAZvLtHnsb3g3{tN>#PM# zuQx3QR>wKr`gK}48;t$LE+imyR)ax%=jEEXuUBMV=t~r=kMaj%dk+QZV}6WCRyL<) zJ6k{$5_Aok*UShY2T{k(S-)|$zAt7FEnB?oM&Z30Dei5?m`uEe`H}}`L#@%$33|4U z(CL;<@)6Hjs~sr5oou2*uV6eBVi^lcp%RplSn zeR}bTg|tICOSVJL-ttG zwPicd?1A9zV*YGlSM&D7SI@F^eyVe{_vmx%}X`tH&KKzIPE zOrfCCrqgS4kuBwtmjMTl(>+M0pNVboHM@aYi?Ym`u~Ec1S1mu!7Z5?S$!Cc5_BUXg zc{v|UR2QYLJwNAL>YE@L8PV*Y*=N-{1^S5>%Is_>Gq#ZS$c^P{Wv6fUAcnfncUOfI zXWz($;$4?6#MEr#LM^&@TPEw&43q~9;$%4c5LlRStzz5!&m88S%?)2%9b2=Da-z;C zl_}j29lN?^kaCnxM;(b-VXWF1n)U3hA+LRR564cfVBfB8%S(($cah+qvf1GgCIJvf zhz|ac@{`9IC0m|Os!R4P%VFQ^hwO(~eKptK#!%!?oS$px>TE18V~C24wpwF7$BJu{ zp(6j@Q#yIC%a7)z?pLlIuvE<2pu~AVA>aeXilX;Zf3&v5ECk&xo1`qcoX+#H@2GyN zdC8i8r~KP=b$_p+iD_tbi&?6;?yF5imj*baINFs82}!s)^qN*zN-#kpMV}m7CJ7b^ zlM$ckH14SbKe?2qhO_ZT9L(Ol(WZyvpZ78<*%+-58`cpWC2CG7PR? zW41_+c`0-|O0&`YUGIq0E!QX0fpequs%h0)<|gjtF1-~b4+9U)yShxns#{BU&)cBf zjS{$N{X0n-JDamjjn%Aqw7$>8KjJmCwNX)upmM!lA8xz`GcWa~`yRbQ> zmX>0Dfp2BMRvSt=(OhjYj!Xs#8%?<9U5>9?5?eDd5tK-IL#?)Jr&Ifs+p~9aK}ETB zLL0G}p|npEuN<@Gy?DsRGV-;~K22unYqHSh{>=^VSBuJamjq+MEK@9AYzdW7A57}- zem)0j2hTP~W#W-n%0l|VApxovFPAimv~P0ou^dECXLDbC^J8H0b@3lu(;51KEwy4s zCdik}Yor0e*BN@PgP3XLqjbFca>cJ%d3JVdWfN`~w^co<{Cq#ZX(0A`(US(Z~wY=HgwtM|0Rw-pO zNg(*JyskbkjM~H(vVvw5qH0>+@So!VLtS1&^Lr(+GCig$;}ey;{Nk6q3Y3IgXr3KZ z?inM>(w5VZg++PR=Lb3?vTh1RQYjIr$8{I0I4U%}Uy z?pZBftA$O;%xH_?U8q|abDL`$TT*ZPJrclEF}x848@~b6wcOOIl6KSR8ybL6%8`{b z>#;b8|G+_?)Farjj*y8K{;0CzYZhb7%3mrJYV%~HM`VFyMTUlgKd^Ex?wQi14bEpM zMzNtk*!5ow6nY+k+2hU<2G*L4*O%{1R#Q1))?4g`RxXHrsS=R=?Ej^@lD=|o9jdU{ zMtslJ;zqm5?*SV1FwPSmx*U@7q{PlvB|1W(BP_79uwXWFp8Ul@MHIa>D99Tbk>#8Z zd1~Atr$)9#&OhJ28}VG*R*`-2o$Z>KHTghYKKQwO@Y7W13|{E*KJtrJXVfA+ZhXw2 zrE^JBQV3~yFZt);B6lUT4M%ikw`Ql?xigT%$Tl6>x}d=+EAj&R;d z7XB1gg}Et-H|Fd4*bIcfZ1V~1bVwoNEE4|g$dN>WEs36QjwTI@iE zptHSv0kx+%?izD3Y-T(=_akm#WQx9$G~dua?v9$-sxV7DJs&y+dY0L3JSUsW;P%?W zx4`4^yAQ9P4~osS-h3%~|N3RElxRkUxGaP!r;;^Cjiy%D^Qa9*j6)BYjIVKGLFbr# z1Dzo@wulIae(%mQeFfxLqq<%laBG|Su14;P0ap9zymhHLRc|aieeKiZ%YqIP4kpVe zeEroal;ykUaSAO2-uKVYt!VC9 zwI058at%vWccIC2QQrIXec9Buiv@BQU9c~q|GuJhdM|2Hv07TOR~JPvc9i8q&x%p&Aq*!V^As1BOB7)XCp1fGJoexM0nA5(2DAB%&sFl2Z+C|@3%;sL`0l1< ze;Ha}_Blgguz!mzn8gPj`T@s(wXY&Ia_%)PzWMBVSN$L|9lEW-0Qd^jVNIA~N&G`` z_w9@9n{Pf!^)E5afiJ>z-H}#{z0V>S`ClBWkzOY3`s)rVc-*R zQegFu()$arJH3vY1Z&zf%n zv#M;vsZRX|5h+0Hk4oGT@G*tESzfIbs0`Ao^#HLL2FW}M+rGYgm;HXka+VBs9h=JT zeGv5p8S#zwj$A*sq|00GLwLAd^~=YPNj-D64BoPUk*z|8-`!KGnLv@=3*kD&=J5Gt z4i>tJ6pVC2I;+U{j&y`?tt8ndPw0*LOy>+Um1Np--gBTP!tpU?R2VlLMrJj%b6KNx zrPZzFxC7Xt_5N5rDjql z6@BPdxTf1CmG3=S?^k3pJ9`7|>*t80+#YI>k?pP?4we2v) z;R#LMu1uc?+ZVqv+%29yaUK0ue0iAm66_^J7(L=G*2uXXy!*lQg$nDBVF?~w3vNyL zN{%C8C@>B$Y~eG@uL*g6BZbFOf48`ptkEb@sK($jPuk1`+ZAkcnO8i4jg{@Aa-=#l zdOEIvt#5hbkxilL;@gYY!ms4=1rWxLj+Q5mN&v5E^WBr|jMZfbF!_pgywK2IWwX_3yn&m$OH2fhe%Z!UjzyEqa~DV63X19L@=5zl1B zB~)GzfCeFvtWH^lHx7C`_`N2~<)#?i-7WW~Vfydav2zP^M|G&uK?I3nsaphFBWwcy zHuSqcv4>en1p2ce;8AUvGV@KmUFJ#~^-P@iPgZS_#i~xWva&X4ZI9JhZ3iL12YSZX zb*E?CR~<^Vnp+@Kceiz^LzV`qO8W<+$<<+eeRPbN@neevLSLK6!ZVoYxkK;+P)-~_ zmu)Cj<^y3zQr?A7U*0*7tXUm!0E7K=NK}8!kZhkc?KcKj?aHdw&xT_OTNVocs9|c0B2aXbFOrZC<3Nc6fP5zjk*06Qj6s zStRKv3bZJAPU<KW-I@Mg3TG<#}dehmFtHcx)0P zRm+w{y3C})u3$kOuGv|>(#+t*Fbki}EhJFB*Jd(ZO^+223Oq}HL2VaWYhKO#WAKE`-xAI5!L5!4q2AzfWjO_|s$ zhzNJH^VRHJELu0{yw<(tW}8JK&3(D&4dQLWAL;Q=-b|fsES?Kfo*W({-xs9s6>MG$ zf)+3q4K6=FpZFkd6#jnXT~=RW%NONc(8{v~v(!kxXZL%2r=!WJ&+LAGt@@d5ZltWh5FgZbdMa(l#U;`*11F^i<#g9eOW zv6{ViF0XlX3$+|sHShnpa3HD+sFv_!@bU3L*kd0;o1c{5O-@&I$Woa~qyJW6q0Y~? zk)8oqnJ(POz131y30&x*&iB( zr}i)FjTtiX(PPNvoLXkEE$5#dbQP~vS8DGAxfD)czAOb@j!$YZA0TXVV1-xMw-Y_n zaOvkJiR=t2`lp+1l>%w7gUzRcOa{@y+P!{Bwp(`!|pisHc!fhWXCM>=ce^Mr cM>638BCZ^}QRhDPI5qaFtLP|~Dq05pAJbN*(f|Me literal 53642 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

U16ZMRTNPPtvJ72d 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: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

!AE>DLycq98vu0o4p2 znTXY9##V}A#H2~BON_fPVk7w7!Ky_YV{J@zedvW&!rGj==Mx!xO=g{v*os-Y`JZh& z*FHhN$7g9s+o{N@kUnOP%!6f6(!@xvEYhLMcjUAH<#Lc_nYfYgDYCBpI&mC}iwcKT zS-p2y?w&7lFii^uH^6H`L|W@_&Fu)vD)C}1#dB&$Pjjb?nOufdy&oTJ%kwt*?J=fs zM3jF+_n;1P{#<(uzT>5#P2U4yqMiP>K$@1H|Dqvr-qjNM?dm)cc3%Y++EFiaTO)V= zr&n-}sR}}HR!^E}rkql$B^8Zbv@}HYj+M@_Tp{Ti|_RVlNm`xNlfeMW=p1pAlF- zdRVz+gB) zUvzG#r+Ju}yD(;K`rbZg@r6D(se& zR}iHgdv!DyyNDEF)1N)1(FTauNmhyc1QT&ChIVu`d_7 zr9`#q^Kdq>4&P=j@nq#KGz%vFwIC=KQYYo;rVU#U2dxH_ypKDA^s4-(4EqxK6NJ(N zw)N<{E?u&8wh>g*JAr7asa0>7r=w`EWfa5}^}=*Cf@cSnJ+f-0OFOJ5hE`wS`c5&q zFIVrq`OzOo)6|!A)BbGO6YBC&Q*nFKDpH<$G27+B8siixBi9D;D({>QYKrw$cC0!) zYZN2jNZP?{DEt;2HRK$pZ8d!ANMCzT9#2G9@Fo-~r&GX7A5xW=_0XTcrzCd8sX5@J zn22MwLJ+%U_51U5$C<$EOCQ5;nPh|uy@~6X=KtApHuQ>dZ24|%Zz4_Zd52qMWMS+s zU5Hk+pP|#G*V^70V*7p)UR_^q*mF#C(0M6l?0cv80fVL6?DJU(I53=N2#_c2#B=gkfABM?56WY^PM(SZvj`<{4B41Czh1 zY$1=KcsP!~+O1Q=tk+gqU%KR~ur9!$s=2A;Ut8HlOW%~+-}zZPhq|XCQlTntt>7WB zjO7rrA^K*NM~rpVcX8>*M@lbMS35RnMUI%i$~V}rEF<*vi2r=$0>6m+q$o+w_r>Bh z8j6raT6uplboqWQXGd2ILb!$<$No2$+QjJ83B1)U&97N%JD(G_+3TLrdt^}%I$`0k zlxZpJxUTa7XJdCPwOSAZBG%7JqWveR<>eW#LTx>idUs-8m`YDO^yNbbQlr1D*r&a$ z>@a(JBx-*7MNM$MDpfaPon6nD#Wx0hsjiqj&K$ntdBYfIrhL`-#M#>9q>5R=k9l(pN*-R?w(jKNB;r!_QHk$