├── .gitignore ├── LICENSE ├── README.md ├── perfect-dll-proxy.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /*.dll 2 | /*.cpp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Perfect DLL Proxy 2 | 3 | A while ago I needed a proxy to perform DLL hijacking, but I did not like how existing solutions generated ASM stubs to deal with the forwarding. It turns out that there is a trick to get forwards to work with an absolute path: 4 | 5 | ```cpp 6 | #pragma comment(linker, 7 | "/EXPORT:CredPackAuthenticationBufferA=\\\\.\\GLOBALROOT\\SystemRoot\\System32\\credui.dll.CredPackAuthenticationBufferA" 8 | ) 9 | ``` 10 | 11 | See the references for more information. 12 | 13 | To automatically generate a DLL that exports everything and loads an arbitrary DLL (without intercepting functions), look at the following project: https://github.com/namazso/dll-proxy-generator 14 | 15 | ## Usage 16 | 17 | ```sh 18 | python -m pip install pefile 19 | python perfect-dll-proxy.py credui.dll 20 | ``` 21 | 22 | ## References 23 | 24 | - https://nibblestew.blogspot.com/2019/05/ 25 | - https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html 26 | - https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function 27 | - https://devblogs.microsoft.com/oldnewthing/20121116-00/?p=6073 28 | - https://medium.com/@lsecqt/weaponizing-dll-hijacking-via-dll-proxying-3983a8249de0 29 | - https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/dll-hijacking 30 | - https://www.ired.team/offensive-security/persistence/dll-proxying-for-persistence 31 | - https://github.com/Flangvik/SharpDllProxy 32 | - https://github.com/hfiref0x/WinObjEx64 33 | -------------------------------------------------------------------------------- /perfect-dll-proxy.py: -------------------------------------------------------------------------------- 1 | import pefile 2 | import argparse 3 | import os 4 | import sys 5 | 6 | """ 7 | References: 8 | - https://nibblestew.blogspot.com/2019/05/ 9 | - https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html 10 | - https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function 11 | - https://devblogs.microsoft.com/oldnewthing/20121116-00/?p=6073 12 | - https://medium.com/@lsecqt/weaponizing-dll-hijacking-via-dll-proxying-3983a8249de0 13 | - https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/dll-hijacking 14 | - https://www.ired.team/offensive-security/persistence/dll-proxying-for-persistence 15 | - https://github.com/Flangvik/SharpDllProxy 16 | - https://github.com/hfiref0x/WinObjEx64 17 | """ 18 | 19 | def main(): 20 | # Parse arguments 21 | parser = argparse.ArgumentParser(description="Generate a proxy DLL") 22 | parser.add_argument("dll", help="Path to the DLL to generate a proxy for") 23 | parser.add_argument("--output", "-o", help="Generated C++ proxy file to write to") 24 | parser.add_argument("--force-ordinals", "-v", action="store_true", help="Force matching ordinals") 25 | args = parser.parse_args() 26 | dll: str = args.dll 27 | output: str = args.output 28 | basename = os.path.basename(dll) 29 | if output is None: 30 | file, _ = os.path.splitext(basename) 31 | output = f"{file}.cpp" 32 | 33 | # Use the system directory if the DLL is not found 34 | if not os.path.exists(dll) and not os.path.isabs(dll): 35 | dll = os.path.join(os.environ["SystemRoot"], "System32", dll) 36 | if not os.path.exists(dll): 37 | print(f"File not found: {dll}") 38 | sys.exit(1) 39 | 40 | # Enumerate the exports 41 | pe = pefile.PE(dll) 42 | commands = [] 43 | for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols: 44 | ordinal = exp.ordinal 45 | if exp.name is None: 46 | command = f"__proxy{ordinal}=\" DLLPATH \".#{ordinal},@{ordinal},NONAME" 47 | else: 48 | name = exp.name.decode() 49 | command = f"{name}=\" DLLPATH \".{name}" 50 | # The first underscore is removed by the linker 51 | if name.startswith("_"): 52 | command = f"_{command}" 53 | # Special case for COM exports 54 | if name in { 55 | "DllCanUnloadNow", 56 | "DllGetClassObject", 57 | "DllInstall", 58 | "DllRegisterServer", 59 | "DllUnregisterServer", 60 | }: 61 | command += ",PRIVATE" 62 | elif args.force_ordinals: 63 | command += f",@{ordinal}" 64 | commands.append(command) 65 | 66 | # Generate the proxy 67 | with open(output, "w") as f: 68 | f.write(f"""#include 69 | 70 | #ifdef _WIN64 71 | #define DLLPATH "\\\\\\\\.\\\\GLOBALROOT\\\\SystemRoot\\\\System32\\\\{basename}" 72 | #else 73 | #define DLLPATH "\\\\\\\\.\\\\GLOBALROOT\\\\SystemRoot\\\\SysWOW64\\\\{basename}" 74 | #endif // _WIN64 75 | 76 | """) 77 | for command in commands: 78 | f.write(f"#pragma comment(linker, \"/EXPORT:{command}\")\n") 79 | f.write(""" 80 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 81 | { 82 | switch (fdwReason) 83 | { 84 | case DLL_PROCESS_ATTACH: 85 | break; 86 | case DLL_THREAD_ATTACH: 87 | break; 88 | case DLL_THREAD_DETACH: 89 | break; 90 | case DLL_PROCESS_DETACH: 91 | break; 92 | } 93 | return TRUE; 94 | } 95 | """) 96 | 97 | 98 | if __name__ == "__main__": 99 | main() -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pefile --------------------------------------------------------------------------------