├── HiveRansomwareV5-file_decryptor
├── HiveRansomwareV5-file_decryptor.vcxproj.user
├── HiveRansomwareV5-file_decryptor.vcxproj.filters
├── HiveRansomwareV5-file_decryptor.vcxproj
└── HiveRansomwareV5-file_decryptor.cpp
└── README.md
/HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;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 | File di origine
20 |
21 |
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HiveV5 file decryptor PoC
2 |
3 | ## Introduction
4 |
5 | The work done in the last few months has been necessary to reveal the malicious file encryption mechanism of Hive v5-5.2.
6 | The work was divided into two parts
7 | 1. [Keystream decryption](https://github.com/reecdeep/HiveV5_keystream_decryptor)
8 | 2. File decryption using the decrypted keystream
9 |
10 | I would like to thank the great [@rivitna](https://twitter.com/rivitna2) for the support, dialogue and advices of these months of work!
11 | Please take note of [rivitna's github](https://github.com/rivitna) full of useful informations about Hive ransomware and more.
12 |
13 | In this readme you will find some information about the file decryption algorithm, referring you to the PoC for a more complete picture of how it works.
14 | A keystream is an encrypted cleartext. A cleartext is a set of 0xA00000 bytes to which the first 0x2FFF00 bytes have been appended, for a total of 0xCFFF00 bytes. These bytes were created with the weak algorithm already discussed in the first part released in July 2022.
15 | Here below is a example of cleartext:
16 |
17 | 
18 |
19 |
20 | The Hive sample analyzed and referred to in this document was chosen from [this list](https://github.com/rivitna/Malware/blob/main/Hive/Hive_samples.txt) created by [@rivitna](https://twitter.com/rivitna2) to which my warmest thanks go.
21 | To get an idea of the complexity of ransomware, please take a look at [this analysis](https://www.microsoft.com/security/blog/2022/07/05/hive-ransomware-gets-upgrades-in-rust/) published by Microsoft Threat Intelligence Center (MSTIC).
22 |
23 |
24 | ## File encryption algorithm
25 |
26 | The cleartext (a decrypted keystream) is used by Hive ransomware when encrypting each file. When encrypting a file, Hive ransomware calculates two integers referring to precise positions in the cleartext (offsets) to be used to encrypt the file according to the following formula:
27 |
28 | 
29 |
30 | where c = i % 0x2FFF00 e d = i % 0x2FFD00 , with i as a byte counter.
31 |
32 |
33 |
34 | ## The encrypted file extension
35 | The preliminary operations before writing a file are:
36 | - Renaming the file using MoveFileExW and changing its extension;
37 | - Writing the renamed file with the result of the xor operation shown above.
38 |
39 | 
40 |
41 | Also in this case the cleartext plays a fundamental role. In fact it is used for:
42 | 1. Determine the keystream ID (first 6 bytes) using a hash function
43 | 2. Encrypt the positions (offsets) used to extract bytes from the cleartext
44 | However, the first offset is encrypted using a fixed position of the cleartext and is different for each Hive 5/5.1/5.2 sample.
45 | A kind of magical value. In many Hive 5/5.1 artifacts this magic value is shown explicitly inside a memory reference, like in this case 0x98072A :
46 |
47 | 
48 |
49 | Or this case 0x7539D:
50 |
51 | 
52 |
53 | But in the next evidence the for loop is slightly different and has been written in such a way as not to explicit the magic value that we need to identify. This concerns an artifact belonging to Hive 5.2:
54 |
55 | 
56 |
57 | In this case it is possible to use the offset bruteforce function present in the released tool, using a file with a known extension and the relative decrypted keystream. Using the header of the encrypted file and the header of the unencrypted file it is possible to understand what is the offset from which the decryptor must start to decrypt the file.
58 |
59 | The file encryption mode can have two values: 0xFB or 0xFF
60 | - 0xFB means that the ransomware encrypted the entire file without leaving any portion of the file unencrypted.
61 | - 0xFF means that the ransomware calculated a NCB (not encrypted block) for each file and encrypting blocks of 0x100000 bytes.
62 | For further information regarding the calculation of the size of the unencrypted blocks and the cleartext offset, please refer to the PoC code.
63 |
64 |
65 | ## Usage
66 | The program offers two options:
67 |
68 | 
69 |
70 | 1. Decryption of files using the decrypted keystream. You need to enter the special offset present in the sample that encrypted the files.
71 | 2. Given a file with a known header (PDF, JPG, PNG, Office files) brute the possible value of the special offset by decrypting the first bytes and looking for a match with the known signature
72 |
73 |
74 | ## References
75 |
76 |
77 |
--------------------------------------------------------------------------------
/HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.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 | 15.0
23 | {BB3DC7CE-8196-4748-B406-A3E18D37834C}
24 | Win32Proj
25 | hivefile
26 | 10.0.17763.0
27 | hive_file_decryption
28 |
29 |
30 |
31 | Application
32 | true
33 | v141
34 | Unicode
35 |
36 |
37 | Application
38 | false
39 | v141
40 | true
41 | Unicode
42 |
43 |
44 | Application
45 | true
46 | v141
47 | Unicode
48 |
49 |
50 | Application
51 | false
52 | v141
53 | true
54 | Unicode
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | true
76 |
77 |
78 | true
79 |
80 |
81 | false
82 |
83 |
84 | false
85 |
86 |
87 |
88 |
89 |
90 | Level3
91 | Disabled
92 | true
93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
94 | true
95 |
96 |
97 | Console
98 | true
99 |
100 |
101 |
102 |
103 |
104 |
105 | Level3
106 | Disabled
107 | true
108 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
109 | true
110 |
111 |
112 | Console
113 | true
114 |
115 |
116 |
117 |
118 |
119 |
120 | Level3
121 | MaxSpeed
122 | true
123 | true
124 | true
125 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
126 | true
127 |
128 |
129 | Console
130 | true
131 | true
132 | true
133 |
134 |
135 |
136 |
137 |
138 |
139 | Level3
140 | MaxSpeed
141 | true
142 | true
143 | true
144 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
145 | true
146 |
147 |
148 | Console
149 | true
150 | true
151 | true
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/HiveRansomwareV5-file_decryptor/HiveRansomwareV5-file_decryptor.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #pragma comment(lib,"shlwapi.lib")
21 |
22 | std::vector base64_decode(const std::string & in);
23 | void openFile(std::string file_name, unsigned char* buffer, int dim);
24 | void file_decrypt();
25 | void offset_bruteforce();
26 |
27 |
28 | //the working path will be updated when selecting files
29 | std::string currentWorkingPath = "";
30 |
31 | const char base64_url_alphabet[] = {
32 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
33 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
34 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
35 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
36 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
37 | };
38 |
39 | //array for storing decrypted keystream
40 | unsigned char* decrypted_keystream = new unsigned char[0xCFFF00]();
41 | //size of NOT Cyphered Block
42 | unsigned int ncb_size;
43 |
44 | //this field changes, on each HIVE sample!
45 | //unsigned int specialOffset = 0x2ABD4E;
46 | //unsigned int specialOffset = 0x98072A;
47 | unsigned int specialOffset = 0;
48 |
49 | //flag NCB swicth /non crypted block)
50 | boolean ncb_override = true;
51 | unsigned char* knownheader;
52 |
53 |
54 |
55 |
56 |
57 | int main()
58 | {
59 |
60 | std::string j;
61 |
62 | std::cout << "Hive ransomware V5 - file decryptor PoC" << std::endl;
63 | std::cout << "--------------------------------------------\n" << std::endl;
64 |
65 | std::cout << "1. Decrypt a file using decrypted keystream" << std::endl;
66 | std::cout << "2. Offset bruteforce" << std::endl;
67 | std::cout << "your move: " << std::endl;
68 |
69 | j = std::cin.get();
70 |
71 |
72 | if (j == "1")
73 | {
74 | file_decrypt();
75 | }
76 | else
77 | if (j == "2")
78 | {
79 | offset_bruteforce();
80 | }
81 |
82 |
83 | }
84 |
85 |
86 |
87 |
88 |
89 | void file_decrypt()
90 | {
91 | //open the decrypted keystream file
92 | std::cout << "Please enter the decrypted keystream file: \n";
93 | std::string path_decrypted_keystream;
94 | std::cin >> path_decrypted_keystream;
95 | path_decrypted_keystream.erase(remove(path_decrypted_keystream.begin(), path_decrypted_keystream.end(), '\"'), path_decrypted_keystream.end());
96 | std::ifstream ifs(path_decrypted_keystream.c_str());
97 |
98 | if (!ifs)
99 | {
100 | std::cout << "error opening keystream file!";
101 | }
102 | else
103 | {
104 | //ask for offset
105 | std::cout << "Please enter the offset value in hex format (0x12345678): \n";
106 | std::cin >> std::hex >> specialOffset;
107 |
108 |
109 |
110 |
111 | while (true)
112 | {
113 | //open the file to be decrypted
114 | std::cout << "Please enter the encrypted file: \n";
115 | std::string path_encrypted_file;
116 | std::cin >> path_encrypted_file;
117 | path_encrypted_file.erase(remove(path_encrypted_file.begin(), path_encrypted_file.end(), '\"'), path_encrypted_file.end());
118 | std::ifstream ifs(path_encrypted_file.c_str());
119 |
120 | //updating working path based on encrypted file position in file system
121 | currentWorkingPath = path_encrypted_file.substr(0, path_encrypted_file.find_last_of("/\\"));
122 |
123 | if (!ifs)
124 | {
125 | std::cout << "error opening encrypted file!\n";
126 | }
127 | else
128 | {
129 | //get encrypted file extension
130 | size_t i = path_encrypted_file.rfind('.', path_encrypted_file.length());
131 | if (i != std::string::npos)
132 | {
133 | std::string extension = (path_encrypted_file.substr(i + 1, path_encrypted_file.length() - i));
134 | std::vector decoded_extension = base64_decode(extension);
135 | //byte dimension of decoded byte
136 | int dim_decoded_extension = decoded_extension.size();
137 |
138 | //transform to array
139 | unsigned char* decoded_extension_array = new unsigned char[dim_decoded_extension]();
140 | std::copy(decoded_extension.begin(), decoded_extension.end(), decoded_extension_array);
141 |
142 | //read the decrypted keystream
143 | openFile(path_decrypted_keystream, decrypted_keystream, 0xCFFF00);
144 |
145 | //extract xor key from decrypted keystream
146 | unsigned char* first_offset_xor = new unsigned char[4]();
147 | memcpy(first_offset_xor, decrypted_keystream + specialOffset - 0x4, 4);
148 |
149 | //get first offset
150 | unsigned char* first_offset = new unsigned char[4]();
151 | memcpy(first_offset, decoded_extension_array + dim_decoded_extension - 8, 4);
152 |
153 | //read decryption mode byte
154 | unsigned char decryption_mode = decoded_extension_array[6];
155 |
156 | //case decryption mode is 0xFB
157 | if (decryption_mode == '0xFB')
158 | {
159 | //no crypted block mode
160 | ncb_override = true;
161 | }
162 | else
163 | {
164 | //case decryption mode is 0xFF
165 | //crypted block mode on!
166 | ncb_override = false;
167 | }
168 |
169 |
170 |
171 | //first offset xored
172 | unsigned char* first_offset_xored = new unsigned char[4]();
173 |
174 | //xor first offset with xor key extracted from decrypted keystream
175 | for (int i = 0; i < 4; i++)
176 | {
177 | first_offset_xored[i] = first_offset[i] ^ first_offset_xor[i];
178 | }
179 |
180 | //first offset xored to littleEndian
181 | unsigned char* first_offset_xored_le = new unsigned char[4]();
182 | first_offset_xored_le[0] = first_offset_xored[3];
183 | first_offset_xored_le[1] = first_offset_xored[2];
184 | first_offset_xored_le[2] = first_offset_xored[1];
185 | first_offset_xored_le[3] = first_offset_xored[0];
186 |
187 | //TO int
188 | unsigned int first_offset_xored_int = (first_offset_xored_le[0] << 24 |
189 | first_offset_xored_le[1] << 16 |
190 | first_offset_xored_le[2] << 8 |
191 | first_offset_xored_le[3]);
192 |
193 | //mul offset with fixed value 0x3333347B
194 | unsigned long long mul1 = (unsigned long long) first_offset_xored_int * 0x3333347B;
195 |
196 | //shifting to the right the upper part of the mul1 long long by 15
197 | unsigned int shift1 = (unsigned int)(mul1 >> 32) >> 0x15;
198 |
199 | //mul shift with fixed value 0x9FFFFC
200 | unsigned int mul2 = shift1 * 0x9FFFFC;
201 |
202 | //difference between offset1 and mul2 (acts like second offset xor key)
203 | unsigned int diff1 = first_offset_xored_int - mul2;
204 |
205 | //extract second xor key from decrypted keystream
206 | unsigned char* second_offset_xor = new unsigned char[4]();
207 | memcpy(second_offset_xor, decrypted_keystream + diff1, 4);
208 |
209 | //get second offset
210 | unsigned char* second_offset = new unsigned char[4]();
211 | memcpy(second_offset, decoded_extension_array + dim_decoded_extension - 4, 4);
212 |
213 |
214 | //second offset xored
215 | unsigned char* second_offset_xored = new unsigned char[4]();
216 |
217 | //xor second offset with second xor key extracted from decrypted keystream
218 | for (int i = 0; i < 4; i++)
219 | {
220 | second_offset_xored[i] = second_offset[i] ^ second_offset_xor[i];
221 | }
222 |
223 | //second offset xored to littleEndian
224 | unsigned char* second_offset_xored_le = new unsigned char[4]();
225 | second_offset_xored_le[0] = second_offset_xored[3];
226 | second_offset_xored_le[1] = second_offset_xored[2];
227 | second_offset_xored_le[2] = second_offset_xored[1];
228 | second_offset_xored_le[3] = second_offset_xored[0];
229 |
230 | //TO int
231 | unsigned int second_offset_xored_int = (second_offset_xored_le[0] << 24 |
232 | second_offset_xored_le[1] << 16 |
233 | second_offset_xored_le[2] << 8 |
234 | second_offset_xored_le[3]);
235 |
236 |
237 | //mul first_offset_xored_le with 0xCCCCCCCD
238 | unsigned long long mul3 = (unsigned long long) first_offset_xored_int * 0xCCCCCCCD;
239 | //mul second_offset_xored_le with 0xCCCCCCCD
240 | unsigned long long mul4 = (unsigned long long) second_offset_xored_int * 0xCCCCCCCD;
241 |
242 | //shifting to the right the upper part of the mul3 long long by 2
243 | unsigned int shift2 = (unsigned int)(mul3 >> 32) >> 0x2;
244 |
245 | //logic-AND between shift2 and 0x3FE00000
246 | unsigned int and1 = shift2 & 0x3FE00000;
247 |
248 | //mul and1 by 5 times
249 | unsigned int mul5 = and1 * 5;
250 |
251 | //REAL FIRST OFFSET FOR XOR KEY
252 | unsigned int real_first_XORKEY_offset = first_offset_xored_int - mul5;
253 |
254 | //shifting to the right the upper part of the mul4 long long by 2
255 | unsigned int shift3 = (unsigned int)(mul4 >> 32) >> 0x2;
256 |
257 | //logic-AND between shift3 and 0x3FE00000
258 | unsigned int and2 = shift3 & 0x3FE00000;
259 |
260 | //mul and1 by 5 times
261 | unsigned int mul6 = and2 * 5;
262 |
263 | //REAL SECOND OFFSET FOR XOR KEY
264 | unsigned int real_second_XORKEY_offset = second_offset_xored_int - mul6;
265 |
266 | //get encrypted file size
267 | std::ifstream in_file(path_encrypted_file.c_str(), std::ios::binary);
268 | in_file.seekg(0, std::ios::end);
269 | int file_size = in_file.tellg();
270 |
271 | //unsigned char* file_clear = new unsigned char[file_size]();
272 | //unsigned char* file_encrypted = new unsigned char[file_size]();
273 |
274 | unsigned char* file_clear;
275 | file_clear = (unsigned char*)malloc(file_size);
276 |
277 | unsigned char* file_encrypted;
278 | file_encrypted = (unsigned char*)malloc(file_size);
279 |
280 | //read the encrypted file
281 | openFile(path_encrypted_file, file_encrypted, file_size);
282 |
283 |
284 | //NCB-Not-cyphered-block size computation
285 | if (file_size > 0x100000)
286 | {
287 |
288 | //unsigned int file_size = 0x4c4b40;
289 | //unsigned int file_size = 0x100001;
290 | unsigned long long shl = (unsigned long long)file_size << 0xB;
291 |
292 | //get the high part of shl operation
293 | unsigned int shl_hp = shl >> 0x20;
294 |
295 | //off
296 | unsigned int edx = 0;
297 |
298 | if (0xC9FFFFF >= file_size)
299 | {
300 | edx = shl_hp;
301 |
302 | }
303 | else
304 | {
305 | edx = 0x64;
306 | }
307 |
308 | unsigned int shl3 = edx << 0x14;
309 |
310 | unsigned int sub1 = file_size - shl3;
311 | unsigned int sub2 = edx - 1;
312 | if (sub2 > 0)
313 | {
314 | ncb_size = sub1 / sub2;
315 | }
316 | else
317 | {
318 | ncb_size = sub1;
319 | }
320 |
321 | }
322 |
323 |
324 |
325 |
326 |
327 | //storing count of byte being decrypted
328 | //this variable is used to access in incremental way the decrypted keystream array
329 | //this variable is never reset
330 | unsigned int total_decrypted_count = 0;
331 |
332 | unsigned int total_decrypted_blocks = 0;
333 |
334 | //if the ncb_size is larger than 0, it means the file is not completely encrypted
335 | //so there are not cyphered blocks
336 | if (ncb_size > 0 && (ncb_override == false))
337 | {
338 | //counter of cyphering block, for resetting the cryptedBlock flag
339 | unsigned int decypher_count = 0;
340 |
341 | //counter of first offset
342 | unsigned int not_cyphered_count = 0;
343 |
344 | //flag to distinguish from encrypted block or not encrypted block
345 | boolean cryptedBlock = true;
346 |
347 | unsigned int mod_first_offset = 0;
348 | unsigned int mod_second_offset = 0;
349 | unsigned int total_decrypted_count = 0;
350 |
351 | for (int i = 0; i < file_size; i++)
352 | {
353 | mod_second_offset = total_decrypted_count % 0x2FFF00;
354 | mod_first_offset = total_decrypted_count % 0x2FFD00;
355 |
356 | //if I'm on a crypted block
357 | if (cryptedBlock)
358 | {
359 | file_clear[i] = decrypted_keystream[real_first_XORKEY_offset + mod_second_offset] ^
360 | decrypted_keystream[real_second_XORKEY_offset + mod_first_offset] ^
361 | file_encrypted[i];
362 |
363 | decypher_count++;
364 | total_decrypted_count++;
365 | }
366 | else
367 | {
368 | file_clear[i] = file_encrypted[i];
369 |
370 | not_cyphered_count++;
371 | }
372 |
373 |
374 | //if cypher_count reaches the end of the encrypted block
375 | if (decypher_count == 0x100000)
376 | {
377 | //update the decryption block count
378 | total_decrypted_blocks++;
379 |
380 | cryptedBlock = false;
381 |
382 | //reset the counter
383 | decypher_count = 0;
384 | }
385 |
386 | //if reach the count of ncb_size, then start deciphering again and set not cyphered_count to zero
387 | if (not_cyphered_count == ncb_size)
388 | {
389 | cryptedBlock = true;
390 |
391 | //reset the counter
392 | not_cyphered_count = 0;
393 | }
394 |
395 |
396 | }
397 | }
398 | else
399 | {
400 | unsigned int mod_first_offset = 0;
401 | unsigned int mod_second_offset = 0;
402 |
403 | for (int i = 0; i < file_size; i++)
404 | {
405 | mod_second_offset = i % 0x2FFF00;
406 | mod_first_offset = i % 0x2FFD00;
407 |
408 | file_clear[i] = decrypted_keystream[real_first_XORKEY_offset + mod_second_offset] ^
409 | decrypted_keystream[real_second_XORKEY_offset + mod_first_offset] ^
410 | file_encrypted[i];
411 |
412 | }
413 |
414 |
415 |
416 |
417 | }
418 |
419 |
420 |
421 | // Character to be found
422 | char ch = '.';
423 |
424 | // To store the index of last
425 | // character found
426 | size_t found;
427 |
428 | // Function to find the last
429 | // character ch in str
430 | found = path_encrypted_file.find_last_of(ch);
431 |
432 | std::string outputFile = path_encrypted_file.substr(0, found);
433 |
434 | std::ofstream outfile_keystream(outputFile, std::ofstream::binary);
435 | outfile_keystream.write((const char *)file_clear, file_size);
436 | outfile_keystream.close();
437 |
438 |
439 |
440 |
441 | }
442 | else
443 | {
444 |
445 | }
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 | }
455 |
456 | }
457 |
458 |
459 |
460 |
461 | }
462 | }
463 |
464 |
465 | std::vector base64_decode(const std::string & in)
466 | {
467 | std::vector out;
468 | std::vector T(256, -1);
469 | unsigned int i;
470 | for (i = 0; i < 64; i++) T[base64_url_alphabet[i]] = i;
471 |
472 | int val = 0, valb = -8;
473 | for (i = 0; i < in.length(); i++) {
474 | unsigned char c = in[i];
475 | if (T[c] == -1) break;
476 | val = (val << 6) + T[c];
477 | valb += 6;
478 | if (valb >= 0) {
479 | out.push_back(BYTE((val >> valb) & 0xFF));
480 | valb -= 8;
481 | }
482 | }
483 | return out;
484 | }
485 |
486 | void openFile(std::string file_name, unsigned char* buffer, int dim)
487 | {
488 |
489 | // open the file:
490 | std::streampos fileSize;
491 | std::ifstream file(file_name, std::ios::binary);
492 |
493 |
494 | // read the data:
495 | //buffer = new unsigned char[dim]();
496 | file.read((char*)&buffer[0], dim);
497 |
498 |
499 | }
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 | void offset_bruteforce()
513 | {
514 | //open the decrypted keystream file
515 | std::cout << "Please enter the decrypted keystream file: \n";
516 | std::string path_decrypted_keystream;
517 | std::cin >> path_decrypted_keystream;
518 | path_decrypted_keystream.erase(remove(path_decrypted_keystream.begin(), path_decrypted_keystream.end(), '\"'), path_decrypted_keystream.end());
519 | std::ifstream ifs(path_decrypted_keystream.c_str());
520 |
521 | if (!ifs)
522 | {
523 | std::cout << "error opening keystream file!";
524 | }
525 | else
526 | {
527 |
528 | int headerLen = 0;
529 |
530 | std::cout << "Please enter the int referring to the extension (0:pdf, 1:doc|xls|ppt, 2:jpg, 3:png, 4:docx|xlsx|pptx ): \n";
531 | int choosen_extension;
532 | std::cin >> choosen_extension;
533 |
534 | switch (choosen_extension)
535 | {
536 | case 0:
537 | printf("you choosed pdf\n");
538 | headerLen = 5;
539 | knownheader = new unsigned char[headerLen]();
540 | knownheader[0] = 0x25;
541 | knownheader[1] = 0x50;
542 | knownheader[2] = 0x44;
543 | knownheader[3] = 0x46;
544 | knownheader[4] = 0x2D;
545 |
546 | break;
547 | case 1:
548 |
549 | printf("you choosed doc|xls|ppt\n");
550 | headerLen = 8;
551 | knownheader = new unsigned char[headerLen]();
552 | knownheader[0] = 0xD0;
553 | knownheader[1] = 0xCF;
554 | knownheader[2] = 0x11;
555 | knownheader[3] = 0xE0;
556 | knownheader[4] = 0xA1;
557 | knownheader[5] = 0xB1;
558 | knownheader[6] = 0x1A;
559 | knownheader[7] = 0xE1;
560 |
561 | break;
562 | case 2:
563 | printf("you choosed jpg\n");
564 | headerLen = 3;
565 | knownheader = new unsigned char[headerLen]();
566 | knownheader[0] = 0xFF;
567 | knownheader[1] = 0xD8;
568 | knownheader[2] = 0xFF;
569 | break;
570 | case 3:
571 | printf("you choosed png\n");
572 | headerLen = 8;
573 | knownheader = new unsigned char[headerLen]();
574 | knownheader[0] = 0x89;
575 | knownheader[1] = 0x50;
576 | knownheader[2] = 0x4E;
577 | knownheader[3] = 0x47;
578 | knownheader[4] = 0x0D;
579 | knownheader[5] = 0x0A;
580 | knownheader[6] = 0x1A;
581 | knownheader[7] = 0x0A;
582 | break;
583 | case 4:
584 | printf("you choosed docx|xlsx|pptx\n");
585 | headerLen = 4;
586 | knownheader = new unsigned char[headerLen]();
587 | knownheader[0] = 0x50;
588 | knownheader[1] = 0x4B;
589 | knownheader[2] = 0x03;
590 | knownheader[3] = 0x04;
591 | break;
592 |
593 |
594 | default:
595 | printf("Wrong choose!\n");
596 | break;
597 | }
598 |
599 |
600 | //open the file to be decrypted
601 | std::cout << "Please enter the encrypted file with the same extension: \n";
602 | std::string path_encrypted_file;
603 | std::cin >> path_encrypted_file;
604 | path_encrypted_file.erase(remove(path_encrypted_file.begin(), path_encrypted_file.end(), '\"'), path_encrypted_file.end());
605 | std::ifstream ifs(path_encrypted_file.c_str());
606 |
607 | //updating working path based on encrypted file position in file system
608 | currentWorkingPath = path_encrypted_file.substr(0, path_encrypted_file.find_last_of("/\\"));
609 |
610 | if (!ifs)
611 | {
612 | std::cout << "error opening encrypted file!";
613 | }
614 | else
615 | {
616 | //get encrypted file extension
617 | size_t i = path_encrypted_file.rfind('.', path_encrypted_file.length());
618 | if (i != std::string::npos)
619 | {
620 | std::string extension = (path_encrypted_file.substr(i + 1, path_encrypted_file.length() - i));
621 | std::vector decoded_extension = base64_decode(extension);
622 | //byte dimension of decoded byte
623 | int dim_decoded_extension = decoded_extension.size();
624 |
625 | //transform to array
626 | unsigned char* decoded_extension_array = new unsigned char[dim_decoded_extension]();
627 | std::copy(decoded_extension.begin(), decoded_extension.end(), decoded_extension_array);
628 |
629 | //read the decrypted keystream
630 | openFile(path_decrypted_keystream, decrypted_keystream, 0xCFFF00);
631 |
632 | //extract xor key from decrypted keystream
633 | unsigned char* first_offset_xor = new unsigned char[4]();
634 |
635 | boolean isOffsetNotFound = true;
636 | //special offset from zero until I find the right offset
637 | specialOffset = 0;
638 |
639 | std::cout << "Starting offset bruteforce from " << specialOffset << " \n";
640 |
641 | //crea vettore per file cifrato
642 | unsigned char* file_encrypted = new unsigned char[4]();
643 | //leggi i primi [lunghezzaHeader] byte del file cifrato
644 | openFile(path_encrypted_file, file_encrypted, headerLen);
645 |
646 | while (isOffsetNotFound)
647 | {
648 | memcpy(first_offset_xor, decrypted_keystream + specialOffset - 0x4, 4);
649 | //get first offset
650 | unsigned char* first_offset = new unsigned char[4]();
651 | memcpy(first_offset, decoded_extension_array + dim_decoded_extension - 8, 4);
652 |
653 | //first offset xored
654 | unsigned char* first_offset_xored = new unsigned char[4]();
655 |
656 | //xor first offset with xor key extracted from decrypted keystream
657 | for (int i = 0; i < 4; i++)
658 | {
659 | first_offset_xored[i] = first_offset[i] ^ first_offset_xor[i];
660 | }
661 |
662 | //first offset xored to littleEndian
663 | unsigned char* first_offset_xored_le = new unsigned char[4]();
664 | first_offset_xored_le[0] = first_offset_xored[3];
665 | first_offset_xored_le[1] = first_offset_xored[2];
666 | first_offset_xored_le[2] = first_offset_xored[1];
667 | first_offset_xored_le[3] = first_offset_xored[0];
668 |
669 | //TO int
670 | unsigned int first_offset_xored_int = (first_offset_xored_le[0] << 24 |
671 | first_offset_xored_le[1] << 16 |
672 | first_offset_xored_le[2] << 8 |
673 | first_offset_xored_le[3]);
674 |
675 | //mul offset with fixed value 0x3333347B
676 | unsigned long long mul1 = (unsigned long long) first_offset_xored_int * 0x3333347B;
677 |
678 | //shifting to the right the upper part of the mul1 long long by 15
679 | unsigned int shift1 = (unsigned int)(mul1 >> 32) >> 0x15;
680 |
681 | //mul shift with fixed value 0x9FFFFC
682 | unsigned int mul2 = shift1 * 0x9FFFFC;
683 |
684 | //difference between offset1 and mul2 (acts like second offset xor key)
685 | unsigned int diff1 = first_offset_xored_int - mul2;
686 |
687 | //extract second xor key from decrypted keystream
688 | unsigned char* second_offset_xor = new unsigned char[4]();
689 | memcpy(second_offset_xor, decrypted_keystream + diff1, 4);
690 |
691 | //get second offset
692 | unsigned char* second_offset = new unsigned char[4]();
693 | memcpy(second_offset, decoded_extension_array + dim_decoded_extension - 4, 4);
694 |
695 |
696 | //second offset xored
697 | unsigned char* second_offset_xored = new unsigned char[4]();
698 |
699 | //xor second offset with second xor key extracted from decrypted keystream
700 | for (int i = 0; i < 4; i++)
701 | {
702 | second_offset_xored[i] = second_offset[i] ^ second_offset_xor[i];
703 | }
704 |
705 | //second offset xored to littleEndian
706 | unsigned char* second_offset_xored_le = new unsigned char[4]();
707 | second_offset_xored_le[0] = second_offset_xored[3];
708 | second_offset_xored_le[1] = second_offset_xored[2];
709 | second_offset_xored_le[2] = second_offset_xored[1];
710 | second_offset_xored_le[3] = second_offset_xored[0];
711 |
712 | //TO int
713 | unsigned int second_offset_xored_int = (second_offset_xored_le[0] << 24 |
714 | second_offset_xored_le[1] << 16 |
715 | second_offset_xored_le[2] << 8 |
716 | second_offset_xored_le[3]);
717 |
718 |
719 | //mul first_offset_xored_le with 0xCCCCCCCD
720 | unsigned long long mul3 = (unsigned long long) first_offset_xored_int * 0xCCCCCCCD;
721 | //mul second_offset_xored_le with 0xCCCCCCCD
722 | unsigned long long mul4 = (unsigned long long) second_offset_xored_int * 0xCCCCCCCD;
723 |
724 | //shifting to the right the upper part of the mul3 long long by 2
725 | unsigned int shift2 = (unsigned int)(mul3 >> 32) >> 0x2;
726 |
727 | //logic-AND between shift2 and 0x3FE00000
728 | unsigned int and1 = shift2 & 0x3FE00000;
729 |
730 | //mul and1 by 5 times
731 | unsigned int mul5 = and1 * 5;
732 |
733 | //REAL FIRST OFFSET FOR XOR KEY
734 | unsigned int real_first_XORKEY_offset = first_offset_xored_int - mul5;
735 |
736 | //shifting to the right the upper part of the mul4 long long by 2
737 | unsigned int shift3 = (unsigned int)(mul4 >> 32) >> 0x2;
738 |
739 | //logic-AND between shift3 and 0x3FE00000
740 | unsigned int and2 = shift3 & 0x3FE00000;
741 |
742 | //mul and1 by 5 times
743 | unsigned int mul6 = and2 * 5;
744 |
745 | //REAL SECOND OFFSET FOR XOR KEY
746 | unsigned int real_second_XORKEY_offset = second_offset_xored_int - mul6;
747 |
748 | //creating an output array of headerLen sie
749 | unsigned char* fileclear = new unsigned char[headerLen]();
750 |
751 | unsigned int mod_first_offset = 0;
752 | unsigned int mod_second_offset = 0;
753 |
754 | //decrypting the first headerLen bytes
755 | for (int i = 0; i < headerLen; i++)
756 | {
757 | mod_second_offset = i % 0x2FFF00;
758 | mod_first_offset = i % 0x2FFD00;
759 |
760 | fileclear[i] = decrypted_keystream[real_first_XORKEY_offset + mod_second_offset] ^
761 | decrypted_keystream[real_second_XORKEY_offset + mod_first_offset] ^
762 | file_encrypted[i];
763 |
764 | }
765 |
766 |
767 | //headerLen is the number of matching bytes
768 | if (strncmp(reinterpret_cast(fileclear + 0), reinterpret_cast(knownheader + 0), headerLen) == 0)
769 | {
770 | isOffsetNotFound = false;
771 |
772 | std::cout << "========= Offset FOUND: " << specialOffset << " (0x" << std::hex << specialOffset << ") ======== \n\n";
773 | }
774 |
775 | if (specialOffset > 0xFFFFFFFF)
776 | {
777 | std::cout << "========= Offset not found! ======== \n\n";
778 | isOffsetNotFound = false;
779 |
780 | }
781 | else
782 | {
783 | //
784 | specialOffset = specialOffset + 1;
785 |
786 | }
787 |
788 |
789 |
790 |
791 | }
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 | }
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 | }
814 |
815 | }
816 | }
817 |
818 |
819 |
--------------------------------------------------------------------------------