Commit ec338a68 authored by Tom Zickel's avatar Tom Zickel Committed by Luke Macken

Initial windows support

parent e51c1e71
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
import os import os
import subprocess import subprocess
import platform
def inject(pid, filename, verbose=False, gdb_prefix=''): def inject(pid, filename, verbose=False, gdb_prefix=''):
"""Executes a file in a running Python process.""" """Executes a file in a running Python process."""
...@@ -39,3 +40,20 @@ def inject(pid, filename, verbose=False, gdb_prefix=''): ...@@ -39,3 +40,20 @@ def inject(pid, filename, verbose=False, gdb_prefix=''):
if verbose: if verbose:
print(out) print(out)
print(err) print(err)
if platform.system() == 'Windows':
def inject_win(pid, filename, verbose=False, gdb_prefix=''):
if gdb_prefix == '':
gdb_prefix = os.path.join(os.path.dirname(__file__), 'win') + os.sep
filename = os.path.abspath(filename)
code = 'import sys; sys.path.insert(0, \\"%s\\"); sys.path.insert(0, \\"%s\\"); exec(open(\\"%s\\").read())' % (os.path.dirname(filename).replace('\\', '/'), os.path.abspath(os.path.join(os.path.dirname(__file__), '..')).replace('\\', '/'), filename.replace('\\', '/'))
p = subprocess.Popen('%sinject_python_32.exe %d \"%s\"' % (gdb_prefix, pid, code), shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
out, err = p.communicate()
if p.wait() == 25:
p = subprocess.Popen('%sinject_python_64.exe %d \"%s\"' % (gdb_prefix, pid, code), shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
out, err = p.communicate()
if verbose:
print(out)
print(err)
inject = inject_win
\ No newline at end of file
...@@ -25,6 +25,7 @@ import socket ...@@ -25,6 +25,7 @@ import socket
import struct import struct
import tempfile import tempfile
import subprocess import subprocess
import platform
from os.path import dirname, abspath, join from os.path import dirname, abspath, join
...@@ -81,6 +82,15 @@ class PyrasiteIPC(object): ...@@ -81,6 +82,15 @@ class PyrasiteIPC(object):
@property @property
def title(self): def title(self):
if not getattr(self, '_title', None): if not getattr(self, '_title', None):
if platform.system() == 'Windows':
p = subprocess.Popen('tasklist /v /fi "pid eq %d" /nh /fo csv' % self.pid,
stdout=subprocess.PIPE, shell=True)
tmp = p.communicate()[0].decode('utf-8').strip().split(',')
if tmp[-1] == '"N/A"':
self._title = tmp[0][1:-1]
else:
self._title = tmp[-1][1:-1]
else:
p = subprocess.Popen('ps --no-heading -o cmd= -p %d' % self.pid, p = subprocess.Popen('ps --no-heading -o cmd= -p %d' % self.pid,
stdout=subprocess.PIPE, shell=True) stdout=subprocess.PIPE, shell=True)
self._title = p.communicate()[0].decode('utf-8') self._title = p.communicate()[0].decode('utf-8')
...@@ -139,6 +149,7 @@ class PyrasiteIPC(object): ...@@ -139,6 +149,7 @@ class PyrasiteIPC(object):
tmp.close() tmp.close()
payload.close() payload.close()
if platform.system() != 'Windows':
os.chmod(filename, stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH) os.chmod(filename, stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH)
return filename return filename
......
...@@ -5,5 +5,11 @@ ...@@ -5,5 +5,11 @@
# https://launchpad.net/meliae # https://launchpad.net/meliae
# http://jam-bazaar.blogspot.com/2009/11/memory-debugging-with-meliae.html # http://jam-bazaar.blogspot.com/2009/11/memory-debugging-with-meliae.html
import os, meliae.scanner import os, meliae.scanner, platform
meliae.scanner.dump_all_objects('/tmp/pyrasite-%d-objects.json' % os.getpid())
if platform.system() == 'Windows':
temp = os.getenv('TEMP', os.getenv('TMP', '/temp'))
path = os.path.join(temp, 'pyrasite-%d-objects.json' % os.getpid())
else:
path = '/tmp/pyrasite-%d-objects.json' % os.getpid()
meliae.scanner.dump_all_objects(path)
#include <stdio.h>
#include <Windows.h>
//TODO
// support both ansi and unicode in the code (path for injection might have non english letters), how does python handle unicode in PyRun_SimpleString ?
// i checked on windows 7 64bit running 32bit python 2.7, check on other windows versions, and other python versions
// try maybe to write a code to auto figure out in which modules the Python functions are, maybe with EnumProcessModules
// do i need to statically compile the runtime libraries here ? (compile with VS2008 which is python 2.7 runtime maybe?)
// check the 64bit process injection especially in the context of ASLR
// in python 64 bit building, does disttools choose the 64 bit compiler?
// if python extension restore previous privilieges after running ?
// set only the privilieges needed for what i want to do..
// itanium support?
// Can be compiled in visual studio command prompt with: cl.exe inject_python.cpp
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "advapi32.lib")
int RaisePrivileges()
{
int retCode = 0;
HANDLE hToken;
TOKEN_PRIVILEGES tp;
TOKEN_PRIVILEGES oldtp;
DWORD dwSize = sizeof(TOKEN_PRIVILEGES);
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
retCode = 1;
goto error1;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
{
retCode = 2;
goto error2;
}
ZeroMemory(&tp, sizeof(tp));
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize))
{
retCode = 3;
goto error2;
}
error2:
CloseHandle(hToken);
error1:
return retCode;
}
typedef struct
{
HMODULE (__stdcall *pGetModuleHandle)(LPCSTR);
FARPROC (__stdcall *pGetProcAddress)(HMODULE, LPCSTR);
char ModuleName[9];
char PyGILState_Ensure[18];
char PyRun_SimpleString[19];
char PyGILState_Release[19];
char *Code;
} REMOTEDATA;
static DWORD WINAPI ExecutePythonCode(REMOTEDATA *data)
{
DWORD retCode = 0;
HMODULE hModule = data->pGetModuleHandle(data->ModuleName);
if (hModule != NULL)
{
int (__cdecl *a)() = reinterpret_cast<int (__cdecl *)()>(data->pGetProcAddress(hModule, data->PyGILState_Ensure));
if (a != NULL)
{
int ret = a();
void (__cdecl *b)(char *) = reinterpret_cast<void (__cdecl *)(char *)>(data->pGetProcAddress(hModule, data->PyRun_SimpleString));
if (b != NULL)
{
b(data->Code);
} else {
retCode = 3;
}
void (__cdecl *c)(int) = reinterpret_cast<void (__cdecl *)(int)>(data->pGetProcAddress(hModule, data->PyGILState_Release));
if (c != NULL)
{
c(ret);
} else {
retCode = 4;
}
} else {
retCode = 2;
}
} else {
retCode = 1;
}
return retCode;
}
static void AfterExecutePythonCode()
{
}
int InjectPythonCode(HANDLE hProcess, const char *code, char *moduleName)
{
int retCode = 0;
REMOTEDATA data;
int cbCodeSize = (PBYTE)AfterExecutePythonCode - (PBYTE)ExecutePythonCode;
void* remoteCodeString = VirtualAllocEx(hProcess, NULL, strlen(code) + 1, MEM_COMMIT, PAGE_READWRITE);
if (remoteCodeString == NULL)
{
retCode = 1;
goto error1;
}
void* remoteCode = VirtualAllocEx(hProcess, NULL, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE);
if (remoteCode == NULL)
{
retCode = 2;
goto error2;
}
void* remoteData = VirtualAllocEx(hProcess, NULL, sizeof(data), MEM_COMMIT, PAGE_READWRITE);
if (remoteData == NULL)
{
retCode = 3;
goto error3;
}
if (!WriteProcessMemory(hProcess, remoteCodeString, (void*)code, strlen(code) + 1, NULL))
{
retCode = 4;
goto error3;
}
data.pGetModuleHandle = GetModuleHandle;
data.pGetProcAddress = GetProcAddress;
strcpy_s(data.ModuleName, moduleName);
strcpy_s(data.PyGILState_Ensure, "PyGILState_Ensure");
strcpy_s(data.PyRun_SimpleString, "PyRun_SimpleString");
strcpy_s(data.PyGILState_Release, "PyGILState_Release");
data.Code = (char *)remoteCodeString;
if (!WriteProcessMemory(hProcess, remoteData, (void*)&data, sizeof(data), NULL))
{
retCode = 5;
goto error3;
}
if (!WriteProcessMemory(hProcess, remoteCode, (void*)ExecutePythonCode, cbCodeSize, NULL))
{
retCode = 6;
goto error3;
}
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteCode, remoteData, 0, NULL);
if (!hRemoteThread)
{
retCode = 7;
goto error3;
}
if (WaitForSingleObject(hRemoteThread, INFINITE) == WAIT_FAILED)
{
retCode = 8;
goto error4;
}
DWORD exitCode;
if (!GetExitCodeThread(hRemoteThread, &exitCode))
{
retCode = 9;
goto error4;
}
if (exitCode != 0)
{
retCode = 10;
goto error4;
}
error4:
CloseHandle(hRemoteThread);
error3:
VirtualFreeEx(hProcess, remoteData, sizeof(data), MEM_RELEASE);
error2:
VirtualFreeEx(hProcess, remoteCode, cbCodeSize, MEM_RELEASE);
error1:
VirtualFreeEx(hProcess, remoteCodeString, strlen(code) + 1, MEM_RELEASE);
return retCode;
}
int InjectPythonCodeToPID(DWORD pid, const char *code)
{
char versions[][9] = { "Python34", "Python33", "Python32", "Python31", "Python30", "Python27", "Python26", "Python25", "Python24" };
unsigned int numVersions = 9;
unsigned int i;
int retCode = 0;
int ret;
BOOL is32Bit;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
if (!hProcess)
{
retCode = 1;
goto error1;
}
//TODO this requires windows xp an above, later check if the function exists first...
if (!IsWow64Process(hProcess, &is32Bit))
{
retCode = 2;
goto error2;
}
#ifdef _WIN64
if (is32Bit)
{
retCode = 5;
goto error2;
}
#else
BOOL amI32Bit;
IsWow64Process(GetCurrentProcess(), &amI32Bit);
if (amI32Bit && !is32Bit)
{
retCode = 5;
goto error2;
}
#endif
for (i = 0; i < numVersions; ++i)
{
ret = InjectPythonCode(hProcess, code, versions[i]);
if (ret == 0)
{
break;
}
if (ret != 10)
{
retCode = 3;
goto error2;
}
}
if (ret != 0)
{
retCode = 4;
goto error2;
}
error2:
CloseHandle(hProcess);
error1:
return retCode;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
fprintf(stderr, "Usage: %s <pid> <code>\n", argv[0]);
return 1;
}
DWORD pid = atoi(argv[1]);
char* code = argv[2];
int ret;
ret = RaisePrivileges();
if (ret)
{
fprintf(stderr, "Could not raise privileges, return value %d, error code %d\n", ret, GetLastError());
return 10 + ret;
}
ret = InjectPythonCodeToPID(pid, code);
if (ret)
{
fprintf(stderr, "Could not inject code, return value %d, error code %d\n", ret, GetLastError());
return 20 + ret;
}
return 0;
}
\ No newline at end of file
import sys import sys
from setuptools import setup, find_packages from setuptools import setup, find_packages
from distutils.command.build_py import build_py as _build_py
import platform
try: try:
# These imports are not used, but make # These imports are not used, but make
...@@ -28,6 +30,35 @@ if sys.version_info[0] == 2: ...@@ -28,6 +30,35 @@ if sys.version_info[0] == 2:
if sys.version_info[1] < 7: if sys.version_info[1] < 7:
tests_require.append('unittest2') tests_require.append('unittest2')
class build_py(_build_py):
def run(self):
_build_py.run(self)
if platform.system() == 'Windows':
import os
try:
import winbuild
except:
self.announce("Could not find an microsoft compiler for supporting windows process injection", 2)
return
#can fail ?
dirs = [x for x in self.get_data_files() if x[0] == 'pyrasite'][0]
srcfile = os.path.join(dirs[1], 'win', 'inject_python.cpp')
out32exe = os.path.join(dirs[2], 'win', 'inject_python_32.exe')
out64exe = os.path.join(dirs[2], 'win', 'inject_python_64.exe')
try:
os.makedirs(os.path.dirname(out32exe))
except:
pass
try:
winbuild.compile(srcfile, out32exe, 'x86')
except:
self.announce("Could not find an x86 microsoft compiler for supporting injection to 32 bit python instances", 2)
try:
winbuild.compile(srcfile, out64exe, 'x64')
except:
self.announce("Could not find an x64 microsoft compiler for supporting injection to 64 bit python instances", 2)
setup(name='pyrasite', setup(name='pyrasite',
version=version, version=version,
description="Inject code into a running Python process", description="Inject code into a running Python process",
...@@ -60,4 +91,5 @@ setup(name='pyrasite', ...@@ -60,4 +91,5 @@ setup(name='pyrasite',
'Programming Language :: Python', 'Programming Language :: Python',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
], ],
cmdclass={'build_py': build_py}
) )
from distutils import msvc9compiler
import subprocess
import os
def compile(filename, outputfilename, arch='x86', vcver=None):
if vcver == None:
if os.getenv('MSVCVER'):
vcver = float(os.getenv('MSVCVER'))
else:
vcver = msvc9compiler.get_build_version()
vcvarsall = msvc9compiler.find_vcvarsall(vcver)
path = os.path.splitext(outputfilename)
objfilename = path[0] + '.obj'
p = subprocess.Popen('"%s" %s & cl %s /Fe%s /Fo%s' % (vcvarsall, arch, filename, outputfilename, objfilename),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
try:
stdout, stderr = p.communicate()
if p.wait() != 0:
raise Exception(stderr.decode("mbcs"))
os.remove(objfilename)
finally:
p.stdout.close()
p.stderr.close()
#try:
# compile('inject_python.cpp', 'inject_python_32.exe', 'x86', 10.0)
#except:
# pass
#try:
# compile('inject_python.cpp', 'inject_python_64.exe', 'amd64', 10.0)
#except:
# pass
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment