From 251c535121d68e5e309158493361e3c708575b2d Mon Sep 17 00:00:00 2001 From: Leo Smith Date: Tue, 10 Oct 2023 16:15:50 +0200 Subject: [PATCH 1/4] replace cmakelist with new files --- client/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 20c492d7..e991d72a 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -68,6 +68,7 @@ set( Include/Havoc/PythonApi/Event.h Include/Havoc/PythonApi/HavocUi.h Include/Havoc/PythonApi/PyAgentClass.hpp + Include/Havoc/PythonApi/PyWidgetClass.hpp # Dialogs Include/UserInterface/Dialogs/Payload.hpp @@ -153,6 +154,7 @@ set( Source/Havoc/PythonApi/PythonApi.cpp Source/Havoc/PythonApi/Havoc.cpp Source/Havoc/PythonApi/HavocUi.cpp + Source/Havoc/PythonApi/PyWidgetClass.cpp Source/Havoc/PythonApi/PyDemonClass.cpp Source/Havoc/PythonApi/Event.cpp Source/Havoc/Havoc.cpp From 7735b92d8eb4df475aa442cb57258b30a0a5b337 Mon Sep 17 00:00:00 2001 From: Leo Smith Date: Tue, 10 Oct 2023 16:16:57 +0200 Subject: [PATCH 2/4] Adding the pymodule class for the widget and the hpp call --- client/Source/Havoc/PythonApi/HavocUi.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/client/Source/Havoc/PythonApi/HavocUi.cpp b/client/Source/Havoc/PythonApi/HavocUi.cpp index 987a0b21..141a7379 100644 --- a/client/Source/Havoc/PythonApi/HavocUi.cpp +++ b/client/Source/Havoc/PythonApi/HavocUi.cpp @@ -1,5 +1,8 @@ #include #include + +#include + #include #include #include @@ -151,5 +154,11 @@ PyObject* PythonAPI::HavocUI::Core::SaveFileDialog(PyObject *self, PyObject *arg PyMODINIT_FUNC PythonAPI::HavocUI::PyInit_HavocUI(void) { - return PyModule_Create( &PythonAPI::HavocUI::PyModule::havocui ); + PyObject* Module = PyModule_Create2( &PythonAPI::HavocUI::PyModule::havocui, PYTHON_API_VERSION ); + + if ( PyType_Ready( &PyWidgetClass_Type ) < 0 ) + spdlog::error( "Couldn't check if WidgetClass is ready" ); + else + PyModule_AddObject( Module, "Widget", (PyObject*) &PyWidgetClass_Type ); + return Module; } From 133adafdecdec34ecb737a6ef8bd51851a9fb762 Mon Sep 17 00:00:00 2001 From: Leo Smith Date: Tue, 10 Oct 2023 16:17:42 +0200 Subject: [PATCH 3/4] Py Widget class to create widgets --- .../Source/Havoc/PythonApi/PyWidgetClass.cpp | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 client/Source/Havoc/PythonApi/PyWidgetClass.cpp diff --git a/client/Source/Havoc/PythonApi/PyWidgetClass.cpp b/client/Source/Havoc/PythonApi/PyWidgetClass.cpp new file mode 100644 index 00000000..aaf1afb2 --- /dev/null +++ b/client/Source/Havoc/PythonApi/PyWidgetClass.cpp @@ -0,0 +1,253 @@ + +#define PY_SSIZE_T_CLEAN +#include +#include + +#include +#include + +PPyWidgetQWindow DialogWindow = NULL; + +PyMemberDef PyWidgetClass_members[] = { + + { "title", T_STRING, offsetof( PyWidgetClass, title ), 0, "title" }, + + { NULL }, +}; + +PyMethodDef PyWidgetClass_methods[] = { + + { "exec", ( PyCFunction ) WidgetClass_exec, METH_VARARGS, "Display the window" }, + { "close", ( PyCFunction ) WidgetClass_close, METH_VARARGS, "Close the window" }, + { "addLabel", ( PyCFunction ) WidgetClass_addLabel, METH_VARARGS, "Insert a label in the window" }, + { "addButton", ( PyCFunction ) WidgetClass_addButton, METH_VARARGS, "Insert a button in the window" }, + { "addCheckbox", ( PyCFunction ) WidgetClass_addCheckbox, METH_VARARGS, "Insert a checkbox in the window" }, + { "addCombobox", ( PyCFunction ) WidgetClass_addCombobox, METH_VARARGS, "Insert a checkbox in the window" }, + { "addLineedit", ( PyCFunction ) WidgetClass_addLineedit, METH_VARARGS, "Insert a Line edit in the window" }, + { "ConsoleWrite", ( PyCFunction ) WidgetClass_ConsoleWrite, METH_VARARGS, "Print message console" }, + + { NULL }, +}; + +PyTypeObject PyWidgetClass_Type = { + PyVarObject_HEAD_INIT( &PyType_Type, 0 ) + + "havocui.widget", /* tp_name */ + sizeof( PyWidgetClass ), /* tp_basicsize */ + 0, /* tp_itemsize */ + ( destructor ) WidgetClass_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Widget Havoc Object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyWidgetClass_methods, /* tp_methods */ + PyWidgetClass_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + ( initproc ) WidgetClass_init, /* tp_init */ + 0, /* tp_alloc */ + WidgetClass_new, /* tp_new */ +}; + +#define AllocMov( des, src, size ) \ + if ( size > 0 ) \ + { \ + des = ( char* ) malloc( size * sizeof( char ) ); \ + memset( des, 0, size ); \ + std::strcpy( des, src ); \ + } + +void WidgetClass_dealloc( PPyWidgetClass self ) +{ + Py_XDECREF( self->title ); + delete DialogWindow->window; + free(DialogWindow); + + Py_TYPE( self )->tp_free( ( PyObject* ) self ); +} + +PyObject* WidgetClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ) +{ + PPyWidgetClass self; + + self = ( PPyWidgetClass ) PyType_Type.tp_alloc( type, 0 ); + + return ( PyObject* ) self; +} + +int WidgetClass_init( PPyWidgetClass self, PyObject *args, PyObject *kwds ) +{ + if ( PyType_Type.tp_init( ( PyObject* ) self, args, kwds ) < 0 ) + return -1; + + char* title = NULL; + const char* kwdlist[] = { "title", NULL }; + + if ( ! PyArg_ParseTupleAndKeywords( args, kwds, "s", const_cast(kwdlist), &title ) ) + return -1; + AllocMov( self->title, title, strlen(title) ); + DialogWindow = (PPyWidgetQWindow)malloc(sizeof(PyWidgetQWindow)); + if (DialogWindow == NULL) + return -1; + DialogWindow->window = new QDialog(HavocX::HavocUserInterface->HavocWindow); + DialogWindow->window->setWindowTitle(title); + DialogWindow->layout = new QVBoxLayout(DialogWindow->window); + + return 0; +} + +// Methods +PyObject* WidgetClass_exec( PPyWidgetClass self, PyObject *args ) +{ + DialogWindow->window->exec(); + + Py_RETURN_NONE; +} + +PyObject* WidgetClass_addLabel( PPyWidgetClass self, PyObject *args ) +{ + char *text = nullptr; + + if( !PyArg_ParseTuple( args, "s", &text) ) + { + Py_RETURN_NONE; + } + QLabel* label = new QLabel(text, DialogWindow->window); + DialogWindow->layout->addWidget(label); + + Py_RETURN_NONE; +} + +PyObject* WidgetClass_addButton( PPyWidgetClass self, PyObject *args ) +{ + char *text = nullptr; + PyObject* button_callback = nullptr; + + if( !PyArg_ParseTuple( args, "sO", &text, &button_callback) ) + { + Py_RETURN_NONE; + } + if ( !PyCallable_Check(button_callback) ) + { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + QPushButton* button = new QPushButton(text, DialogWindow->window); + DialogWindow->layout->addWidget(button); + QObject::connect(button, &QPushButton::clicked, DialogWindow->window, [button_callback]() { + PyObject_CallFunctionObjArgs(button_callback, nullptr); + }); + + Py_RETURN_NONE; +} + +PyObject* WidgetClass_addCheckbox( PPyWidgetClass self, PyObject *args ) +{ + char *text = nullptr; + PyObject* checkbox_callback = nullptr; + + if( !PyArg_ParseTuple( args, "sO", &text, &checkbox_callback) ) + { + Py_RETURN_NONE; + } + if ( !PyCallable_Check(checkbox_callback) ) + { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + QCheckBox* checkbox = new QCheckBox(text, DialogWindow->window); + DialogWindow->layout->addWidget(checkbox); + QObject::connect(checkbox, &QCheckBox::clicked, DialogWindow->window, [checkbox_callback]() { + PyObject_CallFunctionObjArgs(checkbox_callback, nullptr); + }); + + Py_RETURN_NONE; +} + +PyObject* WidgetClass_addCombobox( PPyWidgetClass self, PyObject *args ) +{ + Py_ssize_t tuple_size = PyTuple_Size(args); + QComboBox* comboBox = new QComboBox(DialogWindow->window); + + PyObject* callable_obj = PyTuple_GetItem(args, 0); + if ( !PyCallable_Check(callable_obj) ) + { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + for (Py_ssize_t i = 1; i < tuple_size; i++) { + const char * string_obj = PyUnicode_AsUTF8(PyTuple_GetItem(args, i)); + + comboBox->addItem(string_obj); + } + DialogWindow->layout->addWidget(comboBox); + QObject::connect(comboBox, QOverload::of(&QComboBox::activated), [callable_obj](int index) { + PyObject* pArg = PyLong_FromLong(index); + PyObject_CallFunctionObjArgs(callable_obj, pArg, nullptr); + }); + Py_RETURN_NONE; +} + +PyObject* WidgetClass_addLineedit( PPyWidgetClass self, PyObject *args ) +{ + char *text = nullptr; + PyObject* line_callback = nullptr; + + if( !PyArg_ParseTuple( args, "sO", &text, &line_callback) ) + { + Py_RETURN_NONE; + } + if ( !PyCallable_Check(line_callback) ) + { + PyErr_SetString(PyExc_TypeError, "parameter must be callable"); + return NULL; + } + QLineEdit* line = new QLineEdit(DialogWindow->window); + line->setPlaceholderText(text); + DialogWindow->layout->addWidget(line); + QObject::connect(line, &QLineEdit::editingFinished, DialogWindow->window, [line, line_callback]() { + QString text = line->text(); + QByteArray byteArray = text.toUtf8(); + char *charArray = byteArray.data(); + PyObject* pyString = PyUnicode_DecodeFSDefault(charArray); + PyObject_CallFunctionObjArgs(line_callback, pyString, nullptr); + }); + + Py_RETURN_NONE; +} + +PyObject* WidgetClass_close( PPyWidgetClass self, PyObject *args ) +{ + DialogWindow->window->accept(); + + Py_RETURN_NONE; +} + +PyObject* WidgetClass_ConsoleWrite( PPyWidgetClass self, PyObject *args ) +{ + printf("%s\n", self->title); + + Py_RETURN_NONE; +} From 59e172987c87c4d2169bdfbecb26deea2d862f9a Mon Sep 17 00:00:00 2001 From: Leo Smith Date: Tue, 10 Oct 2023 16:18:27 +0200 Subject: [PATCH 4/4] Header files for py widget class --- .../Include/Havoc/PythonApi/PyWidgetClass.hpp | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 client/Include/Havoc/PythonApi/PyWidgetClass.hpp diff --git a/client/Include/Havoc/PythonApi/PyWidgetClass.hpp b/client/Include/Havoc/PythonApi/PyWidgetClass.hpp new file mode 100644 index 00000000..403ec1cb --- /dev/null +++ b/client/Include/Havoc/PythonApi/PyWidgetClass.hpp @@ -0,0 +1,50 @@ +#ifndef HAVOC_PYWIDGETCLASS_H +#define HAVOC_PYWIDGETCLASS_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef struct +{ + PyObject_HEAD + + // Demon Info + char* title; + +} PyWidgetClass, *PPyWidgetClass; + +typedef struct +{ + + QDialog* window; + QVBoxLayout* layout; + +} PyWidgetQWindow, *PPyWidgetQWindow; + +extern PyTypeObject PyWidgetClass_Type; + +void WidgetClass_dealloc( PPyWidgetClass self ); +PyObject* WidgetClass_new( PyTypeObject *type, PyObject *args, PyObject *kwds ); +int WidgetClass_init( PPyWidgetClass self, PyObject *args, PyObject *kwds ); + +// Methods + +// PyObject* DemonClass_( PPyDemonClass self, PyObject *args ); + +PyObject* WidgetClass_exec( PPyWidgetClass self, PyObject *args ); +PyObject* WidgetClass_close( PPyWidgetClass self, PyObject *args ); +PyObject* WidgetClass_addLabel( PPyWidgetClass self, PyObject *args ); +PyObject* WidgetClass_addButton( PPyWidgetClass self, PyObject *args ); +PyObject* WidgetClass_addCheckbox( PPyWidgetClass self, PyObject *args ); +PyObject* WidgetClass_addCombobox( PPyWidgetClass self, PyObject *args ); +PyObject* WidgetClass_addLineedit( PPyWidgetClass self, PyObject *args ); +PyObject* WidgetClass_ConsoleWrite( PPyWidgetClass self, PyObject *args ); + +#endif