Skip to content

Commit b711517

Browse files
committed
Added disassembler/assembler support
1 parent 3504cfa commit b711517

7 files changed

Lines changed: 255 additions & 2 deletions

File tree

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
This project is born with the aim to develop a lightweight, but useful tool. The reason is that the existing hex editors have some different limitations (e.g. too many dependencies, missing hex coloring features, etc.).
44

55
![screenshot](screenshot.png)
6-
![screenshot2](screenshot2.png)
6+
![screenshot2](screenshot2.png)
7+
![screenshot3](screenshot3.png)
78

8-
This project is based on **qhexedit2**. New features should be added in the future, PRs are welcomed.
9+
This project is based on **qhexedit2**, **capstone** and **keystone** engines. New features could be added in the future, PRs are welcomed.
910

1011
## Features
1112

@@ -30,6 +31,8 @@ This project is based on **qhexedit2**. New features should be added in the futu
3031
* Hex - Dec number converter [`F2`]
3132
* Hex String escaper (e.g from 010203 to \x01\x02\x03) [`F3`]
3233
* Pattern Matching Engine (see later for details)
34+
* Disassebler based on Capstone Engine [`F4`]
35+
* Assembler based on Keystone Engine [`F4`]
3336
* Shortcuts for all these features
3437

3538
## Pattern Matching Engine

fhex.pro

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
2727
RC_ICONS = icon.ico
2828

2929
SOURCES += \
30+
src/fasm.cpp \
3031
src/chunks.cpp \
3132
src/commands.cpp \
3233
src/core/patternmatching.cpp \
@@ -36,10 +37,13 @@ SOURCES += \
3637
src/qhexedit.cpp
3738

3839
HEADERS += \
40+
src/fasm.h \
3941
src/chunks.h \
4042
src/commands.h \
4143
src/core/json.h \
4244
src/core/patternmatching.h \
4345
src/fhex.h \
4446
src/core/hexeditor.h \
4547
src/qhexedit.h
48+
49+
QMAKE_LFLAGS += -lkeystone -lcapstone

screenshot3.png

348 KB
Loading

