├── 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 |
268 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
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)"
--------------------------------------------------------------------------------