yolo
This commit is contained in:
parent
f91da7a47c
commit
d8b52dcb44
12 changed files with 278 additions and 119 deletions
|
@ -34,11 +34,13 @@ add_executable(littlesnitch
|
|||
mainwindow.cpp
|
||||
networkthread.cpp
|
||||
session.cpp
|
||||
mainwindow.ui
|
||||
httpflow.cpp
|
||||
mainwindow.h
|
||||
httpflow.h
|
||||
networkthread.h
|
||||
session.h
|
||||
includes.h
|
||||
mainwindow.ui
|
||||
)
|
||||
|
||||
target_link_libraries(littlesnitch PRIVATE Qt5::Widgets cppzmq sqlite3)
|
||||
|
|
2
httpflow.cpp
Normal file
2
httpflow.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
#include "httpflow.h"
|
||||
|
153
httpflow.h
Normal file
153
httpflow.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
#pragma once
|
||||
|
||||
#include <includes.h>
|
||||
|
||||
/*
|
||||
|
||||
{
|
||||
"flow": {
|
||||
"client_conn": {
|
||||
"address": [
|
||||
"::1",
|
||||
37570,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"alpn_proto_negotiated": "http/1.1",
|
||||
"cipher_name": "TLS_AES_256_GCM_SHA384",
|
||||
"clientcert": null,
|
||||
"id": "a1e82917-2d58-4b99-be9e-2b962bc499b2",
|
||||
"mitmcert": "mitmcertstring",
|
||||
"sni": "yolo.jetzt",
|
||||
"timestamp_end": null,
|
||||
"timestamp_start": 1597284668.6260498,
|
||||
"timestamp_tls_setup": 1597284669.8449724,
|
||||
"tls_established": true,
|
||||
"tls_extensions": [...],
|
||||
"tls_version": "TLSv1.3"
|
||||
},
|
||||
"error": null,
|
||||
"id": "a6aa4e6e-ca31-4f58-bf47-2da7bfcf0000",
|
||||
"intercepted": false,
|
||||
"marked": false,
|
||||
"metadata": {},
|
||||
"mode": "transparent",
|
||||
"request": {
|
||||
"content": "",
|
||||
"first_line_format": "relative",
|
||||
"headers": [
|
||||
[
|
||||
"Host",
|
||||
"yolo.jetzt"
|
||||
],
|
||||
[
|
||||
"User-Agent",
|
||||
"curl/7.68.0"
|
||||
],
|
||||
[
|
||||
"Accept",
|
||||
]
|
||||
],
|
||||
"host": "yolo.jetzt",
|
||||
"http_version": "HTTP/1.1",
|
||||
"is_replay": false,
|
||||
"method": "GET",
|
||||
"path": "/",
|
||||
"port": 443,
|
||||
"scheme": "https",
|
||||
"timestamp_end": 1597284669.92817,
|
||||
"timestamp_start": 1597284669.8761458
|
||||
},
|
||||
"response": null,
|
||||
"server_conn": {
|
||||
"address": [
|
||||
"yolo.jetzt",
|
||||
443
|
||||
],
|
||||
"alpn_proto_negotiated": "http/1.1",
|
||||
"cert": "certstring",
|
||||
"id": "50a3b79d-2912-45f3-991b-c03406a1018f",
|
||||
"ip_address": [
|
||||
"95.156.226.69",
|
||||
443
|
||||
],
|
||||
"sni": "yolo.jetzt",
|
||||
"source_address": [
|
||||
"192.168.42.102",
|
||||
44949
|
||||
],
|
||||
"timestamp_end": null,
|
||||
"timestamp_start": 1597284669.2133315,
|
||||
"timestamp_tcp_setup": 1597284669.2892282,
|
||||
"timestamp_tls_setup": 1597284669.584602,
|
||||
"tls_established": true,
|
||||
"tls_version": "TLSv1.2",
|
||||
"via": null
|
||||
},
|
||||
"type": "http",
|
||||
"version": 7
|
||||
},
|
||||
"msg": "request"
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
namespace http {
|
||||
|
||||
struct Request
|
||||
{
|
||||
std::string server_conn_id;
|
||||
std::string server_ip_address;
|
||||
|
||||
bool tls;
|
||||
std::string content;
|
||||
std::string scheme;
|
||||
std::string method;
|
||||
std::string host;
|
||||
std::string address;
|
||||
unsigned short port;
|
||||
std::string http_version;
|
||||
std::string path;
|
||||
double timestamp_start;
|
||||
double timestamp_end;
|
||||
std::vector<std::tuple<std::string,std::string>> headers;
|
||||
|
||||
std::string error;
|
||||
|
||||
};
|
||||
|
||||
struct Response {};
|
||||
|
||||
struct Flow {
|
||||
Request request;
|
||||
Response response;
|
||||
};
|
||||
|
||||
inline void to_json(json& j, const Flow& flow) {}
|
||||
|
||||
inline void from_json(const json& j, Flow& flow) {
|
||||
std::cout << std::setw(4) << j << "\n\n";
|
||||
if(!j.contains("flow")) {
|
||||
return;
|
||||
}
|
||||
if(j.at("flow").contains("server_conn")) {
|
||||
j.at("flow").at("server_conn").at("id").get_to(flow.request.server_conn_id);
|
||||
j.at("flow").at("server_conn").at("tls_established").get_to(flow.request.tls);
|
||||
}
|
||||
if(j.at("flow").contains("request")) {
|
||||
j.at("flow").at("request").at("port").get_to(flow.request.port);
|
||||
j.at("flow").at("request").at("host").get_to(flow.request.host);
|
||||
j.at("flow").at("request").at("scheme").get_to(flow.request.scheme);
|
||||
j.at("flow").at("request").at("path").get_to(flow.request.path);
|
||||
j.at("flow").at("request").at("content").get_to(flow.request.content);
|
||||
j.at("flow").at("request").at("method").get_to(flow.request.method);
|
||||
j.at("flow").at("request").at("http_version").get_to(flow.request.http_version);
|
||||
|
||||
//j.at("server_conn").at("address").get_to(flow.server_conn_address);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(http::Flow)
|
|
@ -5,5 +5,6 @@
|
|||
#include <sqlite3.h>
|
||||
#include <filesystem>
|
||||
#include <QDebug>
|
||||
#include <iostream>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
|
|
@ -8,6 +8,7 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
: QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
int metatype_id = qRegisterMetaType<http::Flow>("httpflow");
|
||||
current_session = new Session();
|
||||
current_session->load("/tmp/littlesnitch.session");
|
||||
|
||||
|
@ -16,8 +17,8 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
worker->moveToThread(thread);
|
||||
connect(thread, SIGNAL (started()), worker, SLOT (process()));
|
||||
//connect(worker, SIGNAL (error(QString)), this, SLOT (errorString(QString)));
|
||||
connect(worker, SIGNAL (httpMessage(json)), current_session, SLOT (saveRequest(json)));
|
||||
connect(worker, SIGNAL (httpMessage(json)), this, SLOT (updateHistory(json)));
|
||||
connect(worker, SIGNAL (httpMessage(http::Flow)), current_session, SLOT (saveRequest(http::Flow)), Qt::QueuedConnection);
|
||||
connect(worker, SIGNAL (httpMessage(http::Flow)), this, SLOT (updateHistory(http::Flow)), Qt::QueuedConnection);
|
||||
thread->start();
|
||||
|
||||
ui->setupUi(this);
|
||||
|
@ -40,10 +41,10 @@ MainWindow::~MainWindow()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::updateHistory(json data) {
|
||||
ui->historyHTTPTable->setItem(0,0,new QTableWidgetItem(QString::fromStdString(data["id"])));
|
||||
// ui->historyHTTPTable->setItem(0,1,new QTableWidgetItem(QString::fromStdString(data.method)));
|
||||
// ui->historyHTTPTable->setItem(0,2,new QTableWidgetItem(QString::fromStdString(data.url)));
|
||||
// ui->historyHTTPTable->setItem(0,3,new QTableWidgetItem(QString::number(data.ttl)));
|
||||
void MainWindow::updateHistory(http::Flow flow) {
|
||||
ui->historyHTTPTable->setItem(0,0,new QTableWidgetItem(QString::fromStdString(flow.request.server_conn_id)));
|
||||
ui->historyHTTPTable->setItem(0,1,new QTableWidgetItem(QString::fromStdString(flow.request.method)));
|
||||
ui->historyHTTPTable->setItem(0,2,new QTableWidgetItem(QString::fromStdString(flow.request.host)));
|
||||
//ui->historyHTTPTable->setItem(0,3,new QTableWidgetItem(QString::number(data.ttl)));
|
||||
ui->historyHTTPTable->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <string>
|
||||
#include <session.h>
|
||||
#include <includes.h>
|
||||
#include <httpflow.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class MainWindow; }
|
||||
|
@ -21,5 +22,5 @@ public:
|
|||
MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
public slots:
|
||||
void updateHistory(json data);
|
||||
void updateHistory(http::Flow flow);
|
||||
};
|
||||
|
|
|
@ -8,18 +8,6 @@ import zmq
|
|||
import json
|
||||
from enum import Enum
|
||||
|
||||
NO_MSG = {"msg": None}
|
||||
INIT_MSG = {"msg": "init"}
|
||||
ACK_MSG = {"msg": "ack"}
|
||||
PING_MSG = {"msg": "ping"}
|
||||
PONG_MSG = {"msg": "pong"}
|
||||
|
||||
class NetworkState(Enum):
|
||||
DISCONNECTED = auto()
|
||||
CONNECTED = auto()
|
||||
PING = auto()
|
||||
SENDING = auto()
|
||||
|
||||
def convert_to_strings(obj):
|
||||
if isinstance(obj, dict):
|
||||
return {convert_to_strings(key): convert_to_strings(value)
|
||||
|
@ -38,87 +26,57 @@ class NetworkThread(threading.Thread):
|
|||
self.context = zmq.Context()
|
||||
|
||||
def run(self):
|
||||
print("thread started")
|
||||
self.connect()
|
||||
msg = self.send_msg_and_expect()
|
||||
while True:
|
||||
timer = time.monotonic()
|
||||
a = None
|
||||
if not a:
|
||||
try:
|
||||
a = self.q.get(block=False)
|
||||
except Empty:
|
||||
pass
|
||||
if a:
|
||||
self.send_msg_and_ack(a)
|
||||
timer = time.monotonic()
|
||||
if timer - time.monotonic() < -5:
|
||||
self.send_msg_and_ack({"msg": "ping"})
|
||||
|
||||
def disconnect(self):
|
||||
self.socket.setsockopt(zmq.LINGER,0)
|
||||
self.socket.close()
|
||||
print("disconnected")
|
||||
|
||||
def reconnect(self):
|
||||
self.disconnect()
|
||||
time.sleep(1)
|
||||
self.connect()
|
||||
|
||||
def connect(self):
|
||||
self.socket = self.context.socket(zmq.PAIR)
|
||||
self.socket.connect("tcp://127.0.0.1:12345")
|
||||
self.send_msg_and_ack({"msg": "ping"})
|
||||
print("successfully connected")
|
||||
|
||||
def send_msg_and_expect(this, msg, expect, timeout=5, retries=3):
|
||||
while retries:
|
||||
def send_msg_and_ack(self, msg):
|
||||
while True:
|
||||
a = convert_to_strings(msg)
|
||||
self.socket.send(str.encode(json.dumps(a)))
|
||||
if (client.poll(REQUEST_TIMEOUT) & zmq.POLLIN) != 0:
|
||||
if (self.socket.poll(5) & zmq.POLLIN) != 0:
|
||||
msg = self.socket.recv()
|
||||
try:
|
||||
if msg:
|
||||
result = json.loads(msg)
|
||||
if result["msg"] in expect:
|
||||
if result["msg"] == "ack":
|
||||
return result
|
||||
else:
|
||||
print("got unexpected message {result}")
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print(f"malformed message received {msg}")
|
||||
retries -= 1
|
||||
print("no ack received, reconnecting...")
|
||||
self.reconnect()
|
||||
return NO_MSG
|
||||
|
||||
"""
|
||||
def networking(q):
|
||||
print("starting thread")
|
||||
|
||||
state = NetworkState.DISCONNECTED
|
||||
a = None
|
||||
while state == NetworkState.DISCONNECTED:
|
||||
socket = context.socket(zmq.PAIR)
|
||||
socket.connect("tcp://127.0.0.1:12345")
|
||||
msg = get_msg(socket)
|
||||
if msg["msg"] == "init":
|
||||
send_msg(ACK_MSG, socket)
|
||||
state = NetworkState.CONNECTED
|
||||
|
||||
|
||||
|
||||
|
||||
timer = time.monotonic()
|
||||
while state != NetworkState.DISCONNECTED:
|
||||
if state == NetworkState.CONNECTED and timer - time.monotonic() >= 5:
|
||||
timer = time.monotonic()
|
||||
send_msg(PING_MSG,socket)
|
||||
msg = get_msg(socket)
|
||||
if msg["msg"] != "pong":
|
||||
state = NetworkState.
|
||||
|
||||
msg = get_msg(socket)
|
||||
if msg['msg'] == "ping":
|
||||
send_msg(PONG_MSG, socket)
|
||||
timer = time.monotonic()
|
||||
|
||||
if not a:
|
||||
try:
|
||||
a = q.get(block=False)
|
||||
except Empty:
|
||||
pass
|
||||
if a:
|
||||
send_msg(a, socket)
|
||||
msg = get_msg(socket)
|
||||
if msg["msg"] == "ack":
|
||||
timer = time.monotonic()
|
||||
a = None
|
||||
self.q.task_done()
|
||||
else:
|
||||
connected = False
|
||||
"""
|
||||
class LittleSnitchBridge:
|
||||
def __init__(self):
|
||||
self.q = Queue()
|
||||
|
@ -127,11 +85,9 @@ class LittleSnitchBridge:
|
|||
|
||||
def request(self, flow):
|
||||
self.q.put({'msg': 'request', 'flow': flow.get_state()})
|
||||
self.q.join()
|
||||
|
||||
def response(self, flow):
|
||||
self.q.put({'msg': 'response', 'flow': flow.get_state()})
|
||||
self.q.join()
|
||||
|
||||
addons = [
|
||||
LittleSnitchBridge()
|
||||
|
|
|
@ -1,41 +1,71 @@
|
|||
#include "networkthread.h"
|
||||
#include <iostream>
|
||||
|
||||
NetworkThread::NetworkThread(QObject *parent) : QObject(parent)
|
||||
NetworkThread::NetworkThread(QObject *parent) :
|
||||
QObject(parent),
|
||||
context(1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetworkThread::process() {
|
||||
zmq::context_t ctx(1);
|
||||
zmq::socket_t sock(ctx, zmq::socket_type::pair);
|
||||
|
||||
try {
|
||||
sock.bind("tcp://127.0.0.1:12345");
|
||||
} catch (zmq::error_t err) {
|
||||
qDebug() << "failed binding socket" << err.what();
|
||||
emit error(err.what());
|
||||
return;
|
||||
};
|
||||
|
||||
while(true){
|
||||
bool connected = false;
|
||||
while(!connected) {
|
||||
sock.send(zmq::buffer(INIT_MSG_STR.c_str(), INIT_MSG_STR.length()), zmq::send_flags::dontwait);
|
||||
zmq::message_t msg;
|
||||
const auto ret = sock.recv(msg, zmq::recv_flags::dontwait);
|
||||
if(ret) {
|
||||
if(msg.to_string() == ACK_MSG_STR) {
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(connected) {
|
||||
zmq::message_t msg;
|
||||
const auto ret = sock.recv(msg, zmq::recv_flags::dontwait);
|
||||
if(ret) {
|
||||
qDebug() << msg.to_string().c_str();
|
||||
emit httpMessage(json(msg.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
void NetworkThread::connect() {
|
||||
socket = new zmq::socket_t(context, zmq::socket_type::pair);
|
||||
int linger = 0;
|
||||
socket->setsockopt(ZMQ_LINGER, &linger, sizeof (linger));
|
||||
try {
|
||||
socket->bind("tcp://127.0.0.1:12345");
|
||||
} catch (zmq::error_t err) {
|
||||
qDebug() << "failed binding socket" << err.what();
|
||||
emit error(err.what());
|
||||
throw;
|
||||
};
|
||||
qDebug() << "connected";
|
||||
}
|
||||
|
||||
void NetworkThread::disconnect() {
|
||||
delete socket;
|
||||
qDebug() << "disconnected";
|
||||
}
|
||||
|
||||
void NetworkThread::reconnect() {
|
||||
this->disconnect();
|
||||
this->connect();
|
||||
}
|
||||
|
||||
void NetworkThread::process() {
|
||||
connect();
|
||||
while(true) {
|
||||
zmq::message_t response;
|
||||
const auto ret = socket->recv(response, zmq::recv_flags::dontwait);
|
||||
if(ret) {
|
||||
auto j = json::parse(response.to_string());
|
||||
//std::cout << std::setw(4) << j << "\n\n";
|
||||
if(j.contains("msg") && j["msg"].is_string()) {
|
||||
std::string msg_type;
|
||||
try {
|
||||
j["msg"].get_to(msg_type);
|
||||
} catch (nlohmann::detail::type_error& err) {
|
||||
qDebug() << "json type error, message type not a string: " << response.to_string().c_str();
|
||||
emit error("no message type");
|
||||
}
|
||||
if(msg_type == "request") {
|
||||
} else if (msg_type == "response") {
|
||||
try {
|
||||
emit httpMessage(j);
|
||||
} catch (nlohmann::detail::type_error& err) {
|
||||
qDebug() << "error reading HTTP Flow";
|
||||
emit error("error converting to flow");
|
||||
}
|
||||
} else if(msg_type == "ping") {
|
||||
} else {
|
||||
qDebug() << "unknown or broken message type received: " << msg_type.c_str();
|
||||
emit error("unknown message");
|
||||
}
|
||||
std::string m = "{\"msg\": \"ack\"}";
|
||||
socket->send(zmq::buffer(m.c_str(), m.length()), zmq::send_flags::dontwait);
|
||||
} else {
|
||||
qDebug() << "broken message, but correct json: " << response.to_string().c_str();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <includes.h>
|
||||
|
||||
const std::string INIT_MSG_STR = R"({"msg": "init"})";
|
||||
const std::string ACK_MSG_STR = R"({"msg": "ack"})";
|
||||
#include <httpflow.h>
|
||||
|
||||
class NetworkThread : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
// add your variables here
|
||||
zmq::context_t context;
|
||||
zmq::socket_t *socket;
|
||||
void connect();
|
||||
void disconnect();
|
||||
void reconnect();
|
||||
public:
|
||||
explicit NetworkThread(QObject *parent = nullptr);
|
||||
public slots:
|
||||
|
@ -18,5 +20,5 @@ public slots:
|
|||
signals:
|
||||
void finished();
|
||||
void error(QString err);
|
||||
void httpMessage(json data);
|
||||
void httpMessage(http::Flow flow);
|
||||
};
|
||||
|
|
|
@ -32,6 +32,6 @@ void Session::unload() {
|
|||
}
|
||||
}
|
||||
|
||||
void Session::saveRequest(json data) {
|
||||
void Session::saveRequest(http::Flow flow) {
|
||||
// todo
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <includes.h>
|
||||
#include <httpflow.h>
|
||||
|
||||
// static int callback(void *NotUsed, int argc, char **argv, char **azColName){
|
||||
// int i;
|
||||
|
@ -51,7 +52,7 @@ public:
|
|||
void unload();
|
||||
bool isLoaded();
|
||||
public slots:
|
||||
void saveRequest(json data);
|
||||
void saveRequest(http::Flow flow);
|
||||
signals:
|
||||
|
||||
};
|
||||
|
|
12
test.sh
12
test.sh
|
@ -4,5 +4,15 @@ mitmdump -k -p 8080 -s mitmaddon/littlesnitch.py &
|
|||
./build/littlesnitch &
|
||||
|
||||
sleep 1
|
||||
curl -x http://localhost:8080 -k https://yolo.jetzt
|
||||
curl -x http://localhost:8080 -k https://get.yolo.jetzt
|
||||
sleep 1
|
||||
curl -x http://localhost:8080 -k https://get.yolo.jetzt
|
||||
|
||||
killall mitmdump
|
||||
sleep 1
|
||||
killall mitmdump
|
||||
sleep 2
|
||||
killall mitmdump
|
||||
sleep 3
|
||||
killall mitmdump
|
||||
killall mitmdump
|
||||
|
|
Loading…
Reference in a new issue