src/fasm.cpp

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#include "fasm.h"
2+
3+
Fasm::Fasm(QString bytes) : QMainWindow(nullptr)
4+
{
5+
this->setWindowTitle("Fasm");
6+
this->setWindowIcon(QIcon("/usr/share/icons/fhex.png"));
7+
this->setMinimumSize(800, 500);
8+
textAsm = new QTextEdit(this);
9+
textOpcodes = new QTextEdit(this);
10+
textAsm->setStyleSheet(TEXTEDIT_STYLE);
11+
textOpcodes->setStyleSheet(TEXTEDIT_STYLE);
12+
this->textOpcodes->setPlainText(bytes);
13+
statusbar = new QLabel;
14+
QLabel *lblArch = new QLabel("Arch:");
15+
QLabel *lblMode = new QLabel("Mode:");
16+
QLabel *lblAsm = new QLabel("Assembly:");
17+
QLabel *lblOpcodes = new QLabel("Op codes:");
18+
cmbArch = new QComboBox;
19+
cmbArch->addItem("X86");
20+
cmbArch->addItem("ARM");
21+
cmbArch->addItem("ARM64");
22+
cmbArch->addItem("MIPS");
23+
cmbMode = new QComboBox;
24+
cmbMode->addItem("32 bit");
25+
cmbMode->addItem("64 bit");
26+
cmbMode->addItem("ARM");
27+
cmbMode->addItem("THUMB");
28+
cmbMode->addItem("MIPS32");
29+
cmbMode->addItem("MIPS64");
30+
QPushButton *setDisasm = new QPushButton("Disassemble");
31+
connect(setDisasm, &QPushButton::clicked, this, &Fasm::on_btnSetDisasm_click);
32+
QPushButton *setAsm = new QPushButton("Assemble");
33+
connect(setAsm, &QPushButton::clicked, this, &Fasm::on_btnSetAsm_click);
34+
QHBoxLayout *hbox = new QHBoxLayout;
35+
QHBoxLayout *buttons = new QHBoxLayout;
36+
QGridLayout *gridLayout = new QGridLayout;
37+
hbox->addWidget(lblArch, 0, Qt::AlignRight);
38+
hbox->addWidget(cmbArch, 0, Qt::AlignLeft);
39+
hbox->addWidget(lblMode, 0, Qt::AlignRight);
40+
hbox->addWidget(cmbMode, 0, Qt::AlignLeft);
41+
buttons->addWidget(setDisasm, 0, Qt::AlignRight);
42+
buttons->addWidget(setAsm, 0, Qt::AlignLeft);
43+
gridLayout->addLayout(hbox, 0, 0);
44+
gridLayout->addLayout(buttons, 0, 1);
45+
gridLayout->addWidget(lblAsm, 1, 0);
46+
gridLayout->addWidget(lblOpcodes, 1, 1);
47+
gridLayout->addWidget(textAsm, 2, 0);
48+
gridLayout->addWidget(textOpcodes, 2, 1);
49+
gridLayout->addWidget(statusbar, 3, 0, 1, 2);
50+
QWidget *mainWidget = new QWidget();
51+
mainWidget->setLayout(gridLayout);
52+
this->setCentralWidget(mainWidget);
53+
ks = nullptr;
54+
loadKeystone();
55+
loadCapstone();
56+
}
57+
58+
void Fasm::loadCapstone() {
59+
updateArchMode();
60+
cs_opt_skipdata skipdata = {
61+
.mnemonic = "db",
62+
};
63+
cs_err err = cs_open(carch, cmode, &cs);
64+
if (err != CS_ERR_OK){
65+
this->statusbar->setText("Error: Failed capstone initialization");
66+
} else if (this->textOpcodes->toPlainText() != ""){
67+
size_t count;
68+
cs_insn *insn;
69+
cs_option(cs, CS_OPT_DETAIL, CS_OPT_ON);
70+
cs_option(cs, CS_OPT_SKIPDATA, CS_OPT_ON);
71+
cs_option(cs, CS_OPT_SKIPDATA_SETUP, (size_t)&skipdata);
72+
QByteArray bytes = QByteArray::fromHex(this->textOpcodes->toPlainText().toUtf8());
73+
char *t = bytes.data();
74+
count = cs_disasm(cs, reinterpret_cast<uint8_t*>(QByteArray::fromHex(this->textOpcodes->toPlainText().toUtf8()).data()), bytes.size(), 0, 0, &insn);
75+
if (count > 0) {
76+
this->statusbar->setText("Disassembled " + QString::number(count) + " instructions");
77+
this->textAsm->clear();
78+
size_t j;
79+
for (j = 0; j < count; j++) {
80+
this->textAsm->setPlainText(this->textAsm->toPlainText() + insn[j].mnemonic + " " + insn[j].op_str + ";\r\n");
81+
}
82+
cs_free(insn, count);
83+
} else {
84+
this->statusbar->setText("Error: Failed to disassemble given op codes\n");
85+
}
86+
cs_close(&cs);
87+
}
88+
}
89+
90+
void Fasm::updateArchMode() {
91+
QString txtArch = cmbArch->currentText();
92+
QString txtMode = cmbMode->currentText();
93+
94+
if (txtArch == "X86") {
95+
karch = KS_ARCH_X86;
96+
carch = CS_ARCH_X86;
97+
} else if (txtArch == "ARM") {
98+
karch = KS_ARCH_ARM;
99+
carch = CS_ARCH_ARM;
100+
} else if (txtArch == "ARM64") {
101+
karch = KS_ARCH_ARM64;
102+
carch = CS_ARCH_ARM64;
103+
} else if (txtArch == "MIPS") {
104+
karch = KS_ARCH_MIPS;
105+
carch = CS_ARCH_MIPS;
106+
}
107+
108+
if (txtMode == "32 bit") {
109+
kmode = KS_MODE_32;
110+
cmode = CS_MODE_32;
111+
} else if (txtMode == "64 bit") {
112+
kmode = KS_MODE_64;
113+
cmode = CS_MODE_64;
114+
} else if (txtMode == "ARM") {
115+
kmode = KS_MODE_ARM;
116+
cmode = CS_MODE_ARM;
117+
} else if (txtMode == "THUMB") {
118+
kmode = KS_MODE_THUMB;
119+
cmode = CS_MODE_THUMB;
120+
} else if (txtMode == "MIPS32") {
121+
kmode = KS_MODE_MIPS32;
122+
cmode = CS_MODE_MIPS32;
123+
} else if (txtMode == "MIPS64") {
124+
kmode = KS_MODE_MIPS64;
125+
cmode = CS_MODE_MIPS64;
126+
}
127+
}
128+
129+
void Fasm::loadKeystone() {
130+
if (ks != nullptr) {
131+
ks_close(ks);
132+
ks = nullptr;
133+
}
134+
updateArchMode();
135+
ks_err err;
136+
err = ks_open(karch, kmode, &ks);
137+
if (err != KS_ERR_OK) {
138+
statusbar->setText("Error: Failed keystone initialization");
139+
} else if (this->textAsm->toPlainText() != "") {
140+
this->textOpcodes->clear();
141+
QString assembly = this->textAsm->toPlainText();
142+
for (uint8_t b : asmToOpcodes(assembly)) {
143+
this->textOpcodes->setPlainText(this->textOpcodes->toPlainText() + QString::number(b, 16).rightJustified(2,'0'));
144+
}
145+
}
146+
}
147+
148+
vector<uint8_t> Fasm::asmToOpcodes(QString &assembly) {
149+
vector<uint8_t> bytes;
150+
unsigned char *encode;
151+
size_t count;
152+
size_t size;
153+
if (ks_asm(ks, assembly.toStdString().c_str(), 0, &encode, &size, &count) != KS_ERR_OK) {
154+
this->statusbar->setText("Keystone error " + QString::number(ks_errno(ks)));
155+
} else {
156+
size_t i;
157+
for (i = 0; i < size; i++) {
158+
bytes.push_back(encode[i]);
159+
}
160+
this->statusbar->setText("Compiled: " + QString::number(size) + " bytes | Statements: " + QString::number(count) + " | Bytes: " + QString::number(bytes.size()));
161+
}
162+
163+
ks_free(encode);
164+
return bytes;
165+
}
166+
167+
Fasm::~Fasm() {
168+
ks_close(ks);
169+
}
170+
171+
void Fasm::on_btnSetAsm_click() {
172+
this->loadKeystone();
173+
}
174+
175+
void Fasm::on_btnSetDisasm_click() {
176+
this->loadCapstone();
177+
}

