diff --git a/README.md b/README.md index 5cfc236..69cf319 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# SCLauncher - Basic Shellcode Tester and Debugger +# SCLauncher - Basic Shellcode Tester, Debugger and PE-File Wrapper -This program is designed to load 32-bit shellcode and allow for execution or debugging. The provided binary can be found in the `binaries` folder with a SHA 256 of `8fe5bd48daa90fe4be9321244912d23e0ede5010d4ea72a66c66fabe66c03d2e`. +This program is designed to load 32-bit or 64-bit shellcode and allow for execution or debugging. In addition, it can produce executable PE files based on the desired shellcode. This can ease testing as the output binary can be used by standard reverse engineering tools (i.e. IDA Pro (even free) and debuggers). -You can view a demo of this tool on [YouTube](https://youtu.be/U8SkM99TB2g) +Release binaries are available. You can view a demo of this tool on [YouTube](https://youtu.be/U8SkM99TB2g) ## Compiling From Source @@ -24,4 +24,10 @@ You can use the ```-f``` argument to define a path to a file that contains shell The source code also allows for the inclusion of shellcode through an internal array, named ```shellcode```. The bytes of shellcode must be copied into the array value and the size of the array adjusted. The program can then be compiled and executed - this mode does not support the ```-f``` argument. - \ No newline at end of file + + +## Producing a PE file + +You can use the ```-pe``` argument to produce a PE file that essentially wraps the shellcode. The shellcode is placed in the ```.text``` section. The entry point is defined as the beginning of the section, unless the ```-ep``` argument is used. This argument will define an offset from the beginning of the section and be used to update the PE files entry point (i.e. AddressOfEntry field). Additionally, the ```-64``` argument can be used to generate a 64-bit PE file, likely for 64-bit shellcode. The resulting PE file can be analyzed via common reverse engineering tools such as IDA Pro, Ghidra or a debugger such as x32dbg/WinDbg/etc. + + \ No newline at end of file diff --git a/binaries/sclauncher.exe b/binaries/sclauncher.exe deleted file mode 100644 index 643d0e3..0000000 Binary files a/binaries/sclauncher.exe and /dev/null differ diff --git a/images/help.png b/images/help.png index 8455ce2..c457a8a 100644 Binary files a/images/help.png and b/images/help.png differ diff --git a/images/produce_pe.png b/images/produce_pe.png new file mode 100644 index 0000000..997cfbb Binary files /dev/null and b/images/produce_pe.png differ diff --git a/pe_file.h b/pe_file.h new file mode 100644 index 0000000..484ed40 --- /dev/null +++ b/pe_file.h @@ -0,0 +1,226 @@ +#include + +struct _IMAGE_DOS_STUB +{ + char data[64]; + //char rich_header[104]; +}; + +int round_up(int val) { + val = val / 1000; + if ((int)val % 1000 > 0){ + val++; + } + val *= 1000; + return val; +} + +void create_pe(char * sc_inject, int shellcode_size, int entry_point, int is_64) { + unsigned int tmp_offset = 0, section_padding = 0; + char* padding_buffer = NULL; + FILE*fp = NULL, *pe = NULL; + + struct _IMAGE_DOS_HEADER idh = { + 0x5A4D, + 0x0090, + 0x0003, + 0x0000, + 0x0004, + 0x0000, + 0xFFFF, + 0x0000, + 0x00B8, + 0x0000, + 0x0000, + 0x0000, + 0x0040, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x0000, + 0x00000080, + }; + + struct _IMAGE_DOS_STUB ids = {0}; + memmove(&ids.data, "Brought to you by sclauncher.exe",33); + + struct _IMAGE_FILE_HEADER ifh = { + 0x14C, + 0x0001, + 0, + 0, + 0, + 224, + 0x0102 + }; + + struct _IMAGE_OPTIONAL_HEADER ioh = { + 0x10B, + 14, + 16, + 0, + 0, + 0, + 0x1000, + 0x1000, + 0, + 0x400000, + 0x1000, + 0x200, + 6, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 0x400, + 0, + 3, + 0x8140, + 0x100000, + 0x1000, + 0x100000, + 0x1000, + 0, + 0, + 0 + }; + + struct _IMAGE_OPTIONAL_HEADER64 ioh64 = { + 0x20B,//PE32+ + 14, + 16, + 0, + 0, + 0, + 0x1000, + 0x1000, + 0x400000, + 0x1000, + 0x200, + 6, + 0, + 0, + 0, + 6, + 0, + 0, + 0, + 0x400, + 0, + 3, + 0x8140, + 0x100000, + 0x1000, + 0x100000, + 0x1000, + 0, + 0, + 0 + }; + + struct _IMAGE_NT_HEADERS inh = { + 0x00004550, + ifh, + ioh + }; + + struct _IMAGE_NT_HEADERS64 inh64 = { + 0x00004550, + ifh, + ioh64 + }; + + struct _IMAGE_SECTION_HEADER ish = { + ".josh", + 0, + 0x1000, + 0, + 0x400, + 0, + 0, + 0, + 0, + 0xE0000020 + }; + + puts("[PE] Adding shellcode..."); + + //update lfanew based on size of image_dos_header and image_dos_stub + idh.e_lfanew = (sizeof(idh) + sizeof(ids)); + + //calculate difference between headers and first section + if (is_64) { + section_padding = 0x400 - (sizeof(idh) + sizeof(ids) + sizeof(inh64) + sizeof(ish)); + } else { + section_padding = 0x400 - (sizeof(idh) + sizeof(ids) + sizeof(inh) + sizeof(ish)); + } + + //update entry point + if( entry_point ) { + if(is_64) { + inh64.OptionalHeader.AddressOfEntryPoint = entry_point + 0x1000; + } else { + inh.OptionalHeader.AddressOfEntryPoint = entry_point + 0x1000; + } + } + + //update the raw and virtual size of the section + ish.SizeOfRawData = shellcode_size; + ish.Misc.VirtualSize = shellcode_size; + + if (is_64) { + inh64.FileHeader.Machine = 0x8664; + inh64.OptionalHeader.SizeOfCode = shellcode_size; + inh64.OptionalHeader.SizeOfImage = round_up(shellcode_size + 0x1000); + inh64.FileHeader.SizeOfOptionalHeader = sizeof(inh64.OptionalHeader); + + inh64.FileHeader.TimeDateStamp = time(NULL); + } else { + inh.OptionalHeader.SizeOfCode = shellcode_size; + //virtual address + section size rounded up. shellcode size will equal section size + inh.OptionalHeader.SizeOfImage = round_up(shellcode_size + 0x1000); + inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader); + + inh.FileHeader.TimeDateStamp = time(NULL); + } + + //create array for padding bytes + padding_buffer = (char*)calloc(section_padding,1); + + if (is_64) { + pe = fopen("sc_output_x64.exe", "wb"); + printf("[PE] Done building PE file...created file sc_output_x64.exe\n"); + } else { + pe = fopen("sc_output.exe", "wb"); + printf("[PE] Done building PE file...created file sc_output.exe\n"); + } + fwrite(&idh,sizeof(idh),1,pe); + fwrite(&ids,sizeof(ids),1,pe); + if(is_64) { + fwrite(&inh64,sizeof(inh64),1,pe); + } else { + fwrite(&inh,sizeof(inh),1,pe); + } + fwrite(&ish,sizeof(ish),1,pe); + fwrite(padding_buffer, _msize(padding_buffer),1,pe); + fwrite(sc_inject,shellcode_size, 1, pe); + + fclose(pe); + free(padding_buffer); +} \ No newline at end of file diff --git a/sclauncher.c b/sclauncher.c index c4069ad..721e86a 100644 --- a/sclauncher.c +++ b/sclauncher.c @@ -1,8 +1,21 @@ #include #include #include +#include "pe_file.h" -char shellcode[1] = ""; //use this as a byte array to load shellcode. Example: char shellcode[2] = "\x55\xEB" +//use this as a byte array to load shellcode. Example: char shellcode[2] = "\x55\xEB" +char shellcode[] = ""; + +char* validate_argument(char*arg){ + char*p = NULL; + p = strstr(arg,"="); + if (p == NULL){ + printf("[!] Equal sign between \"%s\" parameter not found, exiting!",arg); + exit(1); + } + p++; + return p; +} void usage(void) { puts("[~] Simple shellcode launcher and debugger! This program can read shellcode from a file or use an internal array."); @@ -10,6 +23,8 @@ void usage(void) { puts("\t-f: path to file to load shellocode. If you don't provide a file, \n\t\t it will check for an internal array - see source code."); puts("\t-bp: insert a breakpoint before the shellcode, only use if debugging"); puts("\t-o: adjust entry point offset in bytes based on zero-index"); + puts("\t-pe: creates an executable version of the shellcode in a PE file. Only 32-bit output files are supported at this time"); + puts("\t-64: PE file creation only, creates a 64-bit PE file - assumes 64-bit shellcode"); } int main(int argc, char **argv) { @@ -19,41 +34,39 @@ int main(int argc, char **argv) { int insert_bp = 0; char file_path[100] = {0}; FILE*fp = NULL; + char produce_pe = 0; + int is_64 = 0; void*stage = NULL; int i = 0, len = 0, sc_part1 = 0, sc_part2 = 0; size_t bytes_read = 0; void* target_addy = NULL; + char* sc_stage = NULL; int arg_count = 0; - char*p = NULL; + char*command_arg = NULL; //parse command-line arguments for (arg_count = 0; arg_count < argc; arg_count++) { - if (!strncmp(argv[arg_count],"-h",2)) { usage(); exit(0); - } else if (!strncmp(argv[arg_count],"-o",2)) { - p = strstr(argv[arg_count],"="); - if (p == NULL){ - printf("[!] Equal sign between \"%s\" parameter not found, exiting!",argv[arg_count]); - exit(1); - } - p++; - offset = atoi(p); - printf("[*] Adjusting shellcode entry point (hex): +%08x\n", offset); + } else if (!strncmp(argv[arg_count], "-pe",3)){ + produce_pe = 1; + printf("[*] Producing a PE file...\n"); + } else if (!strncmp(argv[arg_count],"-ep",3)) { + command_arg = validate_argument(argv[arg_count]); + offset = atoi(command_arg); + printf("[*] Adjusting shellcode entry point: +0x%08x\n", offset); } else if(!strncmp(argv[arg_count],"-f",2)){ - p = strstr(argv[arg_count],"="); - if (p == NULL){ - printf("[!] Equal sign between \"%s\" parameter not found, exiting!",argv[arg_count]); - exit(1); - } - p++; - strncpy(file_path,p,strlen(argv[arg_count])); + command_arg = validate_argument(argv[arg_count]); + strncpy(file_path,command_arg,strlen(command_arg)); } else if(!strncmp(argv[arg_count],"-bp",3)){ insert_bp = 1; hexcc[0] = 0xCC; puts("[*] Inserting breakpoint before shellcode"); + } else if(!strncmp(argv[arg_count],"-64",3)){ + is_64 = 1; + puts("[*] Producing a 64-bit PE file"); } } @@ -66,28 +79,35 @@ int main(int argc, char **argv) { fseek(fp, 0L, SEEK_END); shellcode_size = ftell(fp); if (insert_bp && offset >= shellcode_size) { - printf("[!] Breakpoint offset beyond size of shellcode, exiting!"); + printf("[!] Breakpoint entry point beyond size of shellcode, exiting!"); exit(1); } printf("[*] Found %d bytes of shellcode\n",shellcode_size); fseek(fp, 0L, SEEK_SET); - - stage = VirtualAlloc(0, shellcode_size + 1, 0x1000,0x40 ); - printf("[*] Allocated memory at %p\n", stage); - if (insert_bp && offset) { - bytes_read = fread((char*)stage, sizeof(char), offset-1, fp); - printf("[*] %zu bytes of shellcode read\n", bytes_read); - memmove((char*)stage+offset-1, &hexcc, 1); - printf("[*] Breakpoint inserted at %p\n",(char*)stage+offset-1); - bytes_read = fread((char*)stage+offset, sizeof(char), (shellcode_size - offset +1), fp); - printf("[*] %zu remaining bytes of shellcode read\n", bytes_read); - } else if (insert_bp) { - memmove(stage, &hexcc, 1); - fread((char*)stage+1, sizeof(char), shellcode_size, fp); + + if (produce_pe) { + puts("[PE] Producing PE file from shellcode found in a file, then exiting."); + sc_stage = (char*)malloc(shellcode_size); + fread((char*)sc_stage, sizeof(char), shellcode_size, fp); + create_pe(sc_stage,shellcode_size, offset, is_64); + free(sc_stage); } else { - fread((char*)stage, sizeof(char), shellcode_size, fp); + stage = VirtualAlloc(0, shellcode_size + 1, 0x1000,0x40 ); + printf("[*] Allocated memory at %p\n", stage); + if (insert_bp && offset) { + bytes_read = fread((char*)stage, sizeof(char), offset-1, fp); + printf("[*] %zu bytes of shellcode read\n", bytes_read); + memmove((char*)stage+offset-1, &hexcc, 1); + printf("[*] Breakpoint inserted at %p\n",(char*)stage+offset-1); + bytes_read = fread((char*)stage+offset, sizeof(char), (shellcode_size - offset +1), fp); + printf("[*] %zu remaining bytes of shellcode read\n", bytes_read); + } else if (insert_bp) { + memmove(stage, &hexcc, 1); + fread((char*)stage+1, sizeof(char), shellcode_size, fp); + } else { + fread((char*)stage, sizeof(char), shellcode_size, fp); + } } - printf("[*] Shellcode copied\n"); fclose(fp); } else { puts("[!] Error opening file... exiting"); @@ -99,35 +119,39 @@ int main(int argc, char **argv) { shellcode_size = strlen(shellcode); printf("[*] Found %d bytes of shellcode\n",shellcode_size); - stage = VirtualAlloc(0, shellcode_size + 1, 0x1000,0x40 ); - printf("[*] Allocated memory at %p\n", stage); - - if(insert_bp && offset) { - memmove(stage, &shellcode, offset -1 ); - memmove((char*) stage+offset-1, &hexcc, 1); - memmove((char*) stage+offset, &shellcode[offset-1],shellcode_size - offset +1); - } else if (insert_bp) { - memmove(stage, &hexcc, 1); - memmove((char*)stage+1, &shellcode, shellcode_size); + if (produce_pe ) { + puts("[PE] Producing PE file from shellcode found internally, then exiting."); + create_pe(shellcode, shellcode_size, offset, is_64); } else { - memmove((char*)stage, &shellcode, shellcode_size); - } + stage = VirtualAlloc(0, shellcode_size + 1, 0x1000,0x40 ); + printf("[*] Allocated memory at %p\n", stage); - printf("[*] Shellcode copied\n"); - + if(insert_bp && offset) { + memmove(stage, &shellcode, offset -1 ); + memmove((char*) stage+offset-1, &hexcc, 1); + memmove((char*) stage+offset, &shellcode[offset-1],shellcode_size - offset +1); + } else if (insert_bp) { + memmove(stage, &hexcc, 1); + memmove((char*)stage+1, &shellcode, shellcode_size); + } else { + memmove((char*)stage, &shellcode, shellcode_size); + } + } } else { puts("[!] No shellcode found, exiting..."); exit(1); } - if (offset) { - target_addy = (char*)stage + offset - 1; //adjust for zero-based address - printf("[*] Adjusting offset, new entry point: 0x%p\n", target_addy); - } else { - target_addy = stage; - } + if( !produce_pe) { + if (offset) { + target_addy = (char*)stage + offset - 1; //adjust for zero-based address + printf("[*] Adjusting offset, new entry point: 0x%p\n", target_addy); + } else { + target_addy = stage; + } - printf("[*} Executing shellcode at %p, enjoy :)\n",target_addy); - int(*sc)() = target_addy; - sc(); + printf("[*} Executing shellcode at %p, enjoy :)\n",target_addy); + int(*sc)() = target_addy; + sc(); + } } \ No newline at end of file