├── LICENSE ├── README.md ├── aes.cpp ├── aes.hpp ├── base64.cpp ├── base64.hpp ├── decrypter.cpp ├── decrypter.hpp ├── handlers.cpp ├── handlers.hpp ├── heuristics.cpp ├── heuristics.hpp ├── main.cpp ├── pikabot_plugin.dll ├── plugin.cpp ├── plugin.hpp ├── utils.cpp └── utils.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Zscaler ThreatLabz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pikabot Deobfuscator 2 | An IDA plugin to deobfuscate Pikabot's strings using RC4 and AES 3 | 4 | # Compatibility 5 | The Pikabot plugin has been tested with IDA versions 8 and newer. The plugin can be executed by compiling the source code using IDA's SDK and/or copying the generated DLL into the IDA plugins folder. After a Pikabot sample is loaded, the user can decompile a function and right-click in the decompiled output and either choose to decrypt strings in the current function or in all of them. 6 | 7 | ![](https://www.zscaler.com/cdn-cgi/image/format=auto/sites/default/files/images/blogs/fig_3_3.png) 8 | 9 | For each decrypted string, the plugin sets a comment in the decompiled output. 10 | 11 | # Before 12 | ![](https://www.zscaler.com/cdn-cgi/image/format=auto/sites/default/files/images/blogs/fig_4_2.png) 13 | 14 | 15 | # After 16 | 17 | ![](https://www.zscaler.com/cdn-cgi/image/format=auto/sites/default/files/images/blogs/fig_5_1.png) 18 | 19 | 20 | # Example Pikabot Samples 21 | |SHA256| 22 | |:------------------------------------------------------------------| 23 | |aebff5134e07a1586b911271a49702c8623b8ac8da2c135d4d3b0145a826f507| 24 | |4c53383c1088c069573f918c0f99fe30fa2dc9e28e800d33c4d212a5e4d36839| 25 | |15e4de42f49ea4041e4063b991ddfc6523184310f03e645c17710b370ee75347| 26 | |e97fd71f076a7724e665873752c68d7a12b1b0c796bc7b9d9924ec3d49561272| 27 | |a9f0c978cc851959773b90d90921527dbf48977b9354b8baf024d16fc72eae01| 28 | |1c125a10c33d862e6179b6827131e1aac587d23f1b7be0dbcb32571d70e34de4| 29 | |62f2adbc73cbdde282ae3749aa63c2bc9c5ded8888f23160801db2db851cde8f| 30 | |b178620d56a927672654ce2df9ec82522a2eeb81dd3cde7e1003123e794b7116| 31 | |72f1a5476a845ea02344c9b7edecfe399f64b52409229edaf856fcb9535e3242| 32 | 33 | -------------------------------------------------------------------------------- /aes.cpp: -------------------------------------------------------------------------------- 1 | #include "AES.hpp" 2 | 3 | AES::AES(const AESKeyLength keyLength) { 4 | switch (keyLength) { 5 | case AESKeyLength::AES_128: 6 | this->Nk = 4; 7 | this->Nr = 10; 8 | break; 9 | case AESKeyLength::AES_192: 10 | this->Nk = 6; 11 | this->Nr = 12; 12 | break; 13 | case AESKeyLength::AES_256: 14 | this->Nk = 8; 15 | this->Nr = 14; 16 | break; 17 | } 18 | } 19 | 20 | unsigned char* AES::EncryptECB(const unsigned char in[], unsigned int inLen, 21 | const unsigned char key[]) { 22 | CheckLength(inLen); 23 | unsigned char* out = new unsigned char[inLen]; 24 | unsigned char* roundKeys = new unsigned char[4 * Nb * (Nr + 1)]; 25 | KeyExpansion(key, roundKeys); 26 | for (unsigned int i = 0; i < inLen; i += blockBytesLen) { 27 | EncryptBlock(in + i, out + i, roundKeys); 28 | } 29 | 30 | delete[] roundKeys; 31 | 32 | return out; 33 | } 34 | 35 | unsigned char* AES::DecryptECB(const unsigned char in[], unsigned int inLen, 36 | const unsigned char key[]) { 37 | CheckLength(inLen); 38 | unsigned char* out = new unsigned char[inLen]; 39 | unsigned char* roundKeys = new unsigned char[4 * Nb * (Nr + 1)]; 40 | KeyExpansion(key, roundKeys); 41 | for (unsigned int i = 0; i < inLen; i += blockBytesLen) { 42 | DecryptBlock(in + i, out + i, roundKeys); 43 | } 44 | 45 | delete[] roundKeys; 46 | 47 | return out; 48 | } 49 | 50 | unsigned char* AES::EncryptCBC(const unsigned char in[], unsigned int inLen, 51 | const unsigned char key[], 52 | const unsigned char* iv) { 53 | CheckLength(inLen); 54 | unsigned char* out = new unsigned char[inLen](); 55 | unsigned char block[blockBytesLen]; 56 | unsigned char* roundKeys = new unsigned char[4 * Nb * (Nr + 1)]; 57 | KeyExpansion(key, roundKeys); 58 | memcpy(block, iv, blockBytesLen); 59 | for (unsigned int i = 0; i < inLen; i += blockBytesLen) { 60 | XorBlocks(block, in + i, block, blockBytesLen); 61 | EncryptBlock(block, out + i, roundKeys); 62 | memcpy(block, out + i, blockBytesLen); 63 | } 64 | 65 | delete[] roundKeys; 66 | 67 | return out; 68 | } 69 | 70 | unsigned char* AES::DecryptCBC(const unsigned char in[], unsigned int inLen, 71 | const unsigned char key[], 72 | const unsigned char* iv) { 73 | CheckLength(inLen); 74 | unsigned char* out = new unsigned char[inLen](); 75 | unsigned char block[blockBytesLen]; 76 | unsigned char* roundKeys = new unsigned char[4 * Nb * (Nr + 1)](); 77 | KeyExpansion(key, roundKeys); 78 | memcpy(block, iv, blockBytesLen); 79 | for (unsigned int i = 0; i < inLen; i += blockBytesLen) { 80 | DecryptBlock(in + i, out + i, roundKeys); 81 | XorBlocks(block, out + i, out + i, blockBytesLen); 82 | memcpy(block, in + i, blockBytesLen); 83 | } 84 | 85 | delete[] roundKeys; 86 | 87 | return out; 88 | } 89 | 90 | unsigned char* AES::EncryptCFB(const unsigned char in[], unsigned int inLen, 91 | const unsigned char key[], 92 | const unsigned char* iv) { 93 | CheckLength(inLen); 94 | unsigned char* out = new unsigned char[inLen]; 95 | unsigned char block[blockBytesLen]; 96 | unsigned char encryptedBlock[blockBytesLen]; 97 | unsigned char* roundKeys = new unsigned char[4 * Nb * (Nr + 1)]; 98 | KeyExpansion(key, roundKeys); 99 | memcpy(block, iv, blockBytesLen); 100 | for (unsigned int i = 0; i < inLen; i += blockBytesLen) { 101 | EncryptBlock(block, encryptedBlock, roundKeys); 102 | XorBlocks(in + i, encryptedBlock, out + i, blockBytesLen); 103 | memcpy(block, out + i, blockBytesLen); 104 | } 105 | 106 | delete[] roundKeys; 107 | 108 | return out; 109 | } 110 | 111 | unsigned char* AES::DecryptCFB(const unsigned char in[], unsigned int inLen, 112 | const unsigned char key[], 113 | const unsigned char* iv) { 114 | CheckLength(inLen); 115 | unsigned char* out = new unsigned char[inLen]; 116 | unsigned char block[blockBytesLen]; 117 | unsigned char encryptedBlock[blockBytesLen]; 118 | unsigned char* roundKeys = new unsigned char[4 * Nb * (Nr + 1)]; 119 | KeyExpansion(key, roundKeys); 120 | memcpy(block, iv, blockBytesLen); 121 | for (unsigned int i = 0; i < inLen; i += blockBytesLen) { 122 | EncryptBlock(block, encryptedBlock, roundKeys); 123 | XorBlocks(in + i, encryptedBlock, out + i, blockBytesLen); 124 | memcpy(block, in + i, blockBytesLen); 125 | } 126 | 127 | delete[] roundKeys; 128 | 129 | return out; 130 | } 131 | 132 | void AES::CheckLength(unsigned int len) { 133 | if (len % blockBytesLen != 0) { 134 | throw std::length_error("Plaintext length must be divisible by " + 135 | std::to_string(blockBytesLen)); 136 | } 137 | } 138 | 139 | void AES::EncryptBlock(const unsigned char in[], unsigned char out[], 140 | unsigned char* roundKeys) { 141 | unsigned char state[4][Nb]; 142 | unsigned int i, j, round; 143 | 144 | for (i = 0; i < 4; i++) { 145 | for (j = 0; j < Nb; j++) { 146 | state[i][j] = in[i + 4 * j]; 147 | } 148 | } 149 | 150 | AddRoundKey(state, roundKeys); 151 | 152 | for (round = 1; round <= Nr - 1; round++) { 153 | SubBytes(state); 154 | ShiftRows(state); 155 | MixColumns(state); 156 | AddRoundKey(state, roundKeys + round * 4 * Nb); 157 | } 158 | 159 | SubBytes(state); 160 | ShiftRows(state); 161 | AddRoundKey(state, roundKeys + Nr * 4 * Nb); 162 | 163 | for (i = 0; i < 4; i++) { 164 | for (j = 0; j < Nb; j++) { 165 | out[i + 4 * j] = state[i][j]; 166 | } 167 | } 168 | } 169 | 170 | void AES::DecryptBlock(const unsigned char in[], unsigned char out[], 171 | unsigned char* roundKeys) { 172 | unsigned char state[4][Nb]; 173 | unsigned int i, j, round; 174 | 175 | for (i = 0; i < 4; i++) { 176 | for (j = 0; j < Nb; j++) { 177 | state[i][j] = in[i + 4 * j]; 178 | } 179 | } 180 | 181 | AddRoundKey(state, roundKeys + Nr * 4 * Nb); 182 | 183 | for (round = Nr - 1; round >= 1; round--) { 184 | InvSubBytes(state); 185 | InvShiftRows(state); 186 | AddRoundKey(state, roundKeys + round * 4 * Nb); 187 | InvMixColumns(state); 188 | } 189 | 190 | InvSubBytes(state); 191 | InvShiftRows(state); 192 | AddRoundKey(state, roundKeys); 193 | 194 | for (i = 0; i < 4; i++) { 195 | for (j = 0; j < Nb; j++) { 196 | out[i + 4 * j] = state[i][j]; 197 | } 198 | } 199 | } 200 | 201 | void AES::SubBytes(unsigned char state[4][Nb]) { 202 | unsigned int i, j; 203 | unsigned char t; 204 | for (i = 0; i < 4; i++) { 205 | for (j = 0; j < Nb; j++) { 206 | t = state[i][j]; 207 | state[i][j] = sbox[t / 16][t % 16]; 208 | } 209 | } 210 | } 211 | 212 | void AES::ShiftRow(unsigned char state[4][Nb], unsigned int i, 213 | unsigned int n) // shift row i on n positions 214 | { 215 | unsigned char tmp[Nb]; 216 | for (unsigned int j = 0; j < Nb; j++) { 217 | tmp[j] = state[i][(j + n) % Nb]; 218 | } 219 | memcpy(state[i], tmp, Nb * sizeof(unsigned char)); 220 | } 221 | 222 | void AES::ShiftRows(unsigned char state[4][Nb]) { 223 | ShiftRow(state, 1, 1); 224 | ShiftRow(state, 2, 2); 225 | ShiftRow(state, 3, 3); 226 | } 227 | 228 | unsigned char AES::xtime(unsigned char b) // multiply on x 229 | { 230 | return (b << 1) ^ (((b >> 7) & 1) * 0x1b); 231 | } 232 | 233 | void AES::MixColumns(unsigned char state[4][Nb]) { 234 | unsigned char temp_state[4][Nb]; 235 | 236 | for (size_t i = 0; i < 4; ++i) { 237 | memset(temp_state[i], 0, 4); 238 | } 239 | 240 | for (size_t i = 0; i < 4; ++i) { 241 | for (size_t k = 0; k < 4; ++k) { 242 | for (size_t j = 0; j < 4; ++j) { 243 | if (CMDS[i][k] == 1) 244 | temp_state[i][j] ^= state[k][j]; 245 | else 246 | temp_state[i][j] ^= GF_MUL_TABLE[CMDS[i][k]][state[k][j]]; 247 | } 248 | } 249 | } 250 | 251 | for (size_t i = 0; i < 4; ++i) { 252 | memcpy(state[i], temp_state[i], 4); 253 | } 254 | } 255 | 256 | void AES::AddRoundKey(unsigned char state[4][Nb], unsigned char* key) { 257 | unsigned int i, j; 258 | for (i = 0; i < 4; i++) { 259 | for (j = 0; j < Nb; j++) { 260 | state[i][j] = state[i][j] ^ key[i + 4 * j]; 261 | } 262 | } 263 | } 264 | 265 | void AES::SubWord(unsigned char* a) { 266 | int i; 267 | for (i = 0; i < 4; i++) { 268 | a[i] = sbox[a[i] / 16][a[i] % 16]; 269 | } 270 | } 271 | 272 | void AES::RotWord(unsigned char* a) { 273 | unsigned char c = a[0]; 274 | a[0] = a[1]; 275 | a[1] = a[2]; 276 | a[2] = a[3]; 277 | a[3] = c; 278 | } 279 | 280 | void AES::XorWords(unsigned char* a, unsigned char* b, unsigned char* c) { 281 | int i; 282 | for (i = 0; i < 4; i++) { 283 | c[i] = a[i] ^ b[i]; 284 | } 285 | } 286 | 287 | void AES::Rcon(unsigned char* a, unsigned int n) { 288 | unsigned int i; 289 | unsigned char c = 1; 290 | for (i = 0; i < n - 1; i++) { 291 | c = xtime(c); 292 | } 293 | 294 | a[0] = c; 295 | a[1] = a[2] = a[3] = 0; 296 | } 297 | 298 | void AES::KeyExpansion(const unsigned char key[], unsigned char w[]) { 299 | unsigned char temp[4]; 300 | unsigned char rcon[4]; 301 | 302 | unsigned int i = 0; 303 | while (i < 4 * Nk) { 304 | w[i] = key[i]; 305 | i++; 306 | } 307 | 308 | i = 4 * Nk; 309 | while (i < 4 * Nb * (Nr + 1)) { 310 | temp[0] = w[i - 4 + 0]; 311 | temp[1] = w[i - 4 + 1]; 312 | temp[2] = w[i - 4 + 2]; 313 | temp[3] = w[i - 4 + 3]; 314 | 315 | if (i / 4 % Nk == 0) { 316 | RotWord(temp); 317 | SubWord(temp); 318 | Rcon(rcon, i / (Nk * 4)); 319 | XorWords(temp, rcon, temp); 320 | } 321 | else if (Nk > 6 && i / 4 % Nk == 4) { 322 | SubWord(temp); 323 | } 324 | 325 | w[i + 0] = w[i - 4 * Nk] ^ temp[0]; 326 | w[i + 1] = w[i + 1 - 4 * Nk] ^ temp[1]; 327 | w[i + 2] = w[i + 2 - 4 * Nk] ^ temp[2]; 328 | w[i + 3] = w[i + 3 - 4 * Nk] ^ temp[3]; 329 | i += 4; 330 | } 331 | } 332 | 333 | void AES::InvSubBytes(unsigned char state[4][Nb]) { 334 | unsigned int i, j; 335 | unsigned char t; 336 | for (i = 0; i < 4; i++) { 337 | for (j = 0; j < Nb; j++) { 338 | t = state[i][j]; 339 | state[i][j] = inv_sbox[t / 16][t % 16]; 340 | } 341 | } 342 | } 343 | 344 | void AES::InvMixColumns(unsigned char state[4][Nb]) { 345 | unsigned char temp_state[4][Nb]; 346 | 347 | for (size_t i = 0; i < 4; ++i) { 348 | memset(temp_state[i], 0, 4); 349 | } 350 | 351 | for (size_t i = 0; i < 4; ++i) { 352 | for (size_t k = 0; k < 4; ++k) { 353 | for (size_t j = 0; j < 4; ++j) { 354 | temp_state[i][j] ^= GF_MUL_TABLE[INV_CMDS[i][k]][state[k][j]]; 355 | } 356 | } 357 | } 358 | 359 | for (size_t i = 0; i < 4; ++i) { 360 | memcpy(state[i], temp_state[i], 4); 361 | } 362 | } 363 | 364 | void AES::InvShiftRows(unsigned char state[4][Nb]) { 365 | ShiftRow(state, 1, Nb - 1); 366 | ShiftRow(state, 2, Nb - 2); 367 | ShiftRow(state, 3, Nb - 3); 368 | } 369 | 370 | void AES::XorBlocks(const unsigned char* a, const unsigned char* b, 371 | unsigned char* c, unsigned int len) { 372 | for (unsigned int i = 0; i < len; i++) { 373 | c[i] = a[i] ^ b[i]; 374 | } 375 | } 376 | 377 | void AES::printHexArray(unsigned char a[], unsigned int n) { 378 | for (unsigned int i = 0; i < n; i++) { 379 | printf("%02x ", a[i]); 380 | } 381 | } 382 | 383 | void AES::printHexVector(std::vector a) { 384 | for (unsigned int i = 0; i < a.size(); i++) { 385 | printf("%02x ", a[i]); 386 | } 387 | } 388 | 389 | std::vector AES::ArrayToVector(unsigned char* a, 390 | unsigned int len) { 391 | std::vector v(a, a + len * sizeof(unsigned char)); 392 | return v; 393 | } 394 | 395 | unsigned char* AES::VectorToArray(std::vector& a) { 396 | return a.data(); 397 | } 398 | 399 | std::vector AES::EncryptECB(std::vector in, 400 | std::vector key) { 401 | unsigned char* out = EncryptECB(VectorToArray(in), (unsigned int)in.size(), 402 | VectorToArray(key)); 403 | std::vector v = ArrayToVector(out, in.size()); 404 | delete[] out; 405 | return v; 406 | } 407 | 408 | std::vector AES::DecryptECB(std::vector in, 409 | std::vector key) { 410 | unsigned char* out = DecryptECB(VectorToArray(in), (unsigned int)in.size(), 411 | VectorToArray(key)); 412 | std::vector v = ArrayToVector(out, (unsigned int)in.size()); 413 | delete[] out; 414 | return v; 415 | } 416 | 417 | std::vector AES::EncryptCBC(std::vector in, 418 | std::vector key, 419 | std::vector iv) { 420 | unsigned char* out = EncryptCBC(VectorToArray(in), (unsigned int)in.size(), 421 | VectorToArray(key), VectorToArray(iv)); 422 | std::vector v = ArrayToVector(out, in.size()); 423 | delete[] out; 424 | return v; 425 | } 426 | 427 | std::vector AES::DecryptCBC(std::vector in, 428 | std::vector key, 429 | std::vector iv) { 430 | unsigned char* out = DecryptCBC(VectorToArray(in), (unsigned int)in.size(), 431 | VectorToArray(key), VectorToArray(iv)); 432 | std::vector v = ArrayToVector(out, (unsigned int)in.size()); 433 | delete[] out; 434 | return v; 435 | } 436 | 437 | std::vector AES::EncryptCFB(std::vector in, 438 | std::vector key, 439 | std::vector iv) { 440 | unsigned char* out = EncryptCFB(VectorToArray(in), (unsigned int)in.size(), 441 | VectorToArray(key), VectorToArray(iv)); 442 | std::vector v = ArrayToVector(out, in.size()); 443 | delete[] out; 444 | return v; 445 | } 446 | 447 | std::vector AES::DecryptCFB(std::vector in, 448 | std::vector key, 449 | std::vector iv) { 450 | unsigned char* out = DecryptCFB(VectorToArray(in), (unsigned int)in.size(), 451 | VectorToArray(key), VectorToArray(iv)); 452 | std::vector v = ArrayToVector(out, (unsigned int)in.size()); 453 | delete[] out; 454 | return v; 455 | } -------------------------------------------------------------------------------- /aes.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _AES_H_ 2 | #define _AES_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | enum class AESKeyLength { AES_128, AES_192, AES_256 }; 12 | 13 | class AES { 14 | private: 15 | static constexpr unsigned int Nb = 4; 16 | static constexpr unsigned int blockBytesLen = 4 * Nb * sizeof(unsigned char); 17 | 18 | unsigned int Nk; 19 | unsigned int Nr; 20 | 21 | void SubBytes(unsigned char state[4][Nb]); 22 | 23 | void ShiftRow(unsigned char state[4][Nb], unsigned int i, 24 | unsigned int n); // shift row i on n positions 25 | 26 | void ShiftRows(unsigned char state[4][Nb]); 27 | 28 | unsigned char xtime(unsigned char b); // multiply on x 29 | 30 | void MixColumns(unsigned char state[4][Nb]); 31 | 32 | void AddRoundKey(unsigned char state[4][Nb], unsigned char* key); 33 | 34 | void SubWord(unsigned char* a); 35 | 36 | void RotWord(unsigned char* a); 37 | 38 | void XorWords(unsigned char* a, unsigned char* b, unsigned char* c); 39 | 40 | void Rcon(unsigned char* a, unsigned int n); 41 | 42 | void InvSubBytes(unsigned char state[4][Nb]); 43 | 44 | void InvMixColumns(unsigned char state[4][Nb]); 45 | 46 | void InvShiftRows(unsigned char state[4][Nb]); 47 | 48 | void CheckLength(unsigned int len); 49 | 50 | void KeyExpansion(const unsigned char key[], unsigned char w[]); 51 | 52 | void EncryptBlock(const unsigned char in[], unsigned char out[], 53 | unsigned char key[]); 54 | 55 | void DecryptBlock(const unsigned char in[], unsigned char out[], 56 | unsigned char key[]); 57 | 58 | void XorBlocks(const unsigned char* a, const unsigned char* b, 59 | unsigned char* c, unsigned int len); 60 | 61 | std::vector ArrayToVector(unsigned char* a, unsigned int len); 62 | 63 | unsigned char* VectorToArray(std::vector& a); 64 | 65 | public: 66 | explicit AES(const AESKeyLength keyLength = AESKeyLength::AES_256); 67 | 68 | unsigned char* EncryptECB(const unsigned char in[], unsigned int inLen, 69 | const unsigned char key[]); 70 | 71 | unsigned char* DecryptECB(const unsigned char in[], unsigned int inLen, 72 | const unsigned char key[]); 73 | 74 | unsigned char* EncryptCBC(const unsigned char in[], unsigned int inLen, 75 | const unsigned char key[], const unsigned char* iv); 76 | 77 | unsigned char* DecryptCBC(const unsigned char in[], unsigned int inLen, 78 | const unsigned char key[], const unsigned char* iv); 79 | 80 | unsigned char* EncryptCFB(const unsigned char in[], unsigned int inLen, 81 | const unsigned char key[], const unsigned char* iv); 82 | 83 | unsigned char* DecryptCFB(const unsigned char in[], unsigned int inLen, 84 | const unsigned char key[], const unsigned char* iv); 85 | 86 | std::vector EncryptECB(std::vector in, 87 | std::vector key); 88 | 89 | std::vector DecryptECB(std::vector in, 90 | std::vector key); 91 | 92 | std::vector EncryptCBC(std::vector in, 93 | std::vector key, 94 | std::vector iv); 95 | 96 | std::vector DecryptCBC(std::vector in, 97 | std::vector key, 98 | std::vector iv); 99 | 100 | std::vector EncryptCFB(std::vector in, 101 | std::vector key, 102 | std::vector iv); 103 | 104 | std::vector DecryptCFB(std::vector in, 105 | std::vector key, 106 | std::vector iv); 107 | 108 | void printHexArray(unsigned char a[], unsigned int n); 109 | 110 | void printHexVector(std::vector a); 111 | }; 112 | 113 | const unsigned char sbox[16][16] = { 114 | {0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 115 | 0xfe, 0xd7, 0xab, 0x76}, 116 | {0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 117 | 0x9c, 0xa4, 0x72, 0xc0}, 118 | {0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 119 | 0x71, 0xd8, 0x31, 0x15}, 120 | {0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 121 | 0xeb, 0x27, 0xb2, 0x75}, 122 | {0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 123 | 0x29, 0xe3, 0x2f, 0x84}, 124 | {0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 125 | 0x4a, 0x4c, 0x58, 0xcf}, 126 | {0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 127 | 0x50, 0x3c, 0x9f, 0xa8}, 128 | {0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 129 | 0x10, 0xff, 0xf3, 0xd2}, 130 | {0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 131 | 0x64, 0x5d, 0x19, 0x73}, 132 | {0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 133 | 0xde, 0x5e, 0x0b, 0xdb}, 134 | {0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 135 | 0x91, 0x95, 0xe4, 0x79}, 136 | {0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 137 | 0x65, 0x7a, 0xae, 0x08}, 138 | {0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 139 | 0x4b, 0xbd, 0x8b, 0x8a}, 140 | {0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 141 | 0x86, 0xc1, 0x1d, 0x9e}, 142 | {0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 143 | 0xce, 0x55, 0x28, 0xdf}, 144 | {0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 145 | 0xb0, 0x54, 0xbb, 0x16} }; 146 | 147 | const unsigned char inv_sbox[16][16] = { 148 | {0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 149 | 0x81, 0xf3, 0xd7, 0xfb}, 150 | {0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 151 | 0xc4, 0xde, 0xe9, 0xcb}, 152 | {0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 153 | 0x42, 0xfa, 0xc3, 0x4e}, 154 | {0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 155 | 0x6d, 0x8b, 0xd1, 0x25}, 156 | {0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 157 | 0x5d, 0x65, 0xb6, 0x92}, 158 | {0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 159 | 0xa7, 0x8d, 0x9d, 0x84}, 160 | {0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 161 | 0xb8, 0xb3, 0x45, 0x06}, 162 | {0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 163 | 0x01, 0x13, 0x8a, 0x6b}, 164 | {0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 165 | 0xf0, 0xb4, 0xe6, 0x73}, 166 | {0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 167 | 0x1c, 0x75, 0xdf, 0x6e}, 168 | {0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 169 | 0xaa, 0x18, 0xbe, 0x1b}, 170 | {0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 171 | 0x78, 0xcd, 0x5a, 0xf4}, 172 | {0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 173 | 0x27, 0x80, 0xec, 0x5f}, 174 | {0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 175 | 0x93, 0xc9, 0x9c, 0xef}, 176 | {0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 177 | 0x83, 0x53, 0x99, 0x61}, 178 | {0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 179 | 0x55, 0x21, 0x0c, 0x7d} }; 180 | 181 | /// Galois Multiplication lookup tables 182 | static const unsigned char GF_MUL_TABLE[15][256] = { 183 | {}, 184 | {}, 185 | 186 | // mul 2 187 | {0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 188 | 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 189 | 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46, 190 | 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 191 | 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 192 | 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 193 | 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6, 194 | 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 195 | 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 196 | 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 197 | 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x1b, 0x19, 0x1f, 0x1d, 198 | 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05, 199 | 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 200 | 0x23, 0x21, 0x27, 0x25, 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 201 | 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45, 0x7b, 0x79, 0x7f, 0x7d, 202 | 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65, 203 | 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 204 | 0x83, 0x81, 0x87, 0x85, 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 205 | 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, 0xdb, 0xd9, 0xdf, 0xdd, 206 | 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5, 207 | 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 208 | 0xe3, 0xe1, 0xe7, 0xe5}, 209 | 210 | // mul 3 211 | {0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 212 | 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 213 | 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, 0x60, 0x63, 0x66, 0x65, 214 | 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71, 215 | 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 216 | 0x44, 0x47, 0x42, 0x41, 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 217 | 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, 0xf0, 0xf3, 0xf6, 0xf5, 218 | 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1, 219 | 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 220 | 0xb4, 0xb7, 0xb2, 0xb1, 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 221 | 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, 0x9b, 0x98, 0x9d, 0x9e, 222 | 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a, 223 | 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 224 | 0xbf, 0xbc, 0xb9, 0xba, 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 225 | 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea, 0xcb, 0xc8, 0xcd, 0xce, 226 | 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda, 227 | 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 228 | 0x4f, 0x4c, 0x49, 0x4a, 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 229 | 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a, 0x3b, 0x38, 0x3d, 0x3e, 230 | 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a, 231 | 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 232 | 0x1f, 0x1c, 0x19, 0x1a}, 233 | 234 | {}, 235 | {}, 236 | {}, 237 | {}, 238 | {}, 239 | 240 | // mul 9 241 | {0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 242 | 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 243 | 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, 0x3b, 0x32, 0x29, 0x20, 244 | 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, 245 | 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 246 | 0xc7, 0xce, 0xd5, 0xdc, 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 247 | 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01, 0xe6, 0xef, 0xf4, 0xfd, 248 | 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91, 249 | 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 250 | 0x21, 0x28, 0x33, 0x3a, 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 251 | 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa, 0xec, 0xe5, 0xfe, 0xf7, 252 | 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b, 253 | 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 254 | 0x10, 0x19, 0x02, 0x0b, 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 255 | 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0, 0x47, 0x4e, 0x55, 0x5c, 256 | 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30, 257 | 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 258 | 0xf6, 0xff, 0xe4, 0xed, 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 259 | 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, 0xa1, 0xa8, 0xb3, 0xba, 260 | 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6, 261 | 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 262 | 0x5d, 0x54, 0x4f, 0x46}, 263 | 264 | {}, 265 | 266 | // mul 11 267 | {0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 268 | 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 269 | 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, 0x7b, 0x70, 0x6d, 0x66, 270 | 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12, 271 | 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 272 | 0xbf, 0xb4, 0xa9, 0xa2, 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 273 | 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f, 0x46, 0x4d, 0x50, 0x5b, 274 | 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f, 275 | 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 276 | 0xf9, 0xf2, 0xef, 0xe4, 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 277 | 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54, 0xf7, 0xfc, 0xe1, 0xea, 278 | 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e, 279 | 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 280 | 0x33, 0x38, 0x25, 0x2e, 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 281 | 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5, 0x3c, 0x37, 0x2a, 0x21, 282 | 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55, 283 | 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 284 | 0x75, 0x7e, 0x63, 0x68, 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 285 | 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, 0x7a, 0x71, 0x6c, 0x67, 286 | 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13, 287 | 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 288 | 0xbe, 0xb5, 0xa8, 0xa3}, 289 | 290 | {}, 291 | 292 | // mul 13 293 | {0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 294 | 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 295 | 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, 0xbb, 0xb6, 0xa1, 0xac, 296 | 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0, 297 | 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 298 | 0x37, 0x3a, 0x2d, 0x20, 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 299 | 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26, 0xbd, 0xb0, 0xa7, 0xaa, 300 | 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6, 301 | 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 302 | 0x8a, 0x87, 0x90, 0x9d, 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 303 | 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d, 0xda, 0xd7, 0xc0, 0xcd, 304 | 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91, 305 | 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 306 | 0x56, 0x5b, 0x4c, 0x41, 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 307 | 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a, 0xb1, 0xbc, 0xab, 0xa6, 308 | 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa, 309 | 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 310 | 0xeb, 0xe6, 0xf1, 0xfc, 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 311 | 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, 0x0c, 0x01, 0x16, 0x1b, 312 | 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47, 313 | 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 314 | 0x80, 0x8d, 0x9a, 0x97}, 315 | 316 | // mul 14 317 | {0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 318 | 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 319 | 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, 0xdb, 0xd5, 0xc7, 0xc9, 320 | 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81, 321 | 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 322 | 0x73, 0x7d, 0x6f, 0x61, 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 323 | 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, 0x4d, 0x43, 0x51, 0x5f, 324 | 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17, 325 | 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 326 | 0x3e, 0x30, 0x22, 0x2c, 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 327 | 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc, 0x41, 0x4f, 0x5d, 0x53, 328 | 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b, 329 | 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 330 | 0xe9, 0xe7, 0xf5, 0xfb, 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 331 | 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0, 0x7a, 0x74, 0x66, 0x68, 332 | 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20, 333 | 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 334 | 0xa4, 0xaa, 0xb8, 0xb6, 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 335 | 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56, 0x37, 0x39, 0x2b, 0x25, 336 | 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d, 337 | 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 338 | 0x9f, 0x91, 0x83, 0x8d} }; 339 | 340 | /// circulant MDS matrix 341 | static const unsigned char CMDS[4][4] = { 342 | {2, 3, 1, 1}, {1, 2, 3, 1}, {1, 1, 2, 3}, {3, 1, 1, 2} }; 343 | 344 | /// Inverse circulant MDS matrix 345 | static const unsigned char INV_CMDS[4][4] = { 346 | {14, 11, 13, 9}, {9, 14, 11, 13}, {13, 9, 14, 11}, {11, 13, 9, 14} }; 347 | 348 | #endif -------------------------------------------------------------------------------- /base64.cpp: -------------------------------------------------------------------------------- 1 | #include "base64.hpp" 2 | 3 | /*********************************************** 4 | decodes base64 format string into ASCCI string 5 | @param plain encoded base64 format string 6 | @return ASCII string to be encoded 7 | ***********************************************/ 8 | 9 | size_t decode_base64(unsigned char* cipher) 10 | { 11 | int counts = 0; 12 | char buffer[4]; 13 | int i = 0, p = 0; 14 | 15 | for (i = 0; cipher[i] != '\0'; i++) { 16 | unsigned char k; 17 | for (k = 0; k < 64 && base64_map[k] != cipher[i]; k++); 18 | buffer[counts++] = k; 19 | if (counts == 4) { 20 | cipher[p++] = (buffer[0] << 2) + (buffer[1] >> 4); 21 | if (buffer[2] != 64) 22 | cipher[p++] = (buffer[1] << 4) + (buffer[2] >> 2); 23 | if (buffer[3] != 64) 24 | cipher[p++] = (buffer[2] << 6) + buffer[3]; 25 | counts = 0; 26 | } 27 | } 28 | return p; 29 | } 30 | -------------------------------------------------------------------------------- /base64.hpp: -------------------------------------------------------------------------------- 1 | /*********************************************************** 2 | * Base64 library * 3 | * @author Ahmed Elzoughby * 4 | * @date July 23, 2017 * 5 | * Purpose: encode and decode base64 format * 6 | ***********************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | 11 | 12 | const char base64_map[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 13 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 14 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 15 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; 16 | 17 | /*********************************************** 18 | decodes base64 format string into ASCCI string 19 | @param plain encoded base64 format string 20 | @return ASCII string to be encoded 21 | ***********************************************/ 22 | size_t decode_base64(unsigned char* cipher); -------------------------------------------------------------------------------- /decrypter.cpp: -------------------------------------------------------------------------------- 1 | #include "decrypter.hpp" 2 | 3 | std::vector Decrypter::decryptCNCs(std::string input) 4 | { 5 | std::string cncAESKey; 6 | std::string cncAESIV; 7 | std::string decryptedCNC; 8 | std::string encryptedCNC; 9 | std::string token; 10 | std::vector decryptedCNCs; 11 | std::stringstream inputStream(input); 12 | 13 | size_t delimiter_occurences = std::count_if(input.begin(), input.end(), [](char delimiter) {return delimiter == '&'; }); 14 | if (delimiter_occurences < 1 || input.length() < 48) 15 | return decryptedCNCs; 16 | while (getline(inputStream, token, '&')) 17 | { 18 | std::replace(token.begin(), token.end(), '_', '='); 19 | cncAESKey = token.substr(3, 16); 20 | cncAESKey += token.substr(token.size() - 16); 21 | cncAESIV = token.substr(19, 16); 22 | encryptedCNC = token.substr(35, token.size() - cncAESKey.size() - cncAESIV.size() - 3); 23 | size_t decodedBase64Length = decode_base64((unsigned char*)encryptedCNC.data()); 24 | if (!decodedBase64Length || (decodedBase64Length % 16) || encryptedCNC.empty() || cncAESKey.empty() || cncAESIV.empty()) 25 | continue; 26 | unsigned char* decryptedString = utils::decryptAESCBC(reinterpret_cast(cncAESKey.data()), reinterpret_cast(cncAESIV.data()), 27 | reinterpret_cast(encryptedCNC.data()), decodedBase64Length); 28 | if (!utils::isPrintableAscii(decryptedString)) 29 | { 30 | error("[-] Failed to decrypt CNC\n"); 31 | delete[] decryptedString; 32 | return decryptedCNCs; 33 | } 34 | decryptedCNC = reinterpret_cast(decryptedString); 35 | msg("[+] Decrypted CNC: %s\n", decryptedCNC.c_str()); 36 | decryptedCNCs.push_back(decryptedCNC); 37 | delete[] decryptedString; 38 | } 39 | return decryptedCNCs; 40 | } 41 | 42 | struct FindEncryptedArray : public mop_visitor_t 43 | { 44 | size_t m_EncryptedStringSize = 0; 45 | std::vector m_EncryptedArrayVec; 46 | 47 | int idaapi visit_mop(mop_t* op, const tinfo_t* type, bool is_target) override 48 | { 49 | mblock_t* initialBlock = blk; 50 | minsn_t* functionHead = blk->prevb->head; 51 | mblock_t* loopBlock = blk->prevb; 52 | sval_t stackOffset = 0; 53 | if (op->t == mop_S && curins->opcode == m_add) 54 | { 55 | op->get_stkoff(&stackOffset); 56 | uint32 functionStartAddress = get_func(blk->head->ea)->start_ea; 57 | while (functionHead != nullptr) 58 | { 59 | for (minsn_t* instruction = functionHead; instruction != nullptr; instruction = instruction->next) 60 | { 61 | if (instruction->opcode == m_mov && instruction->l.t == mop_n && instruction->d.t == mop_S) 62 | { 63 | sval_t currentStackOffset = 0; 64 | instruction->d.get_stkoff(¤tStackOffset); 65 | if (currentStackOffset == stackOffset) 66 | { 67 | // Reset 68 | loopBlock = initialBlock; 69 | stackOffset += instruction->l.size; 70 | msg("[+] Found part of encrypted array at: %x with value: %x\n", instruction->ea, instruction->l.nnn->value); 71 | utils::pushIntegerToVector(m_EncryptedArrayVec, instruction->l.nnn->value, instruction->l.size); 72 | break; 73 | } 74 | } 75 | else if (instruction->opcode == m_call && instruction->l.t == mop_h && instruction->d.t == mop_f && utils::validateCopyFunction(instruction) 76 | && instruction->d.f->args[0].t == mop_a && (sval_t)instruction->d.f->args[0].a->nnn->value == stackOffset) 77 | { 78 | size_t sourceDataSize = 0; 79 | if (!qstrcmp(instruction->l.helper, "memcpy")) 80 | { 81 | sourceDataSize = instruction->d.f->args[2].nnn->value; 82 | } 83 | else { 84 | sourceDataSize = instruction->d.f->args[1].size; 85 | } 86 | stackOffset += sourceDataSize; 87 | // In case of cncs, the encrypted arrays can be stored in a global var. 88 | if (!qstrcmp(instruction->l.helper, "memcpy") && instruction->d.f->args[1].t == mop_a && instruction->d.f->args[1].is_glbaddr() 89 | && instruction->d.f->args[2].nnn->value == m_EncryptedStringSize && m_EncryptedStringSize > 0) 90 | { 91 | msg("[+] Potential encrypted CNCs detected at: %x\n", instruction->ea); 92 | unsigned char* encryptedCNCsBuffer = (unsigned char*)malloc(m_EncryptedStringSize); 93 | memset(encryptedCNCsBuffer, 0, m_EncryptedStringSize); 94 | if (get_bytes(encryptedCNCsBuffer, m_EncryptedStringSize, instruction->d.f->args[1].nnn->value)) 95 | m_EncryptedArrayVec.insert(m_EncryptedArrayVec.end(), encryptedCNCsBuffer, encryptedCNCsBuffer + m_EncryptedStringSize); 96 | free(encryptedCNCsBuffer); 97 | return m_EncryptedArrayVec.size(); 98 | } 99 | char* functionSourceString = instruction->d.f->args[1].cstr; 100 | if (strlen(functionSourceString) != sourceDataSize && sourceDataSize > 0) 101 | { 102 | std::string parsedString = utils::parseEscapedString(functionSourceString); 103 | utils::pushToVector(m_EncryptedArrayVec, parsedString); 104 | utils::padEncryptedArray(m_EncryptedArrayVec, parsedString.size(), sourceDataSize); 105 | } 106 | else { 107 | utils::pushToVector(m_EncryptedArrayVec, functionSourceString); 108 | } 109 | loopBlock = initialBlock; 110 | break; 111 | } 112 | } 113 | if (functionHead == nullptr || functionHead->ea == functionStartAddress || m_EncryptedStringSize == m_EncryptedArrayVec.size()) 114 | break; 115 | loopBlock = loopBlock->prevb; 116 | functionHead = loopBlock->head; 117 | } 118 | } 119 | if (m_EncryptedArrayVec.size() == m_EncryptedStringSize) 120 | return true; 121 | return false; 122 | } 123 | }; 124 | 125 | std::vector Decrypter::getEncryptedArray(minsn_t* insn, mblock_t* block, const size_t& encryptedStringSize) 126 | { 127 | FindEncryptedArray searchEncryptedArray; 128 | searchEncryptedArray.blk = block; 129 | searchEncryptedArray.m_EncryptedStringSize = encryptedStringSize; 130 | if (!insn->for_all_ops(searchEncryptedArray)) 131 | { 132 | error("[-] Failed to extract encrypted array. Got incorrect size of: %d\n", searchEncryptedArray.m_EncryptedArrayVec.size()); 133 | return {}; 134 | } 135 | return searchEncryptedArray.m_EncryptedArrayVec; 136 | } 137 | 138 | size_t Decrypter::getRC4EncryptedStringSize(mblock_t* block) 139 | { 140 | for (minsn_t* instruction = block->tail; instruction != nullptr; instruction = instruction->prev) 141 | { 142 | if ((instruction->opcode == m_jb || instruction->opcode == m_jae || instruction->opcode == m_setb) && instruction->r.t == mop_n) 143 | { 144 | msg("[+] Found encrypted array size: %d\n", instruction->r.nnn->value); 145 | return instruction->r.nnn->value; 146 | } 147 | } 148 | msg("[-] Failed to extract encrypted array size\n"); 149 | return 0; 150 | } 151 | 152 | std::string Decrypter::decryptAESString(std::string inputString) 153 | { 154 | std::string aesdecryptedString; 155 | if (m_AESKey.empty() || m_AESIV.empty()) 156 | { 157 | msg("[-] AES key/IV not set\n"); 158 | return aesdecryptedString; 159 | } 160 | std::replace(inputString.begin(), inputString.end(), '_', '='); 161 | size_t decodedBase64Length = decode_base64(reinterpret_cast(inputString.data())); 162 | if (decodedBase64Length && !(decodedBase64Length % 16)) 163 | { 164 | unsigned char* decryptedString = utils::decryptAESCBC(reinterpret_cast(m_AESKey.data()), 165 | reinterpret_cast(m_AESIV.data()), reinterpret_cast(inputString.data()), decodedBase64Length); 166 | if (utils::isPrintableAscii(decryptedString)) 167 | { 168 | msg("[+] Decrypted string: %s\n", decryptedString); 169 | aesdecryptedString = reinterpret_cast(decryptedString); 170 | delete[] decryptedString; 171 | return aesdecryptedString; 172 | } 173 | delete[] decryptedString; 174 | } 175 | return aesdecryptedString; 176 | } 177 | 178 | std::string Decrypter::decryptRC4String(std::vector encryptedArray, char* RC4Key, const size_t& encryptedRC4StringSize) 179 | { 180 | std::string decryptedString; 181 | if (!RC4Key) 182 | return decryptedString; 183 | std::string parsedRC4Key; 184 | utils::unescapeString(RC4Key, parsedRC4Key); 185 | if (!parsedRC4Key.length()) 186 | return {}; 187 | utils::rc4_state RC4Ctx = { 0 }; 188 | utils::rc4_setup(&RC4Ctx, reinterpret_cast(parsedRC4Key.data()), parsedRC4Key.length()); 189 | utils::rc4_crypt(&RC4Ctx, encryptedArray.data(), encryptedArray.size()); 190 | encryptedArray.push_back(0); 191 | decryptedString = reinterpret_cast(encryptedArray.data()); 192 | if (utils::isPrintableAscii(encryptedArray.data()) && decryptedString.length() == encryptedRC4StringSize) 193 | { 194 | msg("[+] RC4 decrypted string: %s with key %s\n", decryptedString.c_str(), RC4Key); 195 | return decryptedString; 196 | } 197 | msg("[-] RC4 decryption failed with key %s\n", parsedRC4Key.c_str()); 198 | return {}; 199 | } -------------------------------------------------------------------------------- /decrypter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "base64.hpp" 5 | #include "heuristics.hpp" 6 | #include "utils.hpp" 7 | 8 | class Decrypter 9 | { 10 | public: 11 | // Parses and decrypts the CNCs. The CNCs list is splitted based on the delimiter `&` 12 | std::vector decryptCNCs(std::string input); 13 | // Decrypts a RC4 string 14 | std::string decryptRC4String(std::vector encryptedArray, char* RC4Key, const size_t& encryptedRC4StringSize); 15 | // Decrypts a AES string 16 | std::string decryptAESString(std::string inputString); 17 | size_t getRC4EncryptedStringSize(mblock_t* block); 18 | std::vector getEncryptedArray(minsn_t* insn, mblock_t* block, const size_t& encryptedStringSize); 19 | std::string m_AESKey; 20 | std::string m_AESIV; 21 | }; -------------------------------------------------------------------------------- /handlers.cpp: -------------------------------------------------------------------------------- 1 | #include "handlers.hpp" 2 | 3 | int handlers::DecryptPikabotString::visit_minsn() 4 | { 5 | if (!heuristic::isRC4Encrypted(*curins)) 6 | return 0; 7 | msg("[+] Searching for the size of encrypted array at: %x\n", curins->ea); 8 | size_t encryptedRC4StringSize = m_Decrypter.getRC4EncryptedStringSize(blk); 9 | if (!encryptedRC4StringSize) 10 | return 0; 11 | std::vector encryptedArray = m_Decrypter.getEncryptedArray(curins, blk, encryptedRC4StringSize); 12 | if (encryptedArray.empty()) 13 | return 0; 14 | std::string decryptedRC4String; 15 | for (auto& [keyAddress, RC4Key] : m_rc4Keys) 16 | { 17 | if (std::find(m_RC4SuccessfullKeysAddresses.begin(), m_RC4SuccessfullKeysAddresses.end(), keyAddress) != m_RC4SuccessfullKeysAddresses.end()) 18 | continue; 19 | decryptedRC4String = m_Decrypter.decryptRC4String(encryptedArray, RC4Key.data(), encryptedRC4StringSize); 20 | if (!decryptedRC4String.empty()) 21 | { 22 | m_RC4DecryptedStrings.push_back(decryptedRC4String); 23 | m_RC4SuccessfullKeysAddresses.push_back(keyAddress); 24 | break; 25 | } 26 | } 27 | if (decryptedRC4String.empty()) 28 | { 29 | msg("[-] Failed to decrypt with any of RC4 candidate keys at: %x\n", curins->ea); 30 | return 0; 31 | } 32 | std::string decryptedAESString = m_Decrypter.decryptAESString(decryptedRC4String); 33 | if (!decryptedAESString.empty()) 34 | { 35 | decryptedStrings[curins->ea] = decryptedAESString.c_str(); 36 | return 0; 37 | } 38 | std::vector decryptedCNCs = m_Decrypter.decryptCNCs(decryptedRC4String); 39 | if (decryptedCNCs.empty()) 40 | { 41 | decryptedStrings[curins->ea] = decryptedRC4String.c_str(); 42 | msg("[!] Failed to AES decrypt at: %x. Using RC4 decrypted string as a comment\n", curins->ea); 43 | return 0; 44 | } 45 | std::ostringstream concatenatedCNCs; 46 | std::copy(decryptedCNCs.begin(), decryptedCNCs.end(), 47 | std::ostream_iterator(concatenatedCNCs, "\r\n")); 48 | decryptedStrings[curins->ea] = concatenatedCNCs.str(); 49 | return 0; 50 | } 51 | 52 | int handlers::getAllRC4Keys::visit_minsn() 53 | { 54 | if (curins->opcode == m_call && curins->l.t == mop_h && !qstrcmp(curins->l.helper, "strcpy") && 55 | curins->d.t == mop_f && utils::validateCopyFunction(curins) && curins->d.f->args[1].t == mop_str) 56 | { 57 | m_rc4Keys[curins->ea] = curins->d.f->args[1].cstr; 58 | } 59 | else if (curins->opcode == m_mov && curins->l.t == mop_v && curins->d.t == mop_S && curins->l.g != 0) 60 | { 61 | unsigned char sourceString[50] = { 0 }; 62 | // Should be enough to cover all cases. 63 | size_t readBytes = get_bytes(sourceString, 50, curins->l.g); 64 | if (readBytes > 0 && utils::isPrintableAscii(sourceString)) 65 | m_rc4Keys[curins->ea] = reinterpret_cast(sourceString); 66 | } 67 | return 0; 68 | 69 | } 70 | 71 | void handlers::decryptAllStrings(pluginCtx* plugin) 72 | { 73 | for (size_t i = 0; i < get_func_qty(); i++) 74 | { 75 | func_t* currentFunction = getn_func(i); 76 | mba_ranges_t mbr(currentFunction); 77 | hexrays_failure_t hexraysFailure; 78 | mbl_array_t* mba = gen_microcode(mbr, &hexraysFailure); 79 | if (mba == nullptr) 80 | { 81 | msg("[-] Failed to decompile at: %x - with error: %s\n", currentFunction->start_ea, hexraysFailure.desc().c_str()); 82 | continue; 83 | } 84 | getAllRC4Keys getRC4Keys; 85 | mba->for_all_topinsns(getRC4Keys); 86 | if (getRC4Keys.m_rc4Keys.empty()) 87 | { 88 | delete mba; 89 | continue; 90 | } 91 | DecryptPikabotString visitor(plugin->decrypter, getRC4Keys.m_rc4Keys); 92 | mba->for_all_topinsns(visitor); 93 | if (visitor.decryptedStrings.empty()) 94 | { 95 | delete mba; 96 | continue; 97 | } 98 | cfuncptr_t cfunc = create_cfunc(mba); 99 | for (auto const& [address, decryptedString] : visitor.decryptedStrings) 100 | { 101 | utils::setDecompilerComment(*cfunc, address, decryptedString); 102 | } 103 | cfunc->save_user_cmts(); 104 | cfunc->refresh_func_ctext(); 105 | visitor.decryptedStrings.clear(); 106 | } 107 | msg("[+] Plugin task completed\n"); 108 | } 109 | 110 | bool handlers::extractAESInfo(pluginCtx* plugin) 111 | { 112 | if (!plugin->decrypter.m_AESKey.empty() && !plugin->decrypter.m_AESIV.empty()) 113 | { 114 | msg("[+] AES info function already found. Current AES key/IV %s - %s\n", plugin->decrypter.m_AESKey.c_str(), plugin->decrypter.m_AESIV.c_str()); 115 | return true; 116 | } 117 | for (size_t i = 0; i < get_func_qty(); i++) 118 | { 119 | func_t* currentFunction = getn_func(i); 120 | // We limit the block size that we scan 121 | if (currentFunction->size() < 600 || currentFunction->size() > 1600) 122 | continue; 123 | mba_ranges_t mbr(currentFunction); 124 | hexrays_failure_t hexraysFailure; 125 | mbl_array_t* mba = gen_microcode(mbr, &hexraysFailure); 126 | if (mba == nullptr) 127 | { 128 | msg("[-] Failed to decompile at: %x - with error: %s\n", currentFunction->start_ea, hexraysFailure.desc().c_str()); 129 | continue; 130 | } 131 | msg("[+] Scanning function %x for AES/IV with size: %d\n", currentFunction->start_ea, currentFunction->size()); 132 | if (heuristic::isAESBlock(*mba)) 133 | { 134 | msg("[+] Found AES func at: %x\n", currentFunction->start_ea); 135 | getAllRC4Keys getRC4Keys; 136 | mba->for_all_topinsns(getRC4Keys); 137 | if (getRC4Keys.m_rc4Keys.empty()) 138 | { 139 | msg("[-] Failed to extract RC4 keys candidates in AES function\n"); 140 | delete mba; 141 | return false; 142 | } 143 | DecryptPikabotString visitor(plugin->decrypter, getRC4Keys.m_rc4Keys); 144 | mba->for_all_topinsns(visitor); 145 | if (visitor.m_RC4DecryptedStrings.empty() || visitor.m_RC4DecryptedStrings.size() != 2) 146 | { 147 | msg("[!] Incorrect number of decrypted strings during AES searching.\n"); 148 | delete mba; 149 | return false; 150 | } 151 | else 152 | { 153 | std::string AESKey = *std::max_element(visitor.m_RC4DecryptedStrings.begin(), visitor.m_RC4DecryptedStrings.end(), [](const auto& firstElement, const auto& secondElement) { 154 | return firstElement.size() < secondElement.size(); }); 155 | std::erase(visitor.m_RC4DecryptedStrings, AESKey); 156 | AESKey = AESKey.substr(0, 32); 157 | plugin->decrypter.m_AESKey = AESKey; 158 | std::string AESIV = visitor.m_RC4DecryptedStrings.front(); 159 | AESIV = AESIV.substr(0, 16); 160 | plugin->decrypter.m_AESIV = AESIV; 161 | msg("[+] AES key: %s and IV %s\n", AESKey.c_str(), AESIV.c_str()); 162 | delete mba; 163 | return true; 164 | } 165 | } 166 | delete mba; 167 | } 168 | msg("[-] Failed to detect AES key/IV\n"); 169 | return false; 170 | } -------------------------------------------------------------------------------- /handlers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "plugin.hpp" 3 | 4 | namespace handlers 5 | { 6 | /* 7 | * Decrypts a detected Pikabot string. The following steps are taken: 8 | * 1) Detects the size of the encrypted array. 9 | * 2) Detects and extracts the RC4 key. 10 | * 3) Detects and extracts the RC4-Encrypted array. 11 | * 4) Decrypts the encrypted array using RC4 followed by AES. 12 | * 5) In case of CNCs, it parses them. 13 | */ 14 | struct ida_local DecryptPikabotString : public minsn_visitor_t 15 | { 16 | Decrypter& m_Decrypter; 17 | std::vector m_RC4DecryptedStrings; 18 | std::vector m_RC4SuccessfullKeysAddresses; 19 | std::map m_rc4Keys; 20 | std::map decryptedStrings; 21 | DecryptPikabotString(Decrypter& mDecrypter, std::map rc4keys) : m_Decrypter(mDecrypter), m_rc4Keys(rc4keys) {} 22 | int visit_minsn() override; 23 | }; 24 | 25 | // Extract all inlined strings from a function and use them as RC4 keys. 26 | struct ida_local getAllRC4Keys : public minsn_visitor_t 27 | { 28 | std::map m_rc4Keys; 29 | int visit_minsn() override; 30 | }; 31 | 32 | void decryptAllStrings(pluginCtx* plugin); 33 | 34 | // Extract the AES key/IV, which are used for decrypting the last layer of each string. 35 | bool extractAESInfo(pluginCtx* plugin); 36 | } -------------------------------------------------------------------------------- /heuristics.cpp: -------------------------------------------------------------------------------- 1 | #include "heuristics.hpp" 2 | 3 | struct DetectRC4Encryption : public minsn_visitor_t 4 | { 5 | bool m_StxFound = false; 6 | bool m_XorFound = false; 7 | int visit_minsn(void) override 8 | { 9 | switch (curins->opcode) 10 | { 11 | case m_xor: 12 | m_XorFound = true; 13 | break; 14 | case m_stx: 15 | m_StxFound = true; 16 | break; 17 | default: 18 | return 0; 19 | } 20 | return m_StxFound && m_XorFound; 21 | } 22 | }; 23 | 24 | bool heuristic::isRC4Encrypted(const minsn_t& insn) 25 | { 26 | DetectRC4Encryption visitor; 27 | return CONST_CAST(minsn_t*)(&insn)->for_all_insns(visitor) && insn.has_side_effects(true); 28 | } 29 | 30 | struct isAesFunction : minsn_visitor_t 31 | { 32 | bool m_StxFound = false; 33 | bool m_JnzFound = false; 34 | bool m_RC4Found = false; 35 | 36 | int visit_minsn() override 37 | { 38 | if (curins->opcode == m_stx && curins->l.t == mop_n && curins->l.nnn->value == 0x3D) 39 | m_StxFound = true; 40 | else if (curins->opcode == m_jnz && curins->r.t == mop_n && curins->r.nnn->value == 0x5F) 41 | m_JnzFound = true; 42 | else if (heuristic::isRC4Encrypted(*curins)) 43 | m_RC4Found = true; 44 | if (m_StxFound && m_JnzFound && m_RC4Found) 45 | return 1; 46 | return 0; 47 | } 48 | }; 49 | 50 | bool heuristic::isAESBlock(const mbl_array_t& mba) 51 | { 52 | isAesFunction visitor; 53 | return CONST_CAST(mbl_array_t*)(&mba)->for_all_topinsns(visitor); 54 | } -------------------------------------------------------------------------------- /heuristics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ida.hpp" 4 | 5 | namespace heuristic 6 | { 7 | // Similar methodology as used in Goomba plugin. 8 | bool isRC4Encrypted(const minsn_t& insn); 9 | bool isAESBlock(const mbl_array_t& mba); 10 | } -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "plugin.hpp" 2 | 3 | int idaapi decryptFunctionStrsHandler::activate(action_activation_ctx_t* ctx) 4 | { 5 | vdui_t* vdui = get_widget_vdui(ctx->widget); 6 | if (vdui != nullptr) 7 | { 8 | if (!handlers::extractAESInfo(pluginModule)) 9 | return 0; 10 | pluginModule->pluginSingleDecryptActive = true; 11 | vdui->refresh_view(true); 12 | return 1; 13 | } 14 | return 0; 15 | } 16 | 17 | int idaapi decryptAllHandler::activate(action_activation_ctx_t* ctx) 18 | { 19 | vdui_t* vdui = get_widget_vdui(ctx->widget); 20 | if (vdui != nullptr) 21 | { 22 | if (!handlers::extractAESInfo(pluginModule)) 23 | return 0; 24 | handlers::decryptAllStrings(pluginModule); 25 | vdui->refresh_view(true); 26 | return 1; 27 | } 28 | return 0; 29 | } 30 | 31 | static plugmod_t* idaapi init() 32 | { 33 | if (!init_hexrays_plugin()) 34 | { 35 | error("[-] Decompiler plugin not found\n"); 36 | return PLUGIN_SKIP; 37 | } 38 | msg("[+] Pikabot deobfuscator (v%s) initialized\n", VERSION); 39 | pluginCtx* plugmod = new pluginCtx; 40 | return plugmod; 41 | } 42 | 43 | plugin_t PLUGIN = 44 | { 45 | IDP_INTERFACE_VERSION, 46 | PLUGIN_HIDE | PLUGIN_MULTI, 47 | init, 48 | nullptr, 49 | nullptr, 50 | nullptr, 51 | nullptr, 52 | "PikabotDeobfuscator", 53 | nullptr 54 | }; -------------------------------------------------------------------------------- /pikabot_plugin.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThreatLabz/pikabot-deobfuscator/34c6fd5dd7917097a905fdb54909b76191345b7d/pikabot_plugin.dll -------------------------------------------------------------------------------- /plugin.cpp: -------------------------------------------------------------------------------- 1 | #include "plugin.hpp" 2 | // Callback for decompiler events. Once the global optimization has finished, 3 | // we attempt to decrypt the strings of the function. 4 | ssize_t callback(void* ud, hexrays_event_t event, va_list va) 5 | { 6 | pluginCtx* plugin = (pluginCtx*)ud; 7 | switch (event) 8 | { 9 | case hxe_populating_popup: 10 | { 11 | TWidget* widget = va_arg(va, TWidget*); 12 | TPopupMenu* popup = va_arg(va, TPopupMenu*); 13 | attach_action_to_popup(widget, popup, "PikabotDeobfuscator::Deobfuscate"); 14 | attach_action_to_popup(widget, popup, "PikabotDeobfuscator::DeobfuscateAll"); 15 | } 16 | break; 17 | case hxe_glbopt: 18 | { 19 | if (plugin->pluginSingleDecryptActive) 20 | { 21 | mba_t* mba = va_arg(va, mba_t*); 22 | plugin->pluginSingleDecryptActive = false; 23 | handlers::getAllRC4Keys getRC4Keys; 24 | mba->for_all_topinsns(getRC4Keys); 25 | if (getRC4Keys.m_rc4Keys.empty()) 26 | return MERR_OK; 27 | handlers::DecryptPikabotString visitor(plugin->decrypter, getRC4Keys.m_rc4Keys); 28 | mba->for_all_topinsns(visitor); 29 | plugin->m_decryptedStrings = visitor.decryptedStrings; 30 | msg("[+] Plugin task completed\n"); 31 | return MERR_OK; 32 | } 33 | } 34 | break; 35 | case hxe_func_printed: 36 | { 37 | if (!plugin->m_decryptedStrings.empty() && !plugin->pluginSingleDecryptActive) 38 | { 39 | cfunc_t* cfunc = va_arg(va, cfunc_t*); 40 | for (auto const& [address, decryptedString] : plugin->m_decryptedStrings) 41 | { 42 | utils::setDecompilerComment(*cfunc, address, decryptedString); 43 | } 44 | plugin->m_decryptedStrings.clear(); 45 | cfunc->save_user_cmts(); 46 | cfunc->refresh_func_ctext(); 47 | return MERR_LOOP; 48 | } 49 | } 50 | break; 51 | default: 52 | break; 53 | } 54 | return 0; 55 | } 56 | 57 | pluginCtx::pluginCtx() : decryptFunctionStringsHandler(this), decryptAllStringsHandler(this) 58 | { 59 | install_hexrays_callback(callback, this); 60 | register_action(ACTION_DESC_LITERAL_PLUGMOD( 61 | "PikabotDeobfuscator::Deobfuscate", 62 | "Run Pikabot deobfuscator for current function", 63 | &decryptFunctionStringsHandler, 64 | this, 65 | "Ctrl+Shift+F", 66 | "Deobfuscate Pikabot strings", 67 | -1)); 68 | register_action(ACTION_DESC_LITERAL_PLUGMOD( 69 | "PikabotDeobfuscator::DeobfuscateAll", 70 | "Run Pikabot deobfuscator for all strings", 71 | &decryptAllStringsHandler, 72 | this, 73 | "Ctrl+Shift+A", 74 | "Deobfuscate all Pikabot strings", 75 | -1)); 76 | } -------------------------------------------------------------------------------- /plugin.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define VERSION "0.1" 3 | struct pluginCtx; 4 | 5 | #include "decrypter.hpp" 6 | #include "handlers.hpp" 7 | 8 | 9 | // Callback for decompiler events. Once the global optimization has finished, 10 | // we attempt to decrypt the strings of the function. 11 | ssize_t callback(void* ud, hexrays_event_t event, va_list va); 12 | 13 | // Action handler for decrypting all strings in current function. 14 | struct decryptFunctionStrsHandler : public action_handler_t 15 | { 16 | pluginCtx* pluginModule; 17 | decryptFunctionStrsHandler(pluginCtx* _plugmod) : pluginModule(_plugmod) {} 18 | virtual int activate(action_activation_ctx_t* ctx) override; 19 | virtual action_state_t update(action_update_ctx_t*) override 20 | { 21 | return AST_ENABLE; 22 | }; 23 | }; 24 | 25 | // Action handler for decrypting all strings in all identified functions. 26 | struct decryptAllHandler : public action_handler_t 27 | { 28 | pluginCtx* pluginModule; 29 | decryptAllHandler(pluginCtx* _plugmod) : pluginModule(_plugmod) {} 30 | virtual int activate(action_activation_ctx_t* ctx) override; 31 | virtual action_state_t update(action_update_ctx_t*) override 32 | { 33 | return AST_ENABLE; 34 | }; 35 | }; 36 | 37 | // Initialize plugin context 38 | struct pluginCtx : public plugmod_t 39 | { 40 | decryptFunctionStrsHandler decryptFunctionStringsHandler; 41 | decryptAllHandler decryptAllStringsHandler; 42 | bool pluginSingleDecryptActive = false; 43 | std::map m_decryptedStrings; 44 | Decrypter decrypter; 45 | pluginCtx(); 46 | ~pluginCtx() { term_hexrays_plugin();} 47 | virtual bool idaapi run(size_t arg) override { return true; }; 48 | }; -------------------------------------------------------------------------------- /utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.hpp" 2 | 3 | void utils::pushToVector(std::vector& m_EncryptedArrayVec, const std::string& parsedString) 4 | { 5 | for (size_t i = 0; i < parsedString.size(); i++) 6 | { 7 | m_EncryptedArrayVec.push_back(static_cast(parsedString[i])); 8 | } 9 | } 10 | 11 | void utils::padEncryptedArray(std::vector& vectorInput, const size_t parsedStringSize, const size_t expectedLength) 12 | { 13 | if (parsedStringSize < expectedLength) 14 | { 15 | int padLength = expectedLength - parsedStringSize; 16 | for (size_t i = 0; i < padLength; i++) 17 | vectorInput.push_back('\0'); 18 | } 19 | } 20 | 21 | bool utils::validateCopyFunction(minsn_t* instruction) 22 | { 23 | mcallinfo_t* callInfo = instruction->d.f; 24 | if (!callInfo) 25 | { 26 | error("[-] Function's args is empty\n"); 27 | return false; 28 | } 29 | mcallargs_t& callArgs = callInfo->args; 30 | if (callArgs.size() != 2 && !qstrcmp(instruction->l.helper, "strcpy")) 31 | { 32 | error("[-] Number of strcpy function's args is incorrect\n"); 33 | return false; 34 | } 35 | if (callArgs.size() != 3 && !qstrcmp(instruction->l.helper, "memcpy")) 36 | { 37 | error("[-] Number of memcpy function's args is incorrect\n"); 38 | return false; 39 | } 40 | return true; 41 | } 42 | 43 | void utils::pushIntegerToVector(std::vector& vec, unsigned long long input, const size_t length) 44 | { 45 | for (size_t i = 0; i < length; i++) 46 | { 47 | vec.push_back((input >> (i * 8)) & 0xff); 48 | } 49 | } 50 | 51 | std::string utils::parseEscapedString(char* input) 52 | { 53 | std::pair const targetCharacters[] 54 | { 55 | { 'a', 0x7 }, 56 | { 'b', 0x8 }, 57 | { 'f', 0xC }, 58 | { 'n', 0xA }, 59 | { 'r', 0xD }, 60 | { 't', 0x9 }, 61 | { 'v', 0xB }, 62 | { '"', 0x22}, 63 | {'\\', '\\'} 64 | }; 65 | 66 | std::string parsedStr; 67 | for (size_t i = 1; i <= strlen(input); ++i) 68 | { 69 | parsedStr += input[i - 1]; 70 | for (std::pair const character : targetCharacters) 71 | { 72 | if (input[i] == character.first && input[i - 1] == '\\') 73 | { 74 | parsedStr[i - 1] = character.second; 75 | i += 1; 76 | break; 77 | } 78 | } 79 | } 80 | return parsedStr; 81 | } 82 | 83 | void utils::unescapeString(char* input, std::string& parsedRC4Key) 84 | { 85 | for (size_t i = 0; i < strlen(input); i++) 86 | { 87 | if (input[i] == '\\' && input[i + 1] == '\\') 88 | { 89 | parsedRC4Key += "\\"; 90 | i++; 91 | continue; 92 | } 93 | parsedRC4Key += input[i]; 94 | } 95 | } 96 | 97 | bool utils::isPrintableAscii(unsigned char* string) 98 | { 99 | for (int i = 0; string[i] != '\0'; i++) 100 | { 101 | bool isPrintableAscii = (string[i] & ~0x7f) == 0 && (isprint(string[i]) || isspace(string[i])); 102 | if (!isPrintableAscii) 103 | return false; 104 | } 105 | return true; 106 | } 107 | 108 | void utils::unpad(unsigned char* input, const size_t arrayLen) 109 | { 110 | size_t lastElement = input[arrayLen - 1]; 111 | if (lastElement > arrayLen) 112 | { 113 | return; 114 | } 115 | size_t stringLen = arrayLen - lastElement; 116 | std::memcpy(input, input, stringLen); 117 | input[stringLen] = '\0'; 118 | } 119 | 120 | void utils::rc4_setup(struct rc4_state* s, unsigned char* key, int length) 121 | { 122 | int i, j, k, * m, a; 123 | 124 | s->x = 0; 125 | s->y = 0; 126 | m = s->m; 127 | 128 | for (i = 0; i < 256; i++) 129 | { 130 | m[i] = i; 131 | } 132 | 133 | j = k = 0; 134 | 135 | for (i = 0; i < 256; i++) 136 | { 137 | a = m[i]; 138 | j = (unsigned char)(j + a + key[k]); 139 | m[i] = m[j]; m[j] = a; 140 | if (++k >= length) k = 0; 141 | } 142 | } 143 | 144 | void utils::rc4_crypt(struct rc4_state* s, unsigned char* data, int length) 145 | { 146 | int i, x, y, * m, a, b; 147 | 148 | x = s->x; 149 | y = s->y; 150 | m = s->m; 151 | 152 | for (i = 0; i < length; i++) 153 | { 154 | x = (unsigned char)(x + 1); a = m[x]; 155 | y = (unsigned char)(y + a); 156 | m[x] = b = m[y]; 157 | m[y] = a; 158 | data[i] ^= m[static_cast(a + b)]; 159 | } 160 | s->x = x; 161 | s->y = y; 162 | } 163 | 164 | unsigned char* utils::decryptAESCBC(const unsigned char* AESKey, const unsigned char* AESIV, const unsigned char* input, const size_t& inputLength) 165 | { 166 | if (!inputLength || (inputLength % 16)) 167 | return nullptr; 168 | AES aes(AESKeyLength::AES_256); 169 | unsigned char* decryptedString = aes.DecryptCBC(input, inputLength, AESKey, AESIV); 170 | unpad(decryptedString, inputLength); 171 | return decryptedString; 172 | } 173 | 174 | void utils::setDecompilerComment(cfunc_t& cfunc, unsigned long address, const std::string& decryptedString) 175 | { 176 | treeloc_t ref_tree; 177 | ref_tree.ea = address; 178 | ref_tree.itp = ITP_SEMI; 179 | cfunc.set_user_cmt(ref_tree, decryptedString.c_str()); 180 | } -------------------------------------------------------------------------------- /utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "aes.hpp" 5 | 6 | namespace utils 7 | { 8 | struct rc4_state 9 | { 10 | int x, y, m[256]; 11 | }; 12 | void setDecompilerComment(cfunc_t& cfunc, unsigned long address, const std::string& decryptedString); 13 | void pushIntegerToVector(std::vector& vec, unsigned long long input, const size_t len); 14 | bool isPrintableAscii(unsigned char* str); 15 | bool validateCopyFunction(minsn_t* p); 16 | void unescapeString(char* input, std::string& parsedRC4Key); 17 | void unpad(unsigned char* input, const size_t arr_len); 18 | void rc4_setup(struct rc4_state* s, unsigned char* key, int length); 19 | void rc4_crypt(struct rc4_state* s, unsigned char* data, int length); 20 | unsigned char* decryptAESCBC(const unsigned char* AESKey, const unsigned char* AESIV, const unsigned char* input, const size_t& inputLength); 21 | // Ugly hack to parse characters, which have been already escaped by IDA. e.g. t\n 22 | std::string parseEscapedString(char* input); 23 | void padEncryptedArray(std::vector& vectorInput, const size_t parsedStringSize, const size_t expectedSize); 24 | void pushToVector(std::vector& m_EncryptedArrayVec, const std::string& parsedString); 25 | } --------------------------------------------------------------------------------