src/fasm.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#ifndef FASM_H
2+
#define FASM_H
3+
4+
#include <QMainWindow>
5+
#include <QWidget>
6+
#include <QGridLayout>
7+
#include <QTextEdit>
8+
#include <QDebug>
9+
#include <keystone/keystone.h>
10+
#include <capstone/capstone.h>
11+
#include <QLabel>
12+
#include <vector>
13+
#include <QComboBox>
14+
#include <QPushButton>
15+
#include <QByteArray>
16+
17+
#define TEXTEDIT_STYLE "QTextEdit { background-color: #17120f; color: #ebe5e1; font-size: 16px; font-family: monospace; }"
18+
19+
using namespace std;
20+
21+
class Fasm : public QMainWindow
22+
{
23+
Q_OBJECT
24+
private:
25+
QComboBox *cmbArch;
26+
QComboBox *cmbMode;
27+
QLabel *statusbar;
28+
ks_engine *ks;
29+
csh cs;
30+
QTextEdit *textAsm;
31+
QTextEdit *textOpcodes;
32+
ks_arch karch;
33+
ks_mode kmode;
34+
cs_arch carch;
35+
cs_mode cmode;
36+
vector<uint8_t> asmToOpcodes(QString &assembly);
37+
void loadKeystone();
38+
void loadCapstone();
39+
void updateArchMode();
40+
public:
41+
Fasm(QString bytes);
42+
~Fasm();
43+
44+
public slots:
45+
void on_btnSetDisasm_click();
46+
void on_btnSetAsm_click();
47+
};
48+
49+
#endif // FASM_H

src/fhex.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,13 @@ Fhex::Fhex(QWidget *parent, QApplication *app, QString filepath)
7777
QAction *escapeHex = new QAction("&Escape Hex Bytes", this);
7878
escapeHex->setShortcut(QKeySequence(Qt::Key_F3));
7979
tools->addAction(escapeHex);
80+
QAction *fasm = new QAction(QIcon::fromTheme("map-flat"), "&Assembler/Disassembler", this);
81+
fasm->setShortcut(QKeySequence(Qt::Key_F4));
82+
tools->addAction(fasm);
8083

8184
connect(hexDec, &QAction::triggered, this, &Fhex::on_menu_hex_dec_converter_click);
8285
connect(escapeHex, &QAction::triggered, this, &Fhex::on_menu_escape_hex_click);
86+
connect(fasm, &QAction::triggered, this, &Fhex::on_menu_fasm_click);
8387

8488
/** End Menu Initialization **/
8589

@@ -211,6 +215,7 @@ Fhex::Fhex(QWidget *parent, QApplication *app, QString filepath)
211215

212216
this->statusBar.setText("Fhex loaded");
213217
this->setCentralWidget(mainWidget);
218+
this->fasm = nullptr;
214219

215220
//If a filepath was passed as argument, open it
216221
if (filepath != "") {
@@ -223,6 +228,18 @@ Fhex::Fhex(QWidget *parent, QApplication *app, QString filepath)
223228
Fhex::~Fhex()
224229
{
225230
delete this->hexEditor;
231+
if (this->fasm != nullptr) {
232+
delete this->fasm;
233+
}
234+
}
235+
236+
void Fhex::on_menu_fasm_click() {
237+
if (fasm != nullptr) {
238+
delete fasm;
239+
this->fasm = nullptr;
240+
}
241+
fasm = new Fasm(this->qhex->selectedData());
242+
fasm->show();
226243
}
227244

228245
void Fhex::on_menu_offset_list_click() {

src/fhex.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
#include "qhexedit.h"
5151
#include "core/hexeditor.h"
52+
#include "fasm.h"
5253

5354
#define MAX_DIFF_BYTES 3000
5455
#define DEFAULT_UNPRINTABLE_CHAR "."
@@ -70,6 +71,7 @@ class Fhex : public QMainWindow
7071
~Fhex();
7172

7273
private:
74+
Fasm *fasm;
7375
qint64 lastCursorPos = 0;
7476
qint64 currentCursorPos = 0;
7577
QChartView *binChartView;
@@ -140,6 +142,7 @@ public slots:
140142
void on_menu_escape_hex_click();
141143
void on_binchart_click(const QPointF &p);
142144
void on_menu_binchart_click();
145+
void on_menu_fasm_click();
143146

144147
};
145148

0 commit comments

Comments
 (0)