diff --git a/pykd_ext/arglist.cpp b/pykd_ext/arglist.cpp new file mode 100644 index 0000000..0a8151b --- /dev/null +++ b/pykd_ext/arglist.cpp @@ -0,0 +1,81 @@ +#include "stdafx.h" + +#include +#include + +#include "arglist.h" + +typedef boost::escaped_list_separator char_separator_t; +typedef boost::tokenizer< char_separator_t > char_tokenizer_t; + + +ArgsList getArgsList(const char* args) +{ + std::string argsStr(args); + + char_tokenizer_t token(argsStr, char_separator_t("", " \t", "\"")); + ArgsList argsList; + + for (char_tokenizer_t::iterator it = token.begin(); it != token.end(); ++it) + { + if (*it != "") + argsList.push_back(*it); + } + + return argsList; +} + +static const std::regex versionRe("^-([2,3])(?:\\.(\\d+))?$"); + +Options::Options(const ArgsList& argList) : + pyMajorVersion(2), + pyMinorVersion(-1), + global(true), + showHelp(false) +{ + + args = argList; + + for (auto it = args.begin(); it != args.end();) + { + if (*it == "--global" || *it == "-g") + { + global = true; + it = args.erase(it); + continue; + } + + if (*it == "--local" || *it == "-l") + { + global = false; + it = args.erase(it); + continue; + } + + if (*it == "--help" || *it == "-h") + { + showHelp = true; + it = args.erase(it); + continue; + } + + std::smatch mres; + if (std::regex_match(*it, mres, versionRe)) + { + pyMajorVersion = atol(std::string(mres[1].first, mres[1].second).c_str()); + + if (mres[2].matched) + { + pyMinorVersion = atol(std::string(mres[2].first, mres[2].second).c_str()); + } + + it = args.erase(it); + continue; + } + + break; + } + +} + + diff --git a/pykd_ext/arglist.h b/pykd_ext/arglist.h new file mode 100644 index 0000000..e921f7f --- /dev/null +++ b/pykd_ext/arglist.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +typedef std::vector< std::string > ArgsList; + +ArgsList getArgsList(const char* args); + +struct Options +{ + int pyMajorVersion; + int pyMinorVersion; + bool global; + bool showHelp; + std::vector args; + + Options() : + pyMajorVersion(-1), + pyMinorVersion(-1), + global(true), + showHelp(false) + {} + + Options(const ArgsList& argList); + +}; diff --git a/pykd_ext/dbgout.h b/pykd_ext/dbgout.h new file mode 100644 index 0000000..1c6e3dd --- /dev/null +++ b/pykd_ext/dbgout.h @@ -0,0 +1,116 @@ +#pragma once + + +#include +#include +#include + +#include +#include + +#include "pycontext.h" +#include "pyclass.h" + +////////////////////////////////////////////////////////////////////////////// + +class DbgOut +{ +public: + + DbgOut(PDEBUG_CLIENT client) : + m_control(client) + {} + + void write(const std::wstring& str) + { + AutoRestorePyState pystate; + + m_control->ControlledOutputWide( + DEBUG_OUTCTL_THIS_CLIENT, + DEBUG_OUTPUT_NORMAL, + L"%ws", + str.c_str() + ); + + } + + void writedml(const std::wstring& str) + { + AutoRestorePyState pystate; + + m_control->ControlledOutputWide( + DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_DML, + DEBUG_OUTPUT_NORMAL, + L"%ws", + str.c_str() + ); + } + + void flush() { + } + + std::wstring encoding() { + return L"ascii"; + } + + bool closed() { + return false; + } + +public: + + BEGIN_PYTHON_METHOD_MAP(DbgOut, "dbgout") + PYTHON_METHOD1("write", write, "write"); + PYTHON_METHOD0("flush", flush, "flush"); + PYTHON_METHOD0("encoding", encoding, "encoding"); + PYTHON_METHOD0("closed", closed, "closed"); + END_PYTHON_METHOD_MAP + +private: + + CComQIPtr m_control; + +}; + +/////////////////////////////////////////////////////////////////////////////// + +class DbgIn +{ +public: + + DbgIn(PDEBUG_CLIENT client) : + m_control(client) + {} + + std::wstring readline() + { + AutoRestorePyState pystate; + + std::vector inputBuffer(0x10000); + + ULONG read = 0; + m_control->InputWide(&inputBuffer[0], static_cast(inputBuffer.size()), &read); + + std::wstring inputstr = std::wstring(&inputBuffer[0]); + + return inputstr.empty() ? L"\n" : inputstr; + } + + bool closed() { + return false; + } + +public: + + BEGIN_PYTHON_METHOD_MAP(DbgIn, "dbgin") + PYTHON_METHOD0("readline", readline, "readline"); + PYTHON_METHOD0("closed", closed, "closed"); + END_PYTHON_METHOD_MAP + +private: + + CComQIPtr m_control; +}; + +////////////////////////////////////////////////////////////////////////////// + diff --git a/pykd_ext/dllmain.cpp b/pykd_ext/dllmain.cpp new file mode 100644 index 0000000..6284baf --- /dev/null +++ b/pykd_ext/dllmain.cpp @@ -0,0 +1,30 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + + +static HMODULE pinHandle = NULL; + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + if (!pinHandle) + { + GetModuleHandleEx( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + (LPCTSTR)hModule, + &pinHandle); + } + + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/pykd_ext/export.def b/pykd_ext/export.def new file mode 100644 index 0000000..e355850 --- /dev/null +++ b/pykd_ext/export.def @@ -0,0 +1,6 @@ +EXPORTS + DebugExtensionInitialize + DebugExtensionUninitialize + py + info + pip diff --git a/pykd_ext/packages.pykd_ext_vc120.config b/pykd_ext/packages.pykd_ext_vc120.config new file mode 100644 index 0000000..5122450 --- /dev/null +++ b/pykd_ext/packages.pykd_ext_vc120.config @@ -0,0 +1,4 @@ + + + + diff --git a/pykd_ext/pyapi.h b/pykd_ext/pyapi.h new file mode 100644 index 0000000..d1c9211 --- /dev/null +++ b/pykd_ext/pyapi.h @@ -0,0 +1,201 @@ +#pragma once + + +typedef void* PyObject; +typedef void* PyThreadState; +typedef PyObject *(*PyCFunction)(PyObject *, PyObject *); + + +typedef enum { PyGILState_LOCKED, PyGILState_UNLOCKED } PyGILState_STATE; + +const int Py_single_input = 256; +const int Py_file_input = 257; +const int Py_eval_input = 258; + +const int METH_VARARGS = 0x0001; + +struct PyMethodDef { + const char *ml_name; /* The name of the built-in function/method */ + PyCFunction ml_meth; /* The C function that implements it */ + int ml_flags; /* Combination of METH_xxx flags, which mostly + describe the args expected by the C func */ + const char *ml_doc; /* The __doc__ attribute, or NULL */ +}; + +typedef struct PyMethodDef PyMethodDef; + +void __stdcall Py_IncRef(PyObject* object); +void __stdcall Py_DecRef(PyObject* object); + +PyObject* __stdcall PyString_FromString(const char *v); +char* PyString_AsString(PyObject *string); + +PyObject* PyImport_Import(PyObject *name); +PyObject* __stdcall PyImport_ImportModule(const char *name); + +PyObject* __stdcall PyDict_New(); +int __stdcall PyDict_SetItemString(PyObject *p, const char *key, PyObject *val); + +PyObject* __stdcall PyTuple_New(size_t len); +PyObject* __stdcall PyTuple_GetItem(PyObject *p, size_t pos); +int __stdcall PyTuple_SetItem(PyObject *p, size_t pos, PyObject *obj); +PyObject* __stdcall PyDict_GetItemString(PyObject *p, const char *key); +size_t __stdcall PyTuple_Size(PyObject *p); + +size_t PyList_Size(PyObject* list); +PyObject* PyList_GetItem(PyObject *list, size_t index); + +PyObject* __stdcall PyCFunction_NewEx(PyMethodDef *, PyObject *, PyObject *); +PyObject* __stdcall PyClass_New(PyObject* className, PyObject* classBases, PyObject* classDict); +PyObject* __stdcall PyMethod_New(PyObject *func, PyObject *self, PyObject *classobj); +PyObject* __stdcall PyInstance_New(PyObject *classobj, PyObject *arg, PyObject *kw); + +PyThreadState* __stdcall PyEval_SaveThread(); +void __stdcall PyEval_RestoreThread(PyThreadState *tstate); + +int __stdcall PySys_SetObject(char *name, PyObject *v); +PyObject* __stdcall PySys_GetObject(char *name); +void __stdcall PySys_SetArgv(int argc, char **argv); +void __stdcall PySys_SetArgv_Py3(int argc, wchar_t **argv); + +int __stdcall PyRun_SimpleString(const char* str); +PyObject* __stdcall PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals); +PyObject* PyRun_File(FILE *fp, const char *filename, int start, PyObject *globals, PyObject *locals); + +typedef void(*PyCapsule_Destructor)(PyObject *); +PyObject* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor); +void* PyCapsule_GetPointer(PyObject *capsule, const char *name); + +int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v); +PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name); +PyObject* PyObject_CallObject(PyObject *callable_object, PyObject *args); +PyObject* PyObject_Call(PyObject *callable_object, PyObject *args, PyObject *kw); +PyObject* PyUnicode_FromWideChar(const wchar_t *w, size_t size); + +PyObject* PyBool_FromLong(long v); + +PyObject* Py_None(); +PyObject* PyExc_SystemExit(); +PyObject* PyType_Type(); + +void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback); +void PyErr_NormalizeException(PyObject**exc, PyObject**val, PyObject**tb); +void PyErr_SetString(PyObject *type, const char *message); + +PyObject* PyFile_FromString(char *filename, char *mode); +FILE* PyFile_AsFile(PyObject *pyfile); +FILE* _Py_fopen(const char* filename, const char* mode); + +PyObject* __stdcall PyUnicode_FromString(const char* str); +PyObject* __stdcall PyInstanceMethod_New(PyObject *func); +size_t __stdcall PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, size_t size); +int __stdcall Py_AddPendingCall(int(*func)(void *), void *arg); + +PyGILState_STATE __stdcall PyGILState_Ensure(); +void __stdcall PyGILState_Release(PyGILState_STATE); + +bool IsPy3(); + +class PyObjectRef; + + +class PyObjectBorrowedRef +{ + friend PyObjectRef; + +public: + + PyObjectBorrowedRef(PyObject* obj) + { + m_obj = obj; + if (m_obj) + Py_IncRef(m_obj); + } + + ~PyObjectBorrowedRef() + { + if (m_obj) + Py_DecRef(m_obj); + } + + operator PyObject*() + { + return m_obj; + } + +private: + + + PyObjectBorrowedRef(const PyObjectRef& obj) = delete; + + PyObject* m_obj; +}; + + +class PyObjectRef +{ +public: + + PyObjectRef() : m_obj(0) + {} + + PyObjectRef(PyObject* obj) + { + m_obj = obj; + } + + ~PyObjectRef() + { + if (m_obj) + Py_DecRef(m_obj); + } + + operator PyObject*() + { + return m_obj; + } + + PyObjectRef& operator= (const PyObjectRef& ref) + { + if (m_obj) + Py_DecRef(m_obj); + + m_obj = ref.m_obj; + if (m_obj) + Py_IncRef(m_obj); + + return *this; + } + + PyObjectRef& operator= (const PyObjectBorrowedRef& ref) + { + if (m_obj) + Py_DecRef(m_obj); + + m_obj = ref.m_obj; + if (m_obj) + Py_IncRef(m_obj); + + return *this; + } + + PyObjectRef& operator= (PyObject* obj) + { + if (m_obj) + Py_DecRef(m_obj); + + m_obj = obj; + + return *this; + } + +private: + + PyObjectRef(const PyObjectRef& obj) = delete; + + PyObject* m_obj; +}; + + + + diff --git a/pykd_ext/pyclass.h b/pykd_ext/pyclass.h new file mode 100644 index 0000000..282c901 --- /dev/null +++ b/pykd_ext/pyclass.h @@ -0,0 +1,198 @@ +#pragma once + +#include "pyapi.h" + + +#include + +#include + +struct convert_from_python +{ + convert_from_python(PyObject* obj) : m_obj(obj){} + + operator std::wstring() + { + if (IsPy3()) + { + wchar_t buf[0x100]; + size_t len = 0x100; + len = PyUnicode_AsWideChar(m_obj, buf, len); + return std::wstring(buf, len); + } + else + return std::wstring(_bstr_t(PyString_AsString(m_obj))); + } + + operator std::string() + { + if (IsPy3()) + { + wchar_t buf[0x100]; + size_t len = 0x100; + len = PyUnicode_AsWideChar(m_obj, buf, len); + std::wstring str(buf, len); + return std::string(_bstr_t(str.c_str())); + } + else + return std::string(PyString_AsString(m_obj)); + } + + PyObject* m_obj; +}; + +struct convert_to_python +{ + operator PyObject* (){ + return m_obj; + } + + convert_to_python() + { + + } + + convert_to_python(const std::wstring& str) + { + m_obj = PyUnicode_FromWideChar(str.c_str(), str.size()); + } + + convert_to_python(bool v) + { + m_obj = PyBool_FromLong(v == true ? 1 : 0); + } + + PyObject* m_obj; +}; + + + +#define BEGIN_PYTHON_METHOD_MAP(classType, className) \ + template \ + PyObject* callMethod0( \ + TRet (classType::*method)()) \ + { \ + TRet r = (this->*method)(); \ + return convert_to_python(r); \ + } \ + PyObject* callMethod0(\ + void (classType::*method)()) \ + { \ + (this->*method)(); \ + Py_IncRef(Py_None()); \ + return Py_None(); \ + } \ + template \ + PyObject* callMethod1( \ + TRet (classType::*method)(V1& v1), \ + convert_from_python& v1)\ + { \ + return (this->*method)(v1); \ + } \ + template \ + PyObject* callMethod1(\ + void(classType::*method)(V1& v1), \ + convert_from_python& v1)\ + { \ + (this->*method)(v1); \ + Py_IncRef(Py_None()); \ + return Py_None(); \ + } \ +template \ +static PyObject* getPythonClass() { \ + PyObject* classNameObj = IsPy3() ? PyUnicode_FromString(className) : PyString_FromString(className); \ + PyObject* classBases = PyTuple_New(0); \ + PyObject* classDictObj = PyDict_New(); \ + PyObject* args = PyTuple_New(3); \ + PyTuple_SetItem(args, 0, classNameObj); \ + PyTuple_SetItem(args, 1, classBases); \ + PyTuple_SetItem(args, 2, classDictObj); \ + PyObject* classTypeObj = PyObject_CallObject(PyType_Type(), args); \ + Py_DecRef(args), Py_DecRef(classNameObj), Py_DecRef(classDictObj), Py_DecRef(classBases); \ + +#define END_PYTHON_METHOD_MAP \ + return classTypeObj; \ + } + +#define PYTHON_METHOD0(name, fn, doc) \ + struct Call_##fn { \ + static PyObject* pycall(PyObject *s, PyObject *args) \ + { \ + PyObject* self = PyTuple_GetItem(args, 0); \ + PyObject* cppobj = PyObject_GetAttrString(self, "cppobject"); \ + T* _this = reinterpret_cast(PyCapsule_GetPointer(cppobj, "cppobject")); \ + Py_DecRef(cppobj); \ + return _this->callMethod0(&fn); \ + } \ + }; \ + {\ + static PyMethodDef methodDef = { name, Call_##fn::pycall, METH_VARARGS }; \ + PyObject* cFuncObj = PyCFunction_NewEx(&methodDef, NULL, NULL); \ + PyObject* methodObj = IsPy3() ? PyInstanceMethod_New(cFuncObj) : PyMethod_New(cFuncObj, NULL, classTypeObj); \ + PyObject_SetAttrString(classTypeObj, name, methodObj); \ + Py_DecRef(cFuncObj), Py_DecRef(methodObj); \ + } + +#define PYTHON_METHOD1(name, fn, doc) \ + struct Call_##fn { \ + static PyObject* pycall(PyObject *s, PyObject *args) \ + { \ + PyObject* self = PyTuple_GetItem(args, 0); \ + PyObject* cppobj = PyObject_GetAttrString(self, "cppobject"); \ + T* _this = reinterpret_cast(PyCapsule_GetPointer(cppobj, "cppobject")); \ + Py_DecRef(cppobj); \ + PyObject* v1 = PyTuple_GetItem(args, 1); \ + return _this->callMethod1(&fn, convert_from_python(v1)); \ + } \ + }; \ + {\ + static PyMethodDef methodDef = { name, Call_##fn::pycall, METH_VARARGS }; \ + PyObject* cFuncObj = PyCFunction_NewEx(&methodDef, NULL, NULL); \ + PyObject* methodObj = IsPy3() ? PyInstanceMethod_New(cFuncObj) : PyMethod_New(cFuncObj, NULL, classTypeObj); \ + PyObject_SetAttrString(classTypeObj, name, methodObj); \ + Py_DecRef(cFuncObj), Py_DecRef(methodObj); \ + } + + + +template +void delete_pyobject(PyObject* obj) +{ + T1* cppobj = reinterpret_cast(PyCapsule_GetPointer(obj, "cppobject")); + delete cppobj; +} + +template +PyObject* make_pyobject(const T2& var) +{ + PyObject* cls = T1::getPythonClass(); + PyObject* p1 = PyObject_CallObject(cls, NULL); + + PyObject *errtype = NULL, *errvalue = NULL, *traceback = NULL; + PyErr_Fetch(&errtype, &errvalue, &traceback); + + char* str; + if (errtype) + Py_DecRef(errtype); + if (errvalue) + { + str = PyString_AsString(errvalue); + Py_DecRef(errvalue); + } + if (traceback) + Py_DecRef(traceback); + + Py_DecRef(cls); + + T1* t1 = new T1(var); + PyObject* p2 = PyCapsule_New(t1, "cppobject", delete_pyobject); + + PyObject_SetAttrString(p1, "cppobject", p2); + + Py_DecRef(p2); + + return p1; +} + + + diff --git a/pykd_ext/pycontext.h b/pykd_ext/pycontext.h new file mode 100644 index 0000000..5d06fd2 --- /dev/null +++ b/pykd_ext/pycontext.h @@ -0,0 +1,23 @@ +#pragma once + +#include "pyapi.h" + +class AutoRestorePyState +{ +public: + + AutoRestorePyState() + { + m_state = PyEval_SaveThread(); + } + + ~AutoRestorePyState() + { + PyEval_RestoreThread(m_state); + } + +private: + + PyThreadState* m_state; +}; + diff --git a/pykd_ext/pyinterpret.cpp b/pykd_ext/pyinterpret.cpp new file mode 100644 index 0000000..caf2d8b --- /dev/null +++ b/pykd_ext/pyinterpret.cpp @@ -0,0 +1,692 @@ +#include "stdafx.h" +#include "pyinterpret.h" + +#include +#include +#include + +#include "pymodule.h" +#include "pyclass.h" +#include "dbgout.h" + +class PyModule; +class PythonInterpreter; + +class HKey +{ + +public: + + HKey() : m_key(NULL) + {} + + ~HKey() + { + if (m_key) + RegCloseKey(m_key); + } + + operator HKEY*() + { + return &m_key; + } + + operator HKEY() const + { + return m_key; + } + +private: + + HKEY m_key; +}; + + +class PyModule +{ +public: + + PyModule(int majorVesion, int minorVersion); + + ~PyModule(); + + bool isPy3; + + PyObject* PyType_Type; + PyObject* Py_None; + PyObject* PyExc_SystemExit; + + void(__stdcall *Py_Initialize)(); + void(__stdcall *Py_Finalize)(); + + PyThreadState* (__stdcall *Py_NewInterpreter)(); + void(__stdcall *Py_EndInterpreter)(PyThreadState *tstate); + PyObject* (__stdcall *PyEval_GetGlobals)(); + PyObject* (__stdcall *PyImport_Import)(PyObject *name); + PyObject* (__stdcall *PyImport_ImportModule)(const char *name); + void(__stdcall *PyEval_InitThreads)(); + PyThreadState* (__stdcall *PyEval_SaveThread)(); + void(__stdcall *PyEval_RestoreThread)(PyThreadState *tstate); + PyThreadState* (__stdcall *PyThreadState_Swap)(PyThreadState *tstate); + PyObject* (__stdcall *PyRun_String)(const char *str, int start, PyObject *globals, PyObject *locals); + int(__stdcall *PyRun_SimpleString)(const char* str); + PyObject* (__stdcall *PyRun_File)(FILE *fp, const char *filename, int start, PyObject *globals, PyObject *locals); + PyObject* (__stdcall *PyDict_New)(); + int(__stdcall *PyDict_SetItemString)(PyObject *p, const char *key, PyObject *val); + PyObject*(__stdcall *PyDict_GetItemString)(PyObject *p, const char* key); + void(__stdcall *Py_IncRef)(PyObject* object); + void(__stdcall *Py_DecRef)(PyObject* object); + PyObject* (__stdcall *PyObject_Call)(PyObject *callable_object, PyObject *args, PyObject *kw); + PyObject* (__stdcall *PyObject_GetAttr)(PyObject *object, PyObject *attr_name); + PyObject* (__stdcall *PyObject_GetAttrString)(PyObject *object, const char *attr_name); + int(__stdcall *PyObject_SetAttr)(PyObject *object, PyObject *attr_name, PyObject *value); + PyObject* (__stdcall *PyObject_CallObject)(PyObject *callable_object, PyObject *args); + PyObject* (__stdcall *PyTuple_New)(size_t len); + int(__stdcall *PyTuple_SetItem)(PyObject *p, size_t pos, PyObject *o); + PyObject* (__stdcall *PyTuple_GetItem)(PyObject *p, size_t pos); + size_t(__stdcall *PyTuple_Size)(PyObject *p); + PyObject* (__stdcall *PyCFunction_NewEx)(PyMethodDef *, PyObject *, PyObject *); + PyObject* (__stdcall *PySys_GetObject)(char *name); + int(__stdcall *PySys_SetObject)(char *name, PyObject *v); + void(__stdcall *PySys_SetArgv)(int argc, char **argv); + void(__stdcall *PySys_SetArgv_Py3)(int argc, wchar_t **argv); + PyObject* (__stdcall *PyString_FromString)(const char *v); + char* (__stdcall *PyString_AsString)(PyObject *string); + void(__stdcall *PyErr_Fetch)(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback); + void(__stdcall *PyErr_NormalizeException)(PyObject**exc, PyObject**val, PyObject**tb); + void (__stdcall *PyErr_SetString)(PyObject *type, const char *message); + PyObject* (__stdcall *PyImport_AddModule)(const char *name); + PyObject* (__stdcall *PyClass_New)(PyObject* className, PyObject* classBases, PyObject* classDict); + PyObject* (__stdcall *PyInstance_New)(PyObject *classobj, PyObject *arg, PyObject *kw); + PyObject* (__stdcall *PyMethod_New)(PyObject *func, PyObject *self, PyObject *classobj); + PyObject* (__stdcall *PyCapsule_New)(void *pointer, const char *name, PyCapsule_Destructor destructor); + void* (__stdcall *PyCapsule_GetPointer)(PyObject *capsule, const char *name); + int(__stdcall *PyObject_SetAttrString)(PyObject *o, const char *attr_name, PyObject *v); + PyObject* (__stdcall *PyUnicode_FromWideChar)(const wchar_t *w, size_t size); + PyObject* (__stdcall *PyBool_FromLong)(long v); + size_t(__stdcall *PyList_Size)(PyObject* list); + PyObject* (__stdcall *PyList_GetItem)(PyObject *list, size_t index); + PyObject* (__stdcall *PyFile_FromString)(char *filename, char *mode); + FILE* (__stdcall *PyFile_AsFile)(PyObject *pyfile); + PyObject* (__stdcall *PyUnicode_FromString)(const char *u); + PyObject* (__stdcall *PyInstanceMethod_New)(PyObject *func); + size_t(__stdcall *PyUnicode_AsWideChar)(PyObject *unicode, wchar_t *w, size_t size); + FILE* ( __stdcall *_Py_fopen)(const char* filename, const char* mode); + int(__stdcall *Py_AddPendingCall)(int(*func)(void *), void *arg); + PyGILState_STATE(__stdcall *PyGILState_Ensure)(); + void(__stdcall *PyGILState_Release)(PyGILState_STATE state); + + HMODULE m_handlePython; + PyThreadState* m_globalState; + PythonInterpreter* m_globalInterpreter; +}; + +class PythonInterpreter +{ +public: + + PythonInterpreter(PyModule* mod) : + m_module(mod) + { + PyThreadState* state = mod->Py_NewInterpreter(); + + m_module->PyThreadState_Swap(state); + + m_state = m_module->PyEval_SaveThread(); + } + + ~PythonInterpreter() + { + m_module->PyEval_RestoreThread(m_state); + + m_module->Py_EndInterpreter(m_state); + } + + PyModule* m_module; + + PyThreadState* m_state; +}; + + + + +class PythonSingleton +{ +public: + + + static PythonInterpreter* getInterpreter(int majorVersion, int minorVersion, bool global) + { + return getSingleton()->_getInterpreter(majorVersion, minorVersion, global); + } + + static void releaseInterpretor(PythonInterpreter* interpret) + { + return getSingleton()->_releaseInterpretor(interpret); + } + + static PythonInterpreter* currentInterpreter() + { + return getSingleton()->m_currentInterpter; + } + +private: + + static std::auto_ptr m_singleton; + + static PythonSingleton* getSingleton() + { + if (m_singleton.get() == 0) + m_singleton.reset(new PythonSingleton()); + return m_singleton.get(); + } + + PythonInterpreter* _getInterpreter(int majorVersion, int minorVersion, bool global) + { + + PyModule* module = 0; + + if (m_modules.find(std::make_pair(majorVersion, minorVersion)) == m_modules.end()) + { + module = new PyModule(majorVersion, minorVersion); + m_modules.insert(std::make_pair(std::make_pair(majorVersion, minorVersion), module)); + } + else + { + module = m_modules[std::make_pair(majorVersion, minorVersion)]; + } + + if (global) + { + if (module->m_globalInterpreter == 0) + { + module->PyEval_RestoreThread(module->m_globalState); + module->m_globalInterpreter = new PythonInterpreter(module); + } + + m_currentInterpter = module->m_globalInterpreter; + m_currentIsGlobal = true; + } + else + { + module->PyEval_RestoreThread(module->m_globalState); + m_currentInterpter = new PythonInterpreter(module); + m_currentIsGlobal = false; + } + + m_currentInterpter->m_module->PyEval_RestoreThread(m_currentInterpter->m_state); + + return m_currentInterpter; + } + + void _releaseInterpretor(PythonInterpreter* interpret) + { + PyModule* module = m_currentInterpter->m_module; + + m_currentInterpter->m_state = module->PyEval_SaveThread(); + + if (!m_currentIsGlobal) + { + delete m_currentInterpter; + + module->PyThreadState_Swap(module->m_globalState); + module->m_globalState = module->PyEval_SaveThread(); + } + + m_currentInterpter = 0; + } + +public: + + std::map, PyModule*> m_modules; + + PythonInterpreter* m_currentInterpter; + bool m_currentIsGlobal; +}; + +std::auto_ptr PythonSingleton::m_singleton; + + +HMODULE LoadPythonLibrary(int majorVesion, int minorVersion) +{ + + HKey pythonCoreKey; + + for (auto rootKey : std::list({ HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER })) + { + if (ERROR_SUCCESS == RegOpenKeyA(rootKey, "SOFTWARE\\Python\\PythonCore", pythonCoreKey)) + { + HKey installPathKey; + + std::stringstream installPathStr; + installPathStr << majorVesion << '.' << minorVersion << "\\InstallPath"; + + if (ERROR_SUCCESS == RegOpenKeyA(pythonCoreKey, installPathStr.str().c_str(), installPathKey)) + { + char installPath[1000]; + DWORD installPathSize = sizeof(installPath); + + if (ERROR_SUCCESS == RegQueryValueExA(installPathKey, NULL, NULL, NULL, (LPBYTE)installPath, &installPathSize)) + { + std::stringstream dllName; + dllName << "python" << majorVesion << minorVersion << ".dll"; + + HMODULE hmodule = LoadLibraryA(dllName.str().c_str()); + if (hmodule) + return hmodule; + + std::stringstream imagePath; + imagePath << installPath << dllName.str(); + + hmodule = LoadLibraryExA(imagePath.str().c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (hmodule) + return hmodule; + } + } + } + } + + return NULL; +} + +std::list getInstalledInterpreter() +{ + std::list lst; + + for (auto rootKey : std::list({ HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER })) + { + HKey pythonCoreKey; + + if (ERROR_SUCCESS != RegOpenKeyA(rootKey, "SOFTWARE\\Python\\PythonCore", pythonCoreKey)) + continue; + + for (DWORD i = 0;; ++i) + { + char versionStr[20]; + if (ERROR_SUCCESS != RegEnumKeyA(pythonCoreKey, i, versionStr, sizeof(versionStr))) + break; + + int majorVersion, minorVersion; + + sscanf_s(versionStr, "%d.%d", &majorVersion, &minorVersion); + + HMODULE hmodule = LoadPythonLibrary(majorVersion, minorVersion); + + if (hmodule) + { + char fullPath[1000]; + + if (GetModuleFileNameA(hmodule, fullPath, sizeof(fullPath))) + lst.push_back({ majorVersion, minorVersion, fullPath }); + + FreeLibrary(hmodule); + } + } + } + + return lst; +} + +PyModule::PyModule(int majorVesion, int minorVersion) +{ + m_handlePython = LoadPythonLibrary(majorVesion, minorVersion); + + if (!m_handlePython) + throw std::exception("failed to load python module"); + + isPy3 = majorVesion == 3; + + *reinterpret_cast(&PyType_Type) = GetProcAddress(m_handlePython, "PyType_Type"); + *reinterpret_cast(&Py_None) = GetProcAddress(m_handlePython, "_Py_NoneStruct"); + PyExc_SystemExit = *reinterpret_cast(GetProcAddress(m_handlePython, "PyExc_SystemExit")); + + *reinterpret_cast(&Py_Initialize) = GetProcAddress(m_handlePython, "Py_Initialize"); + *reinterpret_cast(&Py_Finalize) = GetProcAddress(m_handlePython, "Py_Finalize"); + *reinterpret_cast(&Py_NewInterpreter) = GetProcAddress(m_handlePython, "Py_NewInterpreter"); + *reinterpret_cast(&Py_EndInterpreter) = GetProcAddress(m_handlePython, "Py_EndInterpreter"); + *reinterpret_cast(&Py_DecRef) = GetProcAddress(m_handlePython, "Py_DecRef"); + *reinterpret_cast(&Py_IncRef) = GetProcAddress(m_handlePython, "Py_IncRef"); + *reinterpret_cast(&PyEval_GetGlobals) = GetProcAddress(m_handlePython, "PyEval_GetGlobals"); + *reinterpret_cast(&PyEval_InitThreads) = GetProcAddress(m_handlePython, "PyEval_InitThreads"); + *reinterpret_cast(&PyEval_SaveThread) = GetProcAddress(m_handlePython, "PyEval_SaveThread"); + *reinterpret_cast(&PyEval_RestoreThread) = GetProcAddress(m_handlePython, "PyEval_RestoreThread"); + *reinterpret_cast(&PyThreadState_Swap) = GetProcAddress(m_handlePython, "PyThreadState_Swap"); + *reinterpret_cast(&PyRun_String) = GetProcAddress(m_handlePython, "PyRun_String"); + *reinterpret_cast(&PyRun_SimpleString) = GetProcAddress(m_handlePython, "PyRun_SimpleString"); + *reinterpret_cast(&PyRun_File) = GetProcAddress(m_handlePython, "PyRun_File"); + *reinterpret_cast(&PyDict_New) = GetProcAddress(m_handlePython, "PyDict_New"); + *reinterpret_cast(&PyDict_SetItemString) = GetProcAddress(m_handlePython, "PyDict_SetItemString"); + *reinterpret_cast(&PyDict_GetItemString) = GetProcAddress(m_handlePython, "PyDict_GetItemString"); + *reinterpret_cast(&PyObject_Call) = GetProcAddress(m_handlePython, "PyObject_Call"); + *reinterpret_cast(&PyObject_GetAttr) = GetProcAddress(m_handlePython, "PyObject_GetAttr"); + *reinterpret_cast(&PyObject_GetAttrString) = GetProcAddress(m_handlePython, "PyObject_GetAttrString"); + *reinterpret_cast(&PyObject_SetAttr) = GetProcAddress(m_handlePython, "PyObject_SetAttr"); + *reinterpret_cast(&PyObject_CallObject) = GetProcAddress(m_handlePython, "PyObject_CallObject"); + *reinterpret_cast(&PyTuple_New) = GetProcAddress(m_handlePython, "PyTuple_New"); + *reinterpret_cast(&PyTuple_SetItem) = GetProcAddress(m_handlePython, "PyTuple_SetItem"); + *reinterpret_cast(&PyTuple_GetItem) = GetProcAddress(m_handlePython, "PyTuple_GetItem"); + *reinterpret_cast(&PyTuple_Size) = GetProcAddress(m_handlePython, "PyTuple_Size"); + *reinterpret_cast(&PyString_FromString) = GetProcAddress(m_handlePython, "PyString_FromString"); + *reinterpret_cast(&PyCFunction_NewEx) = GetProcAddress(m_handlePython, "PyCFunction_NewEx"); + *reinterpret_cast(&PySys_GetObject) = GetProcAddress(m_handlePython, "PySys_GetObject"); + *reinterpret_cast(&PySys_SetObject) = GetProcAddress(m_handlePython, "PySys_SetObject"); + *reinterpret_cast(&PySys_SetArgv) = !isPy3 ? GetProcAddress(m_handlePython, "PySys_SetArgv") : 0 ; + *reinterpret_cast(&PySys_SetArgv_Py3) = isPy3 ? GetProcAddress(m_handlePython, "PySys_SetArgv") : 0; + *reinterpret_cast(&PyString_FromString) = GetProcAddress(m_handlePython, "PyString_FromString"); + *reinterpret_cast(&PyString_AsString) = GetProcAddress(m_handlePython, "PyString_AsString"); + *reinterpret_cast(&PyErr_Fetch) = GetProcAddress(m_handlePython, "PyErr_Fetch"); + *reinterpret_cast(&PyErr_NormalizeException) = GetProcAddress(m_handlePython, "PyErr_NormalizeException"); + *reinterpret_cast(&PyErr_SetString) = GetProcAddress(m_handlePython, "PyErr_SetString"); + *reinterpret_cast(&PyImport_AddModule) = GetProcAddress(m_handlePython, "PyImport_AddModule"); + *reinterpret_cast(&PyImport_ImportModule) = GetProcAddress(m_handlePython, "PyImport_ImportModule"); + *reinterpret_cast(&PyClass_New) = GetProcAddress(m_handlePython, "PyClass_New"); + *reinterpret_cast(&PyInstance_New) = GetProcAddress(m_handlePython, "PyInstance_New"); + *reinterpret_cast(&PyMethod_New) = GetProcAddress(m_handlePython, "PyMethod_New"); + *reinterpret_cast(&PyCapsule_New) = GetProcAddress(m_handlePython, "PyCapsule_New"); + *reinterpret_cast(&PyCapsule_GetPointer) = GetProcAddress(m_handlePython, "PyCapsule_GetPointer"); + *reinterpret_cast(&PyObject_SetAttrString) = GetProcAddress(m_handlePython, "PyObject_SetAttrString"); + *reinterpret_cast(&PyUnicode_FromWideChar) = isPy3 ? GetProcAddress(m_handlePython, "PyUnicode_FromWideChar") : + GetProcAddress(m_handlePython, "PyUnicodeUCS2_FromWideChar"); + *reinterpret_cast(&PyImport_Import) = GetProcAddress(m_handlePython, "PyImport_Import"); + *reinterpret_cast(&PyBool_FromLong) = GetProcAddress(m_handlePython, "PyBool_FromLong"); + *reinterpret_cast(&PyList_Size) = GetProcAddress(m_handlePython, "PyList_Size"); + *reinterpret_cast(&PyList_GetItem) = GetProcAddress(m_handlePython, "PyList_GetItem"); + *reinterpret_cast(&PyFile_FromString) = GetProcAddress(m_handlePython, "PyFile_FromString"); + *reinterpret_cast(&PyFile_AsFile) = GetProcAddress(m_handlePython, "PyFile_AsFile"); + *reinterpret_cast(&PyUnicode_FromString) = GetProcAddress(m_handlePython, "PyUnicode_FromString"); + *reinterpret_cast(&PyInstanceMethod_New) = GetProcAddress(m_handlePython, "PyInstanceMethod_New"); + *reinterpret_cast(&PyUnicode_AsWideChar) = GetProcAddress(m_handlePython, "PyUnicode_AsWideChar"); + *reinterpret_cast(&_Py_fopen) = GetProcAddress(m_handlePython, "_Py_fopen"); + *reinterpret_cast(&Py_AddPendingCall) = GetProcAddress(m_handlePython, "Py_AddPendingCall"); + *reinterpret_cast(&PyGILState_Ensure) = GetProcAddress(m_handlePython, "PyGILState_Ensure"); + *reinterpret_cast(&PyGILState_Release) = GetProcAddress(m_handlePython, "PyGILState_Release"); + + Py_Initialize(); + PyEval_InitThreads(); + m_globalState = PyEval_SaveThread(); +} + + +PyModule::~PyModule() +{ + PyEval_RestoreThread(m_globalState); + + Py_Finalize(); + + FreeLibrary(m_handlePython); +} + + +PythonInterpreter* activateInterpreter(bool global, int majorVersion, int minorVersion) +{ + return PythonSingleton::getInterpreter(majorVersion, minorVersion, global); +} + +void releaseInterpretor(PythonInterpreter* interpret) +{ + PythonSingleton::releaseInterpretor(interpret); +} + + +void __stdcall Py_IncRef(PyObject* object) +{ + PythonSingleton::currentInterpreter()->m_module->Py_IncRef(object); +} + +void __stdcall Py_DecRef(PyObject* object) +{ + PythonSingleton::currentInterpreter()->m_module->Py_DecRef(object); +} + +PyObject* __stdcall PyString_FromString(const char *v) +{ + return PythonSingleton::currentInterpreter()->m_module->PyString_FromString(v); +} + +PyObject* __stdcall PyDict_New() +{ + return PythonSingleton::currentInterpreter()->m_module->PyDict_New(); +} + +PyObject* __stdcall PyDict_GetItemString(PyObject *p, const char *key) +{ + return PythonSingleton::currentInterpreter()->m_module->PyDict_GetItemString(p, key); +} + +int __stdcall PyDict_SetItemString(PyObject *p, const char *key, PyObject *val) +{ + return PythonSingleton::currentInterpreter()->m_module->PyDict_SetItemString(p, key, val); +} + +PyObject* __stdcall PyCFunction_NewEx(PyMethodDef* pydef, PyObject *p1, PyObject *p2) +{ + return PythonSingleton::currentInterpreter()->m_module->PyCFunction_NewEx(pydef, p1, p2); +} + +PyObject* __stdcall PyClass_New(PyObject* className, PyObject* classBases, PyObject* classDict) +{ + return PythonSingleton::currentInterpreter()->m_module->PyClass_New(className, classBases, classDict); +} + +PyObject* __stdcall PyMethod_New(PyObject *func, PyObject *self, PyObject *classobj) +{ + return PythonSingleton::currentInterpreter()->m_module->PyMethod_New(func, self, classobj); +} + +int __stdcall PySys_SetObject(char *name, PyObject *v) +{ + return PythonSingleton::currentInterpreter()->m_module->PySys_SetObject(name, v); +} + +void __stdcall PySys_SetArgv(int argc, char **argv) +{ + PythonSingleton::currentInterpreter()->m_module->PySys_SetArgv(argc, argv); +} + +void __stdcall PySys_SetArgv_Py3(int argc, wchar_t **argv) +{ + PythonSingleton::currentInterpreter()->m_module->PySys_SetArgv_Py3(argc, argv); +} + +PyObject* __stdcall PySys_GetObject(char *name) +{ + return PythonSingleton::currentInterpreter()->m_module->PySys_GetObject(name); +} + +PyObject* __stdcall PyInstance_New(PyObject *classobj, PyObject *arg, PyObject *kw) +{ + return PythonSingleton::currentInterpreter()->m_module->PyInstance_New(classobj, arg, kw); +} + +int __stdcall PyRun_SimpleString(const char* str) +{ + return PythonSingleton::currentInterpreter()->m_module->PyRun_SimpleString(str); +} + +PyObject* __stdcall PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals) +{ + return PythonSingleton::currentInterpreter()->m_module->PyRun_String(str, start, globals, locals); +} + +PyObject* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor) +{ + return PythonSingleton::currentInterpreter()->m_module->PyCapsule_New(pointer, name, destructor); +} + +void* PyCapsule_GetPointer(PyObject *capsule, const char *name) +{ + return PythonSingleton::currentInterpreter()->m_module->PyCapsule_GetPointer(capsule, name); +} + +int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v) +{ + return PythonSingleton::currentInterpreter()->m_module->PyObject_SetAttrString(o, attr_name, v); +} + +PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name) +{ + return PythonSingleton::currentInterpreter()->m_module->PyObject_GetAttrString(o, attr_name); +} + +PyObject* PyObject_CallObject(PyObject *callable_object, PyObject *args) +{ + return PythonSingleton::currentInterpreter()->m_module->PyObject_CallObject(callable_object, args); +} + +PyObject* PyObject_Call(PyObject *callable_object, PyObject *args, PyObject *kw) +{ + return PythonSingleton::currentInterpreter()->m_module->PyObject_Call(callable_object, args, kw); +} + +PyObject* __stdcall PyTuple_New(size_t len) +{ + return PythonSingleton::currentInterpreter()->m_module->PyTuple_New(len); +} + +PyObject* __stdcall PyTuple_GetItem(PyObject *p, size_t pos) +{ + return PythonSingleton::currentInterpreter()->m_module->PyTuple_GetItem(p, pos); +} + +int __stdcall PyTuple_SetItem(PyObject *p, size_t pos, PyObject *obj) +{ + return PythonSingleton::currentInterpreter()->m_module->PyTuple_SetItem(p, pos, obj); +} + +size_t __stdcall PyTuple_Size(PyObject *p) +{ + return PythonSingleton::currentInterpreter()->m_module->PyTuple_Size(p); +} + +char* PyString_AsString(PyObject *string) +{ + return PythonSingleton::currentInterpreter()->m_module->PyString_AsString(string); +} + +PyObject* PyUnicode_FromWideChar(const wchar_t *w, size_t size) +{ + return PythonSingleton::currentInterpreter()->m_module->PyUnicode_FromWideChar(w, size); +} + +PyObject* PyImport_Import(PyObject *name) +{ + return PythonSingleton::currentInterpreter()->m_module->PyImport_Import(name); +} + +PyObject* PyBool_FromLong(long v) +{ + return PythonSingleton::currentInterpreter()->m_module->PyBool_FromLong(v); +} + +PyObject* Py_None() +{ + return PythonSingleton::currentInterpreter()->m_module->Py_None; +} + +PyObject* PyExc_SystemExit() +{ + return PythonSingleton::currentInterpreter()->m_module->PyExc_SystemExit; +} + +PyObject* PyType_Type() +{ + return PythonSingleton::currentInterpreter()->m_module->PyType_Type; +} + +void PyErr_Fetch(PyObject **ptype, PyObject **pvalue, PyObject **ptraceback) +{ + PythonSingleton::currentInterpreter()->m_module->PyErr_Fetch(ptype, pvalue, ptraceback); +} + + +void PyErr_NormalizeException(PyObject**exc, PyObject**val, PyObject**tb) +{ + PythonSingleton::currentInterpreter()->m_module->PyErr_NormalizeException(exc, val, tb); +} + +void PyErr_SetString(PyObject *type, const char *message) +{ + PythonSingleton::currentInterpreter()->m_module->PyErr_SetString(type, message); +} + +size_t PyList_Size(PyObject* list) +{ + return PythonSingleton::currentInterpreter()->m_module->PyList_Size(list); +} + +PyObject* PyList_GetItem(PyObject *list, size_t index) +{ + return PythonSingleton::currentInterpreter()->m_module->PyList_GetItem(list, index); +} + +PyObject* PyFile_FromString(char *filename, char *mode) +{ + return PythonSingleton::currentInterpreter()->m_module->PyFile_FromString(filename, mode); +} + +FILE* PyFile_AsFile(PyObject *pyfile) +{ + return PythonSingleton::currentInterpreter()->m_module->PyFile_AsFile(pyfile); +} + +PyObject* PyRun_File(FILE *fp, const char *filename, int start, PyObject *globals, PyObject *locals) +{ + return PythonSingleton::currentInterpreter()->m_module->PyRun_File(fp, filename, start, globals, locals); +} + +PyObject* __stdcall PyUnicode_FromString(const char* str) +{ + return PythonSingleton::currentInterpreter()->m_module->PyUnicode_FromString(str); +} + +PyObject* __stdcall PyInstanceMethod_New(PyObject *func) +{ + return PythonSingleton::currentInterpreter()->m_module->PyInstanceMethod_New(func); +} + +size_t __stdcall PyUnicode_AsWideChar(PyObject *unicode, wchar_t *w, size_t size) +{ + return PythonSingleton::currentInterpreter()->m_module->PyUnicode_AsWideChar(unicode, w, size); +} + +PyObject* __stdcall PyImport_ImportModule(const char *name) +{ + return PythonSingleton::currentInterpreter()->m_module->PyImport_ImportModule(name); +} + +PyThreadState* __stdcall PyEval_SaveThread() +{ + return PythonSingleton::currentInterpreter()->m_module->PyEval_SaveThread(); +} + +void __stdcall PyEval_RestoreThread(PyThreadState *tstate) +{ + PythonSingleton::currentInterpreter()->m_module->PyEval_RestoreThread(tstate); +} + +FILE* _Py_fopen(const char* filename, const char* mode) +{ + return PythonSingleton::currentInterpreter()->m_module->_Py_fopen(filename, mode); +} + +int __stdcall Py_AddPendingCall(int(*func)(void *), void *arg) +{ + return PythonSingleton::currentInterpreter()->m_module->Py_AddPendingCall(func, arg); +} + +PyGILState_STATE __stdcall PyGILState_Ensure() +{ + return PythonSingleton::currentInterpreter()->m_module->PyGILState_Ensure(); +} + +void __stdcall PyGILState_Release(PyGILState_STATE state) +{ + PythonSingleton::currentInterpreter()->m_module->PyGILState_Release(state); +} + +bool IsPy3() +{ + return PythonSingleton::currentInterpreter()->m_module->isPy3; +} + + diff --git a/pykd_ext/pyinterpret.h b/pykd_ext/pyinterpret.h new file mode 100644 index 0000000..08f772c --- /dev/null +++ b/pykd_ext/pyinterpret.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include "pymodule.h" + +class PythonInterpreter; + +PythonInterpreter* activateInterpreter(bool global = true, int majorVersion = -1, int minorVersion = -1); + +void releaseInterpretor(PythonInterpreter* interpret); + +class AutoInterpreter +{ +public: + + explicit AutoInterpreter(bool global = true, int majorVersion = -1, int minorVersion = -1) + { + m_interpreter = activateInterpreter(global, majorVersion, minorVersion); + } + + ~AutoInterpreter() + { + if (m_interpreter) + releaseInterpretor(m_interpreter); + } + +private: + + AutoInterpreter(const AutoInterpreter&) = delete; + + PythonInterpreter* m_interpreter; +}; + + +struct InterpreterDesc { + int majorVersion; + int minorVersion; + std::string imagePath; +}; + +std::list getInstalledInterpreter(); + + + diff --git a/pykd_ext/pykd_ext_vc120.vcxproj b/pykd_ext/pykd_ext_vc120.vcxproj new file mode 100644 index 0000000..c5fad86 --- /dev/null +++ b/pykd_ext/pykd_ext_vc120.vcxproj @@ -0,0 +1,229 @@ + + + + + Debug_2.7 + Win32 + + + Debug_2.7 + x64 + + + Release_2.7 + Win32 + + + Release_2.7 + x64 + + + + {583F9A6C-AF6D-45E0-A8F4-290D93611185} + Win32Proj + pykd_bootstrapper + pykd_ext_2.0 + ..\ + true + + + + DynamicLibrary + true + v120_xp + Unicode + + + DynamicLibrary + true + v120_xp + Unicode + + + DynamicLibrary + false + v120_xp + true + Unicode + + + DynamicLibrary + false + v120_xp + true + Unicode + + + + + + + + + + + + + + + + + + 7f4aed3e + + + true + $(ProjectName) + $(SolutionDir)out\$(Platform)\$(Configuration)\ + + + true + $(ProjectName) + $(SolutionDir)out\$(Platform)\$(Configuration)\ + + + false + $(ProjectName) + $(SolutionDir)out\$(Platform)\$(Configuration)\ + + + false + $(ProjectName) + $(SolutionDir)out\$(Platform)\$(Configuration)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;PYKD_BOOTSTRAPPER_EXPORTS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\kdlibcpp\include; + + + Windows + true + export.def + $(OutDir)pykd$(TargetExt) + comsuppw.lib;%(AdditionalDependencies) + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;PYKD_BOOTSTRAPPER_EXPORTS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\kdlibcpp\include; + + + Windows + true + export.def + $(OutDir)pykd$(TargetExt) + comsuppw.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;PYKD_BOOTSTRAPPER_EXPORTS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\kdlibcpp\include; + MultiThreaded + + + Windows + true + true + true + export.def + $(OutDir)pykd$(TargetExt) + comsuppw.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;PYKD_BOOTSTRAPPER_EXPORTS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\kdlibcpp\include; + MultiThreaded + + + Windows + true + true + true + export.def + $(OutDir)pykd$(TargetExt) + comsuppw.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + false + false + + + + + false + false + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/pykd_ext/pykd_ext_vc120.vcxproj.filters b/pykd_ext/pykd_ext_vc120.vcxproj.filters new file mode 100644 index 0000000..4873e94 --- /dev/null +++ b/pykd_ext/pykd_ext_vc120.vcxproj.filters @@ -0,0 +1,82 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/pykd_ext/pymodule.h b/pykd_ext/pymodule.h new file mode 100644 index 0000000..e0af157 --- /dev/null +++ b/pykd_ext/pymodule.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "pyapi.h" + + + + + diff --git a/pykd_ext/resource.h b/pykd_ext/resource.h new file mode 100644 index 0000000..c3bc570 --- /dev/null +++ b/pykd_ext/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by version.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/pykd_ext/stdafx.cpp b/pykd_ext/stdafx.cpp new file mode 100644 index 0000000..75c83a9 --- /dev/null +++ b/pykd_ext/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pykd_bootstrapper.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/pykd_ext/stdafx.h b/pykd_ext/stdafx.h new file mode 100644 index 0000000..a8b112f --- /dev/null +++ b/pykd_ext/stdafx.h @@ -0,0 +1,17 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + +#define BOOST_PYTHON_STATIC_LIB + +// TODO: reference additional headers your program requires here diff --git a/pykd_ext/targetver.h b/pykd_ext/targetver.h new file mode 100644 index 0000000..90e767b --- /dev/null +++ b/pykd_ext/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/pykd_ext/version.h b/pykd_ext/version.h new file mode 100644 index 0000000..e455f7a --- /dev/null +++ b/pykd_ext/version.h @@ -0,0 +1,15 @@ +#pragma once + +#define PYKDEXT_VERSION_MAJOR 2 +#define PYKDEXT_VERSION_MINOR 0 +#define PYKDEXT_VERSION_SUBVERSION 0 +#define PYKDEXT_VERSION_BUILDNO 1 + +#define __VER_STR2__(x) #x +#define __VER_STR1__(x) __VER_STR2__(x) + +#define PYKDEXT_VERSION_BUILD_COMMA PYKDEXT_VERSION_MAJOR, PYKDEXT_VERSION_MINOR, PYKDEXT_VERSION_SUBVERSION, PYKDEXT_VERSION_BUILDNO +#define PYKDEXT_VERSION_BUILD PYKDEXT_VERSION_MAJOR.PYKDEXT_VERSION_MINOR.PYKDEXT_VERSION_SUBVERSION.PYKDEXT_VERSION_BUILDNO + +#define PYKDEXT_VERSION_BUILD_STR_COMMA __VER_STR1__(PYKDEXT_VERSION_BUILD_COMMA) +#define PYKDEXT_VERSION_BUILD_STR __VER_STR1__(PYKDEXT_VERSION_BUILD) diff --git a/pykd_ext/version.rc b/pykd_ext/version.rc new file mode 100644 index 0000000..d21dc69 Binary files /dev/null and b/pykd_ext/version.rc differ diff --git a/pykd_ext/windbgext.cpp b/pykd_ext/windbgext.cpp new file mode 100644 index 0000000..f265a80 --- /dev/null +++ b/pykd_ext/windbgext.cpp @@ -0,0 +1,1204 @@ +#include "stdafx.h" + +#include +#include +#include + +#include + +#include "dbgout.h" +#include "arglist.h" +#include "pyinterpret.h" +#include "pyapi.h" +#include "pyclass.h" + + +////////////////////////////////////////////////////////////////////////////// + +void handleException(); +std::string getScriptFileName(const std::string &scriptName); +void getPythonVersion(int& majorVersion, int& minorVersion); + +////////////////////////////////////////////////////////////////////////////// + +class InterruptWatch +{ +public: + + InterruptWatch(PDEBUG_CLIENT client) + { + m_control = client; + m_stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + m_thread = CreateThread(NULL, 0, threadRoutine, this, 0, NULL); + } + + ~InterruptWatch() + { + SetEvent(m_stopEvent); + WaitForSingleObject(m_thread, INFINITE); + CloseHandle(m_stopEvent); + CloseHandle(m_thread); + } + + static int quit(void *context) + { + HANDLE quitEvent = (HANDLE)context; + PyErr_SetString(PyExc_SystemExit(), "CTRL+BREAK"); + SetEvent(quitEvent); + return -1; + } + +private: + + static DWORD WINAPI threadRoutine(LPVOID lpParameter) { + return static_cast(lpParameter)->interruptWatchRoutine(); + } + + DWORD InterruptWatch::interruptWatchRoutine() + { + while (WAIT_TIMEOUT == WaitForSingleObject(m_stopEvent, 250)) + { + HRESULT hres = m_control->GetInterrupt(); + if (hres == S_OK) + { + HANDLE quitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + PyGILState_STATE state = PyGILState_Ensure(); + Py_AddPendingCall(&quit, (void*)quitEvent); + PyGILState_Release(state); + WaitForSingleObject(quitEvent, INFINITE); + CloseHandle(quitEvent); + } + } + + return 0; + } + + HANDLE m_thread; + + HANDLE m_stopEvent; + + CComQIPtr m_control; +}; + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +HRESULT +CALLBACK +DebugExtensionInitialize( + PULONG Version, + PULONG Flags +) +{ + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +VOID +CALLBACK +DebugExtensionUninitialize() +{ +} + +////////////////////////////////////////////////////////////////////////////// + +std::string make_version(int major, int minor) +{ + std::stringstream sstr; + sstr << std::dec << major << '.' << minor; + return sstr.str(); +} + +extern "C" +HRESULT +CALLBACK +info( + PDEBUG_CLIENT client, + PCSTR args +) +{ + std::list interpreterList = getInstalledInterpreter(); + std::stringstream sstr; + + sstr << std::endl << "Installed python" << std::endl << std::endl; + sstr << std::setw(12) << std::left << "Version:" << std::left << "Image:" << std::endl; + sstr << "------------------------------------------------------------------------------" << std::endl; + if (interpreterList.size() > 0) + { + for (const InterpreterDesc& desc : interpreterList) + { + sstr << std::setw(12) << std::left << make_version(desc.majorVersion, desc.minorVersion); + sstr << desc.imagePath << std::endl; + } + } + else + { + sstr << "No python interpreter found" << std::endl; + } + + sstr << std::endl; + + CComQIPtr control = client; + + control->ControlledOutput( + DEBUG_OUTCTL_THIS_CLIENT, + DEBUG_OUTPUT_NORMAL, + sstr.str().c_str() + ); + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// + +static const char printUsageMsg[] = + "usage:\n" + "!py [options] [file]\n" + "\tOptions:\n" + "\t-g --global : run code in the common namespace\n" + "\t-l --local : run code in the isolate namespace\n" + "!install\n" + "!upgrade\n"; + +extern "C" +HRESULT +CALLBACK +py( + PDEBUG_CLIENT client, + PCSTR args +) +{ + ULONG oldMask; + client->GetOutputMask(&oldMask); + client->SetOutputMask(DEBUG_OUTPUT_NORMAL | DEBUG_OUTPUT_ERROR); + + try { + + Options opts(getArgsList(args)); + + if (opts.showHelp) + throw std::exception(printUsageMsg); + + int majorVersion = opts.pyMajorVersion; + int minorVersion = opts.pyMinorVersion; + + getPythonVersion(majorVersion, minorVersion); + + AutoInterpreter autoInterpreter(opts.global, majorVersion, minorVersion); + + PyObjectRef dbgOut = make_pyobject(client); + PySys_SetObject("stdout", dbgOut); + + PyObjectRef dbgErr = make_pyobject(client); + PySys_SetObject("stderr", dbgErr); + + PyObjectRef dbgIn = make_pyobject(client); + PySys_SetObject("stdin", dbgIn); + + PyObjectRef mainName = IsPy3() ? PyUnicode_FromString("__main__") : PyString_FromString("__main__"); + PyObjectRef mainMod = PyImport_Import(mainName); + PyObjectRef globals = PyObject_GetAttrString(mainMod, "__dict__"); + + InterruptWatch interruptWatch(client); + + if (opts.args.empty()) + { + PyObjectRef result = PyRun_String("__import__('code').InteractiveConsole(__import__('__main__').__dict__).interact()\n", Py_file_input, globals, globals); + } + else + { + std::string scriptFileName = getScriptFileName(opts.args[0]); + if (scriptFileName.empty()) + throw std::invalid_argument("script not found\n"); + + if (IsPy3()) + { + // óñòàíàâèëèâàåì ïèòîíîâñêèå àðãóìåíòû + std::vector pythonArgs(opts.args.size()); + + std::wstring scriptFileNameW = _bstr_t(scriptFileName.c_str()); + + pythonArgs[0] = const_cast(scriptFileNameW.c_str()); + + for (size_t i = 1; i < opts.args.size(); ++i) + { + std::wstring argw = _bstr_t(opts.args[i].c_str()); + pythonArgs[i] = const_cast(argw.c_str()); + } + + PySys_SetArgv_Py3((int)opts.args.size(), &pythonArgs[0]); + + FILE* fs = _Py_fopen(scriptFileName.c_str(), "r"); + if ( !fs ) + throw std::invalid_argument("script not found\n"); + + PyObjectRef result = PyRun_File(fs, scriptFileName.c_str(), Py_file_input, globals, globals); + } + else + { + std::vector pythonArgs(opts.args.size()); + + pythonArgs[0] = const_cast(scriptFileName.c_str()); + + for (size_t i = 1; i < opts.args.size(); ++i) + pythonArgs[i] = const_cast(opts.args[i].c_str()); + + PySys_SetArgv((int)opts.args.size(), &pythonArgs[0]); + + PyObjectRef pyfile = PyFile_FromString(pythonArgs[0], "r"); + if (!pyfile) + throw std::invalid_argument("script not found\n"); + + FILE *fs = PyFile_AsFile(pyfile); + + PyObjectRef result = PyRun_File(fs, scriptFileName.c_str(), Py_file_input, globals, globals); + } + } + + handleException(); + } + catch (std::exception &e) + { + CComQIPtr control = client; + + control->ControlledOutput( + DEBUG_OUTCTL_THIS_CLIENT, + DEBUG_OUTPUT_ERROR, + e.what() + ); + } + + client->SetOutputMask(oldMask); + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +HRESULT +CALLBACK +pip( + PDEBUG_CLIENT client, + PCSTR args +) +{ + + return S_OK; + +} + + +////////////////////////////////////////////////////////////////////////////// + + +void handleException() +{ + // îøèáêà â ñêðèïòå + PyObject *errtype = NULL, *errvalue = NULL, *traceback = NULL; + + PyErr_Fetch(&errtype, &errvalue, &traceback); + + PyErr_NormalizeException(&errtype, &errvalue, &traceback); + + if (errtype != PyExc_SystemExit() ) + { + PyObjectRef traceback_module = PyImport_ImportModule("traceback"); + + std::stringstream sstr; + + PyObjectRef format_exception = PyObject_GetAttrString(traceback_module, "format_exception"); + + PyObjectRef args = PyTuple_New(3); + PyTuple_SetItem(args, 0, errtype ? errtype : Py_None()); + PyTuple_SetItem(args, 1, errvalue ? errvalue : Py_None()); + PyTuple_SetItem(args, 2, traceback ? traceback : Py_None()); + + PyObjectRef lst = PyObject_Call(format_exception, args, NULL); + + sstr << std::endl << std::endl; + + for (size_t i = 0; i < PyList_Size(lst); ++i) + { + PyObjectBorrowedRef item = PyList_GetItem(lst, i); + sstr << std::string(convert_from_python(item)) << std::endl; + } + + throw std::exception(sstr.str().c_str()); + } + + Py_DecRef(errtype); + if (errvalue) Py_DecRef(errvalue); + if (traceback) Py_DecRef(traceback); +} + +/////////////////////////////////////////////////////////////////////////////// + +std::string findScript(const std::string &fullFileName) +{ + if (GetFileAttributesA(fullFileName.c_str()) != INVALID_FILE_ATTRIBUTES) + return fullFileName; + + PyObjectBorrowedRef pathLst = PySys_GetObject("path"); + + size_t pathLstSize = PyList_Size(pathLst); + + for (size_t i = 0; i < pathLstSize; i++) + { + char *path = PyString_AsString(PyList_GetItem(pathLst, i)); + + DWORD bufSize = SearchPathA( + path, + fullFileName.c_str(), + NULL, + 0, + NULL, + NULL); + + if (bufSize > 0) + { + bufSize += 1; + std::vector fullFileNameCStr(bufSize); + char *partFileNameCStr = NULL; + + bufSize = SearchPathA( + path, + fullFileName.c_str(), + NULL, + bufSize, + &fullFileNameCStr[0], + &partFileNameCStr); + + DWORD fileAttr = GetFileAttributesA(&fullFileNameCStr[0]); + + if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0) + return std::string(&fullFileNameCStr[0]); + } + + } + + return ""; +} + +/////////////////////////////////////////////////////////////////////////////// + +std::string getScriptFileName(const std::string &scriptName) +{ + std::string scriptFileName = findScript(scriptName); + + if (scriptFileName.empty()) + { + std::string scriptNameLow; + scriptNameLow.resize(scriptName.size()); + std::transform( + scriptName.begin(), + scriptName.end(), + scriptNameLow.begin(), + ::tolower); + if (scriptNameLow.rfind(".py") != (scriptNameLow.length() - 3)) + scriptFileName = findScript(scriptName + ".py"); + } + + return scriptFileName; +} + +/////////////////////////////////////////////////////////////////////////////// + +void getPythonVersion(int& majorVersion, int& minorVersion) +{ + std::list interpreterList = getInstalledInterpreter(); + + bool found = false; + bool anyMinorVersion = minorVersion == -1; + for (auto interpret : interpreterList) + { + if (majorVersion == interpret.majorVersion && + anyMinorVersion ? (minorVersion <= interpret.minorVersion) : (minorVersion == interpret.minorVersion)) + { + found = true; + minorVersion = interpret.minorVersion; + } + } + + if (!found) + throw std::exception("failed to find python interpreter\n"); +} + +/////////////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + + + + + + + +/* +#include + +#include +#include +#include + +#include +namespace python = boost::python; + +#include + +/////////////////////////////////////////////////////////////////////////////// + +class AutoRestorePyState +{ +public: + + AutoRestorePyState() + { + m_state = PyEval_SaveThread(); + } + + ~AutoRestorePyState() + { + PyEval_RestoreThread(m_state); + } + +private: + + PyThreadState* m_state; +}; + +////////////////////////////////////////////////////////////////////////////// + +class DbgOut +{ +public: + + DbgOut(PDEBUG_CLIENT client) + : m_control(client) + {} + + void write(const std::wstring& str) + { + AutoRestorePyState pystate; + + m_control->ControlledOutputWide( + DEBUG_OUTCTL_THIS_CLIENT, + DEBUG_OUTPUT_NORMAL, + L"%ws", + str.c_str() + ); + + } + + void writedml(const std::wstring& str) + { + AutoRestorePyState pystate; + + m_control->ControlledOutputWide( + DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_DML, + DEBUG_OUTPUT_NORMAL, + L"%ws", + str.c_str() + ); + } + + void flush() { + } + + std::wstring encoding() { + return L"ascii"; + } + + bool closed() { + return false; + } + +private: + + CComQIPtr m_control; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class DbgIn +{ +public: + + DbgIn(PDEBUG_CLIENT client) + : m_control(client) + {} + + std::wstring readline() + { + AutoRestorePyState pystate; + + std::vector inputBuffer(0x10000); + + ULONG read = 0; + m_control->InputWide(&inputBuffer[0], static_cast(inputBuffer.size()), &read); + + std::wstring inputstr = std::wstring(&inputBuffer[0]); + + return inputstr.empty() ? L"\n" : inputstr; + } + + bool closed() { + return false; + } + + +private: + + CComQIPtr m_control; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class PythonInterpreter +{ +public: + + PythonInterpreter() + { + PyThreadState* state = Py_NewInterpreter(); + + PyThreadState_Swap(state); + + m_state = PyEval_SaveThread(); + } + + ~PythonInterpreter() + { + PyEval_RestoreThread(m_state); + + PyInterpreterState *interpreter = m_state->interp; + + while (interpreter->tstate_head != NULL) + { + PyThreadState *threadState = (PyThreadState*)(interpreter->tstate_head); + + PyThreadState_Clear(threadState); + + PyThreadState_Swap(NULL); + + PyThreadState_Delete(threadState); + } + + PyInterpreterState_Clear(interpreter); + + PyInterpreterState_Delete(interpreter); + } + + void acivate() + { + PyEval_RestoreThread(m_state); + } + + void deactivate() + { + m_state = PyEval_SaveThread(); + } + +private: + + PyThreadState* m_state; + +}; + +/////////////////////////////////////////////////////////////////////////////// + +class PythonSingleton +{ + +public: + + static PythonSingleton* get() + { + if (!m_instance) + m_instance = new PythonSingleton(); + + return m_instance; + } + + void stop() + { + delete m_globalInterpreter; + m_globalInterpreter = 0; + PyThreadState_Swap(m_globalState); + m_globalState = PyEval_SaveThread(); + } + + void start() + { + PyEval_RestoreThread(m_globalState); + + m_globalInterpreter = new PythonInterpreter(); + + //m_globalInterpreter->acivate(); + + //python::object main = boost::python::import("__main__"); + + //python::object main_namespace = main.attr("__dict__"); + + //m_globalInterpreter->deactivate(); + } + + void acivateGlobal() { + m_globalInterpreter->acivate(); + } + + void deactivateGlobal() { + m_globalInterpreter->deactivate(); + } + + void acivateLocal() { + PyEval_RestoreThread(m_globalState); + m_locallInterpreter = new PythonInterpreter(); + m_locallInterpreter->acivate(); + } + + void deactivateLocal() { + m_locallInterpreter->deactivate(); + delete m_locallInterpreter; + m_locallInterpreter = 0; + PyThreadState_Swap(m_globalState); + m_globalState = PyEval_SaveThread(); + } + + void checkPykd() + { + if (m_pykdInit) + return; + + python::handle<> pykdHandle(python::allow_null(PyImport_ImportModule("pykd"))); + if (!pykdHandle) + throw std::exception("Pykd package is not installed.You can install it by command \"!pykd.install\""); + + python::object main = python::import("__main__"); + python::object globalScope(main.attr("__dict__")); + python::exec("__import__('pykd').initialize()", globalScope); + + m_pykdInit = true; + } + +private: + + static PythonSingleton* m_instance; + + PythonSingleton() + { + Py_Initialize(); + PyEval_InitThreads(); + + // Python debug output console helper classes + python::class_("dout", "dout", python::no_init) + .def("write", &DbgOut::write) + .def("writedml", &DbgOut::writedml) + .def("flush", &DbgOut::flush) + .add_property("encoding", &DbgOut::encoding) + .add_property("closed", &DbgOut::closed); + + python::class_("din", "din", python::no_init) + .def("readline", &DbgIn::readline) + .add_property("closed", &DbgIn::closed); + + + m_globalState = PyEval_SaveThread(); + } + + PythonInterpreter* m_globalInterpreter; + PythonInterpreter* m_locallInterpreter; + PyThreadState* m_globalState; + + bool m_pykdInit; +}; + +PythonSingleton* PythonSingleton::m_instance = 0; + +////////////////////////////////////////////////////////////////////////////// + +class InterruptWatch +{ +public: + + InterruptWatch(PDEBUG_CLIENT client) + { + m_control = client; + m_stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + m_thread = CreateThread(NULL, 0, threadRoutine, this, 0, NULL); + } + + ~InterruptWatch() + { + SetEvent(m_stopEvent); + WaitForSingleObject(m_thread, INFINITE); + CloseHandle(m_stopEvent); + CloseHandle(m_thread); + } + + bool onInterrupt() + { + HANDLE quitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + PyGILState_STATE state = PyGILState_Ensure(); + Py_AddPendingCall(&quit, (void*)quitEvent); + PyGILState_Release(state); + WaitForSingleObject(quitEvent, INFINITE); + CloseHandle(quitEvent); + return true; + } + + static int quit(void *context) + { + HANDLE quitEvent = (HANDLE)context; + PyErr_SetString(PyExc_SystemExit, "CTRL+BREAK"); + SetEvent(quitEvent); + return -1; + } + +private: + + static DWORD WINAPI threadRoutine(LPVOID lpParameter) { + return static_cast(lpParameter)->interruptWatchRoutine(); + } + + DWORD InterruptWatch::interruptWatchRoutine() + { + while (WAIT_TIMEOUT == WaitForSingleObject(m_stopEvent, 250)) + { + HRESULT hres = m_control->GetInterrupt(); + if (hres == S_OK) + { + HANDLE quitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + PyGILState_STATE state = PyGILState_Ensure(); + Py_AddPendingCall(&quit, (void*)quitEvent); + PyGILState_Release(state); + WaitForSingleObject(quitEvent, INFINITE); + CloseHandle(quitEvent); + } + } + + return 0; + } + + HANDLE m_thread; + + HANDLE m_stopEvent; + + CComQIPtr m_control; +}; + +////////////////////////////////////////////////////////////////////////////// + +typedef std::vector< std::string > ArgsList; +typedef boost::escaped_list_separator char_separator_t; +typedef boost::tokenizer< char_separator_t > char_tokenizer_t; + +ArgsList getArgsList( + PCSTR args + ) +{ + std::string argsStr(args); + + char_tokenizer_t token(argsStr, char_separator_t("", " \t", "\"")); + ArgsList argsList; + + for (char_tokenizer_t::iterator it = token.begin(); it != token.end(); ++it) + { + if (*it != "") + argsList.push_back(*it); + } + + return argsList; +} + +////////////////////////////////////////////////////////////////////////////// + +static const char printUsageMsg[] = + "usage:\n" + "!py [options] [file]\n" + "\tOptions:\n" + "\t-g --global : run code in the common namespace\n" + "\t-l --local : run code in the isolate namespace\n" + "!install\n" + "!upgrade\n"; + +void printUsage(PDEBUG_CLIENT client) +{ + CComQIPtr(client)-> + ControlledOutput( + DEBUG_OUTCTL_THIS_CLIENT, + DEBUG_OUTPUT_NORMAL, + "%s", + printUsageMsg + ); +} + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +HRESULT +CALLBACK +DebugExtensionInitialize( + PULONG Version, + PULONG Flags + ) +{ + PythonSingleton::get()->start(); + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +VOID +CALLBACK +DebugExtensionUninitialize() +{ + PythonSingleton::get()->stop(); +} + +////////////////////////////////////////////////////////////////////////////// + +std::string getScriptFileName(const std::string &scriptName); + +void printException(DbgOut &dbgOut); + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +HRESULT +CALLBACK +py( + PDEBUG_CLIENT client, + PCSTR args + ) +{ + ArgsList argsList = getArgsList(args); + + bool global = false; + bool local = false; + bool clean = false; + + if (!argsList.empty()) + { + if (argsList[0] == "-h" || argsList[0] == "--help") + { + printUsage(client); + return S_OK; + } + else + if (argsList[0] == "-g" || argsList[0] == "--global") + { + global = true; + argsList.erase(argsList.begin()); + } + else + if (argsList[0] == "-l" || argsList[0] == "--local") + { + local = true; + argsList.erase(argsList.begin()); + } + } + + if (argsList.size() > 0) + { + global = !(global || local) ? false : global; //set local by default + } + else + { + global = !(global || local) ? true : global; //set global by default + } + + DbgOut dbgOut(client); + DbgOut dbgErr(client); + DbgIn dbgIn(client); + + ULONG oldMask; + client->GetOutputMask(&oldMask); + client->SetOutputMask(DEBUG_OUTPUT_NORMAL | DEBUG_OUTPUT_ERROR); + + + if (global) + PythonSingleton::get()->acivateGlobal(); + else + PythonSingleton::get()->acivateLocal(); + + try { + + InterruptWatch interruptWatch(client); + + python::object sys = python::import("sys"); + + sys.attr("stdout") = python::object(dbgOut); + sys.attr("stderr") = python::object(dbgErr); + sys.attr("stdin") = python::object(dbgIn); + + python::object main = python::import("__main__"); + python::object globalScope(main.attr("__dict__")); + + PythonSingleton::get()->checkPykd(); + + if (argsList.size() == 0) + { + + python::exec("import pykd", globalScope); + python::exec("from pykd import *", globalScope); + python::exec("__import__('code').InteractiveConsole(__import__('__main__').__dict__).interact()", globalScope); + } + else + { + + std::string scriptFileName = getScriptFileName(argsList[0]); + if (scriptFileName.empty()) + throw std::invalid_argument("script not found"); + + // óñòàíàâèëèâàåì ïèòîíîâñêèå àðãóìåíòû + char **pythonArgs = new char*[argsList.size()]; + + pythonArgs[0] = const_cast(scriptFileName.c_str()); + + for (size_t i = 1; i < argsList.size(); ++i) + pythonArgs[i] = const_cast(argsList[i].c_str()); + + PySys_SetArgv((int)argsList.size(), pythonArgs); + + delete[] pythonArgs; + + python::exec_file(scriptFileName.c_str(), globalScope); + } + } + catch (const python::error_already_set&) + { + printException(dbgOut); + } + catch (const std::exception& invalidArg) + { + _bstr_t bstrInavalidArg(invalidArg.what()); + dbgOut.write(std::wstring(bstrInavalidArg)); + } + + if (global) + PythonSingleton::get()->deactivateGlobal(); + else + PythonSingleton::get()->deactivateLocal(); + + client->SetOutputMask(oldMask); + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +HRESULT +CALLBACK +install( + PDEBUG_CLIENT client, + PCSTR args + ) +{ + DbgOut dbgOut(client); + DbgOut dbgErr(client); + DbgIn dbgIn(client); + + PythonSingleton::get()->acivateGlobal(); + + try { + + python::object sys = python::import("sys"); + + sys.attr("stdout") = python::object(dbgOut); + sys.attr("stderr") = python::object(dbgErr); + sys.attr("stdin") = python::object(dbgIn); + + // ïîëó÷àåì äîñòóï ê ãëîáàëüíîìó ìàïó ( íóæåí äëÿ âûçîâà exec_file ) + python::object main = python::import("__main__"); + python::object global(main.attr("__dict__")); + + python::exec("import pip\n", global); + python::exec("pip.logger.consumers = []\n", global); + python::exec("pip.main(['install', 'pykd'])\n", global); + + } + catch (const python::error_already_set&) + { + printException(dbgOut); + } + catch (const std::exception& invalidArg) + { + _bstr_t bstrInavalidArg(invalidArg.what()); + dbgOut.write(std::wstring(bstrInavalidArg)); + } + + PythonSingleton::get()->deactivateGlobal(); + + return S_OK; +} + +////////////////////////////////////////////////////////////////////////////// + +extern "C" +HRESULT +CALLBACK +upgrade( + PDEBUG_CLIENT client, + PCSTR args + ) +{ + DbgOut dbgOut(client); + DbgOut dbgErr(client); + DbgIn dbgIn(client); + + PythonSingleton::get()->acivateGlobal(); + + try { + + python::object sys = python::import("sys"); + + sys.attr("stdout") = python::object(dbgOut); + sys.attr("stderr") = python::object(dbgErr); + sys.attr("stdin") = python::object(dbgIn); + + // ïîëó÷àåì äîñòóï ê ãëîáàëüíîìó ìàïó ( íóæåí äëÿ âûçîâà exec_file ) + python::object main = python::import("__main__"); + python::object global(main.attr("__dict__")); + + python::exec("import pip\n", global); + python::exec("pip.logger.consumers = []\n", global); + python::exec("pip.main(['install', '--upgrade', 'pykd'])\n", global); + + } + catch (const python::error_already_set&) + { + printException(dbgOut); + } + catch (const std::exception& invalidArg) + { + _bstr_t bstrInavalidArg(invalidArg.what()); + dbgOut.write(std::wstring(bstrInavalidArg)); + } + + PythonSingleton::get()->deactivateGlobal(); + + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +std::string findScript(const std::string &fullFileName) +{ + if (GetFileAttributesA(fullFileName.c_str()) != INVALID_FILE_ATTRIBUTES) + return fullFileName; + + python::object sys = python::import("sys"); + + python::list pathList(sys.attr("path")); + + python::ssize_t n = python::len(pathList); + + for (python::ssize_t i = 0; i < n; i++) + { + std::string path = boost::python::extract(pathList[i]); + + DWORD bufSize = SearchPathA( + path.c_str(), + fullFileName.c_str(), + NULL, + 0, + NULL, + NULL); + + if (bufSize > 0) + { + bufSize += 1; + std::vector fullFileNameCStr(bufSize); + char *partFileNameCStr = NULL; + + bufSize = SearchPathA( + path.c_str(), + fullFileName.c_str(), + NULL, + bufSize, + &fullFileNameCStr[0], + &partFileNameCStr); + + DWORD fileAttr = GetFileAttributesA(&fullFileNameCStr[0]); + + if ((fileAttr & FILE_ATTRIBUTE_DIRECTORY) == 0) + return std::string(&fullFileNameCStr[0]); + } + } + + return ""; +} + +/////////////////////////////////////////////////////////////////////////////// + +std::string getScriptFileName(const std::string &scriptName) +{ + std::string scriptFileName = findScript(scriptName); + + if (scriptFileName.empty()) + { + std::string scriptNameLow; + scriptNameLow.resize(scriptName.size()); + std::transform( + scriptName.begin(), + scriptName.end(), + scriptNameLow.begin(), + ::tolower); + if (scriptNameLow.rfind(".py") != (scriptNameLow.length() - 3)) + scriptFileName = findScript(scriptName + ".py"); + } + + return scriptFileName; +} + +/////////////////////////////////////////////////////////////////////////////// + +void printException(DbgOut &dbgOut) +{ + // îøèáêà â ñêðèïòå + PyObject *errtype = NULL, *errvalue = NULL, *traceback = NULL; + + PyErr_Fetch(&errtype, &errvalue, &traceback); + + PyErr_NormalizeException(&errtype, &errvalue, &traceback); + + if (errtype != PyExc_SystemExit) + { + python::object tracebackModule = python::import("traceback"); + + std::wstringstream sstr; + + python::object lst = + python::object(tracebackModule.attr("format_exception"))( + python::handle<>(errtype), + python::handle<>(python::allow_null(errvalue)), + python::handle<>(python::allow_null(traceback))); + + sstr << std::endl << std::endl; + + for (long i = 0; i < python::len(lst); ++i) + sstr << std::wstring(python::extract(lst[i])) << std::endl; + + dbgOut.write(sstr.str()); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +*/