Created
October 25, 2012 16:40
-
-
Save juntalis/3953921 to your computer and use it in GitHub Desktop.
Remote DLL
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# encoding: utf-8 | |
""" | |
rdll.py | |
Created by Charles on 10/25/12. | |
This program is free software. It comes without any warranty, to | |
the extent permitted by applicable law. You can redistribute it | |
and/or modify it under the terms of the Do What The Fuck You Want | |
To Public License, Version 2, as published by Sam Hocevar. See | |
http://sam.zoy.org/wtfpl/COPYING for more details. | |
""" | |
import os | |
from cutil import * | |
from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL,\ | |
FUNCFLAG_PYTHONAPI as _FUNCFLAG_PYTHONAPI,\ | |
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO,\ | |
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR, \ | |
CFuncPtr as _CFuncPtr | |
from _kernel32 import PLoadLibraryW as PLoadLibrary | |
from extern import pefile | |
__all__ = [] | |
def _pack_args(*args): | |
class _Args(Structure): pass | |
fields = [] | |
for i, arg in enumerate(args): | |
fields.push(('arg%d' % i, type(arg),)) | |
_Args._fields_ = fields | |
return _Args(*args) | |
class _RCFuncPtr(object): | |
_addr_ = 0 | |
_flags_ = None | |
_restype_ = None | |
_funcptr_ = None | |
_hprocess_ = None | |
def _valueof(self, arg): | |
if not hasattr(arg, '_type_'): | |
return arg | |
elif hasattr(arg, 'value'): | |
return arg.value | |
elif hasattr(arg, 'contents'): | |
return arg.contents | |
else: | |
raise Exception('Don\'t know how to get the value of arg.\nType: %s' % type(arg)) | |
def _valtoargtype(self, arg, argtype): | |
result = 0 | |
if type(arg) in [str, unicode]: | |
if argtype == c_char_p: | |
result = create_string_buffer(arg) | |
elif argtype == c_wchar_p: | |
result = create_unicode_buffer(arg) | |
elif argtype._type_ == c_ubyte: | |
result = (c_ubyte * len(arg) + 1)() | |
for i, c in enumerate(arg): | |
result[i] = c | |
else: | |
raise Exception('Don\'t know how to convert string, "%s" into type: %s' % (arg, argtype)) | |
# Array type | |
elif hasattr(argtype, '_length_')\ | |
or type(argtype._type_) != str: # Pointer type | |
result = cast(arg, argtype) | |
elif hasattr(argtype, 'value'): | |
result = argtype(arg) | |
else: | |
raise Exception('Don\'t know how to convert arg to argtype.\nArg: %s\nArgtype: %s' % (arg, argtype)) | |
return result | |
def _alloc_set_var(self, val): | |
""" | |
BOOL alloc_set_varA(LPCSTR* buffer, HANDLE hProcess, LPCSTR val) | |
{ | |
SIZE_T buflen = (lstrlen(val) + 1) * sizeof(const char); | |
if (!(*buffer = (LPCSTR) VirtualAllocEx(hProcess, NULL, buflen, MEM_COMMIT, PAGE_READWRITE))) | |
return_error("Could not allocate memory for our test call."); | |
if (!WriteProcessMemory(hProcess, (LPVOID)*buffer, (LPCVOID)val, (SIZE_T)buflen, NULL)) | |
return_error("Could write to our remote variable.."); | |
return TRUE; | |
} | |
""" | |
buflen = sizeof(val) | |
buffer = VirtualAllocEx(self._hprocess_, 0L, buflen, MEM_COMMIT, PAGE_READWRITE) | |
if buffer == NULL: | |
raise Exception('Could not allocate our remote buffer.') | |
if WriteProcessMemory(self._hprocess_, LPCVOID(buffer), val, buflen, byref(c_ulong(0L))) == FALSE: | |
raise Exception('Could not write to our remote variable.') | |
return buffer | |
def __call__(self, *more): # real signature unknown; restored from __doc__ | |
""" x.__call__(...) <==> x(...) """ | |
funcptr = self._funcptr_ | |
result = DWORD(0L) if funcptr.restype is None else funcptr.restype() | |
lpParameter = NULL | |
if funcptr.argtypes is not None and len(funcptr.argtypes) > 0: | |
args = [] | |
argcount = len(more) | |
for i, argtype in enumerate(funcptr.argtypes): | |
arg = 0 | |
if i > argcount: | |
arg = argtype() | |
elif hasattr(more[i], '_type_'): | |
if more[i]._type_ == argtype: | |
arg = more[i] | |
else: | |
arg = self._valtoargtype(self._valueof(more[i]), argtype) | |
else: | |
arg = self._valtoargtype(more[i]) | |
args.append(arg) | |
if argcount > 1: | |
lpParameter = _pack_args(*args) | |
else: | |
lpParameter = args[0] | |
if hasattr(lpParameter, '_b_needsfree_') and lpParameter._b_needsfree_ == 1 and bool(lpParameter): | |
lpParameter = self._alloc_set_var(lpParameter) | |
hRemoteThread = CreateRemoteThread( | |
self._hprocess_, NULL_SECURITY_ATTRIBUTES, 0, | |
cast(self._addr_, LPTHREAD_START_ROUTINE), | |
lpParameter, 0L, byref(c_ulong(0L)) | |
) | |
if hRemoteThread == NULL: | |
if hasattr(lpParameter, '_b_needsfree_') and lpParameter._b_needsfree_ == 1 and bool(lpParameter): | |
VirtualFreeEx(self._hprocess_, lpParameter, 0, MEM_RELEASE) | |
CloseHandle(self._hprocess_) | |
raise WinError('Failed to start our remote thread.') | |
WaitForSingleObject(hRemoteThread, INFINITE) | |
GetExitCodeThread(hRemoteThread, cast(byref(result), LPDWORD)) | |
CloseHandle(hRemoteThread) | |
if hasattr(lpParameter, '_b_needsfree_') and lpParameter._b_needsfree_ == 1 and bool(lpParameter): | |
VirtualFreeEx(self._hprocess_, lpParameter, 0, MEM_RELEASE) | |
return result | |
def __init__(self, offset, funcid, rdll): | |
self._addr_ = offset | |
if self._flags_ == _FUNCFLAG_CDECL: | |
self._funcptr_ = CFUNCTYPE(self._restype_) | |
elif self._flags_ == _FUNCFLAG_STDCALL: | |
self._funcptr_ = WINFUNCTYPE(self._restype_) | |
elif self._flags_ == _FUNCFLAG_PYTHONAPI: | |
self._funcptr_ = PYFUNCTYPE(self._restype_) | |
self._funcptr_._func_flags_ = self._flags_ | |
def __nonzero__(self): | |
""" x.__nonzero__() <==> x != 0 """ | |
return self._funcptr_.__nonzero__() | |
def __repr__(self): # real signature unknown; restored from __doc__ | |
""" x.__repr__() <==> repr(x) """ | |
return self._funcptr_.__repr__() | |
@memoize | |
def _has(self, key): return key in dir(_RCFuncPtr) | |
def __setattr__(self, key, value): | |
if self._has(key): | |
super(_RCFuncPtr, self).__setattr__(key, value) | |
else: | |
setattr(self._funcptr_, key, value) | |
def __getattr__(self, key): | |
return super(_RCFuncPtr, self).__getattr__(key) if \ | |
self._has(key) else \ | |
getattr(self._funcptr_, key) | |
class RDLL(object): | |
_func_flags_ = _FUNCFLAG_CDECL | |
_func_restype_ = c_int | |
_hprocess_ = 0 | |
_hthread_ = 0 | |
_exports_ = {} | |
def __init__(self, name = None, pid = 0, thid = 0, mode = DEFAULT_MODE, handle = None, use_errno = False, use_last_error = False): | |
if name is None and handle is None: | |
raise WindowsError('We need either a name or a handle to a preloaded DLL to create a DLL interface.') | |
elif name is None: | |
self._name = GetModuleFileName(handle) | |
else: | |
self._name = name | |
flags = self._func_flags_ | |
if use_errno: | |
flags |= _FUNCFLAG_USE_ERRNO | |
if use_last_error: | |
flags |= _FUNCFLAG_USE_LASTERROR | |
self._hthread_ = thid | |
pi, ti = 0, 0 | |
if pid == 0: | |
check = find_parent_process() | |
if check is None: | |
raise WinError('Failed to open our parent process and no pid specified.') | |
pi, ti = check | |
else: | |
pi, ti = bypid(pid) | |
if self._hthread_ == 0: | |
self._hthread_ = ti | |
self._hprocess_ = OpenProcess(PROCESS_MOST, FALSE, pi) | |
class _FuncPtr(_RCFuncPtr): | |
_flags_ = flags | |
_restype_ = self._func_restype_ | |
_hprocess_ = self._hprocess_ | |
self._FuncPtr = _FuncPtr | |
self._handle = self.__inject__() | |
if self._handle == 0: | |
raise WindowsError('Could not inject your library: %s' % self._name) | |
self.__populate_exports__() | |
def __inject__(self): | |
val = create_unicode_buffer(self._name, len(self._name) + 1) | |
buflen = sizeof(val) | |
buffer = VirtualAllocEx(self._hprocess_, 0L, buflen, MEM_COMMIT, PAGE_READWRITE) | |
if buffer == NULL: | |
raise Exception('Could not allocate our remote buffer.') | |
if WriteProcessMemory(self._hprocess_, buffer, cast(val, LPCVOID), buflen, byref(c_ulong(0L))) == FALSE: | |
raise Exception('Could not write to our remote variable.') | |
hRemoteThread = CreateRemoteThread( | |
self._hprocess_, NULL_SECURITY_ATTRIBUTES, 0, | |
PLoadLibrary, buffer, 0L, byref(c_ulong(0L)) | |
) | |
if hRemoteThread == NULL: | |
VirtualFreeEx(self._hprocess_, buffer, 0, MEM_RELEASE) | |
CloseHandle(self._hprocess_) | |
raise WinError('Failed to start our remote thread.') | |
WaitForSingleObject(hRemoteThread, INFINITE) | |
result = c_ulong(0) | |
GetExitCodeThread(hRemoteThread, byref(result)) | |
CloseHandle(hRemoteThread) | |
VirtualFreeEx(self._hprocess_, buffer, 0, MEM_RELEASE) | |
return result.value | |
def __populate_exports__(self): | |
if len(os.path.splitext(self._name)[1].lower()) == 0: | |
self._name += '.dll' | |
pe = pefile.PE(self._name, fast_load=True) | |
direxport = pe.OPTIONAL_HEADER.DATA_DIRECTORY[0] | |
exportsobj = pe.parse_export_directory(direxport.VirtualAddress, direxport.Size) | |
pe.close() | |
for export in exportsobj.symbols: | |
self._exports_[export.name] = \ | |
self._exports_[export.ordinal] = \ | |
self._handle + export.address | |
def __repr__(self): | |
return "<%s '%s', handle %x at %x>" %\ | |
(self.__class__.__name__, self._name, | |
(self._handle & (_sys.maxint * 2 + 1)), | |
id(self) & (_sys.maxint * 2 + 1)) | |
def __getattr__(self, name): | |
if name.startswith('__') and name.endswith('__'): | |
raise AttributeError(name) | |
func = self.__getitem__(name) | |
setattr(self, name, func) | |
return func | |
def __getitem__(self, name_or_ordinal): | |
ordinal = isinstance(name_or_ordinal, (int, long)) | |
if not self._exports_.has_key(name_or_ordinal): | |
if ordinal: raise WindowsError('Could not find address of function at ordinal: %d' % name_or_ordinal) | |
else: raise WindowsError('Could not find address of function named: %s' % name_or_ordinal) | |
func = self._FuncPtr(self._exports_[name_or_ordinal], name_or_ordinal, self) | |
if not ordinal: | |
func.__name__ = name_or_ordinal | |
return func | |
if __name__=='__main__': | |
testdll = RDLL('testdll.dll') | |
Initialize = testdll.Initialize | |
Initialize.restype = None | |
Initialize.argtypes = [] | |
Initialize() | |
testdll.Finalize() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment