├── Hannibal.svg ├── README.md ├── asm └── x64 │ └── stub_wrapper.asm ├── bin └── obj │ └── .gitkeep ├── debug_makefile ├── include ├── Native.h ├── hannibal.h ├── hannibal_tasking.h ├── utility_memory.h ├── utility_serialization.h ├── utility_strings.h └── utility_winapi_function_resolution.h ├── linux_makefile ├── scripts ├── Linker.ld └── build.py ├── src ├── hannibal.c ├── hannibal_tasking.c ├── utility_memory.c ├── utility_serialization.c ├── utility_strings.c └── utility_winapi_function_resolution.c └── windows_makefile /Hannibal.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Would you segfault me? I'd segfault me. 9 | 10 | 11 | 12 | 13 | 15 | 16 | 259 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Hannibal](Hannibal.svg) 2 | 3 | HBINS are mini versions of [Hannibal](https://github.com/silentwarble/Hannibal), to be used with Hannibal. Instead of the usual BOF/COFF files we are using a PIC .bin. 4 | 5 | See the article [Making Mini Monsters](https://silentwarble.com/posts/making-mini-monsters/) for a deeper dive. 6 | 7 | ## Installation 8 | 9 | You will need make, MingW, and Python installed to build these. See the [Making Monsters](https://silentwarble.com/posts/making-monsters-1/#installation) installation section. It is the same for HBINs. 10 | 11 | Simply clone the repo, invoke the appropriate makefile ```make -f windows_makefile```, and use execute_hbin in Mythic to upload bin/hannibal.bin. 12 | 13 | ## Note 14 | 15 | - These have not been extensively tested. They are functional, but need much more testing before being considered Op worthy. 16 | - The debug process for these is different from Hannibal. A TODO is to get a standalone debug build working for these, but currently that is not supported. Use x64dbg or similar. -------------------------------------------------------------------------------- /asm/x64/stub_wrapper.asm: -------------------------------------------------------------------------------- 1 | [BITS 64] 2 | 3 | DEFAULT REL 4 | 5 | EXTERN hbin_main 6 | 7 | GLOBAL Start 8 | GLOBAL StRipStart 9 | GLOBAL StRipEnd 10 | GLOBAL GetInStruct 11 | GLOBAL ___chkstk_ms 12 | 13 | [SECTION .text$STUB] 14 | 15 | Start: 16 | push rsi 17 | mov rsi, rsp 18 | and rsp, 0FFFFFFFFFFFFFFF0h 19 | sub rsp, 020h 20 | call hbin_main 21 | mov rsp, rsi 22 | pop rsi 23 | ret 24 | 25 | StRipStart: 26 | call StRipPtrStart 27 | ret 28 | 29 | StRipPtrStart: 30 | mov rax, [rsp] 31 | sub rax, 0x1b 32 | ret 33 | 34 | GetInStruct: 35 | call StRipStart ;; Get start of agent 36 | sub rax, 8 ;; Go backwards 8 bytes 37 | ret ;; Return a ptr to a ptr to the input struct 38 | 39 | ___chkstk_ms: 40 | ret 41 | 42 | [SECTION .text$E] 43 | 44 | StRipEnd: 45 | call StRetPtrEnd 46 | ret 47 | 48 | StRetPtrEnd: 49 | mov rax, [rsp] 50 | add rax, 0xb 51 | ret 52 | 53 | [SECTION .text$P] 54 | 55 | SymHannibalEnd: 56 | db 'H', 'A', 'N', 'N', 'I', 'B', 'A', 'L', '-', 'E', 'N', 'D' 57 | -------------------------------------------------------------------------------- /bin/obj/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silentwarble/hbin_template/b00d99798f6638d7b01eb20e7904d80383111158/bin/obj/.gitkeep -------------------------------------------------------------------------------- /debug_makefile: -------------------------------------------------------------------------------- 1 | # Generates an exe with all debugging symbols on for easy stepping. 2 | 3 | # TODO: Not tested with HBINs yet. 4 | 5 | #---------- Performance ----------# 6 | 7 | # MAKEFLAGS += -s -j1 8 | MAKEFLAGS += -s -j$(NUMBER_OF_PROCESSORS) 9 | 10 | #---------- Project Settings ----------# 11 | 12 | PROJECT := hannibal 13 | CC_X64 := x86_64-w64-mingw32-g++ 14 | 15 | #---------- Compiler Flags ----------# 16 | 17 | DEBUG_CFLAGS := -O0 -Iinclude -D DEBUG_BUILD -D PROFILE_MYTHIC_HTTP 18 | DEBUG_CFLAGS += -fpermissive -Wunknown-pragmas -Wignored-qualifiers -w 19 | 20 | #---------- Linker Flags ----------# 21 | 22 | DEBUG_LDFLAGS = -g -m64 23 | 24 | #---------- Paths ----------# 25 | 26 | SRC_DIR := src 27 | OBJ_DIR := bin/obj 28 | BIN_DIR := bin 29 | SRC_FILES := $(wildcard $(SRC_DIR)/*.c) 30 | OBJ_FILES := $(SRC_FILES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) 31 | 32 | # This makefile creates a normal .exe, so the stub_wrapper and hannibal_initialize.c can't be part of this build. 33 | SRC_FILES_DEBUG := $(filter-out src/hannibal_initialize.c, $(SRC_FILES)) 34 | OBJ_FILES_DEBUG := $(SRC_FILES_DEBUG:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) 35 | 36 | #---------- Targets ----------# 37 | 38 | all: $(BIN_DIR)/$(PROJECT).exe 39 | 40 | $(BIN_DIR)/$(PROJECT).exe: clean $(OBJ_FILES_DEBUG) 41 | @ echo "[+] Linking x64 Executable" 42 | @ $(CC_X64) bin/obj/*.o -o $(BIN_DIR)/$(PROJECT).exe $(DEBUG_CFLAGS) $(DEBUG_LDFLAGS) 43 | @ del /q bin\obj\*.o 2>nul 44 | 45 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 46 | @echo "[+] Compiling $? -> $@" 47 | @ $(CC_X64) -o $@ -c $? $(DEBUG_CFLAGS) $(DEBUG_LDFLAGS) 48 | 49 | #---------- Utility ----------# 50 | 51 | clean: 52 | @ del /q bin\obj\*.o 2>nul 53 | @ del /q bin\obj\*.s 2>nul 54 | @ del /q bin\obj\*.ii 2>nul 55 | @ del /q bin\*.bin 2>nul 56 | 57 | print: 58 | @echo "SRC_FILES_DEBUG: $(SRC_FILES_DEBUG)" 59 | @echo "SRC_FILES: $(SRC_FILES)" 60 | @echo "OBJ_FILES: $(OBJ_FILES)" 61 | @echo "OBJ_FILES_DEBUG: $(OBJ_FILES_DEBUG)" -------------------------------------------------------------------------------- /include/hannibal.h: -------------------------------------------------------------------------------- 1 | #ifndef HANNIBAL_H 2 | #define HANNIBAL_H 3 | 4 | // Note: The order of the includes matters. 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "utility_strings.h" 17 | #include "utility_winapi_function_resolution.h" 18 | #include "utility_serialization.h" 19 | #include "utility_memory.h" 20 | #include "hannibal_tasking.h" 21 | 22 | 23 | // HBIN INPUT 24 | typedef struct _HBIN_IN { 25 | LPVOID args; 26 | int arg_size; 27 | LPVOID hannibal_instance; 28 | char *controller_uuid; 29 | } HBIN_IN; 30 | 31 | #define CONCURRENT_FILE_DOWNLOADS 5 32 | #define CONCURRENT_FILE_UPLOADS 5 33 | #define FILE_DOWNLOAD_CHUNK_SIZE 512000 // Bytes. Agent > Controller 34 | #define FILE_UPLOAD_CHUNK_SIZE 512000 // Bytes. Controller > Agent 35 | 36 | #define ENCRYPT_KEY_SIZE 32 // AES keysize 37 | #define LOCAL_ENCRYPT_KEY_SIZE 16 // Randomly generated each run for sleep or heap encryption. 16 bytes to work with Ekko. 38 | 39 | #define PTR_OF_TYPE( x ) __typeof__( x ) * x; 40 | #define SECTION_CODE __attribute__( ( section( ".text$CODE" ) ) ) 41 | #define SECTION_GLOBAL __attribute__( ( section( ".global" ) ) ) 42 | #define SECTION_RDATA __attribute__( ( section( ".rdata" ) ) ) 43 | 44 | typedef struct _BUFFER { 45 | PVOID Buffer; 46 | ULONG Length; 47 | } BUFFER, *PBUFFER; 48 | 49 | #ifdef PIC_BUILD 50 | EXTERN_C ULONG __Instance_offset; 51 | EXTERN_C PVOID __Instance; 52 | 53 | EXTERN_C PVOID StRipStart(); 54 | EXTERN_C PVOID StRipEnd(); 55 | EXTERN_C PVOID GetInStruct(); 56 | 57 | EXTERN_C VOID hbin_main( 58 | _In_ PVOID Param 59 | ); 60 | 61 | #endif 62 | 63 | typedef struct 64 | { 65 | DWORD Length; 66 | DWORD MaximumLength; 67 | PVOID Buffer; 68 | } USTRING; 69 | 70 | typedef struct 71 | { // Tracking file downloads. Agent > Mythic 72 | LPCSTR task_uuid; 73 | LPCSTR download_uuid; 74 | LPCWSTR path; 75 | UINT64 bytes_sent; 76 | UINT32 chunks_sent; 77 | UINT32 chunk_count; 78 | UINT64 filesize; 79 | } FILE_DOWNLOAD; 80 | 81 | typedef struct 82 | { // Tracking file uploads. Mythic > Agent 83 | LPCSTR task_uuid; 84 | LPCSTR upload_uuid; 85 | LPCWSTR path; 86 | UINT64 bytes_received; 87 | UINT32 chunks_received; 88 | UINT32 chunk_count; 89 | UINT64 filesize; 90 | } FILE_UPLOAD; 91 | 92 | 93 | typedef struct _TASK_QUEUE TASK_QUEUE; 94 | typedef struct _TASK_ENTRY TASK_ENTRY; 95 | 96 | typedef struct _INSTANCE { 97 | 98 | BUFFER Base; 99 | 100 | struct { 101 | 102 | // Ntdll.dll 103 | PTR_OF_TYPE( RtlRandomEx ) 104 | PTR_OF_TYPE( NtCreateEvent ) 105 | PTR_OF_TYPE( RtlAllocateHeap ) 106 | PTR_OF_TYPE( RtlCreateTimerQueue ) 107 | PTR_OF_TYPE( NtProtectVirtualMemory ) 108 | PTR_OF_TYPE( RtlCreateTimer ) 109 | PTR_OF_TYPE( RtlRegisterWait ) 110 | PTR_OF_TYPE( RtlCaptureContext ) 111 | PTR_OF_TYPE( NtSetEvent ) 112 | PTR_OF_TYPE( NtContinue ) 113 | PTR_OF_TYPE( NtClose ) 114 | PTR_OF_TYPE( NtSignalAndWaitForSingleObject ) 115 | PTR_OF_TYPE( RtlDeleteTimerQueue ) 116 | PTR_OF_TYPE( NtQueryInformationProcess ) 117 | PTR_OF_TYPE( RtlExitUserThread ) 118 | 119 | 120 | // Kernel32.dll 121 | PTR_OF_TYPE( Sleep ) 122 | PTR_OF_TYPE( FindClose ) 123 | PTR_OF_TYPE( ExitProcess ) 124 | PTR_OF_TYPE( VirtualFree ) 125 | PTR_OF_TYPE( VirtualAlloc ) 126 | PTR_OF_TYPE( VirtualProtect ) 127 | PTR_OF_TYPE( LoadLibraryA ) 128 | PTR_OF_TYPE( GetLastError ) 129 | PTR_OF_TYPE( FindNextFileW ) 130 | PTR_OF_TYPE( DeleteFileW ) 131 | PTR_OF_TYPE( RemoveDirectoryW ) 132 | PTR_OF_TYPE( GetTickCount ) 133 | PTR_OF_TYPE( FindFirstFileW ) 134 | PTR_OF_TYPE( CreateProcessW ) 135 | PTR_OF_TYPE( OpenProcess ) 136 | PTR_OF_TYPE( WaitForSingleObject ) 137 | PTR_OF_TYPE( CreateTimerQueueTimer ) 138 | PTR_OF_TYPE( CreateEventW ) 139 | PTR_OF_TYPE( CreateTimerQueue ) 140 | PTR_OF_TYPE( SetEvent ) 141 | PTR_OF_TYPE( DeleteTimerQueue ) 142 | PTR_OF_TYPE( GetProcAddress ) 143 | PTR_OF_TYPE( GetFileAttributesW ) 144 | PTR_OF_TYPE( CreateFileW ) 145 | PTR_OF_TYPE( GetFileSizeEx ) 146 | PTR_OF_TYPE( SetFilePointer ) 147 | PTR_OF_TYPE( ReadFile ) 148 | PTR_OF_TYPE( CloseHandle ) 149 | PTR_OF_TYPE( WriteFile ) 150 | PTR_OF_TYPE( CopyFileW ) 151 | PTR_OF_TYPE( MoveFileExW ) 152 | PTR_OF_TYPE( Process32FirstW ) 153 | PTR_OF_TYPE( Process32NextW ) 154 | PTR_OF_TYPE( IsWow64Process ) 155 | PTR_OF_TYPE( CreateDirectoryW ) 156 | PTR_OF_TYPE( GetLogicalDrives ) 157 | PTR_OF_TYPE( GetComputerNameExW ) 158 | PTR_OF_TYPE( GetModuleFileNameW ) 159 | PTR_OF_TYPE( GetDiskFreeSpaceExW ) 160 | PTR_OF_TYPE( GetCurrentProcessId ) 161 | PTR_OF_TYPE( SetCurrentDirectoryW ) 162 | PTR_OF_TYPE( FileTimeToSystemTime ) 163 | PTR_OF_TYPE( SystemTimeToFileTime ) 164 | PTR_OF_TYPE( GetCurrentDirectoryW ) 165 | PTR_OF_TYPE( WaitForSingleObjectEx ) 166 | PTR_OF_TYPE( GetVolumeInformationW ) 167 | PTR_OF_TYPE( CreateToolhelp32Snapshot ) 168 | PTR_OF_TYPE( NtSetInformationVirtualMemory ) 169 | 170 | 171 | // User32.dll 172 | PTR_OF_TYPE( MessageBoxA ) 173 | PTR_OF_TYPE( MessageBoxW ) 174 | 175 | 176 | // Wininet.dll 177 | PTR_OF_TYPE( InternetOpenW ); 178 | PTR_OF_TYPE( InternetConnectW ); 179 | PTR_OF_TYPE( HttpOpenRequestW ); 180 | PTR_OF_TYPE( HttpSendRequestW ); 181 | PTR_OF_TYPE( InternetReadFile ); 182 | PTR_OF_TYPE( InternetSetOptionW ); 183 | PTR_OF_TYPE( InternetCloseHandle ); 184 | PTR_OF_TYPE( InternetQueryOptionW ); 185 | 186 | 187 | // Advapi32.dll 188 | NTSTATUS ( WINAPI* SystemFunction032 ) ( USTRING* data, USTRING* key ); 189 | PTR_OF_TYPE( OpenProcessToken ); 190 | PTR_OF_TYPE( LookupAccountSidW ); 191 | PTR_OF_TYPE( GetSidSubAuthority ); 192 | PTR_OF_TYPE( GetTokenInformation ); 193 | PTR_OF_TYPE( LookupPrivilegeValueW ); 194 | PTR_OF_TYPE( AdjustTokenPrivileges ); 195 | 196 | 197 | // Bcrypt.dll 198 | PTR_OF_TYPE( BCryptGenRandom ); 199 | PTR_OF_TYPE( BCryptOpenAlgorithmProvider ); 200 | PTR_OF_TYPE( BCryptCloseAlgorithmProvider ); 201 | 202 | 203 | // Iphlpapi.dll 204 | PTR_OF_TYPE( GetAdaptersAddresses ); // Note: The order of the includes matters. 205 | 206 | 207 | // Ws2_32.dll 208 | PTR_OF_TYPE( InetNtopW ); 209 | 210 | } Win32; 211 | 212 | struct { 213 | PVOID Ntdll; 214 | PVOID Kernel32; 215 | PVOID User32; 216 | PVOID WinInet; 217 | PVOID Advapi32; 218 | PVOID Bcrypt; 219 | PVOID Iphlpapi; 220 | PVOID Ws2_32; 221 | } Modules; 222 | 223 | struct { 224 | 225 | // Core 226 | UINT32 sleep; 227 | UINT32 jitter; 228 | CHAR *uuid; 229 | BOOL checked_in; 230 | 231 | // HTTP 232 | WCHAR *controller_host; 233 | WCHAR *http_method; 234 | WCHAR *user_agent; 235 | WCHAR *controller_url; 236 | 237 | // Encryption 238 | CHAR *encrypt_key; // Used in the AES encrypted comms between the controller and agent 239 | UINT32 encrypt_key_size; 240 | CHAR *decrypt_key; 241 | UINT32 decrypt_key_size; 242 | CHAR *local_encryption_key; // Randomly generated each run for sleep or heap encryption. 243 | CHAR *local_encryption_key_size; 244 | 245 | } config; 246 | 247 | struct { 248 | 249 | TASK_QUEUE *tasks_queue; // From controller 250 | TASK_QUEUE *tasks_response_queue; // To controller 251 | TASK_ENTRY *task_func_ptrs; // Array of function ptrs pointing to commands 252 | UINT32 task_func_ptrs_size; // How many function ptrs 253 | 254 | UINT32 download_count; // How many files are being downloaded concurrently 255 | FILE_DOWNLOAD *file_downloads; // Array tracking which files are being downloaded 256 | 257 | UINT32 upload_count; // How many files are being uploaded concurrently 258 | FILE_UPLOAD *file_uploads; // Array tracking which files are being uploaded 259 | 260 | } tasks; 261 | 262 | } INSTANCE, *PINSTANCE; 263 | 264 | 265 | /** 266 | * This is what retrieve the struct that contains the instance ptr and arg buffer. 267 | * It is in the same allocated memory section as this HBIN, just 8 bytes before it. 268 | */ 269 | #ifdef PIC_BUILD 270 | #define HANNIBAL_INSTANCE_PTR PINSTANCE hannibal_instance_ptr = (*(HBIN_IN **)(GetInStruct()))->hannibal_instance; 271 | #else 272 | #define HANNIBAL_INSTANCE_PTR extern PINSTANCE hannibal_instance_ptr; 273 | #endif 274 | 275 | 276 | #endif 277 | 278 | 279 | -------------------------------------------------------------------------------- /include/hannibal_tasking.h: -------------------------------------------------------------------------------- 1 | #ifndef HANNIBAL_TASKING_H 2 | #define HANNIBAL_TASKING_H 3 | 4 | #include 5 | #include "utility_strings.h" 6 | 7 | #define TASK_CIRCULAR_QUEUE_SIZE 10 8 | #define TASK_RESPONSE_CIRCULAR_QUEUE_SIZE 10 9 | 10 | #ifdef PIC_BUILD 11 | #define FUNC_OFFSET( x ) ((UINT_PTR)StRipStart() + (DWORD)&x) 12 | #else 13 | #define FUNC_OFFSET( x ) (&x) 14 | #endif 15 | 16 | 17 | /////////////////////// Task Structures 18 | 19 | typedef struct _TASK { 20 | char *task_uuid; // Tracking on the controller 21 | int cmd_id; // Identify specific function to execute 22 | int timestamp; 23 | void *cmd; 24 | LPCSTR output; 25 | int output_size; 26 | } TASK, *PTASK; 27 | 28 | typedef struct _TASK_QUEUE { 29 | int front; 30 | int rear; 31 | int size; // How many tasks in queue 32 | int capacity; // How many tasks can it hold 33 | TASK *queue_ptr; 34 | } TASK_QUEUE, *PTASK_QUEUE; 35 | 36 | typedef struct _TASK_ENTRY { 37 | int cmd_id; 38 | (*cmd_ptr)(TASK t); 39 | } TASK_ENTRY; 40 | 41 | 42 | BOOL task_enqueue(TASK_QUEUE *queue_struct, TASK *TASK); 43 | BOOL task_dequeue(TASK_QUEUE *queue_struct, TASK *TASK); 44 | 45 | #endif -------------------------------------------------------------------------------- /include/utility_memory.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_MACROS_H 2 | #define UTILITY_MACROS_H 3 | 4 | #include "hannibal.h" 5 | 6 | // TODO: Implement these and don't use builtins 7 | #define pic_memset __stosb 8 | #define pic_free __builtin_free 9 | #define pic_realloc __builtin_realloc 10 | #define pic_RtlSecureZeroMemory RtlSecureZeroMemory 11 | 12 | void *pic_memcpy(void* dest, const void* src, size_t n); 13 | 14 | INT MemCompare( PVOID s1, PVOID s2, INT len); 15 | 16 | #endif // UTILITY_MACROS_H -------------------------------------------------------------------------------- /include/utility_serialization.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_SERIALIZATION_H 2 | #define UTILITY_SERIALIZATION_H 3 | 4 | #include "hannibal.h" 5 | 6 | 7 | void WriteUint8(UINT8 **buffer, UINT8 value); 8 | void WriteUint32(UINT8 **buffer, UINT32 value); 9 | void WriteUint64(UINT8 **buffer, UINT64 value); 10 | 11 | void WriteString(UINT8 **buffer, const char *str, BOOL include_null); 12 | void WriteStringW(UINT8 **buffer, const wchar_t *wstr, BOOL include_null); 13 | 14 | void WriteBytes(UINT8 **buffer, const char *str, int size); 15 | 16 | UINT8 ReadUint8(UINT8 **buffer); 17 | UINT32 ReadUint32(UINT8 **buffer); 18 | 19 | UINT8* ReadBytes(UINT8 **buffer, UINT32 length); 20 | 21 | char* ReadString(UINT8 **buffer, UINT32 length); 22 | wchar_t* ReadStringW(UINT8 **buffer, UINT32 length); 23 | 24 | #endif -------------------------------------------------------------------------------- /include/utility_strings.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_STRINGS_H 2 | #define UTILITY_STRINGS_H 3 | 4 | #include "hannibal.h" 5 | 6 | 7 | #define H_MAGIC_KEY 5381 8 | #define H_MAGIC_SEED 5 9 | #define H_MODULE_NTDLL 0x70e61753 10 | #define H_MODULE_KERNEL32 0xadd31df0 11 | 12 | 13 | #ifdef __cplusplus 14 | #define CONSTEXPR constexpr 15 | #else 16 | #define CONSTEXPR 17 | #endif 18 | 19 | #define HASH_STR( x ) ExprHashStringA( ( x ) ) 20 | 21 | CONSTEXPR ULONG ExprHashStringA( 22 | _In_ PCHAR String 23 | ) { 24 | ULONG Hash = { 0 }; 25 | CHAR Char = { 0 }; 26 | 27 | Hash = H_MAGIC_KEY; 28 | 29 | if ( ! String ) { 30 | return 0; 31 | } 32 | 33 | while ( ( Char = *String++ ) ) { 34 | /* turn current character to uppercase */ 35 | if ( Char >= 'a' ) { 36 | Char -= 0x20; 37 | } 38 | 39 | Hash = ( ( Hash << H_MAGIC_SEED ) + Hash ) + Char; 40 | } 41 | 42 | return Hash; 43 | } 44 | 45 | 46 | ULONG HashString( 47 | _In_ PVOID String, 48 | _In_ SIZE_T Length 49 | ); 50 | 51 | size_t pic_strlen(const char *str); 52 | size_t pic_strlenW(const wchar_t *wstr); 53 | 54 | char *pic_strcpy(char* dest, const char* src); 55 | int pic_strcmp(const char *str1, const char *str2); 56 | int pic_strcmpW(const wchar_t *wstr1, const wchar_t *wstr2); 57 | int pic_strncmp(const char *s1, const char *s2, size_t n); 58 | 59 | char *pic_strncpy(char *dest, const char *src, size_t n); 60 | 61 | void pic_strcat(char *str1, char *str2); 62 | void pic_strcatW(wchar_t *wstr1, wchar_t *wstr2); 63 | 64 | void dword_to_wchar(DWORD value, WCHAR* buffer, int base); 65 | void ulong_to_wchar(ULONG64 value, WCHAR *buffer); 66 | 67 | #endif // UTILITY_STRINGS_H 68 | -------------------------------------------------------------------------------- /include/utility_winapi_function_resolution.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_WINAPI_FUNCTION_RESOLUTION_H 2 | #define UTILITY_WINAPI_FUNCTION_RESOLUTION_H 3 | 4 | #include "hannibal.h" 5 | 6 | 7 | PVOID get_module_ptr_from_peb( 8 | _In_ ULONG Hash 9 | ); 10 | 11 | PVOID get_func_ptr_from_module_eat( 12 | _In_ PVOID Module, 13 | _In_ ULONG Function 14 | ); 15 | 16 | #endif // UTILITY_WINAPI_FUNCTION_RESOLUTION_H 17 | -------------------------------------------------------------------------------- /linux_makefile: -------------------------------------------------------------------------------- 1 | # Generates a fully position independent .bin 2 | 3 | # Haven't tested this yet for making hbins. 4 | 5 | #---------- Performance ----------# 6 | 7 | # First build is the longest, after that it's incremental. 8 | # MAKEFLAGS += -s -j1 9 | MAKEFLAGS += -s -j$(shell nproc) 10 | 11 | #---------- Project Settings ----------# 12 | 13 | PROJECT := hannibal 14 | CC_X64 := x86_64-w64-mingw32-g++ 15 | 16 | #---------- Compiler Flags ----------# 17 | 18 | # https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html 19 | # https://gcc.gnu.org/onlinedocs/gcc/Cygwin-and-MinGW-Options.html 20 | # https://forum.tinycorelinux.net/index.php/topic,26375.0.html 21 | 22 | CFLAGS := -Os -fno-asynchronous-unwind-tables -nostdlib 23 | CFLAGS += -nolibc 24 | 25 | # Not supported on MingW 12: -nostdlib++ 26 | 27 | CFLAGS += -fno-exceptions # Makes the .eh_frame ld message go away 28 | CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1 29 | CFLAGS += -s -ffunction-sections -falign-jumps=1 -w 30 | CFLAGS += -falign-labels=1 -fPIC 31 | CFLAGS += -Iinclude -masm=intel -fpermissive -mrdrnd 32 | CFLAGS += -D PIC_BUILD -D PROFILE_MYTHIC_HTTP 33 | 34 | #---------- Linker Flags ----------# 35 | 36 | LDFLAGS := -Wl,-Tscripts/Linker.ld 37 | LDFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup 38 | 39 | #---------- Paths ----------# 40 | 41 | ASM_DIR := asm/x64 42 | SRC_DIR := src 43 | OBJ_DIR := bin/obj 44 | BIN_DIR := bin 45 | SRC_FILES := $(wildcard $(SRC_DIR)/*.c) 46 | OBJ_FILES := $(SRC_FILES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) 47 | ASM_FILES := $(wildcard $(ASM_DIR)/*.asm) 48 | ASM_OBJ_FILES := $(ASM_FILES:asm/x64/%.asm=bin/obj/%.o) 49 | 50 | #---------- Targets ----------# 51 | 52 | all: $(BIN_DIR)/$(PROJECT).exe 53 | 54 | $(BIN_DIR)/$(PROJECT).exe: $(ASM_OBJ_FILES) $(OBJ_FILES) 55 | @ echo "[+] Linking x64 Executable" 56 | @$(CC_X64) bin/obj/*.o -o $(BIN_DIR)/$(PROJECT).exe $(CFLAGS) $(LDFLAGS) 57 | @python scripts/build.py -f $(BIN_DIR)/$(PROJECT).exe -o $(BIN_DIR)/$(PROJECT).bin 58 | @rm -f "$(BIN_DIR)\$(PROJECT).exe" 59 | @rm -rf bin/obj/*.o 60 | 61 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 62 | @echo "[+] Compiling $? -> $@" 63 | @$(CC_X64) -o $@ -c $? $(CFLAGS) $(LDFLAGS) 64 | 65 | $(ASM_OBJ_FILES): $(OBJ_DIR)/%.o: asm/x64/%.asm 66 | @echo "[+] Assembling $? -> $@" 67 | @nasm -f win64 $? -o $@ 68 | 69 | #---------- Utility ----------# 70 | 71 | clean: 72 | @rm -rf bin/obj/*.o 73 | @rm -rf bin/obj/*.s 74 | @rm -rf bin/obj/*.ii 75 | @rm -rf bin/*.bin 76 | @rm -rf bin/*.exe 77 | 78 | print: 79 | @echo "SRC_FILES: $(SRC_FILES)" 80 | @echo "OBJ_FILES: $(OBJ_FILES)" 81 | @echo "ASM_FILES: $(ASM_FILES)" 82 | @echo "ASM_OBJ_FILES": $(ASM_OBJ_FILES)" -------------------------------------------------------------------------------- /scripts/Linker.ld: -------------------------------------------------------------------------------- 1 | LINK_BASE = 0x0000; 2 | 3 | ENTRY( Start ) 4 | 5 | SECTIONS 6 | { 7 | . = LINK_BASE; 8 | .text : { 9 | . = LINK_BASE; 10 | *( .text$STUB ); 11 | *( .text$CODE ); 12 | *( .rdata* ); 13 | *( .global ); 14 | *( .text$E ); 15 | *( .text$P ); 16 | } 17 | 18 | .eh_frame : { 19 | *( .eh_frame ) 20 | } 21 | } -------------------------------------------------------------------------------- /scripts/build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding:utf-8 -*- 3 | 4 | import pefile 5 | import argparse 6 | 7 | HANNIBAL_END : bytes = b'HANNIBAL-END' 8 | PAGE_SIZE : int = 0x1000 9 | 10 | def size_to_pages( size: int ) -> int: 11 | PAGE_MASK : int = 0xfff 12 | BASE_PAGE_SHIFT : int = 12 13 | 14 | return ( size >> BASE_PAGE_SHIFT ) + ( ( size & PAGE_MASK ) != 0 ) 15 | 16 | ## 17 | ## parse specified executable file and 18 | ## save .text section shellcode into a file 19 | ## 20 | def main() -> None: 21 | parser = argparse.ArgumentParser( description = 'Extracts shellcode from a PE.' ) 22 | parser.add_argument( '-f', required = True, help = 'Path to the source executable', type = str ) 23 | parser.add_argument( '-o', required = True, help = 'Path to store the output raw binary', type = str ) 24 | option = parser.parse_args() 25 | 26 | executable = pefile.PE( option.f ) 27 | shellcode = bytearray( executable.sections[ 0 ].get_data() ) 28 | shellcode = shellcode[ : shellcode.find( HANNIBAL_END ) ] 29 | size = len( shellcode ) 30 | 31 | pages = size_to_pages( size ) 32 | padding = ( ( pages * PAGE_SIZE ) - size ) 33 | 34 | for i in range( padding ): 35 | shellcode.append( 0 ) 36 | 37 | size = len( shellcode ) 38 | 39 | print( f"[*] payload len : { size - padding } bytes" ) 40 | print( f"[*] size : { size } bytes" ) 41 | print( f"[*] padding : { padding } bytes" ) 42 | print( f"[*] page count : { size / PAGE_SIZE } pages" ) 43 | 44 | file = open( option.o, 'wb+' ) 45 | 46 | file.write( shellcode ) 47 | file.close() 48 | 49 | return 50 | 51 | if __name__ in '__main__': 52 | main() -------------------------------------------------------------------------------- /src/hannibal.c: -------------------------------------------------------------------------------- 1 | #include "hannibal.h" 2 | 3 | 4 | ////////////////////////////////////////////////// PIC BUILD 5 | #ifdef PIC_BUILD 6 | 7 | 8 | /** 9 | * Remember this is passing in the hannibal instance structure 10 | * from the main agent. The base address for this HBIN will be 11 | * different. 12 | * 13 | * The current buffer looks like this: 14 | * 8 Bytes N Bytes 15 | * PTR To PTR > HBIN_IN struct | HBIN 16 | */ 17 | 18 | EXTERN_C SECTION_CODE VOID hbin_main( 19 | _In_ PVOID Param 20 | ) { 21 | 22 | PVOID base = StRipStart(); 23 | PVOID in_struct = GetInStruct(); 24 | 25 | HBIN_IN *in = *(HBIN_IN **)in_struct; 26 | LPVOID arg_buff = in->args; 27 | int arg_buff_size = in->arg_size; 28 | 29 | PINSTANCE hannibal_instance_ptr = in->hannibal_instance; 30 | 31 | 32 | #else 33 | 34 | ////////////////////////////////////////////////// DEBUG BUILD 35 | 36 | 37 | /** 38 | * Not supported at the moment. You'll have to either load up a full instance or just remove 39 | * the hannibal_instance_ptr->Win32. part from function calls when testing. 40 | */ 41 | 42 | // INSTANCE hannibal_instance; 43 | // PINSTANCE hannibal_instance_ptr = &hannibal_instance; 44 | 45 | int main( 46 | _In_ PVOID Param 47 | ) { 48 | 49 | #endif 50 | 51 | 52 | /** 53 | * The arg buffer is serialized in the order you passed them in via the Mythic Modal. 54 | * You will need to read them one at a time using the correct deserializer functions. 55 | * They must be read in the same order you passed them into Mythic. 56 | */ 57 | 58 | LPVOID arg_buff_cursor = arg_buff; 59 | 60 | UINT32 test_arg_uint32 = ReadUint32(&arg_buff_cursor); 61 | CHAR *test_arg_string = ReadString(&arg_buff_cursor, arg_buff_size); 62 | WCHAR *test_arg_wstring = ReadStringW(&arg_buff_cursor, arg_buff_size); 63 | 64 | WCHAR uint32W[9] = {0}; 65 | dword_to_wchar(test_arg_uint32, uint32W, 10); 66 | 67 | /** 68 | * For demonstration purposes, I'm ensuring that User32.dll is 69 | * loaded and these functions are resolved. If you know the agent 70 | * has already resolved these into the instance pointer then this is 71 | * not needed. If you call a function that has not been resolved you 72 | * will crash the agent. TODO: Hash strings. 73 | */ 74 | 75 | hannibal_instance_ptr->Modules.User32 = hannibal_instance_ptr->Win32.LoadLibraryA("User32"); 76 | hannibal_instance_ptr->Win32.MessageBoxA = get_func_ptr_from_module_eat(hannibal_instance_ptr->Modules.User32, HASH_STR("MessageBoxA")); 77 | hannibal_instance_ptr->Win32.MessageBoxW = get_func_ptr_from_module_eat(hannibal_instance_ptr->Modules.User32, HASH_STR("MessageBoxW")); 78 | 79 | hannibal_instance_ptr->Win32.MessageBoxW( NULL, (LPCWSTR)uint32W, L"ARG_1", MB_OK ); 80 | hannibal_instance_ptr->Win32.MessageBoxA( NULL, (LPCSTR)test_arg_string, "ARG_2", MB_OK ); 81 | hannibal_instance_ptr->Win32.MessageBoxW( NULL, (LPCWSTR)test_arg_wstring, L"ARG_3", MB_OK ); 82 | 83 | 84 | 85 | /** 86 | * All contents of the response buffer must be encoded as wide characters. 87 | * Only a single wide null byte at the end. So effectively it is a single 88 | * WCHAR string. These examples show how to handle numbers. 89 | */ 90 | 91 | WCHAR favabeans[] = L" - FROM INSIDE HBIN"; 92 | 93 | UINT32 resp_size = sizeof(uint32W) + sizeof(favabeans); 94 | 95 | CHAR *res_buf = (CHAR *)hannibal_instance_ptr->Win32.VirtualAlloc( 96 | NULL, 97 | resp_size, 98 | MEM_COMMIT, 99 | PAGE_READWRITE 100 | ); 101 | 102 | 103 | pic_memcpy(res_buf, uint32W, sizeof(uint32W)); // There's some extra nulls in this so Mythic renders it a bit incorrect. TODO: Do this cleaner. 104 | pic_memcpy(res_buf + sizeof(uint32W), favabeans, sizeof(favabeans)); 105 | 106 | /** 107 | * If you don't submit a task response 108 | * the task.uuid buffer won't get freed in post_tasking and will make a leak. 109 | * You have to alloc a new buffer, and copy some sort of WCHAR into it. Even just 110 | * L"OK" will do. 111 | */ 112 | 113 | TASK response_task; 114 | response_task.task_uuid = in->controller_uuid; 115 | response_task.output = (LPCSTR)res_buf; 116 | response_task.output_size = resp_size; 117 | 118 | task_enqueue(hannibal_instance_ptr->tasks.tasks_response_queue, &response_task); 119 | 120 | 121 | /** 122 | * Remember the ReadString, ReadStringW functions alloc new memory. 123 | * Free them or you'll get memory leaks. 124 | */ 125 | 126 | hannibal_instance_ptr->Win32.VirtualFree(test_arg_string, 0, MEM_RELEASE); 127 | hannibal_instance_ptr->Win32.VirtualFree(test_arg_wstring, 0, MEM_RELEASE); 128 | 129 | } -------------------------------------------------------------------------------- /src/hannibal_tasking.c: -------------------------------------------------------------------------------- 1 | #include "hannibal_tasking.h" 2 | 3 | 4 | SECTION_CODE BOOL task_enqueue(TASK_QUEUE *queue_struct, TASK *TASK) 5 | { 6 | if (queue_struct->size == queue_struct->capacity){ 7 | return FALSE; 8 | } 9 | 10 | queue_struct->rear = (queue_struct->rear + 1) % queue_struct->capacity; // Ensure wrap-around 11 | queue_struct->queue_ptr[queue_struct->rear] = *TASK; 12 | queue_struct->size++; 13 | return TRUE; 14 | 15 | } 16 | 17 | SECTION_CODE BOOL task_dequeue(TASK_QUEUE *queue_struct, TASK *TASK) 18 | { 19 | 20 | HANNIBAL_INSTANCE_PTR 21 | 22 | if (queue_struct->size == 0){ 23 | return FALSE; 24 | } 25 | 26 | *TASK = queue_struct->queue_ptr[queue_struct->front]; 27 | 28 | queue_struct->front = (queue_struct->front + 1) % queue_struct->capacity; 29 | queue_struct->size--; 30 | 31 | return TRUE; 32 | } -------------------------------------------------------------------------------- /src/utility_memory.c: -------------------------------------------------------------------------------- 1 | #include "utility_memory.h" 2 | 3 | 4 | SECTION_CODE void *pic_memcpy(void* dest, const void* src, size_t n) 5 | { 6 | unsigned char* d = (unsigned char*)dest; 7 | const unsigned char* s = (const unsigned char*)src; 8 | 9 | // Copy byte by byte 10 | while (n--) { 11 | *d++ = *s++; 12 | } 13 | 14 | return dest; 15 | } 16 | 17 | SECTION_CODE INT MemCompare( PVOID s1, PVOID s2, INT len) 18 | { 19 | PUCHAR p = s1; 20 | PUCHAR q = s2; 21 | INT charCompareStatus = 0; 22 | 23 | if ( s1 == s2 ) { 24 | return charCompareStatus; 25 | } 26 | 27 | while (len > 0) 28 | { 29 | if (*p != *q) 30 | { 31 | charCompareStatus = (*p >*q)?1:-1; 32 | break; 33 | } 34 | len--; 35 | p++; 36 | q++; 37 | } 38 | return charCompareStatus; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/utility_serialization.c: -------------------------------------------------------------------------------- 1 | #include "utility_serialization.h" 2 | 3 | 4 | SECTION_CODE void WriteUint8(UINT8 **buffer, UINT8 value) 5 | { 6 | **buffer = value; 7 | (*buffer)++; 8 | } 9 | 10 | SECTION_CODE void WriteUint32(UINT8 **buffer, UINT32 value) 11 | { 12 | // Ensure little-endian encoding 13 | for (int i = 0; i < 4; i++) { 14 | **buffer = (UINT8)(value & 0xFF); // Write the least significant byte first 15 | (*buffer)++; 16 | value >>= 8; // Shift right to get the next byte 17 | } 18 | } 19 | 20 | SECTION_CODE void WriteUint64(UINT8 **buffer, UINT64 value) 21 | { 22 | for (size_t i = 0; i < sizeof(UINT64); i++) { 23 | **buffer = (UINT8)(value & 0xFF); 24 | (*buffer)++; 25 | value >>= 8; 26 | } 27 | } 28 | 29 | SECTION_CODE void WriteString(UINT8 **buffer, const char *str, BOOL include_null) 30 | { 31 | while (*str) { 32 | **buffer = *str; 33 | (*buffer)++; 34 | str++; 35 | } 36 | if (include_null){ 37 | **buffer = 0; 38 | (*buffer)++; 39 | } 40 | } 41 | 42 | SECTION_CODE void WriteStringW(UINT8 **buffer, const wchar_t *str, BOOL include_null) 43 | { 44 | while (*str) { 45 | // Write each wide character (2 bytes) 46 | **buffer = (UINT8)(*str & 0xFF); // Lower byte 47 | (*buffer)++; 48 | **buffer = (UINT8)((*str >> 8) & 0xFF); // Upper byte 49 | (*buffer)++; 50 | str++; 51 | } 52 | if (include_null) { 53 | // Write the null terminator (0x0000) as 2 bytes 54 | **buffer = 0x00; 55 | (*buffer)++; 56 | **buffer = 0x00; 57 | (*buffer)++; 58 | } 59 | } 60 | 61 | SECTION_CODE void WriteBytes(UINT8 **buffer, const char *str, int size) 62 | { 63 | for (int i = 0; i < size; i++){ 64 | **buffer = *str; 65 | (*buffer)++; 66 | str++; 67 | } 68 | } 69 | 70 | SECTION_CODE UINT8 ReadUint8(UINT8 **buffer) 71 | { 72 | UINT8 value = **buffer; 73 | (*buffer)++; 74 | return value; 75 | } 76 | 77 | /** 78 | * TODO: Ensure we don't read past end of arg buffer 79 | */ 80 | SECTION_CODE UINT32 ReadUint32(UINT8 **buffer) 81 | { 82 | UINT32 value = 0; 83 | for (int i = 0; i < 4; i++) { 84 | value |= ((UINT32)**buffer) << (8 * i); 85 | (*buffer)++; 86 | } 87 | return value; 88 | } 89 | 90 | SECTION_CODE UINT8* ReadBytes(UINT8 **buffer, UINT32 length) 91 | { 92 | HANNIBAL_INSTANCE_PTR 93 | 94 | UINT8* bytes = (UINT8*)hannibal_instance_ptr->Win32.VirtualAlloc(NULL, length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 95 | if (!bytes) return NULL; 96 | 97 | pic_memcpy(bytes, *buffer, length); 98 | 99 | *buffer += length; 100 | 101 | return bytes; 102 | } 103 | 104 | SECTION_CODE char* ReadString(UINT8 **buffer, UINT32 length) 105 | { 106 | HANNIBAL_INSTANCE_PTR 107 | 108 | UINT32 bytesRead = 0; 109 | 110 | while (bytesRead < length && (*buffer)[bytesRead] != 0x00) { 111 | bytesRead++; 112 | } 113 | 114 | if (bytesRead == length) { 115 | return NULL; 116 | } 117 | 118 | char* str = (char*)hannibal_instance_ptr->Win32.VirtualAlloc(NULL, bytesRead + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 119 | if (!str) return NULL; 120 | 121 | pic_memcpy(str, *buffer, bytesRead + 1); // +1 to include the null byte 122 | 123 | *buffer += bytesRead + 1; 124 | 125 | return str; 126 | } 127 | 128 | 129 | SECTION_CODE wchar_t* ReadStringW(UINT8 **buffer, UINT32 length) 130 | { 131 | HANNIBAL_INSTANCE_PTR 132 | 133 | UINT32 wcharCount = 0; 134 | 135 | // Loop to find the null wide character (L'\0' which is 0x00 0x00 in UTF-16) 136 | while (wcharCount < length / 2) { // length is in bytes, so divide by 2 for wchar_t units 137 | if (((*buffer)[wcharCount * 2] == 0x00) && ((*buffer)[wcharCount * 2 + 1] == 0x00)) { 138 | break; 139 | } 140 | wcharCount++; 141 | } 142 | 143 | // If no null wide character found, return NULL (or handle error) 144 | if (wcharCount == length / 2) { 145 | return NULL; 146 | } 147 | 148 | wchar_t* str = (wchar_t*)hannibal_instance_ptr->Win32.VirtualAlloc(NULL, (wcharCount + 1) * sizeof(wchar_t), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 149 | if (!str) return NULL; 150 | 151 | pic_memcpy(str, *buffer, (wcharCount + 1) * sizeof(wchar_t)); // +1 to include the null wchar_t 152 | 153 | *buffer += (wcharCount + 1) * sizeof(wchar_t); 154 | 155 | return str; 156 | } -------------------------------------------------------------------------------- /src/utility_strings.c: -------------------------------------------------------------------------------- 1 | #include "utility_strings.h" 2 | 3 | /** 4 | * TODO: Cleanup. Functions are inconsistent in design and operation. 5 | */ 6 | 7 | 8 | 9 | /*! 10 | * @brief 11 | * Hashing data 12 | * 13 | * @param String 14 | * Data/String to hash 15 | * 16 | * @param Length 17 | * size of data/string to hash. 18 | * if 0 then hash data til null terminator is found. 19 | * 20 | * @return 21 | * hash of specified data/string 22 | */ 23 | SECTION_CODE ULONG HashString( 24 | _In_ PVOID String, 25 | _In_ SIZE_T Length 26 | ) { 27 | ULONG Hash = { 0 }; 28 | PUCHAR Ptr = { 0 }; 29 | UCHAR Char = { 0 }; 30 | 31 | if ( ! String ) { 32 | return 0; 33 | } 34 | 35 | Hash = H_MAGIC_KEY; 36 | Ptr = ( ( PUCHAR ) String ); 37 | 38 | do { 39 | Char = *Ptr; 40 | 41 | if ( ! Length ) { 42 | if ( ! *Ptr ) break; 43 | } else { 44 | if ( (UINT_PTR)( Ptr - (UINT_PTR)( String ) ) >= Length ) break; 45 | if ( !*Ptr ) ++Ptr; 46 | } 47 | 48 | if ( Char >= 'a' ) { 49 | Char -= 0x20; 50 | } 51 | 52 | Hash = ( ( Hash << 5 ) + Hash ) + Char; 53 | 54 | ++Ptr; 55 | } while ( TRUE ); 56 | 57 | return Hash; 58 | } 59 | 60 | SECTION_CODE char* pic_strcpy(char* dest, const char* src) 61 | { 62 | char* d = dest; 63 | const char* s = src; 64 | 65 | // Copy characters one by one until the null terminator is found 66 | while ((*d++ = *s++)) { 67 | // Loop continues until null terminator is copied 68 | } 69 | 70 | return dest; 71 | } 72 | 73 | SECTION_CODE size_t pic_strlen(const char *str) 74 | { // Len not including null terminator 75 | const char *s = str; 76 | while (*s != '\0') { 77 | s++; 78 | } 79 | return (size_t)(s - str); 80 | } 81 | 82 | SECTION_CODE size_t pic_strlenW(const wchar_t *wstr) 83 | { 84 | const wchar_t *s = wstr; 85 | while (*s != L'\0') { 86 | s++; 87 | } 88 | return (size_t)(s - wstr); 89 | } 90 | 91 | /* 92 | 0: If the strings are identical up to the end of the shortest string (i.e., they are equal). 93 | Positive Value: If the first differing character in str1 is greater than the corresponding character in str2. 94 | Negative Value: If the first differing character in str1 is less than the corresponding character in str2. 95 | */ 96 | SECTION_CODE int pic_strcmp(const char *str1, const char *str2) 97 | { 98 | while (*str1 && (*str1 == *str2)) { 99 | str1++; 100 | str2++; 101 | } 102 | return (*(unsigned char *)str1 - *(unsigned char *)str2); 103 | } 104 | 105 | SECTION_CODE int pic_strcmpW(const wchar_t *wstr1, const wchar_t *wstr2) 106 | { 107 | while (*wstr1 && (*wstr1 == *wstr2)) { 108 | wstr1++; 109 | wstr2++; 110 | } 111 | return (int)(*wstr1 - *wstr2); 112 | } 113 | 114 | SECTION_CODE int pic_strncmp(const char *s1, const char *s2, size_t n) 115 | { 116 | // Compare up to `n` characters or until a null terminator is encountered 117 | while (n > 0 && *s1 && *s2) { 118 | if (*s1 != *s2) { 119 | return (unsigned char)*s1 - (unsigned char)*s2; 120 | } 121 | s1++; 122 | s2++; 123 | n--; 124 | } 125 | 126 | // If we've exhausted the `n` characters, or one string ended 127 | if (n == 0) { 128 | return 0; 129 | } 130 | 131 | // If any of the strings are terminated, return the difference of the characters 132 | if (*s1 != *s2) { 133 | return (unsigned char)*s1 - (unsigned char)*s2; 134 | } 135 | 136 | return 0; 137 | } 138 | 139 | SECTION_CODE char *pic_strncpy(char *dest, const char *src, size_t n) 140 | { 141 | char *d = dest; 142 | const char *s = src; 143 | 144 | while (n > 0) { 145 | if (*s == '\0') { 146 | // If the source string ends, fill the rest with null bytes 147 | *d = '\0'; 148 | while (--n > 0) { 149 | *(++d) = '\0'; 150 | } 151 | break; 152 | } 153 | // Copy the character from source to destination 154 | *d++ = *s++; 155 | --n; 156 | } 157 | 158 | // If n reaches 0, ensure null-termination 159 | if (n == 0) { 160 | *d = '\0'; 161 | } 162 | 163 | return dest; 164 | } 165 | 166 | SECTION_CODE void pic_strcat(char *str1, char *str2) 167 | { 168 | 169 | int i, j=0; 170 | 171 | // Otherwise If UUID not initalized, segfault 172 | // if(str2 != '0x00'){ 173 | // return; 174 | // } 175 | 176 | //Copying string 2 to the end of string 1 177 | for( i=pic_strlen(str1); str2[j]!='\0'; i++ ) { 178 | str1[i]=str2[j]; 179 | j++; 180 | } 181 | 182 | str1[i]='\0'; 183 | } 184 | 185 | SECTION_CODE void pic_strcatW(wchar_t *wstr1, wchar_t *wstr2) 186 | { 187 | // Find the end of the first wide string 188 | wchar_t *end = wstr1; 189 | while (*end != L'\0') { 190 | end++; 191 | } 192 | 193 | // Append the second wide string to the end of the first one 194 | while (*wstr2 != L'\0') { 195 | *end = *wstr2; 196 | end++; 197 | wstr2++; 198 | } 199 | 200 | // Null-terminate the concatenated string 201 | *end = L'\0'; 202 | } 203 | 204 | // Helper function to convert DWORD to WCHAR string 205 | SECTION_CODE void dword_to_wchar(DWORD value, WCHAR* buffer, int base) 206 | { 207 | WCHAR* ptr = buffer; 208 | WCHAR* ptr1 = buffer; 209 | WCHAR tmp_char; 210 | DWORD quotient = value; 211 | 212 | if (value == 0) { 213 | *ptr++ = L'0'; 214 | *ptr = L'\0'; 215 | return; 216 | } 217 | 218 | // Convert to string 219 | while (quotient != 0) { 220 | DWORD digit = quotient % base; 221 | *ptr++ = (WCHAR)(digit < 10 ? L'0' + digit : L'A' + digit - 10); 222 | quotient /= base; 223 | } 224 | *ptr-- = L'\0'; 225 | 226 | // Reverse the string 227 | while (ptr1 < ptr) { 228 | tmp_char = *ptr; 229 | *ptr-- = *ptr1; 230 | *ptr1++ = tmp_char; 231 | } 232 | } 233 | 234 | SECTION_CODE void ulong_to_wchar(ULONG64 value, WCHAR *buffer) 235 | { 236 | WCHAR* ptr = buffer; 237 | WCHAR* ptr1 = buffer; 238 | WCHAR tmp_char; 239 | ULONG64 quotient = value; 240 | 241 | // Handle 0 explicitly 242 | if (value == 0) { 243 | *ptr++ = L'0'; 244 | *ptr = L'\0'; 245 | return; 246 | } 247 | 248 | // Convert to string 249 | while (quotient != 0) { 250 | ULONG64 digit = quotient % 10; 251 | *ptr++ = (WCHAR)(digit < 10 ? L'0' + digit : L'A' + digit - 10); 252 | quotient /= 10; 253 | } 254 | *ptr-- = L'\0'; 255 | 256 | // Reverse the string 257 | while (ptr1 < ptr) { 258 | tmp_char = *ptr; 259 | *ptr-- = *ptr1; 260 | *ptr1++ = tmp_char; 261 | } 262 | } -------------------------------------------------------------------------------- /src/utility_winapi_function_resolution.c: -------------------------------------------------------------------------------- 1 | // Credit: https://github.com/Cracked5pider/Stardust 2 | 3 | #include "utility_winapi_function_resolution.h" 4 | 5 | /*! 6 | * @brief 7 | * resolve module from peb 8 | * 9 | * @param Buffer 10 | * Buffer: either string or hash 11 | * 12 | * @param Hashed 13 | * is the Buffer a hash value 14 | * 15 | * @return 16 | * module base pointer 17 | */ 18 | SECTION_CODE PVOID get_module_ptr_from_peb( 19 | _In_ ULONG Hash 20 | ) { 21 | PLDR_DATA_TABLE_ENTRY Data = { 0 }; 22 | PLIST_ENTRY Head = { 0 }; 23 | PLIST_ENTRY Entry = { 0 }; 24 | 25 | Head = & NtCurrentPeb()->Ldr->InLoadOrderModuleList; 26 | Entry = Head->Flink; 27 | 28 | for ( ; Head != Entry ; Entry = Entry->Flink ) { 29 | Data = (PVOID)Entry; 30 | 31 | if ( HashString( Data->BaseDllName.Buffer, Data->BaseDllName.Length ) == Hash ) { 32 | return Data->DllBase; 33 | } 34 | } 35 | 36 | return NULL; 37 | } 38 | 39 | /*! 40 | * @brief 41 | * retrieve image header 42 | * 43 | * @param Image 44 | * image base pointer to retrieve header from 45 | * 46 | * @return 47 | * pointer to Nt Header 48 | */ 49 | SECTION_CODE PIMAGE_NT_HEADERS LdrpImageHeader( 50 | _In_ PVOID Image 51 | ) { 52 | PIMAGE_DOS_HEADER DosHeader = { 0 }; 53 | PIMAGE_NT_HEADERS NtHeader = { 0 }; 54 | 55 | DosHeader = (PVOID)( Image ); 56 | 57 | if ( DosHeader->e_magic != IMAGE_DOS_SIGNATURE ) { 58 | return NULL; 59 | } 60 | 61 | NtHeader = (PVOID)( (UINT_PTR)( Image ) + DosHeader->e_lfanew ); 62 | 63 | if ( NtHeader->Signature != IMAGE_NT_SIGNATURE ) { 64 | return NULL; 65 | } 66 | 67 | return NtHeader; 68 | } 69 | 70 | SECTION_CODE PVOID get_func_ptr_from_module_eat( 71 | _In_ PVOID Library, 72 | _In_ ULONG Function 73 | ) { 74 | 75 | HANNIBAL_INSTANCE_PTR 76 | 77 | PVOID Address = { 0 }; 78 | PIMAGE_NT_HEADERS NtHeader = { 0 }; 79 | PIMAGE_EXPORT_DIRECTORY ExpDir = { 0 }; 80 | SIZE_T ExpDirSize = { 0 }; 81 | PDWORD AddrNames = { 0 }; 82 | PDWORD AddrFuncs = { 0 }; 83 | PWORD AddrOrdns = { 0 }; 84 | PCHAR FuncName = { 0 }; 85 | 86 | if ( ! Library || ! Function ) { 87 | return NULL; 88 | } 89 | 90 | if ( ! ( NtHeader = LdrpImageHeader( Library ) ) ) { 91 | return NULL; 92 | } 93 | 94 | ExpDir = (PVOID)( Library + NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress ); 95 | ExpDirSize = NtHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size; 96 | AddrNames = (PVOID)( Library + ExpDir->AddressOfNames ); 97 | AddrFuncs = (PVOID)( Library + ExpDir->AddressOfFunctions ); 98 | AddrOrdns = (PVOID)( Library + ExpDir->AddressOfNameOrdinals ); 99 | 100 | for ( DWORD i = 0; i < ExpDir->NumberOfNames; i++ ) { 101 | 102 | FuncName = (PVOID)( (UINT_PTR)( Library ) + AddrNames[ i ] ); 103 | 104 | if ( HashString( FuncName, 0 ) != Function ) { 105 | continue; 106 | } 107 | 108 | Address = (PVOID)( (UINT_PTR)( Library ) + AddrFuncs[ AddrOrdns[ i ] ] ); 109 | 110 | if ( ( (UINT_PTR)( Address ) >= (UINT_PTR)( ExpDir ) ) && 111 | ( (UINT_PTR)( Address ) < (UINT_PTR)( ExpDir ) + ExpDirSize ) 112 | ) { 113 | // 114 | // TODO: need to add support for forwarded functions 115 | // 116 | 117 | __debugbreak(); 118 | } 119 | 120 | break; 121 | } 122 | 123 | return Address; 124 | } -------------------------------------------------------------------------------- /windows_makefile: -------------------------------------------------------------------------------- 1 | # Generates a fully position independent .bin 2 | 3 | # Make sure all the structs match what you compiled in the agent you're executing this in. 4 | # For example, if the Instance struct or task structs don't match it will crash and take your agent with it. 5 | 6 | #---------- Performance ----------# 7 | 8 | # MAKEFLAGS += -s -j1 9 | MAKEFLAGS += -s -j$(NUMBER_OF_PROCESSORS) 10 | 11 | #---------- Project Settings ----------# 12 | 13 | PROJECT := hannibal 14 | CC_X64 := x86_64-w64-mingw32-g++ 15 | 16 | #---------- Compiler Flags ----------# 17 | 18 | # https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html 19 | # https://gcc.gnu.org/onlinedocs/gcc/Cygwin-and-MinGW-Options.html 20 | # https://forum.tinycorelinux.net/index.php/topic,26375.0.html 21 | 22 | CFLAGS := -Os -fno-asynchronous-unwind-tables -nostdlib 23 | CFLAGS += -nolibc -nostdlib++ 24 | CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1 25 | CFLAGS += -s -ffunction-sections -falign-jumps=1 -w 26 | CFLAGS += -falign-labels=1 -fPIC 27 | CFLAGS += -Iinclude -masm=intel -fpermissive -mrdrnd 28 | CFLAGS += -D PIC_BUILD -D PROFILE_MYTHIC_HTTP 29 | 30 | #---------- Linker Flags ----------# 31 | 32 | LDFLAGS := -Wl,-Tscripts/Linker.ld 33 | LDFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup 34 | 35 | #---------- Paths ----------# 36 | 37 | ASM_DIR := asm/x64 38 | SRC_DIR := src 39 | OBJ_DIR := bin/obj 40 | BIN_DIR := bin 41 | SRC_FILES := $(wildcard $(SRC_DIR)/*.c) 42 | OBJ_FILES := $(SRC_FILES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) 43 | ASM_FILES := $(wildcard $(ASM_DIR)/*.asm) 44 | ASM_OBJ_FILES := $(ASM_FILES:asm/x64/%.asm=bin/obj/%.o) 45 | 46 | #---------- Targets ----------# 47 | 48 | all: $(BIN_DIR)/$(PROJECT).exe 49 | 50 | $(BIN_DIR)/$(PROJECT).exe: $(ASM_OBJ_FILES) $(OBJ_FILES) 51 | @ echo "[+] Linking x64 Executable" 52 | @ $(CC_X64) bin/obj/*.o -o $(BIN_DIR)/$(PROJECT).exe $(CFLAGS) $(LDFLAGS) 53 | @python scripts/build.py -f $(BIN_DIR)/$(PROJECT).exe -o $(BIN_DIR)/$(PROJECT).bin 54 | @ del /q bin\obj\*.o 2>nul 55 | @ del /q bin\*.exe 2>nul 56 | 57 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 58 | @echo "[+] Compiling $? -> $@" 59 | @ $(CC_X64) -o $@ -c $? $(CFLAGS) $(LDFLAGS) 60 | 61 | $(ASM_OBJ_FILES): $(OBJ_DIR)/%.o: asm/x64/%.asm 62 | @echo "[+] Assembling $? -> $@" 63 | @ nasm -f win64 $? -o $@ 64 | 65 | #---------- Utility ----------# 66 | 67 | clean: 68 | @ del /q bin\obj\*.o 2>nul 69 | @ del /q bin\obj\*.s 2>nul 70 | @ del /q bin\obj\*.ii 2>nul 71 | @ del /q bin\*.bin 2>nul 72 | @ del /q bin\*.exe 2>nul 73 | 74 | print: 75 | @echo "SRC_FILES: $(SRC_FILES)" 76 | @echo "OBJ_FILES: $(OBJ_FILES)" 77 | @echo "ASM_FILES: $(ASM_FILES)" 78 | @echo "ASM_OBJ_FILES": $(ASM_OBJ_FILES)" --------------------------------------------------------------------